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