blob: 9afc2deac9001704baca316673feb5ae0faa12ed [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% DDDD IIIII SSSSS TTTTT OOO RRRR TTTTT %
7% D D I SS T O O R R T %
8% D D I SSS T O O RRRR T %
9% D D I SS T O O R R T %
10% DDDD IIIII SSSSS T OOO R R T %
11% %
12% %
13% MagickCore Image Distortion Methods %
14% %
15% Software Design %
16% John Cristy %
17% Anthony Thyssen %
18% June 2007 %
19% %
20% %
cristy16af1cb2009-12-11 21:38:29 +000021% Copyright 1999-2010 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000022% dedicated to making software imaging solutions freely available. %
23% %
24% You may not use this file except in compliance with the License. You may %
25% obtain a copy of the License at %
26% %
27% http://www.imagemagick.org/script/license.php %
28% %
29% Unless required by applicable law or agreed to in writing, software %
30% distributed under the License is distributed on an "AS IS" BASIS, %
31% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
32% See the License for the specific language governing permissions and %
33% limitations under the License. %
34% %
35%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
36%
37%
38*/
39
40/*
41 Include declarations.
42*/
43#include "magick/studio.h"
44#include "magick/artifact.h"
45#include "magick/cache.h"
46#include "magick/cache-view.h"
47#include "magick/colorspace-private.h"
48#include "magick/composite-private.h"
49#include "magick/distort.h"
50#include "magick/exception.h"
51#include "magick/exception-private.h"
52#include "magick/gem.h"
53#include "magick/hashmap.h"
54#include "magick/image.h"
55#include "magick/list.h"
56#include "magick/matrix.h"
57#include "magick/memory_.h"
58#include "magick/monitor-private.h"
anthony4cf1d892010-03-29 03:12:20 +000059#include "magick/option.h"
cristy3ed852e2009-09-05 21:47:34 +000060#include "magick/pixel.h"
61#include "magick/pixel-private.h"
62#include "magick/resample.h"
63#include "magick/resample-private.h"
64#include "magick/registry.h"
65#include "magick/semaphore.h"
66#include "magick/string_.h"
cristyf2f27272009-12-17 14:48:46 +000067#include "magick/string-private.h"
cristy3ed852e2009-09-05 21:47:34 +000068#include "magick/thread-private.h"
69#include "magick/token.h"
70
71/*
72 Numerous internal routines for image distortions.
73*/
74static inline double MagickMin(const double x,const double y)
75{
76 return( x < y ? x : y);
77}
78static inline double MagickMax(const double x,const double y)
79{
80 return( x > y ? x : y);
81}
82
83static inline void AffineArgsToCoefficients(double *affine)
84{
85 /* map external sx,ry,rx,sy,tx,ty to internal c0,c2,c4,c1,c3,c5 */
86 double tmp[4]; /* note indexes 0 and 5 remain unchanged */
87 tmp[0]=affine[1]; tmp[1]=affine[2]; tmp[2]=affine[3]; tmp[3]=affine[4];
88 affine[3]=tmp[0]; affine[1]=tmp[1]; affine[4]=tmp[2]; affine[2]=tmp[3];
89}
cristy39052ff2010-01-10 15:38:47 +000090
cristy3ed852e2009-09-05 21:47:34 +000091static inline void CoefficientsToAffineArgs(double *coeff)
92{
93 /* map internal c0,c1,c2,c3,c4,c5 to external sx,ry,rx,sy,tx,ty */
94 double tmp[4]; /* note indexes 0 and 5 remain unchanged */
95 tmp[0]=coeff[3]; tmp[1]=coeff[1]; tmp[2]=coeff[4]; tmp[3]=coeff[2];
96 coeff[1]=tmp[0]; coeff[2]=tmp[1]; coeff[3]=tmp[2]; coeff[4]=tmp[3];
97}
98static void InvertAffineCoefficients(const double *coeff,double *inverse)
99{
100 /* From "Digital Image Warping" by George Wolberg, page 50 */
101 double determinant;
102
103 determinant=1.0/(coeff[0]*coeff[4]-coeff[1]*coeff[3]);
104 inverse[0]=determinant*coeff[4];
105 inverse[1]=determinant*(-coeff[1]);
106 inverse[2]=determinant*(coeff[1]*coeff[5]-coeff[2]*coeff[4]);
107 inverse[3]=determinant*(-coeff[3]);
108 inverse[4]=determinant*coeff[0];
109 inverse[5]=determinant*(coeff[2]*coeff[3]-coeff[0]*coeff[5]);
110}
111
112static void InvertPerspectiveCoefficients(const double *coeff,
113 double *inverse)
114{
115 /* From "Digital Image Warping" by George Wolberg, page 53 */
116 double determinant;
117
118 determinant=1.0/(coeff[0]*coeff[4]-coeff[3]*coeff[1]);
119 inverse[0]=determinant*(coeff[4]-coeff[7]*coeff[5]);
120 inverse[1]=determinant*(coeff[7]*coeff[2]-coeff[1]);
121 inverse[2]=determinant*(coeff[1]*coeff[5]-coeff[4]*coeff[2]);
122 inverse[3]=determinant*(coeff[6]*coeff[5]-coeff[3]);
123 inverse[4]=determinant*(coeff[0]-coeff[6]*coeff[2]);
124 inverse[5]=determinant*(coeff[3]*coeff[2]-coeff[0]*coeff[5]);
125 inverse[6]=determinant*(coeff[3]*coeff[7]-coeff[6]*coeff[4]);
126 inverse[7]=determinant*(coeff[6]*coeff[1]-coeff[0]*coeff[7]);
127}
128
129static inline double MagickRound(double x)
130{
cristy06609ee2010-03-17 20:21:27 +0000131 /*
132 Round the fraction to nearest integer.
133 */
cristy3ed852e2009-09-05 21:47:34 +0000134 if (x >= 0.0)
cristybb503372010-05-27 20:51:26 +0000135 return((double) ((ssize_t) (x+0.5)));
136 return((double) ((ssize_t) (x-0.5)));
cristy3ed852e2009-09-05 21:47:34 +0000137}
138
anthony5056b232009-10-10 13:18:06 +0000139/*
140 * Polynomial Term Defining Functions
141 *
142 * Order must either be an integer, or 1.5 to produce
143 * the 2 number_valuesal polyminal function...
144 * affine 1 (3) u = c0 + c1*x + c2*y
145 * bilinear 1.5 (4) u = '' + c3*x*y
146 * quadratic 2 (6) u = '' + c4*x*x + c5*y*y
147 * cubic 3 (10) u = '' + c6*x^3 + c7*x*x*y + c8*x*y*y + c9*y^3
148 * quartic 4 (15) u = '' + c10*x^4 + ... + c14*y^4
149 * quintic 5 (21) u = '' + c15*x^5 + ... + c20*y^5
150 * number in parenthesis minimum number of points needed.
151 * Anything beyond quintic, has not been implemented until
152 * a more automated way of determined terms is found.
153
154 * Note the slight re-ordering of the terms for a quadratic polynomial
155 * which is to allow the use of a bi-linear (order=1.5) polynomial.
156 * All the later polynomials are ordered simply from x^N to y^N
157 */
cristybb503372010-05-27 20:51:26 +0000158static size_t poly_number_terms(double order)
cristy3ed852e2009-09-05 21:47:34 +0000159{
anthony5056b232009-10-10 13:18:06 +0000160 /* Return the number of terms for a 2d polynomial */
cristy3ed852e2009-09-05 21:47:34 +0000161 if ( order < 1 || order > 5 ||
162 ( order != floor(order) && (order-1.5) > MagickEpsilon) )
163 return 0; /* invalid polynomial order */
cristybb503372010-05-27 20:51:26 +0000164 return((size_t) floor((order+1)*(order+2)/2));
cristy3ed852e2009-09-05 21:47:34 +0000165}
166
cristybb503372010-05-27 20:51:26 +0000167static double poly_basis_fn(ssize_t n, double x, double y)
cristy3ed852e2009-09-05 21:47:34 +0000168{
anthony5056b232009-10-10 13:18:06 +0000169 /* Return the result for this polynomial term */
cristy3ed852e2009-09-05 21:47:34 +0000170 switch(n) {
171 case 0: return( 1.0 ); /* constant */
172 case 1: return( x );
173 case 2: return( y ); /* affine order = 1 terms = 3 */
174 case 3: return( x*y ); /* bilinear order = 1.5 terms = 4 */
175 case 4: return( x*x );
176 case 5: return( y*y ); /* quadratic order = 2 terms = 6 */
177 case 6: return( x*x*x );
178 case 7: return( x*x*y );
179 case 8: return( x*y*y );
180 case 9: return( y*y*y ); /* cubic order = 3 terms = 10 */
181 case 10: return( x*x*x*x );
182 case 11: return( x*x*x*y );
183 case 12: return( x*x*y*y );
184 case 13: return( x*y*y*y );
185 case 14: return( y*y*y*y ); /* quartic order = 4 terms = 15 */
186 case 15: return( x*x*x*x*x );
187 case 16: return( x*x*x*x*y );
188 case 17: return( x*x*x*y*y );
189 case 18: return( x*x*y*y*y );
190 case 19: return( x*y*y*y*y );
191 case 20: return( y*y*y*y*y ); /* quintic order = 5 terms = 21 */
192 }
193 return( 0 ); /* should never happen */
194}
cristybb503372010-05-27 20:51:26 +0000195static const char *poly_basis_str(ssize_t n)
cristy3ed852e2009-09-05 21:47:34 +0000196{
197 /* return the result for this polynomial term */
198 switch(n) {
199 case 0: return(""); /* constant */
200 case 1: return("*ii");
201 case 2: return("*jj"); /* affiine order = 1 terms = 3 */
202 case 3: return("*ii*jj"); /* biiliinear order = 1.5 terms = 4 */
203 case 4: return("*ii*ii");
204 case 5: return("*jj*jj"); /* quadratiic order = 2 terms = 6 */
205 case 6: return("*ii*ii*ii");
206 case 7: return("*ii*ii*jj");
207 case 8: return("*ii*jj*jj");
208 case 9: return("*jj*jj*jj"); /* cubiic order = 3 terms = 10 */
209 case 10: return("*ii*ii*ii*ii");
210 case 11: return("*ii*ii*ii*jj");
211 case 12: return("*ii*ii*jj*jj");
212 case 13: return("*ii*jj*jj*jj");
213 case 14: return("*jj*jj*jj*jj"); /* quartiic order = 4 terms = 15 */
214 case 15: return("*ii*ii*ii*ii*ii");
215 case 16: return("*ii*ii*ii*ii*jj");
216 case 17: return("*ii*ii*ii*jj*jj");
217 case 18: return("*ii*ii*jj*jj*jj");
218 case 19: return("*ii*jj*jj*jj*jj");
219 case 20: return("*jj*jj*jj*jj*jj"); /* quiintiic order = 5 terms = 21 */
220 }
221 return( "UNKNOWN" ); /* should never happen */
222}
cristybb503372010-05-27 20:51:26 +0000223static double poly_basis_dx(ssize_t n, double x, double y)
cristy3ed852e2009-09-05 21:47:34 +0000224{
225 /* polynomial term for x derivative */
226 switch(n) {
227 case 0: return( 0.0 ); /* constant */
228 case 1: return( 1.0 );
229 case 2: return( 0.0 ); /* affine order = 1 terms = 3 */
230 case 3: return( y ); /* bilinear order = 1.5 terms = 4 */
231 case 4: return( x );
232 case 5: return( 0.0 ); /* quadratic order = 2 terms = 6 */
233 case 6: return( x*x );
234 case 7: return( x*y );
235 case 8: return( y*y );
236 case 9: return( 0.0 ); /* cubic order = 3 terms = 10 */
237 case 10: return( x*x*x );
238 case 11: return( x*x*y );
239 case 12: return( x*y*y );
240 case 13: return( y*y*y );
241 case 14: return( 0.0 ); /* quartic order = 4 terms = 15 */
242 case 15: return( x*x*x*x );
243 case 16: return( x*x*x*y );
244 case 17: return( x*x*y*y );
245 case 18: return( x*y*y*y );
246 case 19: return( y*y*y*y );
247 case 20: return( 0.0 ); /* quintic order = 5 terms = 21 */
248 }
249 return( 0.0 ); /* should never happen */
250}
cristybb503372010-05-27 20:51:26 +0000251static double poly_basis_dy(ssize_t n, double x, double y)
cristy3ed852e2009-09-05 21:47:34 +0000252{
253 /* polynomial term for y derivative */
254 switch(n) {
255 case 0: return( 0.0 ); /* constant */
256 case 1: return( 0.0 );
257 case 2: return( 1.0 ); /* affine order = 1 terms = 3 */
258 case 3: return( x ); /* bilinear order = 1.5 terms = 4 */
259 case 4: return( 0.0 );
260 case 5: return( y ); /* quadratic order = 2 terms = 6 */
261 default: return( poly_basis_dx(n-1,x,y) ); /* weird but true */
262 }
263 /* NOTE: the only reason that last is not true for 'quadtratic'
264 is due to the re-arrangement of terms to allow for 'bilinear'
265 */
266}
267
268/*
269%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
270% %
271% %
272% %
273+ G e n e r a t e C o e f f i c i e n t s %
274% %
275% %
276% %
277%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
278%
279% GenerateCoefficients() takes user provided input arguments and generates
280% the coefficients, needed to apply the specific distortion for either
281% distorting images (generally using control points) or generating a color
282% gradient from sparsely separated color points.
283%
284% The format of the GenerateCoefficients() method is:
285%
286% Image *GenerateCoefficients(const Image *image,DistortImageMethod method,
cristybb503372010-05-27 20:51:26 +0000287% const size_t number_arguments,const double *arguments,
288% size_t number_values, ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000289%
290% A description of each parameter follows:
291%
292% o image: the image to be distorted.
293%
294% o method: the method of image distortion/ sparse gradient
295%
296% o number_arguments: the number of arguments given.
297%
298% o arguments: the arguments for this distortion method.
299%
300% o number_values: the style and format of given control points, (caller type)
301% 0: 2 dimensional mapping of control points (Distort)
302% Format: u,v,x,y where u,v is the 'source' of the
303% the color to be plotted, for DistortImage()
304% N: Interpolation of control points with N values (usally r,g,b)
305% Format: x,y,r,g,b mapping x,y to color values r,g,b
306% IN future, varible number of values may be given (1 to N)
307%
308% o exception: return any errors or warnings in this structure
309%
310% Note that the returned array of double values must be freed by the
311% calling method using RelinquishMagickMemory(). This however may change in
312% the future to require a more 'method' specific method.
313%
314% Because of this this method should not be classed as stable or used
315% outside other MagickCore library methods.
316*/
317
318static double *GenerateCoefficients(const Image *image,
cristybb503372010-05-27 20:51:26 +0000319 DistortImageMethod *method,const size_t number_arguments,
320 const double *arguments,size_t number_values,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000321{
322 double
323 *coeff;
324
cristybb503372010-05-27 20:51:26 +0000325 register size_t
cristy3ed852e2009-09-05 21:47:34 +0000326 i;
327
cristybb503372010-05-27 20:51:26 +0000328 size_t
cristy3ed852e2009-09-05 21:47:34 +0000329 number_coeff, /* number of coefficients to return (array size) */
330 cp_size, /* number floating point numbers per control point */
331 cp_x,cp_y, /* the x,y indexes for control point */
332 cp_values; /* index of values for this control point */
333 /* number_values Number of values given per control point */
334
335 if ( number_values == 0 ) {
336 /* Image distortion using control points (or other distortion)
337 That is generate a mapping so that x,y->u,v given u,v,x,y
338 */
339 number_values = 2; /* special case: two values of u,v */
340 cp_values = 0; /* the values i,j are BEFORE the destination CP x,y */
341 cp_x = 2; /* location of x,y in input control values */
342 cp_y = 3;
343 /* NOTE: cp_values, also used for later 'reverse map distort' tests */
344 }
345 else {
346 cp_x = 0; /* location of x,y in input control values */
347 cp_y = 1;
348 cp_values = 2; /* and the other values are after x,y */
349 /* Typically in this case the values are R,G,B color values */
350 }
351 cp_size = number_values+2; /* each CP defintion involves this many numbers */
352
353 /* If not enough control point pairs are found for specific distortions
354 fall back to Affine distortion (allowing 0 to 3 point pairs)
355 */
cristy2857ffc2010-03-27 23:44:00 +0000356 if ( number_arguments < 4*cp_size &&
cristy3ed852e2009-09-05 21:47:34 +0000357 ( *method == BilinearForwardDistortion
358 || *method == BilinearReverseDistortion
359 || *method == PerspectiveDistortion
360 ) )
361 *method = AffineDistortion;
362
363 number_coeff=0;
364 switch (*method) {
365 case AffineDistortion:
366 /* also BarycentricColorInterpolate: */
367 number_coeff=3*number_values;
368 break;
369 case PolynomialDistortion:
370 /* number of coefficents depend on the given polynomal 'order' */
371 if ( number_arguments <= 1 && (number_arguments-1)%cp_size != 0)
372 {
373 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
374 "InvalidArgument","%s : '%s'","Polynomial",
375 "Invalid number of args: order [CPs]...");
376 return((double *) NULL);
377 }
378 i = poly_number_terms(arguments[0]);
379 number_coeff = 2 + i*number_values;
380 if ( i == 0 ) {
381 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
382 "InvalidArgument","%s : '%s'","Polynomial",
anthony96b2d382009-11-04 07:28:07 +0000383 "Invalid order, should be interger 1 to 5, or 1.5");
cristy3ed852e2009-09-05 21:47:34 +0000384 return((double *) NULL);
385 }
386 if ( number_arguments < 1+i*cp_size ) {
387 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
cristye8c25f92010-06-03 00:53:06 +0000388 "InvalidArgument", "%s : 'require at least %.20g CPs'",
389 "Polynomial", (double) i);
cristy3ed852e2009-09-05 21:47:34 +0000390 return((double *) NULL);
391 }
392 break;
393 case BilinearReverseDistortion:
394 number_coeff=4*number_values;
395 break;
396 /*
397 The rest are constants as they are only used for image distorts
398 */
399 case BilinearForwardDistortion:
anthony5056b232009-10-10 13:18:06 +0000400 number_coeff=10; /* 2*4 coeff plus 2 constants */
401 cp_x = 0; /* Reverse src/dest coords for forward mapping */
cristy3ed852e2009-09-05 21:47:34 +0000402 cp_y = 1;
403 cp_values = 2;
404 break;
anthony5056b232009-10-10 13:18:06 +0000405#if 0
406 case QuadraterialDistortion:
407 number_coeff=19; /* BilinearForward + BilinearReverse */
408#endif
409 break;
cristy3ed852e2009-09-05 21:47:34 +0000410 case ShepardsDistortion:
411 case VoronoiColorInterpolate:
anthony5056b232009-10-10 13:18:06 +0000412 number_coeff=1; /* not used, but provide some type of return */
cristy3ed852e2009-09-05 21:47:34 +0000413 break;
414 case ArcDistortion:
415 number_coeff=5;
416 break;
417 case ScaleRotateTranslateDistortion:
418 case AffineProjectionDistortion:
419 number_coeff=6;
420 break;
421 case PolarDistortion:
422 case DePolarDistortion:
423 number_coeff=8;
cristy3ed852e2009-09-05 21:47:34 +0000424 break;
425 case PerspectiveDistortion:
426 case PerspectiveProjectionDistortion:
427 number_coeff=9;
428 break;
429 case BarrelDistortion:
430 case BarrelInverseDistortion:
431 number_coeff=10;
432 break;
433 case UndefinedDistortion:
434 default:
435 assert(! "Unknown Method Given"); /* just fail assertion */
436 }
437
438 /* allocate the array of coefficients needed */
439 coeff = (double *) AcquireQuantumMemory(number_coeff,sizeof(*coeff));
440 if (coeff == (double *) NULL) {
441 (void) ThrowMagickException(exception,GetMagickModule(),
442 ResourceLimitError,"MemoryAllocationFailed",
443 "%s", "GenerateCoefficients");
444 return((double *) NULL);
445 }
446
447 /* zero out coeffiecents array */
448 for (i=0; i < number_coeff; i++)
449 coeff[i] = 0.0;
450
451 switch (*method)
452 {
453 case AffineDistortion:
454 {
455 /* Affine Distortion
456 v = c0*x + c1*y + c2
457 for each 'value' given
458
459 Input Arguments are sets of control points...
460 For Distort Images u,v, x,y ...
461 For Sparse Gradients x,y, r,g,b ...
462 */
463 if ( number_arguments%cp_size != 0 ||
464 number_arguments < cp_size ) {
465 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
cristye8c25f92010-06-03 00:53:06 +0000466 "InvalidArgument", "%s : 'require at least %.20g CPs'",
467 "Affine", 1.0);
cristy3ed852e2009-09-05 21:47:34 +0000468 coeff=(double *) RelinquishMagickMemory(coeff);
469 return((double *) NULL);
470 }
471 /* handle special cases of not enough arguments */
472 if ( number_arguments == cp_size ) {
473 /* Only 1 CP Set Given */
474 if ( cp_values == 0 ) {
475 /* image distortion - translate the image */
476 coeff[0] = 1.0;
477 coeff[2] = arguments[0] - arguments[2];
478 coeff[4] = 1.0;
479 coeff[5] = arguments[1] - arguments[3];
480 }
481 else {
482 /* sparse gradient - use the values directly */
483 for (i=0; i<number_values; i++)
484 coeff[i*3+2] = arguments[cp_values+i];
485 }
486 }
487 else {
488 /* 2 or more points (usally 3) given.
489 Solve a least squares simultanious equation for coefficients.
490 */
491 double
492 **matrix,
493 **vectors,
494 terms[3];
495
496 MagickBooleanType
497 status;
498
499 /* create matrix, and a fake vectors matrix */
500 matrix = AcquireMagickMatrix(3UL,3UL);
501 vectors = (double **) AcquireQuantumMemory(number_values,sizeof(*vectors));
502 if (matrix == (double **) NULL || vectors == (double **) NULL)
503 {
504 matrix = RelinquishMagickMatrix(matrix, 3UL);
505 vectors = (double **) RelinquishMagickMemory(vectors);
506 coeff = (double *) RelinquishMagickMemory(coeff);
507 (void) ThrowMagickException(exception,GetMagickModule(),
508 ResourceLimitError,"MemoryAllocationFailed",
509 "%s", "DistortCoefficients");
510 return((double *) NULL);
511 }
512 /* fake a number_values x3 vectors matrix from coefficients array */
513 for (i=0; i < number_values; i++)
514 vectors[i] = &(coeff[i*3]);
515 /* Add given control point pairs for least squares solving */
516 for (i=0; i < number_arguments; i+=cp_size) {
517 terms[0] = arguments[i+cp_x]; /* x */
518 terms[1] = arguments[i+cp_y]; /* y */
519 terms[2] = 1; /* 1 */
520 LeastSquaresAddTerms(matrix,vectors,terms,
521 &(arguments[i+cp_values]),3UL,number_values);
522 }
523 if ( number_arguments == 2*cp_size ) {
524 /* Only two pairs were given, but we need 3 to solve the affine.
525 Fake extra coordinates by rotating p1 around p0 by 90 degrees.
526 x2 = x0 - (y1-y0) y2 = y0 + (x1-x0)
527 */
528 terms[0] = arguments[cp_x]
529 - ( arguments[cp_size+cp_y] - arguments[cp_y] ); /* x2 */
530 terms[1] = arguments[cp_y] +
531 + ( arguments[cp_size+cp_x] - arguments[cp_x] ); /* y2 */
532 terms[2] = 1; /* 1 */
533 if ( cp_values == 0 ) {
534 /* Image Distortion - rotate the u,v coordients too */
535 double
536 uv2[2];
537 uv2[0] = arguments[0] - arguments[5] + arguments[1]; /* u2 */
538 uv2[1] = arguments[1] + arguments[4] - arguments[0]; /* v2 */
539 LeastSquaresAddTerms(matrix,vectors,terms,uv2,3UL,2UL);
540 }
541 else {
542 /* Sparse Gradient - use values of p0 for linear gradient */
543 LeastSquaresAddTerms(matrix,vectors,terms,
544 &(arguments[cp_values]),3UL,number_values);
545 }
546 }
547 /* Solve for LeastSquares Coefficients */
548 status=GaussJordanElimination(matrix,vectors,3UL,number_values);
549 matrix = RelinquishMagickMatrix(matrix, 3UL);
550 vectors = (double **) RelinquishMagickMemory(vectors);
551 if ( status == MagickFalse ) {
552 coeff = (double *) RelinquishMagickMemory(coeff);
553 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
anthony4cf1d892010-03-29 03:12:20 +0000554 "InvalidArgument","%s : 'Unsolvable Matrix'",
555 MagickOptionToMnemonic(MagickDistortOptions, *method) );
cristy3ed852e2009-09-05 21:47:34 +0000556 return((double *) NULL);
557 }
558 }
559 return(coeff);
560 }
561 case AffineProjectionDistortion:
562 {
563 /*
564 Arguments: Affine Matrix (forward mapping)
565 Arguments sx, rx, ry, sy, tx, ty
566 Where u = sx*x + ry*y + tx
567 v = rx*x + sy*y + ty
568
569 Returns coefficients (in there inverse form) ordered as...
570 sx ry tx rx sy ty
571
572 AffineProjection Distortion Notes...
573 + Will only work with a 2 number_values for Image Distortion
574 + Can not be used for generating a sparse gradient (interpolation)
575 */
576 double inverse[8];
577 if (number_arguments != 6) {
578 coeff = (double *) RelinquishMagickMemory(coeff);
579 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
anthony4cf1d892010-03-29 03:12:20 +0000580 "InvalidArgument","%s : 'Needs 6 coeff values'",
581 MagickOptionToMnemonic(MagickDistortOptions, *method) );
cristy3ed852e2009-09-05 21:47:34 +0000582 return((double *) NULL);
583 }
584 /* FUTURE: trap test for sx*sy-rx*ry == 0 (determinate = 0, no inverse) */
585 for(i=0; i<6UL; i++ )
586 inverse[i] = arguments[i];
587 AffineArgsToCoefficients(inverse); /* map into coefficents */
588 InvertAffineCoefficients(inverse, coeff); /* invert */
589 *method = AffineDistortion;
590
591 return(coeff);
592 }
593 case ScaleRotateTranslateDistortion:
594 {
595 /* Scale, Rotate and Translate Distortion
596 An alturnative Affine Distortion
597 Argument options, by number of arguments given:
598 7: x,y, sx,sy, a, nx,ny
599 6: x,y, s, a, nx,ny
600 5: x,y, sx,sy, a
601 4: x,y, s, a
602 3: x,y, a
603 2: s, a
604 1: a
605 Where actions are (in order of application)
606 x,y 'center' of transforms (default = image center)
607 sx,sy scale image by this amount (default = 1)
608 a angle of rotation (argument required)
anthony5056b232009-10-10 13:18:06 +0000609 nx,ny move 'center' here (default = x,y or no movement)
cristy3ed852e2009-09-05 21:47:34 +0000610 And convert to affine mapping coefficients
611
612 ScaleRotateTranslate Distortion Notes...
613 + Does not use a set of CPs in any normal way
614 + Will only work with a 2 number_valuesal Image Distortion
615 + Can not be used for generating a sparse gradient (interpolation)
616 */
617 double
618 cosine, sine,
619 x,y,sx,sy,a,nx,ny;
620
621 /* set default center, and default scale */
622 x = nx = (double)(image->columns)/2.0 + (double)image->page.x;
623 y = ny = (double)(image->rows)/2.0 + (double)image->page.y;
624 sx = sy = 1.0;
625 switch ( number_arguments ) {
626 case 0:
627 coeff = (double *) RelinquishMagickMemory(coeff);
628 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
anthony4cf1d892010-03-29 03:12:20 +0000629 "InvalidArgument","%s : 'Needs at least 1 argument'",
630 MagickOptionToMnemonic(MagickDistortOptions, *method) );
cristy3ed852e2009-09-05 21:47:34 +0000631 return((double *) NULL);
632 case 1:
633 a = arguments[0];
634 break;
635 case 2:
636 sx = sy = arguments[0];
637 a = arguments[1];
638 break;
639 default:
640 x = nx = arguments[0];
641 y = ny = arguments[1];
642 switch ( number_arguments ) {
643 case 3:
644 a = arguments[2];
645 break;
646 case 4:
647 sx = sy = arguments[2];
648 a = arguments[3];
649 break;
650 case 5:
651 sx = arguments[2];
652 sy = arguments[3];
653 a = arguments[4];
654 break;
655 case 6:
656 sx = sy = arguments[2];
657 a = arguments[3];
658 nx = arguments[4];
659 ny = arguments[5];
660 break;
661 case 7:
662 sx = arguments[2];
663 sy = arguments[3];
664 a = arguments[4];
665 nx = arguments[5];
666 ny = arguments[6];
667 break;
668 default:
669 coeff = (double *) RelinquishMagickMemory(coeff);
670 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
anthony4cf1d892010-03-29 03:12:20 +0000671 "InvalidArgument","%s : 'Too Many Arguments (7 or less)'",
672 MagickOptionToMnemonic(MagickDistortOptions, *method) );
cristy3ed852e2009-09-05 21:47:34 +0000673 return((double *) NULL);
674 }
675 break;
676 }
677 /* Trap if sx or sy == 0 -- image is scaled out of existance! */
678 if ( fabs(sx) < MagickEpsilon || fabs(sy) < MagickEpsilon ) {
679 coeff = (double *) RelinquishMagickMemory(coeff);
680 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
anthony4cf1d892010-03-29 03:12:20 +0000681 "InvalidArgument","%s : 'Zero Scale Given'",
682 MagickOptionToMnemonic(MagickDistortOptions, *method) );
cristy3ed852e2009-09-05 21:47:34 +0000683 return((double *) NULL);
684 }
685 /* Save the given arguments as an affine distortion */
686 a=DegreesToRadians(a); cosine=cos(a); sine=sin(a);
687
688 *method = AffineDistortion;
689 coeff[0]=cosine/sx;
690 coeff[1]=sine/sx;
691 coeff[2]=x-nx*coeff[0]-ny*coeff[1];
692 coeff[3]=(-sine)/sy;
693 coeff[4]=cosine/sy;
694 coeff[5]=y-nx*coeff[3]-ny*coeff[4];
695 return(coeff);
696 }
697 case PerspectiveDistortion:
698 { /*
699 Perspective Distortion (a ratio of affine distortions)
700
701 p(x,y) c0*x + c1*y + c2
702 u = ------ = ------------------
703 r(x,y) c6*x + c7*y + 1
704
705 q(x,y) c3*x + c4*y + c5
706 v = ------ = ------------------
707 r(x,y) c6*x + c7*y + 1
708
709 c8 = Sign of 'r', or the denominator affine, for the actual image.
710 This determines what part of the distorted image is 'ground'
711 side of the horizon, the other part is 'sky' or invalid.
712 Valid values are +1.0 or -1.0 only.
713
714 Input Arguments are sets of control points...
715 For Distort Images u,v, x,y ...
716 For Sparse Gradients x,y, r,g,b ...
717
718 Perspective Distortion Notes...
719 + Can be thought of as ratio of 3 affine transformations
720 + Not separatable: r() or c6 and c7 are used by both equations
721 + All 8 coefficients must be determined simultaniously
722 + Will only work with a 2 number_valuesal Image Distortion
723 + Can not be used for generating a sparse gradient (interpolation)
724 + It is not linear, but is simple to generate an inverse
725 + All lines within an image remain lines.
726 + but distances between points may vary.
727 */
728 double
729 **matrix,
730 *vectors[1],
731 terms[8];
732
cristybb503372010-05-27 20:51:26 +0000733 size_t
cristy3ed852e2009-09-05 21:47:34 +0000734 cp_u = cp_values,
735 cp_v = cp_values+1;
736
737 MagickBooleanType
738 status;
739
anthony96b2d382009-11-04 07:28:07 +0000740 if ( number_arguments%cp_size != 0 ||
741 number_arguments < cp_size*4 ) {
742 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
cristye8c25f92010-06-03 00:53:06 +0000743 "InvalidArgument", "%s : 'require at least %.20g CPs'",
744 MagickOptionToMnemonic(MagickDistortOptions, *method), 4.0);
anthony96b2d382009-11-04 07:28:07 +0000745 coeff=(double *) RelinquishMagickMemory(coeff);
746 return((double *) NULL);
747 }
cristy3ed852e2009-09-05 21:47:34 +0000748 /* fake 1x8 vectors matrix directly using the coefficients array */
749 vectors[0] = &(coeff[0]);
750 /* 8x8 least-squares matrix (zeroed) */
751 matrix = AcquireMagickMatrix(8UL,8UL);
752 if (matrix == (double **) NULL) {
753 (void) ThrowMagickException(exception,GetMagickModule(),
754 ResourceLimitError,"MemoryAllocationFailed",
755 "%s", "DistortCoefficients");
756 return((double *) NULL);
757 }
758 /* Add control points for least squares solving */
759 for (i=0; i < number_arguments; i+=4) {
760 terms[0]=arguments[i+cp_x]; /* c0*x */
761 terms[1]=arguments[i+cp_y]; /* c1*y */
762 terms[2]=1.0; /* c2*1 */
763 terms[3]=0.0;
764 terms[4]=0.0;
765 terms[5]=0.0;
766 terms[6]=-terms[0]*arguments[i+cp_u]; /* 1/(c6*x) */
767 terms[7]=-terms[1]*arguments[i+cp_u]; /* 1/(c7*y) */
768 LeastSquaresAddTerms(matrix,vectors,terms,&(arguments[i+cp_u]),
769 8UL,1UL);
770
771 terms[0]=0.0;
772 terms[1]=0.0;
773 terms[2]=0.0;
774 terms[3]=arguments[i+cp_x]; /* c3*x */
775 terms[4]=arguments[i+cp_y]; /* c4*y */
776 terms[5]=1.0; /* c5*1 */
777 terms[6]=-terms[3]*arguments[i+cp_v]; /* 1/(c6*x) */
778 terms[7]=-terms[4]*arguments[i+cp_v]; /* 1/(c7*y) */
779 LeastSquaresAddTerms(matrix,vectors,terms,&(arguments[i+cp_v]),
780 8UL,1UL);
781 }
782 /* Solve for LeastSquares Coefficients */
783 status=GaussJordanElimination(matrix,vectors,8UL,1UL);
784 matrix = RelinquishMagickMatrix(matrix, 8UL);
785 if ( status == MagickFalse ) {
786 coeff = (double *) RelinquishMagickMemory(coeff);
787 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
anthony4cf1d892010-03-29 03:12:20 +0000788 "InvalidArgument","%s : 'Unsolvable Matrix'",
789 MagickOptionToMnemonic(MagickDistortOptions, *method) );
cristy3ed852e2009-09-05 21:47:34 +0000790 return((double *) NULL);
791 }
792 /*
793 Calculate 9'th coefficient! The ground-sky determination.
794 What is sign of the 'ground' in r() denominator affine function?
795 Just use any valid image coordinate (first control point) in
796 destination for determination of what part of view is 'ground'.
797 */
798 coeff[8] = coeff[6]*arguments[cp_x]
799 + coeff[7]*arguments[cp_y] + 1.0;
800 coeff[8] = (coeff[8] < 0.0) ? -1.0 : +1.0;
801
802 return(coeff);
803 }
804 case PerspectiveProjectionDistortion:
805 {
806 /*
807 Arguments: Perspective Coefficents (forward mapping)
808 */
809 if (number_arguments != 8) {
810 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
anthony4cf1d892010-03-29 03:12:20 +0000811 "InvalidArgument", "%s : 'Needs 8 coefficient values'",
812 MagickOptionToMnemonic(MagickDistortOptions, *method));
cristy3ed852e2009-09-05 21:47:34 +0000813 return((double *) NULL);
814 }
815 /* FUTURE: trap test c0*c4-c3*c1 == 0 (determinate = 0, no inverse) */
816 InvertPerspectiveCoefficients(arguments, coeff);
817 /*
818 Calculate 9'th coefficient! The ground-sky determination.
819 What is sign of the 'ground' in r() denominator affine function?
820 Just use any valid image cocodinate in destination for determination.
821 For a forward mapped perspective the images 0,0 coord will map to
822 c2,c5 in the distorted image, so set the sign of denominator of that.
823 */
824 coeff[8] = coeff[6]*arguments[2]
825 + coeff[7]*arguments[5] + 1.0;
826 coeff[8] = (coeff[8] < 0.0) ? -1.0 : +1.0;
827 *method = PerspectiveDistortion;
828
829 return(coeff);
830 }
831 case BilinearForwardDistortion:
832 case BilinearReverseDistortion:
833 {
anthony5056b232009-10-10 13:18:06 +0000834 /* Bilinear Distortion (Forward mapping)
cristy3ed852e2009-09-05 21:47:34 +0000835 v = c0*x + c1*y + c2*x*y + c3;
836 for each 'value' given
837
anthony5056b232009-10-10 13:18:06 +0000838 This is actually a simple polynomial Distortion! The difference
839 however is when we need to reverse the above equation to generate a
840 BilinearForwardDistortion (see below).
841
cristy3ed852e2009-09-05 21:47:34 +0000842 Input Arguments are sets of control points...
843 For Distort Images u,v, x,y ...
844 For Sparse Gradients x,y, r,g,b ...
845
846 */
847 double
848 **matrix,
849 **vectors,
850 terms[4];
851
852 MagickBooleanType
853 status;
854
anthony96b2d382009-11-04 07:28:07 +0000855 /* check the number of arguments */
856 if ( number_arguments%cp_size != 0 ||
857 number_arguments < cp_size*4 ) {
858 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
cristye8c25f92010-06-03 00:53:06 +0000859 "InvalidArgument", "%s : 'require at least %.20g CPs'",
860 MagickOptionToMnemonic(MagickDistortOptions, *method), 4.0);
anthony96b2d382009-11-04 07:28:07 +0000861 coeff=(double *) RelinquishMagickMemory(coeff);
862 return((double *) NULL);
863 }
cristy3ed852e2009-09-05 21:47:34 +0000864 /* create matrix, and a fake vectors matrix */
865 matrix = AcquireMagickMatrix(4UL,4UL);
866 vectors = (double **) AcquireQuantumMemory(number_values,sizeof(*vectors));
867 if (matrix == (double **) NULL || vectors == (double **) NULL)
868 {
869 matrix = RelinquishMagickMatrix(matrix, 4UL);
870 vectors = (double **) RelinquishMagickMemory(vectors);
871 coeff = (double *) RelinquishMagickMemory(coeff);
872 (void) ThrowMagickException(exception,GetMagickModule(),
873 ResourceLimitError,"MemoryAllocationFailed",
874 "%s", "DistortCoefficients");
875 return((double *) NULL);
876 }
877 /* fake a number_values x4 vectors matrix from coefficients array */
878 for (i=0; i < number_values; i++)
879 vectors[i] = &(coeff[i*4]);
880 /* Add given control point pairs for least squares solving */
881 for (i=0; i < number_arguments; i+=cp_size) {
882 terms[0] = arguments[i+cp_x]; /* x */
883 terms[1] = arguments[i+cp_y]; /* y */
884 terms[2] = terms[0]*terms[1]; /* x*y */
885 terms[3] = 1; /* 1 */
886 LeastSquaresAddTerms(matrix,vectors,terms,
887 &(arguments[i+cp_values]),4UL,number_values);
888 }
889 /* Solve for LeastSquares Coefficients */
890 status=GaussJordanElimination(matrix,vectors,4UL,number_values);
891 matrix = RelinquishMagickMatrix(matrix, 4UL);
892 vectors = (double **) RelinquishMagickMemory(vectors);
893 if ( status == MagickFalse ) {
894 coeff = (double *) RelinquishMagickMemory(coeff);
895 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
anthony4cf1d892010-03-29 03:12:20 +0000896 "InvalidArgument","%s : 'Unsolvable Matrix'",
897 MagickOptionToMnemonic(MagickDistortOptions, *method) );
cristy3ed852e2009-09-05 21:47:34 +0000898 return((double *) NULL);
899 }
900 if ( *method == BilinearForwardDistortion ) {
901 /* Bilinear Forward Mapped Distortion
902
903 The above least-squares solved for coefficents but in the forward
904 direction, due to changes to indexing constants.
905
906 i = c0*x + c1*y + c2*x*y + c3;
907 j = c4*x + c5*y + c6*x*y + c7;
908
anthony4cf1d892010-03-29 03:12:20 +0000909 where i,j are in the destination image, NOT the source.
cristy3ed852e2009-09-05 21:47:34 +0000910
anthony5056b232009-10-10 13:18:06 +0000911 Reverse Pixel mapping however needs to use reverse of these
912 functions. It required a full page of algbra to work out the
913 reversed mapping formula, but resolves down to the following...
cristy3ed852e2009-09-05 21:47:34 +0000914
cristy3ed852e2009-09-05 21:47:34 +0000915 c8 = c0*c5-c1*c4;
anthony5056b232009-10-10 13:18:06 +0000916 c9 = 2*(c2*c5-c1*c6); // '2*a' in the quadratic formula
cristy3ed852e2009-09-05 21:47:34 +0000917
918 i = i - c3; j = j - c7;
anthony5056b232009-10-10 13:18:06 +0000919 b = c6*i - c2*j + c8; // So that a*y^2 + b*y + c == 0
920 c = c4*i - c0*j; // y = ( -b +- sqrt(bb - 4ac) ) / (2*a)
cristy3ed852e2009-09-05 21:47:34 +0000921
anthony5056b232009-10-10 13:18:06 +0000922 r = b*b - c9*(c+c);
923 if ( c9 != 0 )
924 y = ( -b + sqrt(r) ) / c9;
925 else
926 y = -c/b;
927
cristy3ed852e2009-09-05 21:47:34 +0000928 x = ( i - c1*y) / ( c1 - c2*y );
929
930 NB: if 'r' is negative there is no solution!
931 NB: the sign of the sqrt() should be negative if image becomes
anthony5056b232009-10-10 13:18:06 +0000932 flipped or flopped, or crosses over itself.
933 NB: techniqually coefficient c5 is not needed, anymore,
934 but kept for completness.
cristy3ed852e2009-09-05 21:47:34 +0000935
anthony5056b232009-10-10 13:18:06 +0000936 See Anthony Thyssen <A.Thyssen@griffith.edu.au>
937 or Fred Weinhaus <fmw@alink.net> for more details.
cristy3ed852e2009-09-05 21:47:34 +0000938
cristy3ed852e2009-09-05 21:47:34 +0000939 */
cristy3ed852e2009-09-05 21:47:34 +0000940 coeff[8] = coeff[0]*coeff[5] - coeff[1]*coeff[4];
anthony5056b232009-10-10 13:18:06 +0000941 coeff[9] = 2*(coeff[2]*coeff[5] - coeff[1]*coeff[6]);
cristy3ed852e2009-09-05 21:47:34 +0000942 }
943 return(coeff);
944 }
anthony5056b232009-10-10 13:18:06 +0000945#if 0
946 case QuadrilateralDistortion:
947 {
948 /* Map a Quadrilateral to a unit square using BilinearReverse
949 Then map that unit square back to the final Quadrilateral
950 using BilinearForward.
951
952 Input Arguments are sets of control points...
953 For Distort Images u,v, x,y ...
954 For Sparse Gradients x,y, r,g,b ...
955
956 */
957 /* UNDER CONSTRUCTION */
958 return(coeff);
959 }
960#endif
961
cristy3ed852e2009-09-05 21:47:34 +0000962 case PolynomialDistortion:
963 {
964 /* Polynomial Distortion
965
966 First two coefficents are used to hole global polynomal information
967 c0 = Order of the polynimial being created
968 c1 = number_of_terms in one polynomial equation
969
970 Rest of the coefficients map to the equations....
971 v = c0 + c1*x + c2*y + c3*x*y + c4*x^2 + c5*y^2 + c6*x^3 + ...
972 for each 'value' (number_values of them) given.
973 As such total coefficients = 2 + number_terms * number_values
974
975 Input Arguments are sets of control points...
976 For Distort Images order [u,v, x,y] ...
977 For Sparse Gradients order [x,y, r,g,b] ...
978
979 Polynomial Distortion Notes...
980 + UNDER DEVELOPMENT -- Do not expect this to remain as is.
981 + Currently polynomial is a reversed mapped distortion.
cristy3ed852e2009-09-05 21:47:34 +0000982 + Order 1.5 is fudged to map into a bilinear distortion.
anthony5056b232009-10-10 13:18:06 +0000983 though it is not the same order as that distortion.
cristy3ed852e2009-09-05 21:47:34 +0000984 */
985 double
986 **matrix,
987 **vectors,
988 *terms;
989
cristybb503372010-05-27 20:51:26 +0000990 size_t
cristy3ed852e2009-09-05 21:47:34 +0000991 nterms; /* number of polynomial terms per number_values */
992
cristybb503372010-05-27 20:51:26 +0000993 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000994 j;
995
996 MagickBooleanType
997 status;
998
999 /* first two coefficients hold polynomial order information */
1000 coeff[0] = arguments[0];
1001 coeff[1] = (double) poly_number_terms(arguments[0]);
cristybb503372010-05-27 20:51:26 +00001002 nterms = (size_t) coeff[1];
cristy3ed852e2009-09-05 21:47:34 +00001003
1004 /* create matrix, a fake vectors matrix, and least sqs terms */
1005 matrix = AcquireMagickMatrix(nterms,nterms);
1006 vectors = (double **) AcquireQuantumMemory(number_values,sizeof(*vectors));
1007 terms = (double *) AcquireQuantumMemory(nterms, sizeof(*terms));
1008 if (matrix == (double **) NULL ||
1009 vectors == (double **) NULL ||
1010 terms == (double *) NULL )
1011 {
1012 matrix = RelinquishMagickMatrix(matrix, nterms);
1013 vectors = (double **) RelinquishMagickMemory(vectors);
1014 terms = (double *) RelinquishMagickMemory(terms);
1015 coeff = (double *) RelinquishMagickMemory(coeff);
1016 (void) ThrowMagickException(exception,GetMagickModule(),
1017 ResourceLimitError,"MemoryAllocationFailed",
1018 "%s", "DistortCoefficients");
1019 return((double *) NULL);
1020 }
1021 /* fake a number_values x3 vectors matrix from coefficients array */
1022 for (i=0; i < number_values; i++)
1023 vectors[i] = &(coeff[2+i*nterms]);
1024 /* Add given control point pairs for least squares solving */
anthony96b2d382009-11-04 07:28:07 +00001025 for (i=1; i < number_arguments; i+=cp_size) { /* NB: start = 1 not 0 */
cristybb503372010-05-27 20:51:26 +00001026 for (j=0; j < (ssize_t) nterms; j++)
cristy3ed852e2009-09-05 21:47:34 +00001027 terms[j] = poly_basis_fn(j,arguments[i+cp_x],arguments[i+cp_y]);
1028 LeastSquaresAddTerms(matrix,vectors,terms,
1029 &(arguments[i+cp_values]),nterms,number_values);
1030 }
1031 terms = (double *) RelinquishMagickMemory(terms);
1032 /* Solve for LeastSquares Coefficients */
1033 status=GaussJordanElimination(matrix,vectors,nterms,number_values);
1034 matrix = RelinquishMagickMatrix(matrix, nterms);
1035 vectors = (double **) RelinquishMagickMemory(vectors);
1036 if ( status == MagickFalse ) {
1037 coeff = (double *) RelinquishMagickMemory(coeff);
1038 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
anthony4cf1d892010-03-29 03:12:20 +00001039 "InvalidArgument","%s : 'Unsolvable Matrix'",
1040 MagickOptionToMnemonic(MagickDistortOptions, *method) );
cristy3ed852e2009-09-05 21:47:34 +00001041 return((double *) NULL);
1042 }
1043 return(coeff);
1044 }
1045 case ArcDistortion:
1046 {
1047 /* Arc Distortion
1048 Args: arc_width rotate top_edge_radius bottom_edge_radius
1049 All but first argument are optional
1050 arc_width The angle over which to arc the image side-to-side
1051 rotate Angle to rotate image from vertical center
1052 top_radius Set top edge of source image at this radius
1053 bottom_radius Set bootom edge to this radius (radial scaling)
1054
1055 By default, if the radii arguments are nor provided the image radius
1056 is calculated so the horizontal center-line is fits the given arc
1057 without scaling.
1058
1059 The output image size is ALWAYS adjusted to contain the whole image,
1060 and an offset is given to position image relative to the 0,0 point of
1061 the origin, allowing users to use relative positioning onto larger
1062 background (via -flatten).
1063
1064 The arguments are converted to these coefficients
1065 c0: angle for center of source image
1066 c1: angle scale for mapping to source image
1067 c2: radius for top of source image
1068 c3: radius scale for mapping source image
1069 c4: centerline of arc within source image
1070
1071 Note the coefficients use a center angle, so asymptotic join is
1072 furthest from both sides of the source image. This also means that
1073 for arc angles greater than 360 the sides of the image will be
1074 trimmed equally.
1075
1076 Arc Distortion Notes...
1077 + Does not use a set of CPs
1078 + Will only work with Image Distortion
1079 + Can not be used for generating a sparse gradient (interpolation)
1080 */
1081 if ( number_arguments >= 1 && arguments[0] < MagickEpsilon ) {
1082 coeff = (double *) RelinquishMagickMemory(coeff);
1083 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
anthony4cf1d892010-03-29 03:12:20 +00001084 "InvalidArgument","%s : 'Arc Angle Too Small'",
1085 MagickOptionToMnemonic(MagickDistortOptions, *method) );
cristy3ed852e2009-09-05 21:47:34 +00001086 return((double *) NULL);
1087 }
1088 if ( number_arguments >= 3 && arguments[2] < MagickEpsilon ) {
1089 coeff = (double *) RelinquishMagickMemory(coeff);
1090 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
anthony4cf1d892010-03-29 03:12:20 +00001091 "InvalidArgument","%s : 'Outer Radius Too Small'",
1092 MagickOptionToMnemonic(MagickDistortOptions, *method) );
cristy3ed852e2009-09-05 21:47:34 +00001093 return((double *) NULL);
1094 }
1095 coeff[0] = -MagickPI2; /* -90, place at top! */
1096 if ( number_arguments >= 1 )
1097 coeff[1] = DegreesToRadians(arguments[0]);
1098 else
1099 coeff[1] = MagickPI2; /* zero arguments - center is at top */
1100 if ( number_arguments >= 2 )
1101 coeff[0] += DegreesToRadians(arguments[1]);
1102 coeff[0] /= Magick2PI; /* normalize radians */
1103 coeff[0] -= MagickRound(coeff[0]);
1104 coeff[0] *= Magick2PI; /* de-normalize back to radians */
1105 coeff[3] = (double)image->rows-1;
1106 coeff[2] = (double)image->columns/coeff[1] + coeff[3]/2.0;
1107 if ( number_arguments >= 3 ) {
1108 if ( number_arguments >= 4 )
1109 coeff[3] = arguments[2] - arguments[3];
1110 else
1111 coeff[3] *= arguments[2]/coeff[2];
1112 coeff[2] = arguments[2];
1113 }
1114 coeff[4] = ((double)image->columns-1.0)/2.0;
1115
1116 return(coeff);
1117 }
1118 case PolarDistortion:
1119 case DePolarDistortion:
1120 {
1121 /* (De)Polar Distortion (same set of arguments)
1122 Args: Rmax, Rmin, Xcenter,Ycenter, Afrom,Ato
1123 DePolar can also have the extra arguments of Width, Height
1124
1125 Coefficients 0 to 5 is the sanatized version first 6 input args
1126 Coefficient 6 is the angle to coord ratio and visa-versa
1127 Coefficient 7 is the radius to coord ratio and visa-versa
1128
1129 WARNING: It is posible for Radius max<min and/or Angle from>to
1130 */
1131 if ( number_arguments == 3
1132 || ( number_arguments > 6 && *method == PolarDistortion )
1133 || number_arguments > 8 ) {
anthony4cf1d892010-03-29 03:12:20 +00001134 (void) ThrowMagickException(exception,GetMagickModule(),
1135 OptionError,"InvalidArgument", "%s : number of arguments",
1136 MagickOptionToMnemonic(MagickDistortOptions, *method) );
cristy3ed852e2009-09-05 21:47:34 +00001137 coeff=(double *) RelinquishMagickMemory(coeff);
1138 return((double *) NULL);
1139 }
1140 /* Rmax - if 0 calculate appropriate value */
1141 if ( number_arguments >= 1 )
1142 coeff[0] = arguments[0];
1143 else
1144 coeff[0] = 0.0;
1145 /* Rmin - usally 0 */
1146 coeff[1] = number_arguments >= 2 ? arguments[1] : 0.0;
1147 /* Center X,Y */
1148 if ( number_arguments >= 4 ) {
1149 coeff[2] = arguments[2];
1150 coeff[3] = arguments[3];
1151 }
1152 else { /* center of actual image */
1153 coeff[2] = (double)(image->columns)/2.0+image->page.x;
1154 coeff[3] = (double)(image->rows)/2.0+image->page.y;
1155 }
1156 /* Angle from,to - about polar center 0 is downward */
1157 coeff[4] = -MagickPI;
1158 if ( number_arguments >= 5 )
1159 coeff[4] = DegreesToRadians(arguments[4]);
1160 coeff[5] = coeff[4];
1161 if ( number_arguments >= 6 )
1162 coeff[5] = DegreesToRadians(arguments[5]);
1163 if ( fabs(coeff[4]-coeff[5]) < MagickEpsilon )
1164 coeff[5] += Magick2PI; /* same angle is a full circle */
1165 /* if radius 0 or negative, its a special value... */
1166 if ( coeff[0] < MagickEpsilon ) {
1167 /* Use closest edge if radius == 0 */
1168 if ( fabs(coeff[0]) < MagickEpsilon ) {
1169 coeff[0]=MagickMin(fabs(coeff[2]-image->page.x),
1170 fabs(coeff[3]-image->page.y));
1171 coeff[0]=MagickMin(coeff[0],
1172 fabs(coeff[2]-image->page.x-image->columns));
1173 coeff[0]=MagickMin(coeff[0],
1174 fabs(coeff[3]-image->page.y-image->rows));
1175 }
1176 /* furthest diagonal if radius == -1 */
1177 if ( fabs(-1.0-coeff[0]) < MagickEpsilon ) {
1178 double rx,ry;
1179 rx = coeff[2]-image->page.x;
1180 ry = coeff[3]-image->page.y;
1181 coeff[0] = rx*rx+ry*ry;
1182 ry = coeff[3]-image->page.y-image->rows;
1183 coeff[0] = MagickMax(coeff[0],rx*rx+ry*ry);
1184 rx = coeff[2]-image->page.x-image->columns;
1185 coeff[0] = MagickMax(coeff[0],rx*rx+ry*ry);
1186 ry = coeff[3]-image->page.y;
1187 coeff[0] = MagickMax(coeff[0],rx*rx+ry*ry);
1188 coeff[0] = sqrt(coeff[0]);
1189 }
1190 }
1191 /* IF Rmax <= 0 or Rmin < 0 OR Rmax < Rmin, THEN error */
1192 if ( coeff[0] < MagickEpsilon || coeff[1] < -MagickEpsilon
1193 || (coeff[0]-coeff[1]) < MagickEpsilon ) {
1194 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
anthony4cf1d892010-03-29 03:12:20 +00001195 "InvalidArgument", "%s : Invalid Radius",
1196 MagickOptionToMnemonic(MagickDistortOptions, *method) );
cristy3ed852e2009-09-05 21:47:34 +00001197 coeff=(double *) RelinquishMagickMemory(coeff);
1198 return((double *) NULL);
1199 }
1200 /* converstion ratios */
1201 if ( *method == PolarDistortion ) {
1202 coeff[6]=(double) image->columns/(coeff[5]-coeff[4]);
1203 coeff[7]=(double) image->rows/(coeff[0]-coeff[1]);
1204 }
1205 else { /* *method == DePolarDistortion */
1206 coeff[6]=(coeff[5]-coeff[4])/image->columns;
1207 coeff[7]=(coeff[0]-coeff[1])/image->rows;
1208 }
1209 return(coeff);
1210 }
1211 case BarrelDistortion:
1212 case BarrelInverseDistortion:
1213 {
1214 /* Barrel Distortion
1215 Rs=(A*Rd^3 + B*Rd^2 + C*Rd + D)*Rd
1216 BarrelInv Distortion
1217 Rs=Rd/(A*Rd^3 + B*Rd^2 + C*Rd + D)
1218
1219 Where Rd is the normalized radius from corner to middle of image
anthonyec40aca2010-04-29 00:20:28 +00001220 Input Arguments are one of the following forms (number of arguments)...
1221 3: A,B,C
1222 4: A,B,C,D
1223 5: A,B,C X,Y
1224 6: A,B,C,D X,Y
1225 8: Ax,Bx,Cx,Dx Ay,By,Cy,Dy
1226 10: Ax,Bx,Cx,Dx Ay,By,Cy,Dy X,Y
cristy3ed852e2009-09-05 21:47:34 +00001227
1228 Returns 10 coefficent values, which are de-normalized (pixel scale)
1229 Ax, Bx, Cx, Dx, Ay, By, Cy, Dy, Xc, Yc
1230 */
1231 /* Radius de-normalization scaling factor */
1232 double
1233 rscale = 2.0/MagickMin((double) image->columns,(double) image->rows);
1234
anthonyec40aca2010-04-29 00:20:28 +00001235 /* sanity check number of args must = 3,4,5,6,8,10 or error */
anthony607c6f12010-04-29 01:04:01 +00001236 if ( (number_arguments < 3) || (number_arguments == 7) ||
1237 (number_arguments == 9) || (number_arguments > 10) )
cristy2857ffc2010-03-27 23:44:00 +00001238 {
anthony4cf1d892010-03-29 03:12:20 +00001239 coeff=(double *) RelinquishMagickMemory(coeff);
1240 (void) ThrowMagickException(exception,GetMagickModule(),
1241 OptionError,"InvalidArgument", "%s : number of arguments",
1242 MagickOptionToMnemonic(MagickDistortOptions, *method) );
1243 return((double *) NULL);
cristy2857ffc2010-03-27 23:44:00 +00001244 }
anthonyec40aca2010-04-29 00:20:28 +00001245 /* A,B,C,D coefficients */
1246 coeff[0] = arguments[0];
1247 coeff[1] = arguments[1];
1248 coeff[2] = arguments[2];
1249 if ((number_arguments == 3) || (number_arguments == 5) )
1250 coeff[3] = 1.0 - coeff[0] - coeff[1] - coeff[2];
1251 else
1252 coeff[3] = arguments[3];
1253 /* de-normalize the coefficients */
cristy3ed852e2009-09-05 21:47:34 +00001254 coeff[0] *= pow(rscale,3.0);
1255 coeff[1] *= rscale*rscale;
1256 coeff[2] *= rscale;
anthonyec40aca2010-04-29 00:20:28 +00001257 /* Y coefficients: as given OR same as X coefficients */
cristy3ed852e2009-09-05 21:47:34 +00001258 if ( number_arguments >= 8 ) {
1259 coeff[4] = arguments[4] * pow(rscale,3.0);
1260 coeff[5] = arguments[5] * rscale*rscale;
1261 coeff[6] = arguments[6] * rscale;
1262 coeff[7] = arguments[7];
1263 }
1264 else {
1265 coeff[4] = coeff[0];
1266 coeff[5] = coeff[1];
1267 coeff[6] = coeff[2];
1268 coeff[7] = coeff[3];
1269 }
anthonyec40aca2010-04-29 00:20:28 +00001270 /* X,Y Center of Distortion (image coodinates) */
cristy3ed852e2009-09-05 21:47:34 +00001271 if ( number_arguments == 5 ) {
1272 coeff[8] = arguments[3];
1273 coeff[9] = arguments[4];
1274 }
anthony4cf1d892010-03-29 03:12:20 +00001275 else if ( number_arguments == 6 ) {
cristy3ed852e2009-09-05 21:47:34 +00001276 coeff[8] = arguments[4];
1277 coeff[9] = arguments[5];
1278 }
anthony4cf1d892010-03-29 03:12:20 +00001279 else if ( number_arguments == 10 ) {
cristy3ed852e2009-09-05 21:47:34 +00001280 coeff[8] = arguments[8];
1281 coeff[9] = arguments[9];
1282 }
anthony4cf1d892010-03-29 03:12:20 +00001283 else {
anthonyec40aca2010-04-29 00:20:28 +00001284 /* center of the image provided (image coodinates) */
1285 coeff[8] = (double)image->columns/2.0 + image->page.x;
1286 coeff[9] = (double)image->rows/2.0 + image->page.y;
anthony4cf1d892010-03-29 03:12:20 +00001287 }
cristy3ed852e2009-09-05 21:47:34 +00001288 return(coeff);
1289 }
1290 case ShepardsDistortion:
1291 case VoronoiColorInterpolate:
1292 {
1293 /* Shepards Distortion input arguments are the coefficents!
1294 Just check the number of arguments is valid!
1295 Args: u1,v1, x1,y1, ...
1296 OR : u1,v1, r1,g1,c1, ...
1297 */
1298 if ( number_arguments%cp_size != 0 ||
1299 number_arguments < cp_size ) {
1300 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
cristye8c25f92010-06-03 00:53:06 +00001301 "InvalidArgument", "%s : 'require at least %.20g CPs'",
1302 MagickOptionToMnemonic(MagickDistortOptions, *method), 1.0);
cristy3ed852e2009-09-05 21:47:34 +00001303 coeff=(double *) RelinquishMagickMemory(coeff);
1304 return((double *) NULL);
1305 }
1306 return(coeff);
1307 }
1308 default:
1309 break;
1310 }
1311 /* you should never reach this point */
1312 assert(! "No Method Handler"); /* just fail assertion */
1313 return((double *) NULL);
1314}
1315
1316/*
1317%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1318% %
1319% %
1320% %
1321% D i s t o r t I m a g e %
1322% %
1323% %
1324% %
1325%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1326%
1327% DistortImage() distorts an image using various distortion methods, by
1328% mapping color lookups of the source image to a new destination image
1329% usally of the same size as the source image, unless 'bestfit' is set to
1330% true.
1331%
1332% If 'bestfit' is enabled, and distortion allows it, the destination image is
1333% adjusted to ensure the whole source 'image' will just fit within the final
1334% destination image, which will be sized and offset accordingly. Also in
1335% many cases the virtual offset of the source image will be taken into
1336% account in the mapping.
1337%
1338% If the '-verbose' control option has been set print to standard error the
1339% equicelent '-fx' formula with coefficients for the function, if practical.
1340%
1341% The format of the DistortImage() method is:
1342%
1343% Image *DistortImage(const Image *image,const DistortImageMethod method,
cristybb503372010-05-27 20:51:26 +00001344% const size_t number_arguments,const double *arguments,
cristy3ed852e2009-09-05 21:47:34 +00001345% MagickBooleanType bestfit, ExceptionInfo *exception)
1346%
1347% A description of each parameter follows:
1348%
1349% o image: the image to be distorted.
1350%
1351% o method: the method of image distortion.
1352%
1353% ArcDistortion always ignores source image offset, and always
1354% 'bestfit' the destination image with the top left corner offset
1355% relative to the polar mapping center.
1356%
1357% Affine, Perspective, and Bilinear, do least squares fitting of the
1358% distrotion when more than the minimum number of control point pairs
1359% are provided.
1360%
1361% Perspective, and Bilinear, fall back to a Affine distortion when less
1362% than 4 control point pairs are provided. While Affine distortions
1363% let you use any number of control point pairs, that is Zero pairs is
1364% a No-Op (viewport only) distortion, one pair is a translation and
1365% two pairs of control points do a scale-rotate-translate, without any
1366% shearing.
1367%
1368% o number_arguments: the number of arguments given.
1369%
1370% o arguments: an array of floating point arguments for this method.
1371%
1372% o bestfit: Attempt to 'bestfit' the size of the resulting image.
1373% This also forces the resulting image to be a 'layered' virtual
1374% canvas image. Can be overridden using 'distort:viewport' setting.
1375%
1376% o exception: return any errors or warnings in this structure
1377%
1378% Extra Controls from Image meta-data (artifacts)...
1379%
1380% o "verbose"
1381% Output to stderr alternatives, internal coefficents, and FX
1382% equivelents for the distortion operation (if feasible).
1383% This forms an extra check of the distortion method, and allows users
1384% access to the internal constants IM calculates for the distortion.
1385%
1386% o "distort:viewport"
1387% Directly set the output image canvas area and offest to use for the
1388% resulting image, rather than use the original images canvas, or a
1389% calculated 'bestfit' canvas.
1390%
1391% o "distort:scale"
1392% Scale the size of the output canvas by this amount to provide a
1393% method of Zooming, and for super-sampling the results.
1394%
1395% Other settings that can effect results include
1396%
1397% o 'interpolate' For source image lookups (scale enlargements)
1398%
1399% o 'filter' Set filter to use for area-resampling (scale shrinking).
1400% Set to 'point' to turn off and use 'interpolate' lookup
1401% instead
1402%
1403*/
1404MagickExport Image *DistortImage(const Image *image,DistortImageMethod method,
cristybb503372010-05-27 20:51:26 +00001405 const size_t number_arguments,const double *arguments,
cristy3ed852e2009-09-05 21:47:34 +00001406 MagickBooleanType bestfit,ExceptionInfo *exception)
1407{
1408#define DistortImageTag "Distort/Image"
1409
1410 double
1411 *coeff,
1412 output_scaling;
1413
1414 Image
1415 *distort_image;
1416
1417 RectangleInfo
1418 geometry; /* geometry of the distorted space viewport */
1419
1420 MagickBooleanType
1421 viewport_given;
1422
1423 assert(image != (Image *) NULL);
1424 assert(image->signature == MagickSignature);
1425 if (image->debug != MagickFalse)
1426 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1427 assert(exception != (ExceptionInfo *) NULL);
1428 assert(exception->signature == MagickSignature);
1429
1430 /*
1431 Convert input arguments (usally as control points for reverse mapping)
1432 into mapping coefficients to apply the distortion.
1433
1434 Note that some distortions are mapped to other distortions,
1435 and as such do not require specific code after this point.
1436 */
1437 coeff = GenerateCoefficients(image, &method, number_arguments,
1438 arguments, 0, exception);
1439 if ( coeff == (double *) NULL )
1440 return((Image *) NULL);
1441
1442 /*
1443 Determine the size and offset for a 'bestfit' destination.
1444 Usally the four corners of the source image is enough.
1445 */
1446
1447 /* default output image bounds, when no 'bestfit' is requested */
1448 geometry.width=image->columns;
1449 geometry.height=image->rows;
1450 geometry.x=0;
1451 geometry.y=0;
1452
1453 if ( method == ArcDistortion ) {
1454 /* always use the 'best fit' viewport */
1455 bestfit = MagickTrue;
1456 }
1457
1458 /* Work out the 'best fit', (required for ArcDistortion) */
1459 if ( bestfit ) {
1460 PointInfo
1461 s,d,min,max;
1462
1463 s.x=s.y=min.x=max.x=min.y=max.y=0.0; /* keep compiler happy */
1464
1465/* defines to figure out the bounds of the distorted image */
1466#define InitalBounds(p) \
1467{ \
1468 /* printf("%lg,%lg -> %lg,%lg\n", s.x,s.y, d.x,d.y); */ \
1469 min.x = max.x = p.x; \
1470 min.y = max.y = p.y; \
1471}
1472#define ExpandBounds(p) \
1473{ \
1474 /* printf("%lg,%lg -> %lg,%lg\n", s.x,s.y, d.x,d.y); */ \
1475 min.x = MagickMin(min.x,p.x); \
1476 max.x = MagickMax(max.x,p.x); \
1477 min.y = MagickMin(min.y,p.y); \
1478 max.y = MagickMax(max.y,p.y); \
1479}
1480
1481 switch (method)
1482 {
1483 case AffineDistortion:
1484 { double inverse[6];
1485 InvertAffineCoefficients(coeff, inverse);
1486 s.x = (double) image->page.x;
1487 s.y = (double) image->page.y;
1488 d.x = inverse[0]*s.x+inverse[1]*s.y+inverse[2];
1489 d.y = inverse[3]*s.x+inverse[4]*s.y+inverse[5];
1490 InitalBounds(d);
1491 s.x = (double) image->page.x+image->columns;
1492 s.y = (double) image->page.y;
1493 d.x = inverse[0]*s.x+inverse[1]*s.y+inverse[2];
1494 d.y = inverse[3]*s.x+inverse[4]*s.y+inverse[5];
1495 ExpandBounds(d);
1496 s.x = (double) image->page.x;
1497 s.y = (double) image->page.y+image->rows;
1498 d.x = inverse[0]*s.x+inverse[1]*s.y+inverse[2];
1499 d.y = inverse[3]*s.x+inverse[4]*s.y+inverse[5];
1500 ExpandBounds(d);
1501 s.x = (double) image->page.x+image->columns;
1502 s.y = (double) image->page.y+image->rows;
1503 d.x = inverse[0]*s.x+inverse[1]*s.y+inverse[2];
1504 d.y = inverse[3]*s.x+inverse[4]*s.y+inverse[5];
1505 ExpandBounds(d);
1506 break;
1507 }
1508 case PerspectiveDistortion:
1509 { double inverse[8], scale;
1510 InvertPerspectiveCoefficients(coeff, inverse);
1511 s.x = (double) image->page.x;
1512 s.y = (double) image->page.y;
1513 scale=inverse[6]*s.x+inverse[7]*s.y+1.0;
1514 scale=1.0/( (fabs(scale) <= MagickEpsilon) ? 1.0 : scale );
1515 d.x = scale*(inverse[0]*s.x+inverse[1]*s.y+inverse[2]);
1516 d.y = scale*(inverse[3]*s.x+inverse[4]*s.y+inverse[5]);
1517 InitalBounds(d);
1518 s.x = (double) image->page.x+image->columns;
1519 s.y = (double) image->page.y;
1520 scale=inverse[6]*s.x+inverse[7]*s.y+1.0;
1521 scale=1.0/( (fabs(scale) <= MagickEpsilon) ? 1.0 : scale );
1522 d.x = scale*(inverse[0]*s.x+inverse[1]*s.y+inverse[2]);
1523 d.y = scale*(inverse[3]*s.x+inverse[4]*s.y+inverse[5]);
1524 ExpandBounds(d);
1525 s.x = (double) image->page.x;
1526 s.y = (double) image->page.y+image->rows;
1527 scale=inverse[6]*s.x+inverse[7]*s.y+1.0;
1528 scale=1.0/( (fabs(scale) <= MagickEpsilon) ? 1.0 : scale );
1529 d.x = scale*(inverse[0]*s.x+inverse[1]*s.y+inverse[2]);
1530 d.y = scale*(inverse[3]*s.x+inverse[4]*s.y+inverse[5]);
1531 ExpandBounds(d);
1532 s.x = (double) image->page.x+image->columns;
1533 s.y = (double) image->page.y+image->rows;
1534 scale=inverse[6]*s.x+inverse[7]*s.y+1.0;
1535 scale=1.0/( (fabs(scale) <= MagickEpsilon) ? 1.0 : scale );
1536 d.x = scale*(inverse[0]*s.x+inverse[1]*s.y+inverse[2]);
1537 d.y = scale*(inverse[3]*s.x+inverse[4]*s.y+inverse[5]);
1538 ExpandBounds(d);
1539 break;
1540 }
1541 case ArcDistortion:
1542 { double a, ca, sa;
1543 /* Forward Map Corners */
1544 a = coeff[0]-coeff[1]/2; ca = cos(a); sa = sin(a);
1545 d.x = coeff[2]*ca;
1546 d.y = coeff[2]*sa;
1547 InitalBounds(d);
1548 d.x = (coeff[2]-coeff[3])*ca;
1549 d.y = (coeff[2]-coeff[3])*sa;
1550 ExpandBounds(d);
1551 a = coeff[0]+coeff[1]/2; ca = cos(a); sa = sin(a);
1552 d.x = coeff[2]*ca;
1553 d.y = coeff[2]*sa;
1554 ExpandBounds(d);
1555 d.x = (coeff[2]-coeff[3])*ca;
1556 d.y = (coeff[2]-coeff[3])*sa;
1557 ExpandBounds(d);
cristycee97112010-05-28 00:44:52 +00001558 /* Orthogonal points along top of arc */
cristyba978e12010-09-12 20:26:50 +00001559 for( a=(double) (ceil((double) ((coeff[0]-coeff[1]/2.0)/MagickPI2))*MagickPI2);
cristy3ed852e2009-09-05 21:47:34 +00001560 a<(coeff[0]+coeff[1]/2.0); a+=MagickPI2 ) {
1561 ca = cos(a); sa = sin(a);
1562 d.x = coeff[2]*ca;
1563 d.y = coeff[2]*sa;
1564 ExpandBounds(d);
1565 }
1566 /*
1567 Convert the angle_to_width and radius_to_height
1568 to appropriate scaling factors, to allow faster processing
1569 in the mapping function.
1570 */
cristyba978e12010-09-12 20:26:50 +00001571 coeff[1] = (double) (Magick2PI*image->columns/coeff[1]);
cristy3ed852e2009-09-05 21:47:34 +00001572 coeff[3] = (double)image->rows/coeff[3];
1573 break;
1574 }
1575 case PolarDistortion:
1576 {
1577 if (number_arguments < 2)
1578 coeff[2] = coeff[3] = 0.0;
1579 min.x = coeff[2]-coeff[0];
1580 max.x = coeff[2]+coeff[0];
1581 min.y = coeff[3]-coeff[0];
1582 max.y = coeff[3]+coeff[0];
1583 /* should be about 1.0 if Rmin = 0 */
1584 coeff[7]=(double) geometry.height/(coeff[0]-coeff[1]);
1585 break;
1586 }
1587 case DePolarDistortion:
1588 {
1589 /* direct calculation as it needs to tile correctly
1590 * for reversibility in a DePolar-Polar cycle */
1591 geometry.x = geometry.y = 0;
cristybb503372010-05-27 20:51:26 +00001592 geometry.height = (size_t) ceil(coeff[0]-coeff[1]);
1593 geometry.width = (size_t)
cristy3ed852e2009-09-05 21:47:34 +00001594 ceil((coeff[0]-coeff[1])*(coeff[5]-coeff[4])*0.5);
1595 break;
1596 }
1597 case ShepardsDistortion:
1598 case BilinearForwardDistortion:
1599 case BilinearReverseDistortion:
anthony5056b232009-10-10 13:18:06 +00001600#if 0
1601 case QuadrilateralDistortion:
1602#endif
cristy3ed852e2009-09-05 21:47:34 +00001603 case PolynomialDistortion:
1604 case BarrelDistortion:
1605 case BarrelInverseDistortion:
1606 default:
1607 /* no bestfit available for this distortion */
1608 bestfit = MagickFalse;
1609 break;
1610 }
anthony4cf1d892010-03-29 03:12:20 +00001611
1612 /* Set the output image geometry to calculated 'bestfit'.
1613 Yes this tends to 'over do' the file image size, ON PURPOSE!
1614 Do not do this for DePolar which needs to be exact for virtual tiling.
cristy3ed852e2009-09-05 21:47:34 +00001615 */
1616 if ( bestfit && method != DePolarDistortion ) {
cristybb503372010-05-27 20:51:26 +00001617 geometry.x = (ssize_t) floor(min.x-0.5);
1618 geometry.y = (ssize_t) floor(min.y-0.5);
1619 geometry.width=(size_t) ceil(max.x-geometry.x+0.5);
1620 geometry.height=(size_t) ceil(max.y-geometry.y+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001621 }
anthony4cf1d892010-03-29 03:12:20 +00001622
1623 /* Now that we have a new size lets some distortions to it exactly
1624 This is for correct handling of Depolar and its virtual tile handling
1625 */
cristy3ed852e2009-09-05 21:47:34 +00001626 if ( method == DePolarDistortion ) {
1627 coeff[6]=(coeff[5]-coeff[4])/geometry.width; /* changed width */
1628 coeff[7]=(coeff[0]-coeff[1])/geometry.height; /* should be about 1.0 */
1629 }
1630 }
1631
1632 /* The user provided a 'viewport' expert option which may
1633 overrides some parts of the current output image geometry.
1634 For ArcDistortion, this also overrides its default 'bestfit' setting.
1635 */
1636 { const char *artifact=GetImageArtifact(image,"distort:viewport");
1637 viewport_given = MagickFalse;
1638 if ( artifact != (const char *) NULL ) {
1639 (void) ParseAbsoluteGeometry(artifact,&geometry);
1640 viewport_given = MagickTrue;
1641 }
1642 }
1643
1644 /* Verbose output */
1645 if ( GetImageArtifact(image,"verbose") != (const char *) NULL ) {
cristybb503372010-05-27 20:51:26 +00001646 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001647 i;
1648 char image_gen[MaxTextExtent];
1649 const char *lookup;
1650
1651 /* Set destination image size and virtual offset */
1652 if ( bestfit || viewport_given ) {
cristye8c25f92010-06-03 00:53:06 +00001653 (void) FormatMagickString(image_gen, MaxTextExtent," -size %.20gx%.20g "
1654 "-page %+.20gx%+.20g xc: +insert \\\n",(double) geometry.width,
1655 (double) geometry.height,(double) geometry.x,(double) geometry.y);
cristy3ed852e2009-09-05 21:47:34 +00001656 lookup="v.p{ xx-v.page.x-.5, yy-v.page.x-.5 }";
1657 }
1658 else {
1659 image_gen[0] = '\0'; /* no destination to generate */
1660 lookup = "p{ xx-page.x-.5, yy-page.x-.5 }"; /* simplify lookup */
1661 }
1662
1663 switch (method) {
1664 case AffineDistortion:
1665 {
1666 double *inverse;
1667
1668 inverse = (double *) AcquireQuantumMemory(6,sizeof(*inverse));
1669 if (inverse == (double *) NULL) {
1670 coeff = (double *) RelinquishMagickMemory(coeff);
1671 (void) ThrowMagickException(exception,GetMagickModule(),
1672 ResourceLimitError,"MemoryAllocationFailed",
1673 "%s", "DistortImages");
1674 return((Image *) NULL);
1675 }
1676 InvertAffineCoefficients(coeff, inverse);
1677 CoefficientsToAffineArgs(inverse);
1678 fprintf(stderr, "Affine Projection:\n");
1679 fprintf(stderr, " -distort AffineProjection \\\n '");
1680 for (i=0; i<5; i++)
anthony5056b232009-10-10 13:18:06 +00001681 fprintf(stderr, "%lf,", inverse[i]);
1682 fprintf(stderr, "%lf'\n", inverse[5]);
cristy3ed852e2009-09-05 21:47:34 +00001683 inverse = (double *) RelinquishMagickMemory(inverse);
1684
1685 fprintf(stderr, "Affine Distort, FX Equivelent:\n");
1686 fprintf(stderr, "%s", image_gen);
1687 fprintf(stderr, " -fx 'ii=i+page.x+0.5; jj=j+page.y+0.5;\n");
1688 fprintf(stderr, " xx=%+lf*ii %+lf*jj %+lf;\n",
1689 coeff[0], coeff[1], coeff[2]);
1690 fprintf(stderr, " yy=%+lf*ii %+lf*jj %+lf;\n",
1691 coeff[3], coeff[4], coeff[5]);
1692 fprintf(stderr, " %s'\n", lookup);
1693
1694 break;
1695 }
1696
1697 case PerspectiveDistortion:
1698 {
1699 double *inverse;
1700
1701 inverse = (double *) AcquireQuantumMemory(8,sizeof(*inverse));
1702 if (inverse == (double *) NULL) {
1703 coeff = (double *) RelinquishMagickMemory(coeff);
1704 (void) ThrowMagickException(exception,GetMagickModule(),
1705 ResourceLimitError,"MemoryAllocationFailed",
1706 "%s", "DistortCoefficients");
1707 return((Image *) NULL);
1708 }
1709 InvertPerspectiveCoefficients(coeff, inverse);
1710 fprintf(stderr, "Perspective Projection:\n");
1711 fprintf(stderr, " -distort PerspectiveProjection \\\n '");
1712 for (i=0; i<4; i++)
anthony5056b232009-10-10 13:18:06 +00001713 fprintf(stderr, "%lf, ", inverse[i]);
cristy3ed852e2009-09-05 21:47:34 +00001714 fprintf(stderr, "\n ");
1715 for (; i<7; i++)
anthony5056b232009-10-10 13:18:06 +00001716 fprintf(stderr, "%lf, ", inverse[i]);
1717 fprintf(stderr, "%lf'\n", inverse[7]);
cristy3ed852e2009-09-05 21:47:34 +00001718 inverse = (double *) RelinquishMagickMemory(inverse);
1719
1720 fprintf(stderr, "Perspective Distort, FX Equivelent:\n");
1721 fprintf(stderr, "%s", image_gen);
1722 fprintf(stderr, " -fx 'ii=i+page.x+0.5; jj=j+page.y+0.5;\n");
1723 fprintf(stderr, " rr=%+lf*ii %+lf*jj + 1;\n",
1724 coeff[6], coeff[7]);
1725 fprintf(stderr, " xx=(%+lf*ii %+lf*jj %+lf)/rr;\n",
1726 coeff[0], coeff[1], coeff[2]);
1727 fprintf(stderr, " yy=(%+lf*ii %+lf*jj %+lf)/rr;\n",
1728 coeff[3], coeff[4], coeff[5]);
1729 fprintf(stderr, " rr%s0 ? %s : blue'\n",
1730 coeff[8] < 0 ? "<" : ">", lookup);
1731 break;
1732 }
1733
1734 case BilinearForwardDistortion:
anthony5056b232009-10-10 13:18:06 +00001735 fprintf(stderr, "BilinearForward Mapping Equations:\n");
cristy3ed852e2009-09-05 21:47:34 +00001736 fprintf(stderr, "%s", image_gen);
1737 fprintf(stderr, " i = %+lf*x %+lf*y %+lf*x*y %+lf;\n",
1738 coeff[0], coeff[1], coeff[2], coeff[3]);
1739 fprintf(stderr, " j = %+lf*x %+lf*y %+lf*x*y %+lf;\n",
1740 coeff[4], coeff[5], coeff[6], coeff[7]);
anthony5056b232009-10-10 13:18:06 +00001741#if 0
1742 /* for debugging */
1743 fprintf(stderr, " c8 = %+lf c9 = 2*a = %+lf;\n",
1744 coeff[8], coeff[9]);
1745#endif
1746 fprintf(stderr, "BilinearForward Distort, FX Equivelent:\n");
cristy3ed852e2009-09-05 21:47:34 +00001747 fprintf(stderr, "%s", image_gen);
1748 fprintf(stderr, " -fx 'ii=i+page.x%+lf; jj=j+page.y%+lf;\n",
1749 0.5-coeff[3], 0.5-coeff[7]);
1750 fprintf(stderr, " bb=%lf*ii %+lf*jj %+lf;\n",
1751 coeff[6], -coeff[2], coeff[8]);
anthony5056b232009-10-10 13:18:06 +00001752 /* Handle Special degenerate (non-quadratic) or trapezoidal case */
1753 if ( coeff[9] != 0 ) {
1754 fprintf(stderr, " rt=bb*bb %+lf*(%lf*ii%+lf*jj);\n",
1755 -2*coeff[9], coeff[4], -coeff[0]);
1756 fprintf(stderr, " yy=( -bb + sqrt(rt) ) / %lf;\n",
1757 coeff[9]);
1758 } else
1759 fprintf(stderr, " yy=(%lf*ii%+lf*jj)/bb;\n",
1760 -coeff[4], coeff[0]);
cristy3ed852e2009-09-05 21:47:34 +00001761 fprintf(stderr, " xx=(ii %+lf*yy)/(%lf %+lf*yy);\n",
1762 -coeff[1], coeff[0], coeff[2]);
anthony5056b232009-10-10 13:18:06 +00001763 if ( coeff[9] != 0 )
1764 fprintf(stderr, " (rt < 0 ) ? red : %s'\n", lookup);
1765 else
1766 fprintf(stderr, " %s'\n", lookup);
cristy3ed852e2009-09-05 21:47:34 +00001767 break;
1768
1769 case BilinearReverseDistortion:
anthonyfbe952e2009-10-11 08:18:27 +00001770#if 0
1771 fprintf(stderr, "Polynomial Projection Distort:\n");
1772 fprintf(stderr, " -distort PolynomialProjection \\\n");
1773 fprintf(stderr, " '1.5, %lf, %lf, %lf, %lf,\n",
anthony5056b232009-10-10 13:18:06 +00001774 coeff[3], coeff[0], coeff[1], coeff[2]);
anthonyfbe952e2009-10-11 08:18:27 +00001775 fprintf(stderr, " %lf, %lf, %lf, %lf'\n",
anthony5056b232009-10-10 13:18:06 +00001776 coeff[7], coeff[4], coeff[5], coeff[6]);
anthonyfbe952e2009-10-11 08:18:27 +00001777#endif
anthony5056b232009-10-10 13:18:06 +00001778 fprintf(stderr, "BilinearReverse Distort, FX Equivelent:\n");
cristy3ed852e2009-09-05 21:47:34 +00001779 fprintf(stderr, "%s", image_gen);
1780 fprintf(stderr, " -fx 'ii=i+page.x+0.5; jj=j+page.y+0.5;\n");
1781 fprintf(stderr, " xx=%+lf*ii %+lf*jj %+lf*ii*jj %+lf;\n",
1782 coeff[0], coeff[1], coeff[2], coeff[3]);
1783 fprintf(stderr, " yy=%+lf*ii %+lf*jj %+lf*ii*jj %+lf;\n",
1784 coeff[4], coeff[5], coeff[6], coeff[7]);
1785 fprintf(stderr, " %s'\n", lookup);
1786 break;
1787
1788 case PolynomialDistortion:
1789 {
cristybb503372010-05-27 20:51:26 +00001790 size_t nterms = (size_t) coeff[1];
cristy3ed852e2009-09-05 21:47:34 +00001791 fprintf(stderr, "Polynomial (order %lg, terms %lu), FX Equivelent\n",
cristyf2faecf2010-05-28 19:19:36 +00001792 coeff[0],(unsigned long) nterms);
cristy3ed852e2009-09-05 21:47:34 +00001793 fprintf(stderr, "%s", image_gen);
1794 fprintf(stderr, " -fx 'ii=i+page.x+0.5; jj=j+page.y+0.5;\n");
1795 fprintf(stderr, " xx =");
cristybb503372010-05-27 20:51:26 +00001796 for (i=0; i<(ssize_t) nterms; i++) {
cristy3ed852e2009-09-05 21:47:34 +00001797 if ( i != 0 && i%4 == 0 ) fprintf(stderr, "\n ");
1798 fprintf(stderr, " %+lf%s", coeff[2+i],
1799 poly_basis_str(i));
1800 }
1801 fprintf(stderr, ";\n yy =");
cristybb503372010-05-27 20:51:26 +00001802 for (i=0; i<(ssize_t) nterms; i++) {
cristy3ed852e2009-09-05 21:47:34 +00001803 if ( i != 0 && i%4 == 0 ) fprintf(stderr, "\n ");
1804 fprintf(stderr, " %+lf%s", coeff[2+i+nterms],
1805 poly_basis_str(i));
1806 }
1807 fprintf(stderr, ";\n %s'\n", lookup);
1808 break;
1809 }
1810 case ArcDistortion:
1811 {
1812 fprintf(stderr, "Arc Distort, Internal Coefficients:\n");
1813 for ( i=0; i<5; i++ )
cristye8c25f92010-06-03 00:53:06 +00001814 fprintf(stderr, " c%.20g = %+lf\n", (double) i, coeff[i]);
cristy3ed852e2009-09-05 21:47:34 +00001815 fprintf(stderr, "Arc Distort, FX Equivelent:\n");
1816 fprintf(stderr, "%s", image_gen);
1817 fprintf(stderr, " -fx 'ii=i+page.x; jj=j+page.y;\n");
1818 fprintf(stderr, " xx=(atan2(jj,ii)%+lf)/(2*pi);\n",
1819 -coeff[0]);
1820 fprintf(stderr, " xx=xx-round(xx);\n");
1821 fprintf(stderr, " xx=xx*%lf %+lf;\n",
1822 coeff[1], coeff[4]);
1823 fprintf(stderr, " yy=(%lf - hypot(ii,jj)) * %lf;\n",
1824 coeff[2], coeff[3]);
1825 fprintf(stderr, " v.p{xx-.5,yy-.5}'\n");
1826 break;
1827 }
1828 case PolarDistortion:
1829 {
1830 fprintf(stderr, "Polar Distort, Internal Coefficents\n");
1831 for ( i=0; i<8; i++ )
cristye8c25f92010-06-03 00:53:06 +00001832 fprintf(stderr, " c%.20g = %+lf\n", (double) i, coeff[i]);
cristy3ed852e2009-09-05 21:47:34 +00001833 fprintf(stderr, "Polar Distort, FX Equivelent:\n");
1834 fprintf(stderr, "%s", image_gen);
1835 fprintf(stderr, " -fx 'ii=i+page.x%+lf; jj=j+page.y%+lf;\n",
1836 -coeff[2], -coeff[3]);
1837 fprintf(stderr, " xx=(atan2(ii,jj)%+lf)/(2*pi);\n",
1838 -(coeff[4]+coeff[5])/2 );
1839 fprintf(stderr, " xx=xx-round(xx);\n");
1840 fprintf(stderr, " xx=xx*2*pi*%lf + v.w/2;\n",
1841 coeff[6] );
1842 fprintf(stderr, " yy=(hypot(ii,jj)%+lf)*%lf;\n",
1843 -coeff[1], coeff[7] );
1844 fprintf(stderr, " v.p{xx-.5,yy-.5}'\n");
1845 break;
1846 }
1847 case DePolarDistortion:
1848 {
1849 fprintf(stderr, "DePolar Distort, Internal Coefficents\n");
1850 for ( i=0; i<8; i++ )
cristye8c25f92010-06-03 00:53:06 +00001851 fprintf(stderr, " c%.20g = %+lf\n", (double) i, coeff[i]);
cristy3ed852e2009-09-05 21:47:34 +00001852 fprintf(stderr, "DePolar Distort, FX Equivelent:\n");
1853 fprintf(stderr, "%s", image_gen);
1854 fprintf(stderr, " -fx 'aa=(i+.5)*%lf %+lf;\n", coeff[6], -coeff[4] );
1855 fprintf(stderr, " rr=(j+.5)*%lf %+lf;\n", coeff[7], +coeff[1] );
1856 fprintf(stderr, " xx=rr*sin(aa) %+lf;\n", coeff[2] );
1857 fprintf(stderr, " yy=rr*cos(aa) %+lf;\n", coeff[3] );
1858 fprintf(stderr, " v.p{xx-.5,yy-.5}'\n");
1859 break;
1860 }
1861 case BarrelDistortion:
1862 case BarrelInverseDistortion:
1863 { double xc,yc;
anthonyec40aca2010-04-29 00:20:28 +00001864 /* NOTE: This does the barrel roll in pixel coords not image coords
1865 ** The internal distortion must do it in image coordinates,
1866 ** so that is what the center coeff (8,9) is given in.
1867 */
cristy3ed852e2009-09-05 21:47:34 +00001868 xc = ((double)image->columns-1.0)/2.0 + image->page.x;
1869 yc = ((double)image->rows-1.0)/2.0 + image->page.y;
1870 fprintf(stderr, "Barrel%s Distort, FX Equivelent:\n",
1871 method == BarrelDistortion ? "" : "Inv");
1872 fprintf(stderr, "%s", image_gen);
anthonyec40aca2010-04-29 00:20:28 +00001873 if ( fabs(coeff[8]-xc-0.5) < 0.1 && fabs(coeff[9]-yc-0.5) < 0.1 )
cristy3ed852e2009-09-05 21:47:34 +00001874 fprintf(stderr, " -fx 'xc=(w-1)/2; yc=(h-1)/2;\n");
1875 else
1876 fprintf(stderr, " -fx 'xc=%lf; yc=%lf;\n",
anthonyec40aca2010-04-29 00:20:28 +00001877 coeff[8]-0.5, coeff[9]-0.5);
cristy3ed852e2009-09-05 21:47:34 +00001878 fprintf(stderr,
1879 " ii=i-xc; jj=j-yc; rr=hypot(ii,jj);\n");
1880 fprintf(stderr, " ii=ii%s(%lf*rr*rr*rr %+lf*rr*rr %+lf*rr %+lf);\n",
1881 method == BarrelDistortion ? "*" : "/",
1882 coeff[0],coeff[1],coeff[2],coeff[3]);
1883 fprintf(stderr, " jj=jj%s(%lf*rr*rr*rr %+lf*rr*rr %+lf*rr %+lf);\n",
1884 method == BarrelDistortion ? "*" : "/",
1885 coeff[4],coeff[5],coeff[6],coeff[7]);
1886 fprintf(stderr, " v.p{fx*ii+xc,fy*jj+yc}'\n");
1887 }
1888 default:
1889 break;
1890 }
1891 }
1892
1893 /* The user provided a 'scale' expert option will scale the
1894 output image size, by the factor given allowing for super-sampling
1895 of the distorted image space. Any scaling factors must naturally
1896 be halved as a result.
1897 */
1898 { const char *artifact;
1899 artifact=GetImageArtifact(image,"distort:scale");
1900 output_scaling = 1.0;
1901 if (artifact != (const char *) NULL) {
cristyf2f27272009-12-17 14:48:46 +00001902 output_scaling = fabs(StringToDouble(artifact));
cristy3ed852e2009-09-05 21:47:34 +00001903 geometry.width *= output_scaling;
1904 geometry.height *= output_scaling;
1905 geometry.x *= output_scaling;
1906 geometry.y *= output_scaling;
1907 if ( output_scaling < 0.1 ) {
1908 coeff = (double *) RelinquishMagickMemory(coeff);
1909 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1910 "InvalidArgument","%s", "-set option:distort:scale" );
1911 return((Image *) NULL);
1912 }
1913 output_scaling = 1/output_scaling;
1914 }
1915 }
1916#define ScaleFilter(F,A,B,C,D) \
1917 ScaleResampleFilter( (F), \
1918 output_scaling*(A), output_scaling*(B), \
1919 output_scaling*(C), output_scaling*(D) )
1920
1921 /*
1922 Initialize the distort image attributes.
1923 */
1924 distort_image=CloneImage(image,geometry.width,geometry.height,MagickTrue,
1925 exception);
1926 if (distort_image == (Image *) NULL)
1927 return((Image *) NULL);
1928 if (SetImageStorageClass(distort_image,DirectClass) == MagickFalse)
1929 { /* if image is ColorMapped - change it to DirectClass */
1930 InheritException(exception,&distort_image->exception);
1931 distort_image=DestroyImage(distort_image);
1932 return((Image *) NULL);
1933 }
1934 distort_image->page.x=geometry.x;
1935 distort_image->page.y=geometry.y;
1936 if (distort_image->background_color.opacity != OpaqueOpacity)
1937 distort_image->matte=MagickTrue;
1938
1939 { /* ----- MAIN CODE -----
1940 Sample the source image to each pixel in the distort image.
1941 */
cristyb65a5b82010-04-15 16:27:51 +00001942 CacheView
1943 *distort_view;
1944
cristy5f959472010-05-27 22:19:46 +00001945 MagickBooleanType
cristy3ed852e2009-09-05 21:47:34 +00001946 status;
1947
cristy5f959472010-05-27 22:19:46 +00001948 MagickOffsetType
1949 progress;
1950
cristy3ed852e2009-09-05 21:47:34 +00001951 MagickPixelPacket
1952 zero;
1953
1954 ResampleFilter
cristyfa112112010-01-04 17:48:07 +00001955 **restrict resample_filter;
cristy3ed852e2009-09-05 21:47:34 +00001956
cristy5f959472010-05-27 22:19:46 +00001957 ssize_t
cristycee97112010-05-28 00:44:52 +00001958 j;
cristy5f959472010-05-27 22:19:46 +00001959
cristy3ed852e2009-09-05 21:47:34 +00001960 status=MagickTrue;
1961 progress=0;
1962 GetMagickPixelPacket(distort_image,&zero);
cristyb2a11ae2010-02-22 00:53:36 +00001963 resample_filter=AcquireResampleFilterThreadSet(image,
1964 UndefinedVirtualPixelMethod,MagickFalse,exception);
cristy3ed852e2009-09-05 21:47:34 +00001965 distort_view=AcquireCacheView(distort_image);
cristyb5d5f722009-11-04 03:03:49 +00001966#if defined(MAGICKCORE_OPENMP_SUPPORT)
1967 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00001968#endif
cristybb503372010-05-27 20:51:26 +00001969 for (j=0; j < (ssize_t) distort_image->rows; j++)
cristy3ed852e2009-09-05 21:47:34 +00001970 {
1971 double
1972 validity; /* how mathematically valid is this the mapping */
1973
cristy688cc422010-07-03 01:30:12 +00001974 int
1975 id;
1976
cristy3ed852e2009-09-05 21:47:34 +00001977 MagickBooleanType
1978 sync;
1979
1980 MagickPixelPacket
1981 pixel, /* pixel color to assign to distorted image */
1982 invalid; /* the color to assign when distort result is invalid */
1983
1984 PointInfo
cristyb2a11ae2010-02-22 00:53:36 +00001985 d,
1986 s; /* transform destination image x,y to source image x,y */
cristy3ed852e2009-09-05 21:47:34 +00001987
1988 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001989 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00001990
cristybb503372010-05-27 20:51:26 +00001991 register ssize_t
cristy688cc422010-07-03 01:30:12 +00001992 i;
cristy3ed852e2009-09-05 21:47:34 +00001993
1994 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001995 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001996
1997 q=QueueCacheViewAuthenticPixels(distort_view,0,j,distort_image->columns,1,
1998 exception);
1999 if (q == (PixelPacket *) NULL)
2000 {
2001 status=MagickFalse;
2002 continue;
2003 }
2004 indexes=GetCacheViewAuthenticIndexQueue(distort_view);
2005 pixel=zero;
2006
2007 /* Define constant scaling vectors for Affine Distortions
2008 Other methods are either variable, or use interpolated lookup
2009 */
2010 id=GetOpenMPThreadId();
2011 switch (method)
2012 {
2013 case AffineDistortion:
2014 ScaleFilter( resample_filter[id],
2015 coeff[0], coeff[1],
2016 coeff[3], coeff[4] );
2017 break;
2018 default:
2019 break;
2020 }
2021
2022 /* Initialize default pixel validity
2023 * negative: pixel is invalid output 'matte_color'
2024 * 0.0 to 1.0: antialiased, mix with resample output
2025 * 1.0 or greater: use resampled output.
2026 */
2027 validity = 1.0;
2028
2029 GetMagickPixelPacket(distort_image,&invalid);
2030 SetMagickPixelPacket(distort_image,&distort_image->matte_color,
2031 (IndexPacket *) NULL, &invalid);
2032 if (distort_image->colorspace == CMYKColorspace)
2033 ConvertRGBToCMYK(&invalid); /* what about other color spaces? */
2034
cristybb503372010-05-27 20:51:26 +00002035 for (i=0; i < (ssize_t) distort_image->columns; i++)
cristy3ed852e2009-09-05 21:47:34 +00002036 {
2037 /* map pixel coordinate to distortion space coordinate */
2038 d.x = (double) (geometry.x+i+0.5)*output_scaling;
2039 d.y = (double) (geometry.y+j+0.5)*output_scaling;
2040 s = d; /* default is a no-op mapping */
2041 switch (method)
2042 {
2043 case AffineDistortion:
2044 {
2045 s.x=coeff[0]*d.x+coeff[1]*d.y+coeff[2];
2046 s.y=coeff[3]*d.x+coeff[4]*d.y+coeff[5];
2047 /* Affine partial derivitives are constant -- set above */
2048 break;
2049 }
2050 case PerspectiveDistortion:
2051 {
2052 double
2053 p,q,r,abs_r,abs_c6,abs_c7,scale;
2054 /* perspective is a ratio of affines */
2055 p=coeff[0]*d.x+coeff[1]*d.y+coeff[2];
2056 q=coeff[3]*d.x+coeff[4]*d.y+coeff[5];
2057 r=coeff[6]*d.x+coeff[7]*d.y+1.0;
2058 /* Pixel Validity -- is it a 'sky' or 'ground' pixel */
2059 validity = (r*coeff[8] < 0.0) ? 0.0 : 1.0;
2060 /* Determine horizon anti-alias blending */
2061 abs_r = fabs(r)*2;
2062 abs_c6 = fabs(coeff[6]);
2063 abs_c7 = fabs(coeff[7]);
2064 if ( abs_c6 > abs_c7 ) {
2065 if ( abs_r < abs_c6 )
2066 validity = 0.5 - coeff[8]*r/coeff[6];
2067 }
2068 else if ( abs_r < abs_c7 )
2069 validity = 0.5 - coeff[8]*r/coeff[7];
2070 /* Perspective Sampling Point (if valid) */
2071 if ( validity > 0.0 ) {
2072 /* divide by r affine, for perspective scaling */
2073 scale = 1.0/r;
2074 s.x = p*scale;
2075 s.y = q*scale;
2076 /* Perspective Partial Derivatives or Scaling Vectors */
2077 scale *= scale;
2078 ScaleFilter( resample_filter[id],
2079 (r*coeff[0] - p*coeff[6])*scale,
2080 (r*coeff[1] - p*coeff[7])*scale,
2081 (r*coeff[3] - q*coeff[6])*scale,
2082 (r*coeff[4] - q*coeff[7])*scale );
2083 }
2084 break;
2085 }
2086 case BilinearReverseDistortion:
2087 {
anthony5056b232009-10-10 13:18:06 +00002088 /* Reversed Mapped is just a simple polynomial */
2089 s.x=coeff[0]*d.x+coeff[1]*d.y+coeff[2]*d.x*d.y+coeff[3];
cristy3ed852e2009-09-05 21:47:34 +00002090 s.y=coeff[4]*d.x+coeff[5]*d.y
2091 +coeff[6]*d.x*d.y+coeff[7];
2092 /* Bilinear partial derivitives of scaling vectors */
2093 ScaleFilter( resample_filter[id],
2094 coeff[0] + coeff[2]*d.y,
2095 coeff[1] + coeff[2]*d.x,
2096 coeff[4] + coeff[6]*d.y,
2097 coeff[5] + coeff[6]*d.x );
2098 break;
2099 }
2100 case BilinearForwardDistortion:
2101 {
anthony5056b232009-10-10 13:18:06 +00002102 /* Forward mapped needs reversed polynomial equations
2103 * which unfortunatally requires a square root! */
2104 double b,c;
cristy3ed852e2009-09-05 21:47:34 +00002105 d.x -= coeff[3]; d.y -= coeff[7];
2106 b = coeff[6]*d.x - coeff[2]*d.y + coeff[8];
anthony5056b232009-10-10 13:18:06 +00002107 c = coeff[4]*d.x - coeff[0]*d.y;
cristy3ed852e2009-09-05 21:47:34 +00002108
anthony5056b232009-10-10 13:18:06 +00002109 validity = 1.0;
2110 /* Handle Special degenerate (non-quadratic) case */
2111 if ( fabs(coeff[9]) < MagickEpsilon )
2112 s.y = -c/b;
2113 else {
2114 c = b*b - 2*coeff[9]*c;
2115 if ( c < 0.0 )
2116 validity = 0.0;
2117 else
2118 s.y = ( -b + sqrt(c) )/coeff[9];
cristy3ed852e2009-09-05 21:47:34 +00002119 }
anthony5056b232009-10-10 13:18:06 +00002120 if ( validity > 0.0 )
2121 s.x = ( d.x - coeff[1]*s.y) / ( coeff[0] + coeff[2]*s.y );
2122
cristy3ed852e2009-09-05 21:47:34 +00002123 /* NOTE: the sign of the square root should be -ve for parts
anthony5056b232009-10-10 13:18:06 +00002124 where the source image becomes 'flipped' or 'mirrored'.
2125 FUTURE: Horizon handling
2126 FUTURE: Scaling factors or Deritives (how?)
cristy3ed852e2009-09-05 21:47:34 +00002127 */
cristy3ed852e2009-09-05 21:47:34 +00002128 break;
2129 }
anthony5056b232009-10-10 13:18:06 +00002130#if 0
2131 case QuadrilateralDistortion:
2132 /* Bilinear mapping of any Quadrilateral to any Quadrilateral */
2133 /* UNDER DEVELOPMENT */
2134 break;
2135#endif
cristy3ed852e2009-09-05 21:47:34 +00002136 case PolynomialDistortion:
2137 {
anthony5056b232009-10-10 13:18:06 +00002138 /* multi-ordered polynomial */
cristybb503372010-05-27 20:51:26 +00002139 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002140 k;
cristybb503372010-05-27 20:51:26 +00002141 ssize_t
2142 nterms=(ssize_t)coeff[1];
cristy3ed852e2009-09-05 21:47:34 +00002143
2144 PointInfo
2145 du,dv; /* the du,dv vectors from unit dx,dy -- derivatives */
2146
2147 s.x=s.y=du.x=du.y=dv.x=dv.y=0.0;
2148 for(k=0; k < nterms; k++) {
2149 s.x += poly_basis_fn(k,d.x,d.y)*coeff[2+k];
2150 du.x += poly_basis_dx(k,d.x,d.y)*coeff[2+k];
2151 du.y += poly_basis_dy(k,d.x,d.y)*coeff[2+k];
2152 s.y += poly_basis_fn(k,d.x,d.y)*coeff[2+k+nterms];
2153 dv.x += poly_basis_dx(k,d.x,d.y)*coeff[2+k+nterms];
2154 dv.y += poly_basis_dy(k,d.x,d.y)*coeff[2+k+nterms];
2155 }
2156 ScaleFilter( resample_filter[id], du.x,du.y,dv.x,dv.y );
2157 break;
2158 }
2159 case ArcDistortion:
2160 {
2161 /* what is the angle and radius in the destination image */
cristyba978e12010-09-12 20:26:50 +00002162 s.x = (double) ((atan2(d.y,d.x) - coeff[0])/Magick2PI);
cristy3ed852e2009-09-05 21:47:34 +00002163 s.x -= MagickRound(s.x); /* angle */
2164 s.y = hypot(d.x,d.y); /* radius */
2165
2166 /* Arc Distortion Partial Scaling Vectors
2167 Are derived by mapping the perpendicular unit vectors
2168 dR and dA*R*2PI rather than trying to map dx and dy
2169 The results is a very simple orthogonal aligned ellipse.
2170 */
2171 if ( s.y > MagickEpsilon )
2172 ScaleFilter( resample_filter[id],
cristyba978e12010-09-12 20:26:50 +00002173 (double) (coeff[1]/(Magick2PI*s.y)), 0, 0, coeff[3] );
cristy3ed852e2009-09-05 21:47:34 +00002174 else
2175 ScaleFilter( resample_filter[id],
2176 distort_image->columns*2, 0, 0, coeff[3] );
2177
2178 /* now scale the angle and radius for source image lookup point */
2179 s.x = s.x*coeff[1] + coeff[4] + image->page.x +0.5;
2180 s.y = (coeff[2] - s.y) * coeff[3] + image->page.y;
2181 break;
2182 }
2183 case PolarDistortion:
2184 { /* Rect/Cartesain/Cylinder to Polar View */
2185 d.x -= coeff[2];
2186 d.y -= coeff[3];
2187 s.x = atan2(d.x,d.y) - (coeff[4]+coeff[5])/2;
2188 s.x /= Magick2PI;
2189 s.x -= MagickRound(s.x);
2190 s.x *= Magick2PI; /* angle - relative to centerline */
2191 s.y = hypot(d.x,d.y); /* radius */
2192
2193 /* Polar Scaling vectors are based on mapping dR and dA vectors
2194 This results in very simple orthogonal scaling vectors
2195 */
2196 if ( s.y > MagickEpsilon )
2197 ScaleFilter( resample_filter[id],
cristyba978e12010-09-12 20:26:50 +00002198 (double) (coeff[6]/(Magick2PI*s.y)), 0, 0, coeff[7] );
cristy3ed852e2009-09-05 21:47:34 +00002199 else
2200 ScaleFilter( resample_filter[id],
2201 distort_image->columns*2, 0, 0, coeff[7] );
2202
2203 /* now finish mapping radius/angle to source x,y coords */
2204 s.x = s.x*coeff[6] + (double)image->columns/2.0 + image->page.x;
2205 s.y = (s.y-coeff[1])*coeff[7] + image->page.y;
2206 break;
2207 }
2208 case DePolarDistortion:
2209 { /* Polar to Cylindical */
2210 /* ignore all destination virtual offsets */
2211 d.x = ((double)i+0.5)*output_scaling*coeff[6]-coeff[4];
2212 d.y = ((double)j+0.5)*output_scaling*coeff[7]+coeff[1];
2213 s.x = d.y*sin(d.x) + coeff[2];
2214 s.y = d.y*cos(d.x) + coeff[3];
2215 /* derivatives are usless - better to use SuperSampling */
2216 break;
2217 }
2218 case BarrelDistortion:
2219 case BarrelInverseDistortion:
2220 {
2221 double r,fx,fy,gx,gy;
2222 /* Radial Polynomial Distortion (de-normalized) */
2223 d.x -= coeff[8];
2224 d.y -= coeff[9];
2225 r = sqrt(d.x*d.x+d.y*d.y);
2226 if ( r > MagickEpsilon ) {
2227 fx = ((coeff[0]*r + coeff[1])*r + coeff[2])*r + coeff[3];
2228 fy = ((coeff[4]*r + coeff[5])*r + coeff[6])*r + coeff[7];
2229 gx = ((3*coeff[0]*r + 2*coeff[1])*r + coeff[2])/r;
2230 gy = ((3*coeff[4]*r + 2*coeff[5])*r + coeff[6])/r;
2231 /* adjust functions and scaling for 'inverse' form */
2232 if ( method == BarrelInverseDistortion ) {
2233 fx = 1/fx; fy = 1/fy;
2234 gx *= -fx*fx; gy *= -fy*fy;
2235 }
anthonyec40aca2010-04-29 00:20:28 +00002236 /* Set the source pixel to lookup and EWA derivative vectors */
cristy3ed852e2009-09-05 21:47:34 +00002237 s.x = d.x*fx + coeff[8];
2238 s.y = d.y*fy + coeff[9];
2239 ScaleFilter( resample_filter[id],
2240 gx*d.x*d.x + fx, gx*d.x*d.y,
2241 gy*d.x*d.y, gy*d.y*d.y + fy );
2242 }
anthonyec40aca2010-04-29 00:20:28 +00002243 else {
2244 /* Special handling to avoid divide by zero when r==0
2245 **
2246 ** The source and destination pixels match in this case
2247 ** which was set at the top of the loop using s = d;
2248 ** otherwise... s.x=coeff[8]; s.y=coeff[9];
2249 */
cristy3ed852e2009-09-05 21:47:34 +00002250 if ( method == BarrelDistortion )
2251 ScaleFilter( resample_filter[id],
2252 coeff[3], 0, 0, coeff[7] );
2253 else /* method == BarrelInverseDistortion */
2254 /* FUTURE, trap for D==0 causing division by zero */
2255 ScaleFilter( resample_filter[id],
2256 1.0/coeff[3], 0, 0, 1.0/coeff[7] );
2257 }
2258 break;
2259 }
2260 case ShepardsDistortion:
2261 { /* Shepards Method, or Inverse Weighted Distance for
2262 displacement around the destination image control points
2263 The input arguments are the coefficents to the function.
2264 This is more of a 'displacement' function rather than an
2265 absolute distortion function.
2266 */
cristybb503372010-05-27 20:51:26 +00002267 size_t
cristy3ed852e2009-09-05 21:47:34 +00002268 i;
2269 double
2270 denominator;
2271
2272 denominator = s.x = s.y = 0;
2273 for(i=0; i<number_arguments; i+=4) {
2274 double weight =
2275 ((double)d.x-arguments[i+2])*((double)d.x-arguments[i+2])
2276 + ((double)d.y-arguments[i+3])*((double)d.y-arguments[i+3]);
2277 if ( weight != 0 )
2278 weight = 1/weight;
2279 else
2280 weight = 1;
2281
2282 s.x += (arguments[ i ]-arguments[i+2])*weight;
2283 s.y += (arguments[i+1]-arguments[i+3])*weight;
2284 denominator += weight;
2285 }
2286 s.x /= denominator;
2287 s.y /= denominator;
2288 s.x += d.x;
2289 s.y += d.y;
2290
2291 /* We can not determine derivatives using shepards method
2292 only color interpolatation, not area-resampling */
2293 break;
2294 }
2295 default:
2296 break; /* use the default no-op given above */
2297 }
2298 /* map virtual canvas location back to real image coordinate */
2299 if ( bestfit && method != ArcDistortion ) {
2300 s.x -= image->page.x;
2301 s.y -= image->page.y;
2302 }
2303 s.x -= 0.5;
2304 s.y -= 0.5;
2305
2306 if ( validity <= 0.0 ) {
2307 /* result of distortion is an invalid pixel - don't resample */
2308 SetPixelPacket(distort_image,&invalid,q,indexes);
2309 }
2310 else {
2311 /* resample the source image to find its correct color */
2312 (void) ResamplePixelColor(resample_filter[id],s.x,s.y,&pixel);
2313 /* if validity between 0.0 and 1.0 mix result with invalid pixel */
2314 if ( validity < 1.0 ) {
2315 /* Do a blend of sample color and invalid pixel */
2316 /* should this be a 'Blend', or an 'Over' compose */
2317 MagickPixelCompositeBlend(&pixel,validity,&invalid,(1.0-validity),
2318 &pixel);
2319 }
2320 SetPixelPacket(distort_image,&pixel,q,indexes);
2321 }
2322 q++;
2323 indexes++;
2324 }
2325 sync=SyncCacheViewAuthenticPixels(distort_view,exception);
2326 if (sync == MagickFalse)
2327 status=MagickFalse;
2328 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2329 {
2330 MagickBooleanType
2331 proceed;
2332
cristyb5d5f722009-11-04 03:03:49 +00002333#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002334 #pragma omp critical (MagickCore_DistortImage)
2335#endif
2336 proceed=SetImageProgress(image,DistortImageTag,progress++,
2337 image->rows);
2338 if (proceed == MagickFalse)
2339 status=MagickFalse;
2340 }
2341#if 0
2342fprintf(stderr, "\n");
2343#endif
2344 }
2345 distort_view=DestroyCacheView(distort_view);
2346 resample_filter=DestroyResampleFilterThreadSet(resample_filter);
2347
2348 if (status == MagickFalse)
2349 distort_image=DestroyImage(distort_image);
2350 }
2351
2352 /* Arc does not return an offset unless 'bestfit' is in effect
2353 And the user has not provided an overriding 'viewport'.
2354 */
2355 if ( method == ArcDistortion && !bestfit && !viewport_given ) {
2356 distort_image->page.x = 0;
2357 distort_image->page.y = 0;
2358 }
2359 coeff = (double *) RelinquishMagickMemory(coeff);
2360 return(distort_image);
2361}
2362
2363/*
2364%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2365% %
2366% %
2367% %
2368% S p a r s e C o l o r I m a g e %
2369% %
2370% %
2371% %
2372%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2373%
2374% SparseColorImage(), given a set of coordinates, interpolates the colors
2375% found at those coordinates, across the whole image, using various methods.
2376%
2377% The format of the SparseColorImage() method is:
2378%
2379% Image *SparseColorImage(const Image *image,const ChannelType channel,
cristybb503372010-05-27 20:51:26 +00002380% const SparseColorMethod method,const size_t number_arguments,
cristy3ed852e2009-09-05 21:47:34 +00002381% const double *arguments,ExceptionInfo *exception)
2382%
2383% A description of each parameter follows:
2384%
2385% o image: the image to be filled in.
2386%
2387% o channel: Specify which color values (in RGBKA sequence) are being set.
2388% This also determines the number of color_values in above.
2389%
2390% o method: the method to fill in the gradient between the control points.
2391%
2392% The methods used for SparseColor() are often simular to methods
2393% used for DistortImage(), and even share the same code for determination
2394% of the function coefficents, though with more dimensions (or resulting
2395% values).
2396%
2397% o number_arguments: the number of arguments given.
2398%
2399% o arguments: array of floating point arguments for this method--
2400% x,y,color_values-- with color_values given as normalized values.
2401%
2402% o exception: return any errors or warnings in this structure
2403%
2404*/
2405MagickExport Image *SparseColorImage(const Image *image,
cristy25ffffb2009-12-07 17:15:07 +00002406 const ChannelType channel,const SparseColorMethod method,
cristybb503372010-05-27 20:51:26 +00002407 const size_t number_arguments,const double *arguments,
cristy3ed852e2009-09-05 21:47:34 +00002408 ExceptionInfo *exception)
2409{
2410#define SparseColorTag "Distort/SparseColor"
2411
2412 DistortImageMethod
cristyb068b7a2010-01-06 02:35:13 +00002413 distort_method;
cristy3ed852e2009-09-05 21:47:34 +00002414
2415 double
2416 *coeff;
2417
2418 Image
2419 *sparse_image;
2420
2421 MagickPixelPacket
2422 zero;
2423
cristybb503372010-05-27 20:51:26 +00002424 size_t
cristy3ed852e2009-09-05 21:47:34 +00002425 number_colors;
2426
2427 assert(image != (Image *) NULL);
2428 assert(image->signature == MagickSignature);
2429 if (image->debug != MagickFalse)
2430 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2431 assert(exception != (ExceptionInfo *) NULL);
2432 assert(exception->signature == MagickSignature);
2433
2434 /* Determine number of color values needed per control point */
2435 number_colors=0;
2436 if ( channel & RedChannel ) number_colors++;
2437 if ( channel & GreenChannel ) number_colors++;
2438 if ( channel & BlueChannel ) number_colors++;
2439 if ( channel & IndexChannel ) number_colors++;
2440 if ( channel & OpacityChannel ) number_colors++;
2441
2442 /*
2443 Convert input arguments into mapping coefficients to apply the distortion.
2444 Note some Methods may fall back to other simpler methods.
2445 */
cristyb068b7a2010-01-06 02:35:13 +00002446 distort_method=(DistortImageMethod) method;
2447 coeff = GenerateCoefficients(image, &distort_method, number_arguments,
cristy3ed852e2009-09-05 21:47:34 +00002448 arguments, number_colors, exception);
2449 if ( coeff == (double *) NULL )
2450 return((Image *) NULL);
2451
2452 /* Verbose output */
2453 if ( GetImageArtifact(image,"verbose") != (const char *) NULL ) {
2454
2455 switch (method) {
2456 case BarycentricColorInterpolate:
2457 {
cristybb503372010-05-27 20:51:26 +00002458 register ssize_t x=0;
cristy3ed852e2009-09-05 21:47:34 +00002459 fprintf(stderr, "Barycentric Sparse Color:\n");
2460 if ( channel & RedChannel )
2461 fprintf(stderr, " -channel R -fx '%+lf*i %+lf*j %+lf' \\\n",
2462 coeff[x], coeff[x+1], coeff[x+2]),x+=3;
2463 if ( channel & GreenChannel )
2464 fprintf(stderr, " -channel G -fx '%+lf*i %+lf*j %+lf' \\\n",
2465 coeff[x], coeff[x+1], coeff[x+2]),x+=3;
2466 if ( channel & BlueChannel )
2467 fprintf(stderr, " -channel B -fx '%+lf*i %+lf*j %+lf' \\\n",
2468 coeff[x], coeff[x+1], coeff[x+2]),x+=3;
2469 if ( channel & IndexChannel )
2470 fprintf(stderr, " -channel K -fx '%+lf*i %+lf*j %+lf' \\\n",
2471 coeff[x], coeff[x+1], coeff[x+2]),x+=3;
2472 if ( channel & OpacityChannel )
2473 fprintf(stderr, " -channel A -fx '%+lf*i %+lf*j %+lf' \\\n",
2474 coeff[x], coeff[x+1], coeff[x+2]),x+=3;
2475 break;
2476 }
2477 case BilinearColorInterpolate:
2478 {
cristybb503372010-05-27 20:51:26 +00002479 register ssize_t x=0;
cristy3ed852e2009-09-05 21:47:34 +00002480 fprintf(stderr, "Bilinear Sparse Color\n");
2481 if ( channel & RedChannel )
2482 fprintf(stderr, " -channel R -fx '%+lf*i %+lf*j %+lf*i*j %+lf;\n",
2483 coeff[ x ], coeff[x+1],
2484 coeff[x+2], coeff[x+3]),x+=4;
2485 if ( channel & GreenChannel )
2486 fprintf(stderr, " -channel G -fx '%+lf*i %+lf*j %+lf*i*j %+lf;\n",
2487 coeff[ x ], coeff[x+1],
2488 coeff[x+2], coeff[x+3]),x+=4;
2489 if ( channel & BlueChannel )
2490 fprintf(stderr, " -channel B -fx '%+lf*i %+lf*j %+lf*i*j %+lf;\n",
2491 coeff[ x ], coeff[x+1],
2492 coeff[x+2], coeff[x+3]),x+=4;
2493 if ( channel & IndexChannel )
2494 fprintf(stderr, " -channel K -fx '%+lf*i %+lf*j %+lf*i*j %+lf;\n",
2495 coeff[ x ], coeff[x+1],
2496 coeff[x+2], coeff[x+3]),x+=4;
2497 if ( channel & OpacityChannel )
2498 fprintf(stderr, " -channel A -fx '%+lf*i %+lf*j %+lf*i*j %+lf;\n",
2499 coeff[ x ], coeff[x+1],
2500 coeff[x+2], coeff[x+3]),x+=4;
2501 break;
2502 }
2503 default:
2504 /* unknown, or which are too complex for FX alturnatives */
2505 break;
2506 }
2507 }
2508
2509 /* Generate new image for generated interpolated gradient.
2510 * ASIDE: Actually we could have just replaced the colors of the original
2511 * image, but IM core policy, is if storage class could change then clone
2512 * the image.
2513 */
2514
2515 sparse_image=CloneImage(image,image->columns,image->rows,MagickTrue,
2516 exception);
2517 if (sparse_image == (Image *) NULL)
2518 return((Image *) NULL);
2519 if (SetImageStorageClass(sparse_image,DirectClass) == MagickFalse)
2520 { /* if image is ColorMapped - change it to DirectClass */
2521 InheritException(exception,&image->exception);
2522 sparse_image=DestroyImage(sparse_image);
2523 return((Image *) NULL);
2524 }
2525 { /* ----- MAIN CODE ----- */
cristycee97112010-05-28 00:44:52 +00002526 CacheView
2527 *sparse_view;
cristy3ed852e2009-09-05 21:47:34 +00002528
2529 MagickBooleanType
2530 status;
2531
cristycee97112010-05-28 00:44:52 +00002532 MagickOffsetType
2533 progress;
2534
2535 ssize_t
2536 j;
cristy3ed852e2009-09-05 21:47:34 +00002537
2538 status=MagickTrue;
2539 progress=0;
2540 GetMagickPixelPacket(sparse_image,&zero);
2541 sparse_view=AcquireCacheView(sparse_image);
cristyb5d5f722009-11-04 03:03:49 +00002542#if defined(MAGICKCORE_OPENMP_SUPPORT)
2543 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00002544#endif
cristybb503372010-05-27 20:51:26 +00002545 for (j=0; j < (ssize_t) sparse_image->rows; j++)
cristy3ed852e2009-09-05 21:47:34 +00002546 {
2547 MagickBooleanType
2548 sync;
2549
2550 MagickPixelPacket
2551 pixel; /* pixel to assign to distorted image */
2552
2553 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002554 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002555
cristybb503372010-05-27 20:51:26 +00002556 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002557 i;
2558
2559 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002560 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002561
2562 q=QueueCacheViewAuthenticPixels(sparse_view,0,j,sparse_image->columns,
2563 1,exception);
2564 if (q == (PixelPacket *) NULL)
2565 {
2566 status=MagickFalse;
2567 continue;
2568 }
2569/* FUTURE: get pixel from source image - so channel can replace parts */
2570 indexes=GetCacheViewAuthenticIndexQueue(sparse_view);
2571 pixel=zero;
cristybb503372010-05-27 20:51:26 +00002572 for (i=0; i < (ssize_t) sparse_image->columns; i++)
cristy3ed852e2009-09-05 21:47:34 +00002573 {
2574 switch (method)
2575 {
2576 case BarycentricColorInterpolate:
2577 {
cristybb503372010-05-27 20:51:26 +00002578 register ssize_t x=0;
cristy3ed852e2009-09-05 21:47:34 +00002579 if ( channel & RedChannel )
2580 pixel.red = coeff[x]*i +coeff[x+1]*j
2581 +coeff[x+2], x+=3;
2582 if ( channel & GreenChannel )
2583 pixel.green = coeff[x]*i +coeff[x+1]*j
2584 +coeff[x+2], x+=3;
2585 if ( channel & BlueChannel )
2586 pixel.blue = coeff[x]*i +coeff[x+1]*j
2587 +coeff[x+2], x+=3;
2588 if ( channel & IndexChannel )
2589 pixel.index = coeff[x]*i +coeff[x+1]*j
2590 +coeff[x+2], x+=3;
2591 if ( channel & OpacityChannel )
2592 pixel.opacity = coeff[x]*i +coeff[x+1]*j
2593 +coeff[x+2], x+=3;
2594 break;
2595 }
2596 case BilinearColorInterpolate:
2597 {
cristybb503372010-05-27 20:51:26 +00002598 register ssize_t x=0;
cristy3ed852e2009-09-05 21:47:34 +00002599 if ( channel & RedChannel )
2600 pixel.red = coeff[x]*i + coeff[x+1]*j +
2601 coeff[x+2]*i*j + coeff[x+3], x+=4;
2602 if ( channel & GreenChannel )
2603 pixel.green = coeff[x]*i + coeff[x+1]*j +
2604 coeff[x+2]*i*j + coeff[x+3], x+=4;
2605 if ( channel & BlueChannel )
2606 pixel.blue = coeff[x]*i + coeff[x+1]*j +
2607 coeff[x+2]*i*j + coeff[x+3], x+=4;
2608 if ( channel & IndexChannel )
2609 pixel.index = coeff[x]*i + coeff[x+1]*j +
2610 coeff[x+2]*i*j + coeff[x+3], x+=4;
2611 if ( channel & OpacityChannel )
2612 pixel.opacity = coeff[x]*i + coeff[x+1]*j +
2613 coeff[x+2]*i*j + coeff[x+3], x+=4;
2614 break;
2615 }
2616 case ShepardsColorInterpolate:
2617 { /* Shepards Method,uses its own input arguments as coefficients.
2618 */
cristybb503372010-05-27 20:51:26 +00002619 size_t
cristy3ed852e2009-09-05 21:47:34 +00002620 k;
2621 double
2622 denominator;
2623
2624 if ( channel & RedChannel ) pixel.red = 0.0;
2625 if ( channel & GreenChannel ) pixel.green = 0.0;
2626 if ( channel & BlueChannel ) pixel.blue = 0.0;
2627 if ( channel & IndexChannel ) pixel.index = 0.0;
2628 if ( channel & OpacityChannel ) pixel.opacity = 0.0;
2629 denominator = 0.0;
2630 for(k=0; k<number_arguments; k+=2+number_colors) {
cristybb503372010-05-27 20:51:26 +00002631 register ssize_t x=(ssize_t) k+2;
cristy3ed852e2009-09-05 21:47:34 +00002632 double weight =
2633 ((double)i-arguments[ k ])*((double)i-arguments[ k ])
2634 + ((double)j-arguments[k+1])*((double)j-arguments[k+1]);
2635 if ( weight != 0 )
2636 weight = 1/weight;
2637 else
2638 weight = 1;
2639 if ( channel & RedChannel )
2640 pixel.red += arguments[x++]*weight;
2641 if ( channel & GreenChannel )
2642 pixel.green += arguments[x++]*weight;
2643 if ( channel & BlueChannel )
2644 pixel.blue += arguments[x++]*weight;
2645 if ( channel & IndexChannel )
2646 pixel.index += arguments[x++]*weight;
2647 if ( channel & OpacityChannel )
2648 pixel.opacity += arguments[x++]*weight;
2649 denominator += weight;
2650 }
2651 if ( channel & RedChannel ) pixel.red /= denominator;
2652 if ( channel & GreenChannel ) pixel.green /= denominator;
2653 if ( channel & BlueChannel ) pixel.blue /= denominator;
2654 if ( channel & IndexChannel ) pixel.index /= denominator;
2655 if ( channel & OpacityChannel ) pixel.opacity /= denominator;
2656 break;
2657 }
2658 case VoronoiColorInterpolate:
2659 default:
2660 { /* Just use the closest control point you can find! */
cristybb503372010-05-27 20:51:26 +00002661 size_t
cristy3ed852e2009-09-05 21:47:34 +00002662 k;
2663 double
2664 minimum = MagickHuge;
2665
2666 for(k=0; k<number_arguments; k+=2+number_colors) {
2667 double distance =
2668 ((double)i-arguments[ k ])*((double)i-arguments[ k ])
2669 + ((double)j-arguments[k+1])*((double)j-arguments[k+1]);
2670 if ( distance < minimum ) {
cristybb503372010-05-27 20:51:26 +00002671 register ssize_t x=(ssize_t) k+2;
cristy3ed852e2009-09-05 21:47:34 +00002672 if ( channel & RedChannel ) pixel.red = arguments[x++];
2673 if ( channel & GreenChannel ) pixel.green = arguments[x++];
2674 if ( channel & BlueChannel ) pixel.blue = arguments[x++];
2675 if ( channel & IndexChannel ) pixel.index = arguments[x++];
2676 if ( channel & OpacityChannel ) pixel.opacity = arguments[x++];
2677 minimum = distance;
2678 }
2679 }
2680 break;
2681 }
2682 }
2683 /* set the color directly back into the source image */
2684 if ( channel & RedChannel ) pixel.red *= QuantumRange;
2685 if ( channel & GreenChannel ) pixel.green *= QuantumRange;
2686 if ( channel & BlueChannel ) pixel.blue *= QuantumRange;
2687 if ( channel & IndexChannel ) pixel.index *= QuantumRange;
2688 if ( channel & OpacityChannel ) pixel.opacity *= QuantumRange;
2689 SetPixelPacket(sparse_image,&pixel,q,indexes);
2690 q++;
2691 indexes++;
2692 }
2693 sync=SyncCacheViewAuthenticPixels(sparse_view,exception);
2694 if (sync == MagickFalse)
2695 status=MagickFalse;
2696 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2697 {
2698 MagickBooleanType
2699 proceed;
2700
cristyb5d5f722009-11-04 03:03:49 +00002701#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002702 #pragma omp critical (MagickCore_SparseColorImage)
2703#endif
2704 proceed=SetImageProgress(image,SparseColorTag,progress++,image->rows);
2705 if (proceed == MagickFalse)
2706 status=MagickFalse;
2707 }
2708 }
2709 sparse_view=DestroyCacheView(sparse_view);
2710 if (status == MagickFalse)
2711 sparse_image=DestroyImage(sparse_image);
2712 }
2713 coeff = (double *) RelinquishMagickMemory(coeff);
2714 return(sparse_image);
2715}