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