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