additional Andrew provided Termux mods +
[goodguy/cinelerra.git] / cinelerra-5.1 / guicast / vframe.C
index 5cd9901b3666e38eb813ac4c0d1f92da3060a741..44d7f97e6284a3f5f2c333902cc3dfceef930afe 100644 (file)
@@ -21,6 +21,7 @@
 
 #include <errno.h>
 #include <png.h>
+#include <jpeglib.h>
 #include <stdio.h>
 #include <string.h>
 #include <stdint.h>
@@ -73,11 +74,6 @@ VFrameScene::~VFrameScene()
 }
 
 
-
-
-
-
-
 //static BCCounter counter;
 
 VFramePng::VFramePng(unsigned char *png_data, double s)
@@ -281,7 +277,11 @@ int VFrame::reset_parameters(int do_opengl)
        is_keyframe = 0;
        pixel_rgb = 0x000000; // BLACK
        pixel_yuv = 0x008080;
+       draw_alpha = 1.f;
+       draw_flags = ALIAS_OFF;
        stipple = 0;
+       clear_color = 0x000000;
+       clear_alpha = 0x00;
 
        if(do_opengl)
        {
@@ -334,7 +334,7 @@ if( memory_type != VFrame::SHARED )
                        shmid = -1;
                        break;
 
-               case VFrame::SHMGET:
+               case VFrame::SHM_GET:
                        if(data)
                                shmdt(data);
                        data = 0;
@@ -390,7 +390,8 @@ int VFrame::get_keyframe()
 
 void VFrame::get_temp(VFrame *&vfrm, int w, int h, int color_model)
 {
-       if( vfrm && ( vfrm->get_w() != w || vfrm->get_h() != h ) ) {
+       if( vfrm && ( vfrm->color_model != color_model ||
+           vfrm->get_w() != w || vfrm->get_h() != h ) ) {
                delete vfrm;  vfrm = 0;
        }
        if( !vfrm ) vfrm = new VFrame(w, h, color_model, 0);
@@ -415,7 +416,7 @@ long VFrame::get_bytes_per_line()
 
 long VFrame::get_data_size()
 {
-       return calculate_data_size(w, h, bytes_per_line, color_model) - 4;
+       return calculate_data_size(w, h, bytes_per_line, color_model) - BC_COLOR_ALIGN;
 }
 
 long VFrame::calculate_data_size(int w, int h, int bytes_per_line, int color_model)
@@ -499,7 +500,7 @@ int VFrame::allocate_data(unsigned char *data, int shmid,
                bytes_per_line : this->bytes_per_pixel * w;
 
 // Allocate data + padding for MMX
-       if(data) {
+       if( data ) {
 //printf("VFrame::allocate_data %d %p\n", __LINE__, this->data);
                memory_type = VFrame::SHARED;
                this->data = data;
@@ -508,9 +509,16 @@ int VFrame::allocate_data(unsigned char *data, int shmid,
                this->u_offset = u_offset;
                this->v_offset = v_offset;
        }
-       else if(shmid >= 0) {
-               memory_type = VFrame::SHMGET;
+       else if( shmid >= 0 ) {
+               memory_type = VFrame::SHM_GET;
                this->data = (unsigned char*)shmat(shmid, NULL, 0);
+               if( this->data == (unsigned char*)-1 ) {
+                       printf("VFrame::allocate_data %d could not attach"
+                               " shared memory, %dx%d (model %d) shmid=0x%08x\n",
+                               __LINE__, w, h, color_model, shmid);
+                       BC_Trace::dump_shm_stats(stdout);
+                       exit(1);
+               }
 //printf("VFrame::allocate_data %d shmid=%d data=%p\n", __LINE__, shmid, this->data);
                this->shmid = shmid;
                this->y_offset = y_offset;
@@ -527,20 +535,24 @@ int VFrame::allocate_data(unsigned char *data, int shmid,
                        this->shmid = shmget(IPC_PRIVATE, size, IPC_CREAT | 0777);
                        if( this->shmid >= 0 ) {
                                this->data = (unsigned char*)shmat(this->shmid, NULL, 0);
-//printf("VFrame::allocate_data %d %d %d %p\n", __LINE__, size, this->shmid, this->data);
-// This causes it to automatically delete when the program exits.
-                               shmctl(this->shmid, IPC_RMID, 0);
+                               if( this->data == (unsigned char *)-1 ) this->data = 0;
                        }
-                       else {
+                       if( !this->data ) {
                                printf("VFrame::allocate_data %d could not allocate"
                                        " shared memory, %dx%d (model %d) size=0x%08x\n",
                                        __LINE__, w, h, color_model, size);
                                BC_Trace::dump_shm_stats(stdout);
+                               exit(1);
                        }
+// This causes it to automatically delete when the program exits.
+                       shmctl(this->shmid, IPC_RMID, 0);
                }
-// Have to use malloc for libpng
-               if( !this->data ) {
+               else {
                        this->data = (unsigned char *)malloc(size);
+                       if( !this->data ) {
+                               printf("VFrame::allocate_data %dx%d: memory exhausted.\n", this->w, this->h);
+                               exit(1);
+                       }
                        this->shmid = -1;
                }
 // Memory check
@@ -548,8 +560,6 @@ int VFrame::allocate_data(unsigned char *data, int shmid,
 // printf("VFrame::allocate_data 2 this=%p w=%d h=%d this->data=%p\n",
 // this, this->w, this->h, this->data);
 
-               if(!this->data)
-                       printf("VFrame::allocate_data %dx%d: memory exhausted.\n", this->w, this->h);
 #ifdef LEAKER
 printf("==new %p from %p sz %d\n", this->data, __builtin_return_address(0), size);
 #endif
@@ -583,7 +593,7 @@ void VFrame::set_memory(unsigned char *data,
        else
        if(shmid >= 0)
        {
-               memory_type = VFrame::SHMGET;
+               memory_type = VFrame::SHM_GET;
                this->data = (unsigned char*)shmat(shmid, NULL, 0);
                this->shmid = shmid;
        }
@@ -625,7 +635,7 @@ void VFrame::set_compressed_memory(unsigned char *data,
        else
        if(shmid >= 0)
        {
-               memory_type = VFrame::SHMGET;
+               memory_type = VFrame::SHM_GET;
                this->data = (unsigned char*)shmat(shmid, NULL, 0);
                this->shmid = shmid;
        }
@@ -664,65 +674,76 @@ int VFrame::reallocate(
 
 int VFrame::allocate_compressed_data(long bytes)
 {
-       if(bytes < 1) return 1;
+       if( bytes < 1 ) return 1;
 
 // Want to preserve original contents
-       if(data && compressed_allocated < bytes)
-       {
+       if( data && compressed_allocated < bytes ) {
                int new_shmid = -1;
                unsigned char *new_data = 0;
-               if(BC_WindowBase::get_resources()->use_vframe_shm() && use_shm)
-               {
-                       new_shmid = shmget(IPC_PRIVATE,
-                               bytes,
-                               IPC_CREAT | 0777);
-                       new_data = (unsigned char*)shmat(new_shmid, NULL, 0);
+               if( BC_WindowBase::get_resources()->use_vframe_shm() && use_shm ) {
+                       new_shmid = shmget(IPC_PRIVATE, bytes, IPC_CREAT | 0777);
+                       if( new_shmid >= 0 ) {
+                               new_data = (unsigned char *) shmat(new_shmid, NULL, 0);
+                               if( new_data == (unsigned char *)-1 ) new_data = 0;
+                       }
+                       if( !new_data ) {
+                               printf("VFrame::allocate_compressed_data %d could not allocate"
+                                       " shared memory, %ld\n", __LINE__, bytes);
+                               BC_Trace::dump_shm_stats(stdout);
+                               exit(1);
+                       }
                        shmctl(new_shmid, IPC_RMID, 0);
                }
-               else
-               {
+               else {
 // Have to use malloc for libpng
                        new_data = (unsigned char *)malloc(bytes);
+                       if( !new_data ) {
+                               printf("VFrame::allocate_compressed_data %ld: memory exhausted.\n", bytes);
+                               exit(1);
+                       }
                }
 
                bcopy(data, new_data, compressed_allocated);
 UNBUFFER(data);
 
-               if(memory_type == VFrame::PRIVATE)
-               {
-                       if(shmid > 0) {
-                               if(data)
-                                       shmdt(data);
+               if( memory_type == VFrame::PRIVATE ) {
+                       if( shmid > 0 ) {
+                               if( data ) shmdt(data);
                        }
                        else
                                free(data);
                }
-               else
-               if(memory_type == VFrame::SHMGET)
-               {
-                       if(data)
-                               shmdt(data);
+               else if( memory_type == VFrame::SHM_GET ) {
+                       if( data ) shmdt(data);
                }
 
                data = new_data;
                shmid = new_shmid;
                compressed_allocated = bytes;
        }
-       else
-       if(!data)
-       {
-               if(BC_WindowBase::get_resources()->use_vframe_shm() && use_shm)
-               {
-                       shmid = shmget(IPC_PRIVATE,
-                               bytes,
-                               IPC_CREAT | 0777);
-                       data = (unsigned char*)shmat(shmid, NULL, 0);
+       else if( !data ) {
+               if( BC_WindowBase::get_resources()->use_vframe_shm() && use_shm ) {
+                       shmid = shmget(IPC_PRIVATE, bytes, IPC_CREAT | 0777);
+                       if( shmid >= 0 ) {
+                               data = (unsigned char *)shmat(shmid, NULL, 0);
+                               if( data == (unsigned char *)-1 ) data = 0;
+                       }
+                       if( !data ) {
+                               printf("VFrame::allocate_compressed_data %d: could not allocate"
+                                       " shared memory, %ld\n", __LINE__, bytes);
+                               BC_Trace::dump_shm_stats(stdout);
+                               exit(1);
+                       }
                        shmctl(shmid, IPC_RMID, 0);
                }
-               else
-               {
+               else {
 // Have to use malloc for libpng
                        data = (unsigned char *)malloc(bytes);
+                       if( !data ) {
+                               printf("VFrame::allocate_compressed_data %d: memory exhausted, %ld\n",
+                                       __LINE__, bytes);
+                               exit(1);
+                       }
                }
 
                compressed_allocated = bytes;
@@ -782,8 +803,7 @@ int VFramePng::read_png(const unsigned char *data, long sz, double xscale, doubl
                if (src_color_model == PNG_COLOR_TYPE_GRAY && png_get_bit_depth(png_ptr, info_ptr) < 8)
                        png_set_expand(png_ptr);
 
-               if (src_color_model == PNG_COLOR_TYPE_GRAY ||
-                   src_color_model == PNG_COLOR_TYPE_GRAY_ALPHA)
+               if (src_color_model == PNG_COLOR_TYPE_GRAY_ALPHA)
                        png_set_gray_to_rgb(png_ptr);
 
                /* expand paletted or RGB images with transparency to full alpha channels
@@ -793,26 +813,22 @@ int VFramePng::read_png(const unsigned char *data, long sz, double xscale, doubl
                        png_set_expand(png_ptr);
                }
 
-               switch(src_color_model)
-               {
-                       case PNG_COLOR_TYPE_GRAY:
-                       case PNG_COLOR_TYPE_RGB:
-                               new_color_model = BC_RGB888;
-                               break;
-
-                       case PNG_COLOR_TYPE_GRAY_ALPHA:
-                       case PNG_COLOR_TYPE_RGB_ALPHA:
-                       default:
-                               new_color_model = BC_RGBA8888;
-                               break;
-
-                       case PNG_COLOR_TYPE_PALETTE:
-                               if(have_alpha)
-                                       new_color_model = BC_RGBA8888;
-                               else
-                                       new_color_model = BC_RGB888;
+               switch(src_color_model) {
+               case PNG_COLOR_TYPE_GRAY:
+                       new_color_model = BC_GREY8;
+                       break;
+               case PNG_COLOR_TYPE_RGB:
+                       new_color_model = BC_RGB888;
+                       break;
+               case PNG_COLOR_TYPE_PALETTE:
+                       new_color_model = have_alpha ? BC_RGBA8888 : BC_RGB888;
+                       break;
+               case PNG_COLOR_TYPE_GRAY_ALPHA:
+               case PNG_COLOR_TYPE_RGB_ALPHA:
+               default:
+                       new_color_model = BC_RGBA8888;
+                       break;
                }
-
                reallocate(NULL, -1, 0, 0, 0, w, h, new_color_model, -1);
 
 //printf("VFrame::read_png %d %d %d %p\n", __LINE__, w, h, get_rows());
@@ -826,6 +842,8 @@ int VFramePng::read_png(const unsigned char *data, long sz, double xscale, doubl
                return 1;
        }
        int ww = w * xscale, hh = h * yscale;
+       if( ww < 1 ) ww = 1;
+       if( hh < 1 ) hh = 1;
        if( ww != w || hh != h ) {
                VFrame vframe(*this);
                reallocate(NULL, -1, 0, 0, 0, ww, hh, color_model, -1);
@@ -874,6 +892,28 @@ int VFrame::write_png(const char *path)
        return 0;
 }
 
+void VFrame::write_ppm(VFrame *vfrm, const char *fmt, ...)
+{
+       va_list ap;
+       va_start(ap, fmt);
+       char fn[BCTEXTLEN];
+       vsnprintf(fn, sizeof(fn), fmt, ap);
+       va_end(ap);
+       FILE *fp = fopen(fn,"w");
+       if( !fp ) { perror("write_ppm"); return; }
+       VFrame *frm = vfrm;
+       if( frm->get_color_model() != BC_RGB888 ) {
+               frm = new VFrame(frm->get_w(), frm->get_h(), BC_RGB888);
+               frm->transfer_from(vfrm);
+       }
+       int w = frm->get_w(), h = frm->get_h();
+       fprintf(fp,"P6\n%d %d\n255\n",w,h);
+       unsigned char **rows = frm->get_rows();
+       for( int i=0; i<h; ++i ) fwrite(rows[i],3,w,fp);
+       fclose(fp);
+       if( frm != vfrm ) delete frm;
+}
+
 
 #define ZERO_YUV(components, type, max) \
 { \
@@ -890,10 +930,10 @@ int VFrame::write_png(const char *path)
        } \
 }
 
-int VFrame::clear_frame()
+void VFrame::black_frame()
 {
        int sz = w * h;
-//printf("VFrame::clear_frame %d\n", __LINE__);
+//printf("VFrame::black_frame %d\n", __LINE__);
        switch(color_model) {
        case BC_COMPRESSED:
                break;
@@ -961,7 +1001,24 @@ int VFrame::clear_frame()
                bzero(data, calculate_data_size(w, h, bytes_per_line, color_model));
                break;
        }
-       return 0;
+}
+
+void VFrame::set_clear_color(int color, int alpha)
+{
+       clear_color = color;
+       clear_alpha = alpha;
+}
+int VFrame::get_clear_color() { return clear_color; }
+int VFrame::get_clear_alpha() { return clear_alpha; }
+
+void VFrame::clear_frame()
+{
+       if( clear_color >= 0 &&
+           !BC_CModels::init_color(clear_color, clear_alpha,
+                       get_rows(), get_color_model(), get_y(), get_u(), get_v(),
+                       0,0, get_w(),get_h(), get_bytes_per_line()) )
+               return;
+       black_frame();
 }
 
 void VFrame::rotate90()
@@ -1077,15 +1134,10 @@ void VFrame::flip_horiz()
 
 int VFrame::copy_from(VFrame *frame)
 {
-       if(this->w != frame->get_w() ||
-               this->h != frame->get_h())
-       {
+       if( this->w != frame->get_w() ||
+           this->h != frame->get_h() ) {
                printf("VFrame::copy_from %d sizes differ src %dx%d != dst %dx%d\n",
-                       __LINE__,
-                       frame->get_w(),
-                       frame->get_h(),
-                       get_w(),
-                       get_h());
+                       __LINE__, frame->get_w(), frame->get_h(), get_w(), get_h());
                return 1;
        }
 
@@ -1093,50 +1145,46 @@ int VFrame::copy_from(VFrame *frame)
        int h = MIN(this->h, frame->get_h());
        timestamp = frame->timestamp;
 
-       switch(frame->color_model)
-       {
-               case BC_COMPRESSED:
-                       allocate_compressed_data(frame->compressed_size);
-                       memcpy(data, frame->data, frame->compressed_size);
-                       this->compressed_size = frame->compressed_size;
-                       break;
+       switch( frame->color_model ) {
+       case BC_COMPRESSED:
+               allocate_compressed_data(frame->compressed_size);
+               memcpy(data, frame->data, frame->compressed_size);
+               this->compressed_size = frame->compressed_size;
+               break;
 
-               case BC_YUV410P:
-                       memcpy(get_y(), frame->get_y(), w * h);
-                       memcpy(get_u(), frame->get_u(), w / 4 * h / 4);
-                       memcpy(get_v(), frame->get_v(), w / 4 * h / 4);
-                       break;
+       case BC_YUV410P:
+               memcpy(get_y(), frame->get_y(), w * h);
+               memcpy(get_u(), frame->get_u(), w / 4 * h / 4);
+               memcpy(get_v(), frame->get_v(), w / 4 * h / 4);
+               break;
 
-               case BC_YUV420P:
-               case BC_YUV420PI:
-               case BC_YUV411P:
+       case BC_YUV420P:
+       case BC_YUV420PI:
+       case BC_YUV411P:
 //printf("%d %d %p %p %p %p %p %p\n", w, h, get_y(), get_u(), get_v(), frame->get_y(), frame->get_u(), frame->get_v());
-                       memcpy(get_y(), frame->get_y(), w * h);
-                       memcpy(get_u(), frame->get_u(), w * h / 4);
-                       memcpy(get_v(), frame->get_v(), w * h / 4);
-                       break;
+               memcpy(get_y(), frame->get_y(), w * h);
+               memcpy(get_u(), frame->get_u(), w * h / 4);
+               memcpy(get_v(), frame->get_v(), w * h / 4);
+               break;
 
-               case BC_YUV422P:
+       case BC_YUV422P:
 //printf("%d %d %p %p %p %p %p %p\n", w, h, get_y(), get_u(), get_v(), frame->get_y(), frame->get_u(), frame->get_v());
-                       memcpy(get_y(), frame->get_y(), w * h);
-                       memcpy(get_u(), frame->get_u(), w * h / 2);
-                       memcpy(get_v(), frame->get_v(), w * h / 2);
-                       break;
+               memcpy(get_y(), frame->get_y(), w * h);
+               memcpy(get_u(), frame->get_u(), w * h / 2);
+               memcpy(get_v(), frame->get_v(), w * h / 2);
+               break;
 
-               case BC_YUV444P:
+       case BC_YUV444P:
 //printf("%d %d %p %p %p %p %p %p\n", w, h, get_y(), get_u(), get_v(), frame->get_y(), frame->get_u(), frame->get_v());
-                       memcpy(get_y(), frame->get_y(), w * h);
-                       memcpy(get_u(), frame->get_u(), w * h);
-                       memcpy(get_v(), frame->get_v(), w * h);
-                       break;
-               default:
-// printf("VFrame::copy_from %d\n", calculate_data_size(w,
-//                             h,
-//                             -1,
-//                             frame->color_model));
+               memcpy(get_y(), frame->get_y(), w * h);
+               memcpy(get_u(), frame->get_u(), w * h);
+               memcpy(get_v(), frame->get_v(), w * h);
+               break;
+       default:
+// printf("VFrame::copy_from %d\n", calculate_data_size(w, h, -1, frame->color_model));
 // Copy without extra 4 bytes in case the source is a hardware device
-                       memmove(data, frame->data, get_data_size());
-                       break;
+               memmove(data, frame->data, get_data_size());
+               break;
        }
 
        return 0;
@@ -1147,7 +1195,8 @@ int VFrame::transfer_from(VFrame *that, int bg_color, int in_x, int in_y, int in
        timestamp = that->timestamp;
        copy_params(that);
 
-       if( this->get_color_model() == that->get_color_model() &&
+       if( in_x == 0 && in_y == 0 && in_w == that->get_w() && in_h == that->get_h() &&
+           bg_color == 0 && this->get_color_model() == that->get_color_model() &&
            this->get_w() == that->get_w() && this->get_h() == that->get_h() &&
            this->get_bytes_per_line() == that->get_bytes_per_line() )
                return this->copy_from(that);
@@ -1354,109 +1403,120 @@ int VFrame::get_memory_usage()
 // a (~alpha) transparency, 0x00==solid .. 0xff==transparent
 void VFrame::set_pixel_color(int rgb, int a)
 {
-       pixel_rgb = (rgb&0xffffff) | ~a<<24;
+       pixel_rgb = (~a<<24) | (rgb&0xffffff);
        int ir = 0xff & (pixel_rgb >> 16);
        int ig = 0xff & (pixel_rgb >> 8);
        int ib = 0xff & (pixel_rgb >> 0);
        YUV::yuv.rgb_to_yuv_8(ir, ig, ib);
-       pixel_yuv =  (~a<<24) | (ir<<16) | (ig<<8) | (ib<<0);
+       pixel_yuv = (~a<<24) | (ir<<16) | (ig<<8) | (ib<<0);
 }
 
 void VFrame::set_stiple(int mask)
 {
        stipple = mask;
 }
-
-int VFrame::draw_pixel(int x, int y)
+void VFrame::set_draw_alpha(float alpha)
 {
-       if( x < 0 || y < 0 || x >= get_w() || y >= get_h() ) return 1;
-
-#define DRAW_PIXEL(type, r, g, b, comps, a) { \
-       type **rows = (type**)get_rows(); \
-       type *rp = rows[y], *bp = rp + x*comps; \
-       bp[0] = r; \
-       if( comps > 1 ) { bp[1] = g; bp[2] = b; } \
-       if( comps == 4 )  bp[3] = a; \
+       draw_alpha = alpha;
+}
+void VFrame::set_draw_flags(int flags)
+{
+       draw_flags = flags;
 }
+
+int VFrame::draw_pixel(float x, float y, float a)
+{
+       int ix = x, iy = y;
+       if( ix < 0 || iy < 0 || ix >= get_w() || iy >= get_h() ) return 1;
+       if( a <= 0 ) return 0;
+       if( a > 1 ) a = 1;
+       int color = BC_CModels::is_yuv(color_model) ? pixel_yuv : pixel_rgb;
        float fr = 0, fg = 0, fb = 0, fa = 0;
-       int pixel_color = BC_CModels::is_yuv(color_model) ? pixel_yuv : pixel_rgb;
-       int ir = (0xff & (pixel_color >> 16));
-       int ig = (0xff & (pixel_color >> 8));
-       int ib = (0xff & (pixel_color >> 0));
-       int ia = (0xff & (pixel_color >> 24)) ^ 0xff;  // transparency, not opacity
-       if( (x+y) & stipple ) {
+       int ir = (0xff & (color >> 16));
+       int ig = (0xff & (color >> 8));
+       int ib = (0xff & (color >> 0));
+       int ia = (0xff & ~(color >> 24));  // transparency, not opacity
+       if( (ix+iy) & stipple ) {
                ir = 255 - ir;  ig = 255 - ig;  ib = 255 - ib;
        }
        int rr = (ir<<8) | ir, gg = (ig<<8) | ig, bb = (ib<<8) | ib, aa = (ia<<8) | ia;
+       float fmax = 65535.f;  fa = aa/fmax;
        if( BC_CModels::is_float(color_model) ) {
-               fr = rr/65535.f;  fg = gg/65535.f;  fb = bb/65535.f;  fa = aa/65535.f;
+               fr = rr/fmax;  fg = gg/fmax;  fb = bb/fmax;
        }
 
+#define DRAW_PIXEL(cmdl, type, r, g, b, ofs, max, comps) \
+case cmdl: { \
+ float src_a = fa*draw_alpha, src_1a = 1 - src_a; \
+ type **rows = (type**)get_rows(); \
+ type *rp = rows[iy], *bp = rp + ix*comps; \
+ bp[0] = src_a * r + src_1a * bp[0]; \
+ if( comps > 1 ) { \
+  bp[1] = src_a * (g-ofs) + src_1a * (bp[1]-ofs) + ofs; \
+  bp[2] = src_a * (b-ofs) + src_1a * (bp[2]-ofs) + ofs; \
+ } \
+ if( comps == 4 ) \
+  bp[3] = src_a * max + src_1a * bp[3]; \
+ break;\
+}
+
        switch(get_color_model()) {
-       case BC_A8:
-               DRAW_PIXEL(uint8_t, ib, 0, 0, 1, 0);
-               break;
-       case BC_RGB888:
-       case BC_YUV888:
-               DRAW_PIXEL(uint8_t, ir, ig, ib, 3, 0);
-               break;
-       case BC_RGBA8888:
-       case BC_YUVA8888:
-               DRAW_PIXEL(uint8_t, ir, ig, ib, 4, ia);
-               break;
-       case BC_RGB161616:
-       case BC_YUV161616:
-               DRAW_PIXEL(uint16_t, rr, gg, bb, 3, 0);
-               break;
-       case BC_RGBA16161616:
-       case BC_YUVA16161616:
-               DRAW_PIXEL(uint16_t, rr, gg, bb, 4, aa);
-               break;
-       case BC_RGB_FLOAT:
-               DRAW_PIXEL(float, fr, fg, fb, 3, 0);
-               break;
-       case BC_RGBA_FLOAT:
-               DRAW_PIXEL(float, fr, fg, fb, 4, fa);
-               break;
+       DRAW_PIXEL(BC_A8,           uint8_t, ib,  0,  0, 0x00, 0xff, 1);
+       DRAW_PIXEL(BC_RGB888,       uint8_t, ir, ig, ib, 0x00, 0xff, 3);
+       DRAW_PIXEL(BC_YUV888,       uint8_t, ir, ig, ib, 0x80, 0xff, 3);
+       DRAW_PIXEL(BC_RGBA8888,     uint8_t, ir, ig, ib, 0x00, 0xff, 4);
+       DRAW_PIXEL(BC_YUVA8888,     uint8_t, ir, ig, ib, 0x80, 0xff, 4);
+       DRAW_PIXEL(BC_RGB161616,    uint16_t, rr, gg, bb, 0x0000, 0xffff, 3);
+       DRAW_PIXEL(BC_YUV161616,    uint16_t, rr, gg, bb, 0x8000, 0xffff, 3);
+       DRAW_PIXEL(BC_RGBA16161616, uint16_t, rr, gg, bb, 0x0000, 0xffff, 4);
+       DRAW_PIXEL(BC_YUVA16161616, uint16_t, rr, gg, bb, 0x8000, 0xffff, 4);
+       DRAW_PIXEL(BC_RGB_FLOAT,    float, fr, fg, fb, 0., 1., 3);
+       DRAW_PIXEL(BC_RGBA_FLOAT,   float, fr, fg, fb, 0., 1., 4);
        }
        return 0;
 }
 
+int VFrame::draw_pixel(float x, float y, float frac, int axis)
+{
+       if( draw_flags ) {
+               int xs = axis, ys = 1-axis;
+               if( draw_flags & ALIAS_TOP ) draw_pixel(x-xs, y-ys, 1-frac);
+               draw_pixel(x, y, draw_flags & ALIAS_CTR ? 1-frac : 1);
+               if( draw_flags & ALIAS_BOT ) draw_pixel(x+xs, y+ys, frac);
+       }
+       else
+               draw_pixel(x, y);
+       return 0;
+}
 
-// Bresenham's
-void VFrame::draw_line(int x1, int y1, int x2, int y2)
+void VFrame::draw_line(float x1, float y1, float x2, float y2)
 {
        if( y1 > y2 ) {
                int tx = x1;  x1 = x2;  x2 = tx;
                int ty = y1;  y1 = y2;  y2 = ty;
        }
-
-       int x = x1, y = y1;
-       int dx = x2-x1, dy = y2-y1;
-       int dx2 = 2*dx, dy2 = 2*dy;
-       if( dx < 0 ) dx = -dx;
-       int r = dx > dy ? dx : dy, n = r;
-       int dir = 0;
-       if( dx2 < 0 ) dir += 1;
-       if( dy >= dx ) {
-               if( dx2 >= 0 ) do {     /* +Y, +X */
-                       draw_pixel(x, y++);
-                       if( (r -= dx2) < 0 ) { r += dy2;  ++x; }
-               } while( --n >= 0 );
-               else do {               /* +Y, -X */
-                       draw_pixel(x, y++);
-                       if( (r += dx2) < 0 ) { r += dy2;  --x; }
-               } while( --n >= 0 );
+       float dx = x2-x1, dy = y2-y1;
+       float s = dx ? dy/dx : 1;
+       float t = dy ? dx/dy : 0;
+       int xs = dx < 0 ? -1 : 1;
+       dx *= xs;
+       int idx = (int)x2 - (int)x1;
+       int idy = (int)y2 - (int)y1;
+       int d = dx >= dy ? abs(idx) : idy;
+       float x = x1, y = y1;
+       if( dx > dy ) {
+               draw_pixel(x, y, y-(int)y, 0);
+               while( --d >= 0 ) {
+                       y = y1 + ((x += xs) - x1) * s;
+                       draw_pixel(x, y, y-(int)y, 0);
+               }
        }
        else {
-               if( dx2 >= 0 ) do {     /* +X, +Y */
-                       draw_pixel(x++, y);
-                       if( (r -= dy2) < 0 ) { r += dx2;  ++y; }
-               } while( --n >= 0 );
-               else do {               /* -X, +Y */
-                       draw_pixel(x--, y);
-                       if( (r -= dy2) < 0 ) { r -= dx2;  ++y; }
-               } while( --n >= 0 );
+               draw_pixel(x, y, x-(int)x, 1);
+               while( --d >= 0 ) {
+                       x = x1 + (++y - y1) * t;
+                       draw_pixel(x, y, x-(int)x, 1);
+               }
        }
 }
 
@@ -1522,7 +1582,15 @@ void smooth_line::draw()
                if( abs(rr) < abs(r) )
                        moveX(rr);
        }
-xit:   vframe->draw_pixel(sx, sy);
+xit:
+//     vframe->draw_pixel(sx, sy);
+       float vx = abs(dx), vy = abs(dy);
+       float vv = 4*(vx > vy ? dx : dy);
+       float frac = vv ? -r / vv : 0;
+       frac = (1+frac) / 2;
+       bclamp(frac, 0, 1);
+       int axis = abs(dx) >= abs(dy) ? 1 : 0;
+       vframe->draw_pixel(sx, sy, frac, axis);
 }
 
 void VFrame::draw_smooth(int x1, int y1, int x2, int y2, int x3, int y3)
@@ -1656,10 +1724,10 @@ void smooth_line::init1(int x1,int y1, int x2,int y2, int x3,int y3)
                xmxx = ex; xmxy = ey;
        }
        if( xs > 0 )
-               vframe->draw_pixel(sx, sy);
+               vframe->draw_pixel(sx, sy, 0, 0);
        while( xs*(sx-xmxx) < 0 && (xs*dx < 0 || rx() < 0) ) {
                moveX(rx());
-               vframe->draw_pixel(sx, sy);
+               vframe->draw_pixel(sx, sy, 0, 0);
        }
 }
 
@@ -1683,9 +1751,9 @@ void VFrame::smooth_draw(int x1, int y1, int x2, int y2, int x3, int y3)
        else if( y1 < y2 && y3 < y2 ) {
                smooth_line lt(this), rt(this); // Q on top
                lt.init0(x1, y1, x2, y2, x3, y3, 1);
-               draw_pixel(lt.sx, lt.sy);
+               draw_pixel(lt.sx, lt.sy, 0, 0);
                rt.init0(x1, y1, x2, y2, x3, y3, -1);
-               draw_pixel(rt.sx, rt.sy);
+               draw_pixel(rt.sx, rt.sy, 0, 0);
                while( !lt.done || !rt.done ) {
                        lt.draw();
                        rt.draw();
@@ -1694,7 +1762,7 @@ void VFrame::smooth_draw(int x1, int y1, int x2, int y2, int x3, int y3)
        else {
                smooth_line pt(this);           // Q in between
                pt.init0(x1, y1, x2, y2, x3, y3, 0);
-               draw_pixel(pt.sx, pt.sy);
+               draw_pixel(pt.sx, pt.sy, 0, 1);
                while( !pt.done ) {
                        pt.draw();
                }
@@ -1789,3 +1857,255 @@ void VFrame::draw_t(int x, int y, int sz)
 }
 
 
+// jpeg decompress
+class jpeg_err : public jpeg_error_mgr
+{
+       static void s_error_exit(j_common_ptr cp);
+       static void s_output_message(j_common_ptr cp);
+public:
+       jpeg_err() {
+               jpeg_std_error((jpeg_error_mgr *)this);
+               error_exit = s_error_exit;
+               output_message = s_output_message;
+       }
+       ~jpeg_err() {}
+};
+
+class jpeg_src : public jpeg_source_mgr
+{
+       static void s_init_source(j_decompress_ptr jp);
+       static boolean s_fill_input_buffer(j_decompress_ptr jp);
+       static void s_skip_input_data(j_decompress_ptr jp, long len);
+       static void s_term_source(j_decompress_ptr jp);
+       static boolean s_resync_to_restart(j_decompress_ptr jp, int v);
+public:
+       jpeg_src();
+       ~jpeg_src();
+       int jpeg_file(int fd);
+       int jpeg_mem(const unsigned char *bfr, long len);
+
+       int fd;
+       unsigned char *mbfr;
+       long mlen;
+       enum { buffer_sz=0x10000, file_sz=0x100000, };
+       unsigned char *buffer;
+       boolean fill_buffer();
+       void skip_data(long len);
+};
+
+class JpegVFrame : public jpeg_decompress_struct
+{
+public:
+       JpegVFrame();
+       ~JpegVFrame();
+       int read_jpeg(VFrame *vfrm, double xs, double ys, int jpeg_model);
+
+       jpeg_err jerr;
+       jpeg_src jsrc;
+       int debug, ret;
+};
+
+
+void jpeg_err:: s_error_exit(j_common_ptr cp)
+{
+       JpegVFrame *jpeg = (JpegVFrame *)cp;
+       jpeg->ret = 1;
+       if( !jpeg->debug ) return;
+       printf("s_error_exit()\n");
+}
+
+void jpeg_err::
+s_output_message(j_common_ptr cp)
+{
+       JpegVFrame *jpeg = (JpegVFrame *)cp;
+       if( !jpeg->debug ) return;
+       char msg[JMSG_LENGTH_MAX];
+       (*cp->err->format_message)(cp, msg);
+       printf("s_output_message() = %s\n",&msg[0]);
+}
+
+
+jpeg_src::jpeg_src()
+{
+       init_source = s_init_source;
+       fill_input_buffer = s_fill_input_buffer;
+       skip_input_data = s_skip_input_data;
+       resync_to_restart = s_resync_to_restart;
+       term_source = s_term_source;
+
+       fd = -1;
+       buffer = 0;
+       mbfr = 0;
+       mlen = -1;
+}
+
+jpeg_src::~jpeg_src()
+{
+       if( mbfr ) ::munmap(mbfr, mlen);
+       delete [] buffer;
+}
+
+int jpeg_src::jpeg_file(int fd)
+{
+       this->fd = fd;
+       struct stat st;
+       if( fstat(fd, &st) || !st.st_size ) return 0;
+       if( st.st_size < file_sz ) {
+               mbfr = (unsigned char *)::mmap(0, mlen = st.st_size,
+                               PROT_READ, MAP_SHARED, fd, 0);
+               if( mbfr == MAP_FAILED ) return 0;
+               next_input_byte = mbfr;
+               bytes_in_buffer = mlen;
+       }
+       else {
+               buffer = new unsigned char[buffer_sz];
+               next_input_byte = &buffer[0];
+               bytes_in_buffer = 0;
+       }
+       return 1;
+}
+int jpeg_src::jpeg_mem(const unsigned char *bfr, long len)
+{
+       next_input_byte = bfr;
+       bytes_in_buffer = len;
+       return 1;
+}
+
+void jpeg_src::s_init_source(j_decompress_ptr jp) {}
+void jpeg_src::s_term_source(j_decompress_ptr jp) {}
+
+boolean jpeg_src::s_resync_to_restart(j_decompress_ptr jp, int v)
+{
+       return jpeg_resync_to_restart(jp, v);
+}
+
+boolean jpeg_src::s_fill_input_buffer(j_decompress_ptr jp)
+{
+       JpegVFrame *jpeg = (JpegVFrame *)jp;
+       return jpeg->jsrc.fill_buffer();
+}
+
+boolean jpeg_src::fill_buffer()
+{
+       if( mbfr || fd < 0 ) return 0;
+       long n = ::read(fd, buffer, buffer_sz);
+       if( n < 0 ) perror("jpeg read");
+       if( !n ) return 0;
+       next_input_byte = buffer;
+       bytes_in_buffer = n;
+       return 1;
+}
+
+void jpeg_src::s_skip_input_data(j_decompress_ptr jp, long len)
+{
+       JpegVFrame *jpeg = (JpegVFrame *)jp;
+       jpeg->jsrc.skip_data(len);
+}
+
+void jpeg_src::skip_data(long len)
+{
+       while( len > (long) bytes_in_buffer ) {
+               len -= (long) bytes_in_buffer;
+               if( !fill_buffer() ) return;
+       }
+       next_input_byte += len;
+       bytes_in_buffer -= len;
+}
+
+
+JpegVFrame::JpegVFrame()
+{
+       jpeg_create_decompress(this);
+       debug = 1; ret = 0;
+       err = &jerr;  src = &jsrc;
+}
+JpegVFrame::~JpegVFrame()
+{
+       jpeg_destroy_decompress(this);
+}
+
+int JpegVFrame::read_jpeg(VFrame *vfrm, double xs, double ys, int jpeg_model)
+{
+       VFrame *xfrm = vfrm;
+       int color_model = xfrm->get_color_model();
+       if( color_model == BC_COMPRESSED ) color_model = jpeg_model;
+       jpeg_abort((jpeg_common_struct *)this);
+       if( jpeg_read_header(this, TRUE) != JPEG_HEADER_OK ) return 0;
+       jpeg_calc_output_dimensions(this);
+       quantize_colors = FALSE;
+       out_color_space =
+               jpeg_model == BC_YUV888 ? JCS_YCbCr :
+               jpeg_model == BC_GREY8 ? JCS_GRAYSCALE : JCS_RGB;
+       jpeg_calc_output_dimensions(this);
+       int w = bmax(image_width*xs, 1.);
+       int h = bmax(image_height*ys, 1.);
+       vfrm->reallocate(0, -1, 0, 0, 0, w, h, color_model, -1);
+       if( w != (int)image_width || h != (int)image_height ||
+           color_model != jpeg_model )
+               xfrm = new VFrame(image_width, image_height, jpeg_model);
+       unsigned char *pic = xfrm->get_data();
+       int linesz = xfrm->get_bytes_per_line();
+       jpeg_start_decompress(this);
+       while( !ret && output_scanline < image_height ) {
+               JSAMPROW rowptr = (JSAMPROW) &pic[output_scanline * linesz];
+               jpeg_read_scanlines(this, &rowptr, (JDIMENSION) 1);
+       }
+       jpeg_finish_decompress(this);
+       if( vfrm != xfrm ) {
+               vfrm->transfer_from(xfrm);
+               delete xfrm;
+       }
+       return 1;
+}
+
+int VFrameJpeg::read_jpeg(const unsigned char *data, long sz,
+               double xscale, double yscale, int jpeg_model)
+{
+       JpegVFrame jpeg;
+       jpeg.jsrc.jpeg_mem(data, sz);
+       return jpeg.read_jpeg(this, xscale, yscale, jpeg_model);
+}
+
+VFrameJpeg::VFrameJpeg(const unsigned char *jpeg_data, double s)
+{
+       long image_size =
+               ((long)jpeg_data[0] << 24) | ((long)jpeg_data[1] << 16) |
+               ((long)jpeg_data[2] << 8)  |  (long)jpeg_data[3];
+       if( !s ) s = BC_WindowBase::get_resources()->icon_scale;
+       read_jpeg(jpeg_data+4, image_size, s, s, BC_RGB888);
+}
+
+VFrameJpeg::VFrameJpeg(const unsigned char *jpeg_data, long image_size, double xs, double ys)
+{
+       if( !xs ) xs = BC_WindowBase::get_resources()->icon_scale;
+       if( !ys ) ys = BC_WindowBase::get_resources()->icon_scale;
+       read_jpeg(jpeg_data, image_size, xs, ys, BC_RGB888);
+}
+
+VFrameJpeg::~VFrameJpeg()
+{
+}
+
+
+VFrame *VFrameJpeg::vframe_jpeg(int fd, double xs, double ys, int jpeg_model)
+{
+       JpegVFrame jpeg;
+       jpeg.jsrc.jpeg_file(fd);
+       VFrame *vfrm = new VFrame();
+       if( !jpeg.read_jpeg(vfrm, xs, ys, jpeg_model) ) {
+               delete vfrm;  vfrm = 0;
+       }
+       return vfrm;
+}
+
+VFrame *VFrameJpeg::vframe_jpeg(const char *jpeg_path, double xs, double ys, int jpeg_model)
+{
+       VFrame *vframe = 0;
+       int fd = ::open(jpeg_path, O_RDONLY);
+       if( fd >= 0 ) {
+               vframe = vframe_jpeg(fd, xs, ys, jpeg_model);
+               ::close(fd);
+       }
+       return vframe;
+}
+