1 /* affine transform with a supplied interpolator.
2 *
3 * Copyright N. Dessipris
4 * Written on: 01/11/1991
5 * Modified on: 12/3/92 JC
6 * - rounding error in interpolation routine fixed
7 * - test for scale=1, angle=0 case fixed
8 * - clipping of output removed: redundant
9 * - various little tidies
10 * - problems remain with scale>20, size<10
11 *
12 * Re-written on: 20/08/92, J.Ph Laurent
13 *
14 * 21/02/93, JC
15 * - speed-ups
16 * - simplifications
17 * - im_similarity now calculates a window and calls this routine
18 * 6/7/93 JC
19 * - rewritten for partials
20 * - ANSIfied
21 * - now rotates any non-complex type
22 * 3/6/94 JC
23 * - C revised in bug search
24 * 9/6/94 JC
25 * - im_prepare() was preparing too small an area! oops
26 * 22/5/95 JC
27 * - added code to detect all-black output area case - helps lazy ip
28 * 3/7/95 JC
29 * - IM_CODING_LABQ handling moved to here
30 * 31/7/97 JC
31 * - dx/dy sign reversed to be less confusing ... now follows comment at
32 * top ... ax - by + dx etc.
33 * - tiny speed up, replaced the *++ on interpolation with [z]
34 * - im_similarity() moved in here
35 * - args swapped: was whxy, now xywh
36 * - didn't agree with dispatch fns before :(
37 * 3/3/98 JC
38 * - im_demand_hint() added
39 * 20/12/99 JC
40 * - im_affine() made from im_similarity_area()
41 * - transform stuff cleaned up a bit
42 * 14/4/01 JC
43 * - oops, invert_point() had a rounding problem
44 * 23/2/02 JC
45 * - pre-calculate interpolation matricies
46 * - integer interpolation for int8/16 types, double for
47 * int32/float/double
48 * - faster transformation
49 * 15/8/02 JC
50 * - records Xoffset/Yoffset
51 * 14/4/04
52 * - rounding, clipping and transforming revised, now pixel-perfect (or
53 * better than gimp, anyway)
54 * 22/6/05
55 * - all revised again, simpler and more reliable now
56 * 30/3/06
57 * - gah, still an occasional clipping problem
58 * 12/7/06
59 * - still more tweaking, gah again
60 * 7/10/06
61 * - set THINSTRIP for no-rotate affines
62 * 20/10/08
63 * - version with interpolate parameter, from im_affine()
64 * 30/10/08
65 * - allow complex image types
66 * 4/11/08
67 * - take an interpolator as a param
68 * - replace im_affine with this, provide an im_affine() compat wrapper
69 * - break transform stuff out to transform.c
70 * - revise clipping / transform stuff, again
71 * - now do corner rather than centre: this way the identity transform
72 * returns the input exactly
73 * 12/8/10
74 * - revise window_size / window_offset stuff again, see also
75 * interpolate.c
76 * 2/2/11
77 * - gtk-doc
78 * 14/12/12
79 * - redone as a class
80 * - added input space translation
81 * 22/1/14
82 * - auto RAD decode
83 * 1/8/14
84 * - revise transform ... again
85 * - see new stress test in nip2/test/extras
86 * 7/11/17
87 * - add "extend" param
88 * - add "background" parameter
89 * - better clipping means we have no jaggies on edges
90 * - premultiply alpha
91 * 18/5/20
92 * - add "premultiplied" flag
93 */
94
95 /*
96
97 This file is part of VIPS.
98
99 VIPS is free software; you can redistribute it and/or modify
100 it under the terms of the GNU Lesser General Public License as published by
101 the Free Software Foundation; either version 2 of the License, or
102 (at your option) any later version.
103
104 This program is distributed in the hope that it will be useful,
105 but WITHOUT ANY WARRANTY; without even the implied warranty of
106 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
107 GNU Lesser General Public License for more details.
108
109 You should have received a copy of the GNU Lesser General Public License
110 along with this program; if not, write to the Free Software
111 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
112 02110-1301 USA
113
114 */
115
116 /*
117
118 These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
119
120 */
121
122 /*
123 #define DEBUG_VERBOSE
124 #define DEBUG
125 #define VIPS_DEBUG
126 */
127
128 #ifdef HAVE_CONFIG_H
129 #include <config.h>
130 #endif /*HAVE_CONFIG_H*/
131 #include <vips/intl.h>
132
133 #include <stdio.h>
134 #include <stdlib.h>
135 #include <string.h>
136 #include <math.h>
137 #include <limits.h>
138
139 #include <vips/vips.h>
140 #include <vips/debug.h>
141 #include <vips/internal.h>
142 #include <vips/transform.h>
143
144 #include "presample.h"
145
146 typedef struct _VipsAffine {
147 VipsResample parent_instance;
148
149 VipsArea *matrix;
150 VipsInterpolate *interpolate;
151 VipsArea *oarea;
152 double odx;
153 double ody;
154 double idx;
155 double idy;
156
157 VipsTransformation trn;
158
159 /* How to generate extra edge pixels.
160 */
161 VipsExtend extend;
162
163 /* Background colour.
164 */
165 VipsArrayDouble *background;
166
167 /* The [double] converted to the input image format.
168 */
169 VipsPel *ink;
170
171 /* True if the input is already premultiplied (and we don't need to).
172 */
173 gboolean premultiplied;
174
175 } VipsAffine;
176
177 typedef VipsResampleClass VipsAffineClass;
178
179 G_DEFINE_TYPE( VipsAffine, vips_affine, VIPS_TYPE_RESAMPLE );
180
181 /* We have five (!!) coordinate systems. Working forward through them, these
182 * are:
183 *
184 * 1. The original input image. iarea is defined on this image.
185 *
186 * 2. This is embedded in a larger image to provide borders for the
187 * interpolator. window_offset and window_size control the embedding.
188 * These are the coordinates we pass to VIPS_REGION_ADDR()/
189 * vips_region_prepare() and the interpolator.
190 *
191 * The borders are sized by the interpolator's window_size property and offset
192 * by the interpolator's window_offset property. For example,
193 * for bilinear (window_size 2, window_offset 0) we add a single line
194 * of extra pixels along the bottom and right (window_size - 1). For
195 * bicubic (window_size 4, window_offset 1) we add a single line top and left
196 * (window_offset), and two lines bottom and right (window_size - 1 -
197 * window_offset).
198 *
199 * 3. We need point (0, 0) in (1) to be at (0, 0) for the transformation. So
200 * shift everything up and left to make the displaced input image. This is the
201 * space that the transformation maps from, and can have negative pixels
202 * (up and left of the image, for interpolation). iarea works here too.
203 *
204 * 4. Output transform space. This is the where the transform maps to. Pixels
205 * can be negative, since a rotated image can go up and left of the origin.
206 *
207 * 5. Output image space. This is the wh of the xywh passed to vips_affine()
208 * below. These are the coordinates we pass to VIPS_REGION_ADDR() for the
209 * output image, and that affinei_gen() is asked for.
210 */
211
212 static int
vips_affine_gen(VipsRegion * or,void * seq,void * a,void * b,gboolean * stop)213 vips_affine_gen( VipsRegion *or, void *seq, void *a, void *b, gboolean *stop )
214 {
215 VipsRegion *ir = (VipsRegion *) seq;
216 const VipsAffine *affine = (VipsAffine *) b;
217 const VipsImage *in = (VipsImage *) a;
218 const int window_size =
219 vips_interpolate_get_window_size( affine->interpolate );
220 const int window_offset =
221 vips_interpolate_get_window_offset( affine->interpolate );
222 const VipsInterpolateMethod interpolate =
223 vips_interpolate_get_method( affine->interpolate );
224
225 /* Area we generate in the output image.
226 */
227 const VipsRect *r = &or->valid;
228 const int le = r->left;
229 const int ri = VIPS_RECT_RIGHT( r );
230 const int to = r->top;
231 const int bo = VIPS_RECT_BOTTOM( r );
232
233 const VipsRect *iarea = &affine->trn.iarea;
234 const VipsRect *oarea = &affine->trn.oarea;
235
236 int ps = VIPS_IMAGE_SIZEOF_PEL( in );
237 int x, y, z;
238
239 VipsRect image, want, need, clipped;
240
241 #ifdef DEBUG_VERBOSE
242 printf( "vips_affine_gen: "
243 "generating left=%d, top=%d, width=%d, height=%d\n",
244 r->left,
245 r->top,
246 r->width,
247 r->height );
248 #endif /*DEBUG_VERBOSE*/
249
250 /* We are generating this chunk of the transformed image. This takes
251 * us to space 4.
252 */
253 want = *r;
254 want.left += oarea->left;
255 want.top += oarea->top;
256
257 /* Find the area of the input image we need. This takes us to space 3.
258 */
259 vips__transform_invert_rect( &affine->trn, &want, &need );
260
261 /* That does round-to-nearest, because it has to stop rounding errors
262 * growing images unexpectedly. We need round-down, so we must
263 * add half a pixel along the left and top. But we are int :( so add 1
264 * pixel.
265 *
266 * Add an extra line along the right and bottom as well, for rounding.
267 */
268 vips_rect_marginadjust( &need, 1 );
269
270 /* We need to fetch a larger area for the interpolator.
271 */
272 need.left -= window_offset;
273 need.top -= window_offset;
274 need.width += window_size - 1;
275 need.height += window_size - 1;
276
277 /* Now go to space 2, the expanded input image. This is the one we
278 * read pixels from.
279 */
280 need.left += window_offset;
281 need.top += window_offset;
282
283 /* Clip against the size of (2).
284 */
285 image.left = 0;
286 image.top = 0;
287 image.width = in->Xsize;
288 image.height = in->Ysize;
289 vips_rect_intersectrect( &need, &image, &clipped );
290
291 #ifdef DEBUG_VERBOSE
292 printf( "vips_affine_gen: "
293 "preparing left=%d, top=%d, width=%d, height=%d\n",
294 clipped.left,
295 clipped.top,
296 clipped.width,
297 clipped.height );
298 #endif /*DEBUG_VERBOSE*/
299
300 if( vips_rect_isempty( &clipped ) ) {
301 vips_region_paint_pel( or, r, affine->ink );
302 return( 0 );
303 }
304 if( vips_region_prepare( ir, &clipped ) )
305 return( -1 );
306
307 VIPS_GATE_START( "vips_affine_gen: work" );
308
309 /* Resample! x/y loop over pixels in the output image (5).
310 */
311 for( y = to; y < bo; y++ ) {
312 /* Input clipping rectangle. We offset this so we can clip in
313 * space 2.
314 */
315 const int ile = iarea->left + window_offset;
316 const int ito = iarea->top + window_offset;
317 const int iri = ile + iarea->width;
318 const int ibo = ito + iarea->height;
319
320 /* Derivative of matrix.
321 */
322 const double ddx = affine->trn.ia;
323 const double ddy = affine->trn.ic;
324
325 /* Continuous cods in transformed space.
326 */
327 const double ox = le + oarea->left - affine->trn.odx;
328 const double oy = y + oarea->top - affine->trn.ody;
329
330 /* Continuous cods in input space.
331 */
332 double ix, iy;
333
334 VipsPel *q;
335
336 /* To (3).
337 */
338 ix = affine->trn.ia * ox + affine->trn.ib * oy;
339 iy = affine->trn.ic * ox + affine->trn.id * oy;
340
341 /* And the input offset in (3).
342 */
343 ix -= affine->trn.idx;
344 iy -= affine->trn.idy;
345
346 /* Finally to 2.
347 */
348 ix += window_offset;
349 iy += window_offset;
350
351 q = VIPS_REGION_ADDR( or, le, y );
352
353 for( x = le; x < ri; x++ ) {
354 int fx, fy;
355
356 fx = VIPS_FLOOR( ix );
357 fy = VIPS_FLOOR( iy );
358
359 /* Clip against iarea.
360 */
361 if( fx >= ile &&
362 fx <= iri &&
363 fy >= ito &&
364 fy <= ibo ) {
365 /* Verify that we can read the whole stencil.
366 * With DEBUG on this will range-check.
367 */
368 g_assert( VIPS_REGION_ADDR( ir,
369 (int) ix - window_offset,
370 (int) iy - window_offset ) );
371 g_assert( VIPS_REGION_ADDR( ir,
372 (int) ix - window_offset +
373 window_size - 1,
374 (int) iy - window_offset +
375 window_size - 1 ) );
376
377 interpolate( affine->interpolate,
378 q, ir, ix, iy );
379 }
380 else {
381 /* Out of range: paint the background.
382 */
383 for( z = 0; z < ps; z++ )
384 q[z] = affine->ink[z];
385 }
386
387 ix += ddx;
388 iy += ddy;
389 q += ps;
390 }
391 }
392
393 VIPS_GATE_STOP( "vips_affine_gen: work" );
394
395 VIPS_COUNT_PIXELS( or, "vips_affine_gen" );
396
397 return( 0 );
398 }
399
400 static int
vips_affine_build(VipsObject * object)401 vips_affine_build( VipsObject *object )
402 {
403 VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object );
404 VipsResample *resample = VIPS_RESAMPLE( object );
405 VipsAffine *affine = (VipsAffine *) object;
406 VipsImage **t = (VipsImage **) vips_object_local_array( object, 7 );
407
408 VipsImage *in;
409 VipsDemandStyle hint;
410 int window_size;
411 int window_offset;
412 double edge;
413
414 /* TRUE if we've premultiplied and need to unpremultiply.
415 */
416 gboolean have_premultiplied;
417 VipsBandFormat unpremultiplied_format;
418
419 if( VIPS_OBJECT_CLASS( vips_affine_parent_class )->build( object ) )
420 return( -1 );
421
422 if( vips_check_coding_known( class->nickname, resample->in ) )
423 return( -1 );
424 if( vips_check_vector_length( class->nickname,
425 affine->matrix->n, 4 ) )
426 return( -1 );
427 if( vips_object_argument_isset( object, "oarea" ) &&
428 vips_check_vector_length( class->nickname,
429 affine->oarea->n, 4 ) )
430 return( -1 );
431
432 /* Can be set explicitly to NULL to mean default setting.
433 */
434 if( !affine->interpolate )
435 affine->interpolate = vips_interpolate_new( "bilinear" );
436
437 in = resample->in;
438
439 /* Set up transform.
440 */
441
442 window_size = vips_interpolate_get_window_size( affine->interpolate );
443 window_offset =
444 vips_interpolate_get_window_offset( affine->interpolate );
445
446 affine->trn.iarea.left = 0;
447 affine->trn.iarea.top = 0;
448 affine->trn.iarea.width = in->Xsize;
449 affine->trn.iarea.height = in->Ysize;
450 affine->trn.a = ((double *) affine->matrix->data)[0];
451 affine->trn.b = ((double *) affine->matrix->data)[1];
452 affine->trn.c = ((double *) affine->matrix->data)[2];
453 affine->trn.d = ((double *) affine->matrix->data)[3];
454 affine->trn.idx = 0;
455 affine->trn.idy = 0;
456 affine->trn.odx = 0;
457 affine->trn.ody = 0;
458
459 if( vips__transform_calc_inverse( &affine->trn ) )
460 return( -1 );
461
462 /* Set the default value for oarea.
463 */
464 vips__transform_set_area( &affine->trn );
465
466 if( vips_object_argument_isset( object, "oarea" ) ) {
467 affine->trn.oarea.left = ((int *) affine->oarea->data)[0];
468 affine->trn.oarea.top = ((int *) affine->oarea->data)[1];
469 affine->trn.oarea.width = ((int *) affine->oarea->data)[2];
470 affine->trn.oarea.height = ((int *) affine->oarea->data)[3];
471 }
472
473 if( vips_object_argument_isset( object, "odx" ) )
474 affine->trn.odx = affine->odx;
475 if( vips_object_argument_isset( object, "ody" ) )
476 affine->trn.ody = affine->ody;
477
478 if( vips_object_argument_isset( object, "idx" ) )
479 affine->trn.idx = affine->idx;
480 if( vips_object_argument_isset( object, "idy" ) )
481 affine->trn.idy = affine->idy;
482
483 #ifdef DEBUG
484 printf( "vips_affine_build: copy on identity transform disabled\n" );
485 #else /*!DEBUG*/
486 if( vips__transform_isidentity( &affine->trn ) &&
487 affine->trn.oarea.left == 0 &&
488 affine->trn.oarea.top == 0 &&
489 affine->trn.oarea.width == in->Xsize &&
490 affine->trn.oarea.height == in->Ysize )
491 return( vips_image_write( in, resample->out ) );
492 #endif /*!DEBUG*/
493
494 /* Check for coordinate overflow ... we want to be able to hold the
495 * output space inside INT_MAX / TRANSFORM_SCALE.
496 */
497 edge = (int) (INT_MAX / VIPS_TRANSFORM_SCALE);
498 if( affine->trn.oarea.left < -edge ||
499 affine->trn.oarea.top < -edge ||
500 VIPS_RECT_RIGHT( &affine->trn.oarea ) > edge ||
501 VIPS_RECT_BOTTOM( &affine->trn.oarea ) > edge ) {
502 vips_error( class->nickname,
503 "%s", _( "output coordinates out of range" ) );
504 return( -1 );
505 }
506
507 if( vips_image_decode( in, &t[0] ) )
508 return( -1 );
509 in = t[0];
510
511 /* Add new pixels around the input so we can interpolate at the edges.
512 *
513 * We add the interpolate stencil, plus one extra pixel on all the
514 * edges. This means when we clip in generate (above) we can be sure
515 * we clip outside the real pixels and don't get jaggies on edges.
516 */
517 if( vips_embed( in, &t[2],
518 window_offset + 1, window_offset + 1,
519 in->Xsize + window_size - 1 + 2,
520 in->Ysize + window_size - 1 + 2,
521 "extend", affine->extend,
522 "background", affine->background,
523 NULL ) )
524 return( -1 );
525 in = t[2];
526
527 /* We've added a one-pixel border to the input: displace the transform
528 * to compensate.
529 */
530 affine->trn.idx -= 1;
531 affine->trn.idy -= 1;
532
533 /* If there's an alpha and we've not premultiplied, we have to
534 * premultiply before resampling. See
535 * https://github.com/libvips/libvips/issues/291
536 */
537 have_premultiplied = FALSE;
538 if( vips_image_hasalpha( in ) &&
539 !affine->premultiplied ) {
540 if( vips_premultiply( in, &t[3], NULL ) )
541 return( -1 );
542 have_premultiplied = TRUE;
543
544 /* vips_premultiply() makes a float image. When we
545 * vips_unpremultiply() below, we need to cast back to the
546 * pre-premultiply format.
547 */
548 unpremultiplied_format = in->BandFmt;
549 in = t[3];
550 }
551
552 /* Convert the background to the image's format.
553 */
554 if( !(affine->ink = vips__vector_to_ink( class->nickname,
555 in,
556 VIPS_AREA( affine->background )->data, NULL,
557 VIPS_AREA( affine->background )->n )) )
558 return( -1 );
559
560 /* Normally SMALLTILE ... except if this is strictly a size
561 * up/down affine.
562 */
563 if( affine->trn.b == 0.0 &&
564 affine->trn.c == 0.0 )
565 hint = VIPS_DEMAND_STYLE_FATSTRIP;
566 else
567 hint = VIPS_DEMAND_STYLE_SMALLTILE;
568
569 t[4] = vips_image_new();
570 if( vips_image_pipelinev( t[4], hint, in, NULL ) )
571 return( -1 );
572
573 t[4]->Xsize = affine->trn.oarea.width;
574 t[4]->Ysize = affine->trn.oarea.height;
575
576 #ifdef DEBUG
577 printf( "vips_affine_build: transform: " );
578 vips__transform_print( &affine->trn );
579 printf( " window_offset = %d, window_size = %d\n",
580 window_offset, window_size );
581 printf( " input image width = %d, height = %d\n",
582 in->Xsize, in->Ysize );
583 printf( " output image width = %d, height = %d\n",
584 t[4]->Xsize, t[4]->Ysize );
585 #endif /*DEBUG*/
586
587 /* Generate!
588 */
589 if( vips_image_generate( t[4],
590 vips_start_one, vips_affine_gen, vips_stop_one,
591 in, affine ) )
592 return( -1 );
593
594 /* Finally: can now set Xoffset/Yoffset.
595 */
596 t[4]->Xoffset = affine->trn.odx - affine->trn.oarea.left;
597 t[4]->Yoffset = affine->trn.ody - affine->trn.oarea.top;
598
599 in = t[4];
600
601 if( have_premultiplied ) {
602 if( vips_unpremultiply( in, &t[5], NULL ) ||
603 vips_cast( t[5], &t[6], unpremultiplied_format, NULL ) )
604 return( -1 );
605 in = t[6];
606 }
607
608 if( vips_image_write( in, resample->out ) )
609 return( -1 );
610
611 return( 0 );
612 }
613
614 static void
vips_affine_class_init(VipsAffineClass * class)615 vips_affine_class_init( VipsAffineClass *class )
616 {
617 GObjectClass *gobject_class = G_OBJECT_CLASS( class );
618 VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS( class );
619
620 VIPS_DEBUG_MSG( "vips_affine_class_init\n" );
621
622 gobject_class->set_property = vips_object_set_property;
623 gobject_class->get_property = vips_object_get_property;
624
625 vobject_class->nickname = "affine";
626 vobject_class->description = _( "affine transform of an image" );
627 vobject_class->build = vips_affine_build;
628
629 VIPS_ARG_BOXED( class, "matrix", 110,
630 _( "Matrix" ),
631 _( "Transformation matrix" ),
632 VIPS_ARGUMENT_REQUIRED_INPUT,
633 G_STRUCT_OFFSET( VipsAffine, matrix ),
634 VIPS_TYPE_ARRAY_DOUBLE );
635
636 VIPS_ARG_INTERPOLATE( class, "interpolate", 2,
637 _( "Interpolate" ),
638 _( "Interpolate pixels with this" ),
639 VIPS_ARGUMENT_OPTIONAL_INPUT,
640 G_STRUCT_OFFSET( VipsAffine, interpolate ) );
641
642 VIPS_ARG_BOXED( class, "oarea", 111,
643 _( "Output rect" ),
644 _( "Area of output to generate" ),
645 VIPS_ARGUMENT_OPTIONAL_INPUT,
646 G_STRUCT_OFFSET( VipsAffine, oarea ),
647 VIPS_TYPE_ARRAY_INT );
648
649 VIPS_ARG_DOUBLE( class, "odx", 112,
650 _( "Output offset" ),
651 _( "Horizontal output displacement" ),
652 VIPS_ARGUMENT_OPTIONAL_INPUT,
653 G_STRUCT_OFFSET( VipsAffine, odx ),
654 -10000000, 10000000, 0 );
655
656 VIPS_ARG_DOUBLE( class, "ody", 113,
657 _( "Output offset" ),
658 _( "Vertical output displacement" ),
659 VIPS_ARGUMENT_OPTIONAL_INPUT,
660 G_STRUCT_OFFSET( VipsAffine, ody ),
661 -10000000, 10000000, 0 );
662
663 VIPS_ARG_DOUBLE( class, "idx", 114,
664 _( "Input offset" ),
665 _( "Horizontal input displacement" ),
666 VIPS_ARGUMENT_OPTIONAL_INPUT,
667 G_STRUCT_OFFSET( VipsAffine, idx ),
668 -10000000, 10000000, 0 );
669
670 VIPS_ARG_DOUBLE( class, "idy", 115,
671 _( "Input offset" ),
672 _( "Vertical input displacement" ),
673 VIPS_ARGUMENT_OPTIONAL_INPUT,
674 G_STRUCT_OFFSET( VipsAffine, idy ),
675 -10000000, 10000000, 0 );
676
677 VIPS_ARG_ENUM( class, "extend", 117,
678 _( "Extend" ),
679 _( "How to generate the extra pixels" ),
680 VIPS_ARGUMENT_OPTIONAL_INPUT,
681 G_STRUCT_OFFSET( VipsAffine, extend ),
682 VIPS_TYPE_EXTEND, VIPS_EXTEND_BACKGROUND );
683
684 VIPS_ARG_BOXED( class, "background", 116,
685 _( "Background" ),
686 _( "Background value" ),
687 VIPS_ARGUMENT_OPTIONAL_INPUT,
688 G_STRUCT_OFFSET( VipsAffine, background ),
689 VIPS_TYPE_ARRAY_DOUBLE );
690
691 VIPS_ARG_BOOL( class, "premultiplied", 117,
692 _( "Premultiplied" ),
693 _( "Images have premultiplied alpha" ),
694 VIPS_ARGUMENT_OPTIONAL_INPUT,
695 G_STRUCT_OFFSET( VipsAffine, premultiplied ),
696 FALSE );
697
698 }
699
700 static void
vips_affine_init(VipsAffine * affine)701 vips_affine_init( VipsAffine *affine )
702 {
703 affine->extend = VIPS_EXTEND_BACKGROUND;
704 affine->background = vips_array_double_newv( 1, 0.0 );
705 }
706
707 /**
708 * vips_affine: (method)
709 * @in: input image
710 * @out: (out): output image
711 * @a: transformation matrix coefficient
712 * @b: transformation matrix coefficient
713 * @c: transformation matrix coefficient
714 * @d: transformation matrix coefficient
715 * @...: %NULL-terminated list of optional named arguments
716 *
717 * Optional arguments:
718 *
719 * * @interpolate: #VipsInterpolate, interpolate pixels with this
720 * * @oarea: #VipsArrayInt, output rectangle
721 * * @idx: %gdouble, input horizontal offset
722 * * @idy: %gdouble, input vertical offset
723 * * @odx: %gdouble, output horizontal offset
724 * * @ody: %gdouble, output vertical offset
725 * * @extend: #VipsExtend how to generate new pixels
726 * * @background: #VipsArrayDouble colour for new pixels
727 * * @premultiplied: %gboolean, images are already premultiplied
728 *
729 * This operator performs an affine transform on an image using @interpolate.
730 *
731 * The transform is:
732 *
733 * |[
734 * X = @a * (x + @idx) + @b * (y + @idy) + @odx
735 * Y = @c * (x + @idx) + @d * (y + @idy) + @doy
736 *
737 * where:
738 * x and y are the coordinates in input image.
739 * X and Y are the coordinates in output image.
740 * (0,0) is the upper left corner.
741 * ]|
742 *
743 * The section of the output space defined by @oarea is written to
744 * @out. @oarea is a four-element int array of left, top, width, height.
745 * By default @oarea is just large enough to cover the whole of the
746 * transformed input image.
747 *
748 * By default, new pixels are filled with @background. This defaults to
749 * zero (black). You can set other extend types with @extend. #VIPS_EXTEND_COPY
750 * is better for image upsizing.
751 *
752 * @interpolate defaults to bilinear.
753 *
754 * @idx, @idy, @odx, @ody default to zero.
755 *
756 * Image are normally treated as unpremultiplied, so this operation can be used
757 * directly on PNG images. If your images have been through vips_premultiply(),
758 * set @premultiplied.
759 *
760 * This operation does not change xres or yres. The image resolution needs to
761 * be updated by the application.
762 *
763 * See also: vips_shrink(), vips_resize(), #VipsInterpolate.
764 *
765 * Returns: 0 on success, -1 on error
766 */
767 int
vips_affine(VipsImage * in,VipsImage ** out,double a,double b,double c,double d,...)768 vips_affine( VipsImage *in, VipsImage **out,
769 double a, double b, double c, double d, ... )
770 {
771 va_list ap;
772 VipsArea *matrix;
773 int result;
774
775 matrix = VIPS_AREA( vips_array_double_newv( 4, a, b, c, d ) );
776
777 va_start( ap, d );
778 result = vips_call_split( "affine", ap, in, out, matrix );
779 va_end( ap );
780
781 vips_area_unref( matrix );
782
783 return( result );
784 }
785