Coverage Report

Created: 2026-09-13 07:02

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/imagemagick/MagickCore/distort.c
Line
Count
Source
1
/*
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
%                                   Cristy                                    %
17
%                              Anthony Thyssen                                %
18
%                                 June 2007                                   %
19
%                                                                             %
20
%                                                                             %
21
%  Copyright @ 1999 ImageMagick Studio LLC, a non-profit organization         %
22
%  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
%    https://imagemagick.org/license/                                         %
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 "MagickCore/studio.h"
44
#include "MagickCore/artifact.h"
45
#include "MagickCore/cache.h"
46
#include "MagickCore/cache-view.h"
47
#include "MagickCore/channel.h"
48
#include "MagickCore/colorspace-private.h"
49
#include "MagickCore/composite-private.h"
50
#include "MagickCore/distort.h"
51
#include "MagickCore/exception.h"
52
#include "MagickCore/exception-private.h"
53
#include "MagickCore/gem.h"
54
#include "MagickCore/image.h"
55
#include "MagickCore/linked-list.h"
56
#include "MagickCore/list.h"
57
#include "MagickCore/matrix.h"
58
#include "MagickCore/matrix-private.h"
59
#include "MagickCore/memory_.h"
60
#include "MagickCore/monitor-private.h"
61
#include "MagickCore/option.h"
62
#include "MagickCore/pixel.h"
63
#include "MagickCore/pixel-accessor.h"
64
#include "MagickCore/resample.h"
65
#include "MagickCore/resample-private.h"
66
#include "MagickCore/registry.h"
67
#include "MagickCore/resource_.h"
68
#include "MagickCore/semaphore.h"
69
#include "MagickCore/shear.h"
70
#include "MagickCore/string_.h"
71
#include "MagickCore/string-private.h"
72
#include "MagickCore/thread-private.h"
73
#include "MagickCore/token.h"
74
#include "MagickCore/transform.h"
75

76
/*
77
  Numerous internal routines for image distortions.
78
*/
79
static inline void AffineArgsToCoefficients(double *affine)
80
0
{
81
  /* map  external sx,ry,rx,sy,tx,ty  to  internal c0,c2,c4,c1,c3,c5 */
82
0
  double tmp[4];  /* note indexes  0 and 5 remain unchanged */
83
0
  tmp[0]=affine[1]; tmp[1]=affine[2]; tmp[2]=affine[3]; tmp[3]=affine[4];
84
0
  affine[3]=tmp[0]; affine[1]=tmp[1]; affine[4]=tmp[2]; affine[2]=tmp[3];
85
0
}
86
87
static inline void CoefficientsToAffineArgs(double *coeff)
88
0
{
89
  /* map  internal c0,c1,c2,c3,c4,c5  to  external sx,ry,rx,sy,tx,ty */
90
0
  double tmp[4];  /* note indexes 0 and 5 remain unchanged */
91
0
  tmp[0]=coeff[3]; tmp[1]=coeff[1]; tmp[2]=coeff[4]; tmp[3]=coeff[2];
92
0
  coeff[1]=tmp[0]; coeff[2]=tmp[1]; coeff[3]=tmp[2]; coeff[4]=tmp[3];
93
0
}
94
static void InvertAffineCoefficients(const double *coeff,double *inverse)
95
1.90k
{
96
  /* From "Digital Image Warping" by George Wolberg, page 50 */
97
1.90k
  double determinant;
98
99
1.90k
  determinant=MagickSafeReciprocal(coeff[0]*coeff[4]-coeff[1]*coeff[3]);
100
1.90k
  inverse[0]=determinant*coeff[4];
101
1.90k
  inverse[1]=determinant*(-coeff[1]);
102
1.90k
  inverse[2]=determinant*(coeff[1]*coeff[5]-coeff[2]*coeff[4]);
103
1.90k
  inverse[3]=determinant*(-coeff[3]);
104
1.90k
  inverse[4]=determinant*coeff[0];
105
1.90k
  inverse[5]=determinant*(coeff[2]*coeff[3]-coeff[0]*coeff[5]);
106
1.90k
}
107
108
static void InvertPerspectiveCoefficients(const double *coeff,
109
  double *inverse)
110
0
{
111
  /* From "Digital Image Warping" by George Wolberg, page 53 */
112
0
  double determinant;
113
114
0
  determinant=MagickSafeReciprocal(coeff[0]*coeff[4]-coeff[3]*coeff[1]);
115
0
  inverse[0]=determinant*(coeff[4]-coeff[7]*coeff[5]);
116
0
  inverse[1]=determinant*(coeff[7]*coeff[2]-coeff[1]);
117
0
  inverse[2]=determinant*(coeff[1]*coeff[5]-coeff[4]*coeff[2]);
118
0
  inverse[3]=determinant*(coeff[6]*coeff[5]-coeff[3]);
119
0
  inverse[4]=determinant*(coeff[0]-coeff[6]*coeff[2]);
120
0
  inverse[5]=determinant*(coeff[3]*coeff[2]-coeff[0]*coeff[5]);
121
0
  inverse[6]=determinant*(coeff[3]*coeff[7]-coeff[6]*coeff[4]);
122
0
  inverse[7]=determinant*(coeff[6]*coeff[1]-coeff[0]*coeff[7]);
123
0
}
124
125
/*
126
 * Polynomial Term Defining Functions
127
 *
128
 * Order must either be an integer, or 1.5 to produce
129
 * the 2 number_valuesal polynomial function...
130
 *    affine     1   (3)      u = c0 + c1*x + c2*y
131
 *    bilinear   1.5 (4)      u = '' + c3*x*y
132
 *    quadratic  2   (6)      u = '' + c4*x*x + c5*y*y
133
 *    cubic      3   (10)     u = '' + c6*x^3 + c7*x*x*y + c8*x*y*y + c9*y^3
134
 *    quartic    4   (15)     u = '' + c10*x^4 + ... + c14*y^4
135
 *    quintic    5   (21)     u = '' + c15*x^5 + ... + c20*y^5
136
 * number in parenthesis minimum number of points needed.
137
 * Anything beyond quintic, has not been implemented until
138
 * a more automated way of determining terms is found.
139
140
 * Note the slight re-ordering of the terms for a quadratic polynomial
141
 * which is to allow the use of a bi-linear (order=1.5) polynomial.
142
 * All the later polynomials are ordered simply from x^N to y^N
143
 */
144
static size_t poly_number_terms(double order)
145
0
{
146
 /* Return the number of terms for a 2d polynomial */
147
0
  if ( order < 1 || order > 5 ||
148
0
       ( order != floor(order) && (order-1.5) > MagickEpsilon) )
149
0
    return 0; /* invalid polynomial order */
150
0
  return(CastDoubleToSizeT(floor((order+1.0)*(order+2.0)/2.0)));
151
0
}
152
153
static double poly_basis_fn(ssize_t n, double x, double y)
154
0
{
155
  /* Return the result for this polynomial term */
156
0
  switch(n) {
157
0
    case  0:  return( 1.0 ); /* constant */
158
0
    case  1:  return(  x  );
159
0
    case  2:  return(  y  ); /* affine          order = 1   terms = 3 */
160
0
    case  3:  return( x*y ); /* bilinear        order = 1.5 terms = 4 */
161
0
    case  4:  return( x*x );
162
0
    case  5:  return( y*y ); /* quadratic       order = 2   terms = 6 */
163
0
    case  6:  return( x*x*x );
164
0
    case  7:  return( x*x*y );
165
0
    case  8:  return( x*y*y );
166
0
    case  9:  return( y*y*y ); /* cubic         order = 3   terms = 10 */
167
0
    case 10:  return( x*x*x*x );
168
0
    case 11:  return( x*x*x*y );
169
0
    case 12:  return( x*x*y*y );
170
0
    case 13:  return( x*y*y*y );
171
0
    case 14:  return( y*y*y*y ); /* quartic     order = 4   terms = 15 */
172
0
    case 15:  return( x*x*x*x*x );
173
0
    case 16:  return( x*x*x*x*y );
174
0
    case 17:  return( x*x*x*y*y );
175
0
    case 18:  return( x*x*y*y*y );
176
0
    case 19:  return( x*y*y*y*y );
177
0
    case 20:  return( y*y*y*y*y ); /* quintic   order = 5   terms = 21 */
178
0
  }
179
0
  return( 0 ); /* should never happen */
180
0
}
181
static const char *poly_basis_str(ssize_t n)
182
0
{
183
  /* return the result for this polynomial term */
184
0
  switch(n) {
185
0
    case  0:  return(""); /* constant */
186
0
    case  1:  return("*ii");
187
0
    case  2:  return("*jj"); /* affine                order = 1   terms = 3 */
188
0
    case  3:  return("*ii*jj"); /* bilinear           order = 1.5 terms = 4 */
189
0
    case  4:  return("*ii*ii");
190
0
    case  5:  return("*jj*jj"); /* quadratic          order = 2   terms = 6 */
191
0
    case  6:  return("*ii*ii*ii");
192
0
    case  7:  return("*ii*ii*jj");
193
0
    case  8:  return("*ii*jj*jj");
194
0
    case  9:  return("*jj*jj*jj"); /* cubic           order = 3   terms = 10 */
195
0
    case 10:  return("*ii*ii*ii*ii");
196
0
    case 11:  return("*ii*ii*ii*jj");
197
0
    case 12:  return("*ii*ii*jj*jj");
198
0
    case 13:  return("*ii*jj*jj*jj");
199
0
    case 14:  return("*jj*jj*jj*jj"); /* quartic      order = 4   terms = 15 */
200
0
    case 15:  return("*ii*ii*ii*ii*ii");
201
0
    case 16:  return("*ii*ii*ii*ii*jj");
202
0
    case 17:  return("*ii*ii*ii*jj*jj");
203
0
    case 18:  return("*ii*ii*jj*jj*jj");
204
0
    case 19:  return("*ii*jj*jj*jj*jj");
205
0
    case 20:  return("*jj*jj*jj*jj*jj"); /* quintic   order = 5   terms = 21 */
206
0
  }
207
0
  return( "UNKNOWN" ); /* should never happen */
208
0
}
209
static double poly_basis_dx(ssize_t n, double x, double y)
210
0
{
211
  /* polynomial term for x derivative */
212
0
  switch(n) {
213
0
    case  0:  return( 0.0 ); /* constant */
214
0
    case  1:  return( 1.0 );
215
0
    case  2:  return( 0.0 ); /* affine      order = 1   terms = 3 */
216
0
    case  3:  return(  y  ); /* bilinear    order = 1.5 terms = 4 */
217
0
    case  4:  return(  x  );
218
0
    case  5:  return( 0.0 ); /* quadratic   order = 2   terms = 6 */
219
0
    case  6:  return( x*x );
220
0
    case  7:  return( x*y );
221
0
    case  8:  return( y*y );
222
0
    case  9:  return( 0.0 ); /* cubic       order = 3   terms = 10 */
223
0
    case 10:  return( x*x*x );
224
0
    case 11:  return( x*x*y );
225
0
    case 12:  return( x*y*y );
226
0
    case 13:  return( y*y*y );
227
0
    case 14:  return( 0.0 ); /* quartic     order = 4   terms = 15 */
228
0
    case 15:  return( x*x*x*x );
229
0
    case 16:  return( x*x*x*y );
230
0
    case 17:  return( x*x*y*y );
231
0
    case 18:  return( x*y*y*y );
232
0
    case 19:  return( y*y*y*y );
233
0
    case 20:  return( 0.0 ); /* quintic     order = 5   terms = 21 */
234
0
  }
235
0
  return( 0.0 ); /* should never happen */
236
0
}
237
static double poly_basis_dy(ssize_t n, double x, double y)
238
0
{
239
  /* polynomial term for y derivative */
240
0
  switch(n) {
241
0
    case  0:  return( 0.0 ); /* constant */
242
0
    case  1:  return( 0.0 );
243
0
    case  2:  return( 1.0 ); /* affine      order = 1   terms = 3 */
244
0
    case  3:  return(  x  ); /* bilinear    order = 1.5 terms = 4 */
245
0
    case  4:  return( 0.0 );
246
0
    case  5:  return(  y  ); /* quadratic   order = 2   terms = 6 */
247
0
    default:  return( poly_basis_dx(n-1,x,y) ); /* weird but true */
248
0
  }
249
  /* NOTE: the only reason that last is not true for 'quadratic'
250
     is due to the re-arrangement of terms to allow for 'bilinear'
251
  */
252
0
}
253

254
/*
255
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
256
%                                                                             %
257
%                                                                             %
258
%                                                                             %
259
%     A f f i n e T r a n s f o r m I m a g e                                 %
260
%                                                                             %
261
%                                                                             %
262
%                                                                             %
263
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
264
%
265
%  AffineTransformImage() transforms an image as dictated by the affine matrix.
266
%  It allocates the memory necessary for the new Image structure and returns
267
%  a pointer to the new image.
268
%
269
%  The format of the AffineTransformImage method is:
270
%
271
%      Image *AffineTransformImage(const Image *image,
272
%        AffineMatrix *affine_matrix,ExceptionInfo *exception)
273
%
274
%  A description of each parameter follows:
275
%
276
%    o image: the image.
277
%
278
%    o affine_matrix: the affine matrix.
279
%
280
%    o exception: return any errors or warnings in this structure.
281
%
282
*/
283
MagickExport Image *AffineTransformImage(const Image *image,
284
  const AffineMatrix *affine_matrix,ExceptionInfo *exception)
285
0
{
286
0
  double
287
0
    distort[6];
288
289
0
  Image
290
0
    *deskew_image;
291
292
  /*
293
    Affine transform image.
294
  */
295
0
  assert(image->signature == MagickCoreSignature);
296
0
  assert(affine_matrix != (AffineMatrix *) NULL);
297
0
  assert(exception != (ExceptionInfo *) NULL);
298
0
  assert(exception->signature == MagickCoreSignature);
299
0
  if (IsEventLogging() != MagickFalse)
300
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
301
0
  distort[0]=affine_matrix->sx;
302
0
  distort[1]=affine_matrix->rx;
303
0
  distort[2]=affine_matrix->ry;
304
0
  distort[3]=affine_matrix->sy;
305
0
  distort[4]=affine_matrix->tx;
306
0
  distort[5]=affine_matrix->ty;
307
0
  deskew_image=DistortImage(image,AffineProjectionDistortion,6,distort,
308
0
    MagickTrue,exception);
309
0
  return(deskew_image);
310
0
}
311

312
/*
313
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
314
%                                                                             %
315
%                                                                             %
316
%                                                                             %
317
+   G e n e r a t e C o e f f i c i e n t s                                   %
318
%                                                                             %
319
%                                                                             %
320
%                                                                             %
321
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
322
%
323
%  GenerateCoefficients() takes user provided input arguments and generates
324
%  the coefficients, needed to apply the specific distortion for either
325
%  distorting images (generally using control points) or generating a color
326
%  gradient from sparsely separated color points.
327
%
328
%  The format of the GenerateCoefficients() method is:
329
%
330
%    Image *GenerateCoefficients(const Image *image,DistortMethod method,
331
%        const size_t number_arguments,const double *arguments,
332
%        size_t number_values, ExceptionInfo *exception)
333
%
334
%  A description of each parameter follows:
335
%
336
%    o image: the image to be distorted.
337
%
338
%    o method: the method of image distortion/ sparse gradient
339
%
340
%    o number_arguments: the number of arguments given.
341
%
342
%    o arguments: the arguments for this distortion method.
343
%
344
%    o number_values: the style and format of given control points, (caller type)
345
%         0: 2 dimensional mapping of control points (Distort)
346
%            Format:  u,v,x,y  where u,v is the 'source' of the
347
%            the color to be plotted, for DistortImage()
348
%         N: Interpolation of control points with N values (usually r,g,b)
349
%            Format: x,y,r,g,b    mapping x,y to color values r,g,b
350
%            IN future, variable number of values may be given (1 to N)
351
%
352
%    o exception: return any errors or warnings in this structure
353
%
354
%  Note that the returned array of double values must be freed by the
355
%  calling method using RelinquishMagickMemory().  This however may change in
356
%  the future to require a more 'method' specific method.
357
%
358
%  Because of this, this method should not be classed as stable or used
359
%  outside other MagickCore library methods.
360
*/
361
362
static inline double MagickRound(double x)
363
0
{
364
  /*
365
    Round the fraction to nearest integer.
366
  */
367
0
  if ((x-floor(x)) < (ceil(x)-x))
368
0
    return(floor(x));
369
0
  return(ceil(x));
370
0
}
371
372
static double *GenerateCoefficients(const Image *image,
373
  DistortMethod *method,const size_t number_arguments,const double *arguments,
374
  size_t number_values,ExceptionInfo *exception)
