remove whitespace at eol
[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 <sys/shm.h>
28
29 #include "bcbitmap.h"
30 #include "bchash.h"
31 #include "bcpbuffer.h"
32 #include "bcresources.h"
33 #include "bcsignals.h"
34 #include "bcsynchronous.h"
35 #include "bctexture.h"
36 #include "bcwindowbase.h"
37 #include "clip.h"
38 #include "bccmodels.h"
39 #include "vframe.h"
40
41 class PngReadFunction
42 {
43 public:
44         static void png_read_function(png_structp png_ptr,
45                    png_bytep data, png_size_t length)
46         {
47                 VFrame *frame = (VFrame*)png_get_io_ptr(png_ptr);
48                 if(frame->image_size - frame->image_offset < (long)length)
49                 {
50                         printf("PngReadFunction::png_read_function %d: overrun\n", __LINE__);
51                         length = frame->image_size - frame->image_offset;
52                 }
53
54                 memcpy(data, &frame->image[frame->image_offset], length);
55                 frame->image_offset += length;
56         };
57 };
58
59
60
61
62
63
64
65 VFrameScene::VFrameScene()
66 {
67 }
68
69 VFrameScene::~VFrameScene()
70 {
71 }
72
73
74
75
76
77
78
79 //static BCCounter counter;
80
81
82 VFramePng::VFramePng(unsigned char *png_data, double scale)
83 {
84         long image_size =
85                 ((long)png_data[0] << 24) | ((long)png_data[1] << 16) |
86                 ((long)png_data[2] << 8)  |  (long)png_data[3];
87         if( !scale ) scale = BC_WindowBase::get_resources()->icon_scale;
88         read_png(png_data+4, image_size, scale, scale);
89 }
90
91 VFramePng::VFramePng(unsigned char *png_data, long image_size, double xscale, double yscale)
92 {
93         if( !xscale ) xscale = BC_WindowBase::get_resources()->icon_scale;
94         if( !yscale ) yscale = BC_WindowBase::get_resources()->icon_scale;
95         read_png(png_data, image_size, xscale, yscale);
96 }
97
98 VFramePng::~VFramePng()
99 {
100 }
101
102
103 VFrame::VFrame(VFrame &frame)
104 {
105         reset_parameters(1);
106         params = new BC_Hash;
107         allocate_data(0, -1, 0, 0, 0, frame.w, frame.h,
108                 frame.color_model, frame.bytes_per_line);
109         memcpy(data, frame.data, bytes_per_line * h);
110         copy_stacks(&frame);
111 }
112
113 VFrame::VFrame(int w, int h, int color_model, long bytes_per_line)
114 {
115         reset_parameters(1);
116         params = new BC_Hash;
117         allocate_data(data, -1, 0, 0, 0, w, h,
118                 color_model, bytes_per_line);
119 }
120
121 VFrame::VFrame(unsigned char *data, int shmid, int w, int h,
122         int color_model, long bytes_per_line)
123 {
124         reset_parameters(1);
125         params = new BC_Hash;
126         allocate_data(data, shmid, 0, 0, 0, w, h,
127                 color_model, bytes_per_line);
128 }
129
130 VFrame::VFrame(unsigned char *data, int shmid,
131                 long y_offset, long u_offset, long v_offset,
132                 int w, int h, int color_model, long bytes_per_line)
133 {
134         reset_parameters(1);
135         params = new BC_Hash;
136         allocate_data(data, shmid, y_offset, u_offset, v_offset, w, h,
137                 color_model, bytes_per_line);
138 }
139
140 VFrame::VFrame(BC_Bitmap *bitmap, int w, int h,
141                  int color_model, long bytes_per_line)
142 {
143         reset_parameters(1);
144         params = new BC_Hash;
145         int shmid = 0;
146         unsigned char *data = 0;
147         if( bitmap->is_shared() )
148                 shmid = bitmap->get_shmid();
149         else
150                 data = bitmap->get_data();
151         allocate_data(data, shmid,
152                 bitmap->get_y_offset(),
153                 bitmap->get_u_offset(),
154                 bitmap->get_v_offset(),
155                 w, h, color_model, bytes_per_line);
156 }
157
158 VFrame::VFrame()
159 {
160         reset_parameters(1);
161         params = new BC_Hash;
162         this->color_model = BC_COMPRESSED;
163 }
164
165
166
167 VFrame::~VFrame()
168 {
169         clear_objects(1);
170 // Delete effect stack
171         prev_effects.remove_all_objects();
172         next_effects.remove_all_objects();
173         delete params;
174         delete scene;
175 }
176
177 int VFrame::equivalent(VFrame *src, int test_stacks)
178 {
179         return (src->get_color_model() == get_color_model() &&
180                 src->get_w() == get_w() &&
181                 src->get_h() == get_h() &&
182                 src->bytes_per_line == bytes_per_line &&
183                 (!test_stacks || equal_stacks(src)));
184 }
185
186 int VFrame::data_matches(VFrame *frame)
187 {
188         if(data && frame->get_data() &&
189                 frame->params_match(get_w(), get_h(), get_color_model()) &&
190                 get_data_size() == frame->get_data_size())
191         {
192                 int data_size = get_data_size();
193                 unsigned char *ptr1 = get_data();
194                 unsigned char *ptr2 = frame->get_data();
195                 for(int i = 0; i < data_size; i++)
196                 {
197                         if(*ptr1++ != *ptr2++) return 0;
198                 }
199                 return 1;
200         }
201         return 0;
202 }
203
204 // long VFrame::set_shm_offset(long offset)
205 // {
206 //      shm_offset = offset;
207 //      return 0;
208 // }
209 //
210 // long VFrame::get_shm_offset()
211 // {
212 //      return shm_offset;
213 // }
214 //
215 int VFrame::get_memory_type()
216 {
217         return memory_type;
218 }
219
220 int VFrame::params_match(int w, int h, int color_model)
221 {
222         return (this->w == w &&
223                 this->h == h &&
224                 this->color_model == color_model);
225 }
226
227
228 int VFrame::reset_parameters(int do_opengl)
229 {
230         status = 1;
231         scene = 0;
232         field2_offset = -1;
233         memory_type = VFrame::PRIVATE;
234 //      shm_offset = 0;
235         shmid = -1;
236         use_shm = 1;
237         bytes_per_line = 0;
238         data = 0;
239         rows = 0;
240         color_model = 0;
241         compressed_allocated = 0;
242         compressed_size = 0;   // Size of current image
243         w = 0;
244         h = 0;
245         y = u = v = a = 0;
246         y_offset = 0;
247         u_offset = 0;
248         v_offset = 0;
249         sequence_number = -1;
250         timestamp = -1.;
251         is_keyframe = 0;
252
253         if(do_opengl)
254         {
255 // By default, anything is going to be done in RAM
256                 opengl_state = VFrame::RAM;
257                 pbuffer = 0;
258                 texture = 0;
259         }
260
261         prev_effects.set_array_delete();
262         next_effects.set_array_delete();
263         return 0;
264 }
265
266 int VFrame::clear_objects(int do_opengl)
267 {
268 // Remove texture
269         if(do_opengl)
270         {
271                 delete texture;
272                 texture = 0;
273
274                 delete pbuffer;
275                 pbuffer = 0;
276         }
277
278 // Delete data
279         switch(memory_type)
280         {
281                 case VFrame::PRIVATE:
282 // Memory check
283 // if(this->w * this->h > 1500 * 1100)
284 // printf("VFrame::clear_objects 2 this=%p data=%p\n", this, data);
285                         if(data)
286                         {
287 //printf("VFrame::clear_objects %d this=%p shmid=%p data=%p\n", __LINE__, this, shmid, data);
288                                 if(shmid >= 0)
289                                         shmdt(data);
290                                 else
291                                         free(data);
292 //PRINT_TRACE
293                         }
294
295                         data = 0;
296                         shmid = -1;
297                         break;
298
299                 case VFrame::SHMGET:
300                         if(data)
301                                 shmdt(data);
302                         data = 0;
303                         shmid = -1;
304                         break;
305         }
306
307 // Delete row pointers
308         switch(color_model)
309         {
310                 case BC_COMPRESSED:
311                 case BC_YUV410P:
312                 case BC_YUV411P:
313                 case BC_YUV420P:
314                 case BC_YUV420PI:
315                 case BC_YUV422P:
316                 case BC_YUV444P:
317                 case BC_RGB_FLOATP:
318                 case BC_RGBA_FLOATP:
319                         break;
320
321                 default:
322                         delete [] rows;
323                         rows = 0;
324                         break;
325         }
326
327
328         return 0;
329 }
330
331 int VFrame::get_field2_offset()
332 {
333         return field2_offset;
334 }
335
336 int VFrame::set_field2_offset(int value)
337 {
338         this->field2_offset = value;
339         return 0;
340 }
341
342 void VFrame::set_keyframe(int value)
343 {
344         this->is_keyframe = value;
345 }
346
347 int VFrame::get_keyframe()
348 {
349         return is_keyframe;
350 }
351
352
353 VFrameScene* VFrame::get_scene()
354 {
355         return scene;
356 }
357
358 int VFrame::calculate_bytes_per_pixel(int color_model)
359 {
360         return BC_CModels::calculate_pixelsize(color_model);
361 }
362
363 long VFrame::get_bytes_per_line()
364 {
365         return bytes_per_line;
366 }
367
368 long VFrame::get_data_size()
369 {
370         return calculate_data_size(w, h, bytes_per_line, color_model) - 4;
371 }
372
373 long VFrame::calculate_data_size(int w, int h, int bytes_per_line, int color_model)
374 {
375         return BC_CModels::calculate_datasize(w, h, bytes_per_line, color_model);
376 }
377
378 void VFrame::create_row_pointers()
379 {
380         int sz = w * h;
381         switch(color_model) {
382         case BC_YUV410P:
383                 if( this->v_offset ) break;
384                 this->y_offset = 0;
385                 this->u_offset = sz;
386                 this->v_offset = sz + w / 4 * h / 4;
387                 break;
388
389         case BC_YUV420P:
390         case BC_YUV420PI:
391         case BC_YUV411P:
392                 if( this->v_offset ) break;
393                 this->y_offset = 0;
394                 this->u_offset = sz;
395                 this->v_offset = sz + sz / 4;
396                 break;
397
398         case BC_YUV422P:
399                 if( this->v_offset ) break;
400                 this->y_offset = 0;
401                 this->u_offset = sz;
402                 this->v_offset = sz + sz / 2;
403                 break;
404         case BC_YUV444P:
405                 if( this->v_offset ) break;
406                 this->y_offset = 0;
407                 this->u_offset = sz;
408                 this->v_offset = sz + sz;
409                 break;
410         case BC_RGBA_FLOATP:
411                 if( this->v_offset || a ) break;
412                 a = this->data + 3 * sz * sizeof(float);
413         case BC_RGB_FLOATP:
414                 if( this->v_offset ) break;
415                 this->y_offset = 0;
416                 this->u_offset = sz * sizeof(float);
417                 this->v_offset = 2 * sz * sizeof(float);
418                 break;
419
420         default:
421                 rows = new unsigned char*[h];
422                 for(int i = 0; i < h; i++)
423                         rows[i] = &this->data[i * this->bytes_per_line];
424                 return;
425         }
426         y = this->data + this->y_offset;
427         u = this->data + this->u_offset;
428         v = this->data + this->v_offset;
429 }
430
431 int VFrame::allocate_data(unsigned char *data, int shmid,
432                 long y_offset, long u_offset, long v_offset, int w, int h,
433                 int color_model, long bytes_per_line)
434 {
435         this->w = w;
436         this->h = h;
437         this->color_model = color_model;
438         this->bytes_per_pixel = calculate_bytes_per_pixel(color_model);
439         this->y_offset = this->u_offset = this->v_offset = 0;
440 //      if(shmid == 0) {
441 //              printf("VFrame::allocate_data %d shmid == 0\n", __LINE__, shmid);
442 //      }
443
444         this->bytes_per_line = bytes_per_line >= 0 ?
445                 bytes_per_line : this->bytes_per_pixel * w;
446
447 // Allocate data + padding for MMX
448         if(data) {
449 //printf("VFrame::allocate_data %d %p\n", __LINE__, this->data);
450                 memory_type = VFrame::SHARED;
451                 this->data = data;
452                 this->shmid = -1;
453                 this->y_offset = y_offset;
454                 this->u_offset = u_offset;
455                 this->v_offset = v_offset;
456         }
457         else if(shmid >= 0) {
458                 memory_type = VFrame::SHMGET;
459                 this->data = (unsigned char*)shmat(shmid, NULL, 0);
460 //printf("VFrame::allocate_data %d shmid=%d data=%p\n", __LINE__, shmid, this->data);
461                 this->shmid = shmid;
462                 this->y_offset = y_offset;
463                 this->u_offset = u_offset;
464                 this->v_offset = v_offset;
465         }
466         else {
467                 memory_type = VFrame::PRIVATE;
468                 int size = calculate_data_size(this->w, this->h,
469                         this->bytes_per_line, this->color_model);
470                 if(BC_WindowBase::get_resources()->use_vframe_shm() && use_shm) {
471                         this->shmid = shmget(IPC_PRIVATE, size, IPC_CREAT | 0777);
472                         if(this->shmid < 0) {
473                                 printf("VFrame::allocate_data %d could not allocate shared memory\n", __LINE__);
474                         }
475
476                         this->data = (unsigned char*)shmat(this->shmid, NULL, 0);
477 //printf("VFrame::allocate_data %d %d %d\n", __LINE__, size, this->shmid);
478
479 //printf("VFrame::allocate_data %d %p\n", __LINE__, this->data);
480 // This causes it to automatically delete when the program exits.
481                         shmctl(this->shmid, IPC_RMID, 0);
482                 }
483                 else {
484 // Have to use malloc for libpng
485 //printf("==vframe %d from %p\n", size, __builtin_return_address(0));
486                         this->data = (unsigned char *)malloc(size);
487                 }
488
489 // Memory check
490 // if(this->w * this->h > 1500 * 1100)
491 // printf("VFrame::allocate_data 2 this=%p w=%d h=%d this->data=%p\n",
492 // this, this->w, this->h, this->data);
493
494                 if(!this->data)
495                         printf("VFrame::allocate_data %dx%d: memory exhausted.\n", this->w, this->h);
496
497 //printf("VFrame::allocate_data %d %p data=%p %d %d\n", __LINE__, this, this->data, this->w, this->h);
498 //if(size > 1000000) printf("VFrame::allocate_data %d\n", size);
499         }
500
501 // Create row pointers
502         create_row_pointers();
503         return 0;
504 }
505
506 void VFrame::set_memory(unsigned char *data,
507         int shmid,
508         long y_offset,
509         long u_offset,
510         long v_offset)
511 {
512         clear_objects(0);
513
514         if(data)
515         {
516                 memory_type = VFrame::SHARED;
517                 this->data = data;
518                 this->shmid = -1;
519                 this->y_offset = y_offset;
520                 this->u_offset = u_offset;
521                 this->v_offset = v_offset;
522         }
523         else
524         if(shmid >= 0)
525         {
526                 memory_type = VFrame::SHMGET;
527                 this->data = (unsigned char*)shmat(shmid, NULL, 0);
528                 this->shmid = shmid;
529         }
530
531         y = this->data + this->y_offset;
532         u = this->data + this->u_offset;
533         v = this->data + this->v_offset;
534
535         create_row_pointers();
536 }
537
538 void VFrame::set_memory(BC_Bitmap *bitmap)
539 {
540         int shmid = 0;
541         unsigned char *data = 0;
542         if( bitmap->is_shared() && !bitmap->is_zombie() )
543                 shmid = bitmap->get_shmid();
544         else
545                 data = bitmap->get_data();
546         set_memory(data, shmid,
547                 bitmap->get_y_offset(),
548                 bitmap->get_u_offset(),
549                 bitmap->get_v_offset());
550 }
551
552 void VFrame::set_compressed_memory(unsigned char *data,
553         int shmid,
554         int data_size,
555         int data_allocated)
556 {
557         clear_objects(0);
558
559         if(data)
560         {
561                 memory_type = VFrame::SHARED;
562                 this->data = data;
563                 this->shmid = -1;
564         }
565         else
566         if(shmid >= 0)
567         {
568                 memory_type = VFrame::SHMGET;
569                 this->data = (unsigned char*)shmat(shmid, NULL, 0);
570                 this->shmid = shmid;
571         }
572
573         this->compressed_allocated = data_allocated;
574         this->compressed_size = data_size;
575 }
576
577
578 // Reallocate uncompressed buffer with or without alpha
579 int VFrame::reallocate(
580         unsigned char *data,
581         int shmid,
582         long y_offset,
583         long u_offset,
584         long v_offset,
585         int w,
586         int h,
587         int color_model,
588         long bytes_per_line)
589 {
590 //      if(shmid == 0) printf("VFrame::reallocate %d shmid=%d\n", __LINE__, shmid);
591         clear_objects(0);
592 //      reset_parameters(0);
593         allocate_data(data,
594                 shmid,
595                 y_offset,
596                 u_offset,
597                 v_offset,
598                 w,
599                 h,
600                 color_model,
601                 bytes_per_line);
602         return 0;
603 }
604
605 int VFrame::allocate_compressed_data(long bytes)
606 {
607         if(bytes < 1) return 1;
608
609 // Want to preserve original contents
610         if(data && compressed_allocated < bytes)
611         {
612                 int new_shmid = -1;
613                 unsigned char *new_data = 0;
614                 if(BC_WindowBase::get_resources()->use_vframe_shm() && use_shm)
615                 {
616                         new_shmid = shmget(IPC_PRIVATE,
617                                 bytes,
618                                 IPC_CREAT | 0777);
619                         new_data = (unsigned char*)shmat(new_shmid, NULL, 0);
620                         shmctl(new_shmid, IPC_RMID, 0);
621                 }
622                 else
623                 {
624 // Have to use malloc for libpng
625                         new_data = (unsigned char *)malloc(bytes);
626                 }
627
628                 bcopy(data, new_data, compressed_allocated);
629 UNBUFFER(data);
630
631                 if(memory_type == VFrame::PRIVATE)
632                 {
633                         if(shmid > 0) {
634                                 if(data)
635                                         shmdt(data);
636                         }
637                         else
638                                 free(data);
639                 }
640                 else
641                 if(memory_type == VFrame::SHMGET)
642                 {
643                         if(data)
644                                 shmdt(data);
645                 }
646
647                 data = new_data;
648                 shmid = new_shmid;
649                 compressed_allocated = bytes;
650         }
651         else
652         if(!data)
653         {
654                 if(BC_WindowBase::get_resources()->use_vframe_shm() && use_shm)
655                 {
656                         shmid = shmget(IPC_PRIVATE,
657                                 bytes,
658                                 IPC_CREAT | 0777);
659                         data = (unsigned char*)shmat(shmid, NULL, 0);
660                         shmctl(shmid, IPC_RMID, 0);
661                 }
662                 else
663                 {
664 // Have to use malloc for libpng
665                         data = (unsigned char *)malloc(bytes);
666                 }
667
668                 compressed_allocated = bytes;
669                 compressed_size = 0;
670         }
671
672         return 0;
673 }
674
675 int VFramePng::read_png(const unsigned char *data, long sz, double xscale, double yscale)
676 {
677 // Test for RAW format
678         if(data[0] == 'R' && data[1] == 'A' && data[2] == 'W' && data[3] == ' ') {
679                 int new_color_model = BC_RGBA8888;
680                 w = data[4] | (data[5] << 8) | (data[6]  << 16) | (data[7]  << 24);
681                 h = data[8] | (data[9] << 8) | (data[10] << 16) | (data[11] << 24);
682                 int components = data[12];
683                 new_color_model = components == 3 ? BC_RGB888 : BC_RGBA8888;
684 // This shares the data directly
685 //              reallocate(data + 20, 0, 0, 0, w, h, new_color_model, -1);
686
687 // Can't use shared data for theme since button constructions overlay the
688 // images directly.
689                 reallocate(NULL, -1, 0, 0, 0, w, h, new_color_model, -1);
690                 memcpy(get_data(), data + 16, w * h * components);
691
692         }
693         else if(data[0] == 0x89 && data[1] == 'P' && data[2] == 'N' && data[3] == 'G') {
694                 int have_alpha = 0;
695                 png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
696                 png_infop info_ptr = png_create_info_struct(png_ptr);
697                 int new_color_model;
698
699                 image_offset = 0;
700                 image = data;  image_size = sz;
701                 png_set_read_fn(png_ptr, this, PngReadFunction::png_read_function);
702                 png_read_info(png_ptr, info_ptr);
703
704                 w = png_get_image_width(png_ptr, info_ptr);
705                 h = png_get_image_height(png_ptr, info_ptr);
706
707                 int src_color_model = png_get_color_type(png_ptr, info_ptr);
708
709                 /* tell libpng to strip 16 bit/color files down to 8 bits/color */
710                 png_set_strip_16(png_ptr);
711
712                 /* extract multiple pixels with bit depths of 1, 2, and 4 from a single
713                  * byte into separate bytes (useful for paletted and grayscale images).
714                  */
715                 png_set_packing(png_ptr);
716
717                 /* expand paletted colors into true RGB triplets */
718                 if (src_color_model == PNG_COLOR_TYPE_PALETTE)
719                         png_set_expand(png_ptr);
720
721                 /* expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel */
722                 if (src_color_model == PNG_COLOR_TYPE_GRAY && png_get_bit_depth(png_ptr, info_ptr) < 8)
723                         png_set_expand(png_ptr);
724
725                 if (src_color_model == PNG_COLOR_TYPE_GRAY ||
726                     src_color_model == PNG_COLOR_TYPE_GRAY_ALPHA)
727                         png_set_gray_to_rgb(png_ptr);
728
729                 /* expand paletted or RGB images with transparency to full alpha channels
730                  * so the data will be available as RGBA quartets */
731                 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)){
732                         have_alpha = 1;
733                         png_set_expand(png_ptr);
734                 }
735
736                 switch(src_color_model)
737                 {
738                         case PNG_COLOR_TYPE_GRAY:
739                         case PNG_COLOR_TYPE_RGB:
740                                 new_color_model = BC_RGB888;
741                                 break;
742
743                         case PNG_COLOR_TYPE_GRAY_ALPHA:
744                         case PNG_COLOR_TYPE_RGB_ALPHA:
745                         default:
746                                 new_color_model = BC_RGBA8888;
747                                 break;
748
749                         case PNG_COLOR_TYPE_PALETTE:
750                                 if(have_alpha)
751                                         new_color_model = BC_RGBA8888;
752                                 else
753                                         new_color_model = BC_RGB888;
754                 }
755
756                 reallocate(NULL, -1, 0, 0, 0, w, h, new_color_model, -1);
757
758 //printf("VFrame::read_png %d %d %d %p\n", __LINE__, w, h, get_rows());
759                 png_read_image(png_ptr, get_rows());
760                 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
761         }
762         else {
763                 printf("VFrame::read_png %d: unknown file format"
764                         " 0x%02x 0x%02x 0x%02x 0x%02x\n",
765                         __LINE__, data[4], data[5], data[6], data[7]);
766                 return 1;
767         }
768         int ww = w * xscale, hh = h * yscale;
769         if( ww != w || hh != h ) {
770                 VFrame vframe(*this);
771                 reallocate(NULL, -1, 0, 0, 0, ww, hh, color_model, -1);
772                 transfer_from(&vframe);
773         }
774         return 0;
775 }
776
777 int VFrame::write_png(const char *path)
778 {
779         png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
780         png_infop info_ptr = png_create_info_struct(png_ptr);
781         FILE *out_fd = fopen(path, "w");
782         if(!out_fd)
783         {
784                 printf("VFrame::write_png %d %s %s\n", __LINE__, path, strerror(errno));
785                 return 1;
786         }
787
788         int png_cmodel = PNG_COLOR_TYPE_RGB;
789         switch(get_color_model())
790         {
791                 case BC_RGB888:
792                 case BC_YUV888:
793                         png_cmodel = PNG_COLOR_TYPE_RGB;
794                         break;
795
796                 case BC_RGBA8888:
797                 case BC_YUVA8888:
798                         png_cmodel = PNG_COLOR_TYPE_RGB_ALPHA;
799                         break;
800
801                 case BC_A8:
802                         png_cmodel = PNG_COLOR_TYPE_GRAY;
803                         break;
804         }
805
806         png_init_io(png_ptr, out_fd);
807         png_set_compression_level(png_ptr, 9);
808         png_set_IHDR(png_ptr,
809                 info_ptr,
810                 get_w(),
811                 get_h(),
812         8,
813                 png_cmodel,
814                 PNG_INTERLACE_NONE,
815                 PNG_COMPRESSION_TYPE_DEFAULT,
816                 PNG_FILTER_TYPE_DEFAULT);
817         png_write_info(png_ptr, info_ptr);
818         png_write_image(png_ptr, get_rows());
819         png_write_end(png_ptr, info_ptr);
820         png_destroy_write_struct(&png_ptr, &info_ptr);
821         fclose(out_fd);
822         return 0;
823 }
824
825
826 #define ZERO_YUV(components, type, max) \
827 { \
828         for(int i = 0; i < h; i++) \
829         { \
830                 type *row = (type*)get_rows()[i]; \
831                 for(int j = 0; j < w; j++) \
832                 { \
833                         row[j * components] = 0; \
834                         row[j * components + 1] = (max + 1) / 2; \
835                         row[j * components + 2] = (max + 1) / 2; \
836                         if(components == 4) row[j * components + 3] = 0; \
837                 } \
838         } \
839 }
840
841 int VFrame::clear_frame()
842 {
843         int sz = w * h;
844 //printf("VFrame::clear_frame %d\n", __LINE__);
845         switch(color_model) {
846         case BC_COMPRESSED:
847                 break;
848
849         case BC_YUV410P:
850                 bzero(get_y(), sz);
851                 bzero(get_u(), w / 4 * h / 4);
852                 bzero(get_v(), w / 4 * h / 4);
853                 break;
854
855         case BC_YUV411P:
856         case BC_YUV420P:
857         case BC_YUV420PI:
858                 bzero(get_y(), sz);
859                 bzero(get_u(), sz / 4);
860                 bzero(get_v(), sz / 4);
861                 break;
862
863         case BC_YUV422P:
864                 bzero(get_y(), sz);
865                 bzero(get_u(), sz / 2);
866                 bzero(get_v(), sz / 2);
867                 break;
868
869         case BC_RGBA_FLOATP: if( a ) {
870                 float *ap = (float *)a;
871                 for( int i=sz; --i>=0; ++ap ) *ap = 1.f; }
872         case BC_RGB_FLOATP: {
873                 float *rp = (float *)y;
874                 for( int i=sz; --i>=0; ++rp ) *rp = 0.f;
875                 float *gp = (float *)u;
876                 for( int i=sz; --i>=0; ++gp ) *gp = 0.f;
877                 float *bp = (float *)v;
878                 for( int i=sz; --i>=0; ++bp ) *bp = 0.f;
879                 break; }
880         case BC_YUV444P:
881                 bzero(get_y(), sz);
882                 bzero(get_u(), sz);
883                 bzero(get_v(), sz);
884                 break;
885
886         case BC_YUV888:
887                 ZERO_YUV(3, unsigned char, 0xff);
888                 break;
889
890         case BC_YUVA8888:
891                 ZERO_YUV(4, unsigned char, 0xff);
892                 break;
893
894         case BC_YUV161616:
895                 ZERO_YUV(3, uint16_t, 0xffff);
896                 break;
897
898         case BC_YUVA16161616:
899                 ZERO_YUV(4, uint16_t, 0xffff);
900                 break;
901
902         default:
903                 bzero(data, calculate_data_size(w, h, bytes_per_line, color_model));
904                 break;
905         }
906         return 0;
907 }
908
909 void VFrame::rotate90()
910 {
911 // Allocate new frame
912         int new_w = h, new_h = w, new_bytes_per_line = bytes_per_pixel * new_w;
913         unsigned char *new_data = new unsigned char[calculate_data_size(new_w, new_h, new_bytes_per_line, color_model)];
914         unsigned char **new_rows = new unsigned char*[new_h];
915         for(int i = 0; i < new_h; i++)
916                 new_rows[i] = &new_data[new_bytes_per_line * i];
917
918 // Copy data
919         for(int in_y = 0, out_x = new_w - 1; in_y < h; in_y++, out_x--)
920         {
921                 for(int in_x = 0, out_y = 0; in_x < w; in_x++, out_y++)
922                 {
923                         for(int k = 0; k < bytes_per_pixel; k++)
924                         {
925                                 new_rows[out_y][out_x * bytes_per_pixel + k] =
926                                         rows[in_y][in_x * bytes_per_pixel + k];
927                         }
928                 }
929         }
930
931 // Swap frames
932         clear_objects(0);
933         data = new_data;
934         rows = new_rows;
935         bytes_per_line = new_bytes_per_line;
936         w = new_w;
937         h = new_h;
938 }
939
940 void VFrame::rotate270()
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 = 0; in_y < h; in_y++, out_x++)
951         {
952                 for(int in_x = 0, out_y = new_h - 1; 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::flip_vert()
972 {
973         unsigned char *temp = new unsigned char[bytes_per_line];
974         for(int i = 0, j = h - 1; i < j; i++, j--)
975         {
976                 memcpy(temp, rows[j], bytes_per_line);
977                 memcpy(rows[j], rows[i], bytes_per_line);
978                 memcpy(rows[i], temp, bytes_per_line);
979         }
980         delete [] temp;
981 }
982
983 void VFrame::flip_horiz()
984 {
985         unsigned char temp[32];
986         for(int i = 0; i < h; i++)
987         {
988                 unsigned char *row = rows[i];
989                 for(int j = 0; j < bytes_per_line / 2; j += bytes_per_pixel)
990                 {
991                         memcpy(temp, row + j, bytes_per_pixel);
992                         memcpy(row + j, row + bytes_per_line - j - bytes_per_pixel, bytes_per_pixel);
993                         memcpy(row + bytes_per_line - j - bytes_per_pixel, temp, bytes_per_pixel);
994                 }
995         }
996 }
997
998
999
1000 int VFrame::copy_from(VFrame *frame)
1001 {
1002         int w = MIN(this->w, frame->get_w());
1003         int h = MIN(this->h, frame->get_h());
1004         timestamp = frame->timestamp;
1005
1006         switch(frame->color_model)
1007         {
1008                 case BC_COMPRESSED:
1009                         allocate_compressed_data(frame->compressed_size);
1010                         memcpy(data, frame->data, frame->compressed_size);
1011                         this->compressed_size = frame->compressed_size;
1012                         break;
1013
1014                 case BC_YUV410P:
1015                         memcpy(get_y(), frame->get_y(), w * h);
1016                         memcpy(get_u(), frame->get_u(), w / 4 * h / 4);
1017                         memcpy(get_v(), frame->get_v(), w / 4 * h / 4);
1018                         break;
1019
1020                 case BC_YUV420P:
1021                 case BC_YUV420PI:
1022                 case BC_YUV411P:
1023 //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());
1024                         memcpy(get_y(), frame->get_y(), w * h);
1025                         memcpy(get_u(), frame->get_u(), w * h / 4);
1026                         memcpy(get_v(), frame->get_v(), w * h / 4);
1027                         break;
1028
1029                 case BC_YUV422P:
1030 //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());
1031                         memcpy(get_y(), frame->get_y(), w * h);
1032                         memcpy(get_u(), frame->get_u(), w * h / 2);
1033                         memcpy(get_v(), frame->get_v(), w * h / 2);
1034                         break;
1035
1036                 case BC_YUV444P:
1037 //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());
1038                         memcpy(get_y(), frame->get_y(), w * h);
1039                         memcpy(get_u(), frame->get_u(), w * h);
1040                         memcpy(get_v(), frame->get_v(), w * h);
1041                         break;
1042                 default:
1043 // printf("VFrame::copy_from %d\n", calculate_data_size(w,
1044 //                              h,
1045 //                              -1,
1046 //                              frame->color_model));
1047 // Copy without extra 4 bytes in case the source is a hardware device
1048                         memmove(data, frame->data, get_data_size());
1049                         break;
1050         }
1051
1052         return 0;
1053 }
1054
1055 int VFrame::transfer_from(VFrame *that, int bg_color, int in_x, int in_y, int in_w, int in_h)
1056 {
1057         if( this->get_color_model() == that->get_color_model() &&
1058             this->get_w() == that->get_w() && this->get_h() == that->get_h() &&
1059             this->get_bytes_per_line() == that->get_bytes_per_line() )
1060                 return this->copy_from(that);
1061
1062         timestamp = that->timestamp;
1063 #if 0
1064         BC_CModels::transfer(
1065                 this->get_rows(), that->get_rows(),          // Packed data out/in
1066                 this->get_y(), this->get_u(), this->get_v(), // Planar data out/in
1067                 that->get_y(), that->get_u(), that->get_v(),
1068                 0, 0, that->get_w(), that->get_h(),          // Dimensions in/out
1069                 0, 0, this->get_w(), this->get_h(),
1070                 that->get_color_model(), this->get_color_model(), // Color models in/out
1071                 bg_color,                                    // alpha blend bg_color
1072                 that->get_bytes_per_line(),
1073                 this->get_bytes_per_line());                 // rowspans (of luma for YUV)
1074 #else
1075         unsigned char *in_ptrs[4], *out_ptrs[4];
1076         unsigned char **inp, **outp;
1077         if( BC_CModels::is_planar(that->get_color_model()) ) {
1078                 in_ptrs[0] = that->get_y();
1079                 in_ptrs[1] = that->get_u();
1080                 in_ptrs[2] = that->get_v();
1081                 in_ptrs[3] = that->get_a();
1082                 inp = in_ptrs;
1083         }
1084         else
1085                 inp = that->get_rows();
1086         if( BC_CModels::is_planar(this->get_color_model()) ) {
1087                 out_ptrs[0] = this->get_y();
1088                 out_ptrs[1] = this->get_u();
1089                 out_ptrs[2] = this->get_v();
1090                 out_ptrs[3] = this->get_a();
1091                 outp = out_ptrs;
1092         }
1093         else
1094                 outp = this->get_rows();
1095         BC_CModels::transfer(outp, this->get_color_model(),
1096                         0, 0, this->get_w(), this->get_h(),
1097                         this->get_bytes_per_line(),
1098                 inp, that->get_color_model(),
1099                         in_x, in_y, in_w, in_h,
1100                         that->get_bytes_per_line(),
1101                 bg_color);
1102 #endif
1103         return 0;
1104 }
1105
1106
1107
1108
1109
1110
1111 #define OVERLAY(type, max, components) \
1112 { \
1113         type **in_rows = (type**)src->get_rows(); \
1114         type **out_rows = (type**)get_rows(); \
1115         int in_w = src->get_w(); \
1116         int in_h = src->get_h(); \
1117  \
1118         for(int i = 0; i < in_h; i++) \
1119         { \
1120                 if(i + out_y1 >= 0 && i + out_y1 < h) \
1121                 { \
1122                         type *src_row = in_rows[i]; \
1123                         type *dst_row = out_rows[i + out_y1] + out_x1 * components; \
1124  \
1125                         for(int j = 0; j < in_w; j++) \
1126                         { \
1127                                 if(j + out_x1 >= 0 && j + out_x1 < w) \
1128                                 { \
1129                                         int opacity = src_row[3]; \
1130                                         int transparency = dst_row[3] * (max - src_row[3]) / max; \
1131                                         dst_row[0] = (transparency * dst_row[0] + opacity * src_row[0]) / max; \
1132                                         dst_row[1] = (transparency * dst_row[1] + opacity * src_row[1]) / max; \
1133                                         dst_row[2] = (transparency * dst_row[2] + opacity * src_row[2]) / max; \
1134                                         dst_row[3] = MAX(dst_row[3], src_row[3]); \
1135                                 } \
1136  \
1137                                 dst_row += components; \
1138                                 src_row += components; \
1139                         } \
1140                 } \
1141         } \
1142 }
1143
1144
1145 void VFrame::overlay(VFrame *src,
1146                 int out_x1,
1147                 int out_y1)
1148 {
1149         switch(get_color_model())
1150         {
1151                 case BC_RGBA8888:
1152                         OVERLAY(unsigned char, 0xff, 4);
1153                         break;
1154         }
1155 }
1156
1157
1158
1159 int VFrame::get_scale_tables(int *column_table, int *row_table,
1160                         int in_x1, int in_y1, int in_x2, int in_y2,
1161                         int out_x1, int out_y1, int out_x2, int out_y2)
1162 {
1163         int i;
1164         float w_in = in_x2 - in_x1;
1165         float h_in = in_y2 - in_y1;
1166         int w_out = out_x2 - out_x1;
1167         int h_out = out_y2 - out_y1;
1168
1169         float hscale = w_in / w_out;
1170         float vscale = h_in / h_out;
1171
1172         for(i = 0; i < w_out; i++)
1173         {
1174                 column_table[i] = (int)(hscale * i);
1175         }
1176
1177         for(i = 0; i < h_out; i++)
1178         {
1179                 row_table[i] = (int)(vscale * i) + in_y1;
1180         }
1181         return 0;
1182 }
1183
1184 void VFrame::push_prev_effect(const char *name)
1185 {
1186         char *ptr;
1187         prev_effects.append(ptr = new char[strlen(name) + 1]);
1188         strcpy(ptr, name);
1189         if(prev_effects.total > MAX_STACK_ELEMENTS) prev_effects.remove_object(0);
1190 }
1191
1192 void VFrame::pop_prev_effect()
1193 {
1194         if(prev_effects.total)
1195                 prev_effects.remove_object(prev_effects.last());
1196 }
1197
1198 void VFrame::push_next_effect(const char *name)
1199 {
1200         char *ptr;
1201         next_effects.append(ptr = new char[strlen(name) + 1]);
1202         strcpy(ptr, name);
1203         if(next_effects.total > MAX_STACK_ELEMENTS) next_effects.remove_object(0);
1204 }
1205
1206 void VFrame::pop_next_effect()
1207 {
1208         if(next_effects.total)
1209                 next_effects.remove_object(next_effects.last());
1210 }
1211
1212 const char* VFrame::get_next_effect(int number)
1213 {
1214         if(!next_effects.total) return "";
1215         else
1216         if(number > next_effects.total - 1) number = next_effects.total - 1;
1217
1218         return next_effects.values[next_effects.total - number - 1];
1219 }
1220
1221 const char* VFrame::get_prev_effect(int number)
1222 {
1223         if(!prev_effects.total) return "";
1224         else
1225         if(number > prev_effects.total - 1) number = prev_effects.total - 1;
1226
1227         return prev_effects.values[prev_effects.total - number - 1];
1228 }
1229
1230 BC_Hash* VFrame::get_params()
1231 {
1232         return params;
1233 }
1234
1235 void VFrame::clear_stacks()
1236 {
1237         next_effects.remove_all_objects();
1238         prev_effects.remove_all_objects();
1239         params->clear();
1240         status = 1;
1241 }
1242
1243 void VFrame::copy_params(VFrame *src)
1244 {
1245         status = src->status;
1246         params->copy_from(src->params);
1247 }
1248
1249 void VFrame::copy_stacks(VFrame *src)
1250 {
1251         clear_stacks();
1252
1253         for(int i = 0; i < src->next_effects.total; i++)
1254         {
1255                 char *ptr;
1256                 next_effects.append(ptr = new char[strlen(src->next_effects.values[i]) + 1]);
1257                 strcpy(ptr, src->next_effects.values[i]);
1258         }
1259         for(int i = 0; i < src->prev_effects.total; i++)
1260         {
1261                 char *ptr;
1262                 prev_effects.append(ptr = new char[strlen(src->prev_effects.values[i]) + 1]);
1263                 strcpy(ptr, src->prev_effects.values[i]);
1264         }
1265
1266         copy_params(src);
1267 }
1268
1269 int VFrame::equal_stacks(VFrame *src)
1270 {
1271         for(int i = 0; i < src->next_effects.total && i < next_effects.total; i++)
1272         {
1273                 if(strcmp(src->next_effects.values[i], next_effects.values[i])) return 0;
1274         }
1275
1276         for(int i = 0; i < src->prev_effects.total && i < prev_effects.total; i++)
1277         {
1278                 if(strcmp(src->prev_effects.values[i], prev_effects.values[i])) return 0;
1279         }
1280
1281         if(!params->equivalent(src->params)) return 0;
1282         return 1;
1283 }
1284
1285 void VFrame::dump_stacks()
1286 {
1287         printf("VFrame::dump_stacks\n");
1288         printf("        next_effects:\n");
1289         for(int i = next_effects.total - 1; i >= 0; i--)
1290                 printf("                %s\n", next_effects.values[i]);
1291         printf("        prev_effects:\n");
1292         for(int i = prev_effects.total - 1; i >= 0; i--)
1293                 printf("                %s\n", prev_effects.values[i]);
1294 }
1295
1296 void VFrame::dump_params()
1297 {
1298         params->dump();
1299 }
1300
1301 void VFrame::dump()
1302 {
1303         printf("VFrame::dump %d this=%p\n", __LINE__, this);
1304         printf("    w=%d h=%d colormodel=%d rows=%p use_shm=%d shmid=%d\n",
1305                 w, h, color_model, rows, use_shm, shmid);
1306 }
1307
1308
1309 int VFrame::get_memory_usage()
1310 {
1311         if(get_compressed_allocated()) return get_compressed_allocated();
1312         return get_h() * get_bytes_per_line();
1313 }
1314
1315 void VFrame::draw_pixel(int x, int y)
1316 {
1317         if(!(x >= 0 && y >= 0 && x < get_w() && y < get_h())) return;
1318
1319 #define DRAW_PIXEL(x, y, components, do_yuv, max, type) \
1320 { \
1321         type **rows = (type**)get_rows(); \
1322         rows[y][x * components] = max - rows[y][x * components]; \
1323         if(!do_yuv) \
1324         { \
1325                 rows[y][x * components + 1] = max - rows[y][x * components + 1]; \
1326                 rows[y][x * components + 2] = max - rows[y][x * components + 2]; \
1327         } \
1328         else \
1329         { \
1330                 rows[y][x * components + 1] = (max / 2 + 1) - rows[y][x * components + 1]; \
1331                 rows[y][x * components + 2] = (max / 2 + 1) - rows[y][x * components + 2]; \
1332         } \
1333         if(components == 4) \
1334                 rows[y][x * components + 3] = max; \
1335 }
1336
1337
1338         switch(get_color_model())
1339         {
1340                 case BC_RGB888:
1341                         DRAW_PIXEL(x, y, 3, 0, 0xff, unsigned char);
1342                         break;
1343                 case BC_RGBA8888:
1344                         DRAW_PIXEL(x, y, 4, 0, 0xff, unsigned char);
1345                         break;
1346                 case BC_RGB_FLOAT:
1347                         DRAW_PIXEL(x, y, 3, 0, 1.0, float);
1348                         break;
1349                 case BC_RGBA_FLOAT:
1350                         DRAW_PIXEL(x, y, 4, 0, 1.0, float);
1351                         break;
1352                 case BC_YUV888:
1353                         DRAW_PIXEL(x, y, 3, 1, 0xff, unsigned char);
1354                         break;
1355                 case BC_YUVA8888:
1356                         DRAW_PIXEL(x, y, 4, 1, 0xff, unsigned char);
1357                         break;
1358                 case BC_RGB161616:
1359                         DRAW_PIXEL(x, y, 3, 0, 0xffff, uint16_t);
1360                         break;
1361                 case BC_YUV161616:
1362                         DRAW_PIXEL(x, y, 3, 1, 0xffff, uint16_t);
1363                         break;
1364                 case BC_RGBA16161616:
1365                         DRAW_PIXEL(x, y, 4, 0, 0xffff, uint16_t);
1366                         break;
1367                 case BC_YUVA16161616:
1368                         DRAW_PIXEL(x, y, 4, 1, 0xffff, uint16_t);
1369                         break;
1370         }
1371 }
1372
1373
1374 void VFrame::draw_line(int x1, int y1, int x2, int y2)
1375 {
1376         int w = labs(x2 - x1);
1377         int h = labs(y2 - y1);
1378 //printf("FindObjectMain::draw_line 1 %d %d %d %d\n", x1, y1, x2, y2);
1379
1380         if(!w && !h)
1381         {
1382                 draw_pixel(x1, y1);
1383         }
1384         else
1385         if(w > h)
1386         {
1387 // Flip coordinates so x1 < x2
1388                 if(x2 < x1)
1389                 {
1390                         y2 ^= y1;
1391                         y1 ^= y2;
1392                         y2 ^= y1;
1393                         x1 ^= x2;
1394                         x2 ^= x1;
1395                         x1 ^= x2;
1396                 }
1397                 int numerator = y2 - y1;
1398                 int denominator = x2 - x1;
1399                 for(int i = x1; i <= x2; i++)
1400                 {
1401                         int y = y1 + (int64_t)(i - x1) * (int64_t)numerator / (int64_t)denominator;
1402                         draw_pixel(i, y);
1403                 }
1404         }
1405         else
1406         {
1407 // Flip coordinates so y1 < y2
1408                 if(y2 < y1)
1409                 {
1410                         y2 ^= y1;
1411                         y1 ^= y2;
1412                         y2 ^= y1;
1413                         x1 ^= x2;
1414                         x2 ^= x1;
1415                         x1 ^= x2;
1416                 }
1417                 int numerator = x2 - x1;
1418                 int denominator = y2 - y1;
1419                 for(int i = y1; i <= y2; i++)
1420                 {
1421                         int x = x1 + (int64_t)(i - y1) * (int64_t)numerator / (int64_t)denominator;
1422                         draw_pixel(x, i);
1423                 }
1424         }
1425 //printf("FindObjectMain::draw_line 2\n");
1426 }
1427
1428 void VFrame::draw_rect(int x1, int y1, int x2, int y2)
1429 {
1430         draw_line(x1, y1, x2, y1);
1431         draw_line(x2, y1 + 1, x2, y2);
1432         draw_line(x2 - 1, y2, x1, y2);
1433         draw_line(x1, y2 - 1, x1, y1 + 1);
1434 }
1435
1436 #define ARROW_SIZE 10
1437 void VFrame::draw_arrow(int x1, int y1, int x2, int y2)
1438 {
1439         double angle = atan((float)(y2 - y1) / (float)(x2 - x1));
1440         double angle1 = angle + (float)145 / 360 * 2 * 3.14159265;
1441         double angle2 = angle - (float)145 / 360 * 2 * 3.14159265;
1442         int x3;
1443         int y3;
1444         int x4;
1445         int y4;
1446         if(x2 < x1)
1447         {
1448                 x3 = x2 - (int)(ARROW_SIZE * cos(angle1));
1449                 y3 = y2 - (int)(ARROW_SIZE * sin(angle1));
1450                 x4 = x2 - (int)(ARROW_SIZE * cos(angle2));
1451                 y4 = y2 - (int)(ARROW_SIZE * sin(angle2));
1452         }
1453         else
1454         {
1455                 x3 = x2 + (int)(ARROW_SIZE * cos(angle1));
1456                 y3 = y2 + (int)(ARROW_SIZE * sin(angle1));
1457                 x4 = x2 + (int)(ARROW_SIZE * cos(angle2));
1458                 y4 = y2 + (int)(ARROW_SIZE * sin(angle2));
1459         }
1460
1461 // Main vector
1462         draw_line(x1, y1, x2, y2);
1463 //      draw_line(x1, y1 + 1, x2, y2 + 1);
1464
1465 // Arrow line
1466         if(abs(y2 - y1) || abs(x2 - x1)) draw_line(x2, y2, x3, y3);
1467 // Arrow line
1468         if(abs(y2 - y1) || abs(x2 - x1)) draw_line(x2, y2, x4, y4);
1469 }
1470
1471
1472
1473