4 * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
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.
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.
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
22 #include "condition.h"
23 #include "rotateframe.h"
31 #define SQR(x) ((x) * (x))
33 RotateFrame::RotateFrame(int cpus, int width, int height)
35 int y1, y2, y_increment;
36 y_increment = height / cpus;
40 engine = new RotateEngine*[cpus];
41 for(int i = 0; i < cpus; i++)
43 y2 = y1 + y_increment;
44 if(i == cpus - 1 && y2 < height - 1) y2 = height - 1;
45 engine[i] = new RotateEngine(this, y1, y2);
58 RotateFrame::~RotateFrame()
60 for(int i = 0; i < cpus; i++)
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;
72 void RotateFrame::rotate(VFrame *output,
78 this->output = output;
80 this->interpolate = interpolate;
84 if(angle == 90 || angle == 180 || angle == 270)
85 rotate_rightangle(input,
90 rotate_obliqueangle(input,
97 // Data never processed so copy if necessary
99 output->copy_from(input);
101 this->last_angle = angle;
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)
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;
118 #define ROTATE_RIGHTANGLE(type, components) \
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(); \
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++) \
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]; \
144 in_x2--; in_x1++; in_y2--; in_y1++; \
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]; \
162 get_rightdimensions(input, diameter, \
163 in_x1, in_y1, in_x2, in_y2, \
164 out_x1, out_y1, out_x2, out_y2); \
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]; \
179 in_x2--; in_x1++; in_y2--; in_y1++; \
186 int RotateFrame::rotate_rightangle(VFrame *input, VFrame *output, int angle)
190 int in_x2 = input->get_w();
191 int in_y2 = input->get_h();
192 int out_x1, out_y1, out_x2, out_y2;
195 output->clear_frame();
196 switch(output->get_color_model())
199 ROTATE_RIGHTANGLE(unsigned char, 3);
202 ROTATE_RIGHTANGLE(unsigned char, 4);
205 ROTATE_RIGHTANGLE(float, 3);
208 ROTATE_RIGHTANGLE(float, 4);
211 ROTATE_RIGHTANGLE(unsigned char, 3);
214 ROTATE_RIGHTANGLE(unsigned char, 4);
217 ROTATE_RIGHTANGLE(uint16_t, 3);
219 case BC_RGBA16161616:
220 ROTATE_RIGHTANGLE(uint16_t, 4);
223 ROTATE_RIGHTANGLE(uint16_t, 3);
225 case BC_YUVA16161616:
226 ROTATE_RIGHTANGLE(uint16_t, 4);
232 int RotateFrame::rotate_obliqueangle(VFrame *input,
238 int center_x, center_y;
241 center_x = input->get_w() / 2;
242 center_y = input->get_h() / 2;
244 if(last_angle != angle ||
245 (interpolate && !float_matrix) ||
246 (!interpolate && !int_matrix))
248 if(interpolate && !float_matrix)
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++)
254 float_rows[i] = &float_matrix[i * input->get_w()];
258 if(!interpolate && !int_matrix)
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++)
264 int_rows[i] = &int_matrix[i * input->get_w()];
271 if(last_angle != angle) need_matrix = 1;
272 if(last_interpolate != interpolate) need_matrix = 1;
276 // Last angle != angle implied by first buffer needing to be allocated
277 for(i = 0; i < cpus; i++)
279 engine[i]->generate_matrix(interpolate);
282 for(i = 0; i < cpus; i++)
284 engine[i]->wait_completion();
289 last_interpolate = interpolate;
291 // Perform the rotation
292 for(i = 0; i < cpus; i++)
294 engine[i]->perform_rotation(input, output, interpolate);
297 for(i = 0; i < cpus; i++)
299 engine[i]->wait_completion();
304 #define FILL_CENTER(type, components) \
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; \
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]; \
320 switch(input->get_color_model())
323 FILL_CENTER(float, 3)
326 FILL_CENTER(float, 4)
330 FILL_CENTER(unsigned char, 3)
334 FILL_CENTER(unsigned char, 4)
338 FILL_CENTER(uint16_t, 3)
340 case BC_RGBA16161616:
341 case BC_YUVA16161616:
342 FILL_CENTER(uint16_t, 4)
355 RotateEngine::RotateEngine(RotateFrame *plugin, int row1, int row2)
358 this->plugin = plugin;
359 do_matrix = do_rotation = 0;
363 input_lock = new Condition(0, "RotateEngine::input_lock");
364 output_lock = new Condition(0, "RotateEngine::output_lock");
366 RotateEngine::~RotateEngine()
371 input_lock->unlock();
378 int RotateEngine::generate_matrix(int interpolate)
381 this->interpolate = interpolate;
382 input_lock->unlock();
386 int RotateEngine::perform_rotation(VFrame *input,
391 this->output = output;
392 this->do_rotation = 1;
393 this->interpolate = interpolate;
394 input_lock->unlock();
399 int RotateEngine::wait_completion()
401 output_lock->lock("RotateEngine::wait_completion");
405 int RotateEngine::coords_to_pixel(int &input_y, int &input_x)
407 if(input_y < 0) return -1;
409 if(input_y >= plugin->input->get_h()) return -1;
411 if(input_x < 0) return -1;
413 if(input_x >= plugin->input->get_w()) return -1;
415 return input_y * plugin->input->get_w() + input_x;
418 int RotateEngine::coords_to_pixel(SourceCoord &float_pixel, float &input_y, float &input_x)
420 if(input_y < 0) float_pixel.y = -1;
422 if(input_y >= plugin->input->get_h()) float_pixel.y = -1;
424 float_pixel.y = input_y;
426 if(input_x < 0) float_pixel.x = -1;
428 if(input_x >= plugin->input->get_w()) float_pixel.x = -1;
430 float_pixel.x = input_x;
435 int RotateEngine::create_matrix()
437 // Polar coords of pixel
438 double k, l, magnitude, angle, offset_angle, offset_angle2;
439 double x_offset, y_offset;
442 SourceCoord *float_row = 0;
443 int input_x_i, input_y_i;
444 float input_x_f, input_y_f;
446 //printf("RotateEngine::create_matrix 1\n");
447 // The following is the result of pure trial and error.
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;
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;
457 for(i = row1, l = row1 - plugin->input->get_h() / 2; i < row2; i++, l++)
459 int l_suare = (int)(l * l);
461 int_row = plugin->int_rows[i];
463 float_row = plugin->float_rows[i];
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();
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);
475 angle = atan(-k / l);
481 //printf("RotateEngine::create_matrix 3.3\n");
483 angle += (l < 0) ? offset_angle2 : offset_angle;
485 // Convert back to cartesian coords
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);
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);
499 //printf("RotateEngine::create_matrix 3\n");
501 //printf("RotateEngine::create_matrix 2\n");
505 #define ROTATE_NEAREST(type, components, black_chroma) \
507 type **input_rows = (type**)input->get_rows(); \
508 type **output_rows = (type**)output->get_rows(); \
510 for(int i = row1; i < row2; i++) \
512 int *int_row = plugin->int_rows[i]; \
513 for(int j = 0; j < width; j++) \
517 for(int k = 0; k < components; k++) \
518 output_rows[i][j * components + k] = 0; \
522 for(int k = 0; k < components; k++) \
523 output_rows[i][j * components + k] = *(input_rows[0] + int_row[j] * components + k); \
529 #define ROTATE_INTERPOLATE(type, components, black_chroma) \
531 type zero_pixel[] = { 0, black_chroma, black_chroma, 0 }; \
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; \
541 for(i = row1, k = row1; i < row2; i++, k++) \
543 SourceCoord *float_row = plugin->float_rows[i]; \
544 for(j = 0, l = 0; j < width; j++, l++) \
546 if(float_row[j].x < 0 || float_row[j].y < 0) \
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; \
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]; \
574 for(int m = 0; m < components; m++) \
576 output_rows[i][j * components + m] = \
577 (type)((pixel1[m] * fraction1) + \
578 (pixel2[m] * fraction2) + \
579 (pixel3[m] * fraction3) + \
580 (pixel4[m] * fraction4)); \
587 int RotateEngine::perform_rotation()
589 int width = input->get_w();
590 int height = input->get_h();
594 switch(input->get_color_model())
597 ROTATE_NEAREST(unsigned char, 3, 0x0);
600 ROTATE_NEAREST(float, 3, 0x0);
603 ROTATE_NEAREST(unsigned char, 3, 0x80);
606 ROTATE_NEAREST(unsigned char, 4, 0x0);
609 ROTATE_NEAREST(float, 4, 0x0);
612 ROTATE_NEAREST(unsigned char, 4, 0x80);
616 ROTATE_NEAREST(uint16_t, 3, 0x0);
619 ROTATE_NEAREST(uint16_t, 3, 0x8000);
622 case BC_RGBA16161616:
623 ROTATE_NEAREST(uint16_t, 4, 0x0);
625 case BC_YUVA16161616:
626 ROTATE_NEAREST(uint16_t, 4, 0x8000);
632 switch(input->get_color_model())
635 ROTATE_INTERPOLATE(unsigned char, 3, 0x0);
638 ROTATE_INTERPOLATE(float, 3, 0x0);
641 ROTATE_INTERPOLATE(unsigned char, 3, 0x80);
644 ROTATE_INTERPOLATE(unsigned char, 4, 0x0);
647 ROTATE_INTERPOLATE(float, 4, 0x0);
650 ROTATE_INTERPOLATE(unsigned char, 4, 0x80);
654 ROTATE_INTERPOLATE(uint16_t, 3, 0x0);
657 ROTATE_INTERPOLATE(uint16_t, 3, 0x8000);
660 case BC_RGBA16161616:
661 ROTATE_INTERPOLATE(uint16_t, 4, 0x0);
663 case BC_YUVA16161616:
664 ROTATE_INTERPOLATE(uint16_t, 4, 0x8000);
671 void RotateEngine::run()
675 input_lock->lock("RotateEngine::run");
690 output_lock->unlock();