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