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
23 #define GL_GLEXT_PROTOTYPES
37 AffineMatrix::AffineMatrix()
39 bzero(values, sizeof(values));
42 void AffineMatrix::identity()
44 bzero(values, sizeof(values));
50 void AffineMatrix::translate(double x, double y)
52 double g = values[2][0];
53 double h = values[2][1];
54 double i = values[2][2];
55 values[0][0] += x * g;
56 values[0][1] += x * h;
57 values[0][2] += x * i;
58 values[1][0] += y * g;
59 values[1][1] += y * h;
60 values[1][2] += y * i;
63 void AffineMatrix::scale(double x, double y)
74 void AffineMatrix::multiply(AffineMatrix *dst)
78 for( int i=0; i<3; ++i ) {
79 double t1 = values[i][0], t2 = values[i][1], t3 = values[i][2];
80 for( int j=0; j<3; ++j ) {
81 tmp.values[i][j] = t1 * dst->values[0][j];
82 tmp.values[i][j] += t2 * dst->values[1][j];
83 tmp.values[i][j] += t3 * dst->values[2][j];
89 double AffineMatrix::determinant()
94 values[0][0] * (values[1][1] * values[2][2] - values[1][2] * values[2][1]);
96 values[1][0] * (values[0][1] * values[2][2] - values[0][2] * values[2][1]);
98 values[2][0] * (values[0][1] * values[1][2] - values[0][2] * values[1][1]);
103 void AffineMatrix::invert(AffineMatrix *dst)
107 det_1 = determinant();
115 (values[1][1] * values[2][2] - values[1][2] * values[2][1]) * det_1;
118 - (values[1][0] * values[2][2] - values[1][2] * values[2][0]) * det_1;
121 (values[1][0] * values[2][1] - values[1][1] * values[2][0]) * det_1;
124 - (values[0][1] * values[2][2] - values[0][2] * values[2][1] ) * det_1;
127 (values[0][0] * values[2][2] - values[0][2] * values[2][0]) * det_1;
130 - (values[0][0] * values[2][1] - values[0][1] * values[2][0]) * det_1;
133 (values[0][1] * values[1][2] - values[0][2] * values[1][1]) * det_1;
136 - (values[0][0] * values[1][2] - values[0][2] * values[1][0]) * det_1;
139 (values[0][0] * values[1][1] - values[0][1] * values[1][0]) * det_1;
142 void AffineMatrix::copy_from(AffineMatrix *src)
144 memcpy(&values[0][0], &src->values[0][0], sizeof(values));
147 void AffineMatrix::transform_point(float x,
154 w = values[2][0] * x + values[2][1] * y + values[2][2];
157 *newx = (values[0][0] * x + values[0][1] * y + values[0][2]) * w;
158 *newy = (values[1][0] * x + values[1][1] * y + values[1][2]) * w;
161 void AffineMatrix::dump()
163 printf("AffineMatrix::dump\n");
164 printf("%f %f %f\n", values[0][0], values[0][1], values[0][2]);
165 printf("%f %f %f\n", values[1][0], values[1][1], values[1][2]);
166 printf("%f %f %f\n", values[2][0], values[2][1], values[2][2]);
173 AffinePackage::AffinePackage()
181 AffineUnit::AffineUnit(AffineEngine *server)
184 this->server = server;
195 void AffineUnit::calculate_matrix(
196 double in_x1, double in_y1, double in_x2, double in_y2,
197 double out_x1, double out_y1, double out_x2, double out_y2,
198 double out_x3, double out_y3, double out_x4, double out_y4,
199 AffineMatrix *result)
205 scalex = scaley = 1.0;
207 if( (in_x2 - in_x1) > 0 )
208 scalex = 1.0 / (double)(in_x2 - in_x1);
210 if( (in_y2 - in_y1) > 0 )
211 scaley = 1.0 / (double)(in_y2 - in_y1);
213 /* Determine the perspective transform that maps from
214 * the unit cube to the transformed coordinates
216 double dx1, dx2, dx3, dy1, dy2, dy3;
219 dx1 = out_x2 - out_x4;
220 dx2 = out_x3 - out_x4;
221 dx3 = out_x1 - out_x2 + out_x4 - out_x3;
223 dy1 = out_y2 - out_y4;
224 dy2 = out_y3 - out_y4;
225 dy3 = out_y1 - out_y2 + out_y4 - out_y3;
226 // printf("AffineUnit::calculate_matrix %f %f %f %f %f %f\n",
227 // dx1, dx2, dx3, dy1, dy2, dy3 );
229 /* Is the mapping affine? */
230 if((dx3 == 0.0) && (dy3 == 0.0)) {
231 matrix.values[0][0] = out_x2 - out_x1;
232 matrix.values[0][1] = out_x4 - out_x2;
233 matrix.values[0][2] = out_x1;
234 matrix.values[1][0] = out_y2 - out_y1;
235 matrix.values[1][1] = out_y4 - out_y2;
236 matrix.values[1][2] = out_y1;
237 matrix.values[2][0] = 0.0;
238 matrix.values[2][1] = 0.0;
241 det1 = dx3 * dy2 - dy3 * dx2;
242 det2 = dx1 * dy2 - dy1 * dx2;
243 matrix.values[2][0] = det1 / det2;
244 det1 = dx1 * dy3 - dy1 * dx3;
245 det2 = dx1 * dy2 - dy1 * dx2;
246 matrix.values[2][1] = det1 / det2;
248 matrix.values[0][0] = out_x2 - out_x1 + matrix.values[2][0] * out_x2;
249 matrix.values[0][1] = out_x3 - out_x1 + matrix.values[2][1] * out_x3;
250 matrix.values[0][2] = out_x1;
252 matrix.values[1][0] = out_y2 - out_y1 + matrix.values[2][0] * out_y2;
253 matrix.values[1][1] = out_y3 - out_y1 + matrix.values[2][1] * out_y3;
254 matrix.values[1][2] = out_y1;
257 matrix.values[2][2] = 1.0;
259 // printf("AffineUnit::calculate_matrix 1 %f %f\n", dx3, dy3);
263 result->translate(-in_x1, -in_y1);
264 result->scale(scalex, scaley);
265 matrix.multiply(result);
266 // double test[3][3] =
267 // { { 0.0896, 0.0, 0.0 }, { 0.0, 0.0896, 0.0 }, { -0.00126, 0.0, 1.0 } };
268 // memcpy(&result->values[0][0], test, sizeof(test));
269 // printf("AffineUnit::calculate_matrix 4 %p\n", result);
273 static inline float transform_cubic(float dx,
274 float p0, float p1, float p2, float p3)
276 /* Catmull-Rom - not bad */
277 float result = ((( (- p0 + 3*p1 - 3*p2 + p3) * dx +
278 ( 2*p0 - 5*p1 + 4*p2 - p3 ) ) * dx +
279 ( - p0 + p2 ) ) * dx + (p1 + p1) ) / 2;
280 // printf("%f %f %f %f %f\n", result, p0, p1, p2, p3);
284 static inline float transform_linear(float dx,
287 float result = p1 * (1-dx) + p2 * dx;
292 void AffineUnit::process_package(LoadPackage *package)
294 AffinePackage *pkg = (AffinePackage*)package;
295 int min_in_x = server->in_x;
296 int min_in_y = server->in_y;
297 int max_in_x = server->in_x + server->in_w - 1;
298 int max_in_y = server->in_y + server->in_h - 1;
301 // printf("AffineUnit::process_package %d %d %d %d %d\n",
307 int min_out_x = server->out_x;
308 //int min_out_y = server->out_y;
309 int max_out_x = server->out_x + server->out_w;
310 //int max_out_y = server->out_y + server->out_h;
312 // Amount to shift the input coordinates relative to the output coordinates
313 // To get the pivots to line up
314 int pivot_offset_x = server->in_pivot_x - server->out_pivot_x;
315 int pivot_offset_y = server->in_pivot_y - server->out_pivot_y;
317 // Calculate real coords
318 float out_x1, out_y1, out_x2, out_y2, out_x3, out_y3, out_x4, out_y4;
319 if( server->mode == AffineEngine::STRETCH ||
320 server->mode == AffineEngine::PERSPECTIVE ||
321 server->mode == AffineEngine::ROTATE ||
322 server->mode == AffineEngine::TRANSFORM ) {
323 out_x1 = (float)server->in_x + (float)server->x1 * server->in_w / 100;
324 out_y1 = (float)server->in_y + (float)server->y1 * server->in_h / 100;
325 out_x2 = (float)server->in_x + (float)server->x2 * server->in_w / 100;
326 out_y2 = (float)server->in_y + (float)server->y2 * server->in_h / 100;
327 out_x3 = (float)server->in_x + (float)server->x3 * server->in_w / 100;
328 out_y3 = (float)server->in_y + (float)server->y3 * server->in_h / 100;
329 out_x4 = (float)server->in_x + (float)server->x4 * server->in_w / 100;
330 out_y4 = (float)server->in_y + (float)server->y4 * server->in_h / 100;
333 out_x1 = (float)server->in_x + (float)server->x1 * server->in_w / 100;
334 out_y1 = server->in_y;
335 out_x2 = out_x1 + server->in_w;
336 out_y2 = server->in_y;
337 out_x4 = (float)server->in_x + (float)server->x4 * server->in_w / 100;
338 out_y4 = server->in_y + server->in_h;
339 out_x3 = out_x4 + server->in_w;
340 out_y3 = server->in_y + server->in_h;
345 // Rotation with OpenGL uses a simple quad.
346 if( server->mode == AffineEngine::ROTATE &&
347 server->use_opengl ) {
349 out_x1 -= pivot_offset_x; out_y1 -= pivot_offset_y;
350 out_x2 -= pivot_offset_x; out_y2 -= pivot_offset_y;
351 out_x3 -= pivot_offset_x; out_y3 -= pivot_offset_y;
352 out_x4 -= pivot_offset_x; out_y4 -= pivot_offset_y;
354 server->output->to_texture();
355 server->output->enable_opengl();
356 server->output->init_screen();
357 server->output->bind_texture(0);
358 server->output->clear_pbuffer();
360 int texture_w = server->output->get_texture_w();
361 int texture_h = server->output->get_texture_h();
362 float output_h = server->output->get_h();
363 float in_x1 = (float)server->in_x / texture_w;
364 float in_x2 = (float)(server->in_x + server->in_w) / texture_w;
365 float in_y1 = (float)server->in_y / texture_h;
366 float in_y2 = (float)(server->in_y + server->in_h) / texture_h;
368 // printf("%f %f %f %f\n%f,%f %f,%f %f,%f %f,%f\n", in_x1, in_y1, in_x2, in_y2,
369 // out_x1, out_y1, out_x2, out_y2, out_x3, out_y3, out_x4, out_y4);
372 glNormal3f(0, 0, 1.0);
374 glTexCoord2f(in_x1, in_y1);
375 glVertex3f(out_x1, -output_h+out_y1, 0);
377 glTexCoord2f(in_x2, in_y1);
378 glVertex3f(out_x2, -output_h+out_y2, 0);
380 glTexCoord2f(in_x2, in_y2);
381 glVertex3f(out_x3, -output_h+out_y3, 0);
383 glTexCoord2f(in_x1, in_y2);
384 glVertex3f(out_x4, -output_h+out_y4, 0);
389 server->output->set_opengl_state(VFrame::SCREEN);
393 if( server->mode == AffineEngine::PERSPECTIVE ||
394 server->mode == AffineEngine::SHEER ||
395 server->mode == AffineEngine::ROTATE ||
396 server->mode == AffineEngine::TRANSFORM ) {
411 if( server->mode != AffineEngine::TRANSFORM ) {
412 calculate_matrix( server->in_x, server->in_y,
413 server->in_x + server->in_w,
414 server->in_y + server->in_h,
415 out_x1, out_y1, out_x2, out_y2,
416 out_x3, out_y3, out_x4, out_y4,
420 matrix.copy_from(&server->matrix);
423 //printf("AffineUnit::process_package %d\n%f %f %f\n%f %f %f\n%f %f %f\n", __LINE__,
424 // matrix.values[0][0], matrix.values[0][1], matrix.values[0][2],
425 // matrix.values[1][0], matrix.values[1][1], matrix.values[1][2],
426 // matrix.values[2][0], matrix.values[2][1], matrix.values[2][2]);
427 int reverse = !server->forward;
429 float xinc, yinc, winc;
431 float ttx = 0, tty = 0;
432 int itx = 0, ity = 0;
433 int tx1 = 0, ty1 = 0, tx2 = 0, ty2 = 0;
436 m.copy_from(&matrix);
438 matrix.copy_from(&im);
444 float dx1 = 0, dy1 = 0;
445 float dx2 = 0, dy2 = 0;
446 float dx3 = 0, dy3 = 0;
447 float dx4 = 0, dy4 = 0;
448 matrix.transform_point(server->in_x, server->in_y, &dx1, &dy1);
449 matrix.transform_point(server->in_x + server->in_w, server->in_y, &dx2, &dy2);
450 matrix.transform_point(server->in_x, server->in_y + server->in_h, &dx3, &dy3);
451 matrix.transform_point(server->in_x + server->in_w, server->in_y + server->in_h, &dx4, &dy4);
453 //printf("AffineUnit::process_package 1 y1=%d y2=%d\n", pkg->y1, pkg->y2);
454 //printf("AffineUnit::process_package 1 %f %f %f %f\n", dy1, dy2, dy3, dy4);
455 // printf("AffineUnit::process_package %d use_opengl=%d\n",
456 // __LINE__, server->use_opengl);
458 if( server->use_opengl &&
459 server->interpolation == AffineEngine::AF_DEFAULT ) {
461 static const char *affine_frag =
462 "uniform sampler2D tex;\n"
463 "uniform mat3 affine_matrix;\n"
464 "uniform vec2 texture_extents;\n"
465 "uniform vec2 image_extents;\n"
466 "uniform vec4 border_color;\n"
469 " vec2 outcoord = gl_TexCoord[0].st;\n"
470 " outcoord *= texture_extents;\n"
471 " mat3 coord_matrix = mat3(\n"
472 " outcoord.x, outcoord.y, 1.0, \n"
473 " outcoord.x, outcoord.y, 1.0, \n"
474 " outcoord.x, outcoord.y, 1.0);\n"
475 " mat3 incoord_matrix = affine_matrix * coord_matrix;\n"
476 " vec2 incoord = vec2(incoord_matrix[0][0], incoord_matrix[0][1]);\n"
477 " incoord /= incoord_matrix[0][2];\n"
478 " incoord /= texture_extents;\n"
479 " if(incoord.x > image_extents.x || incoord.y > image_extents.y)\n"
480 " gl_FragColor = border_color;\n"
482 " gl_FragColor = texture2D(tex, incoord);\n"
485 float affine_matrix[9] = {
486 (float)m.values[0][0], (float)m.values[1][0], (float)m.values[2][0],
487 (float)m.values[0][1], (float)m.values[1][1], (float)m.values[2][1],
488 (float)m.values[0][2], (float)m.values[1][2], (float)m.values[2][2]
492 server->output->to_texture();
493 server->output->enable_opengl();
494 unsigned int frag_shader = VFrame::make_shader(0,
497 if( frag_shader > 0 ) {
498 glUseProgram(frag_shader);
499 glUniform1i(glGetUniformLocation(frag_shader, "tex"), 0);
500 glUniformMatrix3fv(glGetUniformLocation(frag_shader, "affine_matrix"),
501 1, 0, affine_matrix);
502 glUniform2f(glGetUniformLocation(frag_shader, "texture_extents"),
503 (GLfloat)server->output->get_texture_w(),
504 (GLfloat)server->output->get_texture_h());
505 glUniform2f(glGetUniformLocation(frag_shader, "image_extents"),
506 (GLfloat)server->output->get_w() / server->output->get_texture_w(),
507 (GLfloat)server->output->get_h() / server->output->get_texture_h());
508 float border_color[] = { 0, 0, 0, 0 };
509 if(BC_CModels::is_yuv(server->output->get_color_model())) {
510 border_color[1] = 0.5;
511 border_color[2] = 0.5;
513 if(!BC_CModels::has_alpha(server->output->get_color_model())) {
514 border_color[3] = 1.0;
517 glUniform4fv(glGetUniformLocation(frag_shader, "border_color"),
518 1, (GLfloat*)border_color);
519 server->output->init_screen();
520 server->output->bind_texture(0);
521 glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
522 glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, border_color);
523 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
524 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
525 server->output->draw_texture();
527 server->output->set_opengl_state(VFrame::SCREEN);
533 #define ROUND(x) ((int)((x > 0) ? (x) + 0.5 : (x) - 0.5))
534 #define MIN4(a,b,c,d) MIN(MIN(MIN(a,b),c),d)
535 #define MAX4(a,b,c,d) MAX(MAX(MAX(a,b),c),d)
537 tx1 = ROUND(MIN4(dx1 - pivot_offset_x, dx2 - pivot_offset_x, dx3 - pivot_offset_x, dx4 - pivot_offset_x));
538 ty1 = ROUND(MIN4(dy1 - pivot_offset_y, dy2 - pivot_offset_y, dy3 - pivot_offset_y, dy4 - pivot_offset_y));
540 tx2 = ROUND(MAX4(dx1 - pivot_offset_x, dx2 - pivot_offset_x, dx3 - pivot_offset_x, dx4 - pivot_offset_x));
541 ty2 = ROUND(MAX4(dy1 - pivot_offset_y, dy2 - pivot_offset_y, dy3 - pivot_offset_y, dy4 - pivot_offset_y));
543 CLAMP(ty1, pkg->y1, pkg->y2);
544 CLAMP(ty2, pkg->y1, pkg->y2);
545 CLAMP(tx1, server->out_x, server->out_x + server->out_w);
546 CLAMP(tx2, server->out_x, server->out_x + server->out_w);
549 xinc = m.values[0][0];
550 yinc = m.values[1][0];
551 winc = m.values[2][0];
553 //printf("AffineUnit::process_package 2 tx1=%d ty1=%d tx2=%d ty2=%d %f %f\n",
554 // tx1, ty1, tx2, ty2, out_x4, out_y4);
555 //printf("AffineUnit::process_package %d %d %d %d %d\n",
556 // __LINE__, min_in_x, max_in_x, min_in_y, max_in_y);
558 #define CUBIC_ROW(in_row, chroma_offset) ( !in_row ? 0 : transform_cubic(dx, \
559 cp>=min_in_x && cp<max_in_x ? in_row[cp*comps]-chroma_offset : 0, \
560 c0>=min_in_x && c0<max_in_x ? in_row[c0*comps]-chroma_offset : 0, \
561 c1>=min_in_x && c1<max_in_x ? in_row[c1*comps]-chroma_offset : 0, \
562 c2>=min_in_x && c2<max_in_x ? in_row[c2*comps]-chroma_offset : 0) )
565 #define DO_CUBIC(tag, components, type, temp_type, chroma_offset, max) \
567 type **inp_rows = (type**)server->input->get_rows(); \
568 type **out_rows = (type**)server->output->get_rows(); \
569 float round_factor = sizeof(type) < 4 ? 0.5 : 0; \
570 int comps = components; \
571 for( int y=ty1; y<ty2; ++y ) { \
572 type *out_row = (type*)out_rows[y]; \
574 int x1 = tx1, x2 = tx2; \
575 if( x1 < min_out_x ) x1 = min_out_x; \
576 if( x2 > max_out_x ) x2 = max_out_x; \
577 tx = xinc * x1 + m.values[0][1] * (y + pivot_offset_y) + m.values[0][2] \
578 + pivot_offset_x * xinc; \
579 ty = yinc * x1 + m.values[1][1] * (y + pivot_offset_y) + m.values[1][2] \
580 + pivot_offset_x * yinc; \
581 tw = winc * x1 + m.values[2][1] * (y + pivot_offset_y) + m.values[2][2] \
582 + pivot_offset_x * winc; \
583 type *out = out_row + x1 * comps; \
584 for( int x=x1; x<x2; ++x ) { \
585 /* Normalize homogeneous coords */ \
586 if( tw == 0.0 ) { ttx = 0.0; tty = 0.0; } \
587 else { ttx = tx / tw; tty = ty / tw; } \
588 itx = (int)ttx; ity = (int)tty; \
589 /* the fractional error */ \
590 float dx = ttx - itx, dy = tty - ity; \
591 if( dx < 0 ) dx += 1; \
592 if( dy < 0 ) dy += 1; \
593 /* row/col index */ \
594 int cp = itx-1, c0 = itx+0, c1 = itx+1, c2 = itx+2; \
595 int rp = ity-1, r0 = ity+0, r1 = ity+1, r2 = ity+2; \
596 type *rpp, *r0p, *r1p, *r2p; \
597 rpp = rp>=min_in_y && rp<max_in_y ? inp_rows[rp] : 0; \
598 r0p = r0>=min_in_y && r0<max_in_y ? inp_rows[r0] : 0; \
599 r1p = r1>=min_in_y && r1<max_in_y ? inp_rows[r1] : 0; \
600 r2p = r2>=min_in_y && r2<max_in_y ? inp_rows[r2] : 0; \
601 temp_type r, g, b, a; \
602 r = (temp_type)(transform_cubic(dy, \
603 CUBIC_ROW(rpp, 0x0), CUBIC_ROW(r0p, 0x0), \
604 CUBIC_ROW(r1p, 0x0), CUBIC_ROW(r2p, 0x0)) \
606 if(rpp) ++rpp; if(r0p) ++r0p; if(r1p) ++r1p; if(r2p) ++r2p; \
607 g = (temp_type)(transform_cubic(dy, \
608 CUBIC_ROW(rpp, chroma_offset), CUBIC_ROW(r0p, chroma_offset), \
609 CUBIC_ROW(r1p, chroma_offset), CUBIC_ROW(r2p, chroma_offset)) \
610 + round_factor) + chroma_offset; \
611 if(rpp) ++rpp; if(r0p) ++r0p; if(r1p) ++r1p; if(r2p) ++r2p; \
612 b = (temp_type)(transform_cubic(dy, \
613 CUBIC_ROW(rpp, chroma_offset), CUBIC_ROW(r0p, chroma_offset), \
614 CUBIC_ROW(r1p, chroma_offset), CUBIC_ROW(r2p, chroma_offset)) \
615 + round_factor) + chroma_offset; \
616 if( components == 4 ) { \
617 if(rpp) ++rpp; if(r0p) ++r0p; if(r1p) ++r1p; if(r2p) ++r2p; \
618 a = (temp_type)(transform_cubic(dy, \
619 CUBIC_ROW(rpp, 0x0), CUBIC_ROW(r0p, 0x0), \
620 CUBIC_ROW(r1p, 0x0), CUBIC_ROW(r2p, 0x0)) \
623 if( sizeof(type) < 4 ) { \
624 *out++ = CLIP(r, 0, max); \
625 *out++ = CLIP(g, 0, max); \
626 *out++ = CLIP(b, 0, max); \
627 if( components == 4 ) *out++ = CLIP(a, 0, max); \
633 if( components == 4 ) *out++ = a; \
636 /* increment the transformed coordinates */ \
637 tx += xinc; ty += yinc; tw += winc; \
642 #define LINEAR_ROW(in_row, chroma_offset) ( !in_row ? 0 : transform_linear(dx, \
643 c0>=min_in_x && c0<max_in_x ? in_row[c0*comps]-chroma_offset : 0, \
644 c1>=min_in_x && c1<max_in_x ? in_row[c1*comps]-chroma_offset : 0) )
646 #define DO_LINEAR(tag, components, type, temp_type, chroma_offset, max) \
648 type **inp_rows = (type**)server->input->get_rows(); \
649 type **out_rows = (type**)server->output->get_rows(); \
650 int comps = components; \
651 float round_factor = sizeof(type) < 4 ? 0.5 : 0; \
652 for( int y=ty1; y<ty2; ++y ) { \
653 type *out_row = (type*)out_rows[y]; \
655 int x1 = tx1, x2 = tx2; \
656 if( x1 < min_out_x ) x1 = min_out_x; \
657 if( x2 > max_out_x ) x2 = max_out_x; \
658 tx = xinc * x1 + m.values[0][1] * (y + pivot_offset_y) + m.values[0][2] \
659 + pivot_offset_x * xinc; \
660 ty = yinc * x1 + m.values[1][1] * (y + pivot_offset_y) + m.values[1][2] \
661 + pivot_offset_x * yinc; \
662 tw = winc * x1 + m.values[2][1] * (y + pivot_offset_y) + m.values[2][2] \
663 + pivot_offset_x * winc; \
664 type *out = out_row + x1 * comps; \
665 for( int x=x1; x<x2; ++x ) { \
666 /* Normalize homogeneous coords */ \
667 if( tw == 0.0 ) { ttx = 0.0; tty = 0.0; } \
668 else { ttx = tx / tw; tty = ty / tw; } \
669 itx = (int)ttx; ity = (int)tty; \
670 /* the fractional error */ \
671 float dx = ttx - itx, dy = tty - ity; \
672 if( dx < 0 ) dx += 1; \
673 if( dy < 0 ) dy += 1; \
674 /* row/col index */ \
675 int c0 = itx+0, c1 = itx+1; \
676 int r0 = ity+0, r1 = ity+1; \
678 r0p = r0>=min_in_y && r0<max_in_y ? inp_rows[r0] : 0; \
679 r1p = r1>=min_in_y && r1<max_in_y ? inp_rows[r1] : 0; \
680 temp_type r, g, b, a; \
681 r = (temp_type)(transform_linear(dy, \
682 LINEAR_ROW(r0p, 0x0), LINEAR_ROW(r1p, 0x0)) \
684 if(r0p) ++r0p; if(r1p) ++r1p; \
685 g = (temp_type)(transform_linear(dy, \
686 LINEAR_ROW(r0p, chroma_offset), LINEAR_ROW(r1p, chroma_offset)) \
687 + round_factor) + chroma_offset; \
688 if(r0p) ++r0p; if(r1p) ++r1p; \
689 b = (temp_type)(transform_linear(dy, \
690 LINEAR_ROW(r0p, chroma_offset), LINEAR_ROW(r1p, chroma_offset)) \
691 + round_factor) + chroma_offset; \
692 if( components == 4 ) { \
693 if(r0p) ++r0p; if(r1p) ++r1p; \
694 a = (temp_type)(transform_linear(dy, \
695 LINEAR_ROW(r0p, 0x0), LINEAR_ROW(r1p, 0x0)) \
698 if( sizeof(type) < 4 ) { \
699 *out++ = CLIP(r, 0, max); \
700 *out++ = CLIP(g, 0, max); \
701 *out++ = CLIP(b, 0, max); \
702 if( components == 4 ) *out++ = CLIP(a, 0, max); \
708 if( components == 4 ) *out++ = a; \
711 /* increment the transformed coordinates */ \
712 tx += xinc; ty += yinc; tw += winc; \
717 #define DO_NEAREST(tag, components, type, temp_type, chroma_offset, max) \
719 type **inp_rows = (type**)server->input->get_rows(); \
720 type **out_rows = (type**)server->output->get_rows(); \
721 for( int y=ty1; y<ty2; ++y ) { \
722 type *out_row = (type*)out_rows[y]; \
724 int x1 = tx1, x2 = tx2; \
725 if( x1 < min_out_x ) x1 = min_out_x; \
726 if( x2 > max_out_x ) x2 = max_out_x; \
727 tx = xinc * x1 + m.values[0][1] * (y + pivot_offset_y) + m.values[0][2] \
728 + pivot_offset_x * xinc; \
729 ty = yinc * x1 + m.values[1][1] * (y + pivot_offset_y) + m.values[1][2] \
730 + pivot_offset_x * yinc; \
731 tw = winc * x1 + m.values[2][1] * (y + pivot_offset_y) + m.values[2][2] \
732 + pivot_offset_x * winc; \
733 type *out = out_row + x1 * components; \
734 for( int x=x1; x<x2; ++x ) { \
735 /* Normalize homogeneous coords */ \
736 if( tw == 0.0 ) { ttx = 0.0; tty = 0.0; } \
737 else { ttx = tx / tw; tty = ty / tw; } \
738 itx = (int)ttx; ity = (int)tty; \
739 /* row/col index */ \
740 type *rp = ity>=min_in_y && ity<max_in_y ? inp_rows[ity] : 0; \
741 temp_type r, g, b, a; \
742 r = (temp_type)( rp && itx>=min_in_x && itx<max_in_x ? rp[itx*components] : 0 ); \
744 g = (temp_type)( rp && itx>=min_in_x && itx<max_in_x ? rp[itx*components] : 0 ); \
746 b = (temp_type)( rp && itx>=min_in_x && itx<max_in_x ? rp[itx*components] : 0 ); \
747 if( components == 4 ) { \
749 a = (temp_type)( rp && itx>=min_in_x && itx<max_in_x ? rp[itx*components] : 0 ); \
751 *out++ = r; *out++ = g; *out++ = b; \
752 if( components == 4 ) *out++ = a; \
754 /* increment the transformed coordinates */ \
755 tx += xinc; ty += yinc; tw += winc; \
760 // printf("AffineUnit::process_package %d tx1=%d ty1=%d tx2=%d ty2=%d\n",
761 // __LINE__, tx1, ty1, tx2, ty2);
763 switch( server->interpolation ) {
764 case AffineEngine::AF_NEAREST:
765 switch( server->input->get_color_model() ) {
766 DO_NEAREST( BC_RGB_FLOAT, 3, float, float, 0x0, 1.0);
767 DO_NEAREST( BC_RGB888, 3, unsigned char, int, 0x0, 0xff);
768 DO_NEAREST( BC_RGBA_FLOAT, 4, float, float, 0x0, 1.0);
769 DO_NEAREST( BC_RGBA8888, 4, unsigned char, int, 0x0, 0xff);
770 DO_NEAREST( BC_YUV888, 3, unsigned char, int, 0x80, 0xff);
771 DO_NEAREST( BC_YUVA8888, 4, unsigned char, int, 0x80, 0xff);
772 DO_NEAREST( BC_RGB161616, 3, uint16_t, int, 0x0, 0xffff);
773 DO_NEAREST( BC_RGBA16161616, 4, uint16_t, int, 0x0, 0xffff);
774 DO_NEAREST( BC_YUV161616, 3, uint16_t, int, 0x8000, 0xffff);
775 DO_NEAREST( BC_YUVA16161616, 4, uint16_t, int, 0x8000, 0xffff);
778 case AffineEngine::AF_LINEAR:
779 switch( server->input->get_color_model() ) {
780 DO_LINEAR( BC_RGB_FLOAT, 3, float, float, 0x0, 1.0);
781 DO_LINEAR( BC_RGB888, 3, unsigned char, int, 0x0, 0xff);
782 DO_LINEAR( BC_RGBA_FLOAT, 4, float, float, 0x0, 1.0);
783 DO_LINEAR( BC_RGBA8888, 4, unsigned char, int, 0x0, 0xff);
784 DO_LINEAR( BC_YUV888, 3, unsigned char, int, 0x80, 0xff);
785 DO_LINEAR( BC_YUVA8888, 4, unsigned char, int, 0x80, 0xff);
786 DO_LINEAR( BC_RGB161616, 3, uint16_t, int, 0x0, 0xffff);
787 DO_LINEAR( BC_RGBA16161616, 4, uint16_t, int, 0x0, 0xffff);
788 DO_LINEAR( BC_YUV161616, 3, uint16_t, int, 0x8000, 0xffff);
789 DO_LINEAR( BC_YUVA16161616, 4, uint16_t, int, 0x8000, 0xffff);
793 case AffineEngine::AF_CUBIC:
794 switch( server->input->get_color_model() ) {
795 DO_CUBIC( BC_RGB_FLOAT, 3, float, float, 0x0, 1.0);
796 DO_CUBIC( BC_RGB888, 3, unsigned char, int, 0x0, 0xff);
797 DO_CUBIC( BC_RGBA_FLOAT, 4, float, float, 0x0, 1.0);
798 DO_CUBIC( BC_RGBA8888, 4, unsigned char, int, 0x0, 0xff);
799 DO_CUBIC( BC_YUV888, 3, unsigned char, int, 0x80, 0xff);
800 DO_CUBIC( BC_YUVA8888, 4, unsigned char, int, 0x80, 0xff);
801 DO_CUBIC( BC_RGB161616, 3, uint16_t, int, 0x0, 0xffff);
802 DO_CUBIC( BC_RGBA16161616, 4, uint16_t, int, 0x0, 0xffff);
803 DO_CUBIC( BC_YUV161616, 3, uint16_t, int, 0x8000, 0xffff);
804 DO_CUBIC( BC_YUVA16161616, 4, uint16_t, int, 0x8000, 0xffff);
811 int min_x = server->in_x * AFFINE_OVERSAMPLE;
812 int min_y = server->in_y * AFFINE_OVERSAMPLE;
813 int max_x = server->in_x * AFFINE_OVERSAMPLE + server->in_w * AFFINE_OVERSAMPLE - 1;
814 int max_y = server->in_y * AFFINE_OVERSAMPLE + server->in_h * AFFINE_OVERSAMPLE - 1;
815 float top_w = out_x2 - out_x1;
816 float bottom_w = out_x3 - out_x4;
817 float left_h = out_y4 - out_y1;
818 float right_h = out_y3 - out_y2;
819 float out_w_diff = bottom_w - top_w;
820 float out_left_diff = out_x4 - out_x1;
821 float out_h_diff = right_h - left_h;
822 float out_top_diff = out_y2 - out_y1;
823 float distance1 = DISTANCE(out_x1, out_y1, out_x2, out_y2);
824 float distance2 = DISTANCE(out_x2, out_y2, out_x3, out_y3);
825 float distance3 = DISTANCE(out_x3, out_y3, out_x4, out_y4);
826 float distance4 = DISTANCE(out_x4, out_y4, out_x1, out_y1);
827 float max_v = MAX(distance1, distance3);
828 float max_h = MAX(distance2, distance4);
829 float max_dimension = MAX(max_v, max_h);
830 float min_dimension = MIN(server->in_h, server->in_w);
831 float step = min_dimension / max_dimension / AFFINE_OVERSAMPLE;
832 float x_f = server->in_x;
833 float y_f = server->in_y;
834 float h_f = server->in_h;
835 float w_f = server->in_w;
837 if(server->use_opengl) {
842 #define DO_STRETCH(tag, type, components) \
844 type **in_rows = (type**)server->input->get_rows(); \
845 type **out_rows = (type**)server->temp->get_rows(); \
847 for(float in_y = pkg->y1; in_y < pkg->y2; in_y += step) \
850 type *in_row = in_rows[i]; \
851 for(float in_x = x_f; in_x < w_f; in_x += step) \
854 float in_x_fraction = (in_x - x_f) / w_f; \
855 float in_y_fraction = (in_y - y_f) / h_f; \
856 int out_x = (int)((out_x1 + \
857 out_left_diff * in_y_fraction + \
858 (top_w + out_w_diff * in_y_fraction) * in_x_fraction) * \
859 AFFINE_OVERSAMPLE); \
860 int out_y = (int)((out_y1 + \
861 out_top_diff * in_x_fraction + \
862 (left_h + out_h_diff * in_x_fraction) * in_y_fraction) * \
863 AFFINE_OVERSAMPLE); \
864 CLAMP(out_x, min_x, max_x); \
865 CLAMP(out_y, min_y, max_y); \
866 type *dst = out_rows[out_y] + out_x * components; \
867 type *src = in_row + j * components; \
871 if(components == 4) dst[3] = src[3]; \
876 switch( server->input->get_color_model() ) {
877 DO_STRETCH( BC_RGB_FLOAT, float, 3 );
878 DO_STRETCH( BC_RGB888, unsigned char, 3 );
879 DO_STRETCH( BC_RGBA_FLOAT, float, 4 );
880 DO_STRETCH( BC_RGBA8888, unsigned char, 4 );
881 DO_STRETCH( BC_YUV888, unsigned char, 3 );
882 DO_STRETCH( BC_YUVA8888, unsigned char, 4 );
883 DO_STRETCH( BC_RGB161616, uint16_t, 3 );
884 DO_STRETCH( BC_RGBA16161616, uint16_t, 4 );
885 DO_STRETCH( BC_YUV161616, uint16_t, 3 );
886 DO_STRETCH( BC_YUVA16161616, uint16_t, 4 );
892 AffineEngine::AffineEngine(int total_clients, int total_packages)
893 : LoadServer(total_clients, total_packages) //(1, 1)
895 user_in_viewport = 0;
897 user_out_viewport = 0;
900 in_x = in_y = in_w = in_h = 0;
901 out_x = out_y = out_w = out_h = 0;
902 in_pivot_x = in_pivot_y = 0;
903 out_pivot_x = out_pivot_y = 0;
904 interpolation = AF_DEFAULT;
905 this->total_packages = total_packages;
908 void AffineEngine::init_packages()
910 int y1 = 0, npkgs = get_total_packages();
911 for( int i=0; i<npkgs; ) {
912 AffinePackage *package = (AffinePackage*)get_package(i);
913 int y2 = out_y + (out_h * ++i / npkgs);
914 package->y1 = y1; package->y2 = y2; y1 = y2;
918 LoadClient* AffineEngine::new_client()
920 return new AffineUnit(this);
923 LoadPackage* AffineEngine::new_package()
925 return new AffinePackage;
928 void AffineEngine::process(VFrame *output, VFrame *input, VFrame *temp, int mode,
929 float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4,
934 // printf("AffineEngine::process %d %.2f %.2f %.2f %.2f %.2f %.2f %.2f %.2f\n",
935 // __LINE__, x1, y1, x2, y2, x3, y3, x4, y4);
937 // printf("AffineEngine::process %d %d %d %d %d\n",
938 // __LINE__, in_x, in_y, in_w, in_h);
940 // printf("AffineEngine::process %d %d %d %d %d\n",
941 // __LINE__, out_x, out_y, out_w, out_h);
943 // printf("AffineEngine::process %d %d %d %d %d\n",
944 // __LINE__, in_pivot_x, in_pivot_y, out_pivot_x, out_pivot_y);
946 // printf("AffineEngine::process %d %d %d %d %d\n",
947 // __LINE__, user_in_pivot, user_out_pivot, user_in_viewport, user_out_viewport);
949 this->output = output;
953 this->x1 = x1; this->y1 = y1;
954 this->x2 = x2; this->y2 = y2;
955 this->x3 = x3; this->y3 = y3;
956 this->x4 = x4; this->y4 = y4;
957 this->forward = forward;
959 if(!user_in_viewport) {
961 in_w = input->get_w();
962 in_h = input->get_h();
965 if(!user_out_viewport) {
966 out_x = 0; out_y = 0;
967 out_w = output->get_w();
968 out_h = output->get_h();
972 set_package_count(1);
976 set_package_count(total_packages);
984 void AffineEngine::rotate(VFrame *output,
988 this->output = output;
994 if( !user_in_viewport ) {
996 in_w = input->get_w();
997 in_h = input->get_h();
1003 // printf("AffineEngine::rotate %d %d %d %d %d\n", __LINE__, in_x, in_w, in_y, in_h);
1006 if( !user_in_pivot ) {
1007 in_pivot_x = in_x + in_w / 2;
1008 in_pivot_y = in_y + in_h / 2;
1011 if( !user_out_viewport ) {
1012 out_x = 0; out_y = 0;
1013 out_w = output->get_w();
1014 out_h = output->get_h();
1017 if( !user_out_pivot ) {
1018 out_pivot_x = out_x + out_w / 2;
1019 out_pivot_y = out_y + out_h / 2;
1022 // All subscripts are clockwise around the quadrangle
1023 angle = angle * 2 * M_PI / 360;
1024 double angle1 = atan((double)(in_pivot_y - in_y) / (double)(in_pivot_x - in_x)) + angle;
1025 double angle2 = atan((double)(in_x + in_w - in_pivot_x) / (double)(in_pivot_y - in_y)) + angle;
1026 double angle3 = atan((double)(in_y + in_h - in_pivot_y) / (double)(in_x + in_w - in_pivot_x)) + angle;
1027 double angle4 = atan((double)(in_pivot_x - in_x) / (double)(in_y + in_h - in_pivot_y)) + angle;
1028 double radius1 = DISTANCE(in_x, in_y, in_pivot_x, in_pivot_y);
1029 double radius2 = DISTANCE(in_x + in_w, in_y, in_pivot_x, in_pivot_y);
1030 double radius3 = DISTANCE(in_x + in_w, in_y + in_h, in_pivot_x, in_pivot_y);
1031 double radius4 = DISTANCE(in_x, in_y + in_h, in_pivot_x, in_pivot_y);
1033 x1 = ((in_pivot_x - in_x) - cos(angle1) * radius1) * 100 / in_w;
1034 y1 = ((in_pivot_y - in_y) - sin(angle1) * radius1) * 100 / in_h;
1035 x2 = ((in_pivot_x - in_x) + sin(angle2) * radius2) * 100 / in_w;
1036 y2 = ((in_pivot_y - in_y) - cos(angle2) * radius2) * 100 / in_h;
1037 x3 = ((in_pivot_x - in_x) + cos(angle3) * radius3) * 100 / in_w;
1038 y3 = ((in_pivot_y - in_y) + sin(angle3) * radius3) * 100 / in_h;
1039 x4 = ((in_pivot_x - in_x) - sin(angle4) * radius4) * 100 / in_w;
1040 y4 = ((in_pivot_y - in_y) + cos(angle4) * radius4) * 100 / in_h;
1042 // printf("AffineEngine::rotate angle=%f\n",
1046 // printf(" angle1=%f angle2=%f angle3=%f angle4=%f\n",
1047 // angle1 * 360 / 2 / M_PI, angle2 * 360 / 2 / M_PI,
1048 // angle3 * 360 / 2 / M_PI, angle4 * 360 / 2 / M_PI);
1050 // printf(" radius1=%f radius2=%f radius3=%f radius4=%f\n",
1051 // radius1, radius2, radius3, radius4);
1053 // printf(" x1=%f y1=%f x2=%f y2=%f x3=%f y3=%f x4=%f y4=%f\n",
1054 // x1 * w / 100, y1 * h / 100,
1055 // x2 * w / 100, y2 * h / 100,
1056 // x3 * w / 100, y3 * h / 100,
1057 // x4 * w / 100, y4 * h / 100);
1060 set_package_count(1);
1064 set_package_count(total_packages);
1069 void AffineEngine::set_matrix(AffineMatrix *matrix)
1071 for( int i=0; i<3; ++i ) {
1072 for( int j=0; j<3; ++j ) {
1073 this->matrix.values[i][j] = matrix->values[i][j];
1078 void AffineEngine::set_in_viewport(int x, int y, int w, int h)
1080 this->in_x = x; this->in_y = y;
1081 this->in_w = w; this->in_h = h;
1082 this->user_in_viewport = 1;
1085 void AffineEngine::set_out_viewport(int x, int y, int w, int h)
1087 this->out_x = x; this->out_y = y;
1088 this->out_w = w; this->out_h = h;
1089 this->user_out_viewport = 1;
1092 void AffineEngine::set_viewport(int x, int y, int w, int h)
1094 set_in_viewport(x, y, w, h);
1095 set_out_viewport(x, y, w, h);
1098 void AffineEngine::set_opengl(int value)
1100 this->use_opengl = value;
1103 void AffineEngine::set_in_pivot(int x, int y)
1105 this->in_pivot_x = x;
1106 this->in_pivot_y = y;
1107 this->user_in_pivot = 1;
1110 void AffineEngine::set_out_pivot(int x, int y)
1112 this->out_pivot_x = x;
1113 this->out_pivot_y = y;
1114 this->user_out_pivot = 1;
1117 void AffineEngine::set_pivot(int x, int y)
1120 set_out_pivot(x, y);
1123 void AffineEngine::unset_pivot()
1129 void AffineEngine::unset_viewport()
1131 user_in_viewport = 0;
1132 user_out_viewport = 0;
1136 void AffineEngine::set_interpolation(int type)
1138 interpolation = type;