375
1.90k
{
376
1.90k
  double
377
1.90k
    *coeff;
378
379
1.90k
  size_t
380
1.90k
    i;
381
382
1.90k
  size_t
383
1.90k
    number_coefficients, /* number of coefficients to return (array size) */
384
1.90k
    cp_size,      /* number floating point numbers per control point */
385
1.90k
    cp_x,cp_y,    /* the x,y indexes for control point */
386
1.90k
    cp_values;    /* index of values for this control point */
387
    /* number_values   Number of values given per control point */
388
389
1.90k
  if ( number_values == 0 ) {
390
    /* Image distortion using control points (or other distortion)
391
       That is generate a mapping so that   x,y->u,v   given  u,v,x,y
392
    */
393
1.90k
    number_values = 2;   /* special case: two values of u,v */
394
1.90k
    cp_values = 0;       /* the values i,j are BEFORE the destination CP x,y */
395
1.90k
    cp_x = 2;            /* location of x,y in input control values */
396
1.90k
    cp_y = 3;
397
    /* NOTE: cp_values, also used for later 'reverse map distort' tests */
398
1.90k
  }
399
0
  else {
400
0
    cp_x = 0;            /* location of x,y in input control values */
401
0
    cp_y = 1;
402
0
    cp_values = 2;       /* and the other values are after x,y */
403
    /* Typically in this case the values are R,G,B color values */
404
0
  }
405
1.90k
  cp_size = number_values+2; /* each CP definition involves this many numbers */
406
407
  /* If not enough control point pairs are found for specific distortions
408
     fall back to Affine distortion (allowing 0 to 3 point pairs)
409
  */
410
1.90k
  if ( number_arguments < 4*cp_size &&
411
1.90k
       (  *method == BilinearForwardDistortion
412
1.90k
       || *method == BilinearReverseDistortion
413
1.90k
       || *method == PerspectiveDistortion
414
1.90k
       ) )
415
0
    *method = AffineDistortion;
416
417
1.90k
  number_coefficients=0;
418
1.90k
  switch (*method) {
419
0
    case AffineDistortion:
420
0
    case RigidAffineDistortion:
421
    /* also BarycentricColorInterpolate: */
422
0
      number_coefficients=3*number_values;
423
0
      break;
424
0
    case PolynomialDistortion:
425
      /* number of coefficients depend on the given polynomial 'order' */
426
0
      if (number_arguments < 1)
427
0
        {
428
0
          (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
429
0
                 "InvalidArgument","%s : '%s'","Polynomial",
430
0
                 "Needs at least 1 argument");
431
0
          return((double *) NULL);
432
0
        }
433
0
      i = poly_number_terms(arguments[0]);
434
0
      number_coefficients = 2 + i*number_values;
435
0
      if (i == 0)
436
0
        {
437
0
          (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
438
0
                     "InvalidArgument","%s : '%s'","Polynomial",
439
0
                     "Invalid order, should be integer 1 to 5, or 1.5");
440
0
          return((double *) NULL);
441
0
        }
442
0
      if ((number_arguments < (1+i*cp_size)) ||
443
0
          (((number_arguments-1) % cp_size) != 0)) 
444
0
        {
445
0
          (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
446
0
                 "InvalidArgument", "%s : 'require at least %.17g CPs'",
447
0
                 "Polynomial", (double) i);
448
0
          return((double *) NULL);
449
0
        }
450
0
      break;
451
0
    case BilinearReverseDistortion:
452
0
      number_coefficients=4*number_values;
453
0
      break;
454
    /*
455
      The rest are constants as they are only used for image distorts
456
    */
457
0
    case BilinearForwardDistortion:
458
0
      number_coefficients=10; /* 2*4 coeff plus 2 constants */
459
0
      cp_x = 0;        /* Reverse src/dest coords for forward mapping */
460
0
      cp_y = 1;
461
0
      cp_values = 2;
462
0
      break;
463
#if 0
464
    case QuadrilateralDistortion:
465
      number_coefficients=19; /* BilinearForward + BilinearReverse */
466
#endif
467
0
      break;
468
0
    case ShepardsDistortion:
469
0
      number_coefficients=1;  /* The power factor to use */
470
0
      break;
471
0
    case ArcDistortion:
472
0
      number_coefficients=5;
473
0
      break;
474
1.90k
    case ScaleRotateTranslateDistortion:
475
1.90k
    case AffineProjectionDistortion:
476
1.90k
    case Plane2CylinderDistortion:
477
1.90k
    case Cylinder2PlaneDistortion:
478
1.90k
      number_coefficients=6;
479
1.90k
      break;
480
0
    case PolarDistortion:
481
0
    case DePolarDistortion:
482
0
      number_coefficients=8;
483
0
      break;
484
0
    case PerspectiveDistortion:
485
0
    case PerspectiveProjectionDistortion:
486
0
      number_coefficients=9;
487
0
      break;
488
0
    case BarrelDistortion:
489
0
    case BarrelInverseDistortion:
490
0
      number_coefficients=10;
491
0
      break;
492
0
    default:
493
0
      perror("unknown method given"); /* just fail assertion */
494
1.90k
  }
495
496
  /* allocate the array of coefficients needed */
497
1.90k
  coeff=(double *) AcquireQuantumMemory(number_coefficients,sizeof(*coeff));
498
1.90k
  if (coeff == (double *) NULL)
499
0
    {
500
0
      (void) ThrowMagickException(exception,GetMagickModule(),
501
0
        ResourceLimitError,"MemoryAllocationFailed","%s",
502
0
        "GenerateCoefficients");
503
0
      return((double *) NULL);
504
0
    }
505
506
  /* zero out coefficients array */
507
13.3k
  for (i=0; i < number_coefficients; i++)
508
11.4k
    coeff[i] = 0.0;
509
510
1.90k
  switch (*method)
511
1.90k
  {
512
0
    case AffineDistortion:
513
0
    {
514
      /* Affine Distortion
515
           v =  c0*x + c1*y + c2
516
         for each 'value' given
517
518
         Input Arguments are sets of control points...
519
         For Distort Images    u,v, x,y  ...
520
         For Sparse Gradients  x,y, r,g,b  ...
521
      */
522
0
      if ( number_arguments%cp_size != 0 ||
523
0
           number_arguments < cp_size ) {
524
0
        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
525
0
               "InvalidArgument", "%s : 'require at least %.17g CPs'",
526
0
               "Affine", 1.0);
527
0
        coeff=(double *) RelinquishMagickMemory(coeff);
528
0
        return((double *) NULL);
529
0
      }
530
      /* handle special cases of not enough arguments */
531
0
      if ( number_arguments == cp_size ) {
532
        /* Only 1 CP Set Given */
533
0
        if ( cp_values == 0 ) {
534
          /* image distortion - translate the image */
535
0
          coeff[0] = 1.0;
536
0
          coeff[2] = arguments[0] - arguments[2];
537
0
          coeff[4] = 1.0;
538
0
          coeff[5] = arguments[1] - arguments[3];
539
0
        }
540
0
        else {
541
          /* sparse gradient - use the values directly */
542
0
          for (i=0; i<number_values; i++)
543
0
            coeff[i*3+2] = arguments[cp_values+i];
544
0
        }
545
0
      }
546
0
      else {
547
        /* 2 or more points (usually 3) given.
548
           Solve a least squares simultaneous equation for coefficients.
549
        */
550
0
        double
551
0
          **matrix,
552
0
          **vectors,
553
0
          terms[3];
554
555
0
        MagickBooleanType
556
0
          status;
557
558
        /* create matrix, and a fake vectors matrix */
559
0
        matrix=AcquireMagickMatrix(3UL,3UL);
560
0
        vectors=(double **) AcquireQuantumMemory(number_values,
561
0
          sizeof(*vectors));
562
0
        if (matrix == (double **) NULL || vectors == (double **) NULL)
563
0
        {
564
0
          matrix  = RelinquishMagickMatrix(matrix, 3UL);
565
0
          vectors = (double **) RelinquishMagickMemory(vectors);
566
0
          coeff   = (double *) RelinquishMagickMemory(coeff);
567
0
          (void) ThrowMagickException(exception,GetMagickModule(),
568
0
                  ResourceLimitError,"MemoryAllocationFailed",
569
0
                  "%s", "DistortCoefficients");
570
0
          return((double *) NULL);
571
0
        }
572
        /* fake a number_values x3 vectors matrix from coefficients array */
573
0
        for (i=0; i < number_values; i++)
574
0
          vectors[i] = &(coeff[i*3]);
575
        /* Add given control point pairs for least squares solving */
576
0
        for (i=0; i < number_arguments; i+=cp_size) {
577
0
          terms[0] = arguments[i+cp_x];  /* x */
578
0
          terms[1] = arguments[i+cp_y];  /* y */
579
0
          terms[2] = 1;                  /* 1 */
580
0
          LeastSquaresAddTerms(matrix,vectors,terms,
581
0
                   &(arguments[i+cp_values]),3UL,number_values);
582
0
        }
583
0
        if ( number_arguments == 2*cp_size ) {
584
          /* Only two pairs were given, but we need 3 to solve the affine.
585
             Fake extra coordinates by rotating p1 around p0 by 90 degrees.
586
               x2 = x0 - (y1-y0)   y2 = y0 + (x1-x0)
587
           */
588
0
          terms[0] = arguments[cp_x]
589
0
                   - ( arguments[cp_size+cp_y] - arguments[cp_y] ); /* x2 */
590
0
          terms[1] = arguments[cp_y] +
591
0
                   + ( arguments[cp_size+cp_x] - arguments[cp_x] ); /* y2 */
592
0
          terms[2] = 1;                                             /* 1 */
593
0
          if ( cp_values == 0 ) {
594
            /* Image Distortion - rotate the u,v coordinates too */
595
0
            double
596
0
              uv2[2];
597
0
            uv2[0] = arguments[0] - arguments[5] + arguments[1];   /* u2 */
598
0
            uv2[1] = arguments[1] + arguments[4] - arguments[0];   /* v2 */
599
0
            LeastSquaresAddTerms(matrix,vectors,terms,uv2,3UL,2UL);
600
0
          }
601
0
          else {
602
            /* Sparse Gradient - use values of p0 for linear gradient */
603
0
            LeastSquaresAddTerms(matrix,vectors,terms,
604
0
                  &(arguments[cp_values]),3UL,number_values);
605
0
          }
606
0
        }
607
        /* Solve for LeastSquares Coefficients */
608
0
        status=GaussJordanElimination(matrix,vectors,3UL,number_values);
609
0
        matrix = RelinquishMagickMatrix(matrix, 3UL);
610
0
        vectors = (double **) RelinquishMagickMemory(vectors);
611
0
        if ( status == MagickFalse ) {
612
0
          coeff = (double *) RelinquishMagickMemory(coeff);
613
0
          (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
614
0
              "InvalidArgument","%s : 'Unsolvable Matrix'",
615
0
              CommandOptionToMnemonic(MagickDistortOptions, *method) );
616
0
          return((double *) NULL);
617
0
        }
618
0
      }
619
0
      return(coeff);
620
0
    }
621
0
    case RigidAffineDistortion:
622
0
    {
623
0
      double
624
0
        inverse[6],
625
0
        **matrix,
626
0
        terms[5],
627
0
        *vectors[1];
628
629
0
      MagickBooleanType
630
0
        status;
631
632
      /*
633
        Rigid affine (also known as a Euclidean transform), restricts affine
634
        coefficients to 4 (S, R, Tx, Ty) with Sy=Sx and Ry = -Rx so that one has
635
        only scale, rotation and translation. No skew.
636
      */
637
0
      if (((number_arguments % cp_size) != 0) || (number_arguments < cp_size))
638
0
        {
639
0
          (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
640
0
            "InvalidArgument", "%s : 'require at least %.17g CPs'",
641
0
            CommandOptionToMnemonic(MagickDistortOptions,*method),2.0);
642
0
          coeff=(double *) RelinquishMagickMemory(coeff);
643
0
          return((double *) NULL);
644
0
        }
645
      /*
646
        Rigid affine requires a 4x4 least-squares matrix (zeroed).
647
      */
648
0
      matrix=AcquireMagickMatrix(4UL,4UL);
649
0
      if (matrix == (double **) NULL)
650
0
        {
651
0
          coeff=(double *) RelinquishMagickMemory(coeff);
652
0
          (void) ThrowMagickException(exception,GetMagickModule(),
653
0
            ResourceLimitError,"MemoryAllocationFailed","%s",
654
0
            CommandOptionToMnemonic(MagickDistortOptions,*method));
655
0
          return((double *) NULL);
656
0
        }
657
      /*
658
        Add control points for least squares solving.
659
      */
660
0
      vectors[0]=(&(coeff[0]));
661
0
      for (i=0; i < number_arguments; i+=4)
662
0
      {
663
0
        terms[0]=arguments[i+0];
664
0
        terms[1]=(-arguments[i+1]);
665
0
        terms[2]=1.0;
666
0
        terms[3]=0.0;
667
0
        LeastSquaresAddTerms(matrix,vectors,terms,&(arguments[i+2]),4UL,1UL);
668
0
        terms[0]=arguments[i+1];
669
0
        terms[1]=arguments[i+0];
670
0
        terms[2]=0.0;
671
0
        terms[3]=1.0;
672
0
        LeastSquaresAddTerms(matrix,vectors,terms,&(arguments[i+3]),4UL,1UL);
673
0
      }
674
      /*
675
        Solve for least-squares coefficients.
676
      */
677
0
      status=GaussJordanElimination(matrix,vectors,4UL,1UL);
678
0
      matrix=RelinquishMagickMatrix(matrix,4UL);
679
0
      if (status == MagickFalse)
680
0
        {
681
0
          coeff=(double *) RelinquishMagickMemory(coeff);
682
0
          (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
683
0
            "InvalidArgument","%s : 'Unsolvable Matrix'",
684
0
            CommandOptionToMnemonic(MagickDistortOptions,*method));
685
0
          return((double *) NULL);
686
0
        }
687
      /*
688
        Convert (S, R, Tx, Ty) to an affine projection.
689
      */
690
0
      inverse[0]=coeff[0];
691
0
      inverse[1]=coeff[1];
692
0
      inverse[2]=(-coeff[1]);
693
0
      inverse[3]=coeff[0];
694
0
      inverse[4]=coeff[2];
695
0
      inverse[5]=coeff[3];
696
0
      AffineArgsToCoefficients(inverse);
697
0
      InvertAffineCoefficients(inverse,coeff);
698
0
      *method=AffineDistortion;
699
0
      return(coeff);
700
0
    }
701
0
    case AffineProjectionDistortion:
702
0
    {
703
      /*
704
        Arguments: Affine Matrix (forward mapping)
705
        Arguments  sx, rx, ry, sy, tx, ty
706
        Where      u = sx*x + ry*y + tx
707
                   v = rx*x + sy*y + ty
708
709
        Returns coefficients (in there inverse form) ordered as...
710
             sx ry tx  rx sy ty
711
712
        AffineProjection Distortion Notes...
713
           + Will only work with a 2 number_values for Image Distortion
714
           + Can not be used for generating a sparse gradient (interpolation)
715
      */
716
0
      double inverse[8];
717
0
      if (number_arguments != 6) {
718
0
        coeff = (double *) RelinquishMagickMemory(coeff);
719
0
        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
720
0
              "InvalidArgument","%s : 'Needs 6 coeff values'",
721
0
              CommandOptionToMnemonic(MagickDistortOptions, *method) );
722
0
        return((double *) NULL);
723
0
      }
724
      /* FUTURE: trap test for sx*sy-rx*ry == 0 (determinant = 0, no inverse) */
725
0
      for(i=0; i<6UL; i++ )
726
0
        inverse[i] = arguments[i];
727
0
      AffineArgsToCoefficients(inverse); /* map into coefficients */
728
0
      InvertAffineCoefficients(inverse, coeff); /* invert */
729
0
      *method = AffineDistortion;
730
731
0
      return(coeff);
732
0
    }
733
1.90k
    case ScaleRotateTranslateDistortion:
734
1.90k
    {
735
      /* Scale, Rotate and Translate Distortion
736
         An alternative Affine Distortion
737
         Argument options, by number of arguments given:
738
           7: x,y, sx,sy, a, nx,ny
739
           6: x,y,   s,   a, nx,ny
740
           5: x,y, sx,sy, a
741
           4: x,y,   s,   a
742
           3: x,y,        a
743
           2:        s,   a
744
           1:             a
745
         Where actions are (in order of application)
746
            x,y     'center' of transforms     (default = image center)
747
            sx,sy   scale image by this amount (default = 1)
748
            a       angle of rotation          (argument required)
749
            nx,ny   move 'center' here         (default = x,y or no movement)
750
         And convert to affine mapping coefficients
751
752
         ScaleRotateTranslate Distortion Notes...
753
           + Does not use a set of CPs in any normal way
754
           + Will only work with a 2 number_valuesal Image Distortion
755
           + Cannot be used for generating a sparse gradient (interpolation)
756
      */
757
1.90k
      double
758
1.90k
        cosine, sine,
759
1.90k
        x,y,sx,sy,a,nx,ny;
760
761
      /* set default center, and default scale */
762
1.90k
      x = nx = (double)(image->columns)/2.0 + (double)image->page.x;
763
1.90k
      y = ny = (double)(image->rows)/2.0    + (double)image->page.y;
764
1.90k
      sx = sy = 1.0;
765
1.90k
      switch ( number_arguments ) {
766
0
      case 0:
767
0
        coeff = (double *) RelinquishMagickMemory(coeff);
768
0
        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
769
0
              "InvalidArgument","%s : 'Needs at least 1 argument'",
770
0
              CommandOptionToMnemonic(MagickDistortOptions, *method) );
771
0
        return((double *) NULL);
772
1.90k
      case 1:
773
1.90k
        a = arguments[0];
774
1.90k
        break;
775
0
      case 2:
776
0
        sx = sy = arguments[0];
777
0
        a = arguments[1];
778
0
        break;
779
0
      default:
780
0
        x = nx = arguments[0];
781
0
        y = ny = arguments[1];
782
0
        switch ( number_arguments ) {
783
0
        case 3:
784
0
          a = arguments[2];
785
0
          break;
786
0
        case 4:
787
0
          sx = sy = arguments[2];
788
0
          a = arguments[3];
789
0
          break;
790
0
        case 5:
791
0
          sx = arguments[2];
792
0
          sy = arguments[3];
793
0
          a = arguments[4];
794
0
          break;
795
0
        case 6:
796
0
          sx = sy = arguments[2];
797
0
          a = arguments[3];
798
0
          nx = arguments[4];
799
0
          ny = arguments[5];
800
0
          break;
801
0
        case 7:
802
0
          sx = arguments[2];
803
0
          sy = arguments[3];
804
0
          a = arguments[4];
805
0
          nx = arguments[5];
806
0
          ny = arguments[6];
807
0
          break;
808
0
        default:
809
0
          coeff = (double *) RelinquishMagickMemory(coeff);
810
0
          (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
811
0
              "InvalidArgument","%s : 'Too Many Arguments (7 or less)'",
812
0
              CommandOptionToMnemonic(MagickDistortOptions, *method) );
813
0
          return((double *) NULL);
814
0
        }
815
0
        break;
816
1.90k
      }
817
      /* Trap if sx or sy == 0 -- image is scaled out of existence! */
818
1.90k
      if ( fabs(sx) < MagickEpsilon || fabs(sy) < MagickEpsilon ) {
819
0
        coeff = (double *) RelinquishMagickMemory(coeff);
820
0
        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
821
0
              "InvalidArgument","%s : 'Zero Scale Given'",
822
0
              CommandOptionToMnemonic(MagickDistortOptions, *method) );
823
0
        return((double *) NULL);
824
0
      }
825
      /* Save the given arguments as an affine distortion */
826
1.90k
      a=DegreesToRadians(a); cosine=cos(a); sine=sin(a);
827
828
1.90k
      *method = AffineDistortion;
829
1.90k
      coeff[0]=cosine/sx;
830
1.90k
      coeff[1]=sine/sx;
831
1.90k
      coeff[2]=x-nx*coeff[0]-ny*coeff[1];
832
1.90k
      coeff[3]=(-sine)/sy;
833
1.90k
      coeff[4]=cosine/sy;
834
1.90k
      coeff[5]=y-nx*coeff[3]-ny*coeff[4];
835
1.90k
      return(coeff);
836
1.90k
    }
837
0
    case PerspectiveDistortion:
838
0
    { /*
839
         Perspective Distortion (a ratio of affine distortions)
840
841
                p(x,y)    c0*x + c1*y + c2
842
            u = ------ = ------------------
843
                r(x,y)    c6*x + c7*y + 1
844
845
                q(x,y)    c3*x + c4*y + c5
846
            v = ------ = ------------------
847
                r(x,y)    c6*x + c7*y + 1
848
849
           c8 = Sign of 'r', or the denominator affine, for the actual image.
850
                This determines what part of the distorted image is 'ground'
851
                side of the horizon, the other part is 'sky' or invalid.
852
                Valid values are  +1.0  or  -1.0  only.
853
854
         Input Arguments are sets of control points...
855
         For Distort Images    u,v, x,y  ...
856
         For Sparse Gradients  x,y, r,g,b  ...
857
858
         Perspective Distortion Notes...
859
           + Can be thought of as ratio of  3 affine transformations
860
           + Not separable: r() or c6 and c7 are used by both equations
861
           + All 8 coefficients must be determined simultaneously
862
           + Will only work with a 2 number_valuesal Image Distortion
863
           + Can not be used for generating a sparse gradient (interpolation)
864
           + It is not linear, but is simple to generate an inverse
865
           + All lines within an image remain lines.
866
           + but distances between points may vary.
867
      */
868
0
      double
869
0
        **matrix,
870
0
        *vectors[1],
871
0
        terms[8];
872
873
0
      size_t
874
0
        cp_u = cp_values,
875
0
        cp_v = cp_values+1;
876
877
0
      MagickBooleanType
878
0
        status;
879
880
0
      if ( number_arguments%cp_size != 0 ||
881
0
           number_arguments < cp_size*4 ) {
882
0
        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
883
0
              "InvalidArgument", "%s : 'require at least %.17g CPs'",
884
0
              CommandOptionToMnemonic(MagickDistortOptions, *method), 4.0);
885
0
        coeff=(double *) RelinquishMagickMemory(coeff);
886
0
        return((double *) NULL);
887
0
      }
888
      /* fake 1x8 vectors matrix directly using the coefficients array */
889
0
      vectors[0] = &(coeff[0]);
890
      /* 8x8 least-squares matrix (zeroed) */
891
0
      matrix = AcquireMagickMatrix(8UL,8UL);
892
0
      if (matrix == (double **) NULL) {
893
0
        coeff=(double *) RelinquishMagickMemory(coeff);
894
0
        (void) ThrowMagickException(exception,GetMagickModule(),
895
0
                  ResourceLimitError,"MemoryAllocationFailed",
896
0
                  "%s", "DistortCoefficients");
897
0
        return((double *) NULL);
898
0
      }
899
      /* Add control points for least squares solving */
900
0
      for (i=0; i < number_arguments; i+=4) {
901
0
        terms[0]=arguments[i+cp_x];            /*   c0*x   */
902
0
        terms[1]=arguments[i+cp_y];            /*   c1*y   */
903
0
        terms[2]=1.0;                          /*   c2*1   */
904
0
        terms[3]=0.0;
905
0
        terms[4]=0.0;
906
0
        terms[5]=0.0;
907
0
        terms[6]=-terms[0]*arguments[i+cp_u];  /* 1/(c6*x) */
908
0
        terms[7]=-terms[1]*arguments[i+cp_u];  /* 1/(c7*y) */
909
0
        LeastSquaresAddTerms(matrix,vectors,terms,&(arguments[i+cp_u]),
910
0
            8UL,1UL);
911
912
0
        terms[0]=0.0;
913
0
        terms[1]=0.0;
914
0
        terms[2]=0.0;
915
0
        terms[3]=arguments[i+cp_x];           /*   c3*x   */
916
0
        terms[4]=arguments[i+cp_y];           /*   c4*y   */
917
0
        terms[5]=1.0;                         /*   c5*1   */
918
0
        terms[6]=-terms[3]*arguments[i+cp_v]; /* 1/(c6*x) */
919
0
        terms[7]=-terms[4]*arguments[i+cp_v]; /* 1/(c7*y) */
920
0
        LeastSquaresAddTerms(matrix,vectors,terms,&(arguments[i+cp_v]),
921
0
            8UL,1UL);
922
0
      }
923
      /* Solve for LeastSquares Coefficients */
924
0
      status=GaussJordanElimination(matrix,vectors,8UL,1UL);
925
0
      matrix = RelinquishMagickMatrix(matrix, 8UL);
926
0
      if ( status == MagickFalse ) {
927
0
        coeff = (double *) RelinquishMagickMemory(coeff);
928
0
        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
929
0
            "InvalidArgument","%s : 'Unsolvable Matrix'",
930
0
            CommandOptionToMnemonic(MagickDistortOptions, *method) );
931
0
        return((double *) NULL);
932
0
      }
933
      /*
934
        Calculate 9'th coefficient! The ground-sky determination.
935
        What is sign of the 'ground' in r() denominator affine function?
936
        Just use any valid image coordinate (first control point) in
937
        destination for determination of what part of view is 'ground'.
938
      */
939
0
      coeff[8] = coeff[6]*arguments[cp_x]
940
0
                      + coeff[7]*arguments[cp_y] + 1.0;
941
0
      coeff[8] = (coeff[8] < 0.0) ? -1.0 : +1.0;
942
943
0
      return(coeff);
944
0
    }
945
0
    case PerspectiveProjectionDistortion:
946
0
    {
947
      /*
948
        Arguments: Perspective Coefficients (forward mapping)
949
      */
950
0
      if (number_arguments != 8) {
951
0
        coeff = (double *) RelinquishMagickMemory(coeff);
952
0
        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
953
0
              "InvalidArgument", "%s : 'Needs 8 coefficient values'",
954
0
              CommandOptionToMnemonic(MagickDistortOptions, *method));
955
0
        return((double *) NULL);
956
0
      }
957
      /* FUTURE: trap test  c0*c4-c3*c1 == 0  (determinate = 0, no inverse) */
958
0
      InvertPerspectiveCoefficients(arguments, coeff);
959
      /*
960
        Calculate 9'th coefficient! The ground-sky determination.
961
        What is sign of the 'ground' in r() denominator affine function?
962
        Just use any valid image coordinate in destination for determination.
963
        For a forward mapped perspective the images 0,0 coord will map to
964
        c2,c5 in the distorted image, so set the sign of denominator of that.
965
      */
966
0
      coeff[8] = coeff[6]*arguments[2]
967
0
                           + coeff[7]*arguments[5] + 1.0;
968
0
      coeff[8] = (coeff[8] < 0.0) ? -1.0 : +1.0;
969
0
      *method = PerspectiveDistortion;
970
971
0
      return(coeff);
972
0
    }
973
0
    case BilinearForwardDistortion:
