d05ff849307050228459fa93e2767774286b14f9
[goodguy/history.git] / cinelerra-5.1 / guicast / vframe.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2011 Adam Williams <broadcast at earthling dot net>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  */
21
22 #include <errno.h>
23 #include <png.h>
24 #include <stdio.h>
25 #include <string.h>
26 #include <stdint.h>
27 #include <fcntl.h>
28 #include <sys/shm.h>
29 #include <sys/mman.h>
30
31 #include "bcbitmap.h"
32 #include "bchash.h"
33 #include "bcpbuffer.h"
34 #include "bcresources.h"
35 #include "bcsignals.h"
36 #include "bcsynchronous.h"
37 #include "bctexture.h"
38 #include "bcwindowbase.h"
39 #include "clip.h"
40 #include "bccmodels.h"
41 #include "vframe.h"
42
43 class PngReadFunction
44 {
45 public:
46         static void png_read_function(png_structp png_ptr,
47                    png_bytep data, png_size_t length)
48         {
49                 VFrame *frame = (VFrame*)png_get_io_ptr(png_ptr);
50                 if(frame->image_size - frame->image_offset < (long)length)
51                 {
52                         printf("PngReadFunction::png_read_function %d: overrun\n", __LINE__);
53                         length = frame->image_size - frame->image_offset;
54                 }
55
56                 memcpy(data, &frame->image[frame->image_offset], length);
57                 frame->image_offset += length;
58         };
59 };
60
61
62
63
64
65
66
67 VFrameScene::VFrameScene()
68 {
69 }
70
71 VFrameScene::~VFrameScene()
72 {
73 }
74
75
76
77
78
79
80
81 //static BCCounter counter;
82
83 VFramePng::VFramePng(unsigned char *png_data, double s)
84 {
85         long image_size =
86                 ((long)png_data[0] << 24) | ((long)png_data[1] << 16) |
87                 ((long)png_data[2] << 8)  |  (long)png_data[3];
88         if( !s ) s = BC_WindowBase::get_resources()->icon_scale;
89         read_png(png_data+4, image_size, s, s);
90 }
91
92 VFramePng::VFramePng(unsigned char *png_data, long image_size, double xs, double ys)
93 {
94         if( !xs ) xs = BC_WindowBase::get_resources()->icon_scale;
95         if( !ys ) ys = BC_WindowBase::get_resources()->icon_scale;
96         read_png(png_data, image_size, xs, ys);
97 }
98
99 VFramePng::~VFramePng()
100 {
101 }
102
103 VFrame *VFramePng::vframe_png(int fd, double xs, double ys)
104 {
105         struct stat st;
106         if( fstat(fd, &st) ) return 0;
107         long len = st.st_size;
108         if( !len ) return 0;
109         int w = 0, h = 0;
110         unsigned char *bfr = (unsigned char *)
111                 ::mmap (NULL, len, PROT_READ, MAP_SHARED, fd, 0);
112         if( bfr == MAP_FAILED ) return 0;
113         VFrame *vframe = new VFramePng(bfr, len, xs, ys);
114         if( (w=vframe->get_w()) <= 0 || (h=vframe->get_h()) <= 0 ||
115             vframe->get_data() == 0 ) { delete vframe;  vframe = 0; }
116         ::munmap(bfr, len);
117         return vframe;
118 }
119 VFrame *VFramePng::vframe_png(const char *png_path, double xs, double ys)
120 {
121         VFrame *vframe = 0;
122         int fd = ::open(png_path, O_RDONLY);
123         if( fd >= 0 ) {
124                 vframe = vframe_png(fd, xs, ys);
125                 ::close(fd);
126         }
127         return vframe;
128 }
129
130 VFrame::VFrame(VFrame &frame)
131 {
132         reset_parameters(1);
133         params = new BC_Hash;
134         allocate_data(0, -1, 0, 0, 0, frame.w, frame.h,
135                 frame.color_model, frame.bytes_per_line);
136         copy_from(&frame);
137 }
138
139 VFrame::VFrame(int w, int h, int color_model, long bytes_per_line)
140 {
141         reset_parameters(1);
142         params = new BC_Hash;
143         allocate_data(data, -1, 0, 0, 0, w, h,
144                 color_model, bytes_per_line);
145 }
146
147 VFrame::VFrame(unsigned char *data, int shmid, int w, int h,
148         int color_model, long bytes_per_line)
149 {
150         reset_parameters(1);
151         params = new BC_Hash;
152         allocate_data(data, shmid, 0, 0, 0, w, h,
153                 color_model, bytes_per_line);
154 }
155
156 VFrame::VFrame(unsigned char *data, int shmid,
157                 long y_offset, long u_offset, long v_offset,
158                 int w, int h, int color_model, long bytes_per_line)
159 {
160         reset_parameters(1);
161         params = new BC_Hash;
162         allocate_data(data, shmid, y_offset, u_offset, v_offset, w, h,
163                 color_model, bytes_per_line);
164 }
165
166 VFrame::VFrame(BC_Bitmap *bitmap, int w, int h,
167                  int color_model, long bytes_per_line)
168 {
169         reset_parameters(1);
170         params = new BC_Hash;
171         int shmid = 0;
172         unsigned char *data = 0;
173         if( bitmap->is_shared() )
174                 shmid = bitmap->get_shmid();
175         else
176                 data = bitmap->get_data();
177         allocate_data(data, shmid,
178                 bitmap->get_y_offset(),
179                 bitmap->get_u_offset(),
180                 bitmap->get_v_offset(),
181                 w, h, color_model, bytes_per_line);
182 }
183
184 VFrame::VFrame()
185 {
186         reset_parameters(1);
187         params = new BC_Hash;
188         this->color_model = BC_COMPRESSED;
189 }
190
191
192
193 VFrame::~VFrame()
194 {
195         clear_objects(1);
196 // Delete effect stack
197         prev_effects.remove_all_objects();
198         next_effects.remove_all_objects();
199         delete params;
200         delete scene;
201 }
202
203 int VFrame::equivalent(VFrame *src, int test_stacks)
204 {
205         return (src->get_color_model() == get_color_model() &&
206                 src->get_w() == get_w() &&
207                 src->get_h() == get_h() &&
208                 src->bytes_per_line == bytes_per_line &&
209                 (!test_stacks || equal_stacks(src)));
210 }
211
212 int VFrame::data_matches(VFrame *frame)
213 {
214         if(data && frame->get_data() &&
215                 frame->params_match(get_w(), get_h(), get_color_model()) &&
216                 get_data_size() == frame->get_data_size())
217         {
218                 int data_size = get_data_size();
219                 unsigned char *ptr1 = get_data();
220                 unsigned char *ptr2 = frame->get_data();
221                 for(int i = 0; i < data_size; i++)
222                 {
223                         if(*ptr1++ != *ptr2++) return 0;
224                 }
225                 return 1;
226         }
227         return 0;
228 }
229
230 // long VFrame::set_shm_offset(long offset)
231 // {
232 //      shm_offset = offset;
233 //      return 0;
234 // }
235 //
236 // long VFrame::get_shm_offset()
237 // {
238 //      return shm_offset;
239 // }
240 //
241 int VFrame::get_memory_type()
242 {
243         return memory_type;
244 }
245
246 int VFrame::params_match(int w, int h, int color_model)
247 {
248         return (this->w == w &&
249                 this->h == h &&
250                 this->color_model == color_model);
251 }
252
253
254 int VFrame::reset_parameters(int do_opengl)
255 {
256         status = 1;
257         scene = 0;
258         field2_offset = -1;
259         memory_type = VFrame::PRIVATE;
260 //      shm_offset = 0;
261         shmid = -1;
262         use_shm = 1;
263         bytes_per_line = 0;
264         data = 0;
265         rows = 0;
266         color_model = 0;
267         compressed_allocated = 0;
268         compressed_size = 0;   // Size of current image
269         w = 0;
270         h = 0;
271         y = u = v = a = 0;
272         y_offset = 0;
273         u_offset = 0;
274         v_offset = 0;
275         sequence_number = -1;
276         timestamp = -1.;
277         is_keyframe = 0;
278         draw_point = 0;
279         set_pixel_color(BLACK);
280         stipple = 0;
281
282         if(do_opengl)
283         {
284 // By default, anything is going to be done in RAM
285                 opengl_state = VFrame::RAM;
286                 pbuffer = 0;
287                 texture = 0;
288         }
289
290         prev_effects.set_array_delete();
291         next_effects.set_array_delete();
292         return 0;
293 }
294
295 int VFrame::clear_objects(int do_opengl)
296 {
297 // Remove texture
298         if(do_opengl)
299         {
300                 delete texture;
301                 texture = 0;
302
303                 delete pbuffer;
304                 pbuffer = 0;
305         }
306
307 #ifdef LEAKER
308 if( memory_type != VFrame::SHARED )
309   printf("==del %p from %p\n", data, __builtin_return_address(0));
310 #endif
311
312 // Delete data
313         switch(memory_type)
314         {
315                 case VFrame::PRIVATE:
316 // Memory check
317 // if(this->w * this->h > 1500 * 1100)
318 // printf("VFrame::clear_objects 2 this=%p data=%p\n", this, data);
319                         if(data)
320                         {
321 //printf("VFrame::clear_objects %d this=%p shmid=%p data=%p\n", __LINE__, this, shmid, data);
322                                 if(shmid >= 0)
323                                         shmdt(data);
324                                 else
325                                         free(data);
326 //PRINT_TRACE
327                         }
328
329                         data = 0;
330                         shmid = -1;
331                         break;
332
333                 case VFrame::SHMGET:
334                         if(data)
335                                 shmdt(data);
336                         data = 0;
337                         shmid = -1;
338                         break;
339         }
340
341 // Delete row pointers
342         switch(color_model)
343         {
344                 case BC_COMPRESSED:
345                 case BC_YUV410P:
346                 case BC_YUV411P:
347                 case BC_YUV420P:
348                 case BC_YUV420PI:
349                 case BC_YUV422P:
350                 case BC_YUV444P:
351                 case BC_RGB_FLOATP:
352                 case BC_RGBA_FLOATP:
353                         break;
354
355                 default:
356                         delete [] rows;
357                         rows = 0;
358                         break;
359         }
360
361
362         return 0;
363 }
364
365 int VFrame::get_field2_offset()
366 {
367         return field2_offset;
368 }
369
370 int VFrame::set_field2_offset(int value)
371 {
372         this->field2_offset = value;
373         return 0;
374 }
375
376 void VFrame::set_keyframe(int value)
377 {
378         this->is_keyframe = value;
379 }
380
381 int VFrame::get_keyframe()
382 {
383         return is_keyframe;
384 }
385
386
387 VFrameScene* VFrame::get_scene()
388 {
389         return scene;
390 }
391
392 int VFrame::calculate_bytes_per_pixel(int color_model)
393 {
394         return BC_CModels::calculate_pixelsize(color_model);
395 }
396
397 long VFrame::get_bytes_per_line()
398 {
399         return bytes_per_line;
400 }
401
402 long VFrame::get_data_size()
403 {
404         return calculate_data_size(w, h, bytes_per_line, color_model) - 4;
405 }
406
407 long VFrame::calculate_data_size(int w, int h, int bytes_per_line, int color_model)
408 {
409         return BC_CModels::calculate_datasize(w, h, bytes_per_line, color_model);
410 }
411
412 void VFrame::create_row_pointers()
413 {
414         int sz = w * h;
415         switch(color_model) {
416         case BC_YUV410P:
417                 if( this->v_offset ) break;
418                 this->y_offset = 0;
419                 this->u_offset = sz;
420                 this->v_offset = sz + w / 4 * h / 4;
421                 break;
422
423         case BC_YUV420P:
424         case BC_YUV420PI:
425         case BC_YUV411P:
426                 if( this->v_offset ) break;
427                 this->y_offset = 0;
428                 this->u_offset = sz;
429                 this->v_offset = sz + sz / 4;
430                 break;
431
432         case BC_YUV422P:
433                 if( this->v_offset ) break;
434                 this->y_offset = 0;
435                 this->u_offset = sz;
436                 this->v_offset = sz + sz / 2;
437                 break;
438         case BC_YUV444P:
439                 if( this->v_offset ) break;
440                 this->y_offset = 0;
441                 this->u_offset = sz;
442                 this->v_offset = sz + sz;
443                 break;
444         case BC_RGBA_FLOATP:
445                 if( this->v_offset || a ) break;
446                 a = this->data + 3 * sz * sizeof(float);
447         case BC_RGB_FLOATP:
448                 if( this->v_offset ) break;
449                 this->y_offset = 0;
450                 this->u_offset = sz * sizeof(float);
451                 this->v_offset = 2 * sz * sizeof(float);
452                 break;
453
454         default:
455                 rows = new unsigned char*[h];
456                 for(int i = 0; i < h; i++)
457                         rows[i] = &this->data[i * this->bytes_per_line];
458                 return;
459         }
460         y = this->data + this->y_offset;
461         u = this->data + this->u_offset;
462         v = this->data + this->v_offset;
463 }
464
465 int VFrame::allocate_data(unsigned char *data, int shmid,
466                 long y_offset, long u_offset, long v_offset, int w, int h,
467                 int color_model, long bytes_per_line)
468 {
469         this->w = w;
470         this->h = h;
471         this->color_model = color_model;
472         this->bytes_per_pixel = calculate_bytes_per_pixel(color_model);
473         this->y_offset = this->u_offset = this->v_offset = 0;
474 //      if(shmid == 0) {
475 //              printf("VFrame::allocate_data %d shmid == 0\n", __LINE__, shmid);
476 //      }
477
478         this->bytes_per_line = bytes_per_line >= 0 ?
479                 bytes_per_line : this->bytes_per_pixel * w;
480
481 // Allocate data + padding for MMX
482         if(data) {
483 //printf("VFrame::allocate_data %d %p\n", __LINE__, this->data);
484                 memory_type = VFrame::SHARED;
485                 this->data = data;
486                 this->shmid = -1;
487                 this->y_offset = y_offset;
488                 this->u_offset = u_offset;
489                 this->v_offset = v_offset;
490         }
491         else if(shmid >= 0) {
492                 memory_type = VFrame::SHMGET;
493                 this->data = (unsigned char*)shmat(shmid, NULL, 0);
494 //printf("VFrame::allocate_data %d shmid=%d data=%p\n", __LINE__, shmid, this->data);
495                 this->shmid = shmid;
496                 this->y_offset = y_offset;
497                 this->u_offset = u_offset;
498                 this->v_offset = v_offset;
499         }
500         else {
501                 memory_type = VFrame::PRIVATE;
502                 this->data = 0;
503                 int size = calculate_data_size(this->w, this->h,
504                         this->bytes_per_line, this->color_model);
505                 if(BC_WindowBase::get_resources()->use_vframe_shm() && use_shm) {
506                         this->shmid = shmget(IPC_PRIVATE, size, IPC_CREAT | 0777);
507                         if( this->shmid >= 0 ) {
508                                 this->data = (unsigned char*)shmat(this->shmid, NULL, 0);
509 //printf("VFrame::allocate_data %d %d %d %p\n", __LINE__, size, this->shmid, this->data);
510 // This causes it to automatically delete when the program exits.
511                                 shmctl(this->shmid, IPC_RMID, 0);
512                         }
513                         else {
514                                 printf("VFrame::allocate_data %d could not allocate"
515                                         " shared memory, %dx%d (model %d) size=0x%08x\n",
516                                         __LINE__, w, h, color_model, size);
517                                 BC_Trace::dump_shm_stats(stdout);
518                         }
519                 }
520 // Have to use malloc for libpng
521                 if( !data ) {
522                         this->data = (unsigned char *)malloc(size);
523                         this->shmid = -1;
524                 }
525 // Memory check
526 // if(this->w * this->h > 1500 * 1100)
527 // printf("VFrame::allocate_data 2 this=%p w=%d h=%d this->data=%p\n",
528 // this, this->w, this->h, this->data);
529
530                 if(!this->data)
531                         printf("VFrame::allocate_data %dx%d: memory exhausted.\n", this->w, this->h);
532 #ifdef LEAKER
533 printf("==new %p from %p sz %d\n", this->data, __builtin_return_address(0), size);
534 #endif
535
536 //printf("VFrame::allocate_data %d %p data=%p %d %d\n", __LINE__, this, this->data, this->w, this->h);
537 //if(size > 1000000) printf("VFrame::allocate_data %d\n", size);
538         }
539
540 // Create row pointers
541         create_row_pointers();
542         return 0;
543 }
544
545 void VFrame::set_memory(unsigned char *data,
546         int shmid,
547         long y_offset,
548         long u_offset,
549         long v_offset)
550 {
551         clear_objects(0);
552
553         if(data)
554         {
555                 memory_type = VFrame::SHARED;
556                 this->data = data;
557                 this->shmid = -1;
558                 this->y_offset = y_offset;
559                 this->u_offset = u_offset;
560                 this->v_offset = v_offset;
561         }
562         else
563         if(shmid >= 0)
564         {
565                 memory_type = VFrame::SHMGET;
566                 this->data = (unsigned char*)shmat(shmid, NULL, 0);
567                 this->shmid = shmid;
568         }
569
570         y = this->data + this->y_offset;
571         u = this->data + this->u_offset;
572         v = this->data + this->v_offset;
573
574         create_row_pointers();
575 }
576
577 void VFrame::set_memory(BC_Bitmap *bitmap)
578 {
579         int shmid = 0;
580         unsigned char *data = 0;
581         if( bitmap->is_shared() && !bitmap->is_zombie() )
582                 shmid = bitmap->get_shmid();
583         else
584                 data = bitmap->get_data();
585         set_memory(data, shmid,
586                 bitmap->get_y_offset(),
587                 bitmap->get_u_offset(),
588                 bitmap->get_v_offset());
589 }
590
591 void VFrame::set_compressed_memory(unsigned char *data,
592         int shmid,
593         int data_size,
594         int data_allocated)
595 {
596         clear_objects(0);
597
598         if(data)
599         {
600                 memory_type = VFrame::SHARED;
601                 this->data = data;
602                 this->shmid = -1;
603         }
604         else
605         if(shmid >= 0)
606         {
607                 memory_type = VFrame::SHMGET;
608                 this->data = (unsigned char*)shmat(shmid, NULL, 0);
609                 this->shmid = shmid;
610         }
611
612         this->compressed_allocated = data_allocated;
613         this->compressed_size = data_size;
614 }
615
616
617 // Reallocate uncompressed buffer with or without alpha
618 int VFrame::reallocate(
619         unsigned char *data,
620         int shmid,
621         long y_offset,
622         long u_offset,
623         long v_offset,
624         int w,
625         int h,
626         int color_model,
627         long bytes_per_line)
628 {
629 //      if(shmid == 0) printf("VFrame::reallocate %d shmid=%d\n", __LINE__, shmid);
630         clear_objects(0);
631 //      reset_parameters(0);
632         allocate_data(data,
633                 shmid,
634                 y_offset,
635                 u_offset,
636                 v_offset,
637                 w,
638                 h,
639                 color_model,
640                 bytes_per_line);
641         return 0;
642 }
643
644 int VFrame::allocate_compressed_data(long bytes)
645 {
646         if(bytes < 1) return 1;
647
648 // Want to preserve original contents
649         if(data && compressed_allocated < bytes)
650         {
651                 int new_shmid = -1;
652                 unsigned char *new_data = 0;
653                 if(BC_WindowBase::get_resources()->use_vframe_shm() && use_shm)
654                 {
655                         new_shmid = shmget(IPC_PRIVATE,
656                                 bytes,
657                                 IPC_CREAT | 0777);
658                         new_data = (unsigned char*)shmat(new_shmid, NULL, 0);
659                         shmctl(new_shmid, IPC_RMID, 0);
660                 }
661                 else
662                 {
663 // Have to use malloc for libpng
664                         new_data = (unsigned char *)malloc(bytes);
665                 }
666
667                 bcopy(data, new_data, compressed_allocated);
668 UNBUFFER(data);
669
670                 if(memory_type == VFrame::PRIVATE)
671                 {
672                         if(shmid > 0) {
673                                 if(data)
674                                         shmdt(data);
675                         }
676                         else
677                                 free(data);
678                 }
679                 else
680                 if(memory_type == VFrame::SHMGET)
681                 {
682                         if(data)
683                                 shmdt(data);
684                 }
685
686                 data = new_data;
687                 shmid = new_shmid;
688                 compressed_allocated = bytes;
689         }
690         else
691         if(!data)
692         {
693                 if(BC_WindowBase::get_resources()->use_vframe_shm() && use_shm)
694                 {
695                         shmid = shmget(IPC_PRIVATE,
696                                 bytes,
697                                 IPC_CREAT | 0777);
698                         data = (unsigned char*)shmat(shmid, NULL, 0);
699                         shmctl(shmid, IPC_RMID, 0);
700                 }
701                 else
702                 {
703 // Have to use malloc for libpng
704                         data = (unsigned char *)malloc(bytes);
705                 }
706
707                 compressed_allocated = bytes;
708                 compressed_size = 0;
709         }
710
711         return 0;
712 }
713
714 int VFramePng::read_png(const unsigned char *data, long sz, double xscale, double yscale)
715 {
716 // Test for RAW format
717         if(data[0] == 'R' && data[1] == 'A' && data[2] == 'W' && data[3] == ' ') {
718                 int new_color_model = BC_RGBA8888;
719                 w = data[4] | (data[5] << 8) | (data[6]  << 16) | (data[7]  << 24);
720                 h = data[8] | (data[9] << 8) | (data[10] << 16) | (data[11] << 24);
721                 int components = data[12];
722                 new_color_model = components == 3 ? BC_RGB888 : BC_RGBA8888;
723 // This shares the data directly
724 //              reallocate(data + 20, 0, 0, 0, w, h, new_color_model, -1);
725
726 // Can't use shared data for theme since button constructions overlay the
727 // images directly.
728                 reallocate(NULL, -1, 0, 0, 0, w, h, new_color_model, -1);
729                 memcpy(get_data(), data + 16, w * h * components);
730
731         }
732         else if(data[0] == 0x89 && data[1] == 'P' && data[2] == 'N' && data[3] == 'G') {
733                 int have_alpha = 0;
734                 png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
735                 png_infop info_ptr = png_create_info_struct(png_ptr);
736                 int new_color_model;
737
738                 image_offset = 0;
739                 image = data;  image_size = sz;
740                 png_set_read_fn(png_ptr, this, PngReadFunction::png_read_function);
741                 png_read_info(png_ptr, info_ptr);
742
743                 w = png_get_image_width(png_ptr, info_ptr);
744                 h = png_get_image_height(png_ptr, info_ptr);
745
746                 int src_color_model = png_get_color_type(png_ptr, info_ptr);
747
748                 /* tell libpng to strip 16 bit/color files down to 8 bits/color */
749                 png_set_strip_16(png_ptr);
750
751                 /* extract multiple pixels with bit depths of 1, 2, and 4 from a single
752                  * byte into separate bytes (useful for paletted and grayscale images).
753                  */
754                 png_set_packing(png_ptr);
755
756                 /* expand paletted colors into true RGB triplets */
757                 if (src_color_model == PNG_COLOR_TYPE_PALETTE)
758                         png_set_expand(png_ptr);
759
760                 /* expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel */
761                 if (src_color_model == PNG_COLOR_TYPE_GRAY && png_get_bit_depth(png_ptr, info_ptr) < 8)
762                         png_set_expand(png_ptr);
763
764                 if (src_color_model == PNG_COLOR_TYPE_GRAY ||
765                     src_color_model == PNG_COLOR_TYPE_GRAY_ALPHA)
766                         png_set_gray_to_rgb(png_ptr);
767
768                 /* expand paletted or RGB images with transparency to full alpha channels
769                  * so the data will be available as RGBA quartets */
770                 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)){
771                         have_alpha = 1;
772                         png_set_expand(png_ptr);
773                 }
774
775                 switch(src_color_model)
776                 {
777                         case PNG_COLOR_TYPE_GRAY:
778                         case PNG_COLOR_TYPE_RGB:
779                                 new_color_model = BC_RGB888;
780                                 break;
781
782                         case PNG_COLOR_TYPE_GRAY_ALPHA:
783                         case PNG_COLOR_TYPE_RGB_ALPHA:
784                         default:
785                                 new_color_model = BC_RGBA8888;
786                                 break;
787
788                         case PNG_COLOR_TYPE_PALETTE:
789                                 if(have_alpha)
790                                         new_color_model = BC_RGBA8888;
791                                 else
792                                         new_color_model = BC_RGB888;
793                 }
794
795                 reallocate(NULL, -1, 0, 0, 0, w, h, new_color_model, -1);
796
797 //printf("VFrame::read_png %d %d %d %p\n", __LINE__, w, h, get_rows());
798                 png_read_image(png_ptr, get_rows());
799                 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
800         }
801         else {
802                 printf("VFrame::read_png %d: unknown file format"
803                         " 0x%02x 0x%02x 0x%02x 0x%02x\n",
804                         __LINE__, data[4], data[5], data[6], data[7]);
805                 return 1;
806         }
807         int ww = w * xscale, hh = h * yscale;
808         if( ww != w || hh != h ) {
809                 VFrame vframe(*this);
810                 reallocate(NULL, -1, 0, 0, 0, ww, hh, color_model, -1);
811                 transfer_from(&vframe);
812         }
813         return 0;
814 }
815
816 int VFrame::write_png(const char *path)
817 {
818         VFrame *vframe = this;
819         png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
820         png_infop info_ptr = png_create_info_struct(png_ptr);
821         FILE *out_fd = fopen(path, "w");
822         if(!out_fd) {
823                 printf("VFrame::write_png %d %s %s\n", __LINE__, path, strerror(errno));
824                 return 1;
825         }
826
827         int png_cmodel = PNG_COLOR_TYPE_RGB;
828         int bc_cmodel = get_color_model();
829         switch( bc_cmodel ) {
830         case BC_RGB888:                                          break;
831         case BC_RGBA8888: png_cmodel = PNG_COLOR_TYPE_RGB_ALPHA; break;
832         case BC_A8:       png_cmodel = PNG_COLOR_TYPE_GRAY;      break;
833         default:
834                 bc_cmodel = BC_RGB888;
835                 if( BC_CModels::has_alpha(bc_cmodel) ) {
836                         bc_cmodel = BC_RGBA8888;
837                         png_cmodel = PNG_COLOR_TYPE_RGB_ALPHA;
838                 }
839                 vframe = new VFrame(get_w(), get_h(), bc_cmodel, -1);
840                 vframe->transfer_from(this);
841                 break;
842         }
843         png_init_io(png_ptr, out_fd);
844         png_set_compression_level(png_ptr, 9);
845         png_set_IHDR(png_ptr, info_ptr, get_w(), get_h(), 8, png_cmodel,
846                 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
847         png_write_info(png_ptr, info_ptr);
848         png_write_image(png_ptr, vframe->get_rows());
849         png_write_end(png_ptr, info_ptr);
850         png_destroy_write_struct(&png_ptr, &info_ptr);
851         fclose(out_fd);
852         if( vframe != this ) delete vframe;
853         return 0;
854 }
855
856
857 #define ZERO_YUV(components, type, max) \
858 { \
859         for(int i = 0; i < h; i++) \
860         { \
861                 type *row = (type*)get_rows()[i]; \
862                 for(int j = 0; j < w; j++) \
863                 { \
864                         row[j * components] = 0; \
865                         row[j * components + 1] = (max + 1) / 2; \
866                         row[j * components + 2] = (max + 1) / 2; \
867                         if(components == 4) row[j * components + 3] = 0; \
868                 } \
869         } \
870 }
871
872 int VFrame::clear_frame()
873 {
874         int sz = w * h;
875 //printf("VFrame::clear_frame %d\n", __LINE__);
876         switch(color_model) {
877         case BC_COMPRESSED:
878                 break;
879
880         case BC_YUV410P:
881                 bzero(get_y(), sz);
882                 bzero(get_u(), w / 4 * h / 4);
883                 bzero(get_v(), w / 4 * h / 4);
884                 break;
885
886         case BC_YUV411P:
887         case BC_YUV420P:
888         case BC_YUV420PI:
889                 bzero(get_y(), sz);
890                 bzero(get_u(), sz / 4);
891                 bzero(get_v(), sz / 4);
892                 break;
893
894         case BC_YUV422P:
895                 bzero(get_y(), sz);
896                 bzero(get_u(), sz / 2);
897                 bzero(get_v(), sz / 2);
898                 break;
899
900         case BC_RGBA_FLOATP: if( a ) {
901                 float *ap = (float *)a;
902                 for( int i=sz; --i>=0; ++ap ) *ap = 1.f; }
903         case BC_RGB_FLOATP: {
904                 float *rp = (float *)y;
905                 for( int i=sz; --i>=0; ++rp ) *rp = 0.f;
906                 float *gp = (float *)u;
907                 for( int i=sz; --i>=0; ++gp ) *gp = 0.f;
908                 float *bp = (float *)v;
909                 for( int i=sz; --i>=0; ++bp ) *bp = 0.f;
910                 break; }
911         case BC_YUV444P:
912                 bzero(get_y(), sz);
913                 bzero(get_u(), sz);
914                 bzero(get_v(), sz);
915                 break;
916
917         case BC_YUV888:
918                 ZERO_YUV(3, unsigned char, 0xff);
919                 break;
920
921         case BC_YUVA8888:
922                 ZERO_YUV(4, unsigned char, 0xff);
923                 break;
924
925         case BC_YUV161616:
926                 ZERO_YUV(3, uint16_t, 0xffff);
927                 break;
928
929         case BC_YUVA16161616:
930                 ZERO_YUV(4, uint16_t, 0xffff);
931                 break;
932
933         default:
934                 bzero(data, calculate_data_size(w, h, bytes_per_line, color_model));
935                 break;
936         }
937         return 0;
938 }
939
940 void VFrame::rotate90()
941 {
942 // Allocate new frame
943         int new_w = h, new_h = w;
944         VFrame new_frame(new_w, new_h, color_model);
945         unsigned char **new_rows = new_frame.get_rows();
946 // Copy data
947         for(int in_y = 0, out_x = new_w - 1; in_y < h; in_y++, out_x--)
948         {
949                 for(int in_x = 0, out_y = 0; in_x < w; in_x++, out_y++)
950                 {
951                         for(int k = 0; k < bytes_per_pixel; k++)
952                         {
953                                 new_rows[out_y][out_x * bytes_per_pixel + k] =
954                                         rows[in_y][in_x * bytes_per_pixel + k];
955                         }
956                 }
957         }
958
959 // Swap frames
960 // swap memory
961         unsigned char *new_data = new_frame.data;
962         new_frame.data = data;
963         data = new_data;
964 // swap rows
965         new_rows = new_frame.rows;
966         new_frame.rows = rows;
967         rows = new_rows;
968 // swap shmid
969         int new_shmid = new_frame.shmid;
970         new_frame.shmid = shmid;
971         shmid = new_shmid;
972 // swap bytes_per_line
973         int new_bpl = new_frame.bytes_per_line;
974         new_frame.bytes_per_line = bytes_per_line;
975         bytes_per_line = new_bpl;
976         new_frame.clear_objects(0);
977
978         w = new_frame.w;
979         h = new_frame.h;
980 }
981
982 void VFrame::rotate270()
983 {
984 // Allocate new frame
985         int new_w = h, new_h = w;
986         VFrame new_frame(new_w, new_h, color_model);
987         unsigned char **new_rows = new_frame.get_rows();
988 // Copy data
989         for(int in_y = 0, out_x = 0; in_y < h; in_y++, out_x++)
990         {
991                 for(int in_x = 0, out_y = new_h - 1; in_x < w; in_x++, out_y--)
992                 {
993                         for(int k = 0; k < bytes_per_pixel; k++)
994                         {
995                                 new_rows[out_y][out_x * bytes_per_pixel + k] =
996                                         rows[in_y][in_x * bytes_per_pixel + k];
997                         }
998                 }
999         }
1000
1001 // Swap frames
1002 // swap memory
1003         unsigned char *new_data = new_frame.data;
1004         new_frame.data = data;
1005         data = new_data;
1006 // swap rows
1007         new_rows = new_frame.rows;
1008         new_frame.rows = rows;
1009         rows = new_rows;
1010 // swap shmid
1011         int new_shmid = new_frame.shmid;
1012         new_frame.shmid = shmid;
1013         shmid = new_shmid;
1014 // swap bytes_per_line
1015         int new_bpl = new_frame.bytes_per_line;
1016         new_frame.bytes_per_line = bytes_per_line;
1017         bytes_per_line = new_bpl;
1018         new_frame.clear_objects(0);
1019
1020         w = new_frame.w;
1021         h = new_frame.h;
1022 }
1023
1024 void VFrame::flip_vert()
1025 {
1026         unsigned char *temp = new unsigned char[bytes_per_line];
1027         for(int i = 0, j = h - 1; i < j; i++, j--)
1028         {
1029                 memcpy(temp, rows[j], bytes_per_line);
1030                 memcpy(rows[j], rows[i], bytes_per_line);
1031                 memcpy(rows[i], temp, bytes_per_line);
1032         }
1033         delete [] temp;
1034 }
1035
1036 void VFrame::flip_horiz()
1037 {
1038         unsigned char temp[32];
1039         for(int i = 0; i < h; i++)
1040         {
1041                 unsigned char *row = rows[i];
1042                 for(int j = 0; j < bytes_per_line / 2; j += bytes_per_pixel)
1043                 {
1044                         memcpy(temp, row + j, bytes_per_pixel);
1045                         memcpy(row + j, row + bytes_per_line - j - bytes_per_pixel, bytes_per_pixel);
1046                         memcpy(row + bytes_per_line - j - bytes_per_pixel, temp, bytes_per_pixel);
1047                 }
1048         }
1049 }
1050
1051
1052
1053 int VFrame::copy_from(VFrame *frame)
1054 {
1055         if(this->w != frame->get_w() ||
1056                 this->h != frame->get_h())
1057         {
1058                 printf("VFrame::copy_from %d sizes differ src %dx%d != dst %dx%d\n",
1059                         __LINE__,
1060                         frame->get_w(),
1061                         frame->get_h(),
1062                         get_w(),
1063                         get_h());
1064                 return 1;
1065         }
1066
1067         int w = MIN(this->w, frame->get_w());
1068         int h = MIN(this->h, frame->get_h());
1069         timestamp = frame->timestamp;
1070
1071         switch(frame->color_model)
1072         {
1073                 case BC_COMPRESSED:
1074                         allocate_compressed_data(frame->compressed_size);
1075                         memcpy(data, frame->data, frame->compressed_size);
1076                         this->compressed_size = frame->compressed_size;
1077                         break;
1078
1079                 case BC_YUV410P:
1080                         memcpy(get_y(), frame->get_y(), w * h);
1081                         memcpy(get_u(), frame->get_u(), w / 4 * h / 4);
1082                         memcpy(get_v(), frame->get_v(), w / 4 * h / 4);
1083                         break;
1084
1085                 case BC_YUV420P:
1086                 case BC_YUV420PI:
1087                 case BC_YUV411P:
1088 //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());
1089                         memcpy(get_y(), frame->get_y(), w * h);
1090                         memcpy(get_u(), frame->get_u(), w * h / 4);
1091                         memcpy(get_v(), frame->get_v(), w * h / 4);
1092                         break;
1093
1094                 case BC_YUV422P:
1095 //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());
1096                         memcpy(get_y(), frame->get_y(), w * h);
1097                         memcpy(get_u(), frame->get_u(), w * h / 2);
1098                         memcpy(get_v(), frame->get_v(), w * h / 2);
1099                         break;
1100
1101                 case BC_YUV444P:
1102 //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());
1103                         memcpy(get_y(), frame->get_y(), w * h);
1104                         memcpy(get_u(), frame->get_u(), w * h);
1105                         memcpy(get_v(), frame->get_v(), w * h);
1106                         break;
1107                 default:
1108 // printf("VFrame::copy_from %d\n", calculate_data_size(w,
1109 //                              h,
1110 //                              -1,
1111 //                              frame->color_model));
1112 // Copy without extra 4 bytes in case the source is a hardware device
1113                         memmove(data, frame->data, get_data_size());
1114                         break;
1115         }
1116
1117         params->copy_from(frame->params);
1118         return 0;
1119 }
1120
1121 int VFrame::transfer_from(VFrame *that, int bg_color, int in_x, int in_y, int in_w, int in_h)
1122 {
1123         if( this->get_color_model() == that->get_color_model() &&
1124             this->get_w() == that->get_w() && this->get_h() == that->get_h() &&
1125             this->get_bytes_per_line() == that->get_bytes_per_line() )
1126                 return this->copy_from(that);
1127
1128         timestamp = that->timestamp;
1129 #if 0
1130         BC_CModels::transfer(
1131                 this->get_rows(), that->get_rows(),          // Packed data out/in
1132                 this->get_y(), this->get_u(), this->get_v(), // Planar data out/in
1133                 that->get_y(), that->get_u(), that->get_v(),
1134                 0, 0, that->get_w(), that->get_h(),          // Dimensions in/out
1135                 0, 0, this->get_w(), this->get_h(),
1136                 that->get_color_model(), this->get_color_model(), // Color models in/out
1137                 bg_color,                                    // alpha blend bg_color
1138                 that->get_bytes_per_line(),
1139                 this->get_bytes_per_line());                 // rowspans (of luma for YUV)
1140 #else
1141         unsigned char *in_ptrs[4], *out_ptrs[4];
1142         unsigned char **inp, **outp;
1143         if( BC_CModels::is_planar(that->get_color_model()) ) {
1144                 in_ptrs[0] = that->get_y();
1145                 in_ptrs[1] = that->get_u();
1146                 in_ptrs[2] = that->get_v();
1147                 in_ptrs[3] = that->get_a();
1148                 inp = in_ptrs;
1149         }
1150         else
1151                 inp = that->get_rows();
1152         if( BC_CModels::is_planar(this->get_color_model()) ) {
1153                 out_ptrs[0] = this->get_y();
1154                 out_ptrs[1] = this->get_u();
1155                 out_ptrs[2] = this->get_v();
1156                 out_ptrs[3] = this->get_a();
1157                 outp = out_ptrs;
1158         }
1159         else
1160                 outp = this->get_rows();
1161         BC_CModels::transfer(outp, this->get_color_model(),
1162                         0, 0, this->get_w(), this->get_h(),
1163                         this->get_bytes_per_line(),
1164                 inp, that->get_color_model(),
1165                         in_x, in_y, in_w, in_h,
1166                         that->get_bytes_per_line(),
1167                 bg_color);
1168 #endif
1169         params->copy_from(that->params);
1170         return 0;
1171 }
1172
1173
1174 int VFrame::get_scale_tables(int *column_table, int *row_table,
1175                         int in_x1, int in_y1, int in_x2, int in_y2,
1176                         int out_x1, int out_y1, int out_x2, int out_y2)
1177 {
1178         int i;
1179         float w_in = in_x2 - in_x1;
1180         float h_in = in_y2 - in_y1;
1181         int w_out = out_x2 - out_x1;
1182         int h_out = out_y2 - out_y1;
1183
1184         float hscale = w_in / w_out;
1185         float vscale = h_in / h_out;
1186
1187         for(i = 0; i < w_out; i++)
1188         {
1189                 column_table[i] = (int)(hscale * i);
1190         }
1191
1192         for(i = 0; i < h_out; i++)
1193         {
1194                 row_table[i] = (int)(vscale * i) + in_y1;
1195         }
1196         return 0;
1197 }
1198
1199 void VFrame::push_prev_effect(const char *name)
1200 {
1201         char *ptr;
1202         prev_effects.append(ptr = new char[strlen(name) + 1]);
1203         strcpy(ptr, name);
1204         if(prev_effects.total > MAX_STACK_ELEMENTS) prev_effects.remove_object(0);
1205 }
1206
1207 void VFrame::pop_prev_effect()
1208 {
1209         if(prev_effects.total)
1210                 prev_effects.remove_object(prev_effects.last());
1211 }
1212
1213 void VFrame::push_next_effect(const char *name)
1214 {
1215         char *ptr;
1216         next_effects.append(ptr = new char[strlen(name) + 1]);
1217         strcpy(ptr, name);
1218         if(next_effects.total > MAX_STACK_ELEMENTS) next_effects.remove_object(0);
1219 }
1220
1221 void VFrame::pop_next_effect()
1222 {
1223         if(next_effects.total)
1224                 next_effects.remove_object(next_effects.last());
1225 }
1226
1227 const char* VFrame::get_next_effect(int number)
1228 {
1229         if(!next_effects.total) return "";
1230         else
1231         if(number > next_effects.total - 1) number = next_effects.total - 1;
1232
1233         return next_effects.values[next_effects.total - number - 1];
1234 }
1235
1236 const char* VFrame::get_prev_effect(int number)
1237 {
1238         if(!prev_effects.total) return "";
1239         else
1240         if(number > prev_effects.total - 1) number = prev_effects.total - 1;
1241
1242         return prev_effects.values[prev_effects.total - number - 1];
1243 }
1244
1245 BC_Hash* VFrame::get_params()
1246 {
1247         return params;
1248 }
1249
1250 void VFrame::clear_stacks()
1251 {
1252         next_effects.remove_all_objects();
1253         prev_effects.remove_all_objects();
1254         params->clear();
1255         status = 1;
1256 }
1257
1258 void VFrame::copy_params(VFrame *src)
1259 {
1260         status = src->status;
1261         params->copy_from(src->params);
1262 }
1263
1264 void VFrame::copy_stacks(VFrame *src)
1265 {
1266         clear_stacks();
1267
1268         for(int i = 0; i < src->next_effects.total; i++)
1269         {
1270                 char *ptr;
1271                 next_effects.append(ptr = new char[strlen(src->next_effects.values[i]) + 1]);
1272                 strcpy(ptr, src->next_effects.values[i]);
1273         }
1274         for(int i = 0; i < src->prev_effects.total; i++)
1275         {
1276                 char *ptr;
1277                 prev_effects.append(ptr = new char[strlen(src->prev_effects.values[i]) + 1]);
1278                 strcpy(ptr, src->prev_effects.values[i]);
1279         }
1280
1281         copy_params(src);
1282 }
1283
1284 int VFrame::equal_stacks(VFrame *src)
1285 {
1286         for(int i = 0; i < src->next_effects.total && i < next_effects.total; i++)
1287         {
1288                 if(strcmp(src->next_effects.values[i], next_effects.values[i])) return 0;
1289         }
1290
1291         for(int i = 0; i < src->prev_effects.total && i < prev_effects.total; i++)
1292         {
1293                 if(strcmp(src->prev_effects.values[i], prev_effects.values[i])) return 0;
1294         }
1295
1296         if(!params->equivalent(src->params)) return 0;
1297         return 1;
1298 }
1299
1300 void VFrame::dump_stacks()
1301 {
1302         printf("VFrame::dump_stacks\n");
1303         printf("        next_effects:\n");
1304         for(int i = next_effects.total - 1; i >= 0; i--)
1305                 printf("                %s\n", next_effects.values[i]);
1306         printf("        prev_effects:\n");
1307         for(int i = prev_effects.total - 1; i >= 0; i--)
1308                 printf("                %s\n", prev_effects.values[i]);
1309 }
1310
1311 void VFrame::dump_params()
1312 {
1313         params->dump();
1314 }
1315
1316 void VFrame::dump()
1317 {
1318         printf("VFrame::dump %d this=%p\n", __LINE__, this);
1319         printf("    w=%d h=%d colormodel=%d rows=%p use_shm=%d shmid=%d\n",
1320                 w, h, color_model, rows, use_shm, shmid);
1321 }
1322
1323
1324 int VFrame::get_memory_usage()
1325 {
1326         if(get_compressed_allocated()) return get_compressed_allocated();
1327         return get_h() * get_bytes_per_line();
1328 }
1329
1330 void VFrame::set_pixel_color(int rgb)
1331 {
1332         pixel_rgb = rgb;
1333         int ir = 0xff & (pixel_rgb >> 16);
1334         int ig = 0xff & (pixel_rgb >> 8);
1335         int ib = 0xff & (pixel_rgb >> 0);
1336         bc_rgb2yuv(ir,ig,ib, ir,ig,ib);
1337         pixel_yuv =  (ir<<16) | (ig<<8) | (ib<<0);
1338 }
1339
1340 void VFrame::set_stiple(int mask)
1341 {
1342         stipple = mask;
1343 }
1344
1345 int VFrame::draw_pixel(int x, int y)
1346 {
1347         if( x < 0 || y < 0 || x >= get_w() || y >= get_h() ) return 1;
1348         if( draw_point ) return (this->*draw_point)(x, y);
1349
1350 #define DRAW_PIXEL(type, r, g, b) { \
1351         type **rows = (type**)get_rows(); \
1352         rows[y][x * components + 0] = r; \
1353         rows[y][x * components + 1] = g; \
1354         rows[y][x * components + 2] = b; \
1355         if( components == 4 ) \
1356                 rows[y][x * components + 3] = mx; \
1357 }
1358         int components = BC_CModels::components(color_model);
1359         int bch = BC_CModels::calculate_pixelsize(color_model) / components;
1360         int sz = 8*bch, mx = BC_CModels::is_float(color_model) ? 1 : (1<<sz)-1;
1361         int is_yuv = BC_CModels::is_yuv(color_model);
1362         int pixel_color = is_yuv ? pixel_yuv : pixel_rgb;
1363         int ir = 0xff & (pixel_color >> 16);  float fr = 0;
1364         int ig = 0xff & (pixel_color >> 8);   float fg = 0;
1365         int ib = 0xff & (pixel_color >> 0);   float fb = 0;
1366         if( (x+y) & stipple ) {
1367                 ir = 255 - ir;  ig = 255 - ig;  ib = 255 - ib;
1368         }
1369         if( BC_CModels::is_float(color_model) ) {
1370                 fr = ir / 255.;  fg = ig / 255.;  fb = ib / 255.;
1371                 mx = 1;
1372         }
1373         else if( (sz-=8) > 0 ) {
1374                 ir <<= sz;  ig <<= sz;  ib <<= sz;
1375         }
1376
1377         switch(get_color_model()) {
1378         case BC_RGB888:
1379         case BC_YUV888:
1380         case BC_RGBA8888:
1381         case BC_YUVA8888:
1382                 DRAW_PIXEL(uint8_t, ir, ig, ib);
1383                 break;
1384         case BC_RGB161616:
1385         case BC_YUV161616:
1386         case BC_RGBA16161616:
1387         case BC_YUVA16161616:
1388                 DRAW_PIXEL(uint16_t, ir, ig, ib);
1389                 break;
1390         case BC_RGB_FLOAT:
1391         case BC_RGBA_FLOAT:
1392                 DRAW_PIXEL(float, fr, fg, fb);
1393                 break;
1394         }
1395         return 0;
1396 }
1397
1398
1399 // Bresenham's
1400 void VFrame::draw_line(int x1, int y1, int x2, int y2)
1401 {
1402         if( y1 > y2 ) {
1403                 int tx = x1;  x1 = x2;  x2 = tx;
1404                 int ty = y1;  y1 = y2;  y2 = ty;
1405         }
1406
1407         int x = x1, y = y1;
1408         int dx = x2-x1, dy = y2-y1;
1409         int dx2 = 2*dx, dy2 = 2*dy;
1410         if( dx < 0 ) dx = -dx;
1411         int r = dx > dy ? dx : dy, n = r;
1412         int dir = 0;
1413         if( dx2 < 0 ) dir += 1;
1414         if( dy >= dx ) {
1415                 if( dx2 >= 0 ) do {     /* +Y, +X */
1416                         draw_pixel(x, y++);
1417                         if( (r -= dx2) < 0 ) { r += dy2;  ++x; }
1418                 } while( --n >= 0 );
1419                 else do {               /* +Y, -X */
1420                         draw_pixel(x, y++);
1421                         if( (r += dx2) < 0 ) { r += dy2;  --x; }
1422                 } while( --n >= 0 );
1423         }
1424         else {
1425                 if( dx2 >= 0 ) do {     /* +X, +Y */
1426                         draw_pixel(x++, y);
1427                         if( (r -= dy2) < 0 ) { r += dx2;  ++y; }
1428                 } while( --n >= 0 );
1429                 else do {               /* -X, +Y */
1430                         draw_pixel(x--, y);
1431                         if( (r -= dy2) < 0 ) { r -= dx2;  ++y; }
1432                 } while( --n >= 0 );
1433         }
1434 }
1435
1436 // g++ -dD -E - < /dev/null | grep DBL_EPSILON
1437 #ifndef __DBL_EPSILON__
1438 #define __DBL_EPSILON__ ((double)2.22044604925031308085e-16L)
1439 #endif
1440 // weakest fraction * graphics integer range
1441 #define RND_EPSILON (__DBL_EPSILON__*65536)
1442
1443 class smooth_line {
1444         int rnd(double v) { return round(v)+RND_EPSILON; }
1445         VFrame *vframe;
1446 public:
1447         int sx, sy, ex, ey;     /* current point, end point */
1448         int xs, ys;             /* x/y quadrant sign -1/1 */
1449         int64_t A, B, C;        /* quadratic coefficients */
1450         int64_t r, dx, dy;      /* residual, dr/dx and dr/dy */
1451         int xmxx, xmxy;         /* x,y at apex */
1452         int done;
1453
1454         void init0(int x1,int y1, int x2,int y2, int x3,int y3, int top);
1455         void init1(int x1,int y1, int x2,int y2, int x3,int y3);
1456         int64_t rx() { return r + xs*8*dx + 4*A; }
1457         void moveX(int64_t r) {
1458                 dx += xs*A;   dy -= xs*B;
1459                 this->r = r;  sx += xs;
1460         }
1461         int64_t ry() { return r + 8*dy + 4*C; }
1462         void moveY(int64_t r) {
1463                 dx -= B;      dy += C;
1464                 this->r = r;  ++sy;
1465         }
1466         void draw();
1467
1468         smooth_line(VFrame *vframe) { this->vframe = vframe; this->done = 0; }
1469 };
1470
1471
1472 void smooth_line::draw()
1473 {
1474         if( done ) return;
1475         if( abs(dy) >= abs(dx) ) {
1476                 if( xs*(sx-xmxx) >= 0 ) {
1477                         if( ys > 0 ) { done = 1;  return; }
1478                         if( dy < 0 || ry() < 0 ) { moveY(ry()); goto xit; }
1479                         xmxx = ex;  xmxy = ey;
1480                         ys = 1;  xs = -xs;
1481                 }
1482                 moveX(rx());
1483                 int64_t rr = ry();
1484                 if( abs(rr) < abs(r) )
1485                         moveY(rr);
1486         }
1487         else {
1488                 if( sy >= xmxy ) {
1489                         if( ys > 0 ) { done = 1;  return; }
1490                         xmxx = ex;  xmxy = ey;
1491                         ys = 1;  xs = -xs;
1492                 }
1493                 moveY(ry());
1494                 int64_t rr = rx();
1495                 if( abs(rr) < abs(r) )
1496                         moveX(rr);
1497         }
1498 xit:    vframe->draw_pixel(sx, sy);
1499 }
1500
1501 void VFrame::draw_smooth(int x1, int y1, int x2, int y2, int x3, int y3)
1502 {
1503         if( (x1 == x2 && y1 == y2) || (x2 == x3 && y2 == y3) )
1504                 draw_line(x1,y1, x3,y3);
1505         else if( x1 == x3 && y1 == y3 )
1506                 draw_line(x1,y1, x2,y2);
1507         else if( (x2-x1) * (y2-y3) == (x2-x3) * (y2-y1) ) {
1508                 // co-linear, draw line from min to max
1509                 if( x1 < x3 ) {
1510                         if( x2 < x1 ) { x1 = x2;  y1 = y2; }
1511                         if( x2 > x3 ) { x3 = x2;  y3 = y2; }
1512                 }
1513                 else {
1514                         if( x2 > x1 ) { x1 = x2;  y1 = y2; }
1515                         if( x2 < x3 ) { x3 = x2;  y3 = y2; }
1516                 }
1517                 draw_line(x1,y1, x3,y3);
1518         }
1519         else
1520                 smooth_draw(x1, y1, x2, y2, x3, y3);
1521 }
1522
1523 /*
1524   Non-Parametric Smooth Curve Generation. Don Kelly 1984
1525
1526      P+-----+Q'= virtual
1527      /     /       origin
1528     /     /
1529   Q+-----+R
1530
1531    Let the starting point be P. the ending point R. and the tangent vertex Q.
1532    A general point Z on the curve is then
1533         Z = (P + R - Q) + (Q - P) sin t + (Q - R) cos t
1534
1535    Expanding the Cartesian coordinates around (P + R - Q) gives
1536         [x y] = Z - (P + R - Q)
1537         [a c] = Q - P
1538         [b d] = Q - R
1539         x = a*sin(t) + b*cos(t)
1540         y = c*sin(t) + d*cos(t)
1541
1542    from which t can now be eliminated via
1543         c*x - a*y = (c*b - a*d)*cos(t)
1544         d*x - b*y = (a*d - c*b)*sin(t)
1545
1546    giving the Cartesian equation for the ellipse as
1547         f(x, y) = (c*x - a*y)**2 + (d*x - b*y)**2 - (a*d - c*b)**2 = 0
1548
1549    or:  f(x, y) = A*x**2 - 2*B*x*y + C*y**2 + B**2 - A*C = 0
1550    where: A = c**2 + d**2,  B = a*c + b*d,  C = a**2 + b**2
1551
1552    The maximum y extent of the ellipse may now be derived as follows:
1553         let df/dx = 0,  2*A*x = 2*B*y,  x = y*B/A
1554         f(x, y) == B**2 * y**2 / A - 2*B**2 * y**2 / A + C*y**2 + B**2 - A*C = 0
1555            (A*C - B**2)*y = (A*C - B**2)*A
1556            max x = sqrt(C), at y = B/sqrt(C)
1557            max y = sqrt(A), at x = B/sqrt(A)
1558
1559  */
1560
1561
1562 /* x1,y1 = P, x2,y2 = Q, x3,y3=R,
1563  *  draw from P to Q to R   if top=0
1564  *    or from P to (x,ymax) if top>0
1565  *    or from Q to (x,ymax) if top<0
1566  */
1567 void smooth_line::init0(int x1,int y1, int x2,int y2, int x3,int y3, int top)
1568 {
1569         int x0 = x1+x3-x2, y0 = y1+y3-y2; // Q'
1570
1571         int a = x2-x1,  c = y2-y1;
1572         int b = x2-x3,  d = y2-y3;
1573         A = c*c + d*d;  C = a*a + b*b;  B = a*c + b*d;
1574
1575         sx = top >= 0 ? x1 : x3;
1576         sy = top >= 0 ? y1 : y3;
1577         xs = x2 > sx || (x2==sx && (x1+x3-sx)>=x2) ? 1 : -1;
1578         int64_t px = sx-x0, py = sy-y0;
1579         dx = A*px - B*py;  dy = C*py - B*px;
1580         r = 0;
1581
1582         if( top ) {
1583                 double ymy = sqrt(A), ymx = B/ymy;
1584                 ex = x0 + rnd(ymx);
1585                 ey = y0 + rnd(ymy);
1586         }
1587         else {
1588                 ex = x3;  ey = y3;
1589         }
1590
1591         ys = a*b > 0 && (!top || top*xs*(b*c - a*d) > 0) ? -1 : 1;
1592         if( ys < 0 ) {
1593                 double xmx = xs*sqrt(C), xmy = B/xmx;
1594                 xmxx = x0 + rnd(xmx);
1595                 xmxy = y0 + rnd(xmy);
1596         }
1597         else {
1598                 xmxx = ex; xmxy = ey;
1599         }
1600 }
1601
1602 /*  x1,y1 = P, x2,y2 = Q, x3,y3=R,
1603  *  draw from (x,ymax) to P
1604  */
1605 void smooth_line::init1(int x1,int y1, int x2,int y2, int x3,int y3)
1606 {
1607         int x0 = x1+x3-x2, y0 = y1+y3-y2; // Q'
1608
1609         int a = x2-x1,  c = y2-y1;
1610         int b = x2-x3,  d = y2-y3;
1611         A = c*c + d*d;  C = a*a + b*b;  B = a*c + b*d;
1612
1613         double ymy = -sqrt(A), ymx = B/ymy;
1614         int64_t px = rnd(ymx), py = rnd(ymy);
1615         sx = x0 + px;  ex = x1;
1616         sy = y0 + py;  ey = y1;
1617         xs = x2 > x1 || (x2==x1 && x3>=x2) ? 1 : -1;
1618         dx = A*px - B*py;  dy = C*py - B*px;
1619         r = 4 * (A*px*px - 2*B*px*py + C*py*py + B*B - A*C);
1620
1621         ys = a*b > 0 && xs*(b*c - a*d) < 0 ? -1 : 1;
1622         if( ys < 0 ) {
1623                 double xmx = xs*sqrt(C), xmy = B/xmx;
1624                 xmxx = x0 + rnd(xmx);
1625                 xmxy = y0 + rnd(xmy);
1626         }
1627         else {
1628                 xs = -xs;
1629                 xmxx = ex; xmxy = ey;
1630         }
1631         if( xs > 0 )
1632                 vframe->draw_pixel(sx, sy);
1633         while( xs*(sx-xmxx) < 0 && (xs*dx < 0 || rx() < 0) ) {
1634                 moveX(rx());
1635                 vframe->draw_pixel(sx, sy);
1636         }
1637 }
1638
1639
1640 void VFrame::smooth_draw(int x1, int y1, int x2, int y2, int x3, int y3)
1641 {
1642 //printf("p smooth_draw( %d,%d, %d,%d, %d,%d )\n", x1,y1,x2,y2,x3,y3);
1643         if( y1 > y3 ) {         // make y3 >= y1
1644                 int xt = x1;  x1 = x3;  x3 = xt;
1645                 int yt = y1;  y1 = y3;  y3 = yt;
1646         }
1647         if( y1 > y2 && y3 > y2 ) {
1648                 smooth_line lt(this), rt(this); // Q on bottom
1649                 lt.init1(x1, y1, x2, y2, x3, y3);
1650                 rt.init1(x3, y3, x2, y2, x1, y1);
1651                 while( !lt.done || !rt.done ) {
1652                         lt.draw();
1653                         rt.draw();
1654                 }
1655         }
1656         else if( y1 < y2 && y3 < y2 ) {
1657                 smooth_line lt(this), rt(this); // Q on top
1658                 lt.init0(x1, y1, x2, y2, x3, y3, 1);
1659                 draw_pixel(lt.sx, lt.sy);
1660                 rt.init0(x1, y1, x2, y2, x3, y3, -1);
1661                 draw_pixel(rt.sx, rt.sy);
1662                 while( !lt.done || !rt.done ) {
1663                         lt.draw();
1664                         rt.draw();
1665                 }
1666         }
1667         else {
1668                 smooth_line pt(this);           // Q in between
1669                 pt.init0(x1, y1, x2, y2, x3, y3, 0);
1670                 draw_pixel(pt.sx, pt.sy);
1671                 while( !pt.done ) {
1672                         pt.draw();
1673                 }
1674         }
1675 }
1676
1677
1678 void VFrame::draw_rect(int x1, int y1, int x2, int y2)
1679 {
1680         draw_line(x1, y1, x2, y1);
1681         draw_line(x2, y1 + 1, x2, y2);
1682         draw_line(x2 - 1, y2, x1, y2);
1683         draw_line(x1, y2 - 1, x1, y1 + 1);
1684 }
1685
1686
1687 void VFrame::draw_oval(int x1, int y1, int x2, int y2)
1688 {
1689         int w = x2 - x1;
1690         int h = y2 - y1;
1691         int center_x = (x2 + x1) / 2;
1692         int center_y = (y2 + y1) / 2;
1693         int x_table[h / 2];
1694
1695 //printf("VFrame::draw_oval %d %d %d %d %d\n", __LINE__, x1, y1, x2, y2);
1696
1697         for(int i = 0; i < h / 2; i++) {
1698 // A^2 = -(B^2) + C^2
1699                 x_table[i] = (int)(sqrt(-SQR(h / 2 - i) + SQR(h / 2)) * w / h);
1700 //printf("VFrame::draw_oval %d i=%d x=%d\n", __LINE__, i, x_table[i]);
1701         }
1702
1703         for(int i = 0; i < h / 2 - 1; i++) {
1704                 int x3 = x_table[i];
1705                 int x4 = x_table[i + 1];
1706
1707                 if(x4 > x3 + 1) {
1708                         for(int j = x3; j < x4; j++) {
1709                                 draw_pixel(center_x + j, y1 + i);
1710                                 draw_pixel(center_x - j, y1 + i);
1711                                 draw_pixel(center_x + j, y2 - i - 1);
1712                                 draw_pixel(center_x - j, y2 - i - 1);
1713                         }
1714                 }
1715                 else {
1716                         draw_pixel(center_x + x3, y1 + i);
1717                         draw_pixel(center_x - x3, y1 + i);
1718                         draw_pixel(center_x + x3, y2 - i - 1);
1719                         draw_pixel(center_x - x3, y2 - i - 1);
1720                 }
1721         }
1722         
1723         draw_pixel(center_x, y1);
1724         draw_pixel(center_x, y2 - 1);
1725         draw_pixel(x1, center_y);
1726         draw_pixel(x2 - 1, center_y);
1727         draw_pixel(x1, center_y - 1);
1728         draw_pixel(x2 - 1, center_y - 1);
1729 }
1730
1731
1732 void VFrame::draw_arrow(int x1, int y1, int x2, int y2, int sz)
1733 {
1734         double angle = atan((float)(y2 - y1) / (float)(x2 - x1));
1735         double angle1 = angle + (float)145 / 360 * 2 * M_PI;
1736         double angle2 = angle - (float)145 / 360 * 2 * M_PI;
1737         int s = x2 < x1 ? -1 : 1;
1738         int x3 = x2 + s * (int)(sz * cos(angle1));
1739         int y3 = y2 + s * (int)(sz * sin(angle1));
1740         int x4 = x2 + s * (int)(sz * cos(angle2));
1741         int y4 = y2 + s * (int)(sz * sin(angle2));
1742
1743 // Main vector
1744         draw_line(x1, y1, x2, y2);
1745 //      draw_line(x1, y1 + 1, x2, y2 + 1);
1746
1747 // Arrow line
1748         if(abs(y2 - y1) || abs(x2 - x1)) draw_line(x2, y2, x3, y3);
1749 // Arrow line
1750         if(abs(y2 - y1) || abs(x2 - x1)) draw_line(x2, y2, x4, y4);
1751 }
1752
1753 void VFrame::draw_x(int x, int y, int sz)
1754 {
1755         draw_line(x-sz,y-sz, x+sz,y+sz);
1756         draw_line(x+sz,y-sz, x-sz,y+sz);
1757 }
1758 void VFrame::draw_t(int x, int y, int sz)
1759 {
1760         draw_line(x,y-sz, x,y+sz);
1761         draw_line(x+sz,y, x-sz,y);
1762 }
1763
1764