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