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