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