974
0
    case BilinearReverseDistortion:
975
0
    {
976
      /* Bilinear Distortion (Forward mapping)
977
            v = c0*x + c1*y + c2*x*y + c3;
978
         for each 'value' given
979
980
         This is actually a simple polynomial Distortion!  The difference
981
         however is when we need to reverse the above equation to generate a
982
         BilinearForwardDistortion (see below).
983
984
         Input Arguments are sets of control points...
985
         For Distort Images    u,v, x,y  ...
986
         For Sparse Gradients  x,y, r,g,b  ...
987
988
      */
989
0
      double
990
0
        **matrix,
991
0
        **vectors,
992
0
        terms[4];
993
994
0
      MagickBooleanType
995
0
        status;
996
997
      /* check the number of arguments */
998
0
      if ( number_arguments%cp_size != 0 ||
999
0
           number_arguments < cp_size*4 ) {
1000
0
        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1001
0
              "InvalidArgument", "%s : 'require at least %.17g CPs'",
1002
0
              CommandOptionToMnemonic(MagickDistortOptions, *method), 4.0);
1003
0
        coeff=(double *) RelinquishMagickMemory(coeff);
1004
0
        return((double *) NULL);
1005
0
      }
1006
      /* create matrix, and a fake vectors matrix */
1007
0
      matrix=AcquireMagickMatrix(4UL,4UL);
1008
0
      vectors=(double **) AcquireQuantumMemory(number_values,sizeof(*vectors));
1009
0
      if (matrix == (double **) NULL || vectors == (double **) NULL)
1010
0
      {
1011
0
        matrix  = RelinquishMagickMatrix(matrix, 4UL);
1012
0
        vectors = (double **) RelinquishMagickMemory(vectors);
1013
0
        coeff   = (double *) RelinquishMagickMemory(coeff);
1014
0
        (void) ThrowMagickException(exception,GetMagickModule(),
1015
0
                ResourceLimitError,"MemoryAllocationFailed",
1016
0
                "%s", "DistortCoefficients");
1017
0
        return((double *) NULL);
1018
0
      }
1019
      /* fake a number_values x4 vectors matrix from coefficients array */
1020
0
      for (i=0; i < number_values; i++)
1021
0
        vectors[i] = &(coeff[i*4]);
1022
      /* Add given control point pairs for least squares solving */
1023
0
      for (i=0; i < number_arguments; i+=cp_size) {
1024
0
        terms[0] = arguments[i+cp_x];   /*  x  */
1025
0
        terms[1] = arguments[i+cp_y];   /*  y  */
1026
0
        terms[2] = terms[0]*terms[1];   /* x*y */
1027
0
        terms[3] = 1;                   /*  1  */
1028
0
        LeastSquaresAddTerms(matrix,vectors,terms,
1029
0
             &(arguments[i+cp_values]),4UL,number_values);
1030
0
      }
1031
      /* Solve for LeastSquares Coefficients */
1032
0
      status=GaussJordanElimination(matrix,vectors,4UL,number_values);
1033
0
      matrix  = RelinquishMagickMatrix(matrix, 4UL);
1034
0
      vectors = (double **) RelinquishMagickMemory(vectors);
1035
0
      if ( status == MagickFalse ) {
1036
0
        coeff = (double *) RelinquishMagickMemory(coeff);
1037
0
        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1038
0
            "InvalidArgument","%s : 'Unsolvable Matrix'",
1039
0
            CommandOptionToMnemonic(MagickDistortOptions, *method) );
1040
0
        return((double *) NULL);
1041
0
      }
1042
0
      if ( *method == BilinearForwardDistortion ) {
1043
         /* Bilinear Forward Mapped Distortion
1044
1045
         The above least-squares solved for coefficients but in the forward
1046
         direction, due to changes to indexing constants.
1047
1048
            i = c0*x + c1*y + c2*x*y + c3;
1049
            j = c4*x + c5*y + c6*x*y + c7;
1050
1051
         where i,j are in the destination image, NOT the source.
1052
1053
         Reverse Pixel mapping however needs to use reverse of these
1054
         functions.  It required a full page of algebra to work out the
1055
         reversed mapping formula, but resolves down to the following...
1056
1057
            c8 = c0*c5-c1*c4;
1058
            c9 = 2*(c2*c5-c1*c6);   // '2*a' in the quadratic formula
1059
1060
            i = i - c3;   j = j - c7;
1061
            b = c6*i - c2*j + c8;   // So that   a*y^2 + b*y + c == 0
1062
            c = c4*i -  c0*j;       // y = ( -b +- sqrt(bb - 4ac) ) / (2*a)
1063
1064
            r = b*b - c9*(c+c);
1065
            if ( c9 != 0 )
1066
              y = ( -b + sqrt(r) ) / c9;
1067
            else
1068
              y = -c/b;
1069
1070
            x = ( i - c1*y) / ( c1 - c2*y );
1071
1072
         NB: if 'r' is negative there is no solution!
1073
         NB: the sign of the sqrt() should be negative if image becomes
1074
             flipped or flopped, or crosses over itself.
1075
         NB: technically coefficient c5 is not needed, anymore,
1076
             but kept for completeness.
1077
1078
         See Anthony Thyssen <A.Thyssen@griffith.edu.au>
1079
         or  Fred Weinhaus <fmw@alink.net>  for more details.
1080
1081
         */
1082
0
         coeff[8] = coeff[0]*coeff[5] - coeff[1]*coeff[4];
1083
0
         coeff[9] = 2*(coeff[2]*coeff[5] - coeff[1]*coeff[6]);
1084
0
      }
1085
0
      return(coeff);
1086
0
    }
1087
#if 0
1088
    case QuadrilateralDistortion:
1089
    {
1090
      /* Map a Quadrilateral to a unit square using BilinearReverse
1091
         Then map that unit square back to the final Quadrilateral
1092
         using BilinearForward.
1093
1094
         Input Arguments are sets of control points...
1095
         For Distort Images    u,v, x,y  ...
1096
         For Sparse Gradients  x,y, r,g,b  ...
1097
1098
      */
1099
      /* UNDER CONSTRUCTION */
1100
      return(coeff);
1101
    }
1102
#endif
1103
1104
0
    case PolynomialDistortion:
1105
0
    {
1106
      /* Polynomial Distortion
1107
1108
         First two coefficients are used to hole global polynomial information
1109
           c0 = Order of the polynomial being created
1110
           c1 = number_of_terms in one polynomial equation
1111
1112
         Rest of the coefficients map to the equations....
1113
            v = c0 + c1*x + c2*y + c3*x*y + c4*x^2 + c5*y^2 + c6*x^3 + ...
1114
         for each 'value' (number_values of them) given.
1115
         As such total coefficients =  2 + number_terms * number_values
1116
1117
         Input Arguments are sets of control points...
1118
         For Distort Images    order  [u,v, x,y] ...
1119
         For Sparse Gradients  order  [x,y, r,g,b] ...
1120
1121
         Polynomial Distortion Notes...
1122
           + UNDER DEVELOPMENT -- Do not expect this to remain as is.
1123
           + Currently polynomial is a reversed mapped distortion.
1124
           + Order 1.5 is fudged to map into a bilinear distortion.
1125
             though it is not the same order as that distortion.
1126
      */
1127
0
      double
1128
0
        **matrix,
1129
0
        **vectors,
1130
0
        *terms;
1131
1132
0
      size_t
1133
0
        nterms;   /* number of polynomial terms per number_values */
1134
1135
0
      ssize_t
1136
0
        j;
1137
1138
0
      MagickBooleanType
1139
0
        status;
1140
1141
      /* first two coefficients hold polynomial order information */
1142
0
      coeff[0] = arguments[0];
1143
0
      coeff[1] = (double) poly_number_terms(arguments[0]);
1144
0
      nterms = CastDoubleToSizeT(coeff[1]);
1145
1146
      /* create matrix, a fake vectors matrix, and least sqs terms */
1147
0
      matrix=AcquireMagickMatrix(nterms,nterms);
1148
0
      vectors=(double **) AcquireQuantumMemory(number_values,
1149
0
        sizeof(*vectors));
1150
0
      terms=(double *) AcquireQuantumMemory(nterms,sizeof(*terms));
1151
0
      if ((matrix  == (double **) NULL) || (vectors == (double **) NULL) ||
1152
0
          (terms   == (double *) NULL))
1153
0
      {
1154
0
        matrix  = RelinquishMagickMatrix(matrix, nterms);
1155
0
        vectors = (double **) RelinquishMagickMemory(vectors);
1156
0
        terms   = (double *) RelinquishMagickMemory(terms);
1157
0
        coeff   = (double *) RelinquishMagickMemory(coeff);
1158
0
        (void) ThrowMagickException(exception,GetMagickModule(),
1159
0
                ResourceLimitError,"MemoryAllocationFailed",
1160
0
                "%s", "DistortCoefficients");
1161
0
        return((double *) NULL);
1162
0
      }
1163
      /* fake a number_values x3 vectors matrix from coefficients array */
1164
0
      for (i=0; i < number_values; i++)
1165
0
        vectors[i] = &(coeff[2+i*nterms]);
1166
      /* Add given control point pairs for least squares solving */
1167
0
      for (i=1; i < number_arguments; i+=cp_size) { /* NB: start = 1 not 0 */
1168
0
        for (j=0; j < (ssize_t) nterms; j++)
1169
0
          terms[j] = poly_basis_fn(j,arguments[i+cp_x],arguments[i+cp_y]);
1170
0
        LeastSquaresAddTerms(matrix,vectors,terms,
1171
0
             &(arguments[i+cp_values]),nterms,number_values);
1172
0
      }
1173
0
      terms = (double *) RelinquishMagickMemory(terms);
1174
      /* Solve for LeastSquares Coefficients */
1175
0
      status=GaussJordanElimination(matrix,vectors,nterms,number_values);
1176
0
      matrix  = RelinquishMagickMatrix(matrix, nterms);
1177
0
      vectors = (double **) RelinquishMagickMemory(vectors);
1178
0
      if ( status == MagickFalse ) {
1179
0
        coeff = (double *) RelinquishMagickMemory(coeff);
1180
0
        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1181
0
            "InvalidArgument","%s : 'Unsolvable Matrix'",
1182
0
            CommandOptionToMnemonic(MagickDistortOptions, *method) );
1183
0
        return((double *) NULL);
1184
0
      }
1185
0
      return(coeff);
1186
0
    }
1187
0
    case ArcDistortion:
1188
0
    {
1189
      /* Arc Distortion
1190
         Args: arc_width  rotate  top_edge_radius  bottom_edge_radius
1191
         All but first argument are optional
1192
            arc_width      The angle over which to arc the image side-to-side
1193
            rotate         Angle to rotate image from vertical center
1194
            top_radius     Set top edge of source image at this radius
1195
            bottom_radius  Set bottom edge to this radius (radial scaling)
1196
1197
         By default, if the radii arguments are nor provided the image radius
1198
         is calculated so the horizontal center-line is fits the given arc
1199
         without scaling.
1200
1201
         The output image size is ALWAYS adjusted to contain the whole image,
1202
         and an offset is given to position image relative to the 0,0 point of
1203
         the origin, allowing users to use relative positioning onto larger
1204
         background (via -flatten).
1205
1206
         The arguments are converted to these coefficients
1207
            c0: angle for center of source image
1208
            c1: angle scale for mapping to source image
1209
            c2: radius for top of source image
1210
            c3: radius scale for mapping source image
1211
            c4: centerline of arc within source image
1212
1213
         Note the coefficients use a center angle, so asymptotic join is
1214
         furthest from both sides of the source image. This also means that
1215
         for arc angles greater than 360 the sides of the image will be
1216
         trimmed equally.
1217
1218
         Arc Distortion Notes...
1219
           + Does not use a set of CPs
1220
           + Will only work with Image Distortion
1221
           + Can not be used for generating a sparse gradient (interpolation)
1222
      */
1223
0
      if ( number_arguments >= 1 && arguments[0] < MagickEpsilon ) {
1224
0
        coeff = (double *) RelinquishMagickMemory(coeff);
1225
0
        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1226
0
            "InvalidArgument","%s : 'Arc Angle Too Small'",
1227
0
            CommandOptionToMnemonic(MagickDistortOptions, *method) );
1228
0
        return((double *) NULL);
1229
0
      }
1230
0
      if ( number_arguments >= 3 && arguments[2] < MagickEpsilon ) {
1231
0
        coeff = (double *) RelinquishMagickMemory(coeff);
1232
0
        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1233
0
            "InvalidArgument","%s : 'Outer Radius Too Small'",
1234
0
            CommandOptionToMnemonic(MagickDistortOptions, *method) );
1235
0
        return((double *) NULL);
1236
0
      }
1237
0
      coeff[0] = -MagickPI2;   /* -90, place at top! */
1238
0
      if ( number_arguments >= 1 )
1239
0
        coeff[1] = DegreesToRadians(arguments[0]);
1240
0
      else
1241
0
        coeff[1] = MagickPI2;   /* zero arguments - center is at top */
1242
0
      if ( number_arguments >= 2 )
1243
0
        coeff[0] += DegreesToRadians(arguments[1]);
1244
0
      coeff[0] /= Magick2PI;  /* normalize radians */
1245
0
      coeff[0] -= MagickRound(coeff[0]);
1246
0
      coeff[0] *= Magick2PI;  /* de-normalize back to radians */
1247
0
      coeff[3] = (double)image->rows-1;
1248
0
      coeff[2] = (double)image->columns/coeff[1] + coeff[3]/2.0;
1249
0
      if ( number_arguments >= 3 ) {
1250
0
        if ( number_arguments >= 4 )
1251
0
          coeff[3] = arguments[2] - arguments[3];
1252
0
        else
1253
0
          coeff[3] *= arguments[2]/coeff[2];
1254
0
        coeff[2] = arguments[2];
1255
0
      }
1256
0
      coeff[4] = ((double)image->columns-1.0)/2.0;
1257
1258
0
      return(coeff);
1259
0
    }
1260
0
    case PolarDistortion:
1261
0
    case DePolarDistortion:
1262
0
    {
1263
      /* (De)Polar Distortion   (same set of arguments)
1264
         Args:  Rmax, Rmin,  Xcenter,Ycenter,  Afrom,Ato
1265
         DePolar can also have the extra arguments of Width, Height
1266
1267
         Coefficients 0 to 5 is the sanitized version first 6 input args
1268
         Coefficient 6  is the angle to coord ratio  and visa-versa
1269
         Coefficient 7  is the radius to coord ratio and visa-versa
1270
1271
         WARNING: It is possible for  Radius max<min  and/or  Angle from>to
1272
      */
1273
0
      if ( number_arguments == 3
1274
0
          || ( number_arguments > 6 && *method == PolarDistortion )
1275
0
          || number_arguments > 8 ) {
1276
0
          (void) ThrowMagickException(exception,GetMagickModule(),
1277
0
            OptionError,"InvalidArgument", "%s : number of arguments",
1278
0
            CommandOptionToMnemonic(MagickDistortOptions, *method) );
1279
0
        coeff=(double *) RelinquishMagickMemory(coeff);
1280
0
        return((double *) NULL);
1281
0
      }
1282
      /* Rmax -  if 0 calculate appropriate value */
1283
0
      if ( number_arguments >= 1 )
1284
0
        coeff[0] = arguments[0];
1285
0
      else
1286
0
        coeff[0] = 0.0;
1287
      /* Rmin  - usually 0 */
1288
0
      coeff[1] = number_arguments >= 2 ? arguments[1] : 0.0;
1289
      /* Center X,Y */
1290
0
      if ( number_arguments >= 4 ) {
1291
0
        coeff[2] = arguments[2];
1292
0
        coeff[3] = arguments[3];
1293
0
      }
1294
0
      else { /* center of actual image */
1295
0
        coeff[2] = (double)(image->columns)/2.0+image->page.x;
1296
0
        coeff[3] = (double)(image->rows)/2.0+image->page.y;
1297
0
      }
1298
      /* Angle from,to - about polar center 0 is downward */
1299
0
      coeff[4] = -MagickPI;
1300
0
      if ( number_arguments >= 5 )
1301
0
        coeff[4] = DegreesToRadians(arguments[4]);
1302
0
      coeff[5] = coeff[4];
1303
0
      if ( number_arguments >= 6 )
1304
0
        coeff[5] = DegreesToRadians(arguments[5]);
1305
0
      if ( fabs(coeff[4]-coeff[5]) < MagickEpsilon )
1306
0
        coeff[5] += Magick2PI; /* same angle is a full circle */
1307
      /* if radius 0 or negative,  its a special value... */
1308
0
      if ( coeff[0] < MagickEpsilon ) {
1309
        /* Use closest edge  if radius == 0 */
1310
0
        if ( fabs(coeff[0]) < MagickEpsilon ) {
1311
0
          coeff[0]=MagickMin(fabs(coeff[2]-image->page.x),
1312
0
                             fabs(coeff[3]-image->page.y));
1313
0
          coeff[0]=MagickMin(coeff[0],
1314
0
                       fabs(coeff[2]-image->page.x-image->columns));
1315
0
          coeff[0]=MagickMin(coeff[0],
1316
0
                       fabs(coeff[3]-image->page.y-image->rows));
1317
0
        }
1318
        /* furthest diagonal if radius == -1 */
1319
0
        if ( fabs(-1.0-coeff[0]) < MagickEpsilon ) {
1320
0
          double rx,ry;
1321
0
          rx = coeff[2]-image->page.x;
1322
0
          ry = coeff[3]-image->page.y;
1323
0
          coeff[0] = rx*rx+ry*ry;
1324
0
          ry = coeff[3]-image->page.y-image->rows;
1325
0
          coeff[0] = MagickMax(coeff[0],rx*rx+ry*ry);
1326
0
          rx = coeff[2]-image->page.x-image->columns;
1327
0
          coeff[0] = MagickMax(coeff[0],rx*rx+ry*ry);
1328
0
          ry = coeff[3]-image->page.y;
1329
0
          coeff[0] = MagickMax(coeff[0],rx*rx+ry*ry);
1330
0
          coeff[0] = sqrt(coeff[0]);
1331
0
        }
1332
0
      }
1333
      /* IF Rmax <= 0 or Rmin < 0 OR Rmax < Rmin, THEN error */
1334
0
      if ( coeff[0] < MagickEpsilon || coeff[1] < -MagickEpsilon
1335
0
           || (coeff[0]-coeff[1]) < MagickEpsilon ) {
1336
0
        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1337
0
            "InvalidArgument", "%s : Invalid Radius",
1338
0
            CommandOptionToMnemonic(MagickDistortOptions, *method) );
1339
0
        coeff=(double *) RelinquishMagickMemory(coeff);
1340
0
        return((double *) NULL);
1341
0
      }
1342
      /* conversion ratios */
1343
0
      if ( *method == PolarDistortion ) {
1344
0
        coeff[6]=(double) image->columns/(coeff[5]-coeff[4]);
1345
0
        coeff[7]=(double) image->rows/(coeff[0]-coeff[1]);
1346
0
      }
1347
0
      else { /* *method == DePolarDistortion */
1348
0
        coeff[6]=(coeff[5]-coeff[4])/image->columns;
1349
0
        coeff[7]=(coeff[0]-coeff[1])/image->rows;
1350
0
      }
1351
0
      return(coeff);
1352
0
    }
1353
0
    case Cylinder2PlaneDistortion:
1354
0
    case Plane2CylinderDistortion:
1355
0
    {
1356
      /* 3D Cylinder to/from a Tangential Plane
1357
1358
         Projection between a cylinder and flat plain from a point on the
1359
         center line of the cylinder.
1360
1361
         The two surfaces coincide in 3D space at the given centers of
1362
         distortion (perpendicular to projection point) on both images.
1363
1364
         Args:  FOV_arc_width
1365
         Coefficients: FOV(radians), Radius, center_x,y, dest_center_x,y
1366
1367
         FOV (Field Of View) the angular field of view of the distortion,
1368
         across the width of the image, in degrees.  The centers are the
1369
         points of least distortion in the input and resulting images.
1370
1371
         These centers are however determined later.
1372
1373
         Coeff 0 is the FOV angle of view of image width in radians
1374
         Coeff 1 is calculated radius of cylinder.
1375
         Coeff 2,3  center of distortion of input image
1376
         Coefficients 4,5 Center of Distortion of dest (determined later)
1377
      */
1378
0
      if (number_arguments < 1) {
1379
0
        coeff=(double *) RelinquishMagickMemory(coeff);
1380
0
        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1381
0
            "InvalidArgument", "%s : 'Needs at least 1 argument'",
1382
0
            CommandOptionToMnemonic(MagickDistortOptions,*method));
1383
0
        return((double *) NULL);
1384
0
      }
1385
0
      if ( arguments[0] < MagickEpsilon || arguments[0] > 160.0 ) {
1386
0
        coeff=(double *) RelinquishMagickMemory(coeff);
1387
0
        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1388
0
            "InvalidArgument", "%s : Invalid FOV Angle",
1389
0
            CommandOptionToMnemonic(MagickDistortOptions,*method));
1390
0
        return((double *) NULL);
1391
0
      }
1392
0
      coeff[0] = DegreesToRadians(arguments[0]);
1393
0
      if ( *method == Cylinder2PlaneDistortion )
1394
        /* image is curved around cylinder, so FOV angle (in radians)
1395
         * scales directly to image X coordinate, according to its radius.
1396
         */
1397
0
        coeff[1] = (double) image->columns/coeff[0];
1398
0
      else
1399
        /* radius is distance away from an image with this angular FOV */
1400
0
        coeff[1] = (double) image->columns / ( 2 * tan(coeff[0]/2) );
1401
1402
0
      coeff[2] = (double)(image->columns)/2.0+image->page.x;
1403
0
      coeff[3] = (double)(image->rows)/2.0+image->page.y;
1404
0
      coeff[4] = coeff[2];
