/* affine transforms */ /* This file is part of VIPS. VIPS is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* */ #define DEBUG #ifdef HAVE_CONFIG_H #include #endif /*HAVE_CONFIG_H*/ #include #include #include #include #include #include #include #include #include #ifdef WITH_DMALLOC #include #endif /*WITH_DMALLOC*/ /* Calculate the inverse transformation. */ int im__transform_calc_inverse( Transformation *trn ) { DOUBLEMASK *msk, *msk2; if( !(msk = im_create_dmaskv( "boink", 2, 2, trn->a, trn->b, trn->c, trn->d )) ) return( -1 ); if( !(msk2 = im_matinv( msk, "boink2" )) ) { (void) im_free_dmask( msk ); return( -1 ); } trn->ia = msk2->coeff[0]; trn->ib = msk2->coeff[1]; trn->ic = msk2->coeff[2]; trn->id = msk2->coeff[3]; (void) im_free_dmask( msk ); (void) im_free_dmask( msk2 ); return( 0 ); } /* Init a Transform. */ void im__transform_init( Transformation *trn ) { trn->oarea.left = 0; trn->oarea.top = 0; trn->oarea.width = -1; trn->oarea.height = -1; trn->iarea.left = 0; trn->iarea.top = 0; trn->iarea.width = -1; trn->iarea.height = -1; trn->a = 1.0; /* Identity transform */ trn->b = 0.0; trn->c = 0.0; trn->d = 1.0; trn->dx = 0.0; trn->dy = 0.0; (void) im__transform_calc_inverse( trn ); } /* Test for transform is identity function. */ int im__transform_isidentity( const Transformation *trn ) { if( trn->a == 1.0 && trn->b == 0.0 && trn->c == 0.0 && trn->d == 1.0 && trn->dx == 0.0 && trn->dy == 0.0 ) return( 1 ); else return( 0 ); } /* Combine two transformations. out can be one of the ins. */ int im__transform_add( const Transformation *in1, const Transformation *in2, Transformation *out ) { out->a = in1->a * in2->a + in1->c * in2->b; out->b = in1->b * in2->a + in1->d * in2->b; out->c = in1->a * in2->c + in1->c * in2->d; out->d = in1->b * in2->c + in1->d * in2->d; out->dx = in1->dx * in2->a + in1->dy * in2->b + in2->dx; out->dy = in1->dx * in2->c + in1->dy * in2->d + in2->dy; if( im__transform_calc_inverse( out ) ) return( -1 ); return( 0 ); } void im__transform_print( const Transformation *trn ) { printf( "im__transform_print:\n" ); printf( " iarea: left=%d, top=%d, width=%d, height=%d\n", trn->iarea.left, trn->iarea.top, trn->iarea.width, trn->iarea.height ); printf( " oarea: left=%d, top=%d, width=%d, height=%d\n", trn->oarea.left, trn->oarea.top, trn->oarea.width, trn->oarea.height ); printf( " mat: a=%g, b=%g, c=%g, d=%g\n", trn->a, trn->b, trn->c, trn->d ); printf( " off: dx=%g, dy=%g\n", trn->dx, trn->dy ); } /* Map a pixel coordinate through the transform. */ void im__transform_forward_point( const Transformation *trn, const double x, const double y, /* In input space */ double *ox, double *oy ) /* In output space */ { *ox = trn->a * x + trn->b * y + trn->dx; *oy = trn->c * x + trn->d * y + trn->dy; } /* Map a pixel coordinate through the inverse transform. */ void im__transform_invert_point( const Transformation *trn, const double x, const double y, /* In output space */ double *ox, double *oy ) /* In input space */ { double mx = x - trn->dx; double my = y - trn->dy; *ox = trn->ia * mx + trn->ib * my; *oy = trn->ic * mx + trn->id * my; } typedef void (*transform_fn)( const Transformation *, const double, const double, double*, double* ); /* Transform a rect using a point transformer. */ static void transform_rect( const Transformation *trn, transform_fn transform, const Rect *in, /* In input space */ Rect *out ) /* In output space */ { double x1, y1; /* Map corners */ double x2, y2; double x3, y3; double x4, y4; double left, right, top, bottom; /* Map input Rect. */ transform( trn, in->left, in->top, &x1, &y1 ); transform( trn, in->left, IM_RECT_BOTTOM( in ), &x3, &y3 ); transform( trn, IM_RECT_RIGHT( in ), in->top, &x2, &y2 ); transform( trn, IM_RECT_RIGHT( in ), IM_RECT_BOTTOM( in ), &x4, &y4 ); /* Find bounding box for these four corners. Round-to-nearest to try * to stop rounding errors growing images. */ left = IM_MIN( x1, IM_MIN( x2, IM_MIN( x3, x4 ) ) ); right = IM_MAX( x1, IM_MAX( x2, IM_MAX( x3, x4 ) ) ); top = IM_MIN( y1, IM_MIN( y2, IM_MIN( y3, y4 ) ) ); bottom = IM_MAX( y1, IM_MAX( y2, IM_MAX( y3, y4 ) ) ); out->left = IM_RINT( left ); out->top = IM_RINT( top ); out->width = IM_RINT( right - left ); out->height = IM_RINT( bottom - top ); } /* Given an area in the input image, calculate the bounding box for those * pixels in the output image. */ void im__transform_forward_rect( const Transformation *trn, const Rect *in, /* In input space */ Rect *out ) /* In output space */ { transform_rect( trn, im__transform_forward_point, in, out ); } /* Given an area in the output image, calculate the bounding box for the * corresponding pixels in the input image. */ void im__transform_invert_rect( const Transformation *trn, const Rect *in, /* In output space */ Rect *out ) /* In input space */ { transform_rect( trn, im__transform_invert_point, in, out ); } /* Set output area of trn so that it just holds all of our input pels. */ void im__transform_set_area( Transformation *trn ) { im__transform_forward_rect( trn, &trn->iarea, &trn->oarea ); }