add binfolder path relative filters, fix gbrp color model, vwdw timebar tweaks, title...
[goodguy/history.git] / cinelerra-5.1 / guicast / vframe.C
index e5d3759724ab1d8c460bd65b1f4806861c17c9cf..3eca676452ca8536fa3f78993e68a24bf35fd9a1 100644 (file)
@@ -24,7 +24,9 @@
 #include <stdio.h>
 #include <string.h>
 #include <stdint.h>
+#include <fcntl.h>
 #include <sys/shm.h>
+#include <sys/mman.h>
 
 #include "bcbitmap.h"
 #include "bchash.h"
@@ -78,41 +80,69 @@ VFrameScene::~VFrameScene()
 
 //static BCCounter counter;
 
-
-VFramePng::VFramePng(unsigned char *png_data, double scale)
+VFramePng::VFramePng(unsigned char *png_data, double s)
 {
        long image_size =
                ((long)png_data[0] << 24) | ((long)png_data[1] << 16) |
                ((long)png_data[2] << 8)  |  (long)png_data[3];
-       if( !scale ) scale = BC_WindowBase::get_resources()->icon_scale;
-       read_png(png_data+4, image_size, scale, scale);
+       if( !s ) s = BC_WindowBase::get_resources()->icon_scale;
+       read_png(png_data+4, image_size, s, s);
 }
 
-VFramePng::VFramePng(unsigned char *png_data, long image_size, double xscale, double yscale)
+VFramePng::VFramePng(unsigned char *png_data, long image_size, double xs, double ys)
 {
-       if( !xscale ) xscale = BC_WindowBase::get_resources()->icon_scale;
-       if( !yscale ) yscale = BC_WindowBase::get_resources()->icon_scale;
-       read_png(png_data, image_size, xscale, yscale);
+       if( !xs ) xs = BC_WindowBase::get_resources()->icon_scale;
+       if( !ys ) ys = BC_WindowBase::get_resources()->icon_scale;
+       read_png(png_data, image_size, xs, ys);
 }
 
 VFramePng::~VFramePng()
 {
 }
 
+VFrame *VFramePng::vframe_png(int fd, double xs, double ys)
+{
+       struct stat st;
+       if( fstat(fd, &st) ) return 0;
+       long len = st.st_size;
+       if( !len ) return 0;
+       int w = 0, h = 0;
+       unsigned char *bfr = (unsigned char *)
+               ::mmap (NULL, len, PROT_READ, MAP_SHARED, fd, 0);
+       if( bfr == MAP_FAILED ) return 0;
+       VFrame *vframe = new VFramePng(bfr, len, xs, ys);
+       if( (w=vframe->get_w()) <= 0 || (h=vframe->get_h()) <= 0 ||
+           vframe->get_data() == 0 ) { delete vframe;  vframe = 0; }
+       ::munmap(bfr, len);
+       return vframe;
+}
+VFrame *VFramePng::vframe_png(const char *png_path, double xs, double ys)
+{
+       VFrame *vframe = 0;
+       int fd = ::open(png_path, O_RDONLY);
+       if( fd >= 0 ) {
+               vframe = vframe_png(fd, xs, ys);
+               ::close(fd);
+       }
+       return vframe;
+}
 
 VFrame::VFrame(VFrame &frame)
 {
        reset_parameters(1);
        params = new BC_Hash;
+       use_shm = frame.use_shm;
        allocate_data(0, -1, 0, 0, 0, frame.w, frame.h,
                frame.color_model, frame.bytes_per_line);
-       memcpy(data, frame.data, bytes_per_line * h);
-       copy_stacks(&frame);
+       copy_vframe(&frame);
 }
 