1405
0
      coeff[5] = coeff[3]; /* assuming image size is the same */
1406
0
      return(coeff);
1407
0
    }
1408
0
    case BarrelDistortion:
1409
0
    case BarrelInverseDistortion:
1410
0
    {
1411
      /* Barrel Distortion
1412
           Rs=(A*Rd^3 + B*Rd^2 + C*Rd + D)*Rd
1413
         BarrelInv Distortion
1414
           Rs=Rd/(A*Rd^3 + B*Rd^2 + C*Rd + D)
1415
1416
        Where Rd is the normalized radius from corner to middle of image
1417
        Input Arguments are one of the following forms (number of arguments)...
1418
            3:  A,B,C
1419
            4:  A,B,C,D
1420
            5:  A,B,C    X,Y
1421
            6:  A,B,C,D  X,Y
1422
            8:  Ax,Bx,Cx,Dx  Ay,By,Cy,Dy
1423
           10:  Ax,Bx,Cx,Dx  Ay,By,Cy,Dy   X,Y
1424
1425
        Returns 10 coefficient values, which are de-normalized (pixel scale)
1426
          Ax, Bx, Cx, Dx,   Ay, By, Cy, Dy,    Xc, Yc
1427
      */
1428
      /* Radius de-normalization scaling factor */
1429
0
      double
1430
0
        rscale = 2.0/MagickMin((double) image->columns,(double) image->rows);
1431
1432
      /* sanity check  number of args must = 3,4,5,6,8,10 or error */
1433
0
      if ( (number_arguments  < 3) || (number_arguments == 7) ||
1434
0
           (number_arguments == 9) || (number_arguments > 10) )
1435
0
        {
1436
0
          coeff=(double *) RelinquishMagickMemory(coeff);
1437
0
          (void) ThrowMagickException(exception,GetMagickModule(),
1438
0
            OptionError,"InvalidArgument", "%s : number of arguments",
1439
0
            CommandOptionToMnemonic(MagickDistortOptions, *method) );
1440
0
          return((double *) NULL);
1441
0
        }
1442
      /* A,B,C,D coefficients */
1443
0
      coeff[0] = arguments[0];
1444
0
      coeff[1] = arguments[1];
1445
0
      coeff[2] = arguments[2];
1446
0
      if ((number_arguments == 3) || (number_arguments == 5) )
1447
0
        coeff[3] = 1.0 - coeff[0] - coeff[1] - coeff[2];
1448
0
      else
1449
0
        coeff[3] = arguments[3];
1450
      /* de-normalize the coefficients */
1451
0
      coeff[0] *= pow(rscale,3.0);
1452
0
      coeff[1] *= rscale*rscale;
1453
0
      coeff[2] *= rscale;
1454
      /* Y coefficients: as given OR same as X coefficients */
1455
0
      if ( number_arguments >= 8 ) {
1456
0
        coeff[4] = arguments[4] * pow(rscale,3.0);
1457
0
        coeff[5] = arguments[5] * rscale*rscale;
1458
0
        coeff[6] = arguments[6] * rscale;
1459
0
        coeff[7] = arguments[7];
1460
0
      }
1461
0
      else {
1462
0
        coeff[4] = coeff[0];
1463
0
        coeff[5] = coeff[1];
1464
0
        coeff[6] = coeff[2];
1465
0
        coeff[7] = coeff[3];
1466
0
      }
1467
      /* X,Y Center of Distortion (image coordinates) */
1468
0
      if ( number_arguments == 5 )  {
1469
0
        coeff[8] = arguments[3];
1470
0
        coeff[9] = arguments[4];
1471
0
      }
1472
0
      else if ( number_arguments == 6 ) {
1473
0
        coeff[8] = arguments[4];
1474
0
        coeff[9] = arguments[5];
1475
0
      }
1476
0
      else if ( number_arguments == 10 ) {
1477
0
        coeff[8] = arguments[8];
1478
0
        coeff[9] = arguments[9];
1479
0
      }
1480
0
      else {
1481
        /* center of the image provided (image coordinates) */
1482
0
        coeff[8] = (double)image->columns/2.0 + image->page.x;
1483
0
        coeff[9] = (double)image->rows/2.0    + image->page.y;
1484
0
      }
1485
0
      return(coeff);
1486
0
    }
1487
0
    case ShepardsDistortion:
1488
0
    {
1489
      /* Shepards Distortion  input arguments are the coefficients!
1490
         Just check the number of arguments is valid!
1491
         Args:  u1,v1, x1,y1, ...
1492
          OR :  u1,v1, r1,g1,c1, ...
1493
      */
1494
0
      if ( number_arguments%cp_size != 0 ||
1495
0
           number_arguments < cp_size ) {
1496
0
        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1497
0
              "InvalidArgument", "%s : 'requires CP's (4 numbers each)'",
1498
0
              CommandOptionToMnemonic(MagickDistortOptions, *method));
1499
0
        coeff=(double *) RelinquishMagickMemory(coeff);
1500
0
        return((double *) NULL);
1501
0
      }
1502
      /* User defined weighting power for Shepard's Method */
1503
0
      { const char *artifact=GetImageArtifact(image,"shepards:power");
1504
0
        if ( artifact != (const char *) NULL ) {
1505
0
          coeff[0]=StringToDouble(artifact,(char **) NULL) / 2.0;
1506
0
          if ( coeff[0] < MagickEpsilon ) {
1507
0
            (void) ThrowMagickException(exception,GetMagickModule(),
1508
0
                OptionError,"InvalidArgument","%s", "-define shepards:power" );
1509
0
            coeff=(double *) RelinquishMagickMemory(coeff);
1510
0
            return((double *) NULL);
1511
0
          }
1512
0
        }
1513
0
        else
1514
0
          coeff[0]=1.0;  /* Default power of 2 (Inverse Squared) */
1515
0
      }
1516
0
      return(coeff);
1517
0
    }
1518
0
    default:
1519
0
      break;
1520
1.90k
  }
1521
  /* you should never reach this point */
1522
0
  perror("no method handler"); /* just fail assertion */
1523
0
  return((double *) NULL);
1524
1.90k
}
1525

1526
/*
1527
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1528
%                                                                             %
1529
%                                                                             %
1530
%                                                                             %
1531
+   D i s t o r t R e s i z e I m a g e                                       %
1532
%                                                                             %
1533
%                                                                             %
1534
%                                                                             %
1535
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1536
%
1537
%  DistortResizeImage() resize image using the equivalent but slower image
1538
%  distortion operator.  The filter is applied using a EWA cylindrical
1539
%  resampling. But like resize the final image size is limited to whole pixels
1540
%  with no effects by virtual-pixels on the result.
1541
%
1542
%  Note that images containing a transparency channel will be twice as slow to
1543
%  resize as images one without transparency.
1544
%
1545
%  The format of the DistortResizeImage method is:
1546
%
1547
%      Image *DistortResizeImage(const Image *image,const size_t columns,
1548
%        const size_t rows,ExceptionInfo *exception)
1549
%
1550
%  A description of each parameter follows:
1551
%
1552
%    o image: the image.
1553
%
1554
%    o columns: the number of columns in the resized image.
1555
%
1556
%    o rows: the number of rows in the resized image.
1557
%
1558
%    o exception: return any errors or warnings in this structure.
1559
%
1560
*/
1561
MagickExport Image *DistortResizeImage(const Image *image,const size_t columns,
1562
  const size_t rows,ExceptionInfo *exception)
1563
0
{
1564
0
#define DistortResizeImageTag  "Distort/Image"
1565
1566
0
  Image
1567
0
    *resize_image,
1568
0
    *tmp_image;
1569
1570
0
  RectangleInfo
1571
0
    crop_area;
1572
1573
0
  double
1574
0
    distort_args[12];
1575
1576
0
  VirtualPixelMethod
1577
0
    vp_save;
1578
1579
  /*
1580
    Distort resize image.
1581
  */
1582
0
  assert(image != (const Image *) NULL);
1583
0
  assert(image->signature == MagickCoreSignature);
1584
0
  assert(exception != (ExceptionInfo *) NULL);
1585
0
  assert(exception->signature == MagickCoreSignature);
1586
0
  if (IsEventLogging() != MagickFalse)
1587
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1588
0
  if ((columns == 0) || (rows == 0))
1589
0
    return((Image *) NULL);
1590
  /* Do not short-circuit this resize if final image size is unchanged */
1591
1592
0
  (void) memset(distort_args,0,sizeof(distort_args));
1593
0
  distort_args[4]=(double) image->columns;
1594
0
  distort_args[6]=(double) columns;
1595
0
  distort_args[9]=(double) image->rows;
1596
0
  distort_args[11]=(double) rows;
1597
1598
0
  vp_save=GetImageVirtualPixelMethod(image);
1599
1600
0
  tmp_image=CloneImage(image,0,0,MagickTrue,exception);
1601
0
  if (tmp_image == (Image *) NULL)
1602
0
    return((Image *) NULL);
1603
0
  (void) SetImageVirtualPixelMethod(tmp_image,TransparentVirtualPixelMethod,
1604
0
    exception);
1605
1606
0
  if ((image->alpha_trait & BlendPixelTrait) == 0)
1607
0
    {
1608
      /*
1609
        Image has no alpha channel, so we are free to use it.
1610
      */
1611
0
      (void) SetImageAlphaChannel(tmp_image,SetAlphaChannel,exception);
1612
0
      resize_image=DistortImage(tmp_image,AffineDistortion,12,distort_args,
1613
0
        MagickTrue,exception),
1614
0
      tmp_image=DestroyImage(tmp_image);
1615
0
      if (resize_image == (Image *) NULL)
1616
0
        return((Image *) NULL);
1617
0
      (void) SetImageAlphaChannel(resize_image,OffAlphaChannel,exception);
1618
0
    }
1619
0
  else
1620
0
    {
1621
      /*
1622
        Image has transparency so handle colors and alpha separately.
1623
        Basically we need to separate Virtual-Pixel alpha in the resized
1624
        image, so only the actual original images alpha channel is used.
1625
1626
        distort alpha channel separately
1627
      */
1628
0
      Image
1629
0
        *resize_alpha;
1630
1631
0
      (void) SetImageAlphaChannel(tmp_image,ExtractAlphaChannel,exception);
1632
0
      (void) SetImageAlphaChannel(tmp_image,OpaqueAlphaChannel,exception);
1633
0
      resize_alpha=DistortImage(tmp_image,AffineDistortion,12,distort_args,
1634
0
        MagickTrue,exception),
1635
0
      tmp_image=DestroyImage(tmp_image);
1636
0
      if (resize_alpha == (Image *) NULL)
1637
0
        return((Image *) NULL);
1638
1639
      /* distort the actual image containing alpha + VP alpha */
1640
0
      tmp_image=CloneImage(image,0,0,MagickTrue,exception);
1641
0
      if (tmp_image == (Image *) NULL)
1642
0
        return((Image *) NULL);
1643
0
      (void) SetImageVirtualPixelMethod(tmp_image,
1644
0
        TransparentVirtualPixelMethod,exception);
1645
0
      resize_image=DistortImage(tmp_image,AffineDistortion,12,distort_args,
1646
0
        MagickTrue,exception),
1647
0
      tmp_image=DestroyImage(tmp_image);
1648
0
      if (resize_image == (Image *) NULL)
1649
0
        {
1650
0
          resize_alpha=DestroyImage(resize_alpha);
1651
0
          return((Image *) NULL);
1652
0
        }
1653
      /* replace resize images alpha with the separately distorted alpha */
1654
0
      (void) SetImageAlphaChannel(resize_image,OffAlphaChannel,exception);
1655
0
      (void) SetImageAlphaChannel(resize_alpha,OffAlphaChannel,exception);
1656
0
      (void) CompositeImage(resize_image,resize_alpha,CopyAlphaCompositeOp,
1657
0
        MagickTrue,0,0,exception);
1658
0
      resize_alpha=DestroyImage(resize_alpha);
1659
0
      resize_image->alpha_trait=image->alpha_trait;
1660
0
      resize_image->compose=image->compose;
1661
0
    }
1662
0
  (void) SetImageVirtualPixelMethod(resize_image,vp_save,exception);
1663
1664
  /*
1665
    Clean up the results of the Distortion
1666
  */
1667
0
  crop_area.width=columns;
1668
0
  crop_area.height=rows;
1669
0
  crop_area.x=0;
1670
0
  crop_area.y=0;
1671
1672
0
  tmp_image=resize_image;
1673
0
  resize_image=CropImage(tmp_image,&crop_area,exception);
1674
0
  tmp_image=DestroyImage(tmp_image);
1675
0
  if (resize_image != (Image *) NULL)
1676
0
    {
1677
0
      resize_image->page.width=0;
1678
0
      resize_image->page.height=0;
1679
0
    }
1680
0
  return(resize_image);
1681
0
}
1682

1683
/*
1684
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1685
%                                                                             %
1686
%                                                                             %
1687
%                                                                             %
1688
%   D i s t o r t I m a g e                                                   %
1689
%                                                                             %
1690
%                                                                             %
1691
%                                                                             %
1692
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1693
%
1694
%  DistortImage() distorts an image using various distortion methods, by
1695
%  mapping color lookups of the source image to a new destination image
1696
%  usually of the same size as the source image, unless 'bestfit' is set to
1697
%  true.
1698
%
1699
%  If 'bestfit' is enabled, and distortion allows it, the destination image is
1700
%  adjusted to ensure the whole source 'image' will just fit within the final
1701
%  destination image, which will be sized and offset accordingly.  Also in
1702
%  many cases the virtual offset of the source image will be taken into
1703
%  account in the mapping.
1704
%
1705
%  If the '-verbose' control option has been set print to standard error the
1706
%  equivalent '-fx' formula with coefficients for the function, if practical.
1707
%
1708
%  The format of the DistortImage() method is:
1709
%
1710
%      Image *DistortImage(const Image *image,const DistortMethod method,
1711
%        const size_t number_arguments,const double *arguments,
1712
%        MagickBooleanType bestfit, ExceptionInfo *exception)
1713
%
1714
%  A description of each parameter follows:
1715
%
1716
%    o image: the image to be distorted.
1717
%
1718
%    o method: the method of image distortion.
1719
%
1720
%        ArcDistortion always ignores source image offset, and always
1721
%        'bestfit' the destination image with the top left corner offset
1722
%        relative to the polar mapping center.
1723
%
1724
%        Affine, Perspective, and Bilinear, do least squares fitting of the
1725
%        distortion when more than the minimum number of control point pairs
1726
%        are provided.
1727
%
1728
%        Perspective, and Bilinear, fall back to a Affine distortion when less
1729
%        than 4 control point pairs are provided.  While Affine distortions
1730
%        let you use any number of control point pairs, that is Zero pairs is
1731
%        a No-Op (viewport only) distortion, one pair is a translation and
1732
%        two pairs of control points do a scale-rotate-translate, without any
1733
%        shearing.
1734
%
1735
%    o number_arguments: the number of arguments given.
1736
%
1737
%    o arguments: an array of floating point arguments for this method.
1738
%
1739
%    o bestfit: Attempt to 'bestfit' the size of the resulting image.
1740
%        This also forces the resulting image to be a 'layered' virtual
1741
%        canvas image.  Can be overridden using 'distort:viewport' setting.
1742
%
1743
%    o exception: return any errors or warnings in this structure
1744
%
1745
%  Extra Controls from Image meta-data (artifacts)...
1746
%
1747
%    o "verbose"
1748
%        Output to stderr alternatives, internal coefficients, and FX
1749
%        equivalents for the distortion operation (if feasible).
1750
%        This forms an extra check of the distortion method, and allows users
1751
%        access to the internal constants IM calculates for the distortion.
1752
%
1753
%    o "distort:viewport"
1754
%        Directly set the output image canvas area and offset to use for the
1755
%        resulting image, rather than use the original images canvas, or a
1756
%        calculated 'bestfit' canvas.
1757
%
1758
%    o "distort:scale"
1759
%        Scale the size of the output canvas by this amount to provide a
1760
%        method of Zooming, and for super-sampling the results.
1761
%
1762
%  Other settings that can effect results include
1763
%
1764
%    o 'interpolate' For source image lookups (scale enlargements)
1765
%
1766
%    o 'filter'      Set filter to use for area-resampling (scale shrinking).
1767
%                    Set to 'point' to turn off and use 'interpolate' lookup
1768
%                    instead
1769
%
1770
*/
1771
MagickExport Image *DistortImage(const Image *image, DistortMethod method,
1772
  const size_t number_arguments,const double *arguments,
1773
  MagickBooleanType bestfit,ExceptionInfo *exception)
