r1024: Add optional graduations to waveform.
[cinelerra_cv.git] / guicast / rotateframe.C
blob9d7601e8d1592277b184c8842564de931b0566b8
1 #include "condition.h"
2 #include "rotateframe.h"
3 #include "vframe.h"
5 #include <math.h>
6 #include <stdint.h>
7 #include <stdio.h>
8 #include <unistd.h>
10 #define SQR(x) ((x) * (x))
12 RotateFrame::RotateFrame(int cpus, int width, int height)
14         int y1, y2, y_increment;
15         y_increment = height / cpus;
16         y1 = 0;
17         this->cpus = cpus;
19         engine = new RotateEngine*[cpus];
20         for(int i = 0; i < cpus; i++)
21         {
22                 y2 = y1 + y_increment;
23                 if(i == cpus - 1 && y2 < height - 1) y2 = height - 1;
24                 engine[i] = new RotateEngine(this, y1, y2);
25                 engine[i]->start();
26                 y1 += y_increment;
27         }
29         float_matrix = 0;
30         int_matrix = 0;
31         int_rows = 0;
32         float_rows = 0;
33         last_angle = 0;
34         last_interpolate = 0;
37 RotateFrame::~RotateFrame()
39         for(int i = 0; i < cpus; i++)
40         {
41                 delete engine[i];
42         }
43         delete [] engine;
44         if(float_matrix) delete [] float_matrix;
45         if(int_matrix) delete [] int_matrix;
46         if(float_rows) delete [] float_rows;
47         if(int_rows) delete [] int_rows;
51 void RotateFrame::rotate(VFrame *output, 
52         VFrame *input, 
53         double angle, 
54         int interpolate)
56         this->angle = angle;
57         this->output = output;
58         this->input = input;
59         this->interpolate = interpolate;
61         if(angle != 0)
62         {
63         if(angle == 90 || angle == 180 || angle == 270)
64             rotate_rightangle(input, 
65                                 output, 
66                                 (int)angle);
67                 else
68                 {
69                         rotate_obliqueangle(input, 
70                                 output, 
71                                 angle,
72                                 interpolate);
73                 }
74         }
75         else
76 // Data never processed so copy if necessary
77         {
78                 output->copy_from(input);
79         }
80         this->last_angle = angle;
83 int RotateFrame::get_rightdimensions(VFrame *frame, 
84         int &diameter, 
85         int &in_x1, 
86         int &in_y1, 
87         int &in_x2, 
88         int &in_y2, 
89         int &out_x1, 
90         int &out_y1, 
91         int &out_x2, 
92         int &out_y2)
94     diameter = frame->get_w() < frame->get_h() ? frame->get_w() : frame->get_h();
95     out_x1 = in_x1 = frame->get_w() / 2 - diameter / 2;
96     out_x2 = in_x2 = in_x1 + diameter - 1;
97     out_y1 = in_y1 = frame->get_h() / 2 - diameter / 2;
98     out_y2 = in_y2 = in_y1 + diameter - 1;
99         return 0;
104 #define ROTATE_RIGHTANGLE(type, components) \
105 { \
106         type **input_rows = (type**)input->get_rows(); \
107         type **output_rows = (type**)output->get_rows(); \
108         int height = output->get_h(); \
109         int width = output->get_w(); \
111         switch(angle) \
112         { \
113                 case 90: \
114                         get_rightdimensions(input, \
115                                 diameter,  \
116                                 in_x1,  \
117                                 in_y1,  \
118                                 in_x2,  \
119                                 in_y2,  \
120                                 out_x1,  \
121                                 out_y1,  \
122                                 out_x2,  \
123                                 out_y2); \
124             while(in_x2 > in_x1) \
125             { \
126                 diameter = in_x2 - in_x1; \
127                 for(int i = 0; i < diameter; i++) \
128                 { \
129                     type temp_pixel[components]; \
130                                         for(int j = 0; j < components; j++) \
131                                         { \
132                                                 temp_pixel[j] = input_rows[in_y1 + i][in_x2 * components + j]; \
134                         output_rows[in_y1 + i][in_x2 * components + j]   = input_rows[in_y1][(in_x1 + i) * components + j]; \
135                         output_rows[in_y1][(in_x1 + i) * components + j] = input_rows[in_y2 - i][in_x1 * components + j]; \
136                         output_rows[in_y2 - i][in_x1 * components + j]   = input_rows[in_y2][(in_x2 - i) * components + j]; \
137                         output_rows[in_y2][(in_x2 - i) * components + j] = temp_pixel[j]; \
138                                         } \
139                 } \
141                 in_x2--; \
142                 in_x1++; \
143                 in_y2--; \
144                 in_y1++; \
145             } \
146                         break; \
148         case 180: \
149                 for(int i = 0, j = height - 1; i < j; i++, j--) \
150             { \
151                 for(int k = 0, l = width - 1; k < width; k++, l--) \
152                 { \
153                     type temp_pixel[components]; \
154                                         for(int m = 0; m < components; m++) \
155                                         { \
156                                 temp_pixel[m] = input_rows[j][k * components + m]; \
157                         output_rows[j][k * components + m] = input_rows[i][l * components + m]; \
158                         output_rows[i][l * components + m] = temp_pixel[m]; \
159                                         } \
160                 } \
161             } \
162                         break; \
164                 case 270: \
165                         get_rightdimensions(input, \
166                                 diameter,  \
167                                 in_x1,  \
168                                 in_y1,  \
169                                 in_x2,  \
170                                 in_y2,  \
171                                 out_x1,  \
172                                 out_y1,  \
173                                 out_x2,  \
174                                 out_y2); \
176             while(in_x2 > in_x1) \
177             { \
178                 diameter = in_x2 - in_x1; \
179                 for(int i = 0; i < diameter; i++) \
180                 { \
181                     type temp_pixel[components]; \
182                                         for(int j = 0; j < components; j++) \
183                                         { \
184                         temp_pixel[j] = input_rows[in_y1 + i][in_x1 * components + j]; \
185                         output_rows[in_y1 + i][in_x1 * components + j]   = input_rows[in_y1][(in_x2 - i) * components + j]; \
186                         output_rows[in_y1][(in_x2 - i) * components + j] = input_rows[in_y2 - i][in_x2 * components + j]; \
187                         output_rows[in_y2 - i][in_x2 * components + j]   = input_rows[in_y2][(in_x1 + i) * components + j]; \
188                         output_rows[in_y2][(in_x1 + i) * components + j] = temp_pixel[j]; \
189                                         } \
190                 } \
192                 in_x2--; \
193                 in_x1++; \
194                 in_y2--; \
195                 in_y1++; \
196             } \
197                         break; \
198         } \
202 int RotateFrame::rotate_rightangle(VFrame *input, 
203         VFrame *output, 
204         int angle)
206         int in_x1 = 0;
207     int in_y1 = 0;
208     int in_x2 = input->get_w();
209     int in_y2 = input->get_h();
210         int out_x1, out_y1, out_x2, out_y2;
211     int diameter;
213         output->clear_frame();
214         switch(output->get_color_model())
215         {
216                 case BC_RGB888:
217                         ROTATE_RIGHTANGLE(unsigned char, 3);
218                         break;
219                 case BC_RGBA8888:
220                         ROTATE_RIGHTANGLE(unsigned char, 4);
221                         break;
222                 case BC_RGB_FLOAT:
223                         ROTATE_RIGHTANGLE(float, 3);
224                         break;
225                 case BC_RGBA_FLOAT:
226                         ROTATE_RIGHTANGLE(float, 4);
227                         break;
228                 case BC_YUV888:
229                         ROTATE_RIGHTANGLE(unsigned char, 3);
230                         break;
231                 case BC_YUVA8888:
232                         ROTATE_RIGHTANGLE(unsigned char, 4);
233                         break;
234                 case BC_RGB161616:
235                         ROTATE_RIGHTANGLE(uint16_t, 3);
236                         break;
237                 case BC_RGBA16161616:
238                         ROTATE_RIGHTANGLE(uint16_t, 4);
239                         break;
240                 case BC_YUV161616:
241                         ROTATE_RIGHTANGLE(uint16_t, 3);
242                         break;
243                 case BC_YUVA16161616:
244                         ROTATE_RIGHTANGLE(uint16_t, 4);
245                         break;
246         }
247         return 0;
250 int RotateFrame::rotate_obliqueangle(VFrame *input, 
251         VFrame *output, 
252         double angle,
253         int interpolate)
255         int i;
256         int center_x, center_y;
257         int need_matrix = 0;
259         center_x = input->get_w() / 2;
260         center_y = input->get_h() / 2;
262         if(last_angle != angle || 
263                 (interpolate && !float_matrix) || 
264                 (!interpolate && !int_matrix))
265         {
266                 if(interpolate && !float_matrix)
267                 {
268                         float_matrix = new SourceCoord[input->get_w() * input->get_h()];
269                         float_rows = new SourceCoord*[input->get_h()];
270                         for(i = 0; i < input->get_h(); i++)
271                         {
272                                 float_rows[i] = &float_matrix[i * input->get_w()];
273                         }
274                 }
275                 else
276                 if(!interpolate && !int_matrix)
277                 {
278                         int_matrix = new int[input->get_w() * input->get_h()];
279                         int_rows = new int*[input->get_h()];
280                         for(i = 0; i < input->get_h(); i++)
281                         {
282                                 int_rows[i] = &int_matrix[i * input->get_w()];
283                         }
284                 }
286                 need_matrix = 1;
287         }
289         if(last_angle != angle) need_matrix = 1;
290         if(last_interpolate != interpolate) need_matrix = 1;
292         if(need_matrix)
293         {
294 // Last angle != angle implied by first buffer needing to be allocated
295                 for(i = 0; i < cpus; i++)
296                 {
297                         engine[i]->generate_matrix(interpolate);
298                 }
300                 for(i = 0; i < cpus; i++)
301                 {
302                         engine[i]->wait_completion();
303                 }
304         }
306         last_angle = angle;
307         last_interpolate = interpolate;
309 // Perform the rotation
310         for(i = 0; i < cpus; i++)
311         {
312                 engine[i]->perform_rotation(input, output, interpolate);
313         }
315         for(i = 0; i < cpus; i++)
316         {
317                 engine[i]->wait_completion();
318         }
322 #define FILL_CENTER(type, components) \
323 { \
324         type *out_pixel = ((type**)output->get_rows())[center_y] + center_x * components; \
325         type *in_pixel = ((type**)input->get_rows())[center_y] + center_x * components; \
327         out_pixel[0] = in_pixel[0]; \
328         out_pixel[1] = in_pixel[1]; \
329         out_pixel[2] = in_pixel[2]; \
330         if(components == 4) out_pixel[3] = in_pixel[3]; \
337 // Fill center pixel
338         switch(input->get_color_model())
339         {
340                 case BC_RGB_FLOAT:
341                         FILL_CENTER(float, 3)
342                         break;
343                 case BC_RGBA_FLOAT:
344                         FILL_CENTER(float, 4)
345                         break;
346                 case BC_RGB888:
347                 case BC_YUV888:
348                         FILL_CENTER(unsigned char, 3)
349                         break;
350                 case BC_RGBA8888:
351                 case BC_YUVA8888:
352                         FILL_CENTER(unsigned char, 4)
353                         break;
354                 case BC_RGB161616:
355                 case BC_YUV161616:
356                         FILL_CENTER(uint16_t, 3)
357                         break;
358                 case BC_RGBA16161616:
359                 case BC_YUVA16161616:
360                         FILL_CENTER(uint16_t, 4)
361                         break;
362         }
363         return 0;
373 RotateEngine::RotateEngine(RotateFrame *plugin, int row1, int row2) : Thread()
375         this->plugin = plugin;
376         Thread::set_synchronous(1);
377         do_matrix = do_rotation = 0;
378         done = 0;
379         this->row1 = row1;
380         this->row2 = row2;
381         input_lock = new Condition(0, "RotateEngine::input_lock");
382         output_lock = new Condition(0, "RotateEngine::output_lock");
384 RotateEngine::~RotateEngine()
386         if(!done)
387         {
388                 done = 1;
389                 input_lock->unlock();
390                 join();
391         }
392         delete input_lock;
393         delete output_lock;
396 int RotateEngine::generate_matrix(int interpolate)
398         this->do_matrix = 1;
399         this->interpolate = interpolate;
400         input_lock->unlock();
401         return 0;
404 int RotateEngine::perform_rotation(VFrame *input, 
405         VFrame *output, 
406         int interpolate)
408         this->input = input;
409         this->output = output;
410         this->do_rotation = 1;
411         this->interpolate = interpolate;
412         input_lock->unlock();
413         return 0;
417 int RotateEngine::wait_completion()
419         output_lock->lock("RotateEngine::wait_completion");
420         return 0;
423 int RotateEngine::coords_to_pixel(int &input_y, int &input_x)
425         if(input_y < 0) return -1;
426         else
427         if(input_y >= plugin->input->get_h()) return -1;
428         else
429         if(input_x < 0) return -1;
430         else
431         if(input_x >= plugin->input->get_w()) return -1;
432         else
433         return input_y * plugin->input->get_w() + input_x;
436 int RotateEngine::coords_to_pixel(SourceCoord &float_pixel, float &input_y, float &input_x)
438         if(input_y < 0) float_pixel.y = -1;
439         else
440         if(input_y >= plugin->input->get_h()) float_pixel.y = -1;
441         else
442         float_pixel.y = input_y;
444         if(input_x < 0) float_pixel.x = -1;
445         else
446         if(input_x >= plugin->input->get_w()) float_pixel.x = -1;
447         else
448         float_pixel.x = input_x;
452 int RotateEngine::create_matrix()
454 // Polar coords of pixel
455         register double k, l, magnitude, angle, offset_angle, offset_angle2;
456         register double x_offset, y_offset;
457         register int i, j;
458         int *int_row;
459         SourceCoord *float_row;
460         int input_x_i, input_y_i;
461         float input_x_f, input_y_f;
463 //printf("RotateEngine::create_matrix 1\n");
464 // The following is the result of pure trial and error.
465 // Fix the angles
466 // The source pixels are seen as if they were rotated counterclockwise so the sign is OK.
467         offset_angle = -(plugin->angle - 90) / 360 * 2 * M_PI;
468         offset_angle2 = -(plugin->angle - 270) / 360 * 2 * M_PI;
470 // Calculate an offset to add to all the pixels to compensate for the quadrant
471         y_offset = plugin->input->get_h() / 2;
472         x_offset = plugin->input->get_w() / 2;
474         for(i = row1, l = row1 - plugin->input->get_h() / 2; i < row2; i++, l++)
475         {
476                 int l_suare = (int)(l * l);
477                 if(!interpolate)
478                         int_row = plugin->int_rows[i];
479                 else
480                         float_row = plugin->float_rows[i];
482 //printf("RotateEngine::create_matrix 2 %d %f\n", i, l);
483                 for(j = 0, k = -plugin->input->get_w() / 2; 
484                         j < plugin->input->get_w(); 
485                         j++, k++)
486                 {
487 // Add offsets to input
488 // Convert to polar coords
489                         magnitude = sqrt(SQR(k) + l_suare);
490 //printf("RotateEngine::create_matrix 3.2 %f %f\n", k, l);
491                         if(l != 0)
492                                 angle = atan(-k / l);
493                         else
494                         if(k < 0)
495                                 angle = M_PI / 2;
496                         else
497                                 angle = M_PI * 1.5;
498 //printf("RotateEngine::create_matrix 3.3\n");
499 // Rotate
500                         angle += (l < 0) ? offset_angle2 : offset_angle;
502 // Convert back to cartesian coords
503                         if(!interpolate)
504                         {
505                                 input_y_i = (int)(y_offset + magnitude * sin(angle));
506                                 input_x_i = (int)(x_offset + magnitude * cos(angle));
507                                 int_row[j] = coords_to_pixel(input_y_i, input_x_i);
508                         }
509                         else
510                         {
511                                 input_y_f = y_offset + magnitude * sin(angle);
512                                 input_x_f = x_offset + magnitude * cos(angle);
513                                 coords_to_pixel(float_row[j], input_y_f, input_x_f);
514                         }
515                 }
516 //printf("RotateEngine::create_matrix 3\n");
517         }
518 //printf("RotateEngine::create_matrix 2\n");
519         return 0;
522 #define ROTATE_NEAREST(type, components, black_chroma) \
523 { \
524         type **input_rows = (type**)input->get_rows(); \
525         type **output_rows = (type**)output->get_rows(); \
527         for(int i = row1; i < row2; i++) \
528         { \
529                 int *int_row = plugin->int_rows[i]; \
530                 for(int j = 0; j < width; j++) \
531                 { \
532                         if(int_row[j] < 0) \
533                         {  \
534                                 for(int k = 0; k < components; k++) \
535                                         output_rows[i][j * components + k] = 0; \
536                         } \
537                         else \
538                         { \
539                                 for(int k = 0; k < components; k++) \
540                                         output_rows[i][j * components + k] = *(input_rows[0] + int_row[j] * components + k); \
541                         } \
542                 } \
543         } \
546 #define ROTATE_INTERPOLATE(type, components, black_chroma) \
547 { \
548         type zero_pixel[] = { 0, black_chroma, black_chroma, 0 }; \
549         int i, j; \
550         float k, l; \
551         type **input_rows = (type**)input->get_rows(); \
552         type **output_rows = (type**)output->get_rows(); \
553         float x_fraction1, x_fraction2, y_fraction1, y_fraction2; \
554         float fraction1, fraction2, fraction3, fraction4; \
555         int x_pixel1, x_pixel2, y_pixel1, y_pixel2; \
556         type *pixel1, *pixel2, *pixel3, *pixel4; \
558         for(i = row1, k = row1; i < row2; i++, k++) \
559         { \
560                 SourceCoord *float_row = plugin->float_rows[i]; \
561                 for(j = 0, l = 0; j < width; j++, l++) \
562                 { \
563                         if(float_row[j].x < 0 || float_row[j].y < 0) \
564                         { \
565                                 output_rows[i][j * components + 0] = 0; \
566                                 output_rows[i][j * components + 1] = black_chroma; \
567                                 output_rows[i][j * components + 2] = black_chroma; \
568                                 if(components == 4) output_rows[i][j * components + 3] = 0; \
569                         } \
570                         else \
571                         { \
572 /* Interpolate input pixels */ \
573                                 x_pixel1 = (int)float_row[j].x; \
574                                 x_pixel2 = (int)(float_row[j].x + 1); \
575                                 y_pixel1 = (int)(float_row[j].y); \
576                                 y_pixel2 = (int)(float_row[j].y + 1); \
577                                 x_fraction1 = float_row[j].x - x_pixel1; \
578                                 x_fraction2 = (float)x_pixel2 - float_row[j].x; \
579                                 y_fraction1 = float_row[j].y - y_pixel1; \
580                                 y_fraction2 = (float)y_pixel2 - float_row[j].y; \
581 /* By trial and error this fraction order seems to work. */ \
582                                 fraction4 = x_fraction1 * y_fraction1; \
583                                 fraction3 = x_fraction2 * y_fraction1; \
584                                 fraction2 = x_fraction1 * y_fraction2; \
585                                 fraction1 = x_fraction2 * y_fraction2; \
586                                 pixel1 =                                                          &input_rows[y_pixel1][x_pixel1 * components]; \
587                                 pixel2 = (x_pixel2 >= width)                       ? zero_pixel : &input_rows[y_pixel1][x_pixel2 * components]; \
588                                 pixel3 = (y_pixel2 >= height)                      ? zero_pixel : &input_rows[y_pixel2][x_pixel1 * components]; \
589                                 pixel4 = (x_pixel2 >= width || y_pixel2 >= height) ? zero_pixel : &input_rows[y_pixel2][x_pixel2 * components]; \
591                                 for(int m = 0; m < components; m++) \
592                                 { \
593                                         output_rows[i][j * components + m] =  \
594                                                 (type)((pixel1[m] * fraction1) +  \
595                                                         (pixel2[m] * fraction2) +  \
596                                                         (pixel3[m] * fraction3) +  \
597                                                         (pixel4[m] * fraction4)); \
598                                 } \
599                         } \
600                 } \
601         } \
604 int RotateEngine::perform_rotation()
606         int width = input->get_w();
607         int height = input->get_h();
609         if(!interpolate)
610         {
611                 switch(input->get_color_model())
612                 {
613                         case BC_RGB888:
614                                 ROTATE_NEAREST(unsigned char, 3, 0x0);
615                                 break;
616                         case BC_RGB_FLOAT:
617                                 ROTATE_NEAREST(float, 3, 0x0);
618                                 break;
619                         case BC_YUV888:
620                                 ROTATE_NEAREST(unsigned char, 3, 0x80);
621                                 break;
622                         case BC_RGBA8888:
623                                 ROTATE_NEAREST(unsigned char, 4, 0x0);
624                                 break;
625                         case BC_RGBA_FLOAT:
626                                 ROTATE_NEAREST(float, 4, 0x0);
627                                 break;
628                         case BC_YUVA8888:
629                                 ROTATE_NEAREST(unsigned char, 4, 0x80);
630                                 break;
632                         case BC_RGB161616:
633                                 ROTATE_NEAREST(uint16_t, 3, 0x0);
634                                 break;
635                         case BC_YUV161616:
636                                 ROTATE_NEAREST(uint16_t, 3, 0x8000);
637                                 break;
639                         case BC_RGBA16161616:
640                                 ROTATE_NEAREST(uint16_t, 4, 0x0);
641                                 break;
642                         case BC_YUVA16161616:
643                                 ROTATE_NEAREST(uint16_t, 4, 0x8000);
644                                 break;
645                 }
646         }
647         else
648         {
649                 switch(input->get_color_model())
650                 {
651                         case BC_RGB888:
652                                 ROTATE_INTERPOLATE(unsigned char, 3, 0x0);
653                                 break;
654                         case BC_RGB_FLOAT:
655                                 ROTATE_INTERPOLATE(float, 3, 0x0);
656                                 break;
657                         case BC_YUV888:
658                                 ROTATE_INTERPOLATE(unsigned char, 3, 0x80);
659                                 break;
660                         case BC_RGBA8888:
661                                 ROTATE_INTERPOLATE(unsigned char, 4, 0x0);
662                                 break;
663                         case BC_RGBA_FLOAT:
664                                 ROTATE_INTERPOLATE(float, 4, 0x0);
665                                 break;
666                         case BC_YUVA8888:
667                                 ROTATE_INTERPOLATE(unsigned char, 4, 0x80);
668                                 break;
670                         case BC_RGB161616:
671                                 ROTATE_INTERPOLATE(uint16_t, 3, 0x0);
672                                 break;
673                         case BC_YUV161616:
674                                 ROTATE_INTERPOLATE(uint16_t, 3, 0x8000);
675                                 break;
677                         case BC_RGBA16161616:
678                                 ROTATE_INTERPOLATE(uint16_t, 4, 0x0);
679                                 break;
680                         case BC_YUVA16161616:
681                                 ROTATE_INTERPOLATE(uint16_t, 4, 0x8000);
682                                 break;
683                 }
684         }
685         return 0;
688 void RotateEngine::run()
690         while(!done)
691         {
692                 input_lock->lock("RotateEngine::run");
693                 if(done) return;
695                 if(do_matrix)
696                 {
697                         create_matrix();
698                 }
699                 else
700                 if(do_rotation)
701                 {
702                         perform_rotation();
703                 }
705                 do_matrix = 0;
706                 do_rotation = 0;
707                 output_lock->unlock();
708         }