+
 VFrame::VFrame(int w, int h, int color_model, long bytes_per_line)
 {
        reset_parameters(1);
+//  use bytes_per_line == 0 to allocate default unshared
+       if( !bytes_per_line ) { bytes_per_line = -1;  use_shm = 0; }
        params = new BC_Hash;
        allocate_data(data, -1, 0, 0, 0, w, h,
                color_model, bytes_per_line);
@@ -249,6 +279,9 @@ int VFrame::reset_parameters(int do_opengl)
        sequence_number = -1;
        timestamp = -1.;
        is_keyframe = 0;
+       pixel_rgb = 0x000000; // BLACK
+       pixel_yuv = 0x008080;
+       stipple = 0;
 
        if(do_opengl)
        {
@@ -275,6 +308,11 @@ int VFrame::clear_objects(int do_opengl)
                pbuffer = 0;
        }
 
+#ifdef LEAKER
+if( memory_type != VFrame::SHARED )
+  printf("==del %p from %p\n", data, __builtin_return_address(0));
+#endif
+
 // Delete data
        switch(memory_type)
        {
@@ -311,10 +349,12 @@ int VFrame::clear_objects(int do_opengl)
                case BC_YUV410P:
                case BC_YUV411P:
                case BC_YUV420P:
+               case BC_YUV420PI:
                case BC_YUV422P:
                case BC_YUV444P:
                case BC_RGB_FLOATP:
                case BC_RGBA_FLOATP:
+               case BC_GBRP:
                        break;
 
                default:
@@ -386,6 +426,7 @@ void VFrame::create_row_pointers()
                break;
 
        case BC_YUV420P:
+       case BC_YUV420PI:
        case BC_YUV411P:
                if( this->v_offset ) break;
                this->y_offset = 0;
@@ -405,6 +446,12 @@ void VFrame::create_row_pointers()
                this->u_offset = sz;
                this->v_offset = sz + sz;
                break;
+       case BC_GBRP:
+               if( this->v_offset ) break;
+               this->y_offset = 0;
+               this->u_offset = sz * sizeof(uint8_t);
+               this->v_offset = 2 * sz * sizeof(uint8_t);
+               break;
        case BC_RGBA_FLOATP:
                if( this->v_offset || a ) break;
                a = this->data + 3 * sz * sizeof(float);
@@ -463,26 +510,30 @@ int VFrame::allocate_data(unsigned char *data, int shmid,
        }
        else {
                memory_type = VFrame::PRIVATE;
+               this->data = 0;
                int size = calculate_data_size(this->w, this->h,
                        this->bytes_per_line, this->color_model);
-               if(BC_WindowBase::get_resources()->use_vframe_shm() && use_shm) {
+               if( use_shm && size >= SHM_MIN_SIZE &&
+                   BC_WindowBase::get_resources()->use_vframe_shm() ) {
                        this->shmid = shmget(IPC_PRIVATE, size, IPC_CREAT | 0777);
-                       if(this->shmid < 0) {
-                               printf("VFrame::allocate_data %d could not allocate shared memory\n", __LINE__);
-                       }
-
-                       this->data = (unsigned char*)shmat(this->shmid, NULL, 0);
-//printf("VFrame::allocate_data %d %d %d\n", __LINE__, size, this->shmid);
-
-//printf("VFrame::allocate_data %d %p\n", __LINE__, this->data);
+                       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);
+                               shmctl(this->shmid, IPC_RMID, 0);
+                       }
+                       else {
+                               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);
+                       }
                }
-               else {
 // Have to use malloc for libpng
+               if( !this->data ) {
                        this->data = (unsigned char *)malloc(size);
+                       this->shmid = -1;
                }
-
 // Memory check
 // if(this->w * this->h > 1500 * 1100)
 // printf("VFrame::allocate_data 2 this=%p w=%d h=%d this->data=%p\n",
@@ -490,6 +541,9 @@ int VFrame::allocate_data(unsigned char *data, int shmid,
 
                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
 
 //printf("VFrame::allocate_data %d %p data=%p %d %d\n", __LINE__, this, this->data, this->w, this->h);
 //if(size > 1000000) printf("VFrame::allocate_data %d\n", size);
@@ -773,49 +827,41 @@ int VFramePng::read_png(const unsigned char *data, long sz, double xscale, doubl
 
 int VFrame::write_png(const char *path)
 {
+       VFrame *vframe = this;
        png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
        png_infop info_ptr = png_create_info_struct(png_ptr);
        FILE *out_fd = fopen(path, "w");
-       if(!out_fd)
-       {
+       if(!out_fd) {
                printf("VFrame::write_png %d %s %s\n", __LINE__, path, strerror(errno));
                return 1;
        }
 
        int png_cmodel = PNG_COLOR_TYPE_RGB;
-       switch(get_color_model())
-       {
-               case BC_RGB888:
-               case BC_YUV888:
-                       png_cmodel = PNG_COLOR_TYPE_RGB;
-                       break;
-
-               case BC_RGBA8888:
-               case BC_YUVA8888:
+       int bc_cmodel = get_color_model();
+       switch( bc_cmodel ) {
+       case BC_RGB888:                                          break;
+       case BC_RGBA8888: png_cmodel = PNG_COLOR_TYPE_RGB_ALPHA; break;
+       case BC_A8:       png_cmodel = PNG_COLOR_TYPE_GRAY;      break;
+       default:
+               bc_cmodel = BC_RGB888;
+               if( BC_CModels::has_alpha(bc_cmodel) ) {
+                       bc_cmodel = BC_RGBA8888;
                        png_cmodel = PNG_COLOR_TYPE_RGB_ALPHA;
-                       break;
-
-               case BC_A8:
-                       png_cmodel = PNG_COLOR_TYPE_GRAY;
-                       break;
+               }
+               vframe = new VFrame(get_w(), get_h(), bc_cmodel, 0);
+               vframe->transfer_from(this);
+               break;
        }
-
        png_init_io(png_ptr, out_fd);
        png_set_compression_level(png_ptr, 9);
-       png_set_IHDR(png_ptr,
-               info_ptr,
-               get_w(),
-               get_h(),
-       8,
-               png_cmodel,
-               PNG_INTERLACE_NONE,
-               PNG_COMPRESSION_TYPE_DEFAULT,
-               PNG_FILTER_TYPE_DEFAULT);
+       png_set_IHDR(png_ptr, info_ptr, get_w(), get_h(), 8, png_cmodel,
+               PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
        png_write_info(png_ptr, info_ptr);
-       png_write_image(png_ptr, get_rows());
+       png_write_image(png_ptr, vframe->get_rows());
        png_write_end(png_ptr, info_ptr);
        png_destroy_write_struct(&png_ptr, &info_ptr);
        fclose(out_fd);
+       if( vframe != this ) delete vframe;
        return 0;
 }
 
@@ -845,26 +891,33 @@ int VFrame::clear_frame()
 
        case BC_YUV410P:
                bzero(get_y(), sz);
-               bzero(get_u(), w / 4 * h / 4);
-               bzero(get_v(), w / 4 * h / 4);
+               memset(get_u(), 0x80, w / 4 * h / 4);
+               memset(get_v(), 0x80, w / 4 * h / 4);
                break;
 
        case BC_YUV411P:
        case BC_YUV420P:
+       case BC_YUV420PI:
                bzero(get_y(), sz);
-               bzero(get_u(), sz / 4);
-               bzero(get_v(), sz / 4);
+               memset(get_u(), 0x80, sz / 4);
+               memset(get_v(), 0x80, sz / 4);
                break;
 
        case BC_YUV422P:
                bzero(get_y(), sz);
-               bzero(get_u(), sz / 2);
-               bzero(get_v(), sz / 2);
+               memset(get_u(), 0x80, sz / 2);
+               memset(get_v(), 0x80, sz / 2);
+               break;
+
+       case BC_GBRP:
+               bzero(get_y(), sz);
+               bzero(get_u(), sz);
+               bzero(get_b(), sz);
                break;
 
        case BC_RGBA_FLOATP: if( a ) {
                float *ap = (float *)a;
-               for( int i=sz; --i>=0; ++ap ) *ap = 1.f; }
+               for( int i=sz; --i>=0; ++ap ) *ap = 0.f; }
        case BC_RGB_FLOATP: {
                float *rp = (float *)y;
                for( int i=sz; --i>=0; ++rp ) *rp = 0.f;
@@ -875,8 +928,8 @@ int VFrame::clear_frame()
                break; }
        case BC_YUV444P:
                bzero(get_y(), sz);
-               bzero(get_u(), sz);
-               bzero(get_v(), sz);
+               memset(get_u(), 0x80, sz);
+               memset(get_v(), 0x80, sz);
                break;
 
        case BC_YUV888:
@@ -905,12 +958,9 @@ int VFrame::clear_frame()
 void VFrame::rotate90()
 {
 // Allocate new frame
-       int new_w = h, new_h = w, new_bytes_per_line = bytes_per_pixel * new_w;
-       unsigned char *new_data = new unsigned char[calculate_data_size(new_w, new_h, new_bytes_per_line, color_model)];
-       unsigned char **new_rows = new unsigned char*[new_h];
-       for(int i = 0; i < new_h; i++)
-               new_rows[i] = &new_data[new_bytes_per_line * i];
-
+       int new_w = h, new_h = w;
+       VFrame new_frame(new_w, new_h, color_model);
+       unsigned char **new_rows = new_frame.get_rows();
 // Copy data
        for(int in_y = 0, out_x = new_w - 1; in_y < h; in_y++, out_x--)
        {
@@ -925,23 +975,34 @@ void VFrame::rotate90()
        }
 
 // Swap frames
-       clear_objects(0);
+// swap memory
+       unsigned char *new_data = new_frame.data;
+       new_frame.data = data;
        data = new_data;
+// swap rows
+       new_rows = new_frame.rows;
+       new_frame.rows = rows;
        rows = new_rows;
-       bytes_per_line = new_bytes_per_line;
-       w = new_w;
-       h = new_h;
+// swap shmid
+       int new_shmid = new_frame.shmid;
+       new_frame.shmid = shmid;
+       shmid = new_shmid;
+// swap bytes_per_line
+       int new_bpl = new_frame.bytes_per_line;
+       new_frame.bytes_per_line = bytes_per_line;
+       bytes_per_line = new_bpl;
+       new_frame.clear_objects(0);
+
+       w = new_frame.w;
+       h = new_frame.h;
 }
 
 void VFrame::rotate270()
 {
 // Allocate new frame
-       int new_w = h, new_h = w, new_bytes_per_line = bytes_per_pixel * new_w;
-       unsigned char *new_data = new unsigned char[calculate_data_size(new_w, new_h, new_bytes_per_line, color_model)];
-       unsigned char **new_rows = new unsigned char*[new_h];
-       for(int i = 0; i < new_h; i++)
-               new_rows[i] = &new_data[new_bytes_per_line * i];
-
+       int new_w = h, new_h = w;
+       VFrame new_frame(new_w, new_h, color_model);
+       unsigned char **new_rows = new_frame.get_rows();
 // Copy data
        for(int in_y = 0, out_x = 0; in_y < h; in_y++, out_x++)
        {
@@ -956,24 +1017,36 @@ void VFrame::rotate270()
        }
 
 // Swap frames
-       clear_objects(0);
+// swap memory
+       unsigned char *new_data = new_frame.data;
+       new_frame.data = data;
        data = new_data;
+// swap rows
+       new_rows = new_frame.rows;
+       new_frame.rows = rows;
        rows = new_rows;
-       bytes_per_line = new_bytes_per_line;
-       w = new_w;
-       h = new_h;
+// swap shmid
+       int new_shmid = new_frame.shmid;
+       new_frame.shmid = shmid;
+       shmid = new_shmid;
+// swap bytes_per_line
+       int new_bpl = new_frame.bytes_per_line;
+       new_frame.bytes_per_line = bytes_per_line;
+       bytes_per_line = new_bpl;
+       new_frame.clear_objects(0);
+
+       w = new_frame.w;
+       h = new_frame.h;
 }
 
 void VFrame::flip_vert()
 {
-       unsigned char *temp = new unsigned char[bytes_per_line];
-       for(int i = 0, j = h - 1; i < j; i++, j--)
-       {
+       unsigned char temp[bytes_per_line];
+       for( int i=0, j=h; --j>i; ++i ) {
                memcpy(temp, rows[j], bytes_per_line);
                memcpy(rows[j], rows[i], bytes_per_line);
                memcpy(rows[i], temp, bytes_per_line);
        }
-       delete [] temp;
 }
 
 void VFrame::flip_horiz()
@@ -995,6 +1068,18 @@ void VFrame::flip_horiz()
 
 int VFrame::copy_from(VFrame *frame)
 {
+       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());
+               return 1;
+       }
+
        int w = MIN(this->w, frame->get_w());
        int h = MIN(this->h, frame->get_h());
        timestamp = frame->timestamp;
@@ -1014,6 +1099,7 @@ int VFrame::copy_from(VFrame *frame)
                        break;
 
                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);
@@ -1049,11 +1135,14 @@ int VFrame::copy_from(VFrame *frame)
 
 int VFrame::transfer_from(VFrame *that, int bg_color, int in_x, int in_y, int in_w, int in_h)
 {
+       timestamp = that->timestamp;
+       copy_params(that);
+
        if( this->get_color_model() == that->get_color_model() &&
-           this->get_w() == that->get_w() && this->get_h() == that->get_h() )
+           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);
 
-       timestamp = that->timestamp;
 #if 0
        BC_CModels::transfer(
                this->get_rows(), that->get_rows(),          // Packed data out/in
@@ -1063,7 +1152,8 @@ int VFrame::transfer_from(VFrame *that, int bg_color, int in_x, int in_y, int in
                0, 0, this->get_w(), this->get_h(),
                that->get_color_model(), this->get_color_model(), // Color models in/out
                bg_color,                                    // alpha blend bg_color
-               that->get_w(), this->get_w());               // rowspans (of luma for YUV)
+               that->get_bytes_per_line(),
+               this->get_bytes_per_line());                 // rowspans (of luma for YUV)
 #else
        unsigned char *in_ptrs[4], *out_ptrs[4];
        unsigned char **inp, **outp;
@@ -1075,7 +1165,7 @@ int VFrame::transfer_from(VFrame *that, int bg_color, int in_x, int in_y, int in
                inp = in_ptrs;
        }
        else
-               inp = that->get_rows(); 
+               inp = that->get_rows();
        if( BC_CModels::is_planar(this->get_color_model()) ) {
                out_ptrs[0] = this->get_y();
                out_ptrs[1] = this->get_u();
@@ -1084,69 +1174,19 @@ int VFrame::transfer_from(VFrame *that, int bg_color, int in_x, int in_y, int in
                outp = out_ptrs;
        }
        else
-               outp = this->get_rows();        
+               outp = this->get_rows();
        BC_CModels::transfer(outp, this->get_color_model(),
-                       0, 0, this->get_w(), this->get_h(), this->get_w(),
+                       0, 0, this->get_w(), this->get_h(),
+                       this->get_bytes_per_line(),
                inp, that->get_color_model(),
-                       in_x, in_y, in_w, in_h, that->get_w(),
+                       in_x, in_y, in_w, in_h,
+                       that->get_bytes_per_line(),
                bg_color);
 #endif
        return 0;
 }
 
 
-
-
-
-
-#define OVERLAY(type, max, components) \
-{ \
-       type **in_rows = (type**)src->get_rows(); \
-       type **out_rows = (type**)get_rows(); \
-       int in_w = src->get_w(); \
-       int in_h = src->get_h(); \
- \
-       for(int i = 0; i < in_h; i++) \
-       { \
-               if(i + out_y1 >= 0 && i + out_y1 < h) \
-               { \
-                       type *src_row = in_rows[i]; \
-                       type *dst_row = out_rows[i + out_y1] + out_x1 * components; \
- \
-                       for(int j = 0; j < in_w; j++) \
-                       { \
-                               if(j + out_x1 >= 0 && j + out_x1 < w) \
-                               { \
-                                       int opacity = src_row[3]; \
-                                       int transparency = dst_row[3] * (max - src_row[3]) / max; \
-                                       dst_row[0] = (transparency * dst_row[0] + opacity * src_row[0]) / max; \
-                                       dst_row[1] = (transparency * dst_row[1] + opacity * src_row[1]) / max; \
-                                       dst_row[2] = (transparency * dst_row[2] + opacity * src_row[2]) / max; \
-                                       dst_row[3] = MAX(dst_row[3], src_row[3]); \
-                               } \
- \
-                               dst_row += components; \
-                               src_row += components; \
-                       } \
-               } \
-       } \
-}
-
-
-void VFrame::overlay(VFrame *src,
-               int out_x1,
-               int out_y1)
-{
-       switch(get_color_model())
-       {
-               case BC_RGBA8888:
-                       OVERLAY(unsigned char, 0xff, 4);
-                       break;
-       }
-}
-
-
-
 int VFrame::get_scale_tables(int *column_table, int *row_table,
                        int in_x1, int in_y1, int in_x2, int in_y2,
                        int out_x1, int out_y1, int out_x2, int out_y2)
@@ -1241,22 +1281,20 @@ void VFrame::copy_stacks(VFrame *src)
 {
        clear_stacks();
 
-       for(int i = 0; i < src->next_effects.total; i++)
-       {
-               char *ptr;
-               next_effects.append(ptr = new char[strlen(src->next_effects.values[i]) + 1]);
-               strcpy(ptr, src->next_effects.values[i]);
-       }
-       for(int i = 0; i < src->prev_effects.total; i++)
-       {
-               char *ptr;
-               prev_effects.append(ptr = new char[strlen(src->prev_effects.values[i]) + 1]);
-               strcpy(ptr, src->prev_effects.values[i]);
-       }
+       for( int i=0; i < src->next_effects.total; ++i )
+               next_effects.append(cstrdup(src->next_effects[i]));
+       for( int i=0; i < src->prev_effects.total; ++i )
+               prev_effects.append(cstrdup(src->prev_effects[i]));
 
        copy_params(src);
 }
 
+int VFrame::copy_vframe(VFrame *frame)
+{
+       copy_stacks(frame);
+       return copy_from(frame);
+}
+
 int VFrame::equal_stacks(VFrame *src)
 {
        for(int i = 0; i < src->next_effects.total && i < next_effects.total; i++)
@@ -1303,119 +1341,353 @@ int VFrame::get_memory_usage()
        return get_h() * get_bytes_per_line();
 }
 
-void VFrame::draw_pixel(int x, int y)
+void VFrame::set_pixel_color(int rgb)
 {
-       if(!(x >= 0 && y >= 0 && x < get_w() && y < get_h())) return;
+       pixel_rgb = rgb;
+       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 =  (ir<<16) | (ig<<8) | (ib<<0);
+}
 
-#define DRAW_PIXEL(x, y, components, do_yuv, max, type) \
-{ \
-       type **rows = (type**)get_rows(); \
-       rows[y][x * components] = max - rows[y][x * components]; \
-       if(!do_yuv) \
-       { \
-               rows[y][x * components + 1] = max - rows[y][x * components + 1]; \
-               rows[y][x * components + 2] = max - rows[y][x * components + 2]; \
-       } \
-       else \
-       { \
-               rows[y][x * components + 1] = (max / 2 + 1) - rows[y][x * components + 1]; \
-               rows[y][x * components + 2] = (max / 2 + 1) - rows[y][x * components + 2]; \
-       } \
-       if(components == 4) \
-               rows[y][x * components + 3] = max; \
+void VFrame::set_stiple(int mask)
+{
+       stipple = mask;
 }
 
+int VFrame::draw_pixel(int x, int y)
+{
+       if( x < 0 || y < 0 || x >= get_w() || y >= get_h() ) return 1;
 
-       switch(get_color_model())
-       {
-               case BC_RGB888:
-                       DRAW_PIXEL(x, y, 3, 0, 0xff, unsigned char);
-                       break;
-               case BC_RGBA8888:
-                       DRAW_PIXEL(x, y, 4, 0, 0xff, unsigned char);
-                       break;
-               case BC_RGB_FLOAT:
-                       DRAW_PIXEL(x, y, 3, 0, 1.0, float);
-                       break;
-               case BC_RGBA_FLOAT:
-                       DRAW_PIXEL(x, y, 4, 0, 1.0, float);
-                       break;
-               case BC_YUV888:
-                       DRAW_PIXEL(x, y, 3, 1, 0xff, unsigned char);
-                       break;
-               case BC_YUVA8888:
-                       DRAW_PIXEL(x, y, 4, 1, 0xff, unsigned char);
-                       break;
-               case BC_RGB161616:
-                       DRAW_PIXEL(x, y, 3, 0, 0xffff, uint16_t);
-                       break;
-               case BC_YUV161616:
-                       DRAW_PIXEL(x, y, 3, 1, 0xffff, uint16_t);
-                       break;
-               case BC_RGBA16161616:
-                       DRAW_PIXEL(x, y, 4, 0, 0xffff, uint16_t);
-                       break;
-               case BC_YUVA16161616:
-                       DRAW_PIXEL(x, y, 4, 1, 0xffff, uint16_t);
-                       break;
+#define DRAW_PIXEL(type, r, g, b) { \
+       type **rows = (type**)get_rows(); \
+       rows[y][x * components + 0] = r; \
+       rows[y][x * components + 1] = g; \
+       rows[y][x * components + 2] = b; \
+       if( components == 4 ) \
+               rows[y][x * components + 3] = mx; \
+}
+       int components = BC_CModels::components(color_model);
+       int bch = BC_CModels::calculate_pixelsize(color_model) / components;
+       int sz = 8*bch, mx = BC_CModels::is_float(color_model) ? 1 : (1<<sz)-1;
+       int is_yuv = BC_CModels::is_yuv(color_model);
+       int pixel_color = is_yuv ? pixel_yuv : pixel_rgb;
+       int ir = 0xff & (pixel_color >> 16);  float fr = 0;
+       int ig = 0xff & (pixel_color >> 8);   float fg = 0;
+       int ib = 0xff & (pixel_color >> 0);   float fb = 0;
+       if( (x+y) & stipple ) {
+               ir = 255 - ir;  ig = 255 - ig;  ib = 255 - ib;
        }
+       if( BC_CModels::is_float(color_model) ) {
+               fr = ir / 255.;  fg = ig / 255.;  fb = ib / 255.;
+               mx = 1;
+       }
+       else if( (sz-=8) > 0 ) {
+               ir <<= sz;  ig <<= sz;  ib <<= sz;
+       }
+
+       switch(get_color_model()) {
+       case BC_RGB888:
+       case BC_YUV888:
+       case BC_RGBA8888:
+       case BC_YUVA8888:
+               DRAW_PIXEL(uint8_t, ir, ig, ib);
+               break;
+       case BC_RGB161616:
+       case BC_YUV161616:
+       case BC_RGBA16161616:
+       case BC_YUVA16161616:
+               DRAW_PIXEL(uint16_t, ir, ig, ib);
+               break;
+       case BC_RGB_FLOAT:
+       case BC_RGBA_FLOAT:
+               DRAW_PIXEL(float, fr, fg, fb);
+               break;
+       }
+       return 0;
 }
 
 
+// Bresenham's
 void VFrame::draw_line(int x1, int y1, int x2, int y2)
 {
-       int w = labs(x2 - x1);
-       int h = labs(y2 - y1);
-//printf("FindObjectMain::draw_line 1 %d %d %d %d\n", x1, y1, x2, y2);
+       if( y1 > y2 ) {
+               int tx = x1;  x1 = x2;  x2 = tx;
+               int ty = y1;  y1 = y2;  y2 = ty;
+       }
 
-       if(!w && !h)
-       {
-               draw_pixel(x1, y1);
+       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 );
        }
-       else
-       if(w > h)
-       {
-// Flip coordinates so x1 < x2
-               if(x2 < x1)
-               {
-                       y2 ^= y1;
-                       y1 ^= y2;
-                       y2 ^= y1;
-                       x1 ^= x2;
-                       x2 ^= x1;
-                       x1 ^= x2;
+       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 );
+       }
+}
+
+// g++ -dD -E - < /dev/null | grep DBL_EPSILON
+#ifndef __DBL_EPSILON__
+#define __DBL_EPSILON__ ((double)2.22044604925031308085e-16L)
+#endif
+// weakest fraction * graphics integer range
+#define RND_EPSILON (__DBL_EPSILON__*65536)
+
+class smooth_line {
+       int rnd(double v) { return round(v)+RND_EPSILON; }
+       VFrame *vframe;
+public:
+       int sx, sy, ex, ey;     /* current point, end point */
+       int xs, ys;             /* x/y quadrant sign -1/1 */
+       int64_t A, B, C;        /* quadratic coefficients */
+       int64_t r, dx, dy;      /* residual, dr/dx and dr/dy */
+       int xmxx, xmxy;         /* x,y at apex */
+       int done;
+
+       void init0(int x1,int y1, int x2,int y2, int x3,int y3, int top);
+       void init1(int x1,int y1, int x2,int y2, int x3,int y3);
+       int64_t rx() { return r + xs*8*dx + 4*A; }
+       void moveX(int64_t r) {
+               dx += xs*A;   dy -= xs*B;
+               this->r = r;  sx += xs;
+       }
+       int64_t ry() { return r + 8*dy + 4*C; }
+       void moveY(int64_t r) {
+               dx -= B;      dy += C;
+               this->r = r;  ++sy;
+       }
+       void draw();
+
+       smooth_line(VFrame *vframe) { this->vframe = vframe; this->done = 0; }
+};
+
+
+void smooth_line::draw()
+{
+       if( done ) return;
+       if( abs(dy) >= abs(dx) ) {
+               if( xs*(sx-xmxx) >= 0 ) {
+                       if( ys > 0 ) { done = 1;  return; }
+                       if( dy < 0 || ry() < 0 ) { moveY(ry()); goto xit; }
+                       xmxx = ex;  xmxy = ey;
+                       ys = 1;  xs = -xs;
                }
-               int numerator = y2 - y1;
-               int denominator = x2 - x1;
-               for(int i = x1; i <= x2; i++)
-               {
-                       int y = y1 + (int64_t)(i - x1) * (int64_t)numerator / (int64_t)denominator;
-                       draw_pixel(i, y);
+               moveX(rx());
+               int64_t rr = ry();
+               if( abs(rr) < abs(r) )
+                       moveY(rr);
+       }
+       else {
+               if( sy >= xmxy ) {
+                       if( ys > 0 ) { done = 1;  return; }
+                       xmxx = ex;  xmxy = ey;
+                       ys = 1;  xs = -xs;
                }
+               moveY(ry());
+               int64_t rr = rx();
+               if( abs(rr) < abs(r) )
+                       moveX(rr);
+       }
+xit:   vframe->draw_pixel(sx, sy);
+}
+
+void VFrame::draw_smooth(int x1, int y1, int x2, int y2, int x3, int y3)
+{
+       if( (x1 == x2 && y1 == y2) || (x2 == x3 && y2 == y3) )
+               draw_line(x1,y1, x3,y3);
+       else if( x1 == x3 && y1 == y3 )
+               draw_line(x1,y1, x2,y2);
+       else if( (x2-x1) * (y2-y3) == (x2-x3) * (y2-y1) ) {
+               // co-linear, draw line from min to max
+               if( x1 < x3 ) {
+                       if( x2 < x1 ) { x1 = x2;  y1 = y2; }
+                       if( x2 > x3 ) { x3 = x2;  y3 = y2; }
+               }
+               else {
+                       if( x2 > x1 ) { x1 = x2;  y1 = y2; }
+                       if( x2 < x3 ) { x3 = x2;  y3 = y2; }
+               }
+               draw_line(x1,y1, x3,y3);
        }
        else
-       {
-// Flip coordinates so y1 < y2
-               if(y2 < y1)
-               {
-                       y2 ^= y1;
-                       y1 ^= y2;
-                       y2 ^= y1;
-                       x1 ^= x2;
-                       x2 ^= x1;
-                       x1 ^= x2;
+               smooth_draw(x1, y1, x2, y2, x3, y3);
+}
+
+/*
+  Non-Parametric Smooth Curve Generation. Don Kelly 1984
+
+     P+-----+Q'= virtual
+     /     /       origin
+    /     /
+  Q+-----+R
+
+   Let the starting point be P. the ending point R. and the tangent vertex Q.
+   A general point Z on the curve is then
+        Z = (P + R - Q) + (Q - P) sin t + (Q - R) cos t
+
+   Expanding the Cartesian coordinates around (P + R - Q) gives
+        [x y] = Z - (P + R - Q)
+        [a c] = Q - P
+        [b d] = Q - R
+        x = a*sin(t) + b*cos(t)
+        y = c*sin(t) + d*cos(t)
+
+   from which t can now be eliminated via
+        c*x - a*y = (c*b - a*d)*cos(t)
+        d*x - b*y = (a*d - c*b)*sin(t)
+
+   giving the Cartesian equation for the ellipse as
+        f(x, y) = (c*x - a*y)**2 + (d*x - b*y)**2 - (a*d - c*b)**2 = 0
+
+   or:  f(x, y) = A*x**2 - 2*B*x*y + C*y**2 + B**2 - A*C = 0
+   where: A = c**2 + d**2,  B = a*c + b*d,  C = a**2 + b**2
+
+   The maximum y extent of the ellipse may now be derived as follows:
+        let df/dx = 0,  2*A*x = 2*B*y,  x = y*B/A
+        f(x, y) == B**2 * y**2 / A - 2*B**2 * y**2 / A + C*y**2 + B**2 - A*C = 0
+           (A*C - B**2)*y = (A*C - B**2)*A
+           max x = sqrt(C), at y = B/sqrt(C)
+           max y = sqrt(A), at x = B/sqrt(A)
+
+ */
+
+
+/* x1,y1 = P, x2,y2 = Q, x3,y3=R,
+ *  draw from P to Q to R   if top=0
+ *    or from P to (x,ymax) if top>0
+ *    or from Q to (x,ymax) if top<0
+ */
+void smooth_line::init0(int x1,int y1, int x2,int y2, int x3,int y3, int top)
+{
+       int x0 = x1+x3-x2, y0 = y1+y3-y2; // Q'
+
+       int a = x2-x1,  c = y2-y1;
+       int b = x2-x3,  d = y2-y3;
+       A = c*c + d*d;  C = a*a + b*b;  B = a*c + b*d;
+
+       sx = top >= 0 ? x1 : x3;
+       sy = top >= 0 ? y1 : y3;
+       xs = x2 > sx || (x2==sx && (x1+x3-sx)>=x2) ? 1 : -1;
+       int64_t px = sx-x0, py = sy-y0;
+       dx = A*px - B*py;  dy = C*py - B*px;
+       r = 0;
+
+       if( top ) {
+               double ymy = sqrt(A), ymx = B/ymy;
+               ex = x0 + rnd(ymx);
+               ey = y0 + rnd(ymy);
+       }
+       else {
+               ex = x3;  ey = y3;
+       }
+
+       ys = a*b > 0 && (!top || top*xs*(b*c - a*d) > 0) ? -1 : 1;
+       if( ys < 0 ) {
+               double xmx = xs*sqrt(C), xmy = B/xmx;
+               xmxx = x0 + rnd(xmx);
+               xmxy = y0 + rnd(xmy);
+       }
+       else {
+               xmxx = ex; xmxy = ey;
+       }
+}
+
+/*  x1,y1 = P, x2,y2 = Q, x3,y3=R,
+ *  draw from (x,ymax) to P
+ */
+void smooth_line::init1(int x1,int y1, int x2,int y2, int x3,int y3)
+{
+       int x0 = x1+x3-x2, y0 = y1+y3-y2; // Q'
+
+       int a = x2-x1,  c = y2-y1;
+       int b = x2-x3,  d = y2-y3;
+       A = c*c + d*d;  C = a*a + b*b;  B = a*c + b*d;
+
+       double ymy = -sqrt(A), ymx = B/ymy;
+       int64_t px = rnd(ymx), py = rnd(ymy);
+       sx = x0 + px;  ex = x1;
+       sy = y0 + py;  ey = y1;
+       xs = x2 > x1 || (x2==x1 && x3>=x2) ? 1 : -1;
+       dx = A*px - B*py;  dy = C*py - B*px;
+       r = 4 * (A*px*px - 2*B*px*py + C*py*py + B*B - A*C);
+
+       ys = a*b > 0 && xs*(b*c - a*d) < 0 ? -1 : 1;
+       if( ys < 0 ) {
+               double xmx = xs*sqrt(C), xmy = B/xmx;
+               xmxx = x0 + rnd(xmx);
+               xmxy = y0 + rnd(xmy);
+       }
+       else {
+               xs = -xs;
+               xmxx = ex; xmxy = ey;
+       }
+       if( xs > 0 )
+               vframe->draw_pixel(sx, sy);
+       while( xs*(sx-xmxx) < 0 && (xs*dx < 0 || rx() < 0) ) {
+               moveX(rx());
+               vframe->draw_pixel(sx, sy);
+       }
+}
+
+
+void VFrame::smooth_draw(int x1, int y1, int x2, int y2, int x3, int y3)
+{
+//printf("p smooth_draw( %d,%d, %d,%d, %d,%d )\n", x1,y1,x2,y2,x3,y3);
+       if( y1 > y3 ) {         // make y3 >= y1
+               int xt = x1;  x1 = x3;  x3 = xt;
+               int yt = y1;  y1 = y3;  y3 = yt;
+       }
+       if( y1 > y2 && y3 > y2 ) {
+               smooth_line lt(this), rt(this); // Q on bottom
+               lt.init1(x1, y1, x2, y2, x3, y3);
+               rt.init1(x3, y3, x2, y2, x1, y1);
+               while( !lt.done || !rt.done ) {
+                       lt.draw();
+                       rt.draw();
                }
-               int numerator = x2 - x1;
-               int denominator = y2 - y1;
-               for(int i = y1; i <= y2; i++)
-               {
-                       int x = x1 + (int64_t)(i - y1) * (int64_t)numerator / (int64_t)denominator;
-                       draw_pixel(x, i);
+       }
+       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);
+               rt.init0(x1, y1, x2, y2, x3, y3, -1);
+               draw_pixel(rt.sx, rt.sy);
+               while( !lt.done || !rt.done ) {
+                       lt.draw();
+                       rt.draw();
+               }
+       }
+       else {
+               smooth_line pt(this);           // Q in between
+               pt.init0(x1, y1, x2, y2, x3, y3, 0);
+               draw_pixel(pt.sx, pt.sy);
+               while( !pt.done ) {
+                       pt.draw();
                }
        }
-//printf("FindObjectMain::draw_line 2\n");
 }
 
+
 void VFrame::draw_rect(int x1, int y1, int x2, int y2)
 {
        draw_line(x1, y1, x2, y1);
@@ -1424,30 +1696,62 @@ void VFrame::draw_rect(int x1, int y1, int x2, int y2)
        draw_line(x1, y2 - 1, x1, y1 + 1);
 }
 
-#define ARROW_SIZE 10
-void VFrame::draw_arrow(int x1, int y1, int x2, int y2)
+
+void VFrame::draw_oval(int x1, int y1, int x2, int y2)
 {
-       double angle = atan((float)(y2 - y1) / (float)(x2 - x1));
-       double angle1 = angle + (float)145 / 360 * 2 * 3.14159265;
-       double angle2 = angle - (float)145 / 360 * 2 * 3.14159265;
-       int x3;
-       int y3;
-       int x4;
-       int y4;
-       if(x2 < x1)
-       {
-               x3 = x2 - (int)(ARROW_SIZE * cos(angle1));
-               y3 = y2 - (int)(ARROW_SIZE * sin(angle1));
-               x4 = x2 - (int)(ARROW_SIZE * cos(angle2));
-               y4 = y2 - (int)(ARROW_SIZE * sin(angle2));
+       int w = x2 - x1;
+       int h = y2 - y1;
+       int center_x = (x2 + x1) / 2;
+       int center_y = (y2 + y1) / 2;
+       int x_table[h / 2];
+
+//printf("VFrame::draw_oval %d %d %d %d %d\n", __LINE__, x1, y1, x2, y2);
+
+       for(int i = 0; i < h / 2; i++) {
+// A^2 = -(B^2) + C^2
+               x_table[i] = (int)(sqrt(-SQR(h / 2 - i) + SQR(h / 2)) * w / h);
+//printf("VFrame::draw_oval %d i=%d x=%d\n", __LINE__, i, x_table[i]);
        }
-       else
-       {
-               x3 = x2 + (int)(ARROW_SIZE * cos(angle1));
-               y3 = y2 + (int)(ARROW_SIZE * sin(angle1));
-               x4 = x2 + (int)(ARROW_SIZE * cos(angle2));
-               y4 = y2 + (int)(ARROW_SIZE * sin(angle2));
+
+       for(int i = 0; i < h / 2 - 1; i++) {
+               int x3 = x_table[i];
+               int x4 = x_table[i + 1];
+
+               if(x4 > x3 + 1) {
+                       for(int j = x3; j < x4; j++) {
+                               draw_pixel(center_x + j, y1 + i);
+                               draw_pixel(center_x - j, y1 + i);
+                               draw_pixel(center_x + j, y2 - i - 1);
+                               draw_pixel(center_x - j, y2 - i - 1);
+                       }
+               }
+               else {
+                       draw_pixel(center_x + x3, y1 + i);
+                       draw_pixel(center_x - x3, y1 + i);
+                       draw_pixel(center_x + x3, y2 - i - 1);
+                       draw_pixel(center_x - x3, y2 - i - 1);
+               }
        }
+       
+       draw_pixel(center_x, y1);
+       draw_pixel(center_x, y2 - 1);
+       draw_pixel(x1, center_y);
+       draw_pixel(x2 - 1, center_y);
+       draw_pixel(x1, center_y - 1);
+       draw_pixel(x2 - 1, center_y - 1);
+}
+
+
+void VFrame::draw_arrow(int x1, int y1, int x2, int y2, int sz)
+{
+       double angle = atan((float)(y2 - y1) / (float)(x2 - x1));
+       double angle1 = angle + (float)145 / 360 * 2 * M_PI;
+       double angle2 = angle - (float)145 / 360 * 2 * M_PI;
+       int s = x2 < x1 ? -1 : 1;
+       int x3 = x2 + s * (int)(sz * cos(angle1));
+       int y3 = y2 + s * (int)(sz * sin(angle1));
+       int x4 = x2 + s * (int)(sz * cos(angle2));
+       int y4 = y2 + s * (int)(sz * sin(angle2));
 
 // Main vector
        draw_line(x1, y1, x2, y2);
@@ -1459,6 +1763,15 @@ void VFrame::draw_arrow(int x1, int y1, int x2, int y2)
        if(abs(y2 - y1) || abs(x2 - x1)) draw_line(x2, y2, x4, y4);
 }
 
-
+void VFrame::draw_x(int x, int y, int sz)
+{
+       draw_line(x-sz,y-sz, x+sz,y+sz);
+       draw_line(x+sz,y-sz, x-sz,y+sz);
+}
+void VFrame::draw_t(int x, int y, int sz)
+{
+       draw_line(x,y-sz, x,y+sz);
+       draw_line(x+sz,y, x-sz,y);
+}