1774
1.90k
{
1775
1.90k
#define DistortImageTag  "Distort/Image"
1776
1777
1.90k
  double
1778
1.90k
    *coeff,
1779
1.90k
    output_scaling;
1780
1781
1.90k
  Image
1782
1.90k
    *distort_image;
1783
1784
1.90k
  RectangleInfo
1785
1.90k
    geometry;  /* geometry of the distorted space viewport */
1786
1787
1.90k
  MagickBooleanType
1788
1.90k
    viewport_given;
1789
1790
1.90k
  PixelInfo
1791
1.90k
    invalid;  /* the color to assign when distort result is invalid */
1792
1793
1.90k
  assert(image != (Image *) NULL);
1794
1.90k
  assert(image->signature == MagickCoreSignature);
1795
1.90k
  assert(exception != (ExceptionInfo *) NULL);
1796
1.90k
  assert(exception->signature == MagickCoreSignature);
1797
1.90k
  if (IsEventLogging() != MagickFalse)
1798
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1799
  /*
1800
    Handle Special Compound Distortions
1801
  */
1802
1.90k
  if ( method == ResizeDistortion )
1803
0
    {
1804
0
      if ( number_arguments != 2 )
1805
0
        {
1806
0
          (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1807
0
                    "InvalidArgument","%s : '%s'","Resize",
1808
0
                    "Invalid number of args: 2 only");
1809
0
          return((Image *) NULL);
1810
0
        }
1811
0
      distort_image=DistortResizeImage(image,CastDoubleToSizeT(arguments[0]),
1812
0
        CastDoubleToSizeT(arguments[1]),exception);
1813
0
      return(distort_image);
1814
0
    }
1815
1816
  /*
1817
    Convert input arguments (usually as control points for reverse mapping)
1818
    into mapping coefficients to apply the distortion.
1819
1820
    Note that some distortions are mapped to other distortions,
1821
    and as such do not require specific code after this point.
1822
  */
1823
1.90k
  coeff = GenerateCoefficients(image, &method, number_arguments,
1824
1.90k
      arguments, 0, exception);
1825
1.90k
  if ( coeff == (double *) NULL )
1826
0
    return((Image *) NULL);
1827
1828
  /*
1829
    Determine the size and offset for a 'bestfit' destination.
1830
    Usually the four corners of the source image is enough.
1831
  */
1832
1833
  /* default output image bounds, when no 'bestfit' is requested */
1834
1.90k
  geometry.width=image->columns;
1835
1.90k
  geometry.height=image->rows;
1836
1.90k
  geometry.x=0;
1837
1.90k
  geometry.y=0;
1838
1839
1.90k
  if ( method == ArcDistortion ) {
1840
0
    bestfit = MagickTrue;  /* always calculate a 'best fit' viewport */
1841
0
  }
1842
1843
  /* Work out the 'best fit', (required for ArcDistortion) */
1844
1.90k
  if ( bestfit ) {
1845
1.90k
    PointInfo
1846
1.90k
      s,d,min,max;  /* source, dest coords --mapping--> min, max coords */
1847
1848
1.90k
    MagickBooleanType
1849
1.90k
      fix_bounds = MagickTrue;   /* enlarge bounds for VP handling */
1850
1851
1.90k
    s.x=s.y=min.x=max.x=min.y=max.y=0.0;   /* keep compiler happy */
1852
1853
/* defines to figure out the bounds of the distorted image */
1854
1.90k
#define InitalBounds(p) \
1855
1.90k
{ \
1856
  /* printf("%lg,%lg -> %lg,%lg\n", s.x,s.y, d.x,d.y); */ \
1857
1.90k
  min.x = max.x = p.x; \
1858
1.90k
  min.y = max.y = p.y; \
1859
1.90k
}
1860
5.72k
#define ExpandBounds(p) \
1861
5.72k
{ \
1862
  /* printf("%lg,%lg -> %lg,%lg\n", s.x,s.y, d.x,d.y); */ \
1863
5.72k
  min.x = MagickMin(min.x,p.x); \
1864
5.72k
  max.x = MagickMax(max.x,p.x); \
1865
5.72k
  min.y = MagickMin(min.y,p.y); \
1866
5.72k
  max.y = MagickMax(max.y,p.y); \
1867
5.72k
}
1868
1869
1.90k
    switch (method)
1870
1.90k
    {
1871
1.90k
      case AffineDistortion:
1872
1.90k
      case RigidAffineDistortion:
1873
1.90k
      { double inverse[6];
1874
1.90k
        InvertAffineCoefficients(coeff, inverse);
1875
1.90k
        s.x = (double) image->page.x;
1876
1.90k
        s.y = (double) image->page.y;
1877
1.90k
        d.x = inverse[0]*s.x+inverse[1]*s.y+inverse[2];
1878
1.90k
        d.y = inverse[3]*s.x+inverse[4]*s.y+inverse[5];
1879
1.90k
        InitalBounds(d);
1880
1.90k
        s.x = (double) image->page.x+image->columns;
1881
1.90k
        s.y = (double) image->page.y;
1882
1.90k
        d.x = inverse[0]*s.x+inverse[1]*s.y+inverse[2];
1883
1.90k
        d.y = inverse[3]*s.x+inverse[4]*s.y+inverse[5];
1884
1.90k
        ExpandBounds(d);
1885
1.90k
        s.x = (double) image->page.x;
1886
1.90k
        s.y = (double) image->page.y+image->rows;
1887
1.90k
        d.x = inverse[0]*s.x+inverse[1]*s.y+inverse[2];
1888
1.90k
        d.y = inverse[3]*s.x+inverse[4]*s.y+inverse[5];
1889
1.90k
        ExpandBounds(d);
1890
1.90k
        s.x = (double) image->page.x+image->columns;
1891
1.90k
        s.y = (double) image->page.y+image->rows;
1892
1.90k
        d.x = inverse[0]*s.x+inverse[1]*s.y+inverse[2];
1893
1.90k
        d.y = inverse[3]*s.x+inverse[4]*s.y+inverse[5];
1894
1.90k
        ExpandBounds(d);
1895
1.90k
        break;
1896
1.90k
      }
1897
0
      case PerspectiveDistortion:
1898
0
      { double inverse[8], scale;
1899
0
        InvertPerspectiveCoefficients(coeff, inverse);
1900
0
        s.x = (double) image->page.x;
1901
0
        s.y = (double) image->page.y;
1902
0
        scale=inverse[6]*s.x+inverse[7]*s.y+1.0;
1903
0
        scale=MagickSafeReciprocal(scale);
1904
0
        d.x = scale*(inverse[0]*s.x+inverse[1]*s.y+inverse[2]);
1905
0
        d.y = scale*(inverse[3]*s.x+inverse[4]*s.y+inverse[5]);
1906
0
        InitalBounds(d);
1907
0
        s.x = (double) image->page.x+image->columns;
1908
0
        s.y = (double) image->page.y;
1909
0
        scale=inverse[6]*s.x+inverse[7]*s.y+1.0;
1910
0
        scale=MagickSafeReciprocal(scale);
1911
0
        d.x = scale*(inverse[0]*s.x+inverse[1]*s.y+inverse[2]);
1912
0
        d.y = scale*(inverse[3]*s.x+inverse[4]*s.y+inverse[5]);
1913
0
        ExpandBounds(d);
1914
0
        s.x = (double) image->page.x;
1915
0
        s.y = (double) image->page.y+image->rows;
1916
0
        scale=inverse[6]*s.x+inverse[7]*s.y+1.0;
1917
0
        scale=MagickSafeReciprocal(scale);
1918
0
        d.x = scale*(inverse[0]*s.x+inverse[1]*s.y+inverse[2]);
1919
0
        d.y = scale*(inverse[3]*s.x+inverse[4]*s.y+inverse[5]);
1920
0
        ExpandBounds(d);
1921
0
        s.x = (double) image->page.x+image->columns;
1922
0
        s.y = (double) image->page.y+image->rows;
1923
0
        scale=inverse[6]*s.x+inverse[7]*s.y+1.0;
1924
0
        scale=MagickSafeReciprocal(scale);
1925
0
        d.x = scale*(inverse[0]*s.x+inverse[1]*s.y+inverse[2]);
1926
0
        d.y = scale*(inverse[3]*s.x+inverse[4]*s.y+inverse[5]);
1927
0
        ExpandBounds(d);
1928
0
        break;
1929
1.90k
      }
1930
0
      case ArcDistortion:
1931
0
      { double a, ca, sa;
1932
        /* Forward Map Corners */
1933
0
        a = coeff[0]-coeff[1]/2; ca = cos(a); sa = sin(a);
1934
0
        d.x = coeff[2]*ca;
1935
0
        d.y = coeff[2]*sa;
1936
0
        InitalBounds(d);
1937
0
        d.x = (coeff[2]-coeff[3])*ca;
1938
0
        d.y = (coeff[2]-coeff[3])*sa;
1939
0
        ExpandBounds(d);
1940
0
        a = coeff[0]+coeff[1]/2; ca = cos(a); sa = sin(a);
1941
0
        d.x = coeff[2]*ca;
1942
0
        d.y = coeff[2]*sa;
1943
0
        ExpandBounds(d);
1944
0
        d.x = (coeff[2]-coeff[3])*ca;
1945
0
        d.y = (coeff[2]-coeff[3])*sa;
1946
0
        ExpandBounds(d);
1947
        /* Orthogonal points along top of arc */
1948
0
        for( a=(double) (ceil((double) ((coeff[0]-coeff[1]/2.0)/MagickPI2))*MagickPI2);
1949
0
               a<(coeff[0]+coeff[1]/2.0); a+=MagickPI2 ) {
1950
0
          ca = cos(a); sa = sin(a);
1951
0
          d.x = coeff[2]*ca;
1952
0
          d.y = coeff[2]*sa;
1953
0
          ExpandBounds(d);
1954
0
        }
1955
        /*
1956
          Convert the angle_to_width and radius_to_height
1957
          to appropriate scaling factors, to allow faster processing
1958
          in the mapping function.
1959
        */
1960
0
        coeff[1] = (double) (Magick2PI*image->columns/coeff[1]);
1961
0
        coeff[3] = (double)image->rows/coeff[3];
1962
0
        break;
1963
1.90k
      }
1964
0
      case PolarDistortion:
1965
0
      {
1966
0
        if (number_arguments < 2)
1967
0
          coeff[2] = coeff[3] = 0.0;
1968
0
        min.x = coeff[2]-coeff[0];
1969
0
        max.x = coeff[2]+coeff[0];
1970
0
        min.y = coeff[3]-coeff[0];
1971
0
        max.y = coeff[3]+coeff[0];
1972
        /* should be about 1.0 if Rmin = 0 */
1973
0
        coeff[7]=(double) geometry.height/(coeff[0]-coeff[1]);
1974
0
        break;
1975
1.90k
      }
1976
0
      case DePolarDistortion:
1977
0
      {
1978
        /* direct calculation as it needs to tile correctly
1979
         * for reversibility in a DePolar-Polar cycle */
1980
0
        fix_bounds = MagickFalse;
1981
0
        geometry.x = geometry.y = 0;
1982
0
        geometry.height = CastDoubleToSizeT(ceil(coeff[0]-coeff[1]));
1983
0
        geometry.width = CastDoubleToSizeT(ceil((coeff[0]-coeff[1])*
1984
0
          (coeff[5]-coeff[4])*0.5));
1985
        /* correct scaling factors relative to new size */
1986
0
        coeff[6]=(coeff[5]-coeff[4]) * MagickSafeReciprocal(
1987
0
          (double) geometry.width); /* changed width */
1988
0
        coeff[7]=(coeff[0]-coeff[1]) * MagickSafeReciprocal(
1989
0
          (double) geometry.height); /* should be about 1.0 */
1990
0
        break;
1991
1.90k
      }
1992
0
      case Cylinder2PlaneDistortion:
1993
0
      {
1994
        /* direct calculation so center of distortion is either a pixel
1995
         * center, or pixel edge. This allows for reversibility of the
1996
         * distortion */
1997
0
        geometry.x = geometry.y = 0;
1998
0
        geometry.width = CastDoubleToSizeT(ceil( 2.0*coeff[1]*tan(coeff[0]/2.0) ));
1999
0
        geometry.height = CastDoubleToSizeT(ceil( 2.0*coeff[3]/cos(coeff[0]/2.0) ));
2000
        /* correct center of distortion relative to new size */
2001
0
        coeff[4] = (double) geometry.width/2.0;
2002
0
        coeff[5] = (double) geometry.height/2.0;
2003
0
        fix_bounds = MagickFalse;
2004
0
        break;
2005
1.90k
      }
2006
0
      case Plane2CylinderDistortion:
2007
0
      {
2008
        /* direct calculation center is either pixel center, or pixel edge
2009
         * so as to allow reversibility of the image distortion */
2010
0
        geometry.x = geometry.y = 0;
2011
0
        geometry.width = CastDoubleToSizeT(ceil(coeff[0]*coeff[1]));  /* FOV * radius */
2012
0
        geometry.height = CastDoubleToSizeT(2.0*coeff[3]);              /* input image height */
2013
        /* correct center of distortion relative to new size */
2014
0
        coeff[4] = (double) geometry.width/2.0;
2015
0
        coeff[5] = (double) geometry.height/2.0;
2016
0
        fix_bounds = MagickFalse;
2017
0
        break;
2018
1.90k
      }
2019
0
      case ShepardsDistortion:
2020
0
      case BilinearForwardDistortion:
2021
0
      case BilinearReverseDistortion:
2022
#if 0
2023
      case QuadrilateralDistortion:
2024
#endif
2025
0
      case PolynomialDistortion:
2026
0
      case BarrelDistortion:
2027
0
      case BarrelInverseDistortion:
2028
0
      default:
2029
        /* no calculated bestfit available for these distortions */
2030
0
        bestfit = MagickFalse;
2031
0
        fix_bounds = MagickFalse;
2032
0
        break;
2033
1.90k
    }
2034
2035
    /* Set the output image geometry to calculated 'bestfit'.
2036
       Yes this tends to 'over do' the file image size, ON PURPOSE!
2037
       Do not do this for DePolar which needs to be exact for virtual tiling.
2038
    */
2039
1.90k
    if ( fix_bounds ) {
2040
1.90k
      geometry.x = CastDoubleToSsizeT(floor(min.x-0.5));
2041
1.90k
      geometry.y = CastDoubleToSsizeT(floor(min.y-0.5));
2042
1.90k
      geometry.width=CastDoubleToSizeT(ceil(max.x-geometry.x+0.5));
2043
1.90k
      geometry.height=CastDoubleToSizeT(ceil(max.y-geometry.y+0.5));
2044
1.90k
    }
2045
2046
1.90k
  }  /* end bestfit destination image calculations */
2047
2048
  /* The user provided a 'viewport' expert option which may
2049
     overrides some parts of the current output image geometry.
2050
     This also overrides its default 'bestfit' setting.
2051
  */
2052
1.90k
  { const char *artifact=GetImageArtifact(image,"distort:viewport");
2053
1.90k
    viewport_given = MagickFalse;
2054
1.90k
    if ( artifact != (const char *) NULL ) {
2055
0
      MagickStatusType flags=ParseAbsoluteGeometry(artifact,&geometry);
2056
0
      if (flags==NoValue)
2057
0
        (void) ThrowMagickException(exception,GetMagickModule(),
2058
0
             OptionWarning,"InvalidSetting","'%s' '%s'",
2059
0
             "distort:viewport",artifact);
2060
0
      else
2061
0
        viewport_given = MagickTrue;
2062
0
    }
2063
1.90k
  }
2064
2065
  /* Verbose output */
2066
1.90k
  if (IsStringTrue(GetImageArtifact(image,"verbose")) != MagickFalse) {
2067
0
    ssize_t
2068
0
       i;
2069
0
    char image_gen[MagickPathExtent];
2070
0
    const char *lookup;
2071
2072
    /* Set destination image size and virtual offset */
2073
0
    if ( bestfit || viewport_given ) {
2074
0
      (void) FormatLocaleString(image_gen,MagickPathExtent,
2075
0
        "  -size %.17gx%.17g -page %+.20g%+.20g xc: +insert \\\n",
2076
0
        (double) geometry.width,(double) geometry.height,(double) geometry.x,
2077
0
        (double) geometry.y);
2078
0
      lookup="v.p{xx-v.page.x-0.5,yy-v.page.y-0.5}";
2079
0
    }
2080
0
    else {
2081
0
      image_gen[0] = '\0';             /* no destination to generate */
2082
0
      lookup = "p{xx-page.x-0.5,yy-page.y-0.5}"; /* simplify lookup */
2083
0
    }
2084
2085
0
    switch (method)
2086
0
    {
2087
0
      case AffineDistortion:
2088
0
      case RigidAffineDistortion:
2089
0
      {
2090
0
        double
2091
0
          *inverse;
2092
2093
0
        inverse=(double *) AcquireQuantumMemory(6,sizeof(*inverse));
2094
0
        if (inverse == (double *) NULL)
2095
0
          {
2096
0
            coeff=(double *) RelinquishMagickMemory(coeff);
2097
0
            (void) ThrowMagickException(exception,GetMagickModule(),
2098
0
              ResourceLimitError,"MemoryAllocationFailed","%s","DistortImages");
2099
0
            return((Image *) NULL);
2100
0
          }
2101
0
        InvertAffineCoefficients(coeff, inverse);
2102
0
        CoefficientsToAffineArgs(inverse);
2103
0
        (void) FormatLocaleFile(stderr, "Affine projection:\n");
2104
0
        (void) FormatLocaleFile(stderr,
2105
0
          "  -distort AffineProjection \\\n    '");
2106
0
        for (i=0; i < 5; i++)
2107
0
          (void) FormatLocaleFile(stderr, "%.*g,",GetMagickPrecision(),
2108
0
            inverse[i]);
2109
0
        (void) FormatLocaleFile(stderr, "%.*g'\n",GetMagickPrecision(),
2110
0
          inverse[5]);
2111
0
        (void) FormatLocaleFile(stderr,
2112
0
          "Equivalent scale, rotation(deg), translation:\n");
2113
0
        (void) FormatLocaleFile(stderr,"  %.*g,%.*g,%.*g,%.*g\n",
2114
0
          GetMagickPrecision(),sqrt(inverse[0]*inverse[0]+
2115
0
          inverse[1]*inverse[1]),GetMagickPrecision(),
2116
0
          RadiansToDegrees(atan2(inverse[1],inverse[0])),
2117
0
          GetMagickPrecision(),inverse[4],GetMagickPrecision(),inverse[5]);
2118
0
        inverse=(double *) RelinquishMagickMemory(inverse);
2119
0
        (void) FormatLocaleFile(stderr,"Affine distort, FX equivalent:\n");
2120
0
        (void) FormatLocaleFile(stderr, "%s", image_gen);
2121
0
        (void) FormatLocaleFile(stderr,
2122
0
          "  -fx 'ii=i+page.x+0.5; jj=j+page.y+0.5;\n");
2123
0
        (void) FormatLocaleFile(stderr,"       xx=%+.*g*ii %+.*g*jj %+.*g;\n",
2124
0
          GetMagickPrecision(),coeff[0],GetMagickPrecision(),coeff[1],
2125
0
          GetMagickPrecision(),coeff[2]);
2126
0
        (void) FormatLocaleFile(stderr,"       yy=%+.*g*ii %+.*g*jj %+.*g;\n",
2127
0
          GetMagickPrecision(),coeff[3],GetMagickPrecision(),coeff[4],
2128
0
          GetMagickPrecision(),coeff[5]);
2129
0
        (void) FormatLocaleFile(stderr,"       %s' \\\n",lookup);
2130
0
        break;
2131
0
      }
2132
0
      case PerspectiveDistortion:
2133
0
      {
2134
0
        double
2135
0
          *inverse;
2136
2137
0
        inverse=(double *) AcquireQuantumMemory(8,sizeof(*inverse));
2138
0
        if (inverse == (double *) NULL)
2139
0
          {
2140
0
            coeff=(double *) RelinquishMagickMemory(coeff);
2141
0
            (void) ThrowMagickException(exception,GetMagickModule(),
2142
0
              ResourceLimitError,"MemoryAllocationFailed","%s",
2143
0
              "DistortCoefficients");
2144
0
            return((Image *) NULL);
2145
0
          }
2146
0
        InvertPerspectiveCoefficients(coeff, inverse);
2147
0
        (void) FormatLocaleFile(stderr,"Perspective Projection:\n");
2148
0
        (void) FormatLocaleFile(stderr,
2149
0
          "  -distort PerspectiveProjection \\\n      '");
2150
0
        for (i=0; i < 4; i++)
2151
0
          (void) FormatLocaleFile(stderr, "%.*g, ",GetMagickPrecision(),
2152
0
            inverse[i]);
2153
0
        (void) FormatLocaleFile(stderr, "\n       ");
2154
0
        for ( ; i < 7; i++)
2155
0
          (void) FormatLocaleFile(stderr, "%.*g, ",GetMagickPrecision(),
2156
0
            inverse[i]);
2157
0
        (void) FormatLocaleFile(stderr, "%.*g'\n",GetMagickPrecision(),
2158
0
          inverse[7]);
2159
0
        inverse=(double *) RelinquishMagickMemory(inverse);
2160
0
        (void) FormatLocaleFile(stderr,"Perspective Distort, FX Equivalent:\n");
2161
0
        (void) FormatLocaleFile(stderr,"%.1024s",image_gen);
2162
0
        (void) FormatLocaleFile(stderr,
2163
0
          "  -fx 'ii=i+page.x+0.5; jj=j+page.y+0.5;\n");
2164
0
        (void) FormatLocaleFile(stderr,"       rr=%+.*g*ii %+.*g*jj + 1;\n",
2165
0
          GetMagickPrecision(),coeff[6],GetMagickPrecision(),coeff[7]);
2166
0
        (void) FormatLocaleFile(stderr,
2167
0
          "       xx=(%+.*g*ii %+.*g*jj %+.*g)/rr;\n",
2168
0
          GetMagickPrecision(),coeff[0],GetMagickPrecision(),coeff[1],
2169
0
          GetMagickPrecision(),coeff[2]);
2170
0
        (void) FormatLocaleFile(stderr,
2171
0
          "       yy=(%+.*g*ii %+.*g*jj %+.*g)/rr;\n",
2172
0
          GetMagickPrecision(),coeff[3],GetMagickPrecision(),coeff[4],
2173
0
          GetMagickPrecision(),coeff[5]);
2174
0
        (void) FormatLocaleFile(stderr,"       rr%s0 ? %s : blue' \\\n",
2175
0
          coeff[8] < 0.0 ? "<" : ">", lookup);
2176
0
        break;
2177
0
      }
2178
0
      case BilinearForwardDistortion:
2179
0
      {
2180
0
        (void) FormatLocaleFile(stderr,"BilinearForward Mapping Equations:\n");
2181
0
        (void) FormatLocaleFile(stderr,"%s", image_gen);
2182
0
        (void) FormatLocaleFile(stderr,"    i = %+lf*x %+lf*y %+lf*x*y %+lf;\n",
2183
0
          coeff[0],coeff[1],coeff[2],coeff[3]);
2184
0
        (void) FormatLocaleFile(stderr,"    j = %+lf*x %+lf*y %+lf*x*y %+lf;\n",
2185
0
          coeff[4],coeff[5],coeff[6],coeff[7]);
2186
#if 0
2187
        /* for debugging */
2188
        (void) FormatLocaleFile(stderr, "   c8 = %+lf  c9 = 2*a = %+lf;\n",
2189
            coeff[8], coeff[9]);
2190
#endif
2191
0
        (void) FormatLocaleFile(stderr,
2192
0
          "BilinearForward Distort, FX Equivalent:\n");
2193
0
        (void) FormatLocaleFile(stderr,"%s", image_gen);
2194
0
        (void) FormatLocaleFile(stderr,
2195
0
          "  -fx 'ii=i+page.x%+lf; jj=j+page.y%+lf;\n",0.5-coeff[3],0.5-
2196
0
          coeff[7]);
2197
0
        (void) FormatLocaleFile(stderr,"       bb=%lf*ii %+lf*jj %+lf;\n",
2198
0
          coeff[6], -coeff[2], coeff[8]);
2199
        /* Handle Special degenerate (non-quadratic) or trapezoidal case */
2200
0
        if (coeff[9] != 0)
2201
0
          {
2202
0
            (void) FormatLocaleFile(stderr,
2203
0
              "       rt=bb*bb %+lf*(%lf*ii%+lf*jj);\n",-2*coeff[9],coeff[4],
2204
0
              -coeff[0]);
2205
0
          (void) FormatLocaleFile(stderr,
2206
0
            "       yy=( -bb + sqrt(rt) ) / %lf;\n",coeff[9]);
2207
0
          }
2208
0
        else
2209
0
          (void) FormatLocaleFile(stderr,"       yy=(%lf*ii%+lf*jj)/bb;\n",
2210
0
            -coeff[4],coeff[0]);
2211
0
        (void) FormatLocaleFile(stderr,
2212
0
          "       xx=(ii %+lf*yy)/(%lf %+lf*yy);\n",-coeff[1],coeff[0],
2213
0
          coeff[2]);
2214
0
        if ( coeff[9] != 0 )
2215
0
          (void) FormatLocaleFile(stderr,"       (rt < 0 ) ? red : %s'\n",
2216
0
            lookup);
2217
0
        else
2218
0
          (void) FormatLocaleFile(stderr,"       %s' \\\n", lookup);
2219
0
        break;
2220
0
      }
2221
0
      case BilinearReverseDistortion:
2222
0
      {
2223
#if 0
2224
        (void) FormatLocaleFile(stderr, "Polynomial Projection Distort:\n");
2225
        (void) FormatLocaleFile(stderr, "  -distort PolynomialProjection \\\n");
2226
        (void) FormatLocaleFile(stderr, "      '1.5, %lf, %lf, %lf, %lf,\n",
2227
            coeff[3], coeff[0], coeff[1], coeff[2]);
2228
        (void) FormatLocaleFile(stderr, "            %lf, %lf, %lf, %lf'\n",
2229
            coeff[7], coeff[4], coeff[5], coeff[6]);
2230
#endif
2231
0
        (void) FormatLocaleFile(stderr,
2232
0
          "BilinearReverse Distort, FX Equivalent:\n");
2233
0
        (void) FormatLocaleFile(stderr,"%s", image_gen);
2234
0
        (void) FormatLocaleFile(stderr,
2235
0
          "  -fx 'ii=i+page.x+0.5; jj=j+page.y+0.5;\n");
2236
0
        (void) FormatLocaleFile(stderr,
2237
0
          "       xx=%+lf*ii %+lf*jj %+lf*ii*jj %+lf;\n",coeff[0],coeff[1],
2238
0
          coeff[2], coeff[3]);
2239
0
        (void) FormatLocaleFile(stderr,
2240
0
           "       yy=%+lf*ii %+lf*jj %+lf*ii*jj %+lf;\n",coeff[4],coeff[5],
2241
0
           coeff[6], coeff[7]);
2242
0
        (void) FormatLocaleFile(stderr,"       %s' \\\n", lookup);
2243
0
        break;
2244
0
      }
2245
0
      case PolynomialDistortion:
2246
0
      {
2247
0
        size_t nterms =  CastDoubleToSizeT(coeff[1]);
2248
0
        (void) FormatLocaleFile(stderr,
2249
0
          "Polynomial (order %lg, terms %lu), FX Equivalent\n",coeff[0],
2250
0
          (unsigned long) nterms);
2251
0
        (void) FormatLocaleFile(stderr,"%s", image_gen);
2252
0
        (void) FormatLocaleFile(stderr,
2253
0
          "  -fx 'ii=i+page.x+0.5; jj=j+page.y+0.5;\n");
2254
0
        (void) FormatLocaleFile(stderr, "       xx =");
2255
0
        for (i=0; i < (ssize_t) nterms; i++)
2256
0
        {
2257
0
          if ((i != 0) && (i%4 == 0))
2258
0
            (void) FormatLocaleFile(stderr, "\n         ");
2259
0
          (void) FormatLocaleFile(stderr," %+lf%s",coeff[2+i],
2260
0
            poly_basis_str(i));
2261
0
        }
2262
0
        (void) FormatLocaleFile(stderr,";\n       yy =");
2263
0
        for (i=0; i < (ssize_t) nterms; i++)
2264
0
        {
2265
0
          if ((i != 0) && (i%4 == 0))
2266
0
            (void) FormatLocaleFile(stderr,"\n         ");
2267
0
          (void) FormatLocaleFile(stderr," %+lf%s",coeff[2+i+(int) nterms],
2268
0
            poly_basis_str(i));
2269
0
        }
2270
0
        (void) FormatLocaleFile(stderr,";\n       %s' \\\n", lookup);
2271
0
        break;
2272
0
      }
2273
0
      case ArcDistortion:
2274
0
      {
2275
0
        (void) FormatLocaleFile(stderr,"Arc Distort, Internal Coefficients:\n");
2276
0
        for (i=0; i < 5; i++)
2277
0
          (void) FormatLocaleFile(stderr,
2278
0
            "  c%.17g = %+lf\n",(double) i,coeff[i]);
2279
0
        (void) FormatLocaleFile(stderr,"Arc Distort, FX Equivalent:\n");
2280
0
        (void) FormatLocaleFile(stderr,"%s", image_gen);
2281
0
        (void) FormatLocaleFile(stderr,"  -fx 'ii=i+page.x; jj=j+page.y;\n");
2282
0
        (void) FormatLocaleFile(stderr,"       xx=(atan2(jj,ii)%+lf)/(2*pi);\n",
2283
0
          -coeff[0]);
2284
0
        (void) FormatLocaleFile(stderr,"       xx=xx-round(xx);\n");
2285
0
        (void) FormatLocaleFile(stderr,"       xx=xx*%lf %+lf;\n",coeff[1],
2286
0
          coeff[4]);
2287
0
        (void) FormatLocaleFile(stderr,
2288
0
          "       yy=(%lf - hypot(ii,jj)) * %lf;\n",coeff[2],coeff[3]);
2289
0
        (void) FormatLocaleFile(stderr,"       v.p{xx-.5,yy-.5}' \\\n");
2290
0
        break;
2291
0
      }
2292
0
      case PolarDistortion:
2293
0
      {
2294
0
        (void) FormatLocaleFile(stderr,"Polar Distort, Internal Coefficients\n");
2295
0
        for (i=0; i < 8; i++)
2296
0
          (void) FormatLocaleFile(stderr,"  c%.17g = %+lf\n",(double) i,
2297
0
            coeff[i]);
2298
0
        (void) FormatLocaleFile(stderr,"Polar Distort, FX Equivalent:\n");
2299
0
        (void) FormatLocaleFile(stderr,"%s", image_gen);
2300
0
        (void) FormatLocaleFile(stderr,
2301
0
          "  -fx 'ii=i+page.x%+lf; jj=j+page.y%+lf;\n",-coeff[2],-coeff[3]);
2302
0
        (void) FormatLocaleFile(stderr,"       xx=(atan2(ii,jj)%+lf)/(2*pi);\n",
2303
0
          -(coeff[4]+coeff[5])/2 );
2304
0
        (void) FormatLocaleFile(stderr,"       xx=xx-round(xx);\n");
2305
0
        (void) FormatLocaleFile(stderr,"       xx=xx*2*pi*%lf + v.w/2;\n",
2306
0
          coeff[6] );
2307
0
        (void) FormatLocaleFile(stderr,"       yy=(hypot(ii,jj)%+lf)*%lf;\n",
2308
0
          -coeff[1],coeff[7] );
2309
0
        (void) FormatLocaleFile(stderr,"       v.p{xx-.5,yy-.5}' \\\n");
2310
0
        break;
2311
0
      }
2312
0
      case DePolarDistortion:
2313
0
      {
2314
0
        (void) FormatLocaleFile(stderr,
2315
0
          "DePolar Distort, Internal Coefficients\n");
2316
0
        for (i=0; i < 8; i++)
2317
0
          (void) FormatLocaleFile(stderr,"  c%.17g = %+lf\n",(double) i,
2318
0
            coeff[i]);
2319
0
        (void) FormatLocaleFile(stderr,"DePolar Distort, FX Equivalent:\n");
2320
0
        (void) FormatLocaleFile(stderr,"%s", image_gen);
2321
0
        (void) FormatLocaleFile(stderr,"  -fx 'aa=(i+.5)*%lf %+lf;\n",
2322
0
          coeff[6],+coeff[4]);
2323
0
        (void) FormatLocaleFile(stderr,"       rr=(j+.5)*%lf %+lf;\n",
2324
0
          coeff[7],+coeff[1]);
2325
0
        (void) FormatLocaleFile(stderr,"       xx=rr*sin(aa) %+lf;\n",
2326
0
          coeff[2]);
2327
0
        (void) FormatLocaleFile(stderr,"       yy=rr*cos(aa) %+lf;\n",
2328
0
          coeff[3]);
2329
0
        (void) FormatLocaleFile(stderr,"       v.p{xx-.5,yy-.5}' \\\n");
2330
0
        break;
2331
0
      }
2332
0
      case Cylinder2PlaneDistortion:
2333
0
      {
2334
0
        (void) FormatLocaleFile(stderr,
2335
0
          "Cylinder to Plane Distort, Internal Coefficients\n");
2336
0
        (void) FormatLocaleFile(stderr,"  cylinder_radius = %+lf\n",coeff[1]);
2337
0
        (void) FormatLocaleFile(stderr,
2338
0
          "Cylinder to Plane Distort, FX Equivalent:\n");
2339
0
        (void) FormatLocaleFile(stderr, "%s", image_gen);
2340
0
        (void) FormatLocaleFile(stderr,
2341
0
          "  -fx 'ii=i+page.x%+lf+0.5; jj=j+page.y%+lf+0.5;\n",-coeff[4],
2342
0
          -coeff[5]);
2343
0
        (void) FormatLocaleFile(stderr,"       aa=atan(ii/%+lf);\n",coeff[1]);
2344
0
        (void) FormatLocaleFile(stderr,"       xx=%lf*aa%+lf;\n",
2345
0
          coeff[1],coeff[2]);
2346
0
        (void) FormatLocaleFile(stderr,"       yy=jj*cos(aa)%+lf;\n",coeff[3]);
2347
0
        (void) FormatLocaleFile(stderr,"       %s' \\\n", lookup);
2348
0
        break;
2349
0
      }
2350
0
      case Plane2CylinderDistortion:
2351
0
      {
2352
0
        (void) FormatLocaleFile(stderr,
2353
0
          "Plane to Cylinder Distort, Internal Coefficients\n");
2354
0
        (void) FormatLocaleFile(stderr,"  cylinder_radius = %+lf\n",coeff[1]);
2355
0
        (void) FormatLocaleFile(stderr,
2356
0
          "Plane to Cylinder Distort, FX Equivalent:\n");
2357
0
        (void) FormatLocaleFile(stderr,"%s", image_gen);
2358
0
        (void) FormatLocaleFile(stderr,
2359
0
          "  -fx 'ii=i+page.x%+lf+0.5; jj=j+page.y%+lf+0.5;\n",-coeff[4],
2360
0
          -coeff[5]);
2361
0
        (void) FormatLocaleFile(stderr,"       ii=ii/%+lf;\n",coeff[1]);
2362
0
        (void) FormatLocaleFile(stderr,"       xx=%lf*tan(ii)%+lf;\n",coeff[1],
2363
0
          coeff[2] );
2364
0
        (void) FormatLocaleFile(stderr,"       yy=jj/cos(ii)%+lf;\n",coeff[3]);
2365
0
        (void) FormatLocaleFile(stderr,"       %s' \\\n", lookup);
2366
0
        break;
2367
0
      }
2368
0
      case BarrelDistortion:
2369
0
      case BarrelInverseDistortion:
2370
0
      {
2371
0
        double
2372
0
          xc,
2373
0
          yc;
2374
2375
        /*
2376
          NOTE: This does the barrel roll in pixel coords not image coords
2377
          The internal distortion must do it in image coordinates,
2378
          so that is what the center coeff (8,9) is given in.
2379
        */
2380
0
        xc=((double)image->columns-1.0)/2.0+image->page.x;
2381
0
        yc=((double)image->rows-1.0)/2.0+image->page.y;
2382
0
        (void) FormatLocaleFile(stderr, "Barrel%s Distort, FX Equivalent:\n",
2383
0
          method == BarrelDistortion ? "" : "Inv");
2384
0
        (void) FormatLocaleFile(stderr, "%s", image_gen);
2385
0
        if ( fabs(coeff[8]-xc-0.5) < 0.1 && fabs(coeff[9]-yc-0.5) < 0.1 )
2386
0
          (void) FormatLocaleFile(stderr,"  -fx 'xc=(w-1)/2;  yc=(h-1)/2;\n");
2387
0
        else
2388
0
          (void) FormatLocaleFile(stderr,"  -fx 'xc=%lf;  yc=%lf;\n",coeff[8]-
2389
0
            0.5,coeff[9]-0.5);
2390
0
        (void) FormatLocaleFile(stderr,
2391
0
          "       ii=i-xc;  jj=j-yc;  rr=hypot(ii,jj);\n");
2392
0
        (void) FormatLocaleFile(stderr,
2393
0
          "       ii=ii%s(%lf*rr*rr*rr %+lf*rr*rr %+lf*rr %+lf);\n",
2394
0
          method == BarrelDistortion ? "*" : "/",coeff[0],coeff[1],coeff[2],
2395
0
          coeff[3]);
2396
0
        (void) FormatLocaleFile(stderr,
2397
0
          "       jj=jj%s(%lf*rr*rr*rr %+lf*rr*rr %+lf*rr %+lf);\n",
2398
0
          method == BarrelDistortion ? "*" : "/",coeff[4],coeff[5],coeff[6],
2399
0
          coeff[7]);
2400
0
        (void) FormatLocaleFile(stderr,"       p{ii+xc,jj+yc}' \\\n");
2401
0
        break;
2402
0
      }
2403
0
      default:
2404
0
        break;
2405
0
    }
2406
0
  }
2407
  /*
2408
    The user provided a 'scale' expert option will scale the output image size,
2409
    by the factor given allowing for super-sampling of the distorted image
2410
    space.  Any scaling factors must naturally be halved as a result.
2411
  */
2412
1.90k
  { const char *artifact;
2413
1.90k
    artifact=GetImageArtifact(image,"distort:scale");
2414
1.90k
    output_scaling = 1.0;
2415
1.90k
    if (artifact != (const char *) NULL) {
2416
0
      output_scaling = fabs(StringToDouble(artifact,(char **) NULL));
2417
0
      geometry.width=CastDoubleToSizeT(output_scaling*geometry.width+0.5);
2418
0
      geometry.height=CastDoubleToSizeT(output_scaling*geometry.height+0.5);
2419
0
      geometry.x=(ssize_t) (output_scaling*geometry.x+0.5);
2420
0
      geometry.y=(ssize_t) (output_scaling*geometry.y+0.5);
2421
0
      if ( output_scaling < 0.1 ) {
2422
0
        coeff = (double *) RelinquishMagickMemory(coeff);
2423
0
        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
2424
0
                "InvalidArgument","%s", "-set option:distort:scale" );
2425
0
        return((Image *) NULL);
2426
0
      }
2427
0
      output_scaling = 1/output_scaling;
2428
0
    }
2429
1.90k
  }
