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