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,
115 diameter = frame->get_w() < frame->get_h() ? frame->get_w() : frame->get_h();
116 out_x1 = in_x1 = frame->get_w() / 2 - diameter / 2;
117 out_x2 = in_x2 = in_x1 + diameter - 1;
118 out_y1 = in_y1 = frame->get_h() / 2 - diameter / 2;
119 out_y2 = in_y2 = in_y1 + diameter - 1;
125 #define ROTATE_RIGHTANGLE(type, components) \
127 type **input_rows = (type**)input->get_rows(); \
128 type **output_rows = (type**)output->get_rows(); \
129 int height = output->get_h(); \
130 int width = output->get_w(); \
135 get_rightdimensions(input, \
145 while(in_x2 > in_x1) \
147 diameter = in_x2 - in_x1; \
148 for(int i = 0; i < diameter; i++) \
150 type temp_pixel[components]; \
151 for(int j = 0; j < components; j++) \
153 temp_pixel[j] = input_rows[in_y1 + i][in_x2 * components + j]; \
155 output_rows[in_y1 + i][in_x2 * components + j] = input_rows[in_y1][(in_x1 + i) * components + j]; \
156 output_rows[in_y1][(in_x1 + i) * components + j] = input_rows[in_y2 - i][in_x1 * components + j]; \
157 output_rows[in_y2 - i][in_x1 * components + j] = input_rows[in_y2][(in_x2 - i) * components + j]; \
158 output_rows[in_y2][(in_x2 - i) * components + j] = temp_pixel[j]; \
170 for(int i = 0, j = height - 1; i < j; i++, j--) \
172 for(int k = 0, l = width - 1; k < width; k++, l--) \
174 type temp_pixel[components]; \
175 for(int m = 0; m < components; m++) \
177 temp_pixel[m] = input_rows[j][k * components + m]; \
178 output_rows[j][k * components + m] = input_rows[i][l * components + m]; \
179 output_rows[i][l * components + m] = temp_pixel[m]; \
186 get_rightdimensions(input, \
197 while(in_x2 > in_x1) \
199 diameter = in_x2 - in_x1; \
200 for(int i = 0; i < diameter; i++) \
202 type temp_pixel[components]; \
203 for(int j = 0; j < components; j++) \
205 temp_pixel[j] = input_rows[in_y1 + i][in_x1 * components + j]; \
206 output_rows[in_y1 + i][in_x1 * components + j] = input_rows[in_y1][(in_x2 - i) * components + j]; \
207 output_rows[in_y1][(in_x2 - i) * components + j] = input_rows[in_y2 - i][in_x2 * components + j]; \
208 output_rows[in_y2 - i][in_x2 * components + j] = input_rows[in_y2][(in_x1 + i) * components + j]; \
209 output_rows[in_y2][(in_x1 + i) * components + j] = temp_pixel[j]; \
223 int RotateFrame::rotate_rightangle(VFrame *input,
229 int in_x2 = input->get_w();
230 int in_y2 = input->get_h();
231 int out_x1, out_y1, out_x2, out_y2;
234 output->clear_frame();
235 switch(output->get_color_model())
238 ROTATE_RIGHTANGLE(unsigned char, 3);
241 ROTATE_RIGHTANGLE(unsigned char, 4);
244 ROTATE_RIGHTANGLE(float, 3);
247 ROTATE_RIGHTANGLE(float, 4);
250 ROTATE_RIGHTANGLE(unsigned char, 3);
253 ROTATE_RIGHTANGLE(unsigned char, 4);
256 ROTATE_RIGHTANGLE(uint16_t, 3);
258 case BC_RGBA16161616:
259 ROTATE_RIGHTANGLE(uint16_t, 4);
262 ROTATE_RIGHTANGLE(uint16_t, 3);
264 case BC_YUVA16161616:
265 ROTATE_RIGHTANGLE(uint16_t, 4);
271 int RotateFrame::rotate_obliqueangle(VFrame *input,
277 int center_x, center_y;
280 center_x = input->get_w() / 2;
281 center_y = input->get_h() / 2;
283 if(last_angle != angle ||
284 (interpolate && !float_matrix) ||
285 (!interpolate && !int_matrix))
287 if(interpolate && !float_matrix)
289 float_matrix = new SourceCoord[input->get_w() * input->get_h()];
290 float_rows = new SourceCoord*[input->get_h()];
291 for(i = 0; i < input->get_h(); i++)
293 float_rows[i] = &float_matrix[i * input->get_w()];
297 if(!interpolate && !int_matrix)
299 int_matrix = new int[input->get_w() * input->get_h()];
300 int_rows = new int*[input->get_h()];
301 for(i = 0; i < input->get_h(); i++)
303 int_rows[i] = &int_matrix[i * input->get_w()];
310 if(last_angle != angle) need_matrix = 1;
311 if(last_interpolate != interpolate) need_matrix = 1;
315 // Last angle != angle implied by first buffer needing to be allocated
316 for(i = 0; i < cpus; i++)
318 engine[i]->generate_matrix(interpolate);
321 for(i = 0; i < cpus; i++)
323 engine[i]->wait_completion();
328 last_interpolate = interpolate;
330 // Perform the rotation
331 for(i = 0; i < cpus; i++)
333 engine[i]->perform_rotation(input, output, interpolate);
336 for(i = 0; i < cpus; i++)
338 engine[i]->wait_completion();
343 #define FILL_CENTER(type, components) \
345 type *out_pixel = ((type**)output->get_rows())[center_y] + center_x * components; \
346 type *in_pixel = ((type**)input->get_rows())[center_y] + center_x * components; \
348 out_pixel[0] = in_pixel[0]; \
349 out_pixel[1] = in_pixel[1]; \
350 out_pixel[2] = in_pixel[2]; \
351 if(components == 4) out_pixel[3] = in_pixel[3]; \
359 switch(input->get_color_model())
362 FILL_CENTER(float, 3)
365 FILL_CENTER(float, 4)
369 FILL_CENTER(unsigned char, 3)
373 FILL_CENTER(unsigned char, 4)
377 FILL_CENTER(uint16_t, 3)
379 case BC_RGBA16161616:
380 case BC_YUVA16161616:
381 FILL_CENTER(uint16_t, 4)
394 RotateEngine::RotateEngine(RotateFrame *plugin, int row1, int row2)
397 this->plugin = plugin;
398 do_matrix = do_rotation = 0;
402 input_lock = new Condition(0, "RotateEngine::input_lock");
403 output_lock = new Condition(0, "RotateEngine::output_lock");
405 RotateEngine::~RotateEngine()
410 input_lock->unlock();
417 int RotateEngine::generate_matrix(int interpolate)
420 this->interpolate = interpolate;
421 input_lock->unlock();
425 int RotateEngine::perform_rotation(VFrame *input,
430 this->output = output;
431 this->do_rotation = 1;
432 this->interpolate = interpolate;
433 input_lock->unlock();
438 int RotateEngine::wait_completion()
440 output_lock->lock("RotateEngine::wait_completion");
444 int RotateEngine::coords_to_pixel(int &input_y, int &input_x)
446 if(input_y < 0) return -1;
448 if(input_y >= plugin->input->get_h()) return -1;
450 if(input_x < 0) return -1;
452 if(input_x >= plugin->input->get_w()) return -1;
454 return input_y * plugin->input->get_w() + input_x;
457 int RotateEngine::coords_to_pixel(SourceCoord &float_pixel, float &input_y, float &input_x)
459 if(input_y < 0) float_pixel.y = -1;
461 if(input_y >= plugin->input->get_h()) float_pixel.y = -1;
463 float_pixel.y = input_y;
465 if(input_x < 0) float_pixel.x = -1;
467 if(input_x >= plugin->input->get_w()) float_pixel.x = -1;
469 float_pixel.x = input_x;
474 int RotateEngine::create_matrix()
476 // Polar coords of pixel
477 register double k, l, magnitude, angle, offset_angle, offset_angle2;
478 register double x_offset, y_offset;
481 SourceCoord *float_row = 0;
482 int input_x_i, input_y_i;
483 float input_x_f, input_y_f;
485 //printf("RotateEngine::create_matrix 1\n");
486 // The following is the result of pure trial and error.
488 // The source pixels are seen as if they were rotated counterclockwise so the sign is OK.
489 offset_angle = -(plugin->angle - 90) / 360 * 2 * M_PI;
490 offset_angle2 = -(plugin->angle - 270) / 360 * 2 * M_PI;
492 // Calculate an offset to add to all the pixels to compensate for the quadrant
493 y_offset = plugin->input->get_h() / 2;
494 x_offset = plugin->input->get_w() / 2;
496 for(i = row1, l = row1 - plugin->input->get_h() / 2; i < row2; i++, l++)
498 int l_suare = (int)(l * l);
500 int_row = plugin->int_rows[i];
502 float_row = plugin->float_rows[i];
504 //printf("RotateEngine::create_matrix 2 %d %f\n", i, l);
505 for(j = 0, k = -plugin->input->get_w() / 2;
506 j < plugin->input->get_w();
509 // Add offsets to input
510 // Convert to polar coords
511 magnitude = sqrt(SQR(k) + l_suare);
512 //printf("RotateEngine::create_matrix 3.2 %f %f\n", k, l);
514 angle = atan(-k / l);
520 //printf("RotateEngine::create_matrix 3.3\n");
522 angle += (l < 0) ? offset_angle2 : offset_angle;
524 // Convert back to cartesian coords
527 input_y_i = (int)(y_offset + magnitude * sin(angle));
528 input_x_i = (int)(x_offset + magnitude * cos(angle));
529 int_row[j] = coords_to_pixel(input_y_i, input_x_i);
533 input_y_f = y_offset + magnitude * sin(angle);
534 input_x_f = x_offset + magnitude * cos(angle);
535 coords_to_pixel(float_row[j], input_y_f, input_x_f);
538 //printf("RotateEngine::create_matrix 3\n");
540 //printf("RotateEngine::create_matrix 2\n");
544 #define ROTATE_NEAREST(type, components, black_chroma) \
546 type **input_rows = (type**)input->get_rows(); \
547 type **output_rows = (type**)output->get_rows(); \
549 for(int i = row1; i < row2; i++) \
551 int *int_row = plugin->int_rows[i]; \
552 for(int j = 0; j < width; j++) \
556 for(int k = 0; k < components; k++) \
557 output_rows[i][j * components + k] = 0; \
561 for(int k = 0; k < components; k++) \
562 output_rows[i][j * components + k] = *(input_rows[0] + int_row[j] * components + k); \
568 #define ROTATE_INTERPOLATE(type, components, black_chroma) \
570 type zero_pixel[] = { 0, black_chroma, black_chroma, 0 }; \
573 type **input_rows = (type**)input->get_rows(); \
574 type **output_rows = (type**)output->get_rows(); \
575 float x_fraction1, x_fraction2, y_fraction1, y_fraction2; \
576 float fraction1, fraction2, fraction3, fraction4; \
577 int x_pixel1, x_pixel2, y_pixel1, y_pixel2; \
578 type *pixel1, *pixel2, *pixel3, *pixel4; \
580 for(i = row1, k = row1; i < row2; i++, k++) \
582 SourceCoord *float_row = plugin->float_rows[i]; \
583 for(j = 0, l = 0; j < width; j++, l++) \
585 if(float_row[j].x < 0 || float_row[j].y < 0) \
587 output_rows[i][j * components + 0] = 0; \
588 output_rows[i][j * components + 1] = black_chroma; \
589 output_rows[i][j * components + 2] = black_chroma; \
590 if(components == 4) output_rows[i][j * components + 3] = 0; \
594 /* Interpolate input pixels */ \
595 x_pixel1 = (int)float_row[j].x; \
596 x_pixel2 = (int)(float_row[j].x + 1); \
597 y_pixel1 = (int)(float_row[j].y); \
598 y_pixel2 = (int)(float_row[j].y + 1); \
599 x_fraction1 = float_row[j].x - x_pixel1; \
600 x_fraction2 = (float)x_pixel2 - float_row[j].x; \
601 y_fraction1 = float_row[j].y - y_pixel1; \
602 y_fraction2 = (float)y_pixel2 - float_row[j].y; \
603 /* By trial and error this fraction order seems to work. */ \
604 fraction4 = x_fraction1 * y_fraction1; \
605 fraction3 = x_fraction2 * y_fraction1; \
606 fraction2 = x_fraction1 * y_fraction2; \
607 fraction1 = x_fraction2 * y_fraction2; \
608 pixel1 = &input_rows[y_pixel1][x_pixel1 * components]; \
609 pixel2 = (x_pixel2 >= width) ? zero_pixel : &input_rows[y_pixel1][x_pixel2 * components]; \
610 pixel3 = (y_pixel2 >= height) ? zero_pixel : &input_rows[y_pixel2][x_pixel1 * components]; \
611 pixel4 = (x_pixel2 >= width || y_pixel2 >= height) ? zero_pixel : &input_rows[y_pixel2][x_pixel2 * components]; \
613 for(int m = 0; m < components; m++) \
615 output_rows[i][j * components + m] = \
616 (type)((pixel1[m] * fraction1) + \
617 (pixel2[m] * fraction2) + \
618 (pixel3[m] * fraction3) + \
619 (pixel4[m] * fraction4)); \
626 int RotateEngine::perform_rotation()
628 int width = input->get_w();
629 int height = input->get_h();
633 switch(input->get_color_model())
636 ROTATE_NEAREST(unsigned char, 3, 0x0);
639 ROTATE_NEAREST(float, 3, 0x0);
642 ROTATE_NEAREST(unsigned char, 3, 0x80);
645 ROTATE_NEAREST(unsigned char, 4, 0x0);
648 ROTATE_NEAREST(float, 4, 0x0);
651 ROTATE_NEAREST(unsigned char, 4, 0x80);
655 ROTATE_NEAREST(uint16_t, 3, 0x0);
658 ROTATE_NEAREST(uint16_t, 3, 0x8000);
661 case BC_RGBA16161616:
662 ROTATE_NEAREST(uint16_t, 4, 0x0);
664 case BC_YUVA16161616:
665 ROTATE_NEAREST(uint16_t, 4, 0x8000);
671 switch(input->get_color_model())
674 ROTATE_INTERPOLATE(unsigned char, 3, 0x0);
677 ROTATE_INTERPOLATE(float, 3, 0x0);
680 ROTATE_INTERPOLATE(unsigned char, 3, 0x80);
683 ROTATE_INTERPOLATE(unsigned char, 4, 0x0);
686 ROTATE_INTERPOLATE(float, 4, 0x0);
689 ROTATE_INTERPOLATE(unsigned char, 4, 0x80);
693 ROTATE_INTERPOLATE(uint16_t, 3, 0x0);
696 ROTATE_INTERPOLATE(uint16_t, 3, 0x8000);
699 case BC_RGBA16161616:
700 ROTATE_INTERPOLATE(uint16_t, 4, 0x0);
702 case BC_YUVA16161616:
703 ROTATE_INTERPOLATE(uint16_t, 4, 0x8000);
710 void RotateEngine::run()
714 input_lock->lock("RotateEngine::run");
729 output_lock->unlock();