2430
1.90k
#define ScaleFilter(F,A,B,C,D) \
2431
70.1k
    ScaleResampleFilter( (F), \
2432
70.1k
      output_scaling*(A), output_scaling*(B), \
2433
70.1k
      output_scaling*(C), output_scaling*(D) )
2434
2435
  /*
2436
    Initialize the distort image attributes.
2437
  */
2438
1.90k
  distort_image=CloneImage(image,geometry.width,geometry.height,MagickTrue,
2439
1.90k
    exception);
2440
1.90k
  if (distort_image == (Image *) NULL)
2441
2
    {
2442
2
      coeff=(double *) RelinquishMagickMemory(coeff);
2443
2
      return((Image *) NULL);
2444
2
    }
2445
  /* if image is ColorMapped - change it to DirectClass */
2446
1.90k
  if (SetImageStorageClass(distort_image,DirectClass,exception) == MagickFalse)
2447
0
    {
2448
0
      coeff=(double *) RelinquishMagickMemory(coeff);
2449
0
      distort_image=DestroyImage(distort_image);
2450
0
      return((Image *) NULL);
2451
0
    }
2452
1.90k
  if ((IsPixelInfoGray(&distort_image->background_color) == MagickFalse) &&
2453
0
      (IsGrayColorspace(distort_image->colorspace) != MagickFalse))
2454
0
    (void) SetImageColorspace(distort_image,sRGBColorspace,exception);
2455
1.90k
  if (distort_image->background_color.alpha_trait != UndefinedPixelTrait)
2456
0
    distort_image->alpha_trait=BlendPixelTrait;
2457
1.90k
  distort_image->page.x=geometry.x;
2458
1.90k
  distort_image->page.y=geometry.y;
2459
1.90k
  ConformPixelInfo(distort_image,&distort_image->matte_color,&invalid,
2460
1.90k
    exception);
