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