13ed852eea50f9d4cd633efb8c2b054b8e33c253cristy/*
23ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
33ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%                                                                             %
43ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%                                                                             %
53ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%                                                                             %
63ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%               DDDD   IIIII  SSSSS  TTTTT   OOO   RRRR   TTTTT               %
73ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%               D   D    I    SS       T    O   O  R   R    T                 %
83ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%               D   D    I     SSS     T    O   O  RRRR     T                 %
93ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%               D   D    I       SS    T    O   O  R R      T                 %
103ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%               DDDD   IIIII  SSSSS    T     OOO   R  R     T                 %
113ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%                                                                             %
123ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%                                                                             %
133ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%                     MagickCore Image Distortion Methods                     %
143ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%                                                                             %
153ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%                              Software Design                                %
16de984cdc3631106b1cbbb8d3972b76a0fc27e8e8cristy%                                   Cristy                                    %
173ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%                              Anthony Thyssen                                %
183ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%                                 June 2007                                   %
193ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%                                                                             %
203ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%                                                                             %
217ce65e7125a4e1df1a274ce373c537a9df9c16cdCristy%  Copyright 1999-2016 ImageMagick Studio LLC, a non-profit organization      %
223ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%  dedicated to making software imaging solutions freely available.           %
233ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%                                                                             %
243ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%  You may not use this file except in compliance with the License.  You may  %
253ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%  obtain a copy of the License at                                            %
263ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%                                                                             %
273ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%    http://www.imagemagick.org/script/license.php                            %
283ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%                                                                             %
293ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%  Unless required by applicable law or agreed to in writing, software        %
303ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%  distributed under the License is distributed on an "AS IS" BASIS,          %
313ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
323ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%  See the License for the specific language governing permissions and        %
333ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%  limitations under the License.                                             %
343ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%                                                                             %
353ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
363ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%
373ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%
383ed852eea50f9d4cd633efb8c2b054b8e33c253cristy*/
393ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
403ed852eea50f9d4cd633efb8c2b054b8e33c253cristy/*
413ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  Include declarations.
423ed852eea50f9d4cd633efb8c2b054b8e33c253cristy*/
434c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/studio.h"
444c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/artifact.h"
454c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/cache.h"
464c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/cache-view.h"
476a2180cee55312a7c0c633670803f9face88a82acristy#include "MagickCore/channel.h"
484c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/colorspace-private.h"
494c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/composite-private.h"
504c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/distort.h"
514c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/exception.h"
524c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/exception-private.h"
534c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/gem.h"
544c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/image.h"
55abed7e293f2e8a83e8036df7f2a3e1d9859e5fb2dirk#include "MagickCore/linked-list.h"
564c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/list.h"
574c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/matrix.h"
58d1dd6e4fefa0810b9893e6ac9418f79c97c1b39acristy#include "MagickCore/matrix-private.h"
594c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/memory_.h"
604c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/monitor-private.h"
614c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/option.h"
624c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/pixel.h"
634c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/pixel-accessor.h"
6435f1530f0591e7380a976a606080774c28bc4da2cristy#include "MagickCore/pixel-private.h"
654c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/resample.h"
664c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/resample-private.h"
674c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/registry.h"
68ac245f8a51ea65b085d751c41d8ca4b426bdfe5bcristy#include "MagickCore/resource_.h"
694c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/semaphore.h"
70b9514f21a501293847bd9ccb2de0c3784cc6a250cristy#include "MagickCore/shear.h"
714c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/string_.h"
724c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/string-private.h"
734c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/thread-private.h"
744c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/token.h"
754c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/transform.h"
763ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
773ed852eea50f9d4cd633efb8c2b054b8e33c253cristy/*
783ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  Numerous internal routines for image distortions.
793ed852eea50f9d4cd633efb8c2b054b8e33c253cristy*/
803ed852eea50f9d4cd633efb8c2b054b8e33c253cristystatic inline void AffineArgsToCoefficients(double *affine)
813ed852eea50f9d4cd633efb8c2b054b8e33c253cristy{
823ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  /* map  external sx,ry,rx,sy,tx,ty  to  internal c0,c2,c4,c1,c3,c5 */
833ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  double tmp[4];  /* note indexes  0 and 5 remain unchanged */
843ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  tmp[0]=affine[1]; tmp[1]=affine[2]; tmp[2]=affine[3]; tmp[3]=affine[4];
853ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  affine[3]=tmp[0]; affine[1]=tmp[1]; affine[4]=tmp[2]; affine[2]=tmp[3];
863ed852eea50f9d4cd633efb8c2b054b8e33c253cristy}
8739052fff02d6a281e1837737e345ecfcfe7cf60fcristy
883ed852eea50f9d4cd633efb8c2b054b8e33c253cristystatic inline void CoefficientsToAffineArgs(double *coeff)
893ed852eea50f9d4cd633efb8c2b054b8e33c253cristy{
903ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  /* map  internal c0,c1,c2,c3,c4,c5  to  external sx,ry,rx,sy,tx,ty */
913ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  double tmp[4];  /* note indexes 0 and 5 remain unchanged */
923ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  tmp[0]=coeff[3]; tmp[1]=coeff[1]; tmp[2]=coeff[4]; tmp[3]=coeff[2];
933ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  coeff[1]=tmp[0]; coeff[2]=tmp[1]; coeff[3]=tmp[2]; coeff[4]=tmp[3];
943ed852eea50f9d4cd633efb8c2b054b8e33c253cristy}
953ed852eea50f9d4cd633efb8c2b054b8e33c253cristystatic void InvertAffineCoefficients(const double *coeff,double *inverse)
963ed852eea50f9d4cd633efb8c2b054b8e33c253cristy{
973ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  /* From "Digital Image Warping" by George Wolberg, page 50 */
983ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  double determinant;
993ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
1003e3ec3afbb0782697f201cbe30a56794c10dc7efcristy  determinant=PerceptibleReciprocal(coeff[0]*coeff[4]-coeff[1]*coeff[3]);
1013ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  inverse[0]=determinant*coeff[4];
1023ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  inverse[1]=determinant*(-coeff[1]);
1033ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  inverse[2]=determinant*(coeff[1]*coeff[5]-coeff[2]*coeff[4]);
1043ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  inverse[3]=determinant*(-coeff[3]);
1053ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  inverse[4]=determinant*coeff[0];
1063ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  inverse[5]=determinant*(coeff[2]*coeff[3]-coeff[0]*coeff[5]);
1073ed852eea50f9d4cd633efb8c2b054b8e33c253cristy}
1083ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
1093ed852eea50f9d4cd633efb8c2b054b8e33c253cristystatic void InvertPerspectiveCoefficients(const double *coeff,
1103ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  double *inverse)
1113ed852eea50f9d4cd633efb8c2b054b8e33c253cristy{
1123ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  /* From "Digital Image Warping" by George Wolberg, page 53 */
1133ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  double determinant;
1143ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
1153e3ec3afbb0782697f201cbe30a56794c10dc7efcristy  determinant=PerceptibleReciprocal(coeff[0]*coeff[4]-coeff[3]*coeff[1]);
1163ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  inverse[0]=determinant*(coeff[4]-coeff[7]*coeff[5]);
1173ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  inverse[1]=determinant*(coeff[7]*coeff[2]-coeff[1]);
1183ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  inverse[2]=determinant*(coeff[1]*coeff[5]-coeff[4]*coeff[2]);
1193ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  inverse[3]=determinant*(coeff[6]*coeff[5]-coeff[3]);
1203ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  inverse[4]=determinant*(coeff[0]-coeff[6]*coeff[2]);
1213ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  inverse[5]=determinant*(coeff[3]*coeff[2]-coeff[0]*coeff[5]);
1223ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  inverse[6]=determinant*(coeff[3]*coeff[7]-coeff[6]*coeff[4]);
1233ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  inverse[7]=determinant*(coeff[6]*coeff[1]-coeff[0]*coeff[7]);
1243ed852eea50f9d4cd633efb8c2b054b8e33c253cristy}
1253ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
1265056b230558c7de0a79d51d82bdc929fef45a225anthony/*
1275056b230558c7de0a79d51d82bdc929fef45a225anthony * Polynomial Term Defining Functions
1285056b230558c7de0a79d51d82bdc929fef45a225anthony *
1295056b230558c7de0a79d51d82bdc929fef45a225anthony * Order must either be an integer, or 1.5 to produce
130972a6e66d79b58ffae5d7512b33c05dccc8f537cnicolas * the 2 number_valuesal polynomial function...
131972a6e66d79b58ffae5d7512b33c05dccc8f537cnicolas *    affine     1   (3)      u = c0 + c1*x + c2*y
132972a6e66d79b58ffae5d7512b33c05dccc8f537cnicolas *    bilinear   1.5 (4)      u = '' + c3*x*y
133972a6e66d79b58ffae5d7512b33c05dccc8f537cnicolas *    quadratic  2   (6)      u = '' + c4*x*x + c5*y*y
134972a6e66d79b58ffae5d7512b33c05dccc8f537cnicolas *    cubic      3   (10)     u = '' + c6*x^3 + c7*x*x*y + c8*x*y*y + c9*y^3
135972a6e66d79b58ffae5d7512b33c05dccc8f537cnicolas *    quartic    4   (15)     u = '' + c10*x^4 + ... + c14*y^4
136972a6e66d79b58ffae5d7512b33c05dccc8f537cnicolas *    quintic    5   (21)     u = '' + c15*x^5 + ... + c20*y^5
1375056b230558c7de0a79d51d82bdc929fef45a225anthony * number in parenthesis minimum number of points needed.
1385056b230558c7de0a79d51d82bdc929fef45a225anthony * Anything beyond quintic, has not been implemented until
1390c63ece53c43017de0184b16fe8cb6e5b58ad51bnicolas * a more automated way of determining terms is found.
1405056b230558c7de0a79d51d82bdc929fef45a225anthony
1415056b230558c7de0a79d51d82bdc929fef45a225anthony * Note the slight re-ordering of the terms for a quadratic polynomial
1425056b230558c7de0a79d51d82bdc929fef45a225anthony * which is to allow the use of a bi-linear (order=1.5) polynomial.
1435056b230558c7de0a79d51d82bdc929fef45a225anthony * All the later polynomials are ordered simply from x^N to y^N
1445056b230558c7de0a79d51d82bdc929fef45a225anthony */
145bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristystatic size_t poly_number_terms(double order)
1463ed852eea50f9d4cd633efb8c2b054b8e33c253cristy{
1475056b230558c7de0a79d51d82bdc929fef45a225anthony /* Return the number of terms for a 2d polynomial */
1483ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  if ( order < 1 || order > 5 ||
1493ed852eea50f9d4cd633efb8c2b054b8e33c253cristy       ( order != floor(order) && (order-1.5) > MagickEpsilon) )
1503ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    return 0; /* invalid polynomial order */
151bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  return((size_t) floor((order+1)*(order+2)/2));
1523ed852eea50f9d4cd633efb8c2b054b8e33c253cristy}
1533ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
154bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristystatic double poly_basis_fn(ssize_t n, double x, double y)
1553ed852eea50f9d4cd633efb8c2b054b8e33c253cristy{
1565056b230558c7de0a79d51d82bdc929fef45a225anthony  /* Return the result for this polynomial term */
1573ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  switch(n) {
1583ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case  0:  return( 1.0 ); /* constant */
1593ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case  1:  return(  x  );
160972a6e66d79b58ffae5d7512b33c05dccc8f537cnicolas    case  2:  return(  y  ); /* affine          order = 1   terms = 3 */
161972a6e66d79b58ffae5d7512b33c05dccc8f537cnicolas    case  3:  return( x*y ); /* bilinear        order = 1.5 terms = 4 */
1623ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case  4:  return( x*x );
163972a6e66d79b58ffae5d7512b33c05dccc8f537cnicolas    case  5:  return( y*y ); /* quadratic       order = 2   terms = 6 */
1643ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case  6:  return( x*x*x );
1653ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case  7:  return( x*x*y );
1663ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case  8:  return( x*y*y );
167972a6e66d79b58ffae5d7512b33c05dccc8f537cnicolas    case  9:  return( y*y*y ); /* cubic         order = 3   terms = 10 */
1683ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case 10:  return( x*x*x*x );
1693ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case 11:  return( x*x*x*y );
1703ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case 12:  return( x*x*y*y );
1713ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case 13:  return( x*y*y*y );
1723ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case 14:  return( y*y*y*y ); /* quartic     order = 4   terms = 15 */
1733ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case 15:  return( x*x*x*x*x );
1743ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case 16:  return( x*x*x*x*y );
1753ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case 17:  return( x*x*x*y*y );
1763ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case 18:  return( x*x*y*y*y );
1773ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case 19:  return( x*y*y*y*y );
178972a6e66d79b58ffae5d7512b33c05dccc8f537cnicolas    case 20:  return( y*y*y*y*y ); /* quintic   order = 5   terms = 21 */
1793ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  }
1803ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  return( 0 ); /* should never happen */
1813ed852eea50f9d4cd633efb8c2b054b8e33c253cristy}
182bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristystatic const char *poly_basis_str(ssize_t n)
1833ed852eea50f9d4cd633efb8c2b054b8e33c253cristy{
1843ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  /* return the result for this polynomial term */
1853ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  switch(n) {
1863ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case  0:  return(""); /* constant */
1873ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case  1:  return("*ii");
188972a6e66d79b58ffae5d7512b33c05dccc8f537cnicolas    case  2:  return("*jj"); /* affine                order = 1   terms = 3 */
189972a6e66d79b58ffae5d7512b33c05dccc8f537cnicolas    case  3:  return("*ii*jj"); /* bilinear           order = 1.5 terms = 4 */
1903ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case  4:  return("*ii*ii");
191972a6e66d79b58ffae5d7512b33c05dccc8f537cnicolas    case  5:  return("*jj*jj"); /* quadratic          order = 2   terms = 6 */
1923ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case  6:  return("*ii*ii*ii");
1933ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case  7:  return("*ii*ii*jj");
1943ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case  8:  return("*ii*jj*jj");
195972a6e66d79b58ffae5d7512b33c05dccc8f537cnicolas    case  9:  return("*jj*jj*jj"); /* cubic           order = 3   terms = 10 */
1963ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case 10:  return("*ii*ii*ii*ii");
1973ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case 11:  return("*ii*ii*ii*jj");
1983ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case 12:  return("*ii*ii*jj*jj");
1993ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case 13:  return("*ii*jj*jj*jj");
200972a6e66d79b58ffae5d7512b33c05dccc8f537cnicolas    case 14:  return("*jj*jj*jj*jj"); /* quartic      order = 4   terms = 15 */
2013ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case 15:  return("*ii*ii*ii*ii*ii");
2023ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case 16:  return("*ii*ii*ii*ii*jj");
2033ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case 17:  return("*ii*ii*ii*jj*jj");
2043ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case 18:  return("*ii*ii*jj*jj*jj");
2053ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case 19:  return("*ii*jj*jj*jj*jj");
206972a6e66d79b58ffae5d7512b33c05dccc8f537cnicolas    case 20:  return("*jj*jj*jj*jj*jj"); /* quintic   order = 5   terms = 21 */
2073ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  }
2083ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  return( "UNKNOWN" ); /* should never happen */
2093ed852eea50f9d4cd633efb8c2b054b8e33c253cristy}
210bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristystatic double poly_basis_dx(ssize_t n, double x, double y)
2113ed852eea50f9d4cd633efb8c2b054b8e33c253cristy{
2123ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  /* polynomial term for x derivative */
2133ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  switch(n) {
2143ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case  0:  return( 0.0 ); /* constant */
2153ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case  1:  return( 1.0 );
2163ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case  2:  return( 0.0 ); /* affine      order = 1   terms = 3 */
2173ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case  3:  return(  y  ); /* bilinear    order = 1.5 terms = 4 */
2183ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case  4:  return(  x  );
2193ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case  5:  return( 0.0 ); /* quadratic   order = 2   terms = 6 */
2203ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case  6:  return( x*x );
2213ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case  7:  return( x*y );
2223ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case  8:  return( y*y );
2233ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case  9:  return( 0.0 ); /* cubic       order = 3   terms = 10 */
2243ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case 10:  return( x*x*x );
2253ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case 11:  return( x*x*y );
2263ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case 12:  return( x*y*y );
2273ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case 13:  return( y*y*y );
2283ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case 14:  return( 0.0 ); /* quartic     order = 4   terms = 15 */
2293ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case 15:  return( x*x*x*x );
2303ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case 16:  return( x*x*x*y );
2313ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case 17:  return( x*x*y*y );
2323ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case 18:  return( x*y*y*y );
2333ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case 19:  return( y*y*y*y );
2343ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case 20:  return( 0.0 ); /* quintic     order = 5   terms = 21 */
2353ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  }
2363ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  return( 0.0 ); /* should never happen */
2373ed852eea50f9d4cd633efb8c2b054b8e33c253cristy}
238bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristystatic double poly_basis_dy(ssize_t n, double x, double y)
2393ed852eea50f9d4cd633efb8c2b054b8e33c253cristy{
2403ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  /* polynomial term for y derivative */
2413ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  switch(n) {
2423ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case  0:  return( 0.0 ); /* constant */
2433ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case  1:  return( 0.0 );
2443ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case  2:  return( 1.0 ); /* affine      order = 1   terms = 3 */
2453ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case  3:  return(  x  ); /* bilinear    order = 1.5 terms = 4 */
2463ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case  4:  return( 0.0 );
2473ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case  5:  return(  y  ); /* quadratic   order = 2   terms = 6 */
2483ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    default:  return( poly_basis_dx(n-1,x,y) ); /* weird but true */
2493ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  }
250972a6e66d79b58ffae5d7512b33c05dccc8f537cnicolas  /* NOTE: the only reason that last is not true for 'quadratic'
2513ed852eea50f9d4cd633efb8c2b054b8e33c253cristy     is due to the re-arrangement of terms to allow for 'bilinear'
2523ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  */
2533ed852eea50f9d4cd633efb8c2b054b8e33c253cristy}
2543ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
2553ed852eea50f9d4cd633efb8c2b054b8e33c253cristy/*
2563ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2573ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%                                                                             %
2583ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%                                                                             %
2593ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%                                                                             %
260987feef6c1e3d08da3d9458fa5c464a32fc30191cristy%     A f f i n e T r a n s f o r m I m a g e                                 %
261987feef6c1e3d08da3d9458fa5c464a32fc30191cristy%                                                                             %
262987feef6c1e3d08da3d9458fa5c464a32fc30191cristy%                                                                             %
263987feef6c1e3d08da3d9458fa5c464a32fc30191cristy%                                                                             %
264987feef6c1e3d08da3d9458fa5c464a32fc30191cristy%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
265987feef6c1e3d08da3d9458fa5c464a32fc30191cristy%
266987feef6c1e3d08da3d9458fa5c464a32fc30191cristy%  AffineTransformImage() transforms an image as dictated by the affine matrix.
267987feef6c1e3d08da3d9458fa5c464a32fc30191cristy%  It allocates the memory necessary for the new Image structure and returns
268987feef6c1e3d08da3d9458fa5c464a32fc30191cristy%  a pointer to the new image.
269987feef6c1e3d08da3d9458fa5c464a32fc30191cristy%
270987feef6c1e3d08da3d9458fa5c464a32fc30191cristy%  The format of the AffineTransformImage method is:
271987feef6c1e3d08da3d9458fa5c464a32fc30191cristy%
272987feef6c1e3d08da3d9458fa5c464a32fc30191cristy%      Image *AffineTransformImage(const Image *image,
273987feef6c1e3d08da3d9458fa5c464a32fc30191cristy%        AffineMatrix *affine_matrix,ExceptionInfo *exception)
274987feef6c1e3d08da3d9458fa5c464a32fc30191cristy%
275987feef6c1e3d08da3d9458fa5c464a32fc30191cristy%  A description of each parameter follows:
276987feef6c1e3d08da3d9458fa5c464a32fc30191cristy%
277987feef6c1e3d08da3d9458fa5c464a32fc30191cristy%    o image: the image.
278987feef6c1e3d08da3d9458fa5c464a32fc30191cristy%
279987feef6c1e3d08da3d9458fa5c464a32fc30191cristy%    o affine_matrix: the affine matrix.
280987feef6c1e3d08da3d9458fa5c464a32fc30191cristy%
281987feef6c1e3d08da3d9458fa5c464a32fc30191cristy%    o exception: return any errors or warnings in this structure.
282987feef6c1e3d08da3d9458fa5c464a32fc30191cristy%
283987feef6c1e3d08da3d9458fa5c464a32fc30191cristy*/
284987feef6c1e3d08da3d9458fa5c464a32fc30191cristyMagickExport Image *AffineTransformImage(const Image *image,
285987feef6c1e3d08da3d9458fa5c464a32fc30191cristy  const AffineMatrix *affine_matrix,ExceptionInfo *exception)
286987feef6c1e3d08da3d9458fa5c464a32fc30191cristy{
287987feef6c1e3d08da3d9458fa5c464a32fc30191cristy  double
288987feef6c1e3d08da3d9458fa5c464a32fc30191cristy    distort[6];
289987feef6c1e3d08da3d9458fa5c464a32fc30191cristy
290987feef6c1e3d08da3d9458fa5c464a32fc30191cristy  Image
291987feef6c1e3d08da3d9458fa5c464a32fc30191cristy    *deskew_image;
292987feef6c1e3d08da3d9458fa5c464a32fc30191cristy
293987feef6c1e3d08da3d9458fa5c464a32fc30191cristy  /*
294987feef6c1e3d08da3d9458fa5c464a32fc30191cristy    Affine transform image.
295987feef6c1e3d08da3d9458fa5c464a32fc30191cristy  */
296e1c94d9d25db6b0dd7a5028ffee31d1057855d73cristy  assert(image->signature == MagickCoreSignature);
297987feef6c1e3d08da3d9458fa5c464a32fc30191cristy  if (image->debug != MagickFalse)
298987feef6c1e3d08da3d9458fa5c464a32fc30191cristy    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
299987feef6c1e3d08da3d9458fa5c464a32fc30191cristy  assert(affine_matrix != (AffineMatrix *) NULL);
300987feef6c1e3d08da3d9458fa5c464a32fc30191cristy  assert(exception != (ExceptionInfo *) NULL);
301e1c94d9d25db6b0dd7a5028ffee31d1057855d73cristy  assert(exception->signature == MagickCoreSignature);
302987feef6c1e3d08da3d9458fa5c464a32fc30191cristy  distort[0]=affine_matrix->sx;
303987feef6c1e3d08da3d9458fa5c464a32fc30191cristy  distort[1]=affine_matrix->rx;
304987feef6c1e3d08da3d9458fa5c464a32fc30191cristy  distort[2]=affine_matrix->ry;
305987feef6c1e3d08da3d9458fa5c464a32fc30191cristy  distort[3]=affine_matrix->sy;
306987feef6c1e3d08da3d9458fa5c464a32fc30191cristy  distort[4]=affine_matrix->tx;
307987feef6c1e3d08da3d9458fa5c464a32fc30191cristy  distort[5]=affine_matrix->ty;
308987feef6c1e3d08da3d9458fa5c464a32fc30191cristy  deskew_image=DistortImage(image,AffineProjectionDistortion,6,distort,
309987feef6c1e3d08da3d9458fa5c464a32fc30191cristy    MagickTrue,exception);
310987feef6c1e3d08da3d9458fa5c464a32fc30191cristy  return(deskew_image);
311987feef6c1e3d08da3d9458fa5c464a32fc30191cristy}
312987feef6c1e3d08da3d9458fa5c464a32fc30191cristy
313987feef6c1e3d08da3d9458fa5c464a32fc30191cristy/*
314987feef6c1e3d08da3d9458fa5c464a32fc30191cristy%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
315987feef6c1e3d08da3d9458fa5c464a32fc30191cristy%                                                                             %
316987feef6c1e3d08da3d9458fa5c464a32fc30191cristy%                                                                             %
317987feef6c1e3d08da3d9458fa5c464a32fc30191cristy%                                                                             %
3183ed852eea50f9d4cd633efb8c2b054b8e33c253cristy+   G e n e r a t e C o e f f i c i e n t s                                   %
3193ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%                                                                             %
3203ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%                                                                             %
3213ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%                                                                             %
3223ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3233ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%
3243ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%  GenerateCoefficients() takes user provided input arguments and generates
3253ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%  the coefficients, needed to apply the specific distortion for either
3263ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%  distorting images (generally using control points) or generating a color
3273ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%  gradient from sparsely separated color points.
3283ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%
3293ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%  The format of the GenerateCoefficients() method is:
3303ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%
331827944d54938c1f8a74ff53cd35c11801060d995dirk%    Image *GenerateCoefficients(const Image *image,DistortMethod method,
332bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy%        const size_t number_arguments,const double *arguments,
333bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy%        size_t number_values, ExceptionInfo *exception)
3343ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%
3353ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%  A description of each parameter follows:
3363ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%
3373ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%    o image: the image to be distorted.
3383ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%
3393ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%    o method: the method of image distortion/ sparse gradient
3403ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%
3413ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%    o number_arguments: the number of arguments given.
3423ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%
3433ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%    o arguments: the arguments for this distortion method.
3443ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%
3453ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%    o number_values: the style and format of given control points, (caller type)
3463ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%         0: 2 dimensional mapping of control points (Distort)
3473ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%            Format:  u,v,x,y  where u,v is the 'source' of the
3483ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%            the color to be plotted, for DistortImage()
3493ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%         N: Interpolation of control points with N values (usally r,g,b)
3503ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%            Format: x,y,r,g,b    mapping x,y to color values r,g,b
351972a6e66d79b58ffae5d7512b33c05dccc8f537cnicolas%            IN future, variable number of values may be given (1 to N)
3523ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%
3533ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%    o exception: return any errors or warnings in this structure
3543ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%
3553ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%  Note that the returned array of double values must be freed by the
3563ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%  calling method using RelinquishMagickMemory().  This however may change in
3573ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%  the future to require a more 'method' specific method.
3583ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%
3593ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%  Because of this this method should not be classed as stable or used
3603ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%  outside other MagickCore library methods.
3613ed852eea50f9d4cd633efb8c2b054b8e33c253cristy*/
3623ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
36372844f182b47d17dc2b4e9f1062ba434e5852fd6cristystatic inline double MagickRound(double x)
36472844f182b47d17dc2b4e9f1062ba434e5852fd6cristy{
36572844f182b47d17dc2b4e9f1062ba434e5852fd6cristy  /*
36672844f182b47d17dc2b4e9f1062ba434e5852fd6cristy    Round the fraction to nearest integer.
36772844f182b47d17dc2b4e9f1062ba434e5852fd6cristy  */
368ae0a3fcb5e88aba2468892d7710c322885d80fcdcristy  if ((x-floor(x)) < (ceil(x)-x))
36972844f182b47d17dc2b4e9f1062ba434e5852fd6cristy    return(floor(x));
37072844f182b47d17dc2b4e9f1062ba434e5852fd6cristy  return(ceil(x));
37172844f182b47d17dc2b4e9f1062ba434e5852fd6cristy}
37272844f182b47d17dc2b4e9f1062ba434e5852fd6cristy
3733ed852eea50f9d4cd633efb8c2b054b8e33c253cristystatic double *GenerateCoefficients(const Image *image,
374827944d54938c1f8a74ff53cd35c11801060d995dirk  DistortMethod *method,const size_t number_arguments,const double *arguments,
375827944d54938c1f8a74ff53cd35c11801060d995dirk  size_t number_values,ExceptionInfo *exception)
3763ed852eea50f9d4cd633efb8c2b054b8e33c253cristy{
3773ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  double
3783ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    *coeff;
3793ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
380bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  register size_t
3813ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    i;
3823ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
383bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  size_t
3843ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    number_coeff, /* number of coefficients to return (array size) */
3853ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    cp_size,      /* number floating point numbers per control point */
3863ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    cp_x,cp_y,    /* the x,y indexes for control point */
3873ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    cp_values;    /* index of values for this control point */
3883ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    /* number_values   Number of values given per control point */
3893ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
3903ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  if ( number_values == 0 ) {
3913ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    /* Image distortion using control points (or other distortion)
3923ed852eea50f9d4cd633efb8c2b054b8e33c253cristy       That is generate a mapping so that   x,y->u,v   given  u,v,x,y
3933ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    */
3943ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    number_values = 2;   /* special case: two values of u,v */
3953ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    cp_values = 0;       /* the values i,j are BEFORE the destination CP x,y */
3963ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    cp_x = 2;            /* location of x,y in input control values */
3973ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    cp_y = 3;
3983ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    /* NOTE: cp_values, also used for later 'reverse map distort' tests */
3993ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  }
4003ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  else {
4013ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    cp_x = 0;            /* location of x,y in input control values */
4023ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    cp_y = 1;
4033ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    cp_values = 2;       /* and the other values are after x,y */
4043ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    /* Typically in this case the values are R,G,B color values */
4053ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  }
4063ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  cp_size = number_values+2; /* each CP defintion involves this many numbers */
4073ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
4083ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  /* If not enough control point pairs are found for specific distortions
409972a6e66d79b58ffae5d7512b33c05dccc8f537cnicolas     fall back to Affine distortion (allowing 0 to 3 point pairs)
4103ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  */
4112857ffcd46fc4d091060c7e8aa9804f4b4d6efaacristy  if ( number_arguments < 4*cp_size &&
4123ed852eea50f9d4cd633efb8c2b054b8e33c253cristy       (  *method == BilinearForwardDistortion
4133ed852eea50f9d4cd633efb8c2b054b8e33c253cristy       || *method == BilinearReverseDistortion
4143ed852eea50f9d4cd633efb8c2b054b8e33c253cristy       || *method == PerspectiveDistortion
4153ed852eea50f9d4cd633efb8c2b054b8e33c253cristy       ) )
4163ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    *method = AffineDistortion;
4173ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
4183ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  number_coeff=0;
4193ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  switch (*method) {
4203ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case AffineDistortion:
4213ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    /* also BarycentricColorInterpolate: */
4223ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      number_coeff=3*number_values;
4233ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      break;
4243ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case PolynomialDistortion:
4253ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      /* number of coefficents depend on the given polynomal 'order' */
4263ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      i = poly_number_terms(arguments[0]);
4273ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      number_coeff = 2 + i*number_values;
4283ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      if ( i == 0 ) {
4293ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
4303ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                   "InvalidArgument","%s : '%s'","Polynomial",
43196b2d38e9e66b8ad2feba7a8a99bad94730efd19anthony                   "Invalid order, should be interger 1 to 5, or 1.5");
4323ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        return((double *) NULL);
4333ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      }
4343ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      if ( number_arguments < 1+i*cp_size ) {
4353ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
436e8c25f9b4c9fb72cad6db08eeda58c7c5784014ecristy               "InvalidArgument", "%s : 'require at least %.20g CPs'",
437e8c25f9b4c9fb72cad6db08eeda58c7c5784014ecristy               "Polynomial", (double) i);
4383ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        return((double *) NULL);
4393ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      }
4403ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      break;
4413ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case BilinearReverseDistortion:
4423ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      number_coeff=4*number_values;
4433ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      break;
4443ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    /*
4453ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      The rest are constants as they are only used for image distorts
4463ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    */
4473ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case BilinearForwardDistortion:
4485056b230558c7de0a79d51d82bdc929fef45a225anthony      number_coeff=10; /* 2*4 coeff plus 2 constants */
4495056b230558c7de0a79d51d82bdc929fef45a225anthony      cp_x = 0;        /* Reverse src/dest coords for forward mapping */
4503ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      cp_y = 1;
4513ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      cp_values = 2;
4523ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      break;
4535056b230558c7de0a79d51d82bdc929fef45a225anthony#if 0
4545056b230558c7de0a79d51d82bdc929fef45a225anthony    case QuadraterialDistortion:
4555056b230558c7de0a79d51d82bdc929fef45a225anthony      number_coeff=19; /* BilinearForward + BilinearReverse */
4565056b230558c7de0a79d51d82bdc929fef45a225anthony#endif
4575056b230558c7de0a79d51d82bdc929fef45a225anthony      break;
4583ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case ShepardsDistortion:
45947bb5df69322136901ccab9ad266d21ceff50500anthony      number_coeff=1;  /* The power factor to use */
4603ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      break;
4613ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case ArcDistortion:
4623ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      number_coeff=5;
4633ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      break;
4643ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case ScaleRotateTranslateDistortion:
4653ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case AffineProjectionDistortion:
466e0d9bbde3eb1826c767fee326a5786a9f28a191banthony    case Plane2CylinderDistortion:
467e0d9bbde3eb1826c767fee326a5786a9f28a191banthony    case Cylinder2PlaneDistortion:
4683ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      number_coeff=6;
4693ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      break;
4703ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case PolarDistortion:
4713ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case DePolarDistortion:
4723ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      number_coeff=8;
4733ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      break;
4743ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case PerspectiveDistortion:
4753ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case PerspectiveProjectionDistortion:
4763ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      number_coeff=9;
4773ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      break;
4783ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case BarrelDistortion:
4793ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case BarrelInverseDistortion:
4803ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      number_coeff=10;
4813ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      break;
4823ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    default:
483cf96e67439ab38056f2ec693bd0b0392c732cdf9cristy      perror("unknown method given"); /* just fail assertion */
4843ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  }
4853ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
4863ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  /* allocate the array of coefficients needed */
4873ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  coeff = (double *) AcquireQuantumMemory(number_coeff,sizeof(*coeff));
4883ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  if (coeff == (double *) NULL) {
4893ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    (void) ThrowMagickException(exception,GetMagickModule(),
4903ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                  ResourceLimitError,"MemoryAllocationFailed",
4913ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                  "%s", "GenerateCoefficients");
4923ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    return((double *) NULL);
4933ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  }
4943ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
4950c63ece53c43017de0184b16fe8cb6e5b58ad51bnicolas  /* zero out coefficients array */
4963ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  for (i=0; i < number_coeff; i++)
4973ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    coeff[i] = 0.0;
4983ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
4993ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  switch (*method)
5003ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  {
5013ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case AffineDistortion:
5023ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    {
5033ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      /* Affine Distortion
5043ed852eea50f9d4cd633efb8c2b054b8e33c253cristy           v =  c0*x + c1*y + c2
5053ed852eea50f9d4cd633efb8c2b054b8e33c253cristy         for each 'value' given
5063ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
5073ed852eea50f9d4cd633efb8c2b054b8e33c253cristy         Input Arguments are sets of control points...
5083ed852eea50f9d4cd633efb8c2b054b8e33c253cristy         For Distort Images    u,v, x,y  ...
5093ed852eea50f9d4cd633efb8c2b054b8e33c253cristy         For Sparse Gradients  x,y, r,g,b  ...
5103ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      */
5113ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      if ( number_arguments%cp_size != 0 ||
5123ed852eea50f9d4cd633efb8c2b054b8e33c253cristy           number_arguments < cp_size ) {
5133ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
514e8c25f9b4c9fb72cad6db08eeda58c7c5784014ecristy               "InvalidArgument", "%s : 'require at least %.20g CPs'",
515e8c25f9b4c9fb72cad6db08eeda58c7c5784014ecristy               "Affine", 1.0);
5163ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        coeff=(double *) RelinquishMagickMemory(coeff);
5173ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        return((double *) NULL);
5183ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      }
5193ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      /* handle special cases of not enough arguments */
5203ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      if ( number_arguments == cp_size ) {
5213ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        /* Only 1 CP Set Given */
5223ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        if ( cp_values == 0 ) {
5233ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          /* image distortion - translate the image */
5243ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          coeff[0] = 1.0;
5253ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          coeff[2] = arguments[0] - arguments[2];
5263ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          coeff[4] = 1.0;
5273ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          coeff[5] = arguments[1] - arguments[3];
5283ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        }
5293ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        else {
5303ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          /* sparse gradient - use the values directly */
5313ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          for (i=0; i<number_values; i++)
5323ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            coeff[i*3+2] = arguments[cp_values+i];
5333ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        }
5343ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      }
5353ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      else {
5363ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        /* 2 or more points (usally 3) given.
537972a6e66d79b58ffae5d7512b33c05dccc8f537cnicolas           Solve a least squares simultaneous equation for coefficients.
5383ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        */
5393ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        double
5403ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          **matrix,
5413ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          **vectors,
5423ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          terms[3];
5433ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
5443ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        MagickBooleanType
5453ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          status;
5463ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
5473ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        /* create matrix, and a fake vectors matrix */
5483ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        matrix = AcquireMagickMatrix(3UL,3UL);
5493ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        vectors = (double **) AcquireQuantumMemory(number_values,sizeof(*vectors));
5503ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        if (matrix == (double **) NULL || vectors == (double **) NULL)
5513ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        {
5523ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          matrix  = RelinquishMagickMatrix(matrix, 3UL);
5533ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          vectors = (double **) RelinquishMagickMemory(vectors);
5543ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          coeff   = (double *) RelinquishMagickMemory(coeff);
5553ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          (void) ThrowMagickException(exception,GetMagickModule(),
5563ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                  ResourceLimitError,"MemoryAllocationFailed",
5573ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                  "%s", "DistortCoefficients");
5583ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          return((double *) NULL);
5593ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        }
5603ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        /* fake a number_values x3 vectors matrix from coefficients array */
5613ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        for (i=0; i < number_values; i++)
5623ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          vectors[i] = &(coeff[i*3]);
5633ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        /* Add given control point pairs for least squares solving */
5643ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        for (i=0; i < number_arguments; i+=cp_size) {
5653ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          terms[0] = arguments[i+cp_x];  /* x */
5663ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          terms[1] = arguments[i+cp_y];  /* y */
5673ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          terms[2] = 1;                  /* 1 */
5683ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          LeastSquaresAddTerms(matrix,vectors,terms,
5693ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                   &(arguments[i+cp_values]),3UL,number_values);
5703ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        }
5713ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        if ( number_arguments == 2*cp_size ) {
5723ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          /* Only two pairs were given, but we need 3 to solve the affine.
5733ed852eea50f9d4cd633efb8c2b054b8e33c253cristy             Fake extra coordinates by rotating p1 around p0 by 90 degrees.
5743ed852eea50f9d4cd633efb8c2b054b8e33c253cristy               x2 = x0 - (y1-y0)   y2 = y0 + (x1-x0)
5753ed852eea50f9d4cd633efb8c2b054b8e33c253cristy           */
5763ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          terms[0] = arguments[cp_x]
5773ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                   - ( arguments[cp_size+cp_y] - arguments[cp_y] ); /* x2 */
5783ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          terms[1] = arguments[cp_y] +
5793ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                   + ( arguments[cp_size+cp_x] - arguments[cp_x] ); /* y2 */
5803ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          terms[2] = 1;                                             /* 1 */
5813ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          if ( cp_values == 0 ) {
5823ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            /* Image Distortion - rotate the u,v coordients too */
5833ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            double
5843ed852eea50f9d4cd633efb8c2b054b8e33c253cristy              uv2[2];
5853ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            uv2[0] = arguments[0] - arguments[5] + arguments[1];   /* u2 */
5863ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            uv2[1] = arguments[1] + arguments[4] - arguments[0];   /* v2 */
5873ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            LeastSquaresAddTerms(matrix,vectors,terms,uv2,3UL,2UL);
5883ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          }
5893ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          else {
5903ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            /* Sparse Gradient - use values of p0 for linear gradient */
5913ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            LeastSquaresAddTerms(matrix,vectors,terms,
5923ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                  &(arguments[cp_values]),3UL,number_values);
5933ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          }
5943ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        }
5953ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        /* Solve for LeastSquares Coefficients */
5963ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        status=GaussJordanElimination(matrix,vectors,3UL,number_values);
5973ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        matrix = RelinquishMagickMatrix(matrix, 3UL);
5983ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        vectors = (double **) RelinquishMagickMemory(vectors);
5993ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        if ( status == MagickFalse ) {
6003ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          coeff = (double *) RelinquishMagickMemory(coeff);
6013ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
6024cf1d89eb32104b1bed4aa71490bc91d4f6c0efeanthony              "InvalidArgument","%s : 'Unsolvable Matrix'",
603042ee78fa9004bf1ac6a95f09d9d1faca631dda1cristy              CommandOptionToMnemonic(MagickDistortOptions, *method) );
6043ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          return((double *) NULL);
6053ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        }
6063ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      }
6073ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      return(coeff);
6083ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    }
6093ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case AffineProjectionDistortion:
6103ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    {
6113ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      /*
6123ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        Arguments: Affine Matrix (forward mapping)
6133ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        Arguments  sx, rx, ry, sy, tx, ty
6143ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        Where      u = sx*x + ry*y + tx
6153ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                   v = rx*x + sy*y + ty
6163ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
6173ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        Returns coefficients (in there inverse form) ordered as...
6183ed852eea50f9d4cd633efb8c2b054b8e33c253cristy             sx ry tx  rx sy ty
6193ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
6203ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        AffineProjection Distortion Notes...
6213ed852eea50f9d4cd633efb8c2b054b8e33c253cristy           + Will only work with a 2 number_values for Image Distortion
6223ed852eea50f9d4cd633efb8c2b054b8e33c253cristy           + Can not be used for generating a sparse gradient (interpolation)
6233ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      */
6243ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      double inverse[8];
6253ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      if (number_arguments != 6) {
6263ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        coeff = (double *) RelinquishMagickMemory(coeff);
6273ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
6284cf1d89eb32104b1bed4aa71490bc91d4f6c0efeanthony              "InvalidArgument","%s : 'Needs 6 coeff values'",
629042ee78fa9004bf1ac6a95f09d9d1faca631dda1cristy              CommandOptionToMnemonic(MagickDistortOptions, *method) );
6303ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        return((double *) NULL);
6313ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      }
63263c023f25199125f673535000a4507d13a7ecac7nicolas      /* FUTURE: trap test for sx*sy-rx*ry == 0 (determinant = 0, no inverse) */
6333ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      for(i=0; i<6UL; i++ )
6343ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        inverse[i] = arguments[i];
6353ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      AffineArgsToCoefficients(inverse); /* map into coefficents */
6363ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      InvertAffineCoefficients(inverse, coeff); /* invert */
6373ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      *method = AffineDistortion;
6383ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
6393ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      return(coeff);
6403ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    }
6413ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case ScaleRotateTranslateDistortion:
6423ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    {
6433ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      /* Scale, Rotate and Translate Distortion
644972a6e66d79b58ffae5d7512b33c05dccc8f537cnicolas         An alternative Affine Distortion
6453ed852eea50f9d4cd633efb8c2b054b8e33c253cristy         Argument options, by number of arguments given:
6463ed852eea50f9d4cd633efb8c2b054b8e33c253cristy           7: x,y, sx,sy, a, nx,ny
6473ed852eea50f9d4cd633efb8c2b054b8e33c253cristy           6: x,y,   s,   a, nx,ny
6483ed852eea50f9d4cd633efb8c2b054b8e33c253cristy           5: x,y, sx,sy, a
6493ed852eea50f9d4cd633efb8c2b054b8e33c253cristy           4: x,y,   s,   a
6503ed852eea50f9d4cd633efb8c2b054b8e33c253cristy           3: x,y,        a
6513ed852eea50f9d4cd633efb8c2b054b8e33c253cristy           2:        s,   a
6523ed852eea50f9d4cd633efb8c2b054b8e33c253cristy           1:             a
6533ed852eea50f9d4cd633efb8c2b054b8e33c253cristy         Where actions are (in order of application)
6543ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            x,y     'center' of transforms     (default = image center)
6553ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            sx,sy   scale image by this amount (default = 1)
6563ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            a       angle of rotation          (argument required)
6575056b230558c7de0a79d51d82bdc929fef45a225anthony            nx,ny   move 'center' here         (default = x,y or no movement)
6583ed852eea50f9d4cd633efb8c2b054b8e33c253cristy         And convert to affine mapping coefficients
6593ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
6603ed852eea50f9d4cd633efb8c2b054b8e33c253cristy         ScaleRotateTranslate Distortion Notes...
6613ed852eea50f9d4cd633efb8c2b054b8e33c253cristy           + Does not use a set of CPs in any normal way
6623ed852eea50f9d4cd633efb8c2b054b8e33c253cristy           + Will only work with a 2 number_valuesal Image Distortion
663972a6e66d79b58ffae5d7512b33c05dccc8f537cnicolas           + Cannot be used for generating a sparse gradient (interpolation)
6643ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      */
6653ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      double
6663ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        cosine, sine,
6673ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        x,y,sx,sy,a,nx,ny;
6683ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
6693ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      /* set default center, and default scale */
6703ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      x = nx = (double)(image->columns)/2.0 + (double)image->page.x;
6713ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      y = ny = (double)(image->rows)/2.0    + (double)image->page.y;
6723ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      sx = sy = 1.0;
6733ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      switch ( number_arguments ) {
6743ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      case 0:
6753ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        coeff = (double *) RelinquishMagickMemory(coeff);
6763ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
6774cf1d89eb32104b1bed4aa71490bc91d4f6c0efeanthony              "InvalidArgument","%s : 'Needs at least 1 argument'",
678042ee78fa9004bf1ac6a95f09d9d1faca631dda1cristy              CommandOptionToMnemonic(MagickDistortOptions, *method) );
6793ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        return((double *) NULL);
6803ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      case 1:
6813ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        a = arguments[0];
6823ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        break;
6833ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      case 2:
6843ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        sx = sy = arguments[0];
6853ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        a = arguments[1];
6863ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        break;
6873ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      default:
6883ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        x = nx = arguments[0];
6893ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        y = ny = arguments[1];
6903ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        switch ( number_arguments ) {
6913ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        case 3:
6923ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          a = arguments[2];
6933ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          break;
6943ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        case 4:
6953ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          sx = sy = arguments[2];
6963ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          a = arguments[3];
6973ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          break;
6983ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        case 5:
6993ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          sx = arguments[2];
7003ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          sy = arguments[3];
7013ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          a = arguments[4];
7023ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          break;
7033ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        case 6:
7043ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          sx = sy = arguments[2];
7053ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          a = arguments[3];
7063ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          nx = arguments[4];
7073ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          ny = arguments[5];
7083ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          break;
7093ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        case 7:
7103ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          sx = arguments[2];
7113ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          sy = arguments[3];
7123ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          a = arguments[4];
7133ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          nx = arguments[5];
7143ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          ny = arguments[6];
7153ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          break;
7163ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        default:
7173ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          coeff = (double *) RelinquishMagickMemory(coeff);
7183ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
7194cf1d89eb32104b1bed4aa71490bc91d4f6c0efeanthony              "InvalidArgument","%s : 'Too Many Arguments (7 or less)'",
720042ee78fa9004bf1ac6a95f09d9d1faca631dda1cristy              CommandOptionToMnemonic(MagickDistortOptions, *method) );
7213ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          return((double *) NULL);
7223ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        }
7233ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        break;
7243ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      }
7253ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      /* Trap if sx or sy == 0 -- image is scaled out of existance! */
7263ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      if ( fabs(sx) < MagickEpsilon || fabs(sy) < MagickEpsilon ) {
7273ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        coeff = (double *) RelinquishMagickMemory(coeff);
7283ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
7294cf1d89eb32104b1bed4aa71490bc91d4f6c0efeanthony              "InvalidArgument","%s : 'Zero Scale Given'",
730042ee78fa9004bf1ac6a95f09d9d1faca631dda1cristy              CommandOptionToMnemonic(MagickDistortOptions, *method) );
7313ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        return((double *) NULL);
7323ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      }
7333ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      /* Save the given arguments as an affine distortion */
7343ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      a=DegreesToRadians(a); cosine=cos(a); sine=sin(a);
7353ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
7363ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      *method = AffineDistortion;
7373ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      coeff[0]=cosine/sx;
7383ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      coeff[1]=sine/sx;
7393ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      coeff[2]=x-nx*coeff[0]-ny*coeff[1];
7403ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      coeff[3]=(-sine)/sy;
7413ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      coeff[4]=cosine/sy;
7423ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      coeff[5]=y-nx*coeff[3]-ny*coeff[4];
7433ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      return(coeff);
7443ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    }
7453ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case PerspectiveDistortion:
7463ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    { /*
7473ed852eea50f9d4cd633efb8c2b054b8e33c253cristy         Perspective Distortion (a ratio of affine distortions)
7483ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
7493ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                p(x,y)    c0*x + c1*y + c2
7503ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            u = ------ = ------------------
7513ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                r(x,y)    c6*x + c7*y + 1
7523ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
7533ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                q(x,y)    c3*x + c4*y + c5
7543ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            v = ------ = ------------------
7553ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                r(x,y)    c6*x + c7*y + 1
7563ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
7573ed852eea50f9d4cd633efb8c2b054b8e33c253cristy           c8 = Sign of 'r', or the denominator affine, for the actual image.
7583ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                This determines what part of the distorted image is 'ground'
7593ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                side of the horizon, the other part is 'sky' or invalid.
7603ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                Valid values are  +1.0  or  -1.0  only.
7613ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
7623ed852eea50f9d4cd633efb8c2b054b8e33c253cristy         Input Arguments are sets of control points...
7633ed852eea50f9d4cd633efb8c2b054b8e33c253cristy         For Distort Images    u,v, x,y  ...
7643ed852eea50f9d4cd633efb8c2b054b8e33c253cristy         For Sparse Gradients  x,y, r,g,b  ...
7653ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
7663ed852eea50f9d4cd633efb8c2b054b8e33c253cristy         Perspective Distortion Notes...
7673ed852eea50f9d4cd633efb8c2b054b8e33c253cristy           + Can be thought of as ratio of  3 affine transformations
7683ed852eea50f9d4cd633efb8c2b054b8e33c253cristy           + Not separatable: r() or c6 and c7 are used by both equations
7693ed852eea50f9d4cd633efb8c2b054b8e33c253cristy           + All 8 coefficients must be determined simultaniously
7703ed852eea50f9d4cd633efb8c2b054b8e33c253cristy           + Will only work with a 2 number_valuesal Image Distortion
7713ed852eea50f9d4cd633efb8c2b054b8e33c253cristy           + Can not be used for generating a sparse gradient (interpolation)
7723ed852eea50f9d4cd633efb8c2b054b8e33c253cristy           + It is not linear, but is simple to generate an inverse
7733ed852eea50f9d4cd633efb8c2b054b8e33c253cristy           + All lines within an image remain lines.
7743ed852eea50f9d4cd633efb8c2b054b8e33c253cristy           + but distances between points may vary.
7753ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      */
7763ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      double
7773ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        **matrix,
7783ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        *vectors[1],
7793ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        terms[8];
7803ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
781bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      size_t
7823ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        cp_u = cp_values,
7833ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        cp_v = cp_values+1;
7843ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
7853ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      MagickBooleanType
7863ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        status;
7873ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
78896b2d38e9e66b8ad2feba7a8a99bad94730efd19anthony      if ( number_arguments%cp_size != 0 ||
78996b2d38e9e66b8ad2feba7a8a99bad94730efd19anthony           number_arguments < cp_size*4 ) {
79096b2d38e9e66b8ad2feba7a8a99bad94730efd19anthony        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
791e8c25f9b4c9fb72cad6db08eeda58c7c5784014ecristy              "InvalidArgument", "%s : 'require at least %.20g CPs'",
792042ee78fa9004bf1ac6a95f09d9d1faca631dda1cristy              CommandOptionToMnemonic(MagickDistortOptions, *method), 4.0);
79396b2d38e9e66b8ad2feba7a8a99bad94730efd19anthony        coeff=(double *) RelinquishMagickMemory(coeff);
79496b2d38e9e66b8ad2feba7a8a99bad94730efd19anthony        return((double *) NULL);
79596b2d38e9e66b8ad2feba7a8a99bad94730efd19anthony      }
7963ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      /* fake 1x8 vectors matrix directly using the coefficients array */
7973ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      vectors[0] = &(coeff[0]);
7983ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      /* 8x8 least-squares matrix (zeroed) */
7993ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      matrix = AcquireMagickMatrix(8UL,8UL);
8003ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      if (matrix == (double **) NULL) {
8013ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        (void) ThrowMagickException(exception,GetMagickModule(),
8023ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                  ResourceLimitError,"MemoryAllocationFailed",
8033ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                  "%s", "DistortCoefficients");
8043ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        return((double *) NULL);
8053ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      }
8063ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      /* Add control points for least squares solving */
8073ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      for (i=0; i < number_arguments; i+=4) {
8083ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        terms[0]=arguments[i+cp_x];            /*   c0*x   */
8093ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        terms[1]=arguments[i+cp_y];            /*   c1*y   */
8103ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        terms[2]=1.0;                          /*   c2*1   */
8113ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        terms[3]=0.0;
8123ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        terms[4]=0.0;
8133ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        terms[5]=0.0;
8143ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        terms[6]=-terms[0]*arguments[i+cp_u];  /* 1/(c6*x) */
8153ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        terms[7]=-terms[1]*arguments[i+cp_u];  /* 1/(c7*y) */
8163ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        LeastSquaresAddTerms(matrix,vectors,terms,&(arguments[i+cp_u]),
8173ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            8UL,1UL);
8183ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
8193ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        terms[0]=0.0;
8203ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        terms[1]=0.0;
8213ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        terms[2]=0.0;
8223ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        terms[3]=arguments[i+cp_x];           /*   c3*x   */
8233ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        terms[4]=arguments[i+cp_y];           /*   c4*y   */
8243ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        terms[5]=1.0;                         /*   c5*1   */
8253ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        terms[6]=-terms[3]*arguments[i+cp_v]; /* 1/(c6*x) */
8263ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        terms[7]=-terms[4]*arguments[i+cp_v]; /* 1/(c7*y) */
8273ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        LeastSquaresAddTerms(matrix,vectors,terms,&(arguments[i+cp_v]),
8283ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            8UL,1UL);
8293ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      }
8303ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      /* Solve for LeastSquares Coefficients */
8313ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      status=GaussJordanElimination(matrix,vectors,8UL,1UL);
8323ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      matrix = RelinquishMagickMatrix(matrix, 8UL);
8333ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      if ( status == MagickFalse ) {
8343ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        coeff = (double *) RelinquishMagickMemory(coeff);
8353ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
8364cf1d89eb32104b1bed4aa71490bc91d4f6c0efeanthony            "InvalidArgument","%s : 'Unsolvable Matrix'",
837042ee78fa9004bf1ac6a95f09d9d1faca631dda1cristy            CommandOptionToMnemonic(MagickDistortOptions, *method) );
8383ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        return((double *) NULL);
8393ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      }
8403ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      /*
8413ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        Calculate 9'th coefficient! The ground-sky determination.
8423ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        What is sign of the 'ground' in r() denominator affine function?
8433ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        Just use any valid image coordinate (first control point) in
8443ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        destination for determination of what part of view is 'ground'.
8453ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      */
8463ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      coeff[8] = coeff[6]*arguments[cp_x]
8473ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                      + coeff[7]*arguments[cp_y] + 1.0;
8483ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      coeff[8] = (coeff[8] < 0.0) ? -1.0 : +1.0;
8493ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
8503ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      return(coeff);
8513ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    }
8523ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case PerspectiveProjectionDistortion:
8533ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    {
8543ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      /*
8553ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        Arguments: Perspective Coefficents (forward mapping)
8563ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      */
8573ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      if (number_arguments != 8) {
8583ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
8594cf1d89eb32104b1bed4aa71490bc91d4f6c0efeanthony              "InvalidArgument", "%s : 'Needs 8 coefficient values'",
860042ee78fa9004bf1ac6a95f09d9d1faca631dda1cristy              CommandOptionToMnemonic(MagickDistortOptions, *method));
8613ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        return((double *) NULL);
8623ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      }
8633ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      /* FUTURE: trap test  c0*c4-c3*c1 == 0  (determinate = 0, no inverse) */
8643ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      InvertPerspectiveCoefficients(arguments, coeff);
8653ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      /*
8663ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        Calculate 9'th coefficient! The ground-sky determination.
8673ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        What is sign of the 'ground' in r() denominator affine function?
8683ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        Just use any valid image cocodinate in destination for determination.
8693ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        For a forward mapped perspective the images 0,0 coord will map to
8703ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        c2,c5 in the distorted image, so set the sign of denominator of that.
8713ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      */
8723ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      coeff[8] = coeff[6]*arguments[2]
8733ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                           + coeff[7]*arguments[5] + 1.0;
8743ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      coeff[8] = (coeff[8] < 0.0) ? -1.0 : +1.0;
8753ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      *method = PerspectiveDistortion;
8763ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
8773ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      return(coeff);
8783ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    }
8793ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case BilinearForwardDistortion:
8803ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case BilinearReverseDistortion:
8813ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    {
8825056b230558c7de0a79d51d82bdc929fef45a225anthony      /* Bilinear Distortion (Forward mapping)
8833ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            v = c0*x + c1*y + c2*x*y + c3;
8843ed852eea50f9d4cd633efb8c2b054b8e33c253cristy         for each 'value' given
8853ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
8865056b230558c7de0a79d51d82bdc929fef45a225anthony         This is actually a simple polynomial Distortion!  The difference
8875056b230558c7de0a79d51d82bdc929fef45a225anthony         however is when we need to reverse the above equation to generate a
8885056b230558c7de0a79d51d82bdc929fef45a225anthony         BilinearForwardDistortion (see below).
8895056b230558c7de0a79d51d82bdc929fef45a225anthony
8903ed852eea50f9d4cd633efb8c2b054b8e33c253cristy         Input Arguments are sets of control points...
8913ed852eea50f9d4cd633efb8c2b054b8e33c253cristy         For Distort Images    u,v, x,y  ...
8923ed852eea50f9d4cd633efb8c2b054b8e33c253cristy         For Sparse Gradients  x,y, r,g,b  ...
8933ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
8943ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      */
8953ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      double
8963ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        **matrix,
8973ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        **vectors,
8983ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        terms[4];
8993ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
9003ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      MagickBooleanType
9013ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        status;
9023ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
90396b2d38e9e66b8ad2feba7a8a99bad94730efd19anthony      /* check the number of arguments */
90496b2d38e9e66b8ad2feba7a8a99bad94730efd19anthony      if ( number_arguments%cp_size != 0 ||
90596b2d38e9e66b8ad2feba7a8a99bad94730efd19anthony           number_arguments < cp_size*4 ) {
90696b2d38e9e66b8ad2feba7a8a99bad94730efd19anthony        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
907e8c25f9b4c9fb72cad6db08eeda58c7c5784014ecristy              "InvalidArgument", "%s : 'require at least %.20g CPs'",
908042ee78fa9004bf1ac6a95f09d9d1faca631dda1cristy              CommandOptionToMnemonic(MagickDistortOptions, *method), 4.0);
90996b2d38e9e66b8ad2feba7a8a99bad94730efd19anthony        coeff=(double *) RelinquishMagickMemory(coeff);
91096b2d38e9e66b8ad2feba7a8a99bad94730efd19anthony        return((double *) NULL);
91196b2d38e9e66b8ad2feba7a8a99bad94730efd19anthony      }
9123ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      /* create matrix, and a fake vectors matrix */
9133ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      matrix = AcquireMagickMatrix(4UL,4UL);
9143ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      vectors = (double **) AcquireQuantumMemory(number_values,sizeof(*vectors));
9153ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      if (matrix == (double **) NULL || vectors == (double **) NULL)
9163ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      {
9173ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        matrix  = RelinquishMagickMatrix(matrix, 4UL);
9183ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        vectors = (double **) RelinquishMagickMemory(vectors);
9193ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        coeff   = (double *) RelinquishMagickMemory(coeff);
9203ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        (void) ThrowMagickException(exception,GetMagickModule(),
9213ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                ResourceLimitError,"MemoryAllocationFailed",
9223ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                "%s", "DistortCoefficients");
9233ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        return((double *) NULL);
9243ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      }
9253ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      /* fake a number_values x4 vectors matrix from coefficients array */
9263ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      for (i=0; i < number_values; i++)
9273ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        vectors[i] = &(coeff[i*4]);
9283ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      /* Add given control point pairs for least squares solving */
9293ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      for (i=0; i < number_arguments; i+=cp_size) {
9303ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        terms[0] = arguments[i+cp_x];   /*  x  */
9313ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        terms[1] = arguments[i+cp_y];   /*  y  */
9323ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        terms[2] = terms[0]*terms[1];   /* x*y */
9333ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        terms[3] = 1;                   /*  1  */
9343ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        LeastSquaresAddTerms(matrix,vectors,terms,
9353ed852eea50f9d4cd633efb8c2b054b8e33c253cristy             &(arguments[i+cp_values]),4UL,number_values);
9363ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      }
9373ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      /* Solve for LeastSquares Coefficients */
9383ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      status=GaussJordanElimination(matrix,vectors,4UL,number_values);
9393ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      matrix  = RelinquishMagickMatrix(matrix, 4UL);
9403ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      vectors = (double **) RelinquishMagickMemory(vectors);
9413ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      if ( status == MagickFalse ) {
9423ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        coeff = (double *) RelinquishMagickMemory(coeff);
9433ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
9444cf1d89eb32104b1bed4aa71490bc91d4f6c0efeanthony            "InvalidArgument","%s : 'Unsolvable Matrix'",
945042ee78fa9004bf1ac6a95f09d9d1faca631dda1cristy            CommandOptionToMnemonic(MagickDistortOptions, *method) );
9463ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        return((double *) NULL);
9473ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      }
9483ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      if ( *method == BilinearForwardDistortion ) {
9493ed852eea50f9d4cd633efb8c2b054b8e33c253cristy         /* Bilinear Forward Mapped Distortion
9503ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
9513ed852eea50f9d4cd633efb8c2b054b8e33c253cristy         The above least-squares solved for coefficents but in the forward
9523ed852eea50f9d4cd633efb8c2b054b8e33c253cristy         direction, due to changes to indexing constants.
9533ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
9543ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            i = c0*x + c1*y + c2*x*y + c3;
9553ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            j = c4*x + c5*y + c6*x*y + c7;
9563ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
9574cf1d89eb32104b1bed4aa71490bc91d4f6c0efeanthony         where i,j are in the destination image, NOT the source.
9583ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
9595056b230558c7de0a79d51d82bdc929fef45a225anthony         Reverse Pixel mapping however needs to use reverse of these
9605056b230558c7de0a79d51d82bdc929fef45a225anthony         functions.  It required a full page of algbra to work out the
9615056b230558c7de0a79d51d82bdc929fef45a225anthony         reversed mapping formula, but resolves down to the following...
9623ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
9633ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            c8 = c0*c5-c1*c4;
9645056b230558c7de0a79d51d82bdc929fef45a225anthony            c9 = 2*(c2*c5-c1*c6);   // '2*a' in the quadratic formula
9653ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
9663ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            i = i - c3;   j = j - c7;
9675056b230558c7de0a79d51d82bdc929fef45a225anthony            b = c6*i - c2*j + c8;   // So that   a*y^2 + b*y + c == 0
9685056b230558c7de0a79d51d82bdc929fef45a225anthony            c = c4*i -  c0*j;       // y = ( -b +- sqrt(bb - 4ac) ) / (2*a)
9695056b230558c7de0a79d51d82bdc929fef45a225anthony
9705056b230558c7de0a79d51d82bdc929fef45a225anthony            r = b*b - c9*(c+c);
9715056b230558c7de0a79d51d82bdc929fef45a225anthony            if ( c9 != 0 )
9725056b230558c7de0a79d51d82bdc929fef45a225anthony              y = ( -b + sqrt(r) ) / c9;
9735056b230558c7de0a79d51d82bdc929fef45a225anthony            else
9745056b230558c7de0a79d51d82bdc929fef45a225anthony              y = -c/b;
9753ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
9763ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            x = ( i - c1*y) / ( c1 - c2*y );
9773ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
9783ed852eea50f9d4cd633efb8c2b054b8e33c253cristy         NB: if 'r' is negative there is no solution!
9793ed852eea50f9d4cd633efb8c2b054b8e33c253cristy         NB: the sign of the sqrt() should be negative if image becomes
9805056b230558c7de0a79d51d82bdc929fef45a225anthony             flipped or flopped, or crosses over itself.
9815056b230558c7de0a79d51d82bdc929fef45a225anthony         NB: techniqually coefficient c5 is not needed, anymore,
9825056b230558c7de0a79d51d82bdc929fef45a225anthony             but kept for completness.
9833ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
9845056b230558c7de0a79d51d82bdc929fef45a225anthony         See Anthony Thyssen <A.Thyssen@griffith.edu.au>
9855056b230558c7de0a79d51d82bdc929fef45a225anthony         or  Fred Weinhaus <fmw@alink.net>  for more details.
9863ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
9873ed852eea50f9d4cd633efb8c2b054b8e33c253cristy         */
9883ed852eea50f9d4cd633efb8c2b054b8e33c253cristy         coeff[8] = coeff[0]*coeff[5] - coeff[1]*coeff[4];
9895056b230558c7de0a79d51d82bdc929fef45a225anthony         coeff[9] = 2*(coeff[2]*coeff[5] - coeff[1]*coeff[6]);
9903ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      }
9913ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      return(coeff);
9923ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    }
9935056b230558c7de0a79d51d82bdc929fef45a225anthony#if 0
9945056b230558c7de0a79d51d82bdc929fef45a225anthony    case QuadrilateralDistortion:
9955056b230558c7de0a79d51d82bdc929fef45a225anthony    {
9965056b230558c7de0a79d51d82bdc929fef45a225anthony      /* Map a Quadrilateral to a unit square using BilinearReverse
9975056b230558c7de0a79d51d82bdc929fef45a225anthony         Then map that unit square back to the final Quadrilateral
9985056b230558c7de0a79d51d82bdc929fef45a225anthony         using BilinearForward.
9995056b230558c7de0a79d51d82bdc929fef45a225anthony
10005056b230558c7de0a79d51d82bdc929fef45a225anthony         Input Arguments are sets of control points...
10015056b230558c7de0a79d51d82bdc929fef45a225anthony         For Distort Images    u,v, x,y  ...
10025056b230558c7de0a79d51d82bdc929fef45a225anthony         For Sparse Gradients  x,y, r,g,b  ...
10035056b230558c7de0a79d51d82bdc929fef45a225anthony
10045056b230558c7de0a79d51d82bdc929fef45a225anthony      */
10055056b230558c7de0a79d51d82bdc929fef45a225anthony      /* UNDER CONSTRUCTION */
10065056b230558c7de0a79d51d82bdc929fef45a225anthony      return(coeff);
10075056b230558c7de0a79d51d82bdc929fef45a225anthony    }
10085056b230558c7de0a79d51d82bdc929fef45a225anthony#endif
10095056b230558c7de0a79d51d82bdc929fef45a225anthony
10103ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case PolynomialDistortion:
10113ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    {
10123ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      /* Polynomial Distortion
10133ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
10143ed852eea50f9d4cd633efb8c2b054b8e33c253cristy         First two coefficents are used to hole global polynomal information
10153ed852eea50f9d4cd633efb8c2b054b8e33c253cristy           c0 = Order of the polynimial being created
10163ed852eea50f9d4cd633efb8c2b054b8e33c253cristy           c1 = number_of_terms in one polynomial equation
10173ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
10183ed852eea50f9d4cd633efb8c2b054b8e33c253cristy         Rest of the coefficients map to the equations....
10193ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            v = c0 + c1*x + c2*y + c3*x*y + c4*x^2 + c5*y^2 + c6*x^3 + ...
10203ed852eea50f9d4cd633efb8c2b054b8e33c253cristy         for each 'value' (number_values of them) given.
10213ed852eea50f9d4cd633efb8c2b054b8e33c253cristy         As such total coefficients =  2 + number_terms * number_values
10223ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
10233ed852eea50f9d4cd633efb8c2b054b8e33c253cristy         Input Arguments are sets of control points...
10243ed852eea50f9d4cd633efb8c2b054b8e33c253cristy         For Distort Images    order  [u,v, x,y] ...
10253ed852eea50f9d4cd633efb8c2b054b8e33c253cristy         For Sparse Gradients  order  [x,y, r,g,b] ...
10263ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
10273ed852eea50f9d4cd633efb8c2b054b8e33c253cristy         Polynomial Distortion Notes...
10283ed852eea50f9d4cd633efb8c2b054b8e33c253cristy           + UNDER DEVELOPMENT -- Do not expect this to remain as is.
10293ed852eea50f9d4cd633efb8c2b054b8e33c253cristy           + Currently polynomial is a reversed mapped distortion.
10303ed852eea50f9d4cd633efb8c2b054b8e33c253cristy           + Order 1.5 is fudged to map into a bilinear distortion.
10315056b230558c7de0a79d51d82bdc929fef45a225anthony             though it is not the same order as that distortion.
10323ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      */
10333ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      double
10343ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        **matrix,
10353ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        **vectors,
10363ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        *terms;
10373ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
1038bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      size_t
10393ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        nterms;   /* number of polynomial terms per number_values */
10403ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
1041bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      register ssize_t
10423ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        j;
10433ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
10443ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      MagickBooleanType
10453ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        status;
10463ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
10473ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      /* first two coefficients hold polynomial order information */
10483ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      coeff[0] = arguments[0];
10493ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      coeff[1] = (double) poly_number_terms(arguments[0]);
1050bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      nterms = (size_t) coeff[1];
10513ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
10523ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      /* create matrix, a fake vectors matrix, and least sqs terms */
10533ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      matrix = AcquireMagickMatrix(nterms,nterms);
10543ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      vectors = (double **) AcquireQuantumMemory(number_values,sizeof(*vectors));
10553ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      terms = (double *) AcquireQuantumMemory(nterms, sizeof(*terms));
10563ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      if (matrix  == (double **) NULL ||
10573ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          vectors == (double **) NULL ||
10583ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          terms   == (double *) NULL )
10593ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      {
10603ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        matrix  = RelinquishMagickMatrix(matrix, nterms);
10613ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        vectors = (double **) RelinquishMagickMemory(vectors);
10623ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        terms   = (double *) RelinquishMagickMemory(terms);
10633ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        coeff   = (double *) RelinquishMagickMemory(coeff);
10643ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        (void) ThrowMagickException(exception,GetMagickModule(),
10653ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                ResourceLimitError,"MemoryAllocationFailed",
10663ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                "%s", "DistortCoefficients");
10673ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        return((double *) NULL);
10683ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      }
10693ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      /* fake a number_values x3 vectors matrix from coefficients array */
10703ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      for (i=0; i < number_values; i++)
10713ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        vectors[i] = &(coeff[2+i*nterms]);
10723ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      /* Add given control point pairs for least squares solving */
107396b2d38e9e66b8ad2feba7a8a99bad94730efd19anthony      for (i=1; i < number_arguments; i+=cp_size) { /* NB: start = 1 not 0 */
1074bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy        for (j=0; j < (ssize_t) nterms; j++)
10753ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          terms[j] = poly_basis_fn(j,arguments[i+cp_x],arguments[i+cp_y]);
10763ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        LeastSquaresAddTerms(matrix,vectors,terms,
10773ed852eea50f9d4cd633efb8c2b054b8e33c253cristy             &(arguments[i+cp_values]),nterms,number_values);
10783ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      }
10793ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      terms = (double *) RelinquishMagickMemory(terms);
10803ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      /* Solve for LeastSquares Coefficients */
10813ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      status=GaussJordanElimination(matrix,vectors,nterms,number_values);
10823ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      matrix  = RelinquishMagickMatrix(matrix, nterms);
10833ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      vectors = (double **) RelinquishMagickMemory(vectors);
10843ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      if ( status == MagickFalse ) {
10853ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        coeff = (double *) RelinquishMagickMemory(coeff);
10863ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
10874cf1d89eb32104b1bed4aa71490bc91d4f6c0efeanthony            "InvalidArgument","%s : 'Unsolvable Matrix'",
1088042ee78fa9004bf1ac6a95f09d9d1faca631dda1cristy            CommandOptionToMnemonic(MagickDistortOptions, *method) );
10893ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        return((double *) NULL);
10903ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      }
10913ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      return(coeff);
10923ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    }
10933ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case ArcDistortion:
10943ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    {
10953ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      /* Arc Distortion
10963ed852eea50f9d4cd633efb8c2b054b8e33c253cristy         Args: arc_width  rotate  top_edge_radius  bottom_edge_radius
10973ed852eea50f9d4cd633efb8c2b054b8e33c253cristy         All but first argument are optional
10983ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            arc_width      The angle over which to arc the image side-to-side
10993ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            rotate         Angle to rotate image from vertical center
11003ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            top_radius     Set top edge of source image at this radius
11013ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            bottom_radius  Set bootom edge to this radius (radial scaling)
11023ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
11033ed852eea50f9d4cd633efb8c2b054b8e33c253cristy         By default, if the radii arguments are nor provided the image radius
11043ed852eea50f9d4cd633efb8c2b054b8e33c253cristy         is calculated so the horizontal center-line is fits the given arc
11053ed852eea50f9d4cd633efb8c2b054b8e33c253cristy         without scaling.
11063ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
11073ed852eea50f9d4cd633efb8c2b054b8e33c253cristy         The output image size is ALWAYS adjusted to contain the whole image,
11083ed852eea50f9d4cd633efb8c2b054b8e33c253cristy         and an offset is given to position image relative to the 0,0 point of
11093ed852eea50f9d4cd633efb8c2b054b8e33c253cristy         the origin, allowing users to use relative positioning onto larger
11103ed852eea50f9d4cd633efb8c2b054b8e33c253cristy         background (via -flatten).
11113ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
11123ed852eea50f9d4cd633efb8c2b054b8e33c253cristy         The arguments are converted to these coefficients
11133ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            c0: angle for center of source image
11143ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            c1: angle scale for mapping to source image
11153ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            c2: radius for top of source image
11163ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            c3: radius scale for mapping source image
11173ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            c4: centerline of arc within source image
11183ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
11193ed852eea50f9d4cd633efb8c2b054b8e33c253cristy         Note the coefficients use a center angle, so asymptotic join is
11203ed852eea50f9d4cd633efb8c2b054b8e33c253cristy         furthest from both sides of the source image. This also means that
11213ed852eea50f9d4cd633efb8c2b054b8e33c253cristy         for arc angles greater than 360 the sides of the image will be
11223ed852eea50f9d4cd633efb8c2b054b8e33c253cristy         trimmed equally.
11233ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
11243ed852eea50f9d4cd633efb8c2b054b8e33c253cristy         Arc Distortion Notes...
11253ed852eea50f9d4cd633efb8c2b054b8e33c253cristy           + Does not use a set of CPs
11263ed852eea50f9d4cd633efb8c2b054b8e33c253cristy           + Will only work with Image Distortion
11273ed852eea50f9d4cd633efb8c2b054b8e33c253cristy           + Can not be used for generating a sparse gradient (interpolation)
11283ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      */
11293ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      if ( number_arguments >= 1 && arguments[0] < MagickEpsilon ) {
11303ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        coeff = (double *) RelinquishMagickMemory(coeff);
11313ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
11324cf1d89eb32104b1bed4aa71490bc91d4f6c0efeanthony            "InvalidArgument","%s : 'Arc Angle Too Small'",
1133042ee78fa9004bf1ac6a95f09d9d1faca631dda1cristy            CommandOptionToMnemonic(MagickDistortOptions, *method) );
11343ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        return((double *) NULL);
11353ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      }
11363ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      if ( number_arguments >= 3 && arguments[2] < MagickEpsilon ) {
11373ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        coeff = (double *) RelinquishMagickMemory(coeff);
11383ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
11394cf1d89eb32104b1bed4aa71490bc91d4f6c0efeanthony            "InvalidArgument","%s : 'Outer Radius Too Small'",
1140042ee78fa9004bf1ac6a95f09d9d1faca631dda1cristy            CommandOptionToMnemonic(MagickDistortOptions, *method) );
11413ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        return((double *) NULL);
11423ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      }
11433ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      coeff[0] = -MagickPI2;   /* -90, place at top! */
11443ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      if ( number_arguments >= 1 )
11453ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        coeff[1] = DegreesToRadians(arguments[0]);
11463ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      else
11473ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        coeff[1] = MagickPI2;   /* zero arguments - center is at top */
11483ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      if ( number_arguments >= 2 )
11493ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        coeff[0] += DegreesToRadians(arguments[1]);
11503ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      coeff[0] /= Magick2PI;  /* normalize radians */
11513ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      coeff[0] -= MagickRound(coeff[0]);
11523ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      coeff[0] *= Magick2PI;  /* de-normalize back to radians */
11533ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      coeff[3] = (double)image->rows-1;
11543ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      coeff[2] = (double)image->columns/coeff[1] + coeff[3]/2.0;
11553ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      if ( number_arguments >= 3 ) {
11563ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        if ( number_arguments >= 4 )
11573ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          coeff[3] = arguments[2] - arguments[3];
11583ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        else
11593ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          coeff[3] *= arguments[2]/coeff[2];
11603ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        coeff[2] = arguments[2];
11613ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      }
11623ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      coeff[4] = ((double)image->columns-1.0)/2.0;
11633ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
11643ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      return(coeff);
11653ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    }
11663ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case PolarDistortion:
11673ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case DePolarDistortion:
11683ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    {
11693ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      /* (De)Polar Distortion   (same set of arguments)
11703ed852eea50f9d4cd633efb8c2b054b8e33c253cristy         Args:  Rmax, Rmin,  Xcenter,Ycenter,  Afrom,Ato
11713ed852eea50f9d4cd633efb8c2b054b8e33c253cristy         DePolar can also have the extra arguments of Width, Height
11723ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
11733ed852eea50f9d4cd633efb8c2b054b8e33c253cristy         Coefficients 0 to 5 is the sanatized version first 6 input args
11743ed852eea50f9d4cd633efb8c2b054b8e33c253cristy         Coefficient 6  is the angle to coord ratio  and visa-versa
11753ed852eea50f9d4cd633efb8c2b054b8e33c253cristy         Coefficient 7  is the radius to coord ratio and visa-versa
11763ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
1177dcabf6d5fddb35c0eedd7a7638c102f16838c4e6glennrp         WARNING: It is possible for  Radius max<min  and/or  Angle from>to
11783ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      */
11793ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      if ( number_arguments == 3
11803ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          || ( number_arguments > 6 && *method == PolarDistortion )
11813ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          || number_arguments > 8 ) {
11824cf1d89eb32104b1bed4aa71490bc91d4f6c0efeanthony          (void) ThrowMagickException(exception,GetMagickModule(),
11834cf1d89eb32104b1bed4aa71490bc91d4f6c0efeanthony            OptionError,"InvalidArgument", "%s : number of arguments",
1184042ee78fa9004bf1ac6a95f09d9d1faca631dda1cristy            CommandOptionToMnemonic(MagickDistortOptions, *method) );
11853ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        coeff=(double *) RelinquishMagickMemory(coeff);
11863ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        return((double *) NULL);
11873ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      }
11883ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      /* Rmax -  if 0 calculate appropriate value */
11893ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      if ( number_arguments >= 1 )
11903ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        coeff[0] = arguments[0];
11913ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      else
11923ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        coeff[0] = 0.0;
11933ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      /* Rmin  - usally 0 */
11943ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      coeff[1] = number_arguments >= 2 ? arguments[1] : 0.0;
11953ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      /* Center X,Y */
11963ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      if ( number_arguments >= 4 ) {
11973ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        coeff[2] = arguments[2];
11983ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        coeff[3] = arguments[3];
11993ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      }
12003ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      else { /* center of actual image */
12013ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        coeff[2] = (double)(image->columns)/2.0+image->page.x;
12023ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        coeff[3] = (double)(image->rows)/2.0+image->page.y;
12033ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      }
12043ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      /* Angle from,to - about polar center 0 is downward */
12053ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      coeff[4] = -MagickPI;
12063ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      if ( number_arguments >= 5 )
12073ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        coeff[4] = DegreesToRadians(arguments[4]);
12083ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      coeff[5] = coeff[4];
12093ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      if ( number_arguments >= 6 )
12103ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        coeff[5] = DegreesToRadians(arguments[5]);
12113ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      if ( fabs(coeff[4]-coeff[5]) < MagickEpsilon )
12123ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        coeff[5] += Magick2PI; /* same angle is a full circle */
12133ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      /* if radius 0 or negative,  its a special value... */
12143ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      if ( coeff[0] < MagickEpsilon ) {
12153ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        /* Use closest edge  if radius == 0 */
12163ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        if ( fabs(coeff[0]) < MagickEpsilon ) {
12173ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          coeff[0]=MagickMin(fabs(coeff[2]-image->page.x),
12183ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                             fabs(coeff[3]-image->page.y));
12193ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          coeff[0]=MagickMin(coeff[0],
12203ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                       fabs(coeff[2]-image->page.x-image->columns));
12213ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          coeff[0]=MagickMin(coeff[0],
12223ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                       fabs(coeff[3]-image->page.y-image->rows));
12233ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        }
12243ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        /* furthest diagonal if radius == -1 */
12253ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        if ( fabs(-1.0-coeff[0]) < MagickEpsilon ) {
12263ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          double rx,ry;
12273ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          rx = coeff[2]-image->page.x;
12283ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          ry = coeff[3]-image->page.y;
12293ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          coeff[0] = rx*rx+ry*ry;
12303ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          ry = coeff[3]-image->page.y-image->rows;
12313ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          coeff[0] = MagickMax(coeff[0],rx*rx+ry*ry);
12323ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          rx = coeff[2]-image->page.x-image->columns;
12333ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          coeff[0] = MagickMax(coeff[0],rx*rx+ry*ry);
12343ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          ry = coeff[3]-image->page.y;
12353ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          coeff[0] = MagickMax(coeff[0],rx*rx+ry*ry);
12363ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          coeff[0] = sqrt(coeff[0]);
12373ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        }
12383ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      }
12393ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      /* IF Rmax <= 0 or Rmin < 0 OR Rmax < Rmin, THEN error */
12403ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      if ( coeff[0] < MagickEpsilon || coeff[1] < -MagickEpsilon
12413ed852eea50f9d4cd633efb8c2b054b8e33c253cristy           || (coeff[0]-coeff[1]) < MagickEpsilon ) {
12423ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
12434cf1d89eb32104b1bed4aa71490bc91d4f6c0efeanthony            "InvalidArgument", "%s : Invalid Radius",
1244042ee78fa9004bf1ac6a95f09d9d1faca631dda1cristy            CommandOptionToMnemonic(MagickDistortOptions, *method) );
12453ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        coeff=(double *) RelinquishMagickMemory(coeff);
12463ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        return((double *) NULL);
12473ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      }
12483ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      /* converstion ratios */
12493ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      if ( *method == PolarDistortion ) {
12503ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        coeff[6]=(double) image->columns/(coeff[5]-coeff[4]);
12513ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        coeff[7]=(double) image->rows/(coeff[0]-coeff[1]);
12523ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      }
12533ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      else { /* *method == DePolarDistortion */
12543ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        coeff[6]=(coeff[5]-coeff[4])/image->columns;
12553ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        coeff[7]=(coeff[0]-coeff[1])/image->rows;
12563ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      }
12573ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      return(coeff);
12583ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    }
1259e0d9bbde3eb1826c767fee326a5786a9f28a191banthony    case Cylinder2PlaneDistortion:
1260e0d9bbde3eb1826c767fee326a5786a9f28a191banthony    case Plane2CylinderDistortion:
1261b9aaf9ed2a83b7d8931306fb3313285098072b25anthony    {
1262b9aaf9ed2a83b7d8931306fb3313285098072b25anthony      /* 3D Cylinder to/from a Tangential Plane
1263b9aaf9ed2a83b7d8931306fb3313285098072b25anthony
1264b9aaf9ed2a83b7d8931306fb3313285098072b25anthony         Projection between a clinder and flat plain from a point on the
1265b9aaf9ed2a83b7d8931306fb3313285098072b25anthony         center line of the cylinder.
1266b9aaf9ed2a83b7d8931306fb3313285098072b25anthony
1267b9aaf9ed2a83b7d8931306fb3313285098072b25anthony         The two surfaces coincide in 3D space at the given centers of
1268b9aaf9ed2a83b7d8931306fb3313285098072b25anthony         distortion (perpendicular to projection point) on both images.
1269b9aaf9ed2a83b7d8931306fb3313285098072b25anthony
1270b9aaf9ed2a83b7d8931306fb3313285098072b25anthony         Args:  FOV_arc_width
1271b9aaf9ed2a83b7d8931306fb3313285098072b25anthony         Coefficents: FOV(radians), Radius, center_x,y, dest_center_x,y
1272b9aaf9ed2a83b7d8931306fb3313285098072b25anthony
1273b9aaf9ed2a83b7d8931306fb3313285098072b25anthony         FOV (Field Of View) the angular field of view of the distortion,
1274b9aaf9ed2a83b7d8931306fb3313285098072b25anthony         across the width of the image, in degrees.  The centers are the
1275b9aaf9ed2a83b7d8931306fb3313285098072b25anthony         points of least distortion in the input and resulting images.
1276b9aaf9ed2a83b7d8931306fb3313285098072b25anthony
1277b9aaf9ed2a83b7d8931306fb3313285098072b25anthony         These centers are however determined later.
1278b9aaf9ed2a83b7d8931306fb3313285098072b25anthony
1279b9aaf9ed2a83b7d8931306fb3313285098072b25anthony         Coeff 0 is the FOV angle of view of image width in radians
1280b9aaf9ed2a83b7d8931306fb3313285098072b25anthony         Coeff 1 is calculated radius of cylinder.
1281b9aaf9ed2a83b7d8931306fb3313285098072b25anthony         Coeff 2,3  center of distortion of input image
1282b9aaf9ed2a83b7d8931306fb3313285098072b25anthony         Coefficents 4,5 Center of Distortion of dest (determined later)
1283b9aaf9ed2a83b7d8931306fb3313285098072b25anthony      */
1284b9aaf9ed2a83b7d8931306fb3313285098072b25anthony      if ( arguments[0] < MagickEpsilon || arguments[0] > 160.0 ) {
1285b9aaf9ed2a83b7d8931306fb3313285098072b25anthony        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1286b9aaf9ed2a83b7d8931306fb3313285098072b25anthony            "InvalidArgument", "%s : Invalid FOV Angle",
1287b9aaf9ed2a83b7d8931306fb3313285098072b25anthony            CommandOptionToMnemonic(MagickDistortOptions, *method) );
1288b9aaf9ed2a83b7d8931306fb3313285098072b25anthony        coeff=(double *) RelinquishMagickMemory(coeff);
1289b9aaf9ed2a83b7d8931306fb3313285098072b25anthony        return((double *) NULL);
1290b9aaf9ed2a83b7d8931306fb3313285098072b25anthony      }
1291b9aaf9ed2a83b7d8931306fb3313285098072b25anthony      coeff[0] = DegreesToRadians(arguments[0]);
1292e0d9bbde3eb1826c767fee326a5786a9f28a191banthony      if ( *method == Cylinder2PlaneDistortion )
1293b9aaf9ed2a83b7d8931306fb3313285098072b25anthony        /* image is curved around cylinder, so FOV angle (in radians)
1294b9aaf9ed2a83b7d8931306fb3313285098072b25anthony         * scales directly to image X coordinate, according to its radius.
1295b9aaf9ed2a83b7d8931306fb3313285098072b25anthony         */
12964c08aed51c5899665ade97263692328eea4af106cristy        coeff[1] = (double) image->columns/coeff[0];
1297b9aaf9ed2a83b7d8931306fb3313285098072b25anthony      else
1298b9aaf9ed2a83b7d8931306fb3313285098072b25anthony        /* radius is distance away from an image with this angular FOV */
12994c08aed51c5899665ade97263692328eea4af106cristy        coeff[1] = (double) image->columns / ( 2 * tan(coeff[0]/2) );
1300b9aaf9ed2a83b7d8931306fb3313285098072b25anthony
1301b9aaf9ed2a83b7d8931306fb3313285098072b25anthony      coeff[2] = (double)(image->columns)/2.0+image->page.x;
1302b9aaf9ed2a83b7d8931306fb3313285098072b25anthony      coeff[3] = (double)(image->rows)/2.0+image->page.y;
1303b9aaf9ed2a83b7d8931306fb3313285098072b25anthony      coeff[4] = coeff[2];
1304b9aaf9ed2a83b7d8931306fb3313285098072b25anthony      coeff[5] = coeff[3]; /* assuming image size is the same */
1305b9aaf9ed2a83b7d8931306fb3313285098072b25anthony      return(coeff);
1306b9aaf9ed2a83b7d8931306fb3313285098072b25anthony    }
13073ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case BarrelDistortion:
13083ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case BarrelInverseDistortion:
13093ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    {
13103ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      /* Barrel Distortion
13113ed852eea50f9d4cd633efb8c2b054b8e33c253cristy           Rs=(A*Rd^3 + B*Rd^2 + C*Rd + D)*Rd
13123ed852eea50f9d4cd633efb8c2b054b8e33c253cristy         BarrelInv Distortion
13133ed852eea50f9d4cd633efb8c2b054b8e33c253cristy           Rs=Rd/(A*Rd^3 + B*Rd^2 + C*Rd + D)
13143ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
13153ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        Where Rd is the normalized radius from corner to middle of image
1316ec40aca22bbc75266cdfdc60dd1fc8ebb8174139anthony        Input Arguments are one of the following forms (number of arguments)...
1317ec40aca22bbc75266cdfdc60dd1fc8ebb8174139anthony            3:  A,B,C
1318ec40aca22bbc75266cdfdc60dd1fc8ebb8174139anthony            4:  A,B,C,D
1319ec40aca22bbc75266cdfdc60dd1fc8ebb8174139anthony            5:  A,B,C    X,Y
1320ec40aca22bbc75266cdfdc60dd1fc8ebb8174139anthony            6:  A,B,C,D  X,Y
1321ec40aca22bbc75266cdfdc60dd1fc8ebb8174139anthony            8:  Ax,Bx,Cx,Dx  Ay,By,Cy,Dy
1322ec40aca22bbc75266cdfdc60dd1fc8ebb8174139anthony           10:  Ax,Bx,Cx,Dx  Ay,By,Cy,Dy   X,Y
13233ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
13243ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        Returns 10 coefficent values, which are de-normalized (pixel scale)
13253ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          Ax, Bx, Cx, Dx,   Ay, By, Cy, Dy,    Xc, Yc
13263ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      */
13273ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      /* Radius de-normalization scaling factor */
13283ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      double
13293ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        rscale = 2.0/MagickMin((double) image->columns,(double) image->rows);
13303ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
1331ec40aca22bbc75266cdfdc60dd1fc8ebb8174139anthony      /* sanity check  number of args must = 3,4,5,6,8,10 or error */
1332607c6f1c496ac78fbb0b66b00f47490b20222e66anthony      if ( (number_arguments  < 3) || (number_arguments == 7) ||
1333607c6f1c496ac78fbb0b66b00f47490b20222e66anthony           (number_arguments == 9) || (number_arguments > 10) )
13342857ffcd46fc4d091060c7e8aa9804f4b4d6efaacristy        {
13354cf1d89eb32104b1bed4aa71490bc91d4f6c0efeanthony          coeff=(double *) RelinquishMagickMemory(coeff);
13364cf1d89eb32104b1bed4aa71490bc91d4f6c0efeanthony          (void) ThrowMagickException(exception,GetMagickModule(),
13374cf1d89eb32104b1bed4aa71490bc91d4f6c0efeanthony            OptionError,"InvalidArgument", "%s : number of arguments",
1338042ee78fa9004bf1ac6a95f09d9d1faca631dda1cristy            CommandOptionToMnemonic(MagickDistortOptions, *method) );
13394cf1d89eb32104b1bed4aa71490bc91d4f6c0efeanthony          return((double *) NULL);
13402857ffcd46fc4d091060c7e8aa9804f4b4d6efaacristy        }
1341ec40aca22bbc75266cdfdc60dd1fc8ebb8174139anthony      /* A,B,C,D coefficients */
1342ec40aca22bbc75266cdfdc60dd1fc8ebb8174139anthony      coeff[0] = arguments[0];
1343ec40aca22bbc75266cdfdc60dd1fc8ebb8174139anthony      coeff[1] = arguments[1];
1344ec40aca22bbc75266cdfdc60dd1fc8ebb8174139anthony      coeff[2] = arguments[2];
1345ec40aca22bbc75266cdfdc60dd1fc8ebb8174139anthony      if ((number_arguments == 3) || (number_arguments == 5) )
1346ec40aca22bbc75266cdfdc60dd1fc8ebb8174139anthony        coeff[3] = 1.0 - coeff[0] - coeff[1] - coeff[2];
1347ec40aca22bbc75266cdfdc60dd1fc8ebb8174139anthony      else
1348ec40aca22bbc75266cdfdc60dd1fc8ebb8174139anthony        coeff[3] = arguments[3];
1349ec40aca22bbc75266cdfdc60dd1fc8ebb8174139anthony      /* de-normalize the coefficients */
13503ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      coeff[0] *= pow(rscale,3.0);
13513ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      coeff[1] *= rscale*rscale;
13523ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      coeff[2] *= rscale;
1353ec40aca22bbc75266cdfdc60dd1fc8ebb8174139anthony      /* Y coefficients: as given OR same as X coefficients */
13543ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      if ( number_arguments >= 8 ) {
13553ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        coeff[4] = arguments[4] * pow(rscale,3.0);
13563ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        coeff[5] = arguments[5] * rscale*rscale;
13573ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        coeff[6] = arguments[6] * rscale;
13583ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        coeff[7] = arguments[7];
13593ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      }
13603ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      else {
13613ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        coeff[4] = coeff[0];
13623ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        coeff[5] = coeff[1];
13633ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        coeff[6] = coeff[2];
13643ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        coeff[7] = coeff[3];
13653ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      }
1366ec40aca22bbc75266cdfdc60dd1fc8ebb8174139anthony      /* X,Y Center of Distortion (image coodinates) */
13673ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      if ( number_arguments == 5 )  {
13683ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        coeff[8] = arguments[3];
13693ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        coeff[9] = arguments[4];
13703ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      }
13714cf1d89eb32104b1bed4aa71490bc91d4f6c0efeanthony      else if ( number_arguments == 6 ) {
13723ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        coeff[8] = arguments[4];
13733ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        coeff[9] = arguments[5];
13743ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      }
13754cf1d89eb32104b1bed4aa71490bc91d4f6c0efeanthony      else if ( number_arguments == 10 ) {
13763ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        coeff[8] = arguments[8];
13773ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        coeff[9] = arguments[9];
13783ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      }
13794cf1d89eb32104b1bed4aa71490bc91d4f6c0efeanthony      else {
1380ec40aca22bbc75266cdfdc60dd1fc8ebb8174139anthony        /* center of the image provided (image coodinates) */
1381ec40aca22bbc75266cdfdc60dd1fc8ebb8174139anthony        coeff[8] = (double)image->columns/2.0 + image->page.x;
1382ec40aca22bbc75266cdfdc60dd1fc8ebb8174139anthony        coeff[9] = (double)image->rows/2.0    + image->page.y;
13834cf1d89eb32104b1bed4aa71490bc91d4f6c0efeanthony      }
13843ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      return(coeff);
13853ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    }
13863ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    case ShepardsDistortion:
13873ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    {
13883ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      /* Shepards Distortion  input arguments are the coefficents!
13893ed852eea50f9d4cd633efb8c2b054b8e33c253cristy         Just check the number of arguments is valid!
13903ed852eea50f9d4cd633efb8c2b054b8e33c253cristy         Args:  u1,v1, x1,y1, ...
13913ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          OR :  u1,v1, r1,g1,c1, ...
13923ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      */
13933ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      if ( number_arguments%cp_size != 0 ||
13943ed852eea50f9d4cd633efb8c2b054b8e33c253cristy           number_arguments < cp_size ) {
13953ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
139647bb5df69322136901ccab9ad266d21ceff50500anthony              "InvalidArgument", "%s : 'requires CP's (4 numbers each)'",
139747bb5df69322136901ccab9ad266d21ceff50500anthony              CommandOptionToMnemonic(MagickDistortOptions, *method));
13983ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        coeff=(double *) RelinquishMagickMemory(coeff);
13993ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        return((double *) NULL);
14003ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      }
140147bb5df69322136901ccab9ad266d21ceff50500anthony      /* User defined weighting power for Shepard's Method */
140247bb5df69322136901ccab9ad266d21ceff50500anthony      { const char *artifact=GetImageArtifact(image,"shepards:power");
140347bb5df69322136901ccab9ad266d21ceff50500anthony        if ( artifact != (const char *) NULL ) {
140447bb5df69322136901ccab9ad266d21ceff50500anthony          coeff[0]=StringToDouble(artifact,(char **) NULL) / 2.0;
140547bb5df69322136901ccab9ad266d21ceff50500anthony          if ( coeff[0] < MagickEpsilon ) {
140647bb5df69322136901ccab9ad266d21ceff50500anthony            (void) ThrowMagickException(exception,GetMagickModule(),
140747bb5df69322136901ccab9ad266d21ceff50500anthony                OptionError,"InvalidArgument","%s", "-define shepards:power" );
140847bb5df69322136901ccab9ad266d21ceff50500anthony            coeff=(double *) RelinquishMagickMemory(coeff);
140947bb5df69322136901ccab9ad266d21ceff50500anthony            return((double *) NULL);
141047bb5df69322136901ccab9ad266d21ceff50500anthony          }
141147bb5df69322136901ccab9ad266d21ceff50500anthony        }
141247bb5df69322136901ccab9ad266d21ceff50500anthony        else
141347bb5df69322136901ccab9ad266d21ceff50500anthony          coeff[0]=1.0;  /* Default power of 2 (Inverse Squared) */
141447bb5df69322136901ccab9ad266d21ceff50500anthony      }
14153ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      return(coeff);
14163ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    }
14173ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    default:
14183ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      break;
14193ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  }
14203ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  /* you should never reach this point */
1421cf96e67439ab38056f2ec693bd0b0392c732cdf9cristy  perror("no method handler"); /* just fail assertion */
14223ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  return((double *) NULL);
14233ed852eea50f9d4cd633efb8c2b054b8e33c253cristy}
14243ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
14253ed852eea50f9d4cd633efb8c2b054b8e33c253cristy/*
14263ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14273ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%                                                                             %
14283ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%                                                                             %
14293ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%                                                                             %
14302029ddc50e8860c787be44f997cbb57e457024baanthony+   D i s t o r t R e s i z e I m a g e                                       %
14312029ddc50e8860c787be44f997cbb57e457024baanthony%                                                                             %
14322029ddc50e8860c787be44f997cbb57e457024baanthony%                                                                             %
14332029ddc50e8860c787be44f997cbb57e457024baanthony%                                                                             %
14342029ddc50e8860c787be44f997cbb57e457024baanthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14352029ddc50e8860c787be44f997cbb57e457024baanthony%
1436f0a92fd8deb68d411304359906b12679b675691fglennrp%  DistortResizeImage() resize image using the equivalent but slower image
14372029ddc50e8860c787be44f997cbb57e457024baanthony%  distortion operator.  The filter is applied using a EWA cylindrical
14382029ddc50e8860c787be44f997cbb57e457024baanthony%  resampling. But like resize the final image size is limited to whole pixels
14392029ddc50e8860c787be44f997cbb57e457024baanthony%  with no effects by virtual-pixels on the result.
14402029ddc50e8860c787be44f997cbb57e457024baanthony%
14412029ddc50e8860c787be44f997cbb57e457024baanthony%  Note that images containing a transparency channel will be twice as slow to
14422029ddc50e8860c787be44f997cbb57e457024baanthony%  resize as images one without transparency.
14432029ddc50e8860c787be44f997cbb57e457024baanthony%
14442029ddc50e8860c787be44f997cbb57e457024baanthony%  The format of the DistortResizeImage method is:
14452029ddc50e8860c787be44f997cbb57e457024baanthony%
14468306a8d2a4724da6e922153284f5910e4149b421Cristy%      Image *DistortResizeImage(const Image *image,const size_t columns,
14472029ddc50e8860c787be44f997cbb57e457024baanthony%        const size_t rows,ExceptionInfo *exception)
14482029ddc50e8860c787be44f997cbb57e457024baanthony%
14492029ddc50e8860c787be44f997cbb57e457024baanthony%  A description of each parameter follows:
14502029ddc50e8860c787be44f997cbb57e457024baanthony%
14512029ddc50e8860c787be44f997cbb57e457024baanthony%    o image: the image.
14522029ddc50e8860c787be44f997cbb57e457024baanthony%
14532029ddc50e8860c787be44f997cbb57e457024baanthony%    o columns: the number of columns in the resized image.
14542029ddc50e8860c787be44f997cbb57e457024baanthony%
14552029ddc50e8860c787be44f997cbb57e457024baanthony%    o rows: the number of rows in the resized image.
14562029ddc50e8860c787be44f997cbb57e457024baanthony%
14572029ddc50e8860c787be44f997cbb57e457024baanthony%    o exception: return any errors or warnings in this structure.
14582029ddc50e8860c787be44f997cbb57e457024baanthony%
14592029ddc50e8860c787be44f997cbb57e457024baanthony*/
14602029ddc50e8860c787be44f997cbb57e457024baanthonyMagickExport Image *DistortResizeImage(const Image *image,
14612029ddc50e8860c787be44f997cbb57e457024baanthony  const size_t columns,const size_t rows,ExceptionInfo *exception)
14622029ddc50e8860c787be44f997cbb57e457024baanthony{
14632029ddc50e8860c787be44f997cbb57e457024baanthony#define DistortResizeImageTag  "Distort/Image"
14642029ddc50e8860c787be44f997cbb57e457024baanthony
14652029ddc50e8860c787be44f997cbb57e457024baanthony  Image
14662029ddc50e8860c787be44f997cbb57e457024baanthony    *resize_image,
14672029ddc50e8860c787be44f997cbb57e457024baanthony    *tmp_image;
14682029ddc50e8860c787be44f997cbb57e457024baanthony
14692029ddc50e8860c787be44f997cbb57e457024baanthony  RectangleInfo
14702029ddc50e8860c787be44f997cbb57e457024baanthony    crop_area;
14712029ddc50e8860c787be44f997cbb57e457024baanthony
147247bb5df69322136901ccab9ad266d21ceff50500anthony  double
147347bb5df69322136901ccab9ad266d21ceff50500anthony    distort_args[12];
147447bb5df69322136901ccab9ad266d21ceff50500anthony
14752029ddc50e8860c787be44f997cbb57e457024baanthony  VirtualPixelMethod
14762029ddc50e8860c787be44f997cbb57e457024baanthony    vp_save;
14772029ddc50e8860c787be44f997cbb57e457024baanthony
14782029ddc50e8860c787be44f997cbb57e457024baanthony  /*
14792029ddc50e8860c787be44f997cbb57e457024baanthony    Distort resize image.
14802029ddc50e8860c787be44f997cbb57e457024baanthony  */
14812029ddc50e8860c787be44f997cbb57e457024baanthony  assert(image != (const Image *) NULL);
1482e1c94d9d25db6b0dd7a5028ffee31d1057855d73cristy  assert(image->signature == MagickCoreSignature);
14832029ddc50e8860c787be44f997cbb57e457024baanthony  if (image->debug != MagickFalse)
14842029ddc50e8860c787be44f997cbb57e457024baanthony    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
14852029ddc50e8860c787be44f997cbb57e457024baanthony  assert(exception != (ExceptionInfo *) NULL);
1486e1c94d9d25db6b0dd7a5028ffee31d1057855d73cristy  assert(exception->signature == MagickCoreSignature);
14872029ddc50e8860c787be44f997cbb57e457024baanthony  if ((columns == 0) || (rows == 0))
14882029ddc50e8860c787be44f997cbb57e457024baanthony    return((Image *) NULL);
14892029ddc50e8860c787be44f997cbb57e457024baanthony  /* Do not short-circuit this resize if final image size is unchanged */
14902029ddc50e8860c787be44f997cbb57e457024baanthony
14912029ddc50e8860c787be44f997cbb57e457024baanthony  (void) ResetMagickMemory(distort_args,0,12*sizeof(double));
14923fcaccdcc85f3e5c5a2ece7309dfe4792a9b0269cristy  distort_args[4]=(double) image->columns;
14933fcaccdcc85f3e5c5a2ece7309dfe4792a9b0269cristy  distort_args[6]=(double) columns;
14943fcaccdcc85f3e5c5a2ece7309dfe4792a9b0269cristy  distort_args[9]=(double) image->rows;
14953fcaccdcc85f3e5c5a2ece7309dfe4792a9b0269cristy  distort_args[11]=(double) rows;
14962029ddc50e8860c787be44f997cbb57e457024baanthony
14972029ddc50e8860c787be44f997cbb57e457024baanthony  vp_save=GetImageVirtualPixelMethod(image);
14982029ddc50e8860c787be44f997cbb57e457024baanthony
14992029ddc50e8860c787be44f997cbb57e457024baanthony  tmp_image=CloneImage(image,0,0,MagickTrue,exception);
15002029ddc50e8860c787be44f997cbb57e457024baanthony  if ( tmp_image == (Image *) NULL )
15012029ddc50e8860c787be44f997cbb57e457024baanthony    return((Image *) NULL);
1502387430f111ba7864990ca03fbc89968e20074ecdcristy  (void) SetImageVirtualPixelMethod(tmp_image,TransparentVirtualPixelMethod,
1503387430f111ba7864990ca03fbc89968e20074ecdcristy    exception);
15042029ddc50e8860c787be44f997cbb57e457024baanthony
150517f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy  if (image->alpha_trait == UndefinedPixelTrait)
15062029ddc50e8860c787be44f997cbb57e457024baanthony    {
15072029ddc50e8860c787be44f997cbb57e457024baanthony      /*
15082029ddc50e8860c787be44f997cbb57e457024baanthony        Image has not transparency channel, so we free to use it
15092029ddc50e8860c787be44f997cbb57e457024baanthony      */
151063240888c3975789a09c2494a4654b523931df96cristy      (void) SetImageAlphaChannel(tmp_image,SetAlphaChannel,exception);
15112029ddc50e8860c787be44f997cbb57e457024baanthony      resize_image=DistortImage(tmp_image,AffineDistortion,12,distort_args,
151263240888c3975789a09c2494a4654b523931df96cristy        MagickTrue,exception),
15132029ddc50e8860c787be44f997cbb57e457024baanthony
15142029ddc50e8860c787be44f997cbb57e457024baanthony      tmp_image=DestroyImage(tmp_image);
15152029ddc50e8860c787be44f997cbb57e457024baanthony      if ( resize_image == (Image *) NULL )
15162029ddc50e8860c787be44f997cbb57e457024baanthony        return((Image *) NULL);
15172029ddc50e8860c787be44f997cbb57e457024baanthony
15188306a8d2a4724da6e922153284f5910e4149b421Cristy      (void) SetImageAlphaChannel(resize_image,DeactivateAlphaChannel,
15198306a8d2a4724da6e922153284f5910e4149b421Cristy        exception);
15202029ddc50e8860c787be44f997cbb57e457024baanthony    }
15212029ddc50e8860c787be44f997cbb57e457024baanthony  else
15222029ddc50e8860c787be44f997cbb57e457024baanthony    {
15232029ddc50e8860c787be44f997cbb57e457024baanthony      /*
15242029ddc50e8860c787be44f997cbb57e457024baanthony        Image has transparency so handle colors and alpha separatly.
15252029ddc50e8860c787be44f997cbb57e457024baanthony        Basically we need to separate Virtual-Pixel alpha in the resized
15262029ddc50e8860c787be44f997cbb57e457024baanthony        image, so only the actual original images alpha channel is used.
15272029ddc50e8860c787be44f997cbb57e457024baanthony
1528bd5a96cd2b69f218f85a7adc306296a736f91a56cristy        distort alpha channel separately
1529bd5a96cd2b69f218f85a7adc306296a736f91a56cristy      */
15307a4d610ce2d062b39a7aa86c65bfcbf0a9cb29d5anthony      Image
15317a4d610ce2d062b39a7aa86c65bfcbf0a9cb29d5anthony        *resize_alpha;
15327a4d610ce2d062b39a7aa86c65bfcbf0a9cb29d5anthony
1533c8d636784204aa4d970c212b156bffc387836b93cristy      (void) SetImageAlphaChannel(tmp_image,ExtractAlphaChannel,exception);
153463240888c3975789a09c2494a4654b523931df96cristy      (void) SetImageAlphaChannel(tmp_image,OpaqueAlphaChannel,exception);
15352029ddc50e8860c787be44f997cbb57e457024baanthony      resize_alpha=DistortImage(tmp_image,AffineDistortion,12,distort_args,
15363139dc2f5df8d442e308b381992a0f8579e061e4cristy        MagickTrue,exception),
15372029ddc50e8860c787be44f997cbb57e457024baanthony      tmp_image=DestroyImage(tmp_image);
15383139dc2f5df8d442e308b381992a0f8579e061e4cristy      if (resize_alpha == (Image *) NULL)
15392029ddc50e8860c787be44f997cbb57e457024baanthony        return((Image *) NULL);
15402029ddc50e8860c787be44f997cbb57e457024baanthony
15412029ddc50e8860c787be44f997cbb57e457024baanthony      /* distort the actual image containing alpha + VP alpha */
15422029ddc50e8860c787be44f997cbb57e457024baanthony      tmp_image=CloneImage(image,0,0,MagickTrue,exception);
15432029ddc50e8860c787be44f997cbb57e457024baanthony      if ( tmp_image == (Image *) NULL )
15442029ddc50e8860c787be44f997cbb57e457024baanthony        return((Image *) NULL);
154536421ee45722da418b8ab99d7e6358e4f86b9f1eCristy      (void) SetImageVirtualPixelMethod(tmp_image,TransparentVirtualPixelMethod,        exception);
15462029ddc50e8860c787be44f997cbb57e457024baanthony      resize_image=DistortImage(tmp_image,AffineDistortion,12,distort_args,
15473139dc2f5df8d442e308b381992a0f8579e061e4cristy        MagickTrue,exception),
15482029ddc50e8860c787be44f997cbb57e457024baanthony      tmp_image=DestroyImage(tmp_image);
15492029ddc50e8860c787be44f997cbb57e457024baanthony      if ( resize_image == (Image *) NULL)
15502029ddc50e8860c787be44f997cbb57e457024baanthony        {
15512029ddc50e8860c787be44f997cbb57e457024baanthony          resize_alpha=DestroyImage(resize_alpha);
15522029ddc50e8860c787be44f997cbb57e457024baanthony          return((Image *) NULL);
15532029ddc50e8860c787be44f997cbb57e457024baanthony        }
15542029ddc50e8860c787be44f997cbb57e457024baanthony      /* replace resize images alpha with the separally distorted alpha */
15558306a8d2a4724da6e922153284f5910e4149b421Cristy      (void) SetImageAlphaChannel(resize_image,OffAlphaChannel,exception);
15568306a8d2a4724da6e922153284f5910e4149b421Cristy      (void) SetImageAlphaChannel(resize_alpha,OffAlphaChannel,exception);
1557feb3e9695150978a5d2372d3fe2f60466a7c8066cristy      (void) CompositeImage(resize_image,resize_alpha,CopyAlphaCompositeOp,
155839172408bad7ef2ef00a815fa9abf9979e7857cbcristy        MagickTrue,0,0,exception);
15592029ddc50e8860c787be44f997cbb57e457024baanthony      resize_alpha=DestroyImage(resize_alpha);
15602029ddc50e8860c787be44f997cbb57e457024baanthony    }
1561387430f111ba7864990ca03fbc89968e20074ecdcristy  (void) SetImageVirtualPixelMethod(resize_image,vp_save,exception);
15622029ddc50e8860c787be44f997cbb57e457024baanthony
15632029ddc50e8860c787be44f997cbb57e457024baanthony  /*
15642029ddc50e8860c787be44f997cbb57e457024baanthony    Clean up the results of the Distortion
15652029ddc50e8860c787be44f997cbb57e457024baanthony  */
15662029ddc50e8860c787be44f997cbb57e457024baanthony  crop_area.width=columns;
15672029ddc50e8860c787be44f997cbb57e457024baanthony  crop_area.height=rows;
15682029ddc50e8860c787be44f997cbb57e457024baanthony  crop_area.x=0;
15692029ddc50e8860c787be44f997cbb57e457024baanthony  crop_area.y=0;
15702029ddc50e8860c787be44f997cbb57e457024baanthony
15712029ddc50e8860c787be44f997cbb57e457024baanthony  tmp_image=resize_image;
15722029ddc50e8860c787be44f997cbb57e457024baanthony  resize_image=CropImage(tmp_image,&crop_area,exception);
15732029ddc50e8860c787be44f997cbb57e457024baanthony  tmp_image=DestroyImage(tmp_image);
15741ca877c27bc1cab1f355b2bf67dffafa97cfc396Cristy  if (resize_image != (Image *) NULL)
15751ca877c27bc1cab1f355b2bf67dffafa97cfc396Cristy    {
15761ca877c27bc1cab1f355b2bf67dffafa97cfc396Cristy      resize_image->alpha_trait=image->alpha_trait;
15771ca877c27bc1cab1f355b2bf67dffafa97cfc396Cristy      resize_image->compose=image->compose;
15781ca877c27bc1cab1f355b2bf67dffafa97cfc396Cristy      resize_image->page.width=0;
15791ca877c27bc1cab1f355b2bf67dffafa97cfc396Cristy      resize_image->page.height=0;
15801ca877c27bc1cab1f355b2bf67dffafa97cfc396Cristy    }
15812029ddc50e8860c787be44f997cbb57e457024baanthony  return(resize_image);
15822029ddc50e8860c787be44f997cbb57e457024baanthony}
15832029ddc50e8860c787be44f997cbb57e457024baanthony
15842029ddc50e8860c787be44f997cbb57e457024baanthony/*
15852029ddc50e8860c787be44f997cbb57e457024baanthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
15862029ddc50e8860c787be44f997cbb57e457024baanthony%                                                                             %
15872029ddc50e8860c787be44f997cbb57e457024baanthony%                                                                             %
15882029ddc50e8860c787be44f997cbb57e457024baanthony%                                                                             %
15893ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%   D i s t o r t I m a g e                                                   %
15903ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%                                                                             %
15913ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%                                                                             %
15923ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%                                                                             %
15933ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
15943ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%
15953ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%  DistortImage() distorts an image using various distortion methods, by
15963ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%  mapping color lookups of the source image to a new destination image
15973ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%  usally of the same size as the source image, unless 'bestfit' is set to
15983ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%  true.
15993ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%
16003ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%  If 'bestfit' is enabled, and distortion allows it, the destination image is
16013ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%  adjusted to ensure the whole source 'image' will just fit within the final
16023ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%  destination image, which will be sized and offset accordingly.  Also in
16033ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%  many cases the virtual offset of the source image will be taken into
16043ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%  account in the mapping.
16053ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%
16063ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%  If the '-verbose' control option has been set print to standard error the
16073ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%  equicelent '-fx' formula with coefficients for the function, if practical.
16083ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%
16093ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%  The format of the DistortImage() method is:
16103ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%
1611827944d54938c1f8a74ff53cd35c11801060d995dirk%      Image *DistortImage(const Image *image,const DistortMethod method,
1612bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy%        const size_t number_arguments,const double *arguments,
16133ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%        MagickBooleanType bestfit, ExceptionInfo *exception)
16143ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%
16153ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%  A description of each parameter follows:
16163ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%
16173ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%    o image: the image to be distorted.
16183ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%
16193ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%    o method: the method of image distortion.
16203ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%
16213ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%        ArcDistortion always ignores source image offset, and always
16223ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%        'bestfit' the destination image with the top left corner offset
16233ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%        relative to the polar mapping center.
16243ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%
16253ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%        Affine, Perspective, and Bilinear, do least squares fitting of the
16263ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%        distrotion when more than the minimum number of control point pairs
16273ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%        are provided.
16283ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%
16293ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%        Perspective, and Bilinear, fall back to a Affine distortion when less
16303ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%        than 4 control point pairs are provided.  While Affine distortions
16313ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%        let you use any number of control point pairs, that is Zero pairs is
16323ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%        a No-Op (viewport only) distortion, one pair is a translation and
16333ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%        two pairs of control points do a scale-rotate-translate, without any
16343ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%        shearing.
16353ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%
16363ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%    o number_arguments: the number of arguments given.
16373ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%
16383ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%    o arguments: an array of floating point arguments for this method.
16393ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%
16403ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%    o bestfit: Attempt to 'bestfit' the size of the resulting image.
16413ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%        This also forces the resulting image to be a 'layered' virtual
16423ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%        canvas image.  Can be overridden using 'distort:viewport' setting.
16433ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%
16443ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%    o exception: return any errors or warnings in this structure
16453ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%
16463ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%  Extra Controls from Image meta-data (artifacts)...
16473ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%
16483ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%    o "verbose"
16493ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%        Output to stderr alternatives, internal coefficents, and FX
1650f0a92fd8deb68d411304359906b12679b675691fglennrp%        equivalents for the distortion operation (if feasible).
16513ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%        This forms an extra check of the distortion method, and allows users
16523ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%        access to the internal constants IM calculates for the distortion.
16533ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%
16543ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%    o "distort:viewport"
16553ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%        Directly set the output image canvas area and offest to use for the
16563ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%        resulting image, rather than use the original images canvas, or a
16573ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%        calculated 'bestfit' canvas.
16583ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%
16593ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%    o "distort:scale"
16603ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%        Scale the size of the output canvas by this amount to provide a
16613ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%        method of Zooming, and for super-sampling the results.
16623ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%
16633ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%  Other settings that can effect results include
16643ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%
16653ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%    o 'interpolate' For source image lookups (scale enlargements)
16663ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%
16673ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%    o 'filter'      Set filter to use for area-resampling (scale shrinking).
16683ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%                    Set to 'point' to turn off and use 'interpolate' lookup
16693ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%                    instead
16703ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%
16713ed852eea50f9d4cd633efb8c2b054b8e33c253cristy*/
1672827944d54938c1f8a74ff53cd35c11801060d995dirkMagickExport Image *DistortImage(const Image *image, DistortMethod method,
1673bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  const size_t number_arguments,const double *arguments,
16743ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  MagickBooleanType bestfit,ExceptionInfo *exception)
16753ed852eea50f9d4cd633efb8c2b054b8e33c253cristy{
16763ed852eea50f9d4cd633efb8c2b054b8e33c253cristy#define DistortImageTag  "Distort/Image"
16773ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
16783ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  double
16793ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    *coeff,
16803ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    output_scaling;
16813ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
16823ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  Image
16833ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    *distort_image;
16843ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
16853ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  RectangleInfo
16863ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    geometry;  /* geometry of the distorted space viewport */
16873ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
16883ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  MagickBooleanType
16893ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    viewport_given;
16903ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
16913ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  assert(image != (Image *) NULL);
1692e1c94d9d25db6b0dd7a5028ffee31d1057855d73cristy  assert(image->signature == MagickCoreSignature);
16933ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  if (image->debug != MagickFalse)
16943ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
16953ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  assert(exception != (ExceptionInfo *) NULL);
1696e1c94d9d25db6b0dd7a5028ffee31d1057855d73cristy  assert(exception->signature == MagickCoreSignature);
16973ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
16982029ddc50e8860c787be44f997cbb57e457024baanthony  /*
1699b9aaf9ed2a83b7d8931306fb3313285098072b25anthony    Handle Special Compound Distortions
17002029ddc50e8860c787be44f997cbb57e457024baanthony  */
17012029ddc50e8860c787be44f997cbb57e457024baanthony  if ( method == ResizeDistortion )
17022029ddc50e8860c787be44f997cbb57e457024baanthony    {
17032029ddc50e8860c787be44f997cbb57e457024baanthony      if ( number_arguments != 2 )
17042029ddc50e8860c787be44f997cbb57e457024baanthony        {
17052029ddc50e8860c787be44f997cbb57e457024baanthony          (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
17062029ddc50e8860c787be44f997cbb57e457024baanthony                    "InvalidArgument","%s : '%s'","Resize",
17072029ddc50e8860c787be44f997cbb57e457024baanthony                    "Invalid number of args: 2 only");
17082029ddc50e8860c787be44f997cbb57e457024baanthony          return((Image *) NULL);
17092029ddc50e8860c787be44f997cbb57e457024baanthony        }
17102029ddc50e8860c787be44f997cbb57e457024baanthony      distort_image=DistortResizeImage(image,(size_t)arguments[0],
17112029ddc50e8860c787be44f997cbb57e457024baanthony         (size_t)arguments[1], exception);
17122029ddc50e8860c787be44f997cbb57e457024baanthony      return(distort_image);
17132029ddc50e8860c787be44f997cbb57e457024baanthony    }
17142029ddc50e8860c787be44f997cbb57e457024baanthony
17153ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  /*
1716b9aaf9ed2a83b7d8931306fb3313285098072b25anthony    Convert input arguments (usually as control points for reverse mapping)
17173ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    into mapping coefficients to apply the distortion.
17183ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
17193ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    Note that some distortions are mapped to other distortions,
17203ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    and as such do not require specific code after this point.
17213ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  */
17223ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  coeff = GenerateCoefficients(image, &method, number_arguments,
17233ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      arguments, 0, exception);
17243ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  if ( coeff == (double *) NULL )
17253ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    return((Image *) NULL);
17263ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
17273ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  /*
17283ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    Determine the size and offset for a 'bestfit' destination.
17293ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    Usally the four corners of the source image is enough.
17303ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  */
17313ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
17323ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  /* default output image bounds, when no 'bestfit' is requested */
17333ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  geometry.width=image->columns;
17343ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  geometry.height=image->rows;
17353ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  geometry.x=0;
17363ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  geometry.y=0;
17373ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
17383ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  if ( method == ArcDistortion ) {
1739b9aaf9ed2a83b7d8931306fb3313285098072b25anthony    bestfit = MagickTrue;  /* always calculate a 'best fit' viewport */
17403ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  }
17413ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
17423ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  /* Work out the 'best fit', (required for ArcDistortion) */
17433ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  if ( bestfit ) {
17443ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    PointInfo
1745b9aaf9ed2a83b7d8931306fb3313285098072b25anthony      s,d,min,max;  /* source, dest coords --mapping--> min, max coords */
1746b9aaf9ed2a83b7d8931306fb3313285098072b25anthony
1747b9aaf9ed2a83b7d8931306fb3313285098072b25anthony    MagickBooleanType
1748b9aaf9ed2a83b7d8931306fb3313285098072b25anthony      fix_bounds = MagickTrue;   /* enlarge bounds for VP handling */
17493ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
17503ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    s.x=s.y=min.x=max.x=min.y=max.y=0.0;   /* keep compiler happy */
17513ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
17523ed852eea50f9d4cd633efb8c2b054b8e33c253cristy/* defines to figure out the bounds of the distorted image */
17533ed852eea50f9d4cd633efb8c2b054b8e33c253cristy#define InitalBounds(p) \
17543ed852eea50f9d4cd633efb8c2b054b8e33c253cristy{ \
17553ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  /* printf("%lg,%lg -> %lg,%lg\n", s.x,s.y, d.x,d.y); */ \
17563ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  min.x = max.x = p.x; \
17573ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  min.y = max.y = p.y; \
17583ed852eea50f9d4cd633efb8c2b054b8e33c253cristy}
17593ed852eea50f9d4cd633efb8c2b054b8e33c253cristy#define ExpandBounds(p) \
17603ed852eea50f9d4cd633efb8c2b054b8e33c253cristy{ \
17613ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  /* printf("%lg,%lg -> %lg,%lg\n", s.x,s.y, d.x,d.y); */ \
17623ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  min.x = MagickMin(min.x,p.x); \
17633ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  max.x = MagickMax(max.x,p.x); \
17643ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  min.y = MagickMin(min.y,p.y); \
17653ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  max.y = MagickMax(max.y,p.y); \
17663ed852eea50f9d4cd633efb8c2b054b8e33c253cristy}
17673ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
17683ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    switch (method)
17693ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    {
17703ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      case AffineDistortion:
17713ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      { double inverse[6];
17723ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        InvertAffineCoefficients(coeff, inverse);
17733ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        s.x = (double) image->page.x;
17743ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        s.y = (double) image->page.y;
17753ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        d.x = inverse[0]*s.x+inverse[1]*s.y+inverse[2];
17763ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        d.y = inverse[3]*s.x+inverse[4]*s.y+inverse[5];
17773ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        InitalBounds(d);
17783ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        s.x = (double) image->page.x+image->columns;
17793ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        s.y = (double) image->page.y;
17803ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        d.x = inverse[0]*s.x+inverse[1]*s.y+inverse[2];
17813ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        d.y = inverse[3]*s.x+inverse[4]*s.y+inverse[5];
17823ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        ExpandBounds(d);
17833ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        s.x = (double) image->page.x;
17843ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        s.y = (double) image->page.y+image->rows;
17853ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        d.x = inverse[0]*s.x+inverse[1]*s.y+inverse[2];
17863ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        d.y = inverse[3]*s.x+inverse[4]*s.y+inverse[5];
17873ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        ExpandBounds(d);
17883ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        s.x = (double) image->page.x+image->columns;
17893ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        s.y = (double) image->page.y+image->rows;
17903ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        d.x = inverse[0]*s.x+inverse[1]*s.y+inverse[2];
17913ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        d.y = inverse[3]*s.x+inverse[4]*s.y+inverse[5];
17923ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        ExpandBounds(d);
17933ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        break;
17943ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      }
17953ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      case PerspectiveDistortion:
17963ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      { double inverse[8], scale;
17973ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        InvertPerspectiveCoefficients(coeff, inverse);
17983ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        s.x = (double) image->page.x;
17993ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        s.y = (double) image->page.y;
18003ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        scale=inverse[6]*s.x+inverse[7]*s.y+1.0;
18013e3ec3afbb0782697f201cbe30a56794c10dc7efcristy        scale=PerceptibleReciprocal(scale);
18023ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        d.x = scale*(inverse[0]*s.x+inverse[1]*s.y+inverse[2]);
18033ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        d.y = scale*(inverse[3]*s.x+inverse[4]*s.y+inverse[5]);
18043ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        InitalBounds(d);
18053ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        s.x = (double) image->page.x+image->columns;
18063ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        s.y = (double) image->page.y;
18073ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        scale=inverse[6]*s.x+inverse[7]*s.y+1.0;
18083e3ec3afbb0782697f201cbe30a56794c10dc7efcristy        scale=PerceptibleReciprocal(scale);
18093ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        d.x = scale*(inverse[0]*s.x+inverse[1]*s.y+inverse[2]);
18103ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        d.y = scale*(inverse[3]*s.x+inverse[4]*s.y+inverse[5]);
18113ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        ExpandBounds(d);
18123ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        s.x = (double) image->page.x;
18133ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        s.y = (double) image->page.y+image->rows;
18143ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        scale=inverse[6]*s.x+inverse[7]*s.y+1.0;
18153e3ec3afbb0782697f201cbe30a56794c10dc7efcristy        scale=PerceptibleReciprocal(scale);
18163ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        d.x = scale*(inverse[0]*s.x+inverse[1]*s.y+inverse[2]);
18173ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        d.y = scale*(inverse[3]*s.x+inverse[4]*s.y+inverse[5]);
18183ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        ExpandBounds(d);
18193ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        s.x = (double) image->page.x+image->columns;
18203ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        s.y = (double) image->page.y+image->rows;
18213ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        scale=inverse[6]*s.x+inverse[7]*s.y+1.0;
18223e3ec3afbb0782697f201cbe30a56794c10dc7efcristy        scale=PerceptibleReciprocal(scale);
18233ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        d.x = scale*(inverse[0]*s.x+inverse[1]*s.y+inverse[2]);
18243ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        d.y = scale*(inverse[3]*s.x+inverse[4]*s.y+inverse[5]);
18253ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        ExpandBounds(d);
18263ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        break;
18273ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      }
18283ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      case ArcDistortion:
18293ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      { double a, ca, sa;
18303ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        /* Forward Map Corners */
18313ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        a = coeff[0]-coeff[1]/2; ca = cos(a); sa = sin(a);
18323ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        d.x = coeff[2]*ca;
18333ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        d.y = coeff[2]*sa;
18343ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        InitalBounds(d);
18353ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        d.x = (coeff[2]-coeff[3])*ca;
18363ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        d.y = (coeff[2]-coeff[3])*sa;
18373ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        ExpandBounds(d);
18383ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        a = coeff[0]+coeff[1]/2; ca = cos(a); sa = sin(a);
18393ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        d.x = coeff[2]*ca;
18403ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        d.y = coeff[2]*sa;
18413ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        ExpandBounds(d);
18423ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        d.x = (coeff[2]-coeff[3])*ca;
18433ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        d.y = (coeff[2]-coeff[3])*sa;
18443ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        ExpandBounds(d);
1845cee9711bbc334b5677d5ec4ea1cc70340d35ee35cristy        /* Orthogonal points along top of arc */
1846ba978e102735661f7777d22c1674d1d974303072cristy        for( a=(double) (ceil((double) ((coeff[0]-coeff[1]/2.0)/MagickPI2))*MagickPI2);
18473ed852eea50f9d4cd633efb8c2b054b8e33c253cristy               a<(coeff[0]+coeff[1]/2.0); a+=MagickPI2 ) {
18483ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          ca = cos(a); sa = sin(a);
18493ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          d.x = coeff[2]*ca;
18503ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          d.y = coeff[2]*sa;
18513ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          ExpandBounds(d);
18523ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        }
18533ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        /*
18543ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          Convert the angle_to_width and radius_to_height
18553ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          to appropriate scaling factors, to allow faster processing
18563ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          in the mapping function.
18573ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        */
1858ba978e102735661f7777d22c1674d1d974303072cristy        coeff[1] = (double) (Magick2PI*image->columns/coeff[1]);
18593ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        coeff[3] = (double)image->rows/coeff[3];
18603ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        break;
18613ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      }
18623ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      case PolarDistortion:
18633ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      {
18643ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        if (number_arguments < 2)
18653ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          coeff[2] = coeff[3] = 0.0;
18663ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        min.x = coeff[2]-coeff[0];
18673ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        max.x = coeff[2]+coeff[0];
18683ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        min.y = coeff[3]-coeff[0];
18693ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        max.y = coeff[3]+coeff[0];
18703ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        /* should be about 1.0 if Rmin = 0 */
18713ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        coeff[7]=(double) geometry.height/(coeff[0]-coeff[1]);
18723ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        break;
18733ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      }
18743ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      case DePolarDistortion:
18753ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      {
18763ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        /* direct calculation as it needs to tile correctly
18773ed852eea50f9d4cd633efb8c2b054b8e33c253cristy         * for reversibility in a DePolar-Polar cycle */
1878b9aaf9ed2a83b7d8931306fb3313285098072b25anthony        fix_bounds = MagickFalse;
18793ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        geometry.x = geometry.y = 0;
1880bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy        geometry.height = (size_t) ceil(coeff[0]-coeff[1]);
1881bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy        geometry.width = (size_t)
18823ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                  ceil((coeff[0]-coeff[1])*(coeff[5]-coeff[4])*0.5);
1883b9aaf9ed2a83b7d8931306fb3313285098072b25anthony        /* correct scaling factors relative to new size */
1884b9aaf9ed2a83b7d8931306fb3313285098072b25anthony        coeff[6]=(coeff[5]-coeff[4])/geometry.width; /* changed width */
1885b9aaf9ed2a83b7d8931306fb3313285098072b25anthony        coeff[7]=(coeff[0]-coeff[1])/geometry.height; /* should be about 1.0 */
1886b9aaf9ed2a83b7d8931306fb3313285098072b25anthony        break;
1887b9aaf9ed2a83b7d8931306fb3313285098072b25anthony      }
1888e0d9bbde3eb1826c767fee326a5786a9f28a191banthony      case Cylinder2PlaneDistortion:
1889b9aaf9ed2a83b7d8931306fb3313285098072b25anthony      {
1890b9aaf9ed2a83b7d8931306fb3313285098072b25anthony        /* direct calculation so center of distortion is either a pixel
1891b9aaf9ed2a83b7d8931306fb3313285098072b25anthony         * center, or pixel edge. This allows for reversibility of the
1892b9aaf9ed2a83b7d8931306fb3313285098072b25anthony         * distortion */
1893b9aaf9ed2a83b7d8931306fb3313285098072b25anthony        geometry.x = geometry.y = 0;
18944c08aed51c5899665ade97263692328eea4af106cristy        geometry.width = (size_t) ceil( 2.0*coeff[1]*tan(coeff[0]/2.0) );
18954c08aed51c5899665ade97263692328eea4af106cristy        geometry.height = (size_t) ceil( 2.0*coeff[3]/cos(coeff[0]/2.0) );
1896b9aaf9ed2a83b7d8931306fb3313285098072b25anthony        /* correct center of distortion relative to new size */
18974c08aed51c5899665ade97263692328eea4af106cristy        coeff[4] = (double) geometry.width/2.0;
18984c08aed51c5899665ade97263692328eea4af106cristy        coeff[5] = (double) geometry.height/2.0;
1899b9aaf9ed2a83b7d8931306fb3313285098072b25anthony        fix_bounds = MagickFalse;
19003ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        break;
19013ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      }
1902e0d9bbde3eb1826c767fee326a5786a9f28a191banthony      case Plane2CylinderDistortion:
1903b9aaf9ed2a83b7d8931306fb3313285098072b25anthony      {
1904b9aaf9ed2a83b7d8931306fb3313285098072b25anthony        /* direct calculation center is either pixel center, or pixel edge
1905b9aaf9ed2a83b7d8931306fb3313285098072b25anthony         * so as to allow reversibility of the image distortion */
1906b9aaf9ed2a83b7d8931306fb3313285098072b25anthony        geometry.x = geometry.y = 0;
19074c08aed51c5899665ade97263692328eea4af106cristy        geometry.width = (size_t) ceil(coeff[0]*coeff[1]);  /* FOV * radius */
19084c08aed51c5899665ade97263692328eea4af106cristy        geometry.height = (size_t) (2*coeff[3]);              /* input image height */
1909b9aaf9ed2a83b7d8931306fb3313285098072b25anthony        /* correct center of distortion relative to new size */
19104c08aed51c5899665ade97263692328eea4af106cristy        coeff[4] = (double) geometry.width/2.0;
19114c08aed51c5899665ade97263692328eea4af106cristy        coeff[5] = (double) geometry.height/2.0;
1912b9aaf9ed2a83b7d8931306fb3313285098072b25anthony        fix_bounds = MagickFalse;
1913b9aaf9ed2a83b7d8931306fb3313285098072b25anthony        break;
1914b9aaf9ed2a83b7d8931306fb3313285098072b25anthony      }
19153ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      case ShepardsDistortion:
19163ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      case BilinearForwardDistortion:
19173ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      case BilinearReverseDistortion:
19185056b230558c7de0a79d51d82bdc929fef45a225anthony#if 0
19195056b230558c7de0a79d51d82bdc929fef45a225anthony      case QuadrilateralDistortion:
19205056b230558c7de0a79d51d82bdc929fef45a225anthony#endif
19213ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      case PolynomialDistortion:
19223ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      case BarrelDistortion:
19233ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      case BarrelInverseDistortion:
19243ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      default:
1925b9aaf9ed2a83b7d8931306fb3313285098072b25anthony        /* no calculated bestfit available for these distortions */
19263ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        bestfit = MagickFalse;
1927b9aaf9ed2a83b7d8931306fb3313285098072b25anthony        fix_bounds = MagickFalse;
19283ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        break;
19293ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    }
19304cf1d89eb32104b1bed4aa71490bc91d4f6c0efeanthony
19314cf1d89eb32104b1bed4aa71490bc91d4f6c0efeanthony    /* Set the output image geometry to calculated 'bestfit'.
19324cf1d89eb32104b1bed4aa71490bc91d4f6c0efeanthony       Yes this tends to 'over do' the file image size, ON PURPOSE!
19334cf1d89eb32104b1bed4aa71490bc91d4f6c0efeanthony       Do not do this for DePolar which needs to be exact for virtual tiling.
19343ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    */
1935b9aaf9ed2a83b7d8931306fb3313285098072b25anthony    if ( fix_bounds ) {
1936bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      geometry.x = (ssize_t) floor(min.x-0.5);
1937bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      geometry.y = (ssize_t) floor(min.y-0.5);
1938bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      geometry.width=(size_t) ceil(max.x-geometry.x+0.5);
1939bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      geometry.height=(size_t) ceil(max.y-geometry.y+0.5);
19403ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    }
19414cf1d89eb32104b1bed4aa71490bc91d4f6c0efeanthony
19424c08aed51c5899665ade97263692328eea4af106cristy  }  /* end bestfit destination image calculations */
19433ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
19443ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  /* The user provided a 'viewport' expert option which may
19453ed852eea50f9d4cd633efb8c2b054b8e33c253cristy     overrides some parts of the current output image geometry.
1946b9aaf9ed2a83b7d8931306fb3313285098072b25anthony     This also overrides its default 'bestfit' setting.
19473ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  */
19483ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  { const char *artifact=GetImageArtifact(image,"distort:viewport");
19493ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    viewport_given = MagickFalse;
19503ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    if ( artifact != (const char *) NULL ) {
19519cb63ccb02272521283da6d251068d91196cc5bfanthony      MagickStatusType flags=ParseAbsoluteGeometry(artifact,&geometry);
19529cb63ccb02272521283da6d251068d91196cc5bfanthony      if (flags==NoValue)
195322de2722b682eb405b60ec6022a7546df994674eanthony        (void) ThrowMagickException(exception,GetMagickModule(),
195422de2722b682eb405b60ec6022a7546df994674eanthony             OptionWarning,"InvalidSetting","'%s' '%s'",
195522de2722b682eb405b60ec6022a7546df994674eanthony             "distort:viewport",artifact);
19569cb63ccb02272521283da6d251068d91196cc5bfanthony      else
195722de2722b682eb405b60ec6022a7546df994674eanthony        viewport_given = MagickTrue;
19583ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    }
19593ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  }
19603ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
19613ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  /* Verbose output */
1962972c2e42a2171341bbf55a48cb36882616a591b1dirk  if (IsStringTrue(GetImageArtifact(image,"verbose")) != MagickFalse) {
1963bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy    register ssize_t
19643ed852eea50f9d4cd633efb8c2b054b8e33c253cristy       i;
1965151b66dffc9e3c2e8c4f8cdaca37ff987ca0f497cristy    char image_gen[MagickPathExtent];
19663ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    const char *lookup;
19673ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
19683ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    /* Set destination image size and virtual offset */
19693ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    if ( bestfit || viewport_given ) {
1970151b66dffc9e3c2e8c4f8cdaca37ff987ca0f497cristy      (void) FormatLocaleString(image_gen, MagickPathExtent,"  -size %.20gx%.20g "
197188ef4d9fc7a5b3b5a194b1c5736df5360c830344anthony        "-page %+.20g%+.20g xc: +insert \\\n",(double) geometry.width,
1972e8c25f9b4c9fb72cad6db08eeda58c7c5784014ecristy        (double) geometry.height,(double) geometry.x,(double) geometry.y);
1973d2a90402dad613980c957433ecff73d2f4a99f8aanthony      lookup="v.p{ xx-v.page.x-.5, yy-v.page.y-.5 }";
19743ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    }
19753ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    else {
19763ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      image_gen[0] = '\0';             /* no destination to generate */
1977d2a90402dad613980c957433ecff73d2f4a99f8aanthony      lookup = "p{ xx-page.x-.5, yy-page.y-.5 }"; /* simplify lookup */
19783ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    }
19793ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
19803ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    switch (method) {
19813ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      case AffineDistortion:
19823ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      {
19833ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        double *inverse;
19843ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
19853ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        inverse = (double *) AcquireQuantumMemory(6,sizeof(*inverse));
19863ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        if (inverse == (double *) NULL) {
19873ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          coeff = (double *) RelinquishMagickMemory(coeff);
19883ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          (void) ThrowMagickException(exception,GetMagickModule(),
19893ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                  ResourceLimitError,"MemoryAllocationFailed",
19903ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                  "%s", "DistortImages");
19913ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          return((Image *) NULL);
19923ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        }
19933ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        InvertAffineCoefficients(coeff, inverse);
19943ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        CoefficientsToAffineArgs(inverse);
19955acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "Affine Projection:\n");
19965acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "  -distort AffineProjection \\\n      '");
19971e604812fad85bb96f757a2393015ae3d061c39acristy        for (i=0; i < 5; i++)
19985acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy          (void) FormatLocaleFile(stderr, "%lf,", inverse[i]);
19995acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "%lf'\n", inverse[5]);
20003ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        inverse = (double *) RelinquishMagickMemory(inverse);
20013ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
20025acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "Affine Distort, FX Equivelent:\n");
20035acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "%s", image_gen);
20045acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "  -fx 'ii=i+page.x+0.5; jj=j+page.y+0.5;\n");
20055acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "       xx=%+lf*ii %+lf*jj %+lf;\n",
20063ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            coeff[0], coeff[1], coeff[2]);
20075acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "       yy=%+lf*ii %+lf*jj %+lf;\n",
20083ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            coeff[3], coeff[4], coeff[5]);
2009b9aaf9ed2a83b7d8931306fb3313285098072b25anthony        (void) FormatLocaleFile(stderr, "       %s' \\\n", lookup);
20103ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
20113ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        break;
20123ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      }
20133ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
20143ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      case PerspectiveDistortion:
20153ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      {
20163ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        double *inverse;
20173ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
20183ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        inverse = (double *) AcquireQuantumMemory(8,sizeof(*inverse));
20193ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        if (inverse == (double *) NULL) {
20203ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          coeff = (double *) RelinquishMagickMemory(coeff);
20213ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          (void) ThrowMagickException(exception,GetMagickModule(),
20223ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                  ResourceLimitError,"MemoryAllocationFailed",
20233ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                  "%s", "DistortCoefficients");
20243ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          return((Image *) NULL);
20253ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        }
20263ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        InvertPerspectiveCoefficients(coeff, inverse);
20275acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "Perspective Projection:\n");
20285acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "  -distort PerspectiveProjection \\\n      '");
20293ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        for (i=0; i<4; i++)
20305acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy          (void) FormatLocaleFile(stderr, "%lf, ", inverse[i]);
20315acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "\n       ");
20323ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        for (; i<7; i++)
20335acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy          (void) FormatLocaleFile(stderr, "%lf, ", inverse[i]);
20345acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "%lf'\n", inverse[7]);
20353ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        inverse = (double *) RelinquishMagickMemory(inverse);
20363ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
20375acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "Perspective Distort, FX Equivelent:\n");
20385acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "%s", image_gen);
20395acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "  -fx 'ii=i+page.x+0.5; jj=j+page.y+0.5;\n");
20405acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "       rr=%+lf*ii %+lf*jj + 1;\n",
20413ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            coeff[6], coeff[7]);
20425acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "       xx=(%+lf*ii %+lf*jj %+lf)/rr;\n",
20433ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            coeff[0], coeff[1], coeff[2]);
20445acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "       yy=(%+lf*ii %+lf*jj %+lf)/rr;\n",
20453ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            coeff[3], coeff[4], coeff[5]);
2046b9aaf9ed2a83b7d8931306fb3313285098072b25anthony        (void) FormatLocaleFile(stderr, "       rr%s0 ? %s : blue' \\\n",
20473ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            coeff[8] < 0 ? "<" : ">", lookup);
20483ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        break;
20493ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      }
20503ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
20513ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      case BilinearForwardDistortion:
20525acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "BilinearForward Mapping Equations:\n");
20535acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "%s", image_gen);
20545acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "    i = %+lf*x %+lf*y %+lf*x*y %+lf;\n",
20553ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            coeff[0], coeff[1], coeff[2], coeff[3]);
20565acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "    j = %+lf*x %+lf*y %+lf*x*y %+lf;\n",
20573ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            coeff[4], coeff[5], coeff[6], coeff[7]);
20585056b230558c7de0a79d51d82bdc929fef45a225anthony#if 0
20595056b230558c7de0a79d51d82bdc929fef45a225anthony        /* for debugging */
20605acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "   c8 = %+lf  c9 = 2*a = %+lf;\n",
20615056b230558c7de0a79d51d82bdc929fef45a225anthony            coeff[8], coeff[9]);
20625056b230558c7de0a79d51d82bdc929fef45a225anthony#endif
20635acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "BilinearForward Distort, FX Equivelent:\n");
20645acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "%s", image_gen);
20655acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "  -fx 'ii=i+page.x%+lf; jj=j+page.y%+lf;\n",
20663ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            0.5-coeff[3], 0.5-coeff[7]);
20675acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "       bb=%lf*ii %+lf*jj %+lf;\n",
20683ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            coeff[6], -coeff[2], coeff[8]);
20695056b230558c7de0a79d51d82bdc929fef45a225anthony        /* Handle Special degenerate (non-quadratic) or trapezoidal case */
20705056b230558c7de0a79d51d82bdc929fef45a225anthony        if ( coeff[9] != 0 ) {
20715acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy          (void) FormatLocaleFile(stderr, "       rt=bb*bb %+lf*(%lf*ii%+lf*jj);\n",
20725056b230558c7de0a79d51d82bdc929fef45a225anthony              -2*coeff[9],  coeff[4], -coeff[0]);
20735acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy          (void) FormatLocaleFile(stderr, "       yy=( -bb + sqrt(rt) ) / %lf;\n",
20745056b230558c7de0a79d51d82bdc929fef45a225anthony               coeff[9]);
20755056b230558c7de0a79d51d82bdc929fef45a225anthony        } else
20765acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy          (void) FormatLocaleFile(stderr, "       yy=(%lf*ii%+lf*jj)/bb;\n",
20775056b230558c7de0a79d51d82bdc929fef45a225anthony                -coeff[4], coeff[0]);
20785acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "       xx=(ii %+lf*yy)/(%lf %+lf*yy);\n",
20793ed852eea50f9d4cd633efb8c2b054b8e33c253cristy             -coeff[1], coeff[0], coeff[2]);
20805056b230558c7de0a79d51d82bdc929fef45a225anthony        if ( coeff[9] != 0 )
20815acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy          (void) FormatLocaleFile(stderr, "       (rt < 0 ) ? red : %s'\n", lookup);
20825056b230558c7de0a79d51d82bdc929fef45a225anthony        else
2083b9aaf9ed2a83b7d8931306fb3313285098072b25anthony          (void) FormatLocaleFile(stderr, "       %s' \\\n", lookup);
20843ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        break;
20853ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
20863ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      case BilinearReverseDistortion:
2087fbe952e8ee289004f7021464a37935ee1b97ab2aanthony#if 0
20885acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "Polynomial Projection Distort:\n");
20895acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "  -distort PolynomialProjection \\\n");
20905acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "      '1.5, %lf, %lf, %lf, %lf,\n",
20915056b230558c7de0a79d51d82bdc929fef45a225anthony            coeff[3], coeff[0], coeff[1], coeff[2]);
20925acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "            %lf, %lf, %lf, %lf'\n",
20935056b230558c7de0a79d51d82bdc929fef45a225anthony            coeff[7], coeff[4], coeff[5], coeff[6]);
2094fbe952e8ee289004f7021464a37935ee1b97ab2aanthony#endif
20955acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "BilinearReverse Distort, FX Equivelent:\n");
20965acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "%s", image_gen);
20975acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "  -fx 'ii=i+page.x+0.5; jj=j+page.y+0.5;\n");
20985acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "       xx=%+lf*ii %+lf*jj %+lf*ii*jj %+lf;\n",
20993ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            coeff[0], coeff[1], coeff[2], coeff[3]);
21005acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "       yy=%+lf*ii %+lf*jj %+lf*ii*jj %+lf;\n",
21013ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            coeff[4], coeff[5], coeff[6], coeff[7]);
2102b9aaf9ed2a83b7d8931306fb3313285098072b25anthony        (void) FormatLocaleFile(stderr, "       %s' \\\n", lookup);
21033ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        break;
21043ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
21053ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      case PolynomialDistortion:
21063ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      {
2107bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy        size_t nterms = (size_t) coeff[1];
21085acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "Polynomial (order %lg, terms %lu), FX Equivelent\n",
2109f2faecf9facdbbb14fcba373365f9f691a9658e0cristy          coeff[0],(unsigned long) nterms);
21105acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "%s", image_gen);
21115acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "  -fx 'ii=i+page.x+0.5; jj=j+page.y+0.5;\n");
21125acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "       xx =");
2113bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy        for (i=0; i<(ssize_t) nterms; i++) {
21145acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy          if ( i != 0 && i%4 == 0 ) (void) FormatLocaleFile(stderr, "\n         ");
21155acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy          (void) FormatLocaleFile(stderr, " %+lf%s", coeff[2+i],
21163ed852eea50f9d4cd633efb8c2b054b8e33c253cristy               poly_basis_str(i));
21173ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        }
21185acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, ";\n       yy =");
2119bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy        for (i=0; i<(ssize_t) nterms; i++) {
21205acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy          if ( i != 0 && i%4 == 0 ) (void) FormatLocaleFile(stderr, "\n         ");
21215acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy          (void) FormatLocaleFile(stderr, " %+lf%s", coeff[2+i+nterms],
21223ed852eea50f9d4cd633efb8c2b054b8e33c253cristy               poly_basis_str(i));
21233ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        }
2124b9aaf9ed2a83b7d8931306fb3313285098072b25anthony        (void) FormatLocaleFile(stderr, ";\n       %s' \\\n", lookup);
21253ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        break;
21263ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      }
21273ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      case ArcDistortion:
21283ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      {
21295acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "Arc Distort, Internal Coefficients:\n");
21303ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        for ( i=0; i<5; i++ )
21315acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy          (void) FormatLocaleFile(stderr, "  c%.20g = %+lf\n", (double) i, coeff[i]);
21325acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "Arc Distort, FX Equivelent:\n");
21335acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "%s", image_gen);
21345acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "  -fx 'ii=i+page.x; jj=j+page.y;\n");
21355acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "       xx=(atan2(jj,ii)%+lf)/(2*pi);\n",
21363ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                                  -coeff[0]);
21375acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "       xx=xx-round(xx);\n");
21385acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "       xx=xx*%lf %+lf;\n",
21393ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                            coeff[1], coeff[4]);
21405acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "       yy=(%lf - hypot(ii,jj)) * %lf;\n",
21413ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                            coeff[2], coeff[3]);
2142b9aaf9ed2a83b7d8931306fb3313285098072b25anthony        (void) FormatLocaleFile(stderr, "       v.p{xx-.5,yy-.5}' \\\n");
21433ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        break;
21443ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      }
21453ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      case PolarDistortion:
21463ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      {
21475acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "Polar Distort, Internal Coefficents\n");
21483ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        for ( i=0; i<8; i++ )
21495acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy          (void) FormatLocaleFile(stderr, "  c%.20g = %+lf\n", (double) i, coeff[i]);
21505acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "Polar Distort, FX Equivelent:\n");
21515acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "%s", image_gen);
21525acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "  -fx 'ii=i+page.x%+lf; jj=j+page.y%+lf;\n",
21533ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                         -coeff[2], -coeff[3]);
21545acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "       xx=(atan2(ii,jj)%+lf)/(2*pi);\n",
21553ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                         -(coeff[4]+coeff[5])/2 );
21565acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "       xx=xx-round(xx);\n");
21575acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "       xx=xx*2*pi*%lf + v.w/2;\n",
21583ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                         coeff[6] );
21595acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "       yy=(hypot(ii,jj)%+lf)*%lf;\n",
21603ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                         -coeff[1], coeff[7] );
2161b9aaf9ed2a83b7d8931306fb3313285098072b25anthony        (void) FormatLocaleFile(stderr, "       v.p{xx-.5,yy-.5}' \\\n");
21623ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        break;
21633ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      }
21643ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      case DePolarDistortion:
21653ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      {
21665acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "DePolar Distort, Internal Coefficents\n");
21673ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        for ( i=0; i<8; i++ )
21685acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy          (void) FormatLocaleFile(stderr, "  c%.20g = %+lf\n", (double) i, coeff[i]);
21695acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "DePolar Distort, FX Equivelent:\n");
21705acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "%s", image_gen);
217173144553d112665377fdd6f1ab5e152cff02b956anthony        (void) FormatLocaleFile(stderr, "  -fx 'aa=(i+.5)*%lf %+lf;\n", coeff[6], +coeff[4] );
21725acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "       rr=(j+.5)*%lf %+lf;\n", coeff[7], +coeff[1] );
21735acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "       xx=rr*sin(aa) %+lf;\n", coeff[2] );
21745acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "       yy=rr*cos(aa) %+lf;\n", coeff[3] );
2175b9aaf9ed2a83b7d8931306fb3313285098072b25anthony        (void) FormatLocaleFile(stderr, "       v.p{xx-.5,yy-.5}' \\\n");
2176b9aaf9ed2a83b7d8931306fb3313285098072b25anthony        break;
2177b9aaf9ed2a83b7d8931306fb3313285098072b25anthony      }
2178e0d9bbde3eb1826c767fee326a5786a9f28a191banthony      case Cylinder2PlaneDistortion:
2179b9aaf9ed2a83b7d8931306fb3313285098072b25anthony      {
2180b9aaf9ed2a83b7d8931306fb3313285098072b25anthony        (void) FormatLocaleFile(stderr, "Cylinder to Plane Distort, Internal Coefficents\n");
2181b9aaf9ed2a83b7d8931306fb3313285098072b25anthony        (void) FormatLocaleFile(stderr, "  cylinder_radius = %+lf\n", coeff[1]);
2182b9aaf9ed2a83b7d8931306fb3313285098072b25anthony        (void) FormatLocaleFile(stderr, "Cylinder to Plane Distort, FX Equivelent:\n");
2183b9aaf9ed2a83b7d8931306fb3313285098072b25anthony        (void) FormatLocaleFile(stderr, "%s", image_gen);
2184b9aaf9ed2a83b7d8931306fb3313285098072b25anthony        (void) FormatLocaleFile(stderr, "  -fx 'ii=i+page.x%+lf+0.5; jj=j+page.y%+lf+0.5;\n",
2185b9aaf9ed2a83b7d8931306fb3313285098072b25anthony                         -coeff[4], -coeff[5]);
2186b9aaf9ed2a83b7d8931306fb3313285098072b25anthony        (void) FormatLocaleFile(stderr, "       aa=atan(ii/%+lf);\n", coeff[1] );
2187b9aaf9ed2a83b7d8931306fb3313285098072b25anthony        (void) FormatLocaleFile(stderr, "       xx=%lf*aa%+lf;\n",
2188b9aaf9ed2a83b7d8931306fb3313285098072b25anthony                         coeff[1], coeff[2] );
2189b9aaf9ed2a83b7d8931306fb3313285098072b25anthony        (void) FormatLocaleFile(stderr, "       yy=jj*cos(aa)%+lf;\n", coeff[3] );
2190b9aaf9ed2a83b7d8931306fb3313285098072b25anthony        (void) FormatLocaleFile(stderr, "       %s' \\\n", lookup);
2191b9aaf9ed2a83b7d8931306fb3313285098072b25anthony        break;
2192b9aaf9ed2a83b7d8931306fb3313285098072b25anthony      }
2193e0d9bbde3eb1826c767fee326a5786a9f28a191banthony      case Plane2CylinderDistortion:
2194b9aaf9ed2a83b7d8931306fb3313285098072b25anthony      {
2195b9aaf9ed2a83b7d8931306fb3313285098072b25anthony        (void) FormatLocaleFile(stderr, "Plane to Cylinder Distort, Internal Coefficents\n");
2196b9aaf9ed2a83b7d8931306fb3313285098072b25anthony        (void) FormatLocaleFile(stderr, "  cylinder_radius = %+lf\n", coeff[1]);
2197b9aaf9ed2a83b7d8931306fb3313285098072b25anthony        (void) FormatLocaleFile(stderr, "Plane to Cylinder Distort, FX Equivelent:\n");
2198b9aaf9ed2a83b7d8931306fb3313285098072b25anthony        (void) FormatLocaleFile(stderr, "%s", image_gen);
2199b9aaf9ed2a83b7d8931306fb3313285098072b25anthony        (void) FormatLocaleFile(stderr, "  -fx 'ii=i+page.x%+lf+0.5; jj=j+page.y%+lf+0.5;\n",
2200b9aaf9ed2a83b7d8931306fb3313285098072b25anthony                         -coeff[4], -coeff[5]);
2201b9aaf9ed2a83b7d8931306fb3313285098072b25anthony        (void) FormatLocaleFile(stderr, "       ii=ii/%+lf;\n", coeff[1] );
2202b9aaf9ed2a83b7d8931306fb3313285098072b25anthony        (void) FormatLocaleFile(stderr, "       xx=%lf*tan(ii)%+lf;\n",
2203b9aaf9ed2a83b7d8931306fb3313285098072b25anthony                         coeff[1], coeff[2] );
2204b9aaf9ed2a83b7d8931306fb3313285098072b25anthony        (void) FormatLocaleFile(stderr, "       yy=jj/cos(ii)%+lf;\n",
2205b9aaf9ed2a83b7d8931306fb3313285098072b25anthony                         coeff[3] );
2206b9aaf9ed2a83b7d8931306fb3313285098072b25anthony        (void) FormatLocaleFile(stderr, "       %s' \\\n", lookup);
2207b9aaf9ed2a83b7d8931306fb3313285098072b25anthony        break;
22083ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      }
22093ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      case BarrelDistortion:
22103ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      case BarrelInverseDistortion:
22113ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      { double xc,yc;
2212ec40aca22bbc75266cdfdc60dd1fc8ebb8174139anthony        /* NOTE: This does the barrel roll in pixel coords not image coords
2213ec40aca22bbc75266cdfdc60dd1fc8ebb8174139anthony        ** The internal distortion must do it in image coordinates,
2214ec40aca22bbc75266cdfdc60dd1fc8ebb8174139anthony        ** so that is what the center coeff (8,9) is given in.
2215ec40aca22bbc75266cdfdc60dd1fc8ebb8174139anthony        */
22163ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        xc = ((double)image->columns-1.0)/2.0 + image->page.x;
22173ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        yc = ((double)image->rows-1.0)/2.0    + image->page.y;
22185acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "Barrel%s Distort, FX Equivelent:\n",
22193ed852eea50f9d4cd633efb8c2b054b8e33c253cristy             method == BarrelDistortion ? "" : "Inv");
22205acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "%s", image_gen);
2221ec40aca22bbc75266cdfdc60dd1fc8ebb8174139anthony        if ( fabs(coeff[8]-xc-0.5) < 0.1 && fabs(coeff[9]-yc-0.5) < 0.1 )
22225acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy          (void) FormatLocaleFile(stderr, "  -fx 'xc=(w-1)/2;  yc=(h-1)/2;\n");
22233ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        else
22245acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy          (void) FormatLocaleFile(stderr, "  -fx 'xc=%lf;  yc=%lf;\n",
2225ec40aca22bbc75266cdfdc60dd1fc8ebb8174139anthony               coeff[8]-0.5, coeff[9]-0.5);
22265acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr,
22273ed852eea50f9d4cd633efb8c2b054b8e33c253cristy             "       ii=i-xc;  jj=j-yc;  rr=hypot(ii,jj);\n");
22285acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "       ii=ii%s(%lf*rr*rr*rr %+lf*rr*rr %+lf*rr %+lf);\n",
22293ed852eea50f9d4cd633efb8c2b054b8e33c253cristy             method == BarrelDistortion ? "*" : "/",
22303ed852eea50f9d4cd633efb8c2b054b8e33c253cristy             coeff[0],coeff[1],coeff[2],coeff[3]);
22315acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "       jj=jj%s(%lf*rr*rr*rr %+lf*rr*rr %+lf*rr %+lf);\n",
22323ed852eea50f9d4cd633efb8c2b054b8e33c253cristy             method == BarrelDistortion ? "*" : "/",
22333ed852eea50f9d4cd633efb8c2b054b8e33c253cristy             coeff[4],coeff[5],coeff[6],coeff[7]);
2234b9aaf9ed2a83b7d8931306fb3313285098072b25anthony        (void) FormatLocaleFile(stderr, "       v.p{fx*ii+xc,fy*jj+yc}' \\\n");
22353ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      }
22363ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      default:
22373ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        break;
22383ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    }
22393ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  }
22403ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
22413ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  /* The user provided a 'scale' expert option will scale the
22423ed852eea50f9d4cd633efb8c2b054b8e33c253cristy     output image size, by the factor given allowing for super-sampling
22433ed852eea50f9d4cd633efb8c2b054b8e33c253cristy     of the distorted image space.  Any scaling factors must naturally
22443ed852eea50f9d4cd633efb8c2b054b8e33c253cristy     be halved as a result.
22453ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  */
22463ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  { const char *artifact;
22473ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    artifact=GetImageArtifact(image,"distort:scale");
22483ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    output_scaling = 1.0;
22493ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    if (artifact != (const char *) NULL) {
2250dbdd0e35efc03c9bccda644f5407db38b7c17eeccristy      output_scaling = fabs(StringToDouble(artifact,(char **) NULL));
2251003ea07237ca13637960e607f6686bd3cf03ef00cristy      geometry.width=(size_t) (output_scaling*geometry.width+0.5);
2252003ea07237ca13637960e607f6686bd3cf03ef00cristy      geometry.height=(size_t) (output_scaling*geometry.height+0.5);
2253003ea07237ca13637960e607f6686bd3cf03ef00cristy      geometry.x=(ssize_t) (output_scaling*geometry.x+0.5);
2254003ea07237ca13637960e607f6686bd3cf03ef00cristy      geometry.y=(ssize_t) (output_scaling*geometry.y+0.5);
22553ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      if ( output_scaling < 0.1 ) {
22563ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        coeff = (double *) RelinquishMagickMemory(coeff);
22573ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
22583ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                "InvalidArgument","%s", "-set option:distort:scale" );
22593ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        return((Image *) NULL);
22603ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      }
22613ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      output_scaling = 1/output_scaling;
22623ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    }
22633ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  }
22643ed852eea50f9d4cd633efb8c2b054b8e33c253cristy#define ScaleFilter(F,A,B,C,D) \
22653ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    ScaleResampleFilter( (F), \
22663ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      output_scaling*(A), output_scaling*(B), \
22673ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      output_scaling*(C), output_scaling*(D) )
22683ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
22693ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  /*
22703ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    Initialize the distort image attributes.
22713ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  */
22723ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  distort_image=CloneImage(image,geometry.width,geometry.height,MagickTrue,
22733ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    exception);
22743ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  if (distort_image == (Image *) NULL)
22753ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    return((Image *) NULL);
2276b6d08c51e1666814b156d42aef650f41d33a87baanthony  /* if image is ColorMapped - change it to DirectClass */
2277574cc26500992189f637cd1cdf93d0654e7df7aecristy  if (SetImageStorageClass(distort_image,DirectClass,exception) == MagickFalse)
2278b6d08c51e1666814b156d42aef650f41d33a87baanthony    {
22793ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      distort_image=DestroyImage(distort_image);
22803ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      return((Image *) NULL);
22813ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    }
2282a6400b1cfc3594644a0f02f3d77d920092f078eecristy  if ((IsPixelInfoGray(&distort_image->background_color) == MagickFalse) &&
2283a6400b1cfc3594644a0f02f3d77d920092f078eecristy      (IsGrayColorspace(distort_image->colorspace) != MagickFalse))
22840c81d063030f7b30a97c7856e95534243cdc9e13cristy    (void) SetImageColorspace(distort_image,sRGBColorspace,exception);
228517f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy  if (distort_image->background_color.alpha_trait != UndefinedPixelTrait)
22868a46d827a124555f0c48fb2368ec1bba8e079ab6cristy    distort_image->alpha_trait=BlendPixelTrait;
22876386b796857c3ef5db3858ebb2d64373273082b1cristy  distort_image->page.x=geometry.x;
22886386b796857c3ef5db3858ebb2d64373273082b1cristy  distort_image->page.y=geometry.y;
22893ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
22903ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  { /* ----- MAIN CODE -----
22913ed852eea50f9d4cd633efb8c2b054b8e33c253cristy       Sample the source image to each pixel in the distort image.
22923ed852eea50f9d4cd633efb8c2b054b8e33c253cristy     */
2293b65a5b83785ab7b6a8f6fc2d47ba5dca776143bbcristy    CacheView
2294b65a5b83785ab7b6a8f6fc2d47ba5dca776143bbcristy      *distort_view;
2295b65a5b83785ab7b6a8f6fc2d47ba5dca776143bbcristy
22965f959473f334e196c6bf39b740c12cb4963fceebcristy    MagickBooleanType
22973ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      status;
22983ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
22995f959473f334e196c6bf39b740c12cb4963fceebcristy    MagickOffsetType
23005f959473f334e196c6bf39b740c12cb4963fceebcristy      progress;
23015f959473f334e196c6bf39b740c12cb4963fceebcristy
23024c08aed51c5899665ade97263692328eea4af106cristy    PixelInfo
23033ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      zero;
23043ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
23053ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    ResampleFilter
230605d2ff7ebf21f659f5b11e45afb294e152f4330cdirk      **magick_restrict resample_filter;
23073ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
23085f959473f334e196c6bf39b740c12cb4963fceebcristy    ssize_t
2309cee9711bbc334b5677d5ec4ea1cc70340d35ee35cristy      j;
23105f959473f334e196c6bf39b740c12cb4963fceebcristy
23113ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    status=MagickTrue;
23123ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    progress=0;
23134c08aed51c5899665ade97263692328eea4af106cristy    GetPixelInfo(distort_image,&zero);
2314b2a11ae782e3f5ba1c020d944bc5ff8382d378fecristy    resample_filter=AcquireResampleFilterThreadSet(image,
2315b2a11ae782e3f5ba1c020d944bc5ff8382d378fecristy      UndefinedVirtualPixelMethod,MagickFalse,exception);
231646ff2676b1044ea4101ac7a59b83289cd8f6cfdacristy    distort_view=AcquireAuthenticCacheView(distort_image,exception);
2317b5d5f725fef80ff5d50db3111c05a1a521b81e7fcristy#if defined(MAGICKCORE_OPENMP_SUPPORT)
2318ac245f8a51ea65b085d751c41d8ca4b426bdfe5bcristy    #pragma omp parallel for schedule(static,4) shared(progress,status) \
23195e6b259130f9dbe0da4666f734937017babe573acristy      magick_threads(image,distort_image,distort_image->rows,1)
23203ed852eea50f9d4cd633efb8c2b054b8e33c253cristy#endif
2321bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy    for (j=0; j < (ssize_t) distort_image->rows; j++)
23223ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    {
23235c9e6f2ec2e7738ede83902931da0f80db60f565cristy      const int
23245c9e6f2ec2e7738ede83902931da0f80db60f565cristy        id = GetOpenMPThreadId();
23255c9e6f2ec2e7738ede83902931da0f80db60f565cristy
23263ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      double
23273ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        validity;  /* how mathematically valid is this the mapping */
23283ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
23293ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      MagickBooleanType
23303ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        sync;
23313ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
23324c08aed51c5899665ade97263692328eea4af106cristy      PixelInfo
23333ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        pixel,    /* pixel color to assign to distorted image */
23343ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        invalid;  /* the color to assign when distort result is invalid */
23353ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
23363ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      PointInfo
2337b2a11ae782e3f5ba1c020d944bc5ff8382d378fecristy        d,
2338b2a11ae782e3f5ba1c020d944bc5ff8382d378fecristy        s;  /* transform destination image x,y  to source image x,y */
23393ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
2340bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      register ssize_t
2341688cc42ece058cfd749a7e941d3abee7af7cdaa5cristy        i;
23423ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
23434c08aed51c5899665ade97263692328eea4af106cristy      register Quantum
234405d2ff7ebf21f659f5b11e45afb294e152f4330cdirk        *magick_restrict q;
23453ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
23463ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      q=QueueCacheViewAuthenticPixels(distort_view,0,j,distort_image->columns,1,
23473ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        exception);
2348acd2ed254c18c254a0ab5aafa06d1645e5d079d8cristy      if (q == (Quantum *) NULL)
23493ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        {
23503ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          status=MagickFalse;
23513ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          continue;
23523ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        }
23533ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      pixel=zero;
23543ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
23553ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      /* Define constant scaling vectors for Affine Distortions
23563ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        Other methods are either variable, or use interpolated lookup
23573ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      */
23583ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      switch (method)
23593ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      {
23603ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        case AffineDistortion:
23613ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          ScaleFilter( resample_filter[id],
23623ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            coeff[0], coeff[1],
23633ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            coeff[3], coeff[4] );
23643ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          break;
23653ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        default:
23663ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          break;
23673ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      }
23683ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
23693ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      /* Initialize default pixel validity
2370b797b2c96f796f4920c9ee807dc5f47e6f160418dirk      *    negative:         pixel is invalid  output 'alpha_color'
23713ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      *    0.0 to 1.0:       antialiased, mix with resample output
23723ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      *    1.0 or greater:   use resampled output.
23733ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      */
23743ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      validity = 1.0;
23753ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
2376b797b2c96f796f4920c9ee807dc5f47e6f160418dirk      ConformPixelInfo(distort_image,&distort_image->alpha_color,&invalid,
2377bfdd5bc06830778f14dfa0659b389ff630b7fbfedirk        exception);
2378bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      for (i=0; i < (ssize_t) distort_image->columns; i++)
23793ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      {
23803ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        /* map pixel coordinate to distortion space coordinate */
23813ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        d.x = (double) (geometry.x+i+0.5)*output_scaling;
23823ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        d.y = (double) (geometry.y+j+0.5)*output_scaling;
23833ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        s = d;  /* default is a no-op mapping */
23843ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        switch (method)
23853ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        {
23863ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          case AffineDistortion:
23873ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          {
23883ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            s.x=coeff[0]*d.x+coeff[1]*d.y+coeff[2];
23893ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            s.y=coeff[3]*d.x+coeff[4]*d.y+coeff[5];
23903ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            /* Affine partial derivitives are constant -- set above */
23913ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            break;
23923ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          }
23933ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          case PerspectiveDistortion:
23943ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          {
23953ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            double
23963ed852eea50f9d4cd633efb8c2b054b8e33c253cristy              p,q,r,abs_r,abs_c6,abs_c7,scale;
23973ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            /* perspective is a ratio of affines */
23983ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            p=coeff[0]*d.x+coeff[1]*d.y+coeff[2];
23993ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            q=coeff[3]*d.x+coeff[4]*d.y+coeff[5];
24003ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            r=coeff[6]*d.x+coeff[7]*d.y+1.0;
24013ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            /* Pixel Validity -- is it a 'sky' or 'ground' pixel */
24023ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            validity = (r*coeff[8] < 0.0) ? 0.0 : 1.0;
24033ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            /* Determine horizon anti-alias blending */
24043ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            abs_r = fabs(r)*2;
24053ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            abs_c6 = fabs(coeff[6]);
24063ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            abs_c7 = fabs(coeff[7]);
24073ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            if ( abs_c6 > abs_c7 ) {
24085c43782df7dd06579c309a16384c1d35657c7891anthony              if ( abs_r < abs_c6*output_scaling )
24095c43782df7dd06579c309a16384c1d35657c7891anthony                validity = 0.5 - coeff[8]*r/(coeff[6]*output_scaling);
24103ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            }
24115c43782df7dd06579c309a16384c1d35657c7891anthony            else if ( abs_r < abs_c7*output_scaling )
24125c43782df7dd06579c309a16384c1d35657c7891anthony              validity = 0.5 - coeff[8]*r/(coeff[7]*output_scaling);
24133ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            /* Perspective Sampling Point (if valid) */
24143ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            if ( validity > 0.0 ) {
24153ed852eea50f9d4cd633efb8c2b054b8e33c253cristy              /* divide by r affine, for perspective scaling */
24163ed852eea50f9d4cd633efb8c2b054b8e33c253cristy              scale = 1.0/r;
24173ed852eea50f9d4cd633efb8c2b054b8e33c253cristy              s.x = p*scale;
24183ed852eea50f9d4cd633efb8c2b054b8e33c253cristy              s.y = q*scale;
24193ed852eea50f9d4cd633efb8c2b054b8e33c253cristy              /* Perspective Partial Derivatives or Scaling Vectors */
24203ed852eea50f9d4cd633efb8c2b054b8e33c253cristy              scale *= scale;
24213ed852eea50f9d4cd633efb8c2b054b8e33c253cristy              ScaleFilter( resample_filter[id],
24223ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                (r*coeff[0] - p*coeff[6])*scale,
24233ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                (r*coeff[1] - p*coeff[7])*scale,
24243ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                (r*coeff[3] - q*coeff[6])*scale,
24253ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                (r*coeff[4] - q*coeff[7])*scale );
24263ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            }
24273ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            break;
24283ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          }
24293ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          case BilinearReverseDistortion:
24303ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          {
24315056b230558c7de0a79d51d82bdc929fef45a225anthony            /* Reversed Mapped is just a simple polynomial */
24325056b230558c7de0a79d51d82bdc929fef45a225anthony            s.x=coeff[0]*d.x+coeff[1]*d.y+coeff[2]*d.x*d.y+coeff[3];
24333ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            s.y=coeff[4]*d.x+coeff[5]*d.y
24343ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                    +coeff[6]*d.x*d.y+coeff[7];
24353ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            /* Bilinear partial derivitives of scaling vectors */
24363ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            ScaleFilter( resample_filter[id],
24373ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                coeff[0] + coeff[2]*d.y,
24383ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                coeff[1] + coeff[2]*d.x,
24393ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                coeff[4] + coeff[6]*d.y,
24403ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                coeff[5] + coeff[6]*d.x );
24413ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            break;
24423ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          }
24433ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          case BilinearForwardDistortion:
24443ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          {
24455056b230558c7de0a79d51d82bdc929fef45a225anthony            /* Forward mapped needs reversed polynomial equations
24465056b230558c7de0a79d51d82bdc929fef45a225anthony             * which unfortunatally requires a square root!  */
24475056b230558c7de0a79d51d82bdc929fef45a225anthony            double b,c;
24483ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            d.x -= coeff[3];  d.y -= coeff[7];
24493ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            b = coeff[6]*d.x - coeff[2]*d.y + coeff[8];
24505056b230558c7de0a79d51d82bdc929fef45a225anthony            c = coeff[4]*d.x - coeff[0]*d.y;
24515056b230558c7de0a79d51d82bdc929fef45a225anthony
24525056b230558c7de0a79d51d82bdc929fef45a225anthony            validity = 1.0;
2453e0d9bbde3eb1826c767fee326a5786a9f28a191banthony            /* Handle Special degenerate (non-quadratic) case
2454e0d9bbde3eb1826c767fee326a5786a9f28a191banthony             * Currently without horizon anti-alising */
24555056b230558c7de0a79d51d82bdc929fef45a225anthony            if ( fabs(coeff[9]) < MagickEpsilon )
24565056b230558c7de0a79d51d82bdc929fef45a225anthony              s.y =  -c/b;
24575056b230558c7de0a79d51d82bdc929fef45a225anthony            else {
24585056b230558c7de0a79d51d82bdc929fef45a225anthony              c = b*b - 2*coeff[9]*c;
24595056b230558c7de0a79d51d82bdc929fef45a225anthony              if ( c < 0.0 )
24605056b230558c7de0a79d51d82bdc929fef45a225anthony                validity = 0.0;
24615056b230558c7de0a79d51d82bdc929fef45a225anthony              else
24625056b230558c7de0a79d51d82bdc929fef45a225anthony                s.y = ( -b + sqrt(c) )/coeff[9];
24633ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            }
24645056b230558c7de0a79d51d82bdc929fef45a225anthony            if ( validity > 0.0 )
24655056b230558c7de0a79d51d82bdc929fef45a225anthony              s.x = ( d.x - coeff[1]*s.y) / ( coeff[0] + coeff[2]*s.y );
24665056b230558c7de0a79d51d82bdc929fef45a225anthony
24673ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            /* NOTE: the sign of the square root should be -ve for parts
24685056b230558c7de0a79d51d82bdc929fef45a225anthony                     where the source image becomes 'flipped' or 'mirrored'.
24695056b230558c7de0a79d51d82bdc929fef45a225anthony               FUTURE: Horizon handling
24705056b230558c7de0a79d51d82bdc929fef45a225anthony               FUTURE: Scaling factors or Deritives (how?)
24713ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            */
24723ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            break;
24733ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          }
24745056b230558c7de0a79d51d82bdc929fef45a225anthony#if 0
247588ef4d9fc7a5b3b5a194b1c5736df5360c830344anthony          case BilinearDistortion:
24765056b230558c7de0a79d51d82bdc929fef45a225anthony            /* Bilinear mapping of any Quadrilateral to any Quadrilateral */
24775056b230558c7de0a79d51d82bdc929fef45a225anthony            /* UNDER DEVELOPMENT */
24785056b230558c7de0a79d51d82bdc929fef45a225anthony            break;
24795056b230558c7de0a79d51d82bdc929fef45a225anthony#endif
24803ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          case PolynomialDistortion:
24813ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          {
24825056b230558c7de0a79d51d82bdc929fef45a225anthony            /* multi-ordered polynomial */
2483bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            register ssize_t
24843ed852eea50f9d4cd633efb8c2b054b8e33c253cristy              k;
24859d314ff2c17a77996c05413c2013880387e50f0ecristy
2486bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            ssize_t
2487bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy              nterms=(ssize_t)coeff[1];
24883ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
24893ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            PointInfo
24903ed852eea50f9d4cd633efb8c2b054b8e33c253cristy              du,dv; /* the du,dv vectors from unit dx,dy -- derivatives */
24913ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
24923ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            s.x=s.y=du.x=du.y=dv.x=dv.y=0.0;
24933ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            for(k=0; k < nterms; k++) {
24943ed852eea50f9d4cd633efb8c2b054b8e33c253cristy              s.x  += poly_basis_fn(k,d.x,d.y)*coeff[2+k];
24953ed852eea50f9d4cd633efb8c2b054b8e33c253cristy              du.x += poly_basis_dx(k,d.x,d.y)*coeff[2+k];
24963ed852eea50f9d4cd633efb8c2b054b8e33c253cristy              du.y += poly_basis_dy(k,d.x,d.y)*coeff[2+k];
24973ed852eea50f9d4cd633efb8c2b054b8e33c253cristy              s.y  += poly_basis_fn(k,d.x,d.y)*coeff[2+k+nterms];
24983ed852eea50f9d4cd633efb8c2b054b8e33c253cristy              dv.x += poly_basis_dx(k,d.x,d.y)*coeff[2+k+nterms];
24993ed852eea50f9d4cd633efb8c2b054b8e33c253cristy              dv.y += poly_basis_dy(k,d.x,d.y)*coeff[2+k+nterms];
25003ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            }
25013ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            ScaleFilter( resample_filter[id], du.x,du.y,dv.x,dv.y );
25023ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            break;
25033ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          }
25043ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          case ArcDistortion:
25053ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          {
25063ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            /* what is the angle and radius in the destination image */
2507ba978e102735661f7777d22c1674d1d974303072cristy            s.x  = (double) ((atan2(d.y,d.x) - coeff[0])/Magick2PI);
25083ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            s.x -= MagickRound(s.x);     /* angle */
25093ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            s.y  = hypot(d.x,d.y);       /* radius */
25103ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
25113ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            /* Arc Distortion Partial Scaling Vectors
25123ed852eea50f9d4cd633efb8c2b054b8e33c253cristy              Are derived by mapping the perpendicular unit vectors
25133ed852eea50f9d4cd633efb8c2b054b8e33c253cristy              dR  and  dA*R*2PI  rather than trying to map dx and dy
25143ed852eea50f9d4cd633efb8c2b054b8e33c253cristy              The results is a very simple orthogonal aligned ellipse.
25153ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            */
25163ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            if ( s.y > MagickEpsilon )
25173ed852eea50f9d4cd633efb8c2b054b8e33c253cristy              ScaleFilter( resample_filter[id],
2518ba978e102735661f7777d22c1674d1d974303072cristy                  (double) (coeff[1]/(Magick2PI*s.y)), 0, 0, coeff[3] );
25193ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            else
25203ed852eea50f9d4cd633efb8c2b054b8e33c253cristy              ScaleFilter( resample_filter[id],
25213ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                  distort_image->columns*2, 0, 0, coeff[3] );
25223ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
25233ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            /* now scale the angle and radius for source image lookup point */
25243ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            s.x = s.x*coeff[1] + coeff[4] + image->page.x +0.5;
25253ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            s.y = (coeff[2] - s.y) * coeff[3] + image->page.y;
25263ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            break;
25273ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          }
25283ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          case PolarDistortion:
2529b9aaf9ed2a83b7d8931306fb3313285098072b25anthony          { /* 2D Cartesain to Polar View */
25303ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            d.x -= coeff[2];
25313ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            d.y -= coeff[3];
25323ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            s.x  = atan2(d.x,d.y) - (coeff[4]+coeff[5])/2;
25333ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            s.x /= Magick2PI;
25343ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            s.x -= MagickRound(s.x);
25353ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            s.x *= Magick2PI;       /* angle - relative to centerline */
25363ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            s.y  = hypot(d.x,d.y);  /* radius */
25373ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
25383ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            /* Polar Scaling vectors are based on mapping dR and dA vectors
25393ed852eea50f9d4cd633efb8c2b054b8e33c253cristy               This results in very simple orthogonal scaling vectors
25403ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            */
25413ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            if ( s.y > MagickEpsilon )
25423ed852eea50f9d4cd633efb8c2b054b8e33c253cristy              ScaleFilter( resample_filter[id],
2543ba978e102735661f7777d22c1674d1d974303072cristy                (double) (coeff[6]/(Magick2PI*s.y)), 0, 0, coeff[7] );
25443ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            else
25453ed852eea50f9d4cd633efb8c2b054b8e33c253cristy              ScaleFilter( resample_filter[id],
25463ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                  distort_image->columns*2, 0, 0, coeff[7] );
25473ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
25483ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            /* now finish mapping radius/angle to source x,y coords */
25493ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            s.x = s.x*coeff[6] + (double)image->columns/2.0 + image->page.x;
25503ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            s.y = (s.y-coeff[1])*coeff[7] + image->page.y;
25513ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            break;
25523ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          }
25533ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          case DePolarDistortion:
2554b9aaf9ed2a83b7d8931306fb3313285098072b25anthony          { /* @D Polar to Carteasain  */
25553ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            /* ignore all destination virtual offsets */
255673144553d112665377fdd6f1ab5e152cff02b956anthony            d.x = ((double)i+0.5)*output_scaling*coeff[6]+coeff[4];
25573ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            d.y = ((double)j+0.5)*output_scaling*coeff[7]+coeff[1];
25583ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            s.x = d.y*sin(d.x) + coeff[2];
25593ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            s.y = d.y*cos(d.x) + coeff[3];
25603ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            /* derivatives are usless - better to use SuperSampling */
25613ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            break;
25623ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          }
2563e0d9bbde3eb1826c767fee326a5786a9f28a191banthony          case Cylinder2PlaneDistortion:
2564b9aaf9ed2a83b7d8931306fb3313285098072b25anthony          { /* 3D Cylinder to Tangential Plane */
2565f3dac0af657aa837c1bda54d1eafeced62b92e19anthony            double ax, cx;
2566f3dac0af657aa837c1bda54d1eafeced62b92e19anthony            /* relative to center of distortion */
2567f3dac0af657aa837c1bda54d1eafeced62b92e19anthony            d.x -= coeff[4]; d.y -= coeff[5];
2568f3dac0af657aa837c1bda54d1eafeced62b92e19anthony            d.x /= coeff[1];        /* x' = x/r */
2569f3dac0af657aa837c1bda54d1eafeced62b92e19anthony            ax=atan(d.x);           /* aa = atan(x/r) = u/r  */
2570f3dac0af657aa837c1bda54d1eafeced62b92e19anthony            cx=cos(ax);             /* cx = cos(atan(x/r)) = 1/sqrt(x^2+u^2) */
2571f3dac0af657aa837c1bda54d1eafeced62b92e19anthony            s.x = coeff[1]*ax;      /* u  = r*atan(x/r) */
2572f3dac0af657aa837c1bda54d1eafeced62b92e19anthony            s.y = d.y*cx;           /* v  = y*cos(u/r) */
2573f3dac0af657aa837c1bda54d1eafeced62b92e19anthony            /* derivatives... (see personnal notes) */
2574f3dac0af657aa837c1bda54d1eafeced62b92e19anthony            ScaleFilter( resample_filter[id],
2575f3dac0af657aa837c1bda54d1eafeced62b92e19anthony                  1.0/(1.0+d.x*d.x), 0.0, -d.x*s.y*cx*cx/coeff[1], s.y/d.y );
2576f3dac0af657aa837c1bda54d1eafeced62b92e19anthony#if 0
2577f3dac0af657aa837c1bda54d1eafeced62b92e19anthonyif ( i == 0 && j == 0 ) {
2578f3dac0af657aa837c1bda54d1eafeced62b92e19anthony  fprintf(stderr, "x=%lf  y=%lf  u=%lf  v=%lf\n", d.x*coeff[1], d.y, s.x, s.y);
2579f3dac0af657aa837c1bda54d1eafeced62b92e19anthony  fprintf(stderr, "phi = %lf\n", (double)(ax * 180.0/MagickPI) );
2580f3dac0af657aa837c1bda54d1eafeced62b92e19anthony  fprintf(stderr, "du/dx=%lf  du/dx=%lf  dv/dx=%lf  dv/dy=%lf\n",
2581f3dac0af657aa837c1bda54d1eafeced62b92e19anthony                1.0/(1.0+d.x*d.x), 0.0, -d.x*s.y*cx*cx/coeff[1], s.y/d.y );
2582f3dac0af657aa837c1bda54d1eafeced62b92e19anthony  fflush(stderr); }
2583f3dac0af657aa837c1bda54d1eafeced62b92e19anthony#endif
2584f3dac0af657aa837c1bda54d1eafeced62b92e19anthony            /* add center of distortion in source */
2585f3dac0af657aa837c1bda54d1eafeced62b92e19anthony            s.x += coeff[2]; s.y += coeff[3];
2586b9aaf9ed2a83b7d8931306fb3313285098072b25anthony            break;
2587b9aaf9ed2a83b7d8931306fb3313285098072b25anthony          }
2588e0d9bbde3eb1826c767fee326a5786a9f28a191banthony          case Plane2CylinderDistortion:
2589b9aaf9ed2a83b7d8931306fb3313285098072b25anthony          { /* 3D Cylinder to Tangential Plane */
2590f3dac0af657aa837c1bda54d1eafeced62b92e19anthony            /* relative to center of distortion */
2591f3dac0af657aa837c1bda54d1eafeced62b92e19anthony            d.x -= coeff[4]; d.y -= coeff[5];
2592e0d9bbde3eb1826c767fee326a5786a9f28a191banthony
2593e0d9bbde3eb1826c767fee326a5786a9f28a191banthony            /* is pixel valid - horizon of a infinite Virtual-Pixel Plane
2594e0d9bbde3eb1826c767fee326a5786a9f28a191banthony             * (see Anthony Thyssen's personal note) */
25954c08aed51c5899665ade97263692328eea4af106cristy            validity = (double) (coeff[1]*MagickPI2 - fabs(d.x))/output_scaling + 0.5;
2596e0d9bbde3eb1826c767fee326a5786a9f28a191banthony
2597e0d9bbde3eb1826c767fee326a5786a9f28a191banthony            if ( validity > 0.0 ) {
2598e0d9bbde3eb1826c767fee326a5786a9f28a191banthony              double cx,tx;
2599e0d9bbde3eb1826c767fee326a5786a9f28a191banthony              d.x /= coeff[1];           /* x'= x/r */
2600e0d9bbde3eb1826c767fee326a5786a9f28a191banthony              cx = 1/cos(d.x);           /* cx = 1/cos(x/r) */
2601e0d9bbde3eb1826c767fee326a5786a9f28a191banthony              tx = tan(d.x);             /* tx = tan(x/r) */
2602e0d9bbde3eb1826c767fee326a5786a9f28a191banthony              s.x = coeff[1]*tx;         /* u = r * tan(x/r) */
2603e0d9bbde3eb1826c767fee326a5786a9f28a191banthony              s.y = d.y*cx;              /* v = y / cos(x/r) */
2604e0d9bbde3eb1826c767fee326a5786a9f28a191banthony              /* derivatives...  (see Anthony Thyssen's personal notes) */
2605e0d9bbde3eb1826c767fee326a5786a9f28a191banthony              ScaleFilter( resample_filter[id],
2606e0d9bbde3eb1826c767fee326a5786a9f28a191banthony                    cx*cx, 0.0, s.y*cx/coeff[1], cx );
260786b99553adc2b1a8bbbe50cac358507bfdde80fcdirk#if 0
26084c08aed51c5899665ade97263692328eea4af106cristy/*if ( i == 0 && j == 0 )*/
2609d2a90402dad613980c957433ecff73d2f4a99f8aanthonyif ( d.x == 0.5 && d.y == 0.5 ) {
2610f3dac0af657aa837c1bda54d1eafeced62b92e19anthony  fprintf(stderr, "x=%lf  y=%lf  u=%lf  v=%lf\n", d.x*coeff[1], d.y, s.x, s.y);
2611e0d9bbde3eb1826c767fee326a5786a9f28a191banthony  fprintf(stderr, "radius = %lf  phi = %lf  validity = %lf\n",
2612e0d9bbde3eb1826c767fee326a5786a9f28a191banthony      coeff[1],  (double)(d.x * 180.0/MagickPI), validity );
2613f3dac0af657aa837c1bda54d1eafeced62b92e19anthony  fprintf(stderr, "du/dx=%lf  du/dx=%lf  dv/dx=%lf  dv/dy=%lf\n",
2614f3dac0af657aa837c1bda54d1eafeced62b92e19anthony      cx*cx, 0.0, s.y*cx/coeff[1], cx);
2615f3dac0af657aa837c1bda54d1eafeced62b92e19anthony  fflush(stderr); }
2616f3dac0af657aa837c1bda54d1eafeced62b92e19anthony#endif
2617e0d9bbde3eb1826c767fee326a5786a9f28a191banthony            }
2618f3dac0af657aa837c1bda54d1eafeced62b92e19anthony            /* add center of distortion in source */
2619f3dac0af657aa837c1bda54d1eafeced62b92e19anthony            s.x += coeff[2]; s.y += coeff[3];
2620b9aaf9ed2a83b7d8931306fb3313285098072b25anthony            break;
2621b9aaf9ed2a83b7d8931306fb3313285098072b25anthony          }
26223ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          case BarrelDistortion:
26233ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          case BarrelInverseDistortion:
2624b9aaf9ed2a83b7d8931306fb3313285098072b25anthony          { /* Lens Barrel Distionion Correction */
26253ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            double r,fx,fy,gx,gy;
26263ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            /* Radial Polynomial Distortion (de-normalized) */
26273ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            d.x -= coeff[8];
26283ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            d.y -= coeff[9];
26293ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            r = sqrt(d.x*d.x+d.y*d.y);
26303ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            if ( r > MagickEpsilon ) {
26313ed852eea50f9d4cd633efb8c2b054b8e33c253cristy              fx = ((coeff[0]*r + coeff[1])*r + coeff[2])*r + coeff[3];
26323ed852eea50f9d4cd633efb8c2b054b8e33c253cristy              fy = ((coeff[4]*r + coeff[5])*r + coeff[6])*r + coeff[7];
26333ed852eea50f9d4cd633efb8c2b054b8e33c253cristy              gx = ((3*coeff[0]*r + 2*coeff[1])*r + coeff[2])/r;
26343ed852eea50f9d4cd633efb8c2b054b8e33c253cristy              gy = ((3*coeff[4]*r + 2*coeff[5])*r + coeff[6])/r;
26353ed852eea50f9d4cd633efb8c2b054b8e33c253cristy              /* adjust functions and scaling for 'inverse' form */
26363ed852eea50f9d4cd633efb8c2b054b8e33c253cristy              if ( method == BarrelInverseDistortion ) {
26373ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                fx = 1/fx;  fy = 1/fy;
26383ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                gx *= -fx*fx;  gy *= -fy*fy;
26393ed852eea50f9d4cd633efb8c2b054b8e33c253cristy              }
2640ec40aca22bbc75266cdfdc60dd1fc8ebb8174139anthony              /* Set the source pixel to lookup and EWA derivative vectors */
26413ed852eea50f9d4cd633efb8c2b054b8e33c253cristy              s.x = d.x*fx + coeff[8];
26423ed852eea50f9d4cd633efb8c2b054b8e33c253cristy              s.y = d.y*fy + coeff[9];
26433ed852eea50f9d4cd633efb8c2b054b8e33c253cristy              ScaleFilter( resample_filter[id],
26443ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                  gx*d.x*d.x + fx, gx*d.x*d.y,
26453ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                  gy*d.x*d.y,      gy*d.y*d.y + fy );
26463ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            }
2647ec40aca22bbc75266cdfdc60dd1fc8ebb8174139anthony            else {
2648ec40aca22bbc75266cdfdc60dd1fc8ebb8174139anthony              /* Special handling to avoid divide by zero when r==0
2649ec40aca22bbc75266cdfdc60dd1fc8ebb8174139anthony              **
2650ec40aca22bbc75266cdfdc60dd1fc8ebb8174139anthony              ** The source and destination pixels match in this case
2651ec40aca22bbc75266cdfdc60dd1fc8ebb8174139anthony              ** which was set at the top of the loop using  s = d;
2652ec40aca22bbc75266cdfdc60dd1fc8ebb8174139anthony              ** otherwise...   s.x=coeff[8]; s.y=coeff[9];
2653ec40aca22bbc75266cdfdc60dd1fc8ebb8174139anthony              */
26543ed852eea50f9d4cd633efb8c2b054b8e33c253cristy              if ( method == BarrelDistortion )
26553ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                ScaleFilter( resample_filter[id],
26563ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                     coeff[3], 0, 0, coeff[7] );
26573ed852eea50f9d4cd633efb8c2b054b8e33c253cristy              else /* method == BarrelInverseDistortion */
26583ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                /* FUTURE, trap for D==0 causing division by zero */
26593ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                ScaleFilter( resample_filter[id],
26603ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                     1.0/coeff[3], 0, 0, 1.0/coeff[7] );
26613ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            }
26623ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            break;
26633ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          }
26643ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          case ShepardsDistortion:
26653ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          { /* Shepards Method, or Inverse Weighted Distance for
266647bb5df69322136901ccab9ad266d21ceff50500anthony               displacement around the destination image control points
266747bb5df69322136901ccab9ad266d21ceff50500anthony               The input arguments are the coefficents to the function.
266847bb5df69322136901ccab9ad266d21ceff50500anthony               This is more of a 'displacement' function rather than an
266947bb5df69322136901ccab9ad266d21ceff50500anthony               absolute distortion function.
267047bb5df69322136901ccab9ad266d21ceff50500anthony
267147bb5df69322136901ccab9ad266d21ceff50500anthony               Note: We can not determine derivatives using shepards method
267247bb5df69322136901ccab9ad266d21ceff50500anthony               so only a point sample interpolatation can be used.
26733ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            */
2674bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            size_t
26753ed852eea50f9d4cd633efb8c2b054b8e33c253cristy              i;
26763ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            double
26773ed852eea50f9d4cd633efb8c2b054b8e33c253cristy              denominator;
26783ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
26793ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            denominator = s.x = s.y = 0;
26803ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            for(i=0; i<number_arguments; i+=4) {
26813ed852eea50f9d4cd633efb8c2b054b8e33c253cristy              double weight =
26823ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                  ((double)d.x-arguments[i+2])*((double)d.x-arguments[i+2])
26833ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                + ((double)d.y-arguments[i+3])*((double)d.y-arguments[i+3]);
268447bb5df69322136901ccab9ad266d21ceff50500anthony              weight = pow(weight,coeff[0]); /* shepards power factor */
268547bb5df69322136901ccab9ad266d21ceff50500anthony              weight = ( weight < 1.0 ) ? 1.0 : 1.0/weight;
26863ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
26873ed852eea50f9d4cd633efb8c2b054b8e33c253cristy              s.x += (arguments[ i ]-arguments[i+2])*weight;
26883ed852eea50f9d4cd633efb8c2b054b8e33c253cristy              s.y += (arguments[i+1]-arguments[i+3])*weight;
26893ed852eea50f9d4cd633efb8c2b054b8e33c253cristy              denominator += weight;
26903ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            }
26913ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            s.x /= denominator;
26923ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            s.y /= denominator;
269347bb5df69322136901ccab9ad266d21ceff50500anthony            s.x += d.x;   /* make it as relative displacement */
26943ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            s.y += d.y;
26953ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            break;
26963ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          }
26973ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          default:
26983ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            break; /* use the default no-op given above */
26993ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        }
27003ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        /* map virtual canvas location back to real image coordinate */
27013ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        if ( bestfit && method != ArcDistortion ) {
27023ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          s.x -= image->page.x;
27033ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          s.y -= image->page.y;
27043ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        }
27053ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        s.x -= 0.5;
27063ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        s.y -= 0.5;
27073ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
27083ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        if ( validity <= 0.0 ) {
27093ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          /* result of distortion is an invalid pixel - don't resample */
271011a06d3f2cac0f17af7963e83bc6e9ebd2a377c0cristy          SetPixelViaPixelInfo(distort_image,&invalid,q);
27113ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        }
27123ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        else {
27133ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          /* resample the source image to find its correct color */
2714db070957cf6bf959df9283a482377e8854c3d4d2cristy          (void) ResamplePixelColor(resample_filter[id],s.x,s.y,&pixel,
2715db070957cf6bf959df9283a482377e8854c3d4d2cristy            exception);
27163ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          /* if validity between 0.0 and 1.0 mix result with invalid pixel */
27173ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          if ( validity < 1.0 ) {
27183ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            /* Do a blend of sample color and invalid pixel */
27193ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            /* should this be a 'Blend', or an 'Over' compose */
27204c08aed51c5899665ade97263692328eea4af106cristy            CompositePixelInfoBlend(&pixel,validity,&invalid,(1.0-validity),
27213ed852eea50f9d4cd633efb8c2b054b8e33c253cristy              &pixel);
27223ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          }
272311a06d3f2cac0f17af7963e83bc6e9ebd2a377c0cristy          SetPixelViaPixelInfo(distort_image,&pixel,q);
27243ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        }
2725ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy        q+=GetPixelChannels(distort_image);
27263ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      }
27273ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      sync=SyncCacheViewAuthenticPixels(distort_view,exception);
27283ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      if (sync == MagickFalse)
27293ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        status=MagickFalse;
27303ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      if (image->progress_monitor != (MagickProgressMonitor) NULL)
27313ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        {
27323ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          MagickBooleanType
27333ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            proceed;
27343ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
2735b5d5f725fef80ff5d50db3111c05a1a521b81e7fcristy#if defined(MAGICKCORE_OPENMP_SUPPORT)
2736ac245f8a51ea65b085d751c41d8ca4b426bdfe5bcristy          #pragma omp critical (MagickCore_DistortImage)
27373ed852eea50f9d4cd633efb8c2b054b8e33c253cristy#endif
27383ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          proceed=SetImageProgress(image,DistortImageTag,progress++,
27393ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            image->rows);
27403ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          if (proceed == MagickFalse)
27413ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            status=MagickFalse;
27423ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        }
27433ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    }
27443ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    distort_view=DestroyCacheView(distort_view);
27453ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    resample_filter=DestroyResampleFilterThreadSet(resample_filter);
27463ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
27473ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    if (status == MagickFalse)
27483ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      distort_image=DestroyImage(distort_image);
27493ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  }
27503ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
27513ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  /* Arc does not return an offset unless 'bestfit' is in effect
27523ed852eea50f9d4cd633efb8c2b054b8e33c253cristy     And the user has not provided an overriding 'viewport'.
27533ed852eea50f9d4cd633efb8c2b054b8e33c253cristy   */
27543ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  if ( method == ArcDistortion && !bestfit && !viewport_given ) {
27553ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    distort_image->page.x = 0;
27563ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    distort_image->page.y = 0;
27573ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  }
27583ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  coeff = (double *) RelinquishMagickMemory(coeff);
27593ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  return(distort_image);
27603ed852eea50f9d4cd633efb8c2b054b8e33c253cristy}
27613ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
27623ed852eea50f9d4cd633efb8c2b054b8e33c253cristy/*
27633ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
27643ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%                                                                             %
27653ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%                                                                             %
27663ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%                                                                             %
2767987feef6c1e3d08da3d9458fa5c464a32fc30191cristy%   R o t a t e I m a g e                                                     %
2768987feef6c1e3d08da3d9458fa5c464a32fc30191cristy%                                                                             %
2769987feef6c1e3d08da3d9458fa5c464a32fc30191cristy%                                                                             %
2770987feef6c1e3d08da3d9458fa5c464a32fc30191cristy%                                                                             %
2771987feef6c1e3d08da3d9458fa5c464a32fc30191cristy%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2772987feef6c1e3d08da3d9458fa5c464a32fc30191cristy%
2773987feef6c1e3d08da3d9458fa5c464a32fc30191cristy%  RotateImage() creates a new image that is a rotated copy of an existing
2774987feef6c1e3d08da3d9458fa5c464a32fc30191cristy%  one.  Positive angles rotate counter-clockwise (right-hand rule), while
2775987feef6c1e3d08da3d9458fa5c464a32fc30191cristy%  negative angles rotate clockwise.  Rotated images are usually larger than
2776987feef6c1e3d08da3d9458fa5c464a32fc30191cristy%  the originals and have 'empty' triangular corners.  X axis.  Empty
2777987feef6c1e3d08da3d9458fa5c464a32fc30191cristy%  triangles left over from shearing the image are filled with the background
2778987feef6c1e3d08da3d9458fa5c464a32fc30191cristy%  color defined by member 'background_color' of the image.  RotateImage
2779987feef6c1e3d08da3d9458fa5c464a32fc30191cristy%  allocates the memory necessary for the new Image structure and returns a
2780987feef6c1e3d08da3d9458fa5c464a32fc30191cristy%  pointer to the new image.
2781987feef6c1e3d08da3d9458fa5c464a32fc30191cristy%
2782987feef6c1e3d08da3d9458fa5c464a32fc30191cristy%  The format of the RotateImage method is:
2783987feef6c1e3d08da3d9458fa5c464a32fc30191cristy%
2784987feef6c1e3d08da3d9458fa5c464a32fc30191cristy%      Image *RotateImage(const Image *image,const double degrees,
2785987feef6c1e3d08da3d9458fa5c464a32fc30191cristy%        ExceptionInfo *exception)
2786987feef6c1e3d08da3d9458fa5c464a32fc30191cristy%
2787987feef6c1e3d08da3d9458fa5c464a32fc30191cristy%  A description of each parameter follows.
2788987feef6c1e3d08da3d9458fa5c464a32fc30191cristy%
2789987feef6c1e3d08da3d9458fa5c464a32fc30191cristy%    o image: the image.
2790987feef6c1e3d08da3d9458fa5c464a32fc30191cristy%
2791987feef6c1e3d08da3d9458fa5c464a32fc30191cristy%    o degrees: Specifies the number of degrees to rotate the image.
2792987feef6c1e3d08da3d9458fa5c464a32fc30191cristy%
2793987feef6c1e3d08da3d9458fa5c464a32fc30191cristy%    o exception: return any errors or warnings in this structure.
2794987feef6c1e3d08da3d9458fa5c464a32fc30191cristy%
2795987feef6c1e3d08da3d9458fa5c464a32fc30191cristy*/
2796987feef6c1e3d08da3d9458fa5c464a32fc30191cristyMagickExport Image *RotateImage(const Image *image,const double degrees,
2797987feef6c1e3d08da3d9458fa5c464a32fc30191cristy  ExceptionInfo *exception)
2798987feef6c1e3d08da3d9458fa5c464a32fc30191cristy{
2799987feef6c1e3d08da3d9458fa5c464a32fc30191cristy  Image
2800e724ba5d4a031534c55b2b63cb38b093fae9e2d4cristy    *distort_image,
2801987feef6c1e3d08da3d9458fa5c464a32fc30191cristy    *rotate_image;
2802987feef6c1e3d08da3d9458fa5c464a32fc30191cristy
2803a19f1d70e9a9f88279c4ecafe6dfafc1f9a09599cristy  double
2804987feef6c1e3d08da3d9458fa5c464a32fc30191cristy    angle;
2805987feef6c1e3d08da3d9458fa5c464a32fc30191cristy
2806987feef6c1e3d08da3d9458fa5c464a32fc30191cristy  PointInfo
2807987feef6c1e3d08da3d9458fa5c464a32fc30191cristy    shear;
2808987feef6c1e3d08da3d9458fa5c464a32fc30191cristy
2809987feef6c1e3d08da3d9458fa5c464a32fc30191cristy  size_t
2810987feef6c1e3d08da3d9458fa5c464a32fc30191cristy    rotations;
2811987feef6c1e3d08da3d9458fa5c464a32fc30191cristy
2812987feef6c1e3d08da3d9458fa5c464a32fc30191cristy  /*
2813987feef6c1e3d08da3d9458fa5c464a32fc30191cristy    Adjust rotation angle.
2814987feef6c1e3d08da3d9458fa5c464a32fc30191cristy  */
2815987feef6c1e3d08da3d9458fa5c464a32fc30191cristy  assert(image != (Image *) NULL);
2816e1c94d9d25db6b0dd7a5028ffee31d1057855d73cristy  assert(image->signature == MagickCoreSignature);
2817987feef6c1e3d08da3d9458fa5c464a32fc30191cristy  if (image->debug != MagickFalse)
2818987feef6c1e3d08da3d9458fa5c464a32fc30191cristy    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2819987feef6c1e3d08da3d9458fa5c464a32fc30191cristy  assert(exception != (ExceptionInfo *) NULL);
2820e1c94d9d25db6b0dd7a5028ffee31d1057855d73cristy  assert(exception->signature == MagickCoreSignature);
2821987feef6c1e3d08da3d9458fa5c464a32fc30191cristy  angle=degrees;
2822987feef6c1e3d08da3d9458fa5c464a32fc30191cristy  while (angle < -45.0)
2823987feef6c1e3d08da3d9458fa5c464a32fc30191cristy    angle+=360.0;
2824987feef6c1e3d08da3d9458fa5c464a32fc30191cristy  for (rotations=0; angle > 45.0; rotations++)
2825987feef6c1e3d08da3d9458fa5c464a32fc30191cristy    angle-=90.0;
2826987feef6c1e3d08da3d9458fa5c464a32fc30191cristy  rotations%=4;
2827987feef6c1e3d08da3d9458fa5c464a32fc30191cristy  shear.x=(-tan((double) DegreesToRadians(angle)/2.0));
2828987feef6c1e3d08da3d9458fa5c464a32fc30191cristy  shear.y=sin((double) DegreesToRadians(angle));
2829987feef6c1e3d08da3d9458fa5c464a32fc30191cristy  if ((fabs(shear.x) < MagickEpsilon) && (fabs(shear.y) < MagickEpsilon))
2830987feef6c1e3d08da3d9458fa5c464a32fc30191cristy    return(IntegralRotateImage(image,rotations,exception));
2831e724ba5d4a031534c55b2b63cb38b093fae9e2d4cristy  distort_image=CloneImage(image,0,0,MagickTrue,exception);
2832e724ba5d4a031534c55b2b63cb38b093fae9e2d4cristy  if (distort_image == (Image *) NULL)
2833e724ba5d4a031534c55b2b63cb38b093fae9e2d4cristy    return((Image *) NULL);
2834387430f111ba7864990ca03fbc89968e20074ecdcristy  (void) SetImageVirtualPixelMethod(distort_image,BackgroundVirtualPixelMethod,
2835387430f111ba7864990ca03fbc89968e20074ecdcristy    exception);
2836e724ba5d4a031534c55b2b63cb38b093fae9e2d4cristy  rotate_image=DistortImage(distort_image,ScaleRotateTranslateDistortion,1,
2837e724ba5d4a031534c55b2b63cb38b093fae9e2d4cristy    &degrees,MagickTrue,exception);
2838e724ba5d4a031534c55b2b63cb38b093fae9e2d4cristy  distort_image=DestroyImage(distort_image);
2839987feef6c1e3d08da3d9458fa5c464a32fc30191cristy  return(rotate_image);
2840987feef6c1e3d08da3d9458fa5c464a32fc30191cristy}
2841987feef6c1e3d08da3d9458fa5c464a32fc30191cristy
2842987feef6c1e3d08da3d9458fa5c464a32fc30191cristy/*
2843987feef6c1e3d08da3d9458fa5c464a32fc30191cristy%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2844987feef6c1e3d08da3d9458fa5c464a32fc30191cristy%                                                                             %
2845987feef6c1e3d08da3d9458fa5c464a32fc30191cristy%                                                                             %
2846987feef6c1e3d08da3d9458fa5c464a32fc30191cristy%                                                                             %
28473ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%   S p a r s e C o l o r I m a g e                                           %
28483ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%                                                                             %
28493ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%                                                                             %
28503ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%                                                                             %
28513ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
28523ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%
28533ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%  SparseColorImage(), given a set of coordinates, interpolates the colors
28543ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%  found at those coordinates, across the whole image, using various methods.
28553ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%
28563ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%  The format of the SparseColorImage() method is:
28573ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%
28583884f69ac6f82e16fc379fb6c6521960c19045a2cristy%      Image *SparseColorImage(const Image *image,
2859bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy%        const SparseColorMethod method,const size_t number_arguments,
28603ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%        const double *arguments,ExceptionInfo *exception)
28613ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%
28623ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%  A description of each parameter follows:
28633ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%
28643ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%    o image: the image to be filled in.
28653ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%
28663ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%    o method: the method to fill in the gradient between the control points.
28673ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%
28683ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%        The methods used for SparseColor() are often simular to methods
28693ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%        used for DistortImage(), and even share the same code for determination
28703ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%        of the function coefficents, though with more dimensions (or resulting
28713ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%        values).
28723ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%
28733ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%    o number_arguments: the number of arguments given.
28743ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%
28753ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%    o arguments: array of floating point arguments for this method--
28763ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%        x,y,color_values-- with color_values given as normalized values.
28773ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%
28783ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%    o exception: return any errors or warnings in this structure
28793ed852eea50f9d4cd633efb8c2b054b8e33c253cristy%
28803ed852eea50f9d4cd633efb8c2b054b8e33c253cristy*/
28813ed852eea50f9d4cd633efb8c2b054b8e33c253cristyMagickExport Image *SparseColorImage(const Image *image,
28823884f69ac6f82e16fc379fb6c6521960c19045a2cristy  const SparseColorMethod method,const size_t number_arguments,
28833884f69ac6f82e16fc379fb6c6521960c19045a2cristy  const double *arguments,ExceptionInfo *exception)
28843ed852eea50f9d4cd633efb8c2b054b8e33c253cristy{
28853ed852eea50f9d4cd633efb8c2b054b8e33c253cristy#define SparseColorTag  "Distort/SparseColor"
28863ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
2887216e87a01399287638cc24e70c8cc8947205004fanthony  SparseColorMethod
2888216e87a01399287638cc24e70c8cc8947205004fanthony    sparse_method;
28893ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
28903ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  double
28913ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    *coeff;
28923ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
28933ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  Image
28943ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    *sparse_image;
28953ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
2896bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  size_t
28973ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    number_colors;
28983ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
28993ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  assert(image != (Image *) NULL);
2900e1c94d9d25db6b0dd7a5028ffee31d1057855d73cristy  assert(image->signature == MagickCoreSignature);
29013ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  if (image->debug != MagickFalse)
29023ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
29033ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  assert(exception != (ExceptionInfo *) NULL);
2904e1c94d9d25db6b0dd7a5028ffee31d1057855d73cristy  assert(exception->signature == MagickCoreSignature);
29053ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
29063ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  /* Determine number of color values needed per control point */
29073ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  number_colors=0;
2908ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy  if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
29093884f69ac6f82e16fc379fb6c6521960c19045a2cristy    number_colors++;
2910ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy  if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
29113884f69ac6f82e16fc379fb6c6521960c19045a2cristy    number_colors++;
2912ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy  if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
29133884f69ac6f82e16fc379fb6c6521960c19045a2cristy    number_colors++;
2914ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy  if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
29153884f69ac6f82e16fc379fb6c6521960c19045a2cristy      (image->colorspace == CMYKColorspace))
29163884f69ac6f82e16fc379fb6c6521960c19045a2cristy    number_colors++;
2917ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy  if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
291817f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy      (image->alpha_trait != UndefinedPixelTrait))
29193884f69ac6f82e16fc379fb6c6521960c19045a2cristy    number_colors++;
29203ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
29213ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  /*
2922216e87a01399287638cc24e70c8cc8947205004fanthony    Convert input arguments into mapping coefficients, this this case
2923216e87a01399287638cc24e70c8cc8947205004fanthony    we are mapping (distorting) colors, rather than coordinates.
29243ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  */
2925827944d54938c1f8a74ff53cd35c11801060d995dirk  { DistortMethod
2926216e87a01399287638cc24e70c8cc8947205004fanthony      distort_method;
2927216e87a01399287638cc24e70c8cc8947205004fanthony
2928827944d54938c1f8a74ff53cd35c11801060d995dirk    distort_method=(DistortMethod) method;
2929216e87a01399287638cc24e70c8cc8947205004fanthony    if ( distort_method >= SentinelDistortion )
2930216e87a01399287638cc24e70c8cc8947205004fanthony      distort_method = ShepardsDistortion; /* Pretend to be Shepards */
2931216e87a01399287638cc24e70c8cc8947205004fanthony    coeff = GenerateCoefficients(image, &distort_method, number_arguments,
2932216e87a01399287638cc24e70c8cc8947205004fanthony                arguments, number_colors, exception);
2933216e87a01399287638cc24e70c8cc8947205004fanthony    if ( coeff == (double *) NULL )
2934216e87a01399287638cc24e70c8cc8947205004fanthony      return((Image *) NULL);
2935216e87a01399287638cc24e70c8cc8947205004fanthony    /*
2936216e87a01399287638cc24e70c8cc8947205004fanthony      Note some Distort Methods may fall back to other simpler methods,
2937216e87a01399287638cc24e70c8cc8947205004fanthony      Currently the only fallback of concern is Bilinear to Affine
2938216e87a01399287638cc24e70c8cc8947205004fanthony      (Barycentric), which is alaso sparse_colr method.  This also ensures
2939216e87a01399287638cc24e70c8cc8947205004fanthony      correct two and one color Barycentric handling.
2940216e87a01399287638cc24e70c8cc8947205004fanthony    */
2941216e87a01399287638cc24e70c8cc8947205004fanthony    sparse_method = (SparseColorMethod) distort_method;
2942216e87a01399287638cc24e70c8cc8947205004fanthony    if ( distort_method == ShepardsDistortion )
294347bb5df69322136901ccab9ad266d21ceff50500anthony      sparse_method = method;   /* return non-distort methods to normal */
294447bb5df69322136901ccab9ad266d21ceff50500anthony    if ( sparse_method == InverseColorInterpolate )
294547bb5df69322136901ccab9ad266d21ceff50500anthony      coeff[0]=0.5;            /* sqrt() the squared distance for inverse */
2946216e87a01399287638cc24e70c8cc8947205004fanthony  }
29473ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
29483ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  /* Verbose output */
2949972c2e42a2171341bbf55a48cb36882616a591b1dirk  if (IsStringTrue(GetImageArtifact(image,"verbose")) != MagickFalse) {
29503ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
2951216e87a01399287638cc24e70c8cc8947205004fanthony    switch (sparse_method) {
29523ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      case BarycentricColorInterpolate:
29533ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      {
2954bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy        register ssize_t x=0;
29555acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "Barycentric Sparse Color:\n");
2956ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy        if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
29575acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy          (void) FormatLocaleFile(stderr, "  -channel R -fx '%+lf*i %+lf*j %+lf' \\\n",
29583ed852eea50f9d4cd633efb8c2b054b8e33c253cristy              coeff[x], coeff[x+1], coeff[x+2]),x+=3;
2959ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy        if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
29605acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy          (void) FormatLocaleFile(stderr, "  -channel G -fx '%+lf*i %+lf*j %+lf' \\\n",
29613ed852eea50f9d4cd633efb8c2b054b8e33c253cristy              coeff[x], coeff[x+1], coeff[x+2]),x+=3;
2962ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy        if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
29635acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy          (void) FormatLocaleFile(stderr, "  -channel B -fx '%+lf*i %+lf*j %+lf' \\\n",
29643ed852eea50f9d4cd633efb8c2b054b8e33c253cristy              coeff[x], coeff[x+1], coeff[x+2]),x+=3;
2965ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy        if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
29663884f69ac6f82e16fc379fb6c6521960c19045a2cristy            (image->colorspace == CMYKColorspace))
29675acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy          (void) FormatLocaleFile(stderr, "  -channel K -fx '%+lf*i %+lf*j %+lf' \\\n",
29683ed852eea50f9d4cd633efb8c2b054b8e33c253cristy              coeff[x], coeff[x+1], coeff[x+2]),x+=3;
2969ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy        if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
297017f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy            (image->alpha_trait != UndefinedPixelTrait))
29715acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy          (void) FormatLocaleFile(stderr, "  -channel A -fx '%+lf*i %+lf*j %+lf' \\\n",
29723ed852eea50f9d4cd633efb8c2b054b8e33c253cristy              coeff[x], coeff[x+1], coeff[x+2]),x+=3;
29733ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        break;
29743ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      }
29753ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      case BilinearColorInterpolate:
29763ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      {
2977bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy        register ssize_t x=0;
29785acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "Bilinear Sparse Color\n");
2979ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy        if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
29805acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy          (void) FormatLocaleFile(stderr, "   -channel R -fx '%+lf*i %+lf*j %+lf*i*j %+lf;\n",
29813ed852eea50f9d4cd633efb8c2b054b8e33c253cristy              coeff[ x ], coeff[x+1],
29823ed852eea50f9d4cd633efb8c2b054b8e33c253cristy              coeff[x+2], coeff[x+3]),x+=4;
2983ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy        if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
29845acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy          (void) FormatLocaleFile(stderr, "   -channel G -fx '%+lf*i %+lf*j %+lf*i*j %+lf;\n",
29853ed852eea50f9d4cd633efb8c2b054b8e33c253cristy              coeff[ x ], coeff[x+1],
29863ed852eea50f9d4cd633efb8c2b054b8e33c253cristy              coeff[x+2], coeff[x+3]),x+=4;
2987ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy        if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
29885acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy          (void) FormatLocaleFile(stderr, "   -channel B -fx '%+lf*i %+lf*j %+lf*i*j %+lf;\n",
29893ed852eea50f9d4cd633efb8c2b054b8e33c253cristy              coeff[ x ], coeff[x+1],
29903ed852eea50f9d4cd633efb8c2b054b8e33c253cristy              coeff[x+2], coeff[x+3]),x+=4;
2991ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy        if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
29923884f69ac6f82e16fc379fb6c6521960c19045a2cristy            (image->colorspace == CMYKColorspace))
29935acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy          (void) FormatLocaleFile(stderr, "   -channel K -fx '%+lf*i %+lf*j %+lf*i*j %+lf;\n",
29943ed852eea50f9d4cd633efb8c2b054b8e33c253cristy              coeff[ x ], coeff[x+1],
29953ed852eea50f9d4cd633efb8c2b054b8e33c253cristy              coeff[x+2], coeff[x+3]),x+=4;
2996ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy        if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
299717f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy            (image->alpha_trait != UndefinedPixelTrait))
29985acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy          (void) FormatLocaleFile(stderr, "   -channel A -fx '%+lf*i %+lf*j %+lf*i*j %+lf;\n",
29993ed852eea50f9d4cd633efb8c2b054b8e33c253cristy              coeff[ x ], coeff[x+1],
30003ed852eea50f9d4cd633efb8c2b054b8e33c253cristy              coeff[x+2], coeff[x+3]),x+=4;
30013ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        break;
30023ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      }
30033ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      default:
3004216e87a01399287638cc24e70c8cc8947205004fanthony        /* sparse color method is too complex for FX emulation */
30053ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        break;
30063ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    }
30073ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  }
30083ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
30093ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  /* Generate new image for generated interpolated gradient.
30103ed852eea50f9d4cd633efb8c2b054b8e33c253cristy   * ASIDE: Actually we could have just replaced the colors of the original
3011216e87a01399287638cc24e70c8cc8947205004fanthony   * image, but IM Core policy, is if storage class could change then clone
30123ed852eea50f9d4cd633efb8c2b054b8e33c253cristy   * the image.
30133ed852eea50f9d4cd633efb8c2b054b8e33c253cristy   */
30143ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
3015c925339225980372a6a2eaf2a93693e57a40e5ffanthony  sparse_image=CloneImage(image,0,0,MagickTrue,exception);
30163ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  if (sparse_image == (Image *) NULL)
30173ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    return((Image *) NULL);
3018574cc26500992189f637cd1cdf93d0654e7df7aecristy  if (SetImageStorageClass(sparse_image,DirectClass,exception) == MagickFalse)
30193ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    { /* if image is ColorMapped - change it to DirectClass */
30203ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      sparse_image=DestroyImage(sparse_image);
30213ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      return((Image *) NULL);
30223ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    }
30233ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  { /* ----- MAIN CODE ----- */
3024cee9711bbc334b5677d5ec4ea1cc70340d35ee35cristy    CacheView
3025cee9711bbc334b5677d5ec4ea1cc70340d35ee35cristy      *sparse_view;
30263ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
30273ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    MagickBooleanType
30283ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      status;
30293ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
3030cee9711bbc334b5677d5ec4ea1cc70340d35ee35cristy    MagickOffsetType
3031cee9711bbc334b5677d5ec4ea1cc70340d35ee35cristy      progress;
3032cee9711bbc334b5677d5ec4ea1cc70340d35ee35cristy
3033cee9711bbc334b5677d5ec4ea1cc70340d35ee35cristy    ssize_t
3034cee9711bbc334b5677d5ec4ea1cc70340d35ee35cristy      j;
30353ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
30363ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    status=MagickTrue;
30373ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    progress=0;
303846ff2676b1044ea4101ac7a59b83289cd8f6cfdacristy    sparse_view=AcquireAuthenticCacheView(sparse_image,exception);
3039b5d5f725fef80ff5d50db3111c05a1a521b81e7fcristy#if defined(MAGICKCORE_OPENMP_SUPPORT)
3040ac245f8a51ea65b085d751c41d8ca4b426bdfe5bcristy    #pragma omp parallel for schedule(static,4) shared(progress,status) \
30415e6b259130f9dbe0da4666f734937017babe573acristy      magick_threads(image,sparse_image,sparse_image->rows,1)
30423ed852eea50f9d4cd633efb8c2b054b8e33c253cristy#endif
3043bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy    for (j=0; j < (ssize_t) sparse_image->rows; j++)
30443ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    {
30453ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      MagickBooleanType
30463ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        sync;
30473ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
30484c08aed51c5899665ade97263692328eea4af106cristy      PixelInfo
30493ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        pixel;    /* pixel to assign to distorted image */
30503ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
3051bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      register ssize_t
30523ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        i;
30533ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
30544c08aed51c5899665ade97263692328eea4af106cristy      register Quantum
305505d2ff7ebf21f659f5b11e45afb294e152f4330cdirk        *magick_restrict q;
30563ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
305735a36a0014370557d0fae15cda1c1766196339e1anthony      q=GetCacheViewAuthenticPixels(sparse_view,0,j,sparse_image->columns,
30583ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        1,exception);
3059acd2ed254c18c254a0ab5aafa06d1645e5d079d8cristy      if (q == (Quantum *) NULL)
30603ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        {
30613ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          status=MagickFalse;
30623ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          continue;
30633ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        }
30644c08aed51c5899665ade97263692328eea4af106cristy      GetPixelInfo(sparse_image,&pixel);
306535a36a0014370557d0fae15cda1c1766196339e1anthony      for (i=0; i < (ssize_t) image->columns; i++)
30663ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      {
3067803640d20a6a664315eddfff6f8531d0c5e0871dcristy        GetPixelInfoPixel(image,q,&pixel);
3068216e87a01399287638cc24e70c8cc8947205004fanthony        switch (sparse_method)
30693ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        {
30703ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          case BarycentricColorInterpolate:
30713ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          {
3072bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            register ssize_t x=0;
3073ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy            if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
30743ed852eea50f9d4cd633efb8c2b054b8e33c253cristy              pixel.red     = coeff[x]*i +coeff[x+1]*j
30753ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                              +coeff[x+2], x+=3;
3076ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy            if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
30773ed852eea50f9d4cd633efb8c2b054b8e33c253cristy              pixel.green   = coeff[x]*i +coeff[x+1]*j
30783ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                              +coeff[x+2], x+=3;
3079ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy            if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
30803ed852eea50f9d4cd633efb8c2b054b8e33c253cristy              pixel.blue    = coeff[x]*i +coeff[x+1]*j
30813ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                              +coeff[x+2], x+=3;
3082ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy            if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
30833884f69ac6f82e16fc379fb6c6521960c19045a2cristy                (image->colorspace == CMYKColorspace))
30844c08aed51c5899665ade97263692328eea4af106cristy              pixel.black   = coeff[x]*i +coeff[x+1]*j
30853ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                              +coeff[x+2], x+=3;
3086ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy            if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
308717f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy                (image->alpha_trait != UndefinedPixelTrait))
30883884f69ac6f82e16fc379fb6c6521960c19045a2cristy              pixel.alpha = coeff[x]*i +coeff[x+1]*j
30893884f69ac6f82e16fc379fb6c6521960c19045a2cristy                              +coeff[x+2], x+=3;
30903ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            break;
30913ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          }
30923ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          case BilinearColorInterpolate:
30933ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          {
3094bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            register ssize_t x=0;
3095ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy            if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
30963ed852eea50f9d4cd633efb8c2b054b8e33c253cristy              pixel.red     = coeff[x]*i     + coeff[x+1]*j +
30973ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                              coeff[x+2]*i*j + coeff[x+3], x+=4;
3098ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy            if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
30993ed852eea50f9d4cd633efb8c2b054b8e33c253cristy              pixel.green   = coeff[x]*i     + coeff[x+1]*j +
31003ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                              coeff[x+2]*i*j + coeff[x+3], x+=4;
3101ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy            if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
31023ed852eea50f9d4cd633efb8c2b054b8e33c253cristy              pixel.blue    = coeff[x]*i     + coeff[x+1]*j +
31033ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                              coeff[x+2]*i*j + coeff[x+3], x+=4;
3104ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy            if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
31053884f69ac6f82e16fc379fb6c6521960c19045a2cristy                (image->colorspace == CMYKColorspace))
31064c08aed51c5899665ade97263692328eea4af106cristy              pixel.black   = coeff[x]*i     + coeff[x+1]*j +
31073ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                              coeff[x+2]*i*j + coeff[x+3], x+=4;
3108ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy            if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
310917f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy                (image->alpha_trait != UndefinedPixelTrait))
31103884f69ac6f82e16fc379fb6c6521960c19045a2cristy              pixel.alpha = coeff[x]*i     + coeff[x+1]*j +
31113884f69ac6f82e16fc379fb6c6521960c19045a2cristy                              coeff[x+2]*i*j + coeff[x+3], x+=4;
31123ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            break;
31133ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          }
311409d867c5236ae386cb2a036298e37aa986478047anthony          case InverseColorInterpolate:
3115216e87a01399287638cc24e70c8cc8947205004fanthony          case ShepardsColorInterpolate:
3116216e87a01399287638cc24e70c8cc8947205004fanthony          { /* Inverse (Squared) Distance weights average (IDW) */
3117bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            size_t
31183ed852eea50f9d4cd633efb8c2b054b8e33c253cristy              k;
31193ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            double
31203ed852eea50f9d4cd633efb8c2b054b8e33c253cristy              denominator;
31213ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
3122ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy            if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
31233884f69ac6f82e16fc379fb6c6521960c19045a2cristy              pixel.red=0.0;
3124ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy            if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
31253884f69ac6f82e16fc379fb6c6521960c19045a2cristy              pixel.green=0.0;
3126ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy            if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
31273884f69ac6f82e16fc379fb6c6521960c19045a2cristy              pixel.blue=0.0;
3128ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy            if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
31293884f69ac6f82e16fc379fb6c6521960c19045a2cristy                (image->colorspace == CMYKColorspace))
31303884f69ac6f82e16fc379fb6c6521960c19045a2cristy              pixel.black=0.0;
3131ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy            if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
313217f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy                (image->alpha_trait != UndefinedPixelTrait))
31333884f69ac6f82e16fc379fb6c6521960c19045a2cristy              pixel.alpha=0.0;
31343ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            denominator = 0.0;
31353ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            for(k=0; k<number_arguments; k+=2+number_colors) {
3136bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy              register ssize_t x=(ssize_t) k+2;
31373ed852eea50f9d4cd633efb8c2b054b8e33c253cristy              double weight =
31383ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                  ((double)i-arguments[ k ])*((double)i-arguments[ k ])
31393ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                + ((double)j-arguments[k+1])*((double)j-arguments[k+1]);
314047bb5df69322136901ccab9ad266d21ceff50500anthony              weight = pow(weight,coeff[0]); /* inverse of power factor */
31413abf6c31cb8c15a2b01903e371a921db702fa838cristy              weight = ( weight < 1.0 ) ? 1.0 : 1.0/weight;
3142ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy              if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
31433ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                pixel.red     += arguments[x++]*weight;
3144ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy              if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
31453ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                pixel.green   += arguments[x++]*weight;
3146ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy              if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
31473ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                pixel.blue    += arguments[x++]*weight;
3148ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy              if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
31493884f69ac6f82e16fc379fb6c6521960c19045a2cristy                  (image->colorspace == CMYKColorspace))
31504c08aed51c5899665ade97263692328eea4af106cristy                pixel.black   += arguments[x++]*weight;
3151ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy              if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
315217f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy                  (image->alpha_trait != UndefinedPixelTrait))
31533884f69ac6f82e16fc379fb6c6521960c19045a2cristy                pixel.alpha += arguments[x++]*weight;
31543ed852eea50f9d4cd633efb8c2b054b8e33c253cristy              denominator += weight;
31553ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            }
3156ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy            if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
31573884f69ac6f82e16fc379fb6c6521960c19045a2cristy              pixel.red/=denominator;
3158ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy            if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
31593884f69ac6f82e16fc379fb6c6521960c19045a2cristy              pixel.green/=denominator;
3160ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy            if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
31613884f69ac6f82e16fc379fb6c6521960c19045a2cristy              pixel.blue/=denominator;
3162ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy            if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
31633884f69ac6f82e16fc379fb6c6521960c19045a2cristy                (image->colorspace == CMYKColorspace))
31643884f69ac6f82e16fc379fb6c6521960c19045a2cristy              pixel.black/=denominator;
3165ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy            if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
316617f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy                (image->alpha_trait != UndefinedPixelTrait))
31673884f69ac6f82e16fc379fb6c6521960c19045a2cristy              pixel.alpha/=denominator;
31683ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            break;
31693ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          }
317004bb72afbdb7cc9ddf0c271f9e815d1a889cdc18Eric McConville          case ManhattanColorInterpolate:
317104bb72afbdb7cc9ddf0c271f9e815d1a889cdc18Eric McConville          {
317204bb72afbdb7cc9ddf0c271f9e815d1a889cdc18Eric McConville            size_t
317304bb72afbdb7cc9ddf0c271f9e815d1a889cdc18Eric McConville              k;
317404bb72afbdb7cc9ddf0c271f9e815d1a889cdc18Eric McConville
317504bb72afbdb7cc9ddf0c271f9e815d1a889cdc18Eric McConville            double
317604bb72afbdb7cc9ddf0c271f9e815d1a889cdc18Eric McConville              minimum = MagickMaximumValue;
317704bb72afbdb7cc9ddf0c271f9e815d1a889cdc18Eric McConville
317804bb72afbdb7cc9ddf0c271f9e815d1a889cdc18Eric McConville            /*
317904bb72afbdb7cc9ddf0c271f9e815d1a889cdc18Eric McConville              Just use the closest control point you can find!
318004bb72afbdb7cc9ddf0c271f9e815d1a889cdc18Eric McConville            */
318104bb72afbdb7cc9ddf0c271f9e815d1a889cdc18Eric McConville            for(k=0; k<number_arguments; k+=2+number_colors) {
318204bb72afbdb7cc9ddf0c271f9e815d1a889cdc18Eric McConville              double distance =
318304bb72afbdb7cc9ddf0c271f9e815d1a889cdc18Eric McConville                  fabs((double)i-arguments[ k ])
318404bb72afbdb7cc9ddf0c271f9e815d1a889cdc18Eric McConville                + fabs((double)j-arguments[k+1]);
318504bb72afbdb7cc9ddf0c271f9e815d1a889cdc18Eric McConville              if ( distance < minimum ) {
318604bb72afbdb7cc9ddf0c271f9e815d1a889cdc18Eric McConville                register ssize_t x=(ssize_t) k+2;
318704bb72afbdb7cc9ddf0c271f9e815d1a889cdc18Eric McConville                if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
318804bb72afbdb7cc9ddf0c271f9e815d1a889cdc18Eric McConville                  pixel.red=arguments[x++];
318904bb72afbdb7cc9ddf0c271f9e815d1a889cdc18Eric McConville                if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
319004bb72afbdb7cc9ddf0c271f9e815d1a889cdc18Eric McConville                  pixel.green=arguments[x++];
319104bb72afbdb7cc9ddf0c271f9e815d1a889cdc18Eric McConville                if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
319204bb72afbdb7cc9ddf0c271f9e815d1a889cdc18Eric McConville                  pixel.blue=arguments[x++];
319304bb72afbdb7cc9ddf0c271f9e815d1a889cdc18Eric McConville                if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
319404bb72afbdb7cc9ddf0c271f9e815d1a889cdc18Eric McConville                    (image->colorspace == CMYKColorspace))
319504bb72afbdb7cc9ddf0c271f9e815d1a889cdc18Eric McConville                  pixel.black=arguments[x++];
319604bb72afbdb7cc9ddf0c271f9e815d1a889cdc18Eric McConville                if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
319704bb72afbdb7cc9ddf0c271f9e815d1a889cdc18Eric McConville                    (image->alpha_trait != UndefinedPixelTrait))
319804bb72afbdb7cc9ddf0c271f9e815d1a889cdc18Eric McConville                  pixel.alpha=arguments[x++];
319904bb72afbdb7cc9ddf0c271f9e815d1a889cdc18Eric McConville                minimum = distance;
320004bb72afbdb7cc9ddf0c271f9e815d1a889cdc18Eric McConville              }
320104bb72afbdb7cc9ddf0c271f9e815d1a889cdc18Eric McConville            }
320204bb72afbdb7cc9ddf0c271f9e815d1a889cdc18Eric McConville            break;
320304bb72afbdb7cc9ddf0c271f9e815d1a889cdc18Eric McConville          }
32043ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          case VoronoiColorInterpolate:
32053ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          default:
32060283b31ed43486c1b3c6d903f6992b6402419c40cristy          {
3207bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            size_t
32083ed852eea50f9d4cd633efb8c2b054b8e33c253cristy              k;
32090283b31ed43486c1b3c6d903f6992b6402419c40cristy
32103ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            double
3211fe181a767c3b3e61b9133829c504e75e34916de8cristy              minimum = MagickMaximumValue;
32123ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
32130283b31ed43486c1b3c6d903f6992b6402419c40cristy            /*
32140283b31ed43486c1b3c6d903f6992b6402419c40cristy              Just use the closest control point you can find!
32150283b31ed43486c1b3c6d903f6992b6402419c40cristy            */
32160283b31ed43486c1b3c6d903f6992b6402419c40cristy            for (k=0; k<number_arguments; k+=2+number_colors) {
32173ed852eea50f9d4cd633efb8c2b054b8e33c253cristy              double distance =
32183ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                  ((double)i-arguments[ k ])*((double)i-arguments[ k ])
32193ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                + ((double)j-arguments[k+1])*((double)j-arguments[k+1]);
32203ed852eea50f9d4cd633efb8c2b054b8e33c253cristy              if ( distance < minimum ) {
3221bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy                register ssize_t x=(ssize_t) k+2;
3222ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
32233884f69ac6f82e16fc379fb6c6521960c19045a2cristy                  pixel.red=arguments[x++];
3224ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
32253884f69ac6f82e16fc379fb6c6521960c19045a2cristy                  pixel.green=arguments[x++];
3226ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
32273884f69ac6f82e16fc379fb6c6521960c19045a2cristy                  pixel.blue=arguments[x++];
3228ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
32293884f69ac6f82e16fc379fb6c6521960c19045a2cristy                    (image->colorspace == CMYKColorspace))
32303884f69ac6f82e16fc379fb6c6521960c19045a2cristy                  pixel.black=arguments[x++];
3231ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
323217f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy                    (image->alpha_trait != UndefinedPixelTrait))
32333884f69ac6f82e16fc379fb6c6521960c19045a2cristy                  pixel.alpha=arguments[x++];
32343ed852eea50f9d4cd633efb8c2b054b8e33c253cristy                minimum = distance;
32353ed852eea50f9d4cd633efb8c2b054b8e33c253cristy              }
32363ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            }
32373ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            break;
32383ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          }
32393ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        }
32403ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        /* set the color directly back into the source image */
3241ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy        if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
3242a29b069eef3fc51ae1f9181367fe82374ced5995cristy          pixel.red=ClampPixel(QuantumRange*pixel.red);
3243ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy        if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3244a29b069eef3fc51ae1f9181367fe82374ced5995cristy          pixel.green=ClampPixel(QuantumRange*pixel.green);
3245ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy        if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3246a29b069eef3fc51ae1f9181367fe82374ced5995cristy          pixel.blue=ClampPixel(QuantumRange*pixel.blue);
3247ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy        if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
32483884f69ac6f82e16fc379fb6c6521960c19045a2cristy            (image->colorspace == CMYKColorspace))
3249a29b069eef3fc51ae1f9181367fe82374ced5995cristy          pixel.black=ClampPixel(QuantumRange*pixel.black);
3250ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy        if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
325117f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy            (image->alpha_trait != UndefinedPixelTrait))
3252a29b069eef3fc51ae1f9181367fe82374ced5995cristy          pixel.alpha=ClampPixel(QuantumRange*pixel.alpha);
325311a06d3f2cac0f17af7963e83bc6e9ebd2a377c0cristy        SetPixelViaPixelInfo(sparse_image,&pixel,q);
3254ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy        q+=GetPixelChannels(sparse_image);
32553ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      }
32563ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      sync=SyncCacheViewAuthenticPixels(sparse_view,exception);
32573ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      if (sync == MagickFalse)
32583ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        status=MagickFalse;
32593ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      if (image->progress_monitor != (MagickProgressMonitor) NULL)
32603ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        {
32613ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          MagickBooleanType
32623ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            proceed;
32633ed852eea50f9d4cd633efb8c2b054b8e33c253cristy
3264b5d5f725fef80ff5d50db3111c05a1a521b81e7fcristy#if defined(MAGICKCORE_OPENMP_SUPPORT)
3265ac245f8a51ea65b085d751c41d8ca4b426bdfe5bcristy          #pragma omp critical (MagickCore_SparseColorImage)
32663ed852eea50f9d4cd633efb8c2b054b8e33c253cristy#endif
32673ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          proceed=SetImageProgress(image,SparseColorTag,progress++,image->rows);
32683ed852eea50f9d4cd633efb8c2b054b8e33c253cristy          if (proceed == MagickFalse)
32693ed852eea50f9d4cd633efb8c2b054b8e33c253cristy            status=MagickFalse;
32703ed852eea50f9d4cd633efb8c2b054b8e33c253cristy        }
32713ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    }
32723ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    sparse_view=DestroyCacheView(sparse_view);
32733ed852eea50f9d4cd633efb8c2b054b8e33c253cristy    if (status == MagickFalse)
32743ed852eea50f9d4cd633efb8c2b054b8e33c253cristy      sparse_image=DestroyImage(sparse_image);
32753ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  }
32763ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  coeff = (double *) RelinquishMagickMemory(coeff);
32773ed852eea50f9d4cd633efb8c2b054b8e33c253cristy  return(sparse_image);
32783ed852eea50f9d4cd633efb8c2b054b8e33c253cristy}
3279