2461
2462
1.90k
  { /* ----- MAIN CODE -----
2463
       Sample the source image to each pixel in the distort image.
2464
     */
2465
1.90k
    CacheView
2466
1.90k
      *distort_view;
2467
2468
1.90k
    MagickBooleanType
2469
1.90k
      status;
2470
2471
1.90k
    MagickOffsetType
2472
1.90k
      progress;
2473
2474
1.90k
    PixelInfo
2475
1.90k
      zero;
2476
2477
1.90k
    ResampleFilter
2478
1.90k
      **magick_restrict resample_filter;
2479
2480
1.90k
    ssize_t
2481
1.90k
      j;
2482
2483
1.90k
    status=MagickTrue;
2484
1.90k
    progress=0;
2485
1.90k
    GetPixelInfo(distort_image,&zero);
2486
1.90k
    resample_filter=AcquireResampleFilterTLS(image,UndefinedVirtualPixelMethod,
2487
1.90k
      MagickFalse,exception);
2488
1.90k
    distort_view=AcquireAuthenticCacheView(distort_image,exception);
2489
#if defined(MAGICKCORE_OPENMP_SUPPORT)
2490
    #pragma omp parallel for schedule(static) shared(progress,status) \
2491
      magick_number_threads(image,distort_image,distort_image->rows,1)
2492
#endif
2493
72.0k
    for (j=0; j < (ssize_t) distort_image->rows; j++)
2494
70.1k
    {
2495
70.1k
      const int
2496
70.1k
        id = GetOpenMPThreadId();
2497
2498
70.1k
      double
2499
70.1k
        validity;  /* how mathematically valid is this the mapping */
2500
2501
70.1k
      MagickBooleanType
2502
70.1k
        sync;
2503
2504
70.1k
      PixelInfo
2505
70.1k
        pixel;    /* pixel color to assign to distorted image */
2506
2507
70.1k
      PointInfo
2508
70.1k
        d,
2509
70.1k
        s;  /* transform destination image x,y  to source image x,y */
2510
2511
70.1k
      ssize_t
2512
70.1k
        i;
2513
2514
70.1k
      Quantum
2515
70.1k
        *magick_restrict q;
2516
2517
70.1k
      q=QueueCacheViewAuthenticPixels(distort_view,0,j,distort_image->columns,1,
2518
70.1k
        exception);
2519
70.1k
      if (q == (Quantum *) NULL)
2520
0
        {
2521
0
          status=MagickFalse;
2522
0
          continue;
2523
0
        }
2524
70.1k
      pixel=zero;
2525
2526
      /* Define constant scaling vectors for Affine Distortions
2527
        Other methods are either variable, or use interpolated lookup
2528
      */
2529
70.1k
      switch (method)
2530
70.1k
      {
2531
70.1k
        case AffineDistortion:
2532
70.1k
        case RigidAffineDistortion:
2533
70.1k
          ScaleFilter( resample_filter[id],
2534
70.1k
            coeff[0], coeff[1],
2535
70.1k
            coeff[3], coeff[4] );
2536
70.1k
          break;
2537
0
        default:
2538
0
          break;
2539
70.1k
      }
2540
2541
      /* Initialize default pixel validity
2542
      *    negative:         pixel is invalid  output 'matte_color'
2543
      *    0.0 to 1.0:       antialiased, mix with resample output
2544
      *    1.0 or greater:   use resampled output.
2545
      */
2546
70.1k
      validity = 1.0;
2547
2548
18.4M
      for (i=0; i < (ssize_t) distort_image->columns; i++)
2549
18.3M
      {
2550
        /* map pixel coordinate to distortion space coordinate */
2551
18.3M
        d.x = (double) (geometry.x+i+0.5)*output_scaling;
2552
18.3M
        d.y = (double) (geometry.y+j+0.5)*output_scaling;
2553
18.3M
        s = d;  /* default is a no-op mapping */
2554
18.3M
        switch (method)
2555
18.3M
        {
2556
18.3M
          case AffineDistortion:
2557
18.3M
          case RigidAffineDistortion:
2558
18.3M
          {
2559
18.3M
            s.x=coeff[0]*d.x+coeff[1]*d.y+coeff[2];
2560
18.3M
            s.y=coeff[3]*d.x+coeff[4]*d.y+coeff[5];
2561
            /* Affine partial derivatives are constant -- set above */
2562
18.3M
            break;
2563
18.3M
          }
2564
0
          case PerspectiveDistortion:
2565
0
          {
2566
0
            double
2567
0
              p,n,r,abs_r,abs_c6,abs_c7,scale;
2568
            /* perspective is a ratio of affines */
2569
0
            p=coeff[0]*d.x+coeff[1]*d.y+coeff[2];
2570
0
            n=coeff[3]*d.x+coeff[4]*d.y+coeff[5];
2571
0
            r=coeff[6]*d.x+coeff[7]*d.y+1.0;
2572
            /* Pixel Validity -- is it a 'sky' or 'ground' pixel */
2573
0
            validity = (r*coeff[8] < 0.0) ? 0.0 : 1.0;
2574
            /* Determine horizon anti-alias blending */
2575
0
            abs_r = fabs(r)*2;
2576
0
            abs_c6 = fabs(coeff[6]);
2577
0
            abs_c7 = fabs(coeff[7]);
2578
0
            if ( abs_c6 > abs_c7 ) {
2579
0
              if ( abs_r < abs_c6*output_scaling )
2580
0
                validity = 0.5 - coeff[8]*r/(coeff[6]*output_scaling);
2581
0
            }
2582
0
            else if ( abs_r < abs_c7*output_scaling )
2583
0
              validity = 0.5 - coeff[8]*r/(coeff[7]*output_scaling);
2584
            /* Perspective Sampling Point (if valid) */
2585
0
            if ( validity > 0.0 ) {
2586
              /* divide by r affine, for perspective scaling */
2587
0
              scale = 1.0/r;
2588
0
              s.x = p*scale;
2589
0
              s.y = n*scale;
2590
              /* Perspective Partial Derivatives or Scaling Vectors */
2591
0
              scale *= scale;
2592
0
              ScaleFilter( resample_filter[id],
2593
0
                (r*coeff[0] - p*coeff[6])*scale,
2594
0
                (r*coeff[1] - p*coeff[7])*scale,
2595
0
                (r*coeff[3] - n*coeff[6])*scale,
2596
0
                (r*coeff[4] - n*coeff[7])*scale );
2597
0
            }
2598
0
            break;
2599
18.3M
          }
2600
0
          case BilinearReverseDistortion:
2601
0
          {
2602
            /* Reversed Mapped is just a simple polynomial */
2603
0
            s.x=coeff[0]*d.x+coeff[1]*d.y+coeff[2]*d.x*d.y+coeff[3];
2604
0
            s.y=coeff[4]*d.x+coeff[5]*d.y
2605
0
                    +coeff[6]*d.x*d.y+coeff[7];
2606
            /* Bilinear partial derivatives of scaling vectors */
2607
0
            ScaleFilter( resample_filter[id],
2608
0
                coeff[0] + coeff[2]*d.y,
2609
0
                coeff[1] + coeff[2]*d.x,
2610
0
                coeff[4] + coeff[6]*d.y,
2611
0
                coeff[5] + coeff[6]*d.x );
2612
0
            break;
2613
18.3M
          }
2614
0
          case BilinearForwardDistortion:
2615
0
          {
2616
            /* Forward mapped needs reversed polynomial equations
2617
             * which unfortunately requires a square root!  */
2618
0
            double b,c;
2619
0
            d.x -= coeff[3];  d.y -= coeff[7];
2620
0
            b = coeff[6]*d.x - coeff[2]*d.y + coeff[8];
2621
0
            c = coeff[4]*d.x - coeff[0]*d.y;
2622
2623
0
            validity = 1.0;
2624
            /* Handle Special degenerate (non-quadratic) case
2625
             * Currently without horizon anti-aliasing */
2626
0
            if ( fabs(coeff[9]) < MagickEpsilon )
2627
0
              s.y =  -c/b;
2628
0
            else {
2629
0
              c = b*b - 2*coeff[9]*c;
2630
0
              if ( c < 0.0 )
2631
0
                validity = 0.0;
2632
0
              else
2633
0
                s.y = ( -b + sqrt(c) )/coeff[9];
2634
0
            }
2635
0
            if ( validity > 0.0 )
2636
0
              s.x = ( d.x - coeff[1]*s.y) / ( coeff[0] + coeff[2]*s.y );
2637
2638
            /* NOTE: the sign of the square root should be -ve for parts
2639
                     where the source image becomes 'flipped' or 'mirrored'.
2640
               FUTURE: Horizon handling
2641
               FUTURE: Scaling factors or Derivatives (how?)
2642
            */
2643
0
            break;
2644
18.3M
          }
2645
#if 0
2646
          case BilinearDistortion:
2647
            /* Bilinear mapping of any Quadrilateral to any Quadrilateral */
2648
            /* UNDER DEVELOPMENT */
2649
            break;
2650
#endif
2651
0
          case PolynomialDistortion:
2652
0
          {
2653
            /* multi-ordered polynomial */
2654
0
            ssize_t
2655
0
              k;
2656
2657
0
            ssize_t
2658
0
              nterms=(ssize_t)coeff[1];
2659
2660
0
            PointInfo
2661
0
              du,dv; /* the du,dv vectors from unit dx,dy -- derivatives */
2662
2663
0
            s.x=s.y=du.x=du.y=dv.x=dv.y=0.0;
2664
0
            for(k=0; k < nterms; k++) {
2665
0
              s.x  += poly_basis_fn(k,d.x,d.y)*coeff[2+k];
2666
0
              du.x += poly_basis_dx(k,d.x,d.y)*coeff[2+k];
2667
0
              du.y += poly_basis_dy(k,d.x,d.y)*coeff[2+k];
2668
0
              s.y  += poly_basis_fn(k,d.x,d.y)*coeff[2+k+nterms];
2669
0
              dv.x += poly_basis_dx(k,d.x,d.y)*coeff[2+k+nterms];
2670
0
              dv.y += poly_basis_dy(k,d.x,d.y)*coeff[2+k+nterms];
2671
0
            }
2672
0
            ScaleFilter( resample_filter[id], du.x,du.y,dv.x,dv.y );
2673
0
            break;
2674
18.3M
          }
2675
0
          case ArcDistortion:
2676
0
          {
2677
            /* what is the angle and radius in the destination image */
2678
0
            s.x  = (double) ((atan2(d.y,d.x) - coeff[0])/Magick2PI);
2679
0
            s.x -= MagickRound(s.x);     /* angle */
2680
0
            s.y  = hypot(d.x,d.y);       /* radius */
2681
2682
            /* Arc Distortion Partial Scaling Vectors
2683
              Are derived by mapping the perpendicular unit vectors
2684
              dR  and  dA*R*2PI  rather than trying to map dx and dy
2685
              The results is a very simple orthogonal aligned ellipse.
2686
            */
2687
0
            if ( s.y > MagickEpsilon )
2688
0
              ScaleFilter( resample_filter[id],
2689
0
                  (double) (coeff[1]/(Magick2PI*s.y)), 0, 0, coeff[3] );
2690
0
            else
2691
0
              ScaleFilter( resample_filter[id],
2692
0
                  distort_image->columns*2, 0, 0, coeff[3] );
2693
2694
            /* now scale the angle and radius for source image lookup point */
2695
0
            s.x = s.x*coeff[1] + coeff[4] + image->page.x +0.5;
2696
0
            s.y = (coeff[2] - s.y) * coeff[3] + image->page.y;
2697
0
            break;
2698
18.3M
          }
2699
0
          case PolarDistortion:
2700
0
          { /* 2D Cartesian to Polar View */
2701
0
            d.x -= coeff[2];
2702
0
            d.y -= coeff[3];
2703
0
            s.x  = atan2(d.x,d.y) - (coeff[4]+coeff[5])/2;
2704
0
            s.x /= Magick2PI;
2705
0
            s.x -= MagickRound(s.x);
2706
0
            s.x *= Magick2PI;       /* angle - relative to centerline */
2707
0
            s.y  = hypot(d.x,d.y);  /* radius */
2708
2709
            /* Polar Scaling vectors are based on mapping dR and dA vectors
2710
               This results in very simple orthogonal scaling vectors
2711
            */
2712
0
            if ( s.y > MagickEpsilon )
2713
0
              ScaleFilter( resample_filter[id],
2714
0
                (double) (coeff[6]/(Magick2PI*s.y)), 0, 0, coeff[7] );
2715
0
            else
2716
0
              ScaleFilter( resample_filter[id],
2717
0
                  distort_image->columns*2, 0, 0, coeff[7] );
2718
2719
            /* now finish mapping radius/angle to source x,y coords */
2720
0
            s.x = s.x*coeff[6] + (double)image->columns/2.0 + image->page.x;
2721
0
            s.y = (s.y-coeff[1])*coeff[7] + image->page.y;
2722
0
            break;
2723
18.3M
          }
2724
0
          case DePolarDistortion:
2725
0
          { /* @D Polar to Cartesian  */
2726
            /* ignore all destination virtual offsets */
2727
0
            d.x = ((double)i+0.5)*output_scaling*coeff[6]+coeff[4];
2728
0
            d.y = ((double)j+0.5)*output_scaling*coeff[7]+coeff[1];
2729
0
            s.x = d.y*sin(d.x) + coeff[2];
2730
0
            s.y = d.y*cos(d.x) + coeff[3];
2731
            /* derivatives are useless - better to use SuperSampling */
2732
0
            break;
2733
18.3M
          }
2734
0
          case Cylinder2PlaneDistortion:
2735
0
          { /* 3D Cylinder to Tangential Plane */
2736
0
            double ax, cx;
2737
            /* relative to center of distortion */
2738
0
            d.x -= coeff[4]; d.y -= coeff[5];
2739
0
            d.x /= coeff[1];        /* x' = x/r */
2740
0
            ax=atan(d.x);           /* aa = atan(x/r) = u/r  */
2741
0
            cx=cos(ax);             /* cx = cos(atan(x/r)) = 1/sqrt(x^2+u^2) */
2742
0
            s.x = coeff[1]*ax;      /* u  = r*atan(x/r) */
2743
0
            s.y = d.y*cx;           /* v  = y*cos(u/r) */
2744
            /* derivatives... (see personal notes) */
2745
0
            ScaleFilter( resample_filter[id],
2746
0
                  1.0/(1.0+d.x*d.x), 0.0, -d.x*s.y*cx*cx/coeff[1], s.y/d.y );
2747
#if 0
2748
if ( i == 0 && j == 0 ) {
2749
  fprintf(stderr, "x=%lf  y=%lf  u=%lf  v=%lf\n", d.x*coeff[1], d.y, s.x, s.y);
2750
  fprintf(stderr, "phi = %lf\n", (double)(ax * 180.0/MagickPI) );
2751
  fprintf(stderr, "du/dx=%lf  du/dx=%lf  dv/dx=%lf  dv/dy=%lf\n",
2752
                1.0/(1.0+d.x*d.x), 0.0, -d.x*s.y*cx*cx/coeff[1], s.y/d.y );
2753
  fflush(stderr); }
2754
#endif
2755
            /* add center of distortion in source */
2756
0
            s.x += coeff[2]; s.y += coeff[3];
2757
0
            break;
2758
18.3M
          }
2759
0
          case Plane2CylinderDistortion:
2760
0
          { /* 3D Cylinder to Tangential Plane */
2761
            /* relative to center of distortion */
2762
0
            d.x -= coeff[4]; d.y -= coeff[5];
2763
2764
            /* is pixel valid - horizon of a infinite Virtual-Pixel Plane
2765
             * (see Anthony Thyssen's personal note) */
2766
0
            validity = (double) (coeff[1]*MagickPI2 - fabs(d.x))/output_scaling + 0.5;
2767
2768
0
            if ( validity > 0.0 ) {
2769
0
              double cx,tx;
2770
0
              d.x /= coeff[1];           /* x'= x/r */
2771
0
              cx = 1/cos(d.x);           /* cx = 1/cos(x/r) */
2772
0
              tx = tan(d.x);             /* tx = tan(x/r) */
2773
0
              s.x = coeff[1]*tx;         /* u = r * tan(x/r) */
2774
0
              s.y = d.y*cx;              /* v = y / cos(x/r) */
2775
              /* derivatives...  (see Anthony Thyssen's personal notes) */
2776
0
              ScaleFilter( resample_filter[id],
2777
0
                    cx*cx, 0.0, s.y*cx/coeff[1], cx );
2778
#if 0
2779
/*if ( i == 0 && j == 0 )*/
2780
if ( d.x == 0.5 && d.y == 0.5 ) {
2781
  fprintf(stderr, "x=%lf  y=%lf  u=%lf  v=%lf\n", d.x*coeff[1], d.y, s.x, s.y);
2782
  fprintf(stderr, "radius = %lf  phi = %lf  validity = %lf\n",
2783
      coeff[1],  (double)(d.x * 180.0/MagickPI), validity );
2784
  fprintf(stderr, "du/dx=%lf  du/dx=%lf  dv/dx=%lf  dv/dy=%lf\n",
2785
      cx*cx, 0.0, s.y*cx/coeff[1], cx);
2786
  fflush(stderr); }
2787
#endif
2788
0
            }
2789
            /* add center of distortion in source */
2790
0
            s.x += coeff[2]; s.y += coeff[3];
2791
0
            break;
2792
18.3M
          }
2793
0
          case BarrelDistortion:
2794
0
          case BarrelInverseDistortion:
2795
0
          { /* Lens Barrel Distortion Correction */
2796
0
            double r,fx,fy,gx,gy;
2797
            /* Radial Polynomial Distortion (de-normalized) */
2798
0
            d.x -= coeff[8];
2799
0
            d.y -= coeff[9];
2800
0
            r = sqrt(d.x*d.x+d.y*d.y);
2801
0
            if ( r > MagickEpsilon ) {
2802
0
              fx = ((coeff[0]*r + coeff[1])*r + coeff[2])*r + coeff[3];
2803
0
              fy = ((coeff[4]*r + coeff[5])*r + coeff[6])*r + coeff[7];
2804
0
              gx = ((3*coeff[0]*r + 2*coeff[1])*r + coeff[2])/r;
2805
0
              gy = ((3*coeff[4]*r + 2*coeff[5])*r + coeff[6])/r;
2806
              /* adjust functions and scaling for 'inverse' form */
2807
0
              if ( method == BarrelInverseDistortion ) {
2808
0
                fx = 1/fx;  fy = 1/fy;
2809
0
                gx *= -fx*fx;  gy *= -fy*fy;
2810
0
              }
2811
              /* Set the source pixel to lookup and EWA derivative vectors */
2812
0
              s.x = d.x*fx + coeff[8];
2813
0
              s.y = d.y*fy + coeff[9];
2814
0
              ScaleFilter( resample_filter[id],
2815
0
                  gx*d.x*d.x + fx, gx*d.x*d.y,
2816
0
                  gy*d.x*d.y,      gy*d.y*d.y + fy );
2817
0
            }
2818
0
            else {
2819
              /* Special handling to avoid divide by zero when r==0
2820
              **
2821
              ** The source and destination pixels match in this case
2822
              ** which was set at the top of the loop using  s = d;
2823
              ** otherwise...   s.x=coeff[8]; s.y=coeff[9];
2824
              */
2825
0
              if ( method == BarrelDistortion )
2826
0
                ScaleFilter( resample_filter[id],
2827
0
                     coeff[3], 0, 0, coeff[7] );
2828
0
              else /* method == BarrelInverseDistortion */
2829
                /* FUTURE, trap for D==0 causing division by zero */
2830
0
                ScaleFilter( resample_filter[id],
2831
0
                     1.0/coeff[3], 0, 0, 1.0/coeff[7] );
2832
0
            }
2833
0
            break;
2834
0
          }
2835
0
          case ShepardsDistortion:
2836
0
          { /* Shepards Method, or Inverse Weighted Distance for
2837
               displacement around the destination image control points
2838
               The input arguments are the coefficients to the function.
2839
               This is more of a 'displacement' function rather than an
2840
               absolute distortion function.
2841
2842
               Note: We can not determine derivatives using shepards method
2843
               so only a point sample interpolation can be used.
2844
            */
2845
0
            double
2846
0
              denominator;
2847
2848
0
            size_t
2849
0
              k;
2850
2851
0
            denominator = s.x = s.y = 0;
2852
0
            for(k=0; k<number_arguments; k+=4) {
2853
0
              double weight =
2854
0
                  ((double)d.x-arguments[k+2])*((double)d.x-arguments[k+2])
2855
0
                + ((double)d.y-arguments[k+3])*((double)d.y-arguments[k+3]);
2856
0
              weight = pow(weight,coeff[0]); /* shepards power factor */
2857
0
              weight = ( weight < 1.0 ) ? 1.0 : 1.0/weight;
2858
2859
0
              s.x += (arguments[ k ]-arguments[k+2])*weight;
2860
0
              s.y += (arguments[k+1]-arguments[k+3])*weight;
2861
0
              denominator += weight;
2862
0
            }
2863
0
            s.x /= denominator;
2864
0
            s.y /= denominator;
2865
0
            s.x += d.x;   /* make it as relative displacement */
2866
0
            s.y += d.y;
2867
0
            break;
2868
0
          }
2869
0
          default:
2870
0
            break; /* use the default no-op given above */
2871
18.3M
        }
2872
        /* map virtual canvas location back to real image coordinate */
2873
18.3M
        if ( bestfit && method != ArcDistortion ) {
2874
18.3M
          s.x -= image->page.x;
2875
18.3M
          s.y -= image->page.y;
2876
18.3M
        }
2877
18.3M
        s.x -= 0.5;
2878
18.3M
        s.y -= 0.5;
2879
2880
18.3M
        if ( validity <= 0.0 ) {
2881
          /* result of distortion is an invalid pixel - don't resample */
2882
0
          SetPixelViaPixelInfo(distort_image,&invalid,q);
2883
0
        }
2884
18.3M
        else {
2885
          /* resample the source image to find its correct color */
2886
18.3M
          status=ResamplePixelColor(resample_filter[id],s.x,s.y,&pixel,
2887
18.3M
            exception);
2888
18.3M
          if (status == MagickFalse)
2889
0
            SetPixelViaPixelInfo(distort_image,&invalid,q);
2890
18.3M
          else
2891
18.3M
            {
2892
              /* if validity between 0.0 & 1.0 mix result with invalid pixel */
2893
18.3M
              if ( validity < 1.0 ) {
2894
                /* Do a blend of sample color and invalid pixel */
2895
                /* should this be a 'Blend', or an 'Over' compose */
2896
0
                CompositePixelInfoBlend(&pixel,validity,&invalid,(1.0-validity),
2897
0
                  &pixel);
2898
0
              }
2899
18.3M
              SetPixelViaPixelInfo(distort_image,&pixel,q);
2900
18.3M
            }
2901
18.3M
        }
2902
18.3M
        q+=(ptrdiff_t) GetPixelChannels(distort_image);
2903
18.3M
      }
2904
70.1k
      sync=SyncCacheViewAuthenticPixels(distort_view,exception);
2905
70.1k
      if (sync == MagickFalse)
2906
0
        status=MagickFalse;
2907
70.1k
      if (image->progress_monitor != (MagickProgressMonitor) NULL)
2908
0
        {
2909
0
          MagickBooleanType
2910
0
            proceed;
2911
2912
#if defined(MAGICKCORE_OPENMP_SUPPORT)
2913
          #pragma omp atomic
2914
#endif
2915
0
          progress++;
2916
0
          proceed=SetImageProgress(image,DistortImageTag,progress,image->rows);
2917
0
          if (proceed == MagickFalse)
2918
0
            status=MagickFalse;
2919
0
        }
2920
70.1k
    }
2921
1.90k
    distort_view=DestroyCacheView(distort_view);
2922
1.90k
    resample_filter=DestroyResampleFilterTLS(resample_filter);
2923
2924
1.90k
    if (status == MagickFalse)
2925
0
      distort_image=DestroyImage(distort_image);
2926
1.90k
  }
2927
2928
  /* Arc does not return an offset unless 'bestfit' is in effect
2929
     And the user has not provided an overriding 'viewport'.
2930
   */
2931
1.90k
  if ( method == ArcDistortion && !bestfit && !viewport_given ) {
2932
0
    distort_image->page.x = 0;
2933
0
    distort_image->page.y = 0;
2934
0
  }
2935
1.90k
  coeff=(double *) RelinquishMagickMemory(coeff);
2936
1.90k
  return(distort_image);
2937
1.90k
}
2938

2939
/*
2940
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2941
%                                                                             %
2942
%                                                                             %
2943
%                                                                             %
2944
%   R o t a t e I m a g e                                                     %
2945
%                                                                             %
2946
%                                                                             %
2947
%                                                                             %
2948
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2949
%
2950
%  RotateImage() creates a new image that is a rotated copy of an existing
2951
%  one.  Positive angles rotate counter-clockwise (right-hand rule), while
2952
%  negative angles rotate clockwise.  Rotated images are usually larger than
2953
%  the originals and have 'empty' triangular corners.  X axis.  Empty
2954
%  triangles left over from shearing the image are filled with the background
2955
%  color defined by member 'background_color' of the image.  RotateImage
2956
%  allocates the memory necessary for the new Image structure and returns a
2957
%  pointer to the new image.
2958
%
2959
%  The format of the RotateImage method is:
2960
%
2961
%      Image *RotateImage(const Image *image,const double degrees,
2962
%        ExceptionInfo *exception)
2963
%
2964
%  A description of each parameter follows.
2965
%
2966
%    o image: the image.
2967
%
2968
%    o degrees: Specifies the number of degrees to rotate the image.
2969
%
2970
%    o exception: return any errors or warnings in this structure.
2971
%
2972
*/
2973
MagickExport Image *RotateImage(const Image *image,const double degrees,
2974
  ExceptionInfo *exception)
2975
7.67k
{
2976
7.67k
  Image
2977
7.67k
    *distort_image,
2978
7.67k
    *rotate_image;
2979
2980
7.67k
  double
2981
7.67k
    angle;
2982
2983
7.67k
  PointInfo
2984
7.67k
    shear;
2985
2986
7.67k
  size_t
2987
7.67k
    rotations;
2988
2989
  /*
2990
    Adjust rotation angle.
2991
  */
2992
7.67k
  assert(image != (Image *) NULL);
2993
7.67k
  assert(image->signature == MagickCoreSignature);
2994
7.67k
  if (IsEventLogging() != MagickFalse)
2995
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2996
7.67k
  assert(exception != (ExceptionInfo *) NULL);
2997
7.67k
  assert(exception->signature == MagickCoreSignature);
2998
7.67k
  angle=fmod(degrees,360.0);
2999
7.67k
  while (angle < -45.0)
3000
0
    angle+=360.0;
3001
18.0k
  for (rotations=0; angle > 45.0; rotations++)
3002
10.3k
    angle-=90.0;
3003
7.67k
  rotations%=4;
3004
7.67k
  shear.x=(-tan((double) DegreesToRadians(angle)/2.0));
3005
7.67k
  shear.y=sin((double) DegreesToRadians(angle));
3006
7.67k
  if ((fabs(shear.x) < MagickEpsilon) && (fabs(shear.y) < MagickEpsilon))
3007
5.76k
    return(IntegralRotateImage(image,rotations,exception));
3008
1.90k
  distort_image=CloneImage(image,0,0,MagickTrue,exception);
3009
1.90k
  if (distort_image == (Image *) NULL)
3010
0
    return((Image *) NULL);
3011
1.90k
  (void) SetImageVirtualPixelMethod(distort_image,BackgroundVirtualPixelMethod,
3012
1.90k
    exception);
3013
1.90k
  rotate_image=DistortImage(distort_image,ScaleRotateTranslateDistortion,1,
3014
1.90k
    &degrees,MagickTrue,exception);
3015
1.90k
  distort_image=DestroyImage(distort_image);
3016
1.90k
  return(rotate_image);
3017
1.90k
}
3018

3019
/*
3020
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3021
%                                                                             %
3022
%                                                                             %
3023
%                                                                             %
3024
%   S p a r s e C o l o r I m a g e                                           %
3025
%                                                                             %
3026
%                                                                             %
3027
%                                                                             %
3028
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3029
%
3030
%  SparseColorImage(), given a set of coordinates, interpolates the colors
3031
%  found at those coordinates, across the whole image, using various methods.
3032
%
3033
%  The format of the SparseColorImage() method is:
3034
%
3035
%      Image *SparseColorImage(const Image *image,
3036
%        const SparseColorMethod method,const size_t number_arguments,
3037
%        const double *arguments,ExceptionInfo *exception)
3038
%
3039
%  A description of each parameter follows:
3040
%
3041
%    o image: the image to be filled in.
3042
%
3043
%    o method: the method to fill in the gradient between the control points.
3044
%
3045
%        The methods used for SparseColor() are often simular to methods
3046
%        used for DistortImage(), and even share the same code for determination
3047
%        of the function coefficients, though with more dimensions (or resulting
3048
%        values).
3049
%
3050
%    o number_arguments: the number of arguments given.
3051
%
3052
%    o arguments: array of floating point arguments for this method--
3053
%        x,y,color_values-- with color_values given as normalized values.
3054
%
3055
%    o exception: return any errors or warnings in this structure
3056
%
3057
*/
3058
MagickExport Image *SparseColorImage(const Image *image,
3059
  const SparseColorMethod method,const size_t number_arguments,
3060
  const double *arguments,ExceptionInfo *exception)
3061
0
{
3062
0
#define SparseColorTag  "Distort/SparseColor"
3063
3064
0
  double
3065
0
    *coeff;
3066
3067
0
  Image
3068
0
    *sparse_image;
3069
3070
0
  size_t
3071
0
    number_colors;
3072
3073
0
  SparseColorMethod
3074
0
    sparse_method;
3075
3076
0
  assert(image != (Image *) NULL);
3077
0
  assert(image->signature == MagickCoreSignature);
3078
0
  assert(exception != (ExceptionInfo *) NULL);
3079
0
  assert(exception->signature == MagickCoreSignature);
3080
0
  if (IsEventLogging() != MagickFalse)
3081
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3082
3083
  /* Determine number of color values needed per control point */
3084
0
  number_colors=0;
3085
0
  if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
3086
0
    number_colors++;
3087
0
  if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3088
0
    number_colors++;
3089
0
  if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3090
0
    number_colors++;
3091
0
  if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
3092
0
      (image->colorspace == CMYKColorspace))
3093
0
    number_colors++;
3094
0
  if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
3095
0
      (image->alpha_trait != UndefinedPixelTrait))
3096
0
    number_colors++;
3097
3098
  /*
3099
    Convert input arguments into mapping coefficients, in this case
3100
    we are mapping (distorting) colors, rather than coordinates.
3101
  */
3102
0
  { DistortMethod
3103
0
      distort_method;
3104
3105
0
    distort_method=(DistortMethod) method;
3106
0
    if ( distort_method >= SentinelDistortion )
3107
0
      distort_method = ShepardsDistortion; /* Pretend to be Shepards */
3108
0
    coeff = GenerateCoefficients(image, &distort_method, number_arguments,
3109
0
                arguments, number_colors, exception);
3110
0
    if ( coeff == (double *) NULL )
3111
0
      return((Image *) NULL);
3112
    /*
3113
      Note some Distort Methods may fall back to other simpler methods,
3114
      Currently the only fallback of concern is Bilinear to Affine
3115
      (Barycentric), which is also sparse_colr method.  This also ensures
3116
      correct two and one color Barycentric handling.
3117
    */
3118
0
    sparse_method = (SparseColorMethod) distort_method;
3119
0
    if ( distort_method == ShepardsDistortion )
3120
0
      sparse_method = method;   /* return non-distort methods to normal */
3121
0
    if ( sparse_method == InverseColorInterpolate )
3122
0
      coeff[0]=0.5;            /* sqrt() the squared distance for inverse */
3123
0
  }
3124
3125
  /* Verbose output */
3126
0
  if (IsStringTrue(GetImageArtifact(image,"verbose")) != MagickFalse) {
3127
3128
0
    switch (sparse_method) {
3129
0
      case BarycentricColorInterpolate:
3130
0
      {
3131
0
        ssize_t x=0;
3132
0
        (void) FormatLocaleFile(stderr, "Barycentric Sparse Color:\n");
3133
0
        if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
3134
0
          (void) FormatLocaleFile(stderr, "  -channel R -fx '%+lf*i %+lf*j %+lf' \\\n",
3135
0
              coeff[x], coeff[x+1], coeff[x+2]),x+=3;
3136
0
        if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3137
0
          (void) FormatLocaleFile(stderr, "  -channel G -fx '%+lf*i %+lf*j %+lf' \\\n",
3138
0
              coeff[x], coeff[x+1], coeff[x+2]),x+=3;
3139
0
        if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3140
0
          (void) FormatLocaleFile(stderr, "  -channel B -fx '%+lf*i %+lf*j %+lf' \\\n",
3141
0
              coeff[x], coeff[x+1], coeff[x+2]),x+=3;
3142
0
        if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
3143
0
            (image->colorspace == CMYKColorspace))
3144
0
          (void) FormatLocaleFile(stderr, "  -channel K -fx '%+lf*i %+lf*j %+lf' \\\n",
3145
0
              coeff[x], coeff[x+1], coeff[x+2]),x+=3;
3146
0
        if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
3147
0
            (image->alpha_trait != UndefinedPixelTrait))
3148
0
          (void) FormatLocaleFile(stderr, "  -channel A -fx '%+lf*i %+lf*j %+lf' \\\n",
3149
0
              coeff[x], coeff[x+1], coeff[x+2]),x+=3;
3150
0
        break;
3151
0
      }
3152
0
      case BilinearColorInterpolate:
3153
0
      {
3154
0
        ssize_t x=0;
3155
0
        (void) FormatLocaleFile(stderr, "Bilinear Sparse Color\n");
3156
0
        if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
3157
0
          (void) FormatLocaleFile(stderr, "   -channel R -fx '%+lf*i %+lf*j %+lf*i*j %+lf;\n",
3158
0
              coeff[ x ], coeff[x+1],
3159
0
              coeff[x+2], coeff[x+3]),x+=4;
3160
0
        if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3161
0
          (void) FormatLocaleFile(stderr, "   -channel G -fx '%+lf*i %+lf*j %+lf*i*j %+lf;\n",
3162
0
              coeff[ x ], coeff[x+1],
3163
0
              coeff[x+2], coeff[x+3]),x+=4;
3164
0
        if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3165
0
          (void) FormatLocaleFile(stderr, "   -channel B -fx '%+lf*i %+lf*j %+lf*i*j %+lf;\n",
3166
0
              coeff[ x ], coeff[x+1],
3167
0
              coeff[x+2], coeff[x+3]),x+=4;
3168
0
        if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
3169
0
            (image->colorspace == CMYKColorspace))
3170
0
          (void) FormatLocaleFile(stderr, "   -channel K -fx '%+lf*i %+lf*j %+lf*i*j %+lf;\n",
3171
0
              coeff[ x ], coeff[x+1],
3172
0
              coeff[x+2], coeff[x+3]),x+=4;
3173
0
        if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
3174
0
            (image->alpha_trait != UndefinedPixelTrait))
3175
0
          (void) FormatLocaleFile(stderr, "   -channel A -fx '%+lf*i %+lf*j %+lf*i*j %+lf;\n",
3176
0
              coeff[ x ], coeff[x+1],
3177
0
              coeff[x+2], coeff[x+3]),x+=4;
3178
0
        break;
3179
0
      }
3180
0
      default:
3181
        /* sparse color method is too complex for FX emulation */
3182
0
        break;
3183
0
    }
3184
0
  }
3185
3186
  /* Generate new image for generated interpolated gradient.
3187
   * ASIDE: Actually we could have just replaced the colors of the original
3188
   * image, but IM Core policy, is if storage class could change then clone
3189
   * the image.
3190
   */
3191
3192
0
  sparse_image=CloneImage(image,0,0,MagickTrue,exception);
3193
0
  if (sparse_image == (Image *) NULL)
3194
0
    return((Image *) NULL);
3195
0
  if (SetImageStorageClass(sparse_image,DirectClass,exception) == MagickFalse)
3196
0
    { /* if image is ColorMapped - change it to DirectClass */
3197
0
      sparse_image=DestroyImage(sparse_image);
3198
0
      return((Image *) NULL);
3199
0
    }
3200
0
  if (IsGrayColorspace(sparse_image->colorspace) != MagickFalse)
3201
0
    (void) SetImageColorspace(sparse_image,sRGBColorspace,exception);
3202
0
  { /* ----- MAIN CODE ----- */
3203
0
    CacheView
3204
0
      *sparse_view;
3205
3206
0
    MagickBooleanType
3207
0
      status;
3208
3209
0
    MagickOffsetType
3210
0
      progress;
3211
3212
0
    ssize_t
3213
0
      j;
3214
3215
0
    status=MagickTrue;
3216
0
    progress=0;
3217
0
    sparse_view=AcquireAuthenticCacheView(sparse_image,exception);
3218
#if defined(MAGICKCORE_OPENMP_SUPPORT)
3219
    #pragma omp parallel for schedule(static) shared(progress,status) \
3220
      magick_number_threads(image,sparse_image,sparse_image->rows,1)
3221
#endif
3222
0
    for (j=0; j < (ssize_t) sparse_image->rows; j++)
3223
0
    {
3224
0
      MagickBooleanType
3225
0
        sync;
3226
3227
0
      PixelInfo
3228
0
        pixel;    /* pixel to assign to distorted image */
3229
3230
0
      Quantum
3231
0
        *magick_restrict q;
3232
3233
0
      ssize_t
3234
0
        i;
3235
3236
0
      q=GetCacheViewAuthenticPixels(sparse_view,0,j,sparse_image->columns,1,
3237
0
        exception);
3238
0
      if (q == (Quantum *) NULL)
3239
0
        {
3240
0
          status=MagickFalse;
3241
0
          continue;
3242
0
        }
3243
0
      GetPixelInfo(sparse_image,&pixel);
3244
0
      for (i=0; i < (ssize_t) sparse_image->columns; i++)
3245
0
      {
3246
0
        GetPixelInfoPixel(sparse_image,q,&pixel);
3247
0
        switch (sparse_method)
3248
0
        {
3249
0
          case BarycentricColorInterpolate:
3250
0
          {
3251
0
            ssize_t x=0;
3252
0
            if ((GetPixelRedTraits(sparse_image) & UpdatePixelTrait) != 0)
3253
0
              pixel.red     = coeff[x]*i +coeff[x+1]*j
3254
0
                              +coeff[x+2], x+=3;
3255
0
            if ((GetPixelGreenTraits(sparse_image) & UpdatePixelTrait) != 0)
3256
0
              pixel.green   = coeff[x]*i +coeff[x+1]*j
3257
0
                              +coeff[x+2], x+=3;
3258
0
            if ((GetPixelBlueTraits(sparse_image) & UpdatePixelTrait) != 0)
3259
0
              pixel.blue    = coeff[x]*i +coeff[x+1]*j
3260
0
                              +coeff[x+2], x+=3;
3261
0
            if (((GetPixelBlackTraits(sparse_image) & UpdatePixelTrait) != 0) &&
3262
0
                (sparse_image->colorspace == CMYKColorspace))
3263
0
              pixel.black   = coeff[x]*i +coeff[x+1]*j
3264
0
                              +coeff[x+2], x+=3;
3265
0
            if (((GetPixelAlphaTraits(sparse_image) & UpdatePixelTrait) != 0) &&
3266
0
                (sparse_image->alpha_trait != UndefinedPixelTrait))
3267
0
              pixel.alpha = coeff[x]*i +coeff[x+1]*j
3268
0
                              +coeff[x+2], x+=3;
3269
0
            break;
3270
0
          }
3271
0
          case BilinearColorInterpolate:
3272
0
          {
3273
0
            ssize_t x=0;
3274
0
            if ((GetPixelRedTraits(sparse_image) & UpdatePixelTrait) != 0)
3275
0
              pixel.red     = coeff[x]*i     + coeff[x+1]*j +
3276
0
                              coeff[x+2]*i*j + coeff[x+3], x+=4;
3277
0
            if ((GetPixelGreenTraits(sparse_image) & UpdatePixelTrait) != 0)
3278
0
              pixel.green   = coeff[x]*i     + coeff[x+1]*j +
3279
0
                              coeff[x+2]*i*j + coeff[x+3], x+=4;
3280
0
            if ((GetPixelBlueTraits(sparse_image) & UpdatePixelTrait) != 0)
3281
0
              pixel.blue    = coeff[x]*i     + coeff[x+1]*j +
3282
0
                              coeff[x+2]*i*j + coeff[x+3], x+=4;
3283
0
            if (((GetPixelBlackTraits(sparse_image) & UpdatePixelTrait) != 0) &&
3284
0
                (image->colorspace == CMYKColorspace))
3285
0
              pixel.black   = coeff[x]*i     + coeff[x+1]*j +
3286
0
                              coeff[x+2]*i*j + coeff[x+3], x+=4;
3287
0
            if (((GetPixelAlphaTraits(sparse_image) & UpdatePixelTrait) != 0) &&
3288
0
                (sparse_image->alpha_trait != UndefinedPixelTrait))
3289
0
              pixel.alpha = coeff[x]*i     + coeff[x+1]*j +
3290
0
                              coeff[x+2]*i*j + coeff[x+3], x+=4;
3291
0
            break;
3292
0
          }
3293
0
          case InverseColorInterpolate:
3294
0
          case ShepardsColorInterpolate:
3295
0
          { /* Inverse (Squared) Distance weights average (IDW) */
3296
0
            double
3297
0
              denominator;
3298
3299
0
            size_t
3300
0
              k;
3301
3302
0
            if ((GetPixelRedTraits(sparse_image) & UpdatePixelTrait) != 0)
3303
0
              pixel.red=0.0;
3304
0
            if ((GetPixelGreenTraits(sparse_image) & UpdatePixelTrait) != 0)
3305
0
              pixel.green=0.0;
3306
0
            if ((GetPixelBlueTraits(sparse_image) & UpdatePixelTrait) != 0)
3307
0
              pixel.blue=0.0;
3308
0
            if (((GetPixelBlackTraits(sparse_image) & UpdatePixelTrait) != 0) &&
3309
0
                (image->colorspace == CMYKColorspace))
3310
0
              pixel.black=0.0;
3311
0
            if (((GetPixelAlphaTraits(sparse_image) & UpdatePixelTrait) != 0) &&
3312
0
                (sparse_image->alpha_trait != UndefinedPixelTrait))
3313
0
              pixel.alpha=0.0;
3314
0
            denominator = 0.0;
3315
0
            for (k=0; k<number_arguments; k+=2+number_colors)
3316
0
            {
3317
0
              double weight =
3318
0
                  ((double) i-arguments[ k ])*((double) i-arguments[ k ])
3319
0
                + ((double) j-arguments[k+1])*((double) j-arguments[k+1]);
3320
0
              ssize_t x = (ssize_t) k+2;
3321
3322
0
              weight = pow(weight,coeff[0]); /* inverse of power factor */
3323
0
              weight = ( weight < 1.0 ) ? 1.0 : 1.0/weight;
3324
0
              if ((GetPixelRedTraits(sparse_image) & UpdatePixelTrait) != 0)
3325
0
                pixel.red     += arguments[x++]*weight;
3326
0
              if ((GetPixelGreenTraits(sparse_image) & UpdatePixelTrait) != 0)
3327
0
                pixel.green   += arguments[x++]*weight;
3328
0
              if ((GetPixelBlueTraits(sparse_image) & UpdatePixelTrait) != 0)
3329
0
                pixel.blue    += arguments[x++]*weight;
3330
0
              if (((GetPixelBlackTraits(sparse_image) & UpdatePixelTrait) != 0) &&
3331
0
                  (image->colorspace == CMYKColorspace))
3332
0
                pixel.black   += arguments[x++]*weight;
3333
0
              if (((GetPixelAlphaTraits(sparse_image) & UpdatePixelTrait) != 0) &&
3334
0
                  (sparse_image->alpha_trait != UndefinedPixelTrait))
3335
0
                pixel.alpha += arguments[x++]*weight;
3336
0
              denominator += weight;
3337
0
            }
3338
0
            if ((GetPixelRedTraits(sparse_image) & UpdatePixelTrait) != 0)
3339
0
              pixel.red/=denominator;
3340
0
            if ((GetPixelGreenTraits(sparse_image) & UpdatePixelTrait) != 0)
3341
0
              pixel.green/=denominator;
3342
0
            if ((GetPixelBlueTraits(sparse_image) & UpdatePixelTrait) != 0)
3343
0
              pixel.blue/=denominator;
3344
0
            if (((GetPixelBlackTraits(sparse_image) & UpdatePixelTrait) != 0) &&
3345
0
                (image->colorspace == CMYKColorspace))
3346
0
              pixel.black/=denominator;
3347
0
            if (((GetPixelAlphaTraits(sparse_image) & UpdatePixelTrait) != 0) &&
3348
0
                (sparse_image->alpha_trait != UndefinedPixelTrait))
3349
0
              pixel.alpha/=denominator;
3350
0
            break;
3351
0
          }
3352
0
          case ManhattanColorInterpolate:
3353
0
          {
3354
0
            double
3355
0
              minimum = MagickMaximumValue;
3356
3357
0
            size_t
3358
0
              k;
3359
3360
            /*
3361
              Just use the closest control point you can find!
3362
            */
3363
0
            for (k=0; k<number_arguments; k+=2+number_colors)
3364
0
            {
3365
0
              double distance = fabs((double)i-arguments[ k ])+
3366
0
                fabs((double)j-arguments[k+1]);
3367
0
              if ( distance < minimum ) {
3368
0
                ssize_t x=(ssize_t) k+2;
3369
0
                if ((GetPixelRedTraits(sparse_image) & UpdatePixelTrait) != 0)
3370
0
                  pixel.red=arguments[x++];
3371
0
                if ((GetPixelGreenTraits(sparse_image) & UpdatePixelTrait) != 0)
3372
0
                  pixel.green=arguments[x++];
3373
0
                if ((GetPixelBlueTraits(sparse_image) & UpdatePixelTrait) != 0)
3374
0
                  pixel.blue=arguments[x++];
3375
0
                if (((GetPixelBlackTraits(sparse_image) & UpdatePixelTrait) != 0) &&
3376
0
                    (image->colorspace == CMYKColorspace))
3377
0
                  pixel.black=arguments[x++];
3378
0
                if (((GetPixelAlphaTraits(sparse_image) & UpdatePixelTrait) != 0) &&
3379
0
                    (sparse_image->alpha_trait != UndefinedPixelTrait))
3380
0
                  pixel.alpha=arguments[x++];
3381
0
                minimum = distance;
3382
0
              }
3383
0
            }
3384
0
            break;
3385
0
          }
3386
0
          case VoronoiColorInterpolate:
3387
0
          default:
3388
0
          {
3389
0
            double
3390
0
              minimum = MagickMaximumValue;
3391
3392
0
            size_t
3393
0
              k;
3394
3395
            /*
3396
              Just use the closest control point you can find!
3397
            */
3398
0
            for (k=0; k<number_arguments; k+=2+number_colors) {
3399
0
              double distance =
3400
0
                  ((double) i-arguments[ k ])*((double) i-arguments[ k ])
3401
0
                + ((double) j-arguments[k+1])*((double) j-arguments[k+1]);
3402
0
              if ( distance < minimum ) {
3403
0
                ssize_t x = (ssize_t) k+2;
3404
0
                if ((GetPixelRedTraits(sparse_image) & UpdatePixelTrait) != 0)
3405
0
                  pixel.red=arguments[x++];
3406
0
                if ((GetPixelGreenTraits(sparse_image) & UpdatePixelTrait) != 0)
3407
0
                  pixel.green=arguments[x++];
3408
0
                if ((GetPixelBlueTraits(sparse_image) & UpdatePixelTrait) != 0)
3409
0
                  pixel.blue=arguments[x++];
3410
0
                if (((GetPixelBlackTraits(sparse_image) & UpdatePixelTrait) != 0) &&
3411
0
                    (image->colorspace == CMYKColorspace))
3412
0
                  pixel.black=arguments[x++];
3413
0
                if (((GetPixelAlphaTraits(sparse_image) & UpdatePixelTrait) != 0) &&
3414
0
                    (sparse_image->alpha_trait != UndefinedPixelTrait))
3415
0
                  pixel.alpha=arguments[x++];
3416
0
                minimum = distance;
3417
0
              }
3418
0
            }
3419
0
            break;
3420
0
          }
3421
0
        }
3422
        /* set the color directly back into the source image */
3423
0
        if ((GetPixelRedTraits(sparse_image) & UpdatePixelTrait) != 0)
3424
0
          pixel.red=(MagickRealType) ClampPixel((double) QuantumRange*
3425
0
            pixel.red);
3426
0
        if ((GetPixelGreenTraits(sparse_image) & UpdatePixelTrait) != 0)
3427
0
          pixel.green=(MagickRealType) ClampPixel((double) QuantumRange*
3428
0
            pixel.green);
3429
0
        if ((GetPixelBlueTraits(sparse_image) & UpdatePixelTrait) != 0)
3430
0
          pixel.blue=(MagickRealType) ClampPixel((double) QuantumRange*
3431
0
            pixel.blue);
3432
0
        if (((GetPixelBlackTraits(sparse_image) & UpdatePixelTrait) != 0) &&
3433
0
            (image->colorspace == CMYKColorspace))
3434
0
          pixel.black=(MagickRealType) ClampPixel((double) QuantumRange*
3435
0
            pixel.black);
3436
0
        if (((GetPixelAlphaTraits(sparse_image) & UpdatePixelTrait) != 0) &&
3437
0
            (image->alpha_trait != UndefinedPixelTrait))
3438
0
          pixel.alpha=(MagickRealType) ClampPixel((double) QuantumRange*
3439
0
            pixel.alpha);
3440
0
        SetPixelViaPixelInfo(sparse_image,&pixel,q);
3441
0
        q+=(ptrdiff_t) GetPixelChannels(sparse_image);
3442
0
      }
3443
0
      sync=SyncCacheViewAuthenticPixels(sparse_view,exception);
3444
0
      if (sync == MagickFalse)
3445
0
        status=MagickFalse;
3446
0
      if (image->progress_monitor != (MagickProgressMonitor) NULL)
3447
0
        {
3448
0
          MagickBooleanType
3449
0
            proceed;
3450
3451
#if defined(MAGICKCORE_OPENMP_SUPPORT)
3452
          #pragma omp atomic
3453
#endif
3454
0
          progress++;
3455
0
          proceed=SetImageProgress(image,SparseColorTag,progress,image->rows);
3456
0
          if (proceed == MagickFalse)
3457
0
            status=MagickFalse;
3458
0
        }
3459
0
    }
3460
0
    sparse_view=DestroyCacheView(sparse_view);
3461
0
    if (status == MagickFalse)
3462
0
      sparse_image=DestroyImage(sparse_image);
3463
0
  }
3464
0
  coeff = (double *) RelinquishMagickMemory(coeff);
3465
0
  return(sparse_image);
3466
0
}