43#include "magick/studio.h"
44#include "magick/artifact.h"
45#include "magick/cache.h"
46#include "magick/cache-view.h"
47#include "magick/channel.h"
48#include "magick/color-private.h"
49#include "magick/colorspace.h"
50#include "magick/colorspace-private.h"
51#include "magick/composite-private.h"
52#include "magick/distort.h"
53#include "magick/exception.h"
54#include "magick/exception-private.h"
55#include "magick/gem.h"
56#include "magick/hashmap.h"
57#include "magick/image.h"
58#include "magick/list.h"
59#include "magick/matrix.h"
60#include "magick/memory_.h"
61#include "magick/monitor-private.h"
62#include "magick/option.h"
63#include "magick/pixel.h"
64#include "magick/pixel-accessor.h"
65#include "magick/pixel-private.h"
66#include "magick/resample.h"
67#include "magick/resample-private.h"
68#include "magick/registry.h"
69#include "magick/resource_.h"
70#include "magick/semaphore.h"
71#include "magick/shear.h"
72#include "magick/string_.h"
73#include "magick/string-private.h"
74#include "magick/thread-private.h"
75#include "magick/token.h"
76#include "magick/transform.h"
81static inline void AffineArgsToCoefficients(
double *affine)
85 tmp[0]=affine[1]; tmp[1]=affine[2]; tmp[2]=affine[3]; tmp[3]=affine[4];
86 affine[3]=tmp[0]; affine[1]=tmp[1]; affine[4]=tmp[2]; affine[2]=tmp[3];
89static inline void CoefficientsToAffineArgs(
double *coeff)
93 tmp[0]=coeff[3]; tmp[1]=coeff[1]; tmp[2]=coeff[4]; tmp[3]=coeff[2];
94 coeff[1]=tmp[0]; coeff[2]=tmp[1]; coeff[3]=tmp[2]; coeff[4]=tmp[3];
96static void InvertAffineCoefficients(
const double *coeff,
double *inverse)
101 determinant=MagickSafeReciprocal(coeff[0]*coeff[4]-coeff[1]*coeff[3]);
102 inverse[0]=determinant*coeff[4];
103 inverse[1]=determinant*(-coeff[1]);
104 inverse[2]=determinant*(coeff[1]*coeff[5]-coeff[2]*coeff[4]);
105 inverse[3]=determinant*(-coeff[3]);
106 inverse[4]=determinant*coeff[0];
107 inverse[5]=determinant*(coeff[2]*coeff[3]-coeff[0]*coeff[5]);
110static void InvertPerspectiveCoefficients(
const double *coeff,
116 determinant=MagickSafeReciprocal(coeff[0]*coeff[4]-coeff[3]*coeff[1]);
117 inverse[0]=determinant*(coeff[4]-coeff[7]*coeff[5]);
118 inverse[1]=determinant*(coeff[7]*coeff[2]-coeff[1]);
119 inverse[2]=determinant*(coeff[1]*coeff[5]-coeff[4]*coeff[2]);
120 inverse[3]=determinant*(coeff[6]*coeff[5]-coeff[3]);
121 inverse[4]=determinant*(coeff[0]-coeff[6]*coeff[2]);
122 inverse[5]=determinant*(coeff[3]*coeff[2]-coeff[0]*coeff[5]);
123 inverse[6]=determinant*(coeff[3]*coeff[7]-coeff[6]*coeff[4]);
124 inverse[7]=determinant*(coeff[6]*coeff[1]-coeff[0]*coeff[7]);
146static size_t poly_number_terms(
double order)
149 if ( order < 1 || order > 5 ||
150 ( order != floor(order) && (order-1.5) > MagickEpsilon) )
152 return(CastDoubleToSizeT(floor((order+1.0)*(order+2.0)/2.0)));
155static double poly_basis_fn(ssize_t n,
double x,
double y)
159 case 0:
return( 1.0 );
162 case 3:
return( x*y );
163 case 4:
return( x*x );
164 case 5:
return( y*y );
165 case 6:
return( x*x*x );
166 case 7:
return( x*x*y );
167 case 8:
return( x*y*y );
168 case 9:
return( y*y*y );
169 case 10:
return( x*x*x*x );
170 case 11:
return( x*x*x*y );
171 case 12:
return( x*x*y*y );
172 case 13:
return( x*y*y*y );
173 case 14:
return( y*y*y*y );
174 case 15:
return( x*x*x*x*x );
175 case 16:
return( x*x*x*x*y );
176 case 17:
return( x*x*x*y*y );
177 case 18:
return( x*x*y*y*y );
178 case 19:
return( x*y*y*y*y );
179 case 20:
return( y*y*y*y*y );
183static const char *poly_basis_str(ssize_t n)
188 case 1:
return(
"*ii");
189 case 2:
return(
"*jj");
190 case 3:
return(
"*ii*jj");
191 case 4:
return(
"*ii*ii");
192 case 5:
return(
"*jj*jj");
193 case 6:
return(
"*ii*ii*ii");
194 case 7:
return(
"*ii*ii*jj");
195 case 8:
return(
"*ii*jj*jj");
196 case 9:
return(
"*jj*jj*jj");
197 case 10:
return(
"*ii*ii*ii*ii");
198 case 11:
return(
"*ii*ii*ii*jj");
199 case 12:
return(
"*ii*ii*jj*jj");
200 case 13:
return(
"*ii*jj*jj*jj");
201 case 14:
return(
"*jj*jj*jj*jj");
202 case 15:
return(
"*ii*ii*ii*ii*ii");
203 case 16:
return(
"*ii*ii*ii*ii*jj");
204 case 17:
return(
"*ii*ii*ii*jj*jj");
205 case 18:
return(
"*ii*ii*jj*jj*jj");
206 case 19:
return(
"*ii*jj*jj*jj*jj");
207 case 20:
return(
"*jj*jj*jj*jj*jj");
211static double poly_basis_dx(ssize_t n,
double x,
double y)
215 case 0:
return( 0.0 );
216 case 1:
return( 1.0 );
217 case 2:
return( 0.0 );
220 case 5:
return( 0.0 );
221 case 6:
return( x*x );
222 case 7:
return( x*y );
223 case 8:
return( y*y );
224 case 9:
return( 0.0 );
225 case 10:
return( x*x*x );
226 case 11:
return( x*x*y );
227 case 12:
return( x*y*y );
228 case 13:
return( y*y*y );
229 case 14:
return( 0.0 );
230 case 15:
return( x*x*x*x );
231 case 16:
return( x*x*x*y );
232 case 17:
return( x*x*y*y );
233 case 18:
return( x*y*y*y );
234 case 19:
return( y*y*y*y );
235 case 20:
return( 0.0 );
239static double poly_basis_dy(ssize_t n,
double x,
double y)
243 case 0:
return( 0.0 );
244 case 1:
return( 0.0 );
245 case 2:
return( 1.0 );
247 case 4:
return( 0.0 );
249 default:
return( poly_basis_dx(n-1,x,y) );
285MagickExport Image *AffineTransformImage(
const Image *image,
286 const AffineMatrix *affine_matrix,ExceptionInfo *exception)
297 assert(image->signature == MagickCoreSignature);
298 assert(affine_matrix != (AffineMatrix *) NULL);
299 assert(exception != (ExceptionInfo *) NULL);
300 assert(exception->signature == MagickCoreSignature);
301 if (IsEventLogging() != MagickFalse)
302 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
303 distort[0]=affine_matrix->sx;
304 distort[1]=affine_matrix->rx;
305 distort[2]=affine_matrix->ry;
306 distort[3]=affine_matrix->sy;
307 distort[4]=affine_matrix->tx;
308 distort[5]=affine_matrix->ty;
309 deskew_image=DistortImage(image,AffineProjectionDistortion,6,distort,
310 MagickTrue,exception);
311 return(deskew_image);
364static inline double MagickRound(
double x)
369 if ((x-floor(x)) < (ceil(x)-x))
374static double *GenerateCoefficients(
const Image *image,
375 DistortImageMethod *method,
const size_t number_arguments,
376 const double *arguments,
size_t number_values,ExceptionInfo *exception)
391 if ( number_values == 0 ) {
407 cp_size = number_values+2;
412 if ( number_arguments < 4*cp_size &&
413 ( *method == BilinearForwardDistortion
414 || *method == BilinearReverseDistortion
415 || *method == PerspectiveDistortion
417 *method = AffineDistortion;
421 case AffineDistortion:
423 number_coeff=3*number_values;
425 case PolynomialDistortion:
427 if (number_arguments < 1) {
428 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
429 "InvalidArgument",
"%s : '%s'",
"Polynomial",
430 "Needs at least 1 argument");
431 return((
double *) NULL);
433 i = poly_number_terms(arguments[0]);
434 number_coeff = 2 + i*number_values;
436 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
437 "InvalidArgument",
"%s : '%s'",
"Polynomial",
438 "Invalid order, should be integer 1 to 5, or 1.5");
439 return((
double *) NULL);
441 if ((number_arguments < (1+i*cp_size)) ||
442 (((number_arguments-1) % cp_size) != 0)) {
443 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
444 "InvalidArgument",
"%s : 'require at least %.20g CPs'",
445 "Polynomial", (
double) i);
446 return((
double *) NULL);
449 case BilinearReverseDistortion:
450 number_coeff=4*number_values;
455 case BilinearForwardDistortion:
462 case QuadraterialDistortion:
466 case ShepardsDistortion:
472 case ScaleRotateTranslateDistortion:
473 case AffineProjectionDistortion:
474 case Plane2CylinderDistortion:
475 case Cylinder2PlaneDistortion:
478 case PolarDistortion:
479 case DePolarDistortion:
482 case PerspectiveDistortion:
483 case PerspectiveProjectionDistortion:
486 case BarrelDistortion:
487 case BarrelInverseDistortion:
491 perror(
"unknown method given");
495 coeff = (
double *) AcquireQuantumMemory(number_coeff,
sizeof(*coeff));
496 if (coeff == (
double *) NULL) {
497 (void) ThrowMagickException(exception,GetMagickModule(),
498 ResourceLimitError,
"MemoryAllocationFailed",
499 "%s",
"GenerateCoefficients");
500 return((
double *) NULL);
504 for (i=0; i < number_coeff; i++)
509 case AffineDistortion:
519 if ( number_arguments%cp_size != 0 ||
520 number_arguments < cp_size ) {
521 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
522 "InvalidArgument",
"%s : 'require at least %.20g CPs'",
524 coeff=(
double *) RelinquishMagickMemory(coeff);
525 return((
double *) NULL);
528 if ( number_arguments == cp_size ) {
530 if ( cp_values == 0 ) {
533 coeff[2] = arguments[0] - arguments[2];
535 coeff[5] = arguments[1] - arguments[3];
539 for (i=0; i<number_values; i++)
540 coeff[i*3+2] = arguments[cp_values+i];
556 matrix = AcquireMagickMatrix(3UL,3UL);
557 vectors = (
double **) AcquireQuantumMemory(number_values,
sizeof(*vectors));
558 if (matrix == (
double **) NULL || vectors == (
double **) NULL)
560 matrix = RelinquishMagickMatrix(matrix, 3UL);
561 vectors = (
double **) RelinquishMagickMemory(vectors);
562 coeff = (
double *) RelinquishMagickMemory(coeff);
563 (void) ThrowMagickException(exception,GetMagickModule(),
564 ResourceLimitError,
"MemoryAllocationFailed",
565 "%s",
"DistortCoefficients");
566 return((
double *) NULL);
569 for (i=0; i < number_values; i++)
570 vectors[i] = &(coeff[i*3]);
572 for (i=0; i < number_arguments; i+=cp_size) {
573 terms[0] = arguments[i+cp_x];
574 terms[1] = arguments[i+cp_y];
576 LeastSquaresAddTerms(matrix,vectors,terms,
577 &(arguments[i+cp_values]),3UL,number_values);
579 if ( number_arguments == 2*cp_size ) {
584 terms[0] = arguments[cp_x]
585 - ( arguments[cp_size+cp_y] - arguments[cp_y] );
586 terms[1] = arguments[cp_y] +
587 + ( arguments[cp_size+cp_x] - arguments[cp_x] );
589 if ( cp_values == 0 ) {
593 uv2[0] = arguments[0] - arguments[5] + arguments[1];
594 uv2[1] = arguments[1] + arguments[4] - arguments[0];
595 LeastSquaresAddTerms(matrix,vectors,terms,uv2,3UL,2UL);
599 LeastSquaresAddTerms(matrix,vectors,terms,
600 &(arguments[cp_values]),3UL,number_values);
604 status=GaussJordanElimination(matrix,vectors,3UL,number_values);
605 matrix = RelinquishMagickMatrix(matrix, 3UL);
606 vectors = (
double **) RelinquishMagickMemory(vectors);
607 if ( status == MagickFalse ) {
608 coeff = (
double *) RelinquishMagickMemory(coeff);
609 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
610 "InvalidArgument",
"%s : 'Unsolvable Matrix'",
611 CommandOptionToMnemonic(MagickDistortOptions, *method) );
612 return((
double *) NULL);
617 case AffineProjectionDistortion:
633 if (number_arguments != 6) {
634 coeff = (
double *) RelinquishMagickMemory(coeff);
635 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
636 "InvalidArgument",
"%s : 'Needs 6 coeff values'",
637 CommandOptionToMnemonic(MagickDistortOptions, *method) );
638 return((
double *) NULL);
641 for(i=0; i<6UL; i++ )
642 inverse[i] = arguments[i];
643 AffineArgsToCoefficients(inverse);
644 InvertAffineCoefficients(inverse, coeff);
645 *method = AffineDistortion;
649 case ScaleRotateTranslateDistortion:
678 x = nx = (double)(image->columns)/2.0 + (double)image->page.x;
679 y = ny = (double)(image->rows)/2.0 + (double)image->page.y;
681 switch ( number_arguments ) {
683 coeff = (
double *) RelinquishMagickMemory(coeff);
684 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
685 "InvalidArgument",
"%s : 'Needs at least 1 argument'",
686 CommandOptionToMnemonic(MagickDistortOptions, *method) );
687 return((
double *) NULL);
692 sx = sy = arguments[0];
696 x = nx = arguments[0];
697 y = ny = arguments[1];
698 switch ( number_arguments ) {
703 sx = sy = arguments[2];
712 sx = sy = arguments[2];
725 coeff = (
double *) RelinquishMagickMemory(coeff);
726 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
727 "InvalidArgument",
"%s : 'Too Many Arguments (7 or less)'",
728 CommandOptionToMnemonic(MagickDistortOptions, *method) );
729 return((
double *) NULL);
734 if ( fabs(sx) < MagickEpsilon || fabs(sy) < MagickEpsilon ) {
735 coeff = (
double *) RelinquishMagickMemory(coeff);
736 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
737 "InvalidArgument",
"%s : 'Zero Scale Given'",
738 CommandOptionToMnemonic(MagickDistortOptions, *method) );
739 return((
double *) NULL);
742 a=DegreesToRadians(a); cosine=cos(a); sine=sin(a);
744 *method = AffineDistortion;
747 coeff[2]=x-nx*coeff[0]-ny*coeff[1];
750 coeff[5]=y-nx*coeff[3]-ny*coeff[4];
753 case PerspectiveDistortion:
796 if ( number_arguments%cp_size != 0 ||
797 number_arguments < cp_size*4 ) {
798 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
799 "InvalidArgument",
"%s : 'require at least %.20g CPs'",
800 CommandOptionToMnemonic(MagickDistortOptions, *method), 4.0);
801 coeff=(
double *) RelinquishMagickMemory(coeff);
802 return((
double *) NULL);
805 vectors[0] = &(coeff[0]);
807 matrix = AcquireMagickMatrix(8UL,8UL);
808 if (matrix == (
double **) NULL) {
809 coeff=(
double *) RelinquishMagickMemory(coeff);
810 (void) ThrowMagickException(exception,GetMagickModule(),
811 ResourceLimitError,
"MemoryAllocationFailed",
812 "%s",
"DistortCoefficients");
813 return((
double *) NULL);
816 for (i=0; i < number_arguments; i+=4) {
817 terms[0]=arguments[i+cp_x];
818 terms[1]=arguments[i+cp_y];
823 terms[6]=-terms[0]*arguments[i+cp_u];
824 terms[7]=-terms[1]*arguments[i+cp_u];
825 LeastSquaresAddTerms(matrix,vectors,terms,&(arguments[i+cp_u]),
831 terms[3]=arguments[i+cp_x];
832 terms[4]=arguments[i+cp_y];
834 terms[6]=-terms[3]*arguments[i+cp_v];
835 terms[7]=-terms[4]*arguments[i+cp_v];
836 LeastSquaresAddTerms(matrix,vectors,terms,&(arguments[i+cp_v]),
840 status=GaussJordanElimination(matrix,vectors,8UL,1UL);
841 matrix = RelinquishMagickMatrix(matrix, 8UL);
842 if ( status == MagickFalse ) {
843 coeff = (
double *) RelinquishMagickMemory(coeff);
844 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
845 "InvalidArgument",
"%s : 'Unsolvable Matrix'",
846 CommandOptionToMnemonic(MagickDistortOptions, *method) );
847 return((
double *) NULL);
855 coeff[8] = coeff[6]*arguments[cp_x]
856 + coeff[7]*arguments[cp_y] + 1.0;
857 coeff[8] = (coeff[8] < 0.0) ? -1.0 : +1.0;
861 case PerspectiveProjectionDistortion:
866 if (number_arguments != 8) {
867 coeff = (
double *) RelinquishMagickMemory(coeff);
868 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
869 "InvalidArgument",
"%s : 'Needs 8 coefficient values'",
870 CommandOptionToMnemonic(MagickDistortOptions, *method));
871 return((
double *) NULL);
874 InvertPerspectiveCoefficients(arguments, coeff);
882 coeff[8] = coeff[6]*arguments[2]
883 + coeff[7]*arguments[5] + 1.0;
884 coeff[8] = (coeff[8] < 0.0) ? -1.0 : +1.0;
885 *method = PerspectiveDistortion;
889 case BilinearForwardDistortion:
890 case BilinearReverseDistortion:
914 if ( number_arguments%cp_size != 0 ||
915 number_arguments < cp_size*4 ) {
916 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
917 "InvalidArgument",
"%s : 'require at least %.20g CPs'",
918 CommandOptionToMnemonic(MagickDistortOptions, *method), 4.0);
919 coeff=(
double *) RelinquishMagickMemory(coeff);
920 return((
double *) NULL);
923 matrix = AcquireMagickMatrix(4UL,4UL);
924 vectors = (
double **) AcquireQuantumMemory(number_values,
sizeof(*vectors));
925 if (matrix == (
double **) NULL || vectors == (
double **) NULL)
927 matrix = RelinquishMagickMatrix(matrix, 4UL);
928 vectors = (
double **) RelinquishMagickMemory(vectors);
929 coeff = (
double *) RelinquishMagickMemory(coeff);
930 (void) ThrowMagickException(exception,GetMagickModule(),
931 ResourceLimitError,
"MemoryAllocationFailed",
932 "%s",
"DistortCoefficients");
933 return((
double *) NULL);
936 for (i=0; i < number_values; i++)
937 vectors[i] = &(coeff[i*4]);
939 for (i=0; i < number_arguments; i+=cp_size) {
940 terms[0] = arguments[i+cp_x];
941 terms[1] = arguments[i+cp_y];
942 terms[2] = terms[0]*terms[1];
944 LeastSquaresAddTerms(matrix,vectors,terms,
945 &(arguments[i+cp_values]),4UL,number_values);
948 status=GaussJordanElimination(matrix,vectors,4UL,number_values);
949 matrix = RelinquishMagickMatrix(matrix, 4UL);
950 vectors = (
double **) RelinquishMagickMemory(vectors);
951 if ( status == MagickFalse ) {
952 coeff = (
double *) RelinquishMagickMemory(coeff);
953 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
954 "InvalidArgument",
"%s : 'Unsolvable Matrix'",
955 CommandOptionToMnemonic(MagickDistortOptions, *method) );
956 return((
double *) NULL);
958 if ( *method == BilinearForwardDistortion ) {
998 coeff[8] = coeff[0]*coeff[5] - coeff[1]*coeff[4];
999 coeff[9] = 2*(coeff[2]*coeff[5] - coeff[1]*coeff[6]);
1004 case QuadrilateralDistortion:
1020 case PolynomialDistortion:
1058 coeff[0] = arguments[0];
1059 coeff[1] = (double) poly_number_terms(arguments[0]);
1060 nterms = CastDoubleToSizeT(coeff[1]);
1063 matrix = AcquireMagickMatrix(nterms,nterms);
1064 vectors = (
double **) AcquireQuantumMemory(number_values,
sizeof(*vectors));
1065 terms = (
double *) AcquireQuantumMemory(nterms,
sizeof(*terms));
1066 if (matrix == (
double **) NULL ||
1067 vectors == (
double **) NULL ||
1068 terms == (
double *) NULL )
1070 matrix = RelinquishMagickMatrix(matrix, nterms);
1071 vectors = (
double **) RelinquishMagickMemory(vectors);
1072 terms = (
double *) RelinquishMagickMemory(terms);
1073 coeff = (
double *) RelinquishMagickMemory(coeff);
1074 (void) ThrowMagickException(exception,GetMagickModule(),
1075 ResourceLimitError,
"MemoryAllocationFailed",
1076 "%s",
"DistortCoefficients");
1077 return((
double *) NULL);
1080 for (i=0; i < number_values; i++)
1081 vectors[i] = &(coeff[2+i*nterms]);
1083 for (i=1; i < number_arguments; i+=cp_size) {
1084 for (j=0; j < (ssize_t) nterms; j++)
1085 terms[j] = poly_basis_fn(j,arguments[i+cp_x],arguments[i+cp_y]);
1086 LeastSquaresAddTerms(matrix,vectors,terms,
1087 &(arguments[i+cp_values]),nterms,number_values);
1089 terms = (
double *) RelinquishMagickMemory(terms);
1091 status=GaussJordanElimination(matrix,vectors,nterms,number_values);
1092 matrix = RelinquishMagickMatrix(matrix, nterms);
1093 vectors = (
double **) RelinquishMagickMemory(vectors);
1094 if ( status == MagickFalse ) {
1095 coeff = (
double *) RelinquishMagickMemory(coeff);
1096 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1097 "InvalidArgument",
"%s : 'Unsolvable Matrix'",
1098 CommandOptionToMnemonic(MagickDistortOptions, *method) );
1099 return((
double *) NULL);
1139 if ( number_arguments >= 1 && arguments[0] < MagickEpsilon ) {
1140 coeff = (
double *) RelinquishMagickMemory(coeff);
1141 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1142 "InvalidArgument",
"%s : 'Arc Angle Too Small'",
1143 CommandOptionToMnemonic(MagickDistortOptions, *method) );
1144 return((
double *) NULL);
1146 if ( number_arguments >= 3 && arguments[2] < MagickEpsilon ) {
1147 coeff = (
double *) RelinquishMagickMemory(coeff);
1148 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1149 "InvalidArgument",
"%s : 'Outer Radius Too Small'",
1150 CommandOptionToMnemonic(MagickDistortOptions, *method) );
1151 return((
double *) NULL);
1153 coeff[0] = -MagickPI2;
1154 if ( number_arguments >= 1 )
1155 coeff[1] = DegreesToRadians(arguments[0]);
1157 coeff[1] = MagickPI2;
1158 if ( number_arguments >= 2 )
1159 coeff[0] += DegreesToRadians(arguments[1]);
1160 coeff[0] /= Magick2PI;
1161 coeff[0] -= MagickRound(coeff[0]);
1162 coeff[0] *= Magick2PI;
1163 coeff[3] = (double)image->rows-1;
1164 coeff[2] = (double)image->columns/coeff[1] + coeff[3]/2.0;
1165 if ( number_arguments >= 3 ) {
1166 if ( number_arguments >= 4 )
1167 coeff[3] = arguments[2] - arguments[3];
1169 coeff[3] *= arguments[2]/coeff[2];
1170 coeff[2] = arguments[2];
1172 coeff[4] = ((double)image->columns-1.0)/2.0;
1176 case PolarDistortion:
1177 case DePolarDistortion:
1189 if ( number_arguments == 3
1190 || ( number_arguments > 6 && *method == PolarDistortion )
1191 || number_arguments > 8 ) {
1192 (void) ThrowMagickException(exception,GetMagickModule(),
1193 OptionError,
"InvalidArgument",
"%s : number of arguments",
1194 CommandOptionToMnemonic(MagickDistortOptions, *method) );
1195 coeff=(
double *) RelinquishMagickMemory(coeff);
1196 return((
double *) NULL);
1199 if ( number_arguments >= 1 )
1200 coeff[0] = arguments[0];
1204 coeff[1] = number_arguments >= 2 ? arguments[1] : 0.0;
1206 if ( number_arguments >= 4 ) {
1207 coeff[2] = arguments[2];
1208 coeff[3] = arguments[3];
1211 coeff[2] = (double)(image->columns)/2.0+image->page.x;
1212 coeff[3] = (double)(image->rows)/2.0+image->page.y;
1215 coeff[4] = -MagickPI;
1216 if ( number_arguments >= 5 )
1217 coeff[4] = DegreesToRadians(arguments[4]);
1218 coeff[5] = coeff[4];
1219 if ( number_arguments >= 6 )
1220 coeff[5] = DegreesToRadians(arguments[5]);
1221 if ( fabs(coeff[4]-coeff[5]) < MagickEpsilon )
1222 coeff[5] += Magick2PI;
1224 if ( coeff[0] < MagickEpsilon ) {
1226 if ( fabs(coeff[0]) < MagickEpsilon ) {
1227 coeff[0]=MagickMin(fabs(coeff[2]-image->page.x),
1228 fabs(coeff[3]-image->page.y));
1229 coeff[0]=MagickMin(coeff[0],
1230 fabs(coeff[2]-image->page.x-image->columns));
1231 coeff[0]=MagickMin(coeff[0],
1232 fabs(coeff[3]-image->page.y-image->rows));
1235 if ( fabs(-1.0-coeff[0]) < MagickEpsilon ) {
1237 rx = coeff[2]-image->page.x;
1238 ry = coeff[3]-image->page.y;
1239 coeff[0] = rx*rx+ry*ry;
1240 ry = coeff[3]-image->page.y-image->rows;
1241 coeff[0] = MagickMax(coeff[0],rx*rx+ry*ry);
1242 rx = coeff[2]-image->page.x-image->columns;
1243 coeff[0] = MagickMax(coeff[0],rx*rx+ry*ry);
1244 ry = coeff[3]-image->page.y;
1245 coeff[0] = MagickMax(coeff[0],rx*rx+ry*ry);
1246 coeff[0] = sqrt(coeff[0]);
1250 if ( coeff[0] < MagickEpsilon || coeff[1] < -MagickEpsilon
1251 || (coeff[0]-coeff[1]) < MagickEpsilon ) {
1252 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1253 "InvalidArgument",
"%s : Invalid Radius",
1254 CommandOptionToMnemonic(MagickDistortOptions, *method) );
1255 coeff=(
double *) RelinquishMagickMemory(coeff);
1256 return((
double *) NULL);
1259 if ( *method == PolarDistortion ) {
1260 coeff[6]=(double) image->columns/(coeff[5]-coeff[4]);
1261 coeff[7]=(double) image->rows/(coeff[0]-coeff[1]);
1264 coeff[6]=(coeff[5]-coeff[4])/image->columns;
1265 coeff[7]=(coeff[0]-coeff[1])/image->rows;
1269 case Cylinder2PlaneDistortion:
1270 case Plane2CylinderDistortion:
1294 if (number_arguments < 1) {
1295 coeff = (
double *) RelinquishMagickMemory(coeff);
1296 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1297 "InvalidArgument",
"%s : 'Needs at least 1 argument'",
1298 CommandOptionToMnemonic(MagickDistortOptions, *method) );
1299 return((
double *) NULL);
1301 if ( arguments[0] < MagickEpsilon || arguments[0] > 160.0 ) {
1302 coeff=(
double *) RelinquishMagickMemory(coeff);
1303 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1304 "InvalidArgument",
"%s : Invalid FOV Angle",
1305 CommandOptionToMnemonic(MagickDistortOptions, *method) );
1306 return((
double *) NULL);
1308 coeff[0] = DegreesToRadians(arguments[0]);
1309 if ( *method == Cylinder2PlaneDistortion )
1313 coeff[1] = (double) image->columns/coeff[0];
1316 coeff[1] = (double) image->columns / ( 2 * tan(coeff[0]/2) );
1318 coeff[2] = (double)(image->columns)/2.0+image->page.x;
1319 coeff[3] = (double)(image->rows)/2.0+image->page.y;
1320 coeff[4] = coeff[2];
1321 coeff[5] = coeff[3];
1324 case BarrelDistortion:
1325 case BarrelInverseDistortion:
1346 rscale = 2.0/MagickMin((
double) image->columns,(
double) image->rows);
1349 if ( (number_arguments < 3) || (number_arguments == 7) ||
1350 (number_arguments == 9) || (number_arguments > 10) )
1352 coeff=(
double *) RelinquishMagickMemory(coeff);
1353 (void) ThrowMagickException(exception,GetMagickModule(),
1354 OptionError,
"InvalidArgument",
"%s : number of arguments",
1355 CommandOptionToMnemonic(MagickDistortOptions, *method) );
1356 return((
double *) NULL);
1359 coeff[0] = arguments[0];
1360 coeff[1] = arguments[1];
1361 coeff[2] = arguments[2];
1362 if ((number_arguments == 3) || (number_arguments == 5) )
1363 coeff[3] = 1.0 - coeff[0] - coeff[1] - coeff[2];
1365 coeff[3] = arguments[3];
1367 coeff[0] *= pow(rscale,3.0);
1368 coeff[1] *= rscale*rscale;
1371 if ( number_arguments >= 8 ) {
1372 coeff[4] = arguments[4] * pow(rscale,3.0);
1373 coeff[5] = arguments[5] * rscale*rscale;
1374 coeff[6] = arguments[6] * rscale;
1375 coeff[7] = arguments[7];
1378 coeff[4] = coeff[0];
1379 coeff[5] = coeff[1];
1380 coeff[6] = coeff[2];
1381 coeff[7] = coeff[3];
1384 if ( number_arguments == 5 ) {
1385 coeff[8] = arguments[3];
1386 coeff[9] = arguments[4];
1388 else if ( number_arguments == 6 ) {
1389 coeff[8] = arguments[4];
1390 coeff[9] = arguments[5];
1392 else if ( number_arguments == 10 ) {
1393 coeff[8] = arguments[8];
1394 coeff[9] = arguments[9];
1398 coeff[8] = (double)image->columns/2.0 + image->page.x;
1399 coeff[9] = (double)image->rows/2.0 + image->page.y;
1403 case ShepardsDistortion:
1410 if ( number_arguments%cp_size != 0 ||
1411 number_arguments < cp_size ) {
1412 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1413 "InvalidArgument",
"%s : 'requires CP's (4 numbers each)'",
1414 CommandOptionToMnemonic(MagickDistortOptions, *method));
1415 coeff=(
double *) RelinquishMagickMemory(coeff);
1416 return((
double *) NULL);
1419 {
const char *artifact=GetImageArtifact(image,
"shepards:power");
1420 if ( artifact != (
const char *) NULL ) {
1421 coeff[0]=StringToDouble(artifact,(
char **) NULL) / 2.0;
1422 if ( coeff[0] < MagickEpsilon ) {
1423 (void) ThrowMagickException(exception,GetMagickModule(),
1424 OptionError,
"InvalidArgument",
"%s",
"-define shepards:power" );
1425 coeff=(
double *) RelinquishMagickMemory(coeff);
1426 return((
double *) NULL);
1438 perror(
"no method handler");
1439 return((
double *) NULL);
1477MagickExport Image *DistortResizeImage(
const Image *image,
1478 const size_t columns,
const size_t rows,ExceptionInfo *exception)
1480#define DistortResizeImageTag "Distort/Image"
1498 assert(image != (
const Image *) NULL);
1499 assert(image->signature == MagickCoreSignature);
1500 assert(exception != (ExceptionInfo *) NULL);
1501 assert(exception->signature == MagickCoreSignature);
1502 if (IsEventLogging() != MagickFalse)
1503 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
1504 if ((columns == 0) || (rows == 0))
1505 return((Image *) NULL);
1508 (void) memset(distort_args,0,12*
sizeof(
double));
1509 distort_args[4]=(double) image->columns;
1510 distort_args[6]=(double) columns;
1511 distort_args[9]=(double) image->rows;
1512 distort_args[11]=(double) rows;
1514 vp_save=GetImageVirtualPixelMethod(image);
1516 tmp_image=CloneImage(image,0,0,MagickTrue,exception);
1517 if ( tmp_image == (Image *) NULL )
1518 return((Image *) NULL);
1519 (void) SetImageVirtualPixelMethod(tmp_image,TransparentVirtualPixelMethod);
1521 if (image->matte == MagickFalse)
1526 (void) SetImageAlphaChannel(tmp_image,SetAlphaChannel);
1527 resize_image=DistortImage(tmp_image,AffineDistortion,12,distort_args,
1528 MagickTrue,exception),
1530 tmp_image=DestroyImage(tmp_image);
1531 if ( resize_image == (Image *) NULL )
1532 return((Image *) NULL);
1534 (void) SetImageAlphaChannel(resize_image,DeactivateAlphaChannel);
1535 InheritException(exception,&image->exception);
1548 (void) SeparateImageChannel(tmp_image,TrueAlphaChannel);
1549 (void) SetImageAlphaChannel(tmp_image,OpaqueAlphaChannel);
1550 resize_alpha=DistortImage(tmp_image,AffineDistortion,12,distort_args,
1551 MagickTrue,exception),
1552 tmp_image=DestroyImage(tmp_image);
1553 if ( resize_alpha == (Image *) NULL )
1554 return((Image *) NULL);
1557 tmp_image=CloneImage(image,0,0,MagickTrue,exception);
1558 if ( tmp_image == (Image *) NULL )
1559 return((Image *) NULL);
1560 (void) SetImageVirtualPixelMethod(tmp_image,
1561 TransparentVirtualPixelMethod);
1562 (void) SetImageVirtualPixelMethod(tmp_image,
1563 TransparentVirtualPixelMethod);
1564 resize_image=DistortImage(tmp_image,AffineDistortion,12,distort_args,
1565 MagickTrue,exception),
1566 tmp_image=DestroyImage(tmp_image);
1567 if ( resize_image == (Image *) NULL)
1569 resize_alpha=DestroyImage(resize_alpha);
1570 return((Image *) NULL);
1574 (void) SetImageAlphaChannel(resize_image,DeactivateAlphaChannel);
1575 (void) SetImageAlphaChannel(resize_alpha,DeactivateAlphaChannel);
1576 (void) CompositeImage(resize_image,CopyOpacityCompositeOp,resize_alpha,
1578 InheritException(exception,&resize_image->exception);
1579 resize_image->matte=image->matte;
1580 resize_image->compose=image->compose;
1581 resize_alpha=DestroyImage(resize_alpha);
1583 (void) SetImageVirtualPixelMethod(resize_image,vp_save);
1588 crop_area.width=columns;
1589 crop_area.height=rows;
1593 tmp_image=resize_image;
1594 resize_image=CropImage(tmp_image,&crop_area,exception);
1595 tmp_image=DestroyImage(tmp_image);
1596 if (resize_image != (Image *) NULL)
1598 resize_image->page.width=0;
1599 resize_image->page.height=0;
1601 return(resize_image);
1693MagickExport Image *DistortImage(
const Image *image,DistortImageMethod method,
1694 const size_t number_arguments,
const double *arguments,
1695 MagickBooleanType bestfit,ExceptionInfo *exception)
1697#define DistortImageTag "Distort/Image"
1712 assert(image != (Image *) NULL);
1713 assert(image->signature == MagickCoreSignature);
1714 assert(exception != (ExceptionInfo *) NULL);
1715 assert(exception->signature == MagickCoreSignature);
1716 if (IsEventLogging() != MagickFalse)
1717 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
1721 if (method == ResizeDistortion)
1723 if (number_arguments != 2)
1725 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1726 "InvalidArgument",
"%s : '%s'",
"Resize",
1727 "Invalid number of args: 2 only");
1728 return((Image *) NULL);
1730 distort_image=DistortResizeImage(image,CastDoubleToSizeT(arguments[0]),
1731 CastDoubleToSizeT(arguments[1]),exception);
1732 return(distort_image);
1742 coeff=GenerateCoefficients(image,&method,number_arguments,arguments,0,
1744 if (coeff == (
double *) NULL)
1745 return((Image *) NULL);
1753 geometry.width=image->columns;
1754 geometry.height=image->rows;
1758 if ( method == ArcDistortion ) {
1759 bestfit = MagickTrue;
1768 fix_bounds = MagickTrue;
1770 s.x=s.y=min.x=max.x=min.y=max.y=0.0;
1773#define InitalBounds(p) \
1776 min.x = max.x = p.x; \
1777 min.y = max.y = p.y; \
1779#define ExpandBounds(p) \
1782 min.x = MagickMin(min.x,p.x); \
1783 max.x = MagickMax(max.x,p.x); \
1784 min.y = MagickMin(min.y,p.y); \
1785 max.y = MagickMax(max.y,p.y); \
1790 case AffineDistortion:
1791 {
double inverse[6];
1792 InvertAffineCoefficients(coeff, inverse);
1793 s.x = (double) image->page.x;
1794 s.y = (double) image->page.y;
1795 d.x = inverse[0]*s.x+inverse[1]*s.y+inverse[2];
1796 d.y = inverse[3]*s.x+inverse[4]*s.y+inverse[5];
1798 s.x = (double) image->page.x+image->columns;
1799 s.y = (double) image->page.y;
1800 d.x = inverse[0]*s.x+inverse[1]*s.y+inverse[2];
1801 d.y = inverse[3]*s.x+inverse[4]*s.y+inverse[5];
1803 s.x = (double) image->page.x;
1804 s.y = (double) image->page.y+image->rows;
1805 d.x = inverse[0]*s.x+inverse[1]*s.y+inverse[2];
1806 d.y = inverse[3]*s.x+inverse[4]*s.y+inverse[5];
1808 s.x = (double) image->page.x+image->columns;
1809 s.y = (double) image->page.y+image->rows;
1810 d.x = inverse[0]*s.x+inverse[1]*s.y+inverse[2];
1811 d.y = inverse[3]*s.x+inverse[4]*s.y+inverse[5];
1815 case PerspectiveDistortion:
1816 {
double inverse[8], scale;
1817 InvertPerspectiveCoefficients(coeff, inverse);
1818 s.x = (double) image->page.x;
1819 s.y = (double) image->page.y;
1820 scale=inverse[6]*s.x+inverse[7]*s.y+1.0;
1821 scale=MagickSafeReciprocal(scale);
1822 d.x = scale*(inverse[0]*s.x+inverse[1]*s.y+inverse[2]);
1823 d.y = scale*(inverse[3]*s.x+inverse[4]*s.y+inverse[5]);
1825 s.x = (double) image->page.x+image->columns;
1826 s.y = (double) image->page.y;
1827 scale=inverse[6]*s.x+inverse[7]*s.y+1.0;
1828 scale=MagickSafeReciprocal(scale);
1829 d.x = scale*(inverse[0]*s.x+inverse[1]*s.y+inverse[2]);
1830 d.y = scale*(inverse[3]*s.x+inverse[4]*s.y+inverse[5]);
1832 s.x = (double) image->page.x;
1833 s.y = (double) image->page.y+image->rows;
1834 scale=inverse[6]*s.x+inverse[7]*s.y+1.0;
1835 scale=MagickSafeReciprocal(scale);
1836 d.x = scale*(inverse[0]*s.x+inverse[1]*s.y+inverse[2]);
1837 d.y = scale*(inverse[3]*s.x+inverse[4]*s.y+inverse[5]);
1839 s.x = (double) image->page.x+image->columns;
1840 s.y = (double) image->page.y+image->rows;
1841 scale=inverse[6]*s.x+inverse[7]*s.y+1.0;
1842 scale=MagickSafeReciprocal(scale);
1843 d.x = scale*(inverse[0]*s.x+inverse[1]*s.y+inverse[2]);
1844 d.y = scale*(inverse[3]*s.x+inverse[4]*s.y+inverse[5]);
1851 a = coeff[0]-coeff[1]/2; ca = cos(a); sa = sin(a);
1855 d.x = (coeff[2]-coeff[3])*ca;
1856 d.y = (coeff[2]-coeff[3])*sa;
1858 a = coeff[0]+coeff[1]/2; ca = cos(a); sa = sin(a);
1862 d.x = (coeff[2]-coeff[3])*ca;
1863 d.y = (coeff[2]-coeff[3])*sa;
1866 for( a=(
double) (ceil((
double) ((coeff[0]-coeff[1]/2.0)/MagickPI2))*MagickPI2);
1867 a<(coeff[0]+coeff[1]/2.0); a+=MagickPI2 ) {
1868 ca = cos(a); sa = sin(a);
1878 coeff[1] = (double) (Magick2PI*image->columns/coeff[1]);
1879 coeff[3] = (double)image->rows/coeff[3];
1882 case PolarDistortion:
1884 if (number_arguments < 2)
1885 coeff[2] = coeff[3] = 0.0;
1886 min.x = coeff[2]-coeff[0];
1887 max.x = coeff[2]+coeff[0];
1888 min.y = coeff[3]-coeff[0];
1889 max.y = coeff[3]+coeff[0];
1891 coeff[7]=(double) geometry.height/(coeff[0]-coeff[1]);
1894 case DePolarDistortion:
1898 fix_bounds = MagickFalse;
1899 geometry.x = geometry.y = 0;
1900 geometry.height = CastDoubleToSizeT(ceil(coeff[0]-coeff[1]));
1901 geometry.width = CastDoubleToSizeT(ceil((coeff[0]-coeff[1])*
1902 (coeff[5]-coeff[4])*0.5));
1904 coeff[6]=(coeff[5]-coeff[4])*MagickSafeReciprocal(geometry.width);
1905 coeff[7]=(coeff[0]-coeff[1])*MagickSafeReciprocal(geometry.height);
1908 case Cylinder2PlaneDistortion:
1913 geometry.x = geometry.y = 0;
1914 geometry.width = CastDoubleToSizeT(ceil( 2.0*coeff[1]*tan(coeff[0]/2.0) ));
1915 geometry.height = CastDoubleToSizeT(ceil( 2.0*coeff[3]/cos(coeff[0]/2.0) ));
1917 coeff[4] = (double) geometry.width/2.0;
1918 coeff[5] = (double) geometry.height/2.0;
1919 fix_bounds = MagickFalse;
1922 case Plane2CylinderDistortion:
1926 geometry.x = geometry.y = 0;
1927 geometry.width = CastDoubleToSizeT(ceil(coeff[0]*coeff[1]));
1928 geometry.height = CastDoubleToSizeT(2.0*coeff[3]);
1930 coeff[4] = (double) geometry.width/2.0;
1931 coeff[5] = (double) geometry.height/2.0;
1932 fix_bounds = MagickFalse;
1936 case ShepardsDistortion:
1937 case BilinearForwardDistortion:
1938 case BilinearReverseDistortion:
1940 case QuadrilateralDistortion:
1942 case PolynomialDistortion:
1943 case BarrelDistortion:
1944 case BarrelInverseDistortion:
1947 bestfit = MagickFalse;
1948 fix_bounds = MagickFalse;
1957 geometry.x = (ssize_t) floor(min.x-0.5);
1958 geometry.y = (ssize_t) floor(min.y-0.5);
1959 geometry.width=CastDoubleToSizeT(ceil(max.x-geometry.x+0.5));
1960 geometry.height=CastDoubleToSizeT(ceil(max.y-geometry.y+0.5));
1969 {
const char *artifact=GetImageArtifact(image,
"distort:viewport");
1970 viewport_given = MagickFalse;
1971 if ( artifact != (
const char *) NULL ) {
1972 MagickStatusType flags=ParseAbsoluteGeometry(artifact,&geometry);
1974 (void) ThrowMagickException(exception,GetMagickModule(),
1975 OptionWarning,
"InvalidGeometry",
"`%s' `%s'",
1976 "distort:viewport",artifact);
1978 viewport_given = MagickTrue;
1983 if ( GetImageArtifact(image,
"verbose") != (
const char *) NULL ) {
1986 char image_gen[MaxTextExtent];
1990 if ( bestfit || viewport_given ) {
1991 (void) FormatLocaleString(image_gen, MaxTextExtent,
" -size %.20gx%.20g "
1992 "-page %+.20g%+.20g xc: +insert \\\n",(
double) geometry.width,
1993 (
double) geometry.height,(
double) geometry.x,(
double) geometry.y);
1994 lookup=
"v.p{ xx-v.page.x-.5, yy-v.page.y-.5 }";
1997 image_gen[0] =
'\0';
1998 lookup =
"p{ xx-page.x-.5, yy-page.y-.5 }";
2003 case AffineDistortion:
2008 inverse=(
double *) AcquireQuantumMemory(6,
sizeof(*inverse));
2009 if (inverse == (
double *) NULL)
2011 coeff=(
double *) RelinquishMagickMemory(coeff);
2012 (void) ThrowMagickException(exception,GetMagickModule(),
2013 ResourceLimitError,
"MemoryAllocationFailed",
"%s",
"DistortImages");
2014 return((Image *) NULL);
2016 InvertAffineCoefficients(coeff, inverse);
2017 CoefficientsToAffineArgs(inverse);
2018 (void) FormatLocaleFile(stderr,
"Affine Projection:\n");
2019 (void) FormatLocaleFile(stderr,
2020 " -distort AffineProjection \\\n '");
2021 for (i=0; i < 5; i++)
2022 (
void) FormatLocaleFile(stderr,
"%lf,", inverse[i]);
2023 (void) FormatLocaleFile(stderr,
"%lf'\n", inverse[5]);
2024 inverse=(
double *) RelinquishMagickMemory(inverse);
2025 (void) FormatLocaleFile(stderr,
"Affine Distort, FX Equivelent:\n");
2026 (void) FormatLocaleFile(stderr,
"%s", image_gen);
2027 (void) FormatLocaleFile(stderr,
2028 " -fx 'ii=i+page.x+0.5; jj=j+page.y+0.5;\n");
2029 (void) FormatLocaleFile(stderr,
" xx=%+lf*ii %+lf*jj %+lf;\n",
2030 coeff[0],coeff[1],coeff[2]);
2031 (void) FormatLocaleFile(stderr,
" yy=%+lf*ii %+lf*jj %+lf;\n",
2032 coeff[3],coeff[4],coeff[5]);
2033 (void) FormatLocaleFile(stderr,
" %s' \\\n",lookup);
2036 case PerspectiveDistortion:
2041 inverse=(
double *) AcquireQuantumMemory(8,
sizeof(*inverse));
2042 if (inverse == (
double *) NULL)
2044 coeff=(
double *) RelinquishMagickMemory(coeff);
2045 (void) ThrowMagickException(exception,GetMagickModule(),
2046 ResourceLimitError,
"MemoryAllocationFailed",
"%s",
2047 "DistortCoefficients");
2048 return((Image *) NULL);
2050 InvertPerspectiveCoefficients(coeff, inverse);
2051 (void) FormatLocaleFile(stderr,
"Perspective Projection:\n");
2052 (void) FormatLocaleFile(stderr,
2053 " -distort PerspectiveProjection \\\n '");
2054 for (i=0; i < 4; i++)
2055 (
void) FormatLocaleFile(stderr,
"%.*g, ",GetMagickPrecision(),
2057 (void) FormatLocaleFile(stderr,
"\n ");
2059 (
void) FormatLocaleFile(stderr,
"%.*g, ",GetMagickPrecision(),
2061 (void) FormatLocaleFile(stderr,
"%.*g'\n",GetMagickPrecision(),
2063 inverse=(
double *) RelinquishMagickMemory(inverse);
2064 (void) FormatLocaleFile(stderr,
"Perspective Distort, FX Equivalent:\n");
2065 (void) FormatLocaleFile(stderr,
"%.1024s",image_gen);
2066 (void) FormatLocaleFile(stderr,
2067 " -fx 'ii=i+page.x+0.5; jj=j+page.y+0.5;\n");
2068 (void) FormatLocaleFile(stderr,
" rr=%+.*g*ii %+.*g*jj + 1;\n",
2069 GetMagickPrecision(),coeff[6],GetMagickPrecision(),coeff[7]);
2070 (void) FormatLocaleFile(stderr,
2071 " xx=(%+.*g*ii %+.*g*jj %+.*g)/rr;\n",
2072 GetMagickPrecision(),coeff[0],GetMagickPrecision(),coeff[1],
2073 GetMagickPrecision(),coeff[2]);
2074 (void) FormatLocaleFile(stderr,
2075 " yy=(%+.*g*ii %+.*g*jj %+.*g)/rr;\n",
2076 GetMagickPrecision(),coeff[3],GetMagickPrecision(),coeff[4],
2077 GetMagickPrecision(),coeff[5]);
2078 (void) FormatLocaleFile(stderr,
" rr%s0 ? %s : blue' \\\n",
2079 coeff[8] < 0.0 ?
"<" :
">", lookup);
2082 case BilinearForwardDistortion:
2084 (void) FormatLocaleFile(stderr,
"BilinearForward Mapping Equations:\n");
2085 (void) FormatLocaleFile(stderr,
"%s", image_gen);
2086 (void) FormatLocaleFile(stderr,
" i = %+lf*x %+lf*y %+lf*x*y %+lf;\n",
2087 coeff[0],coeff[1],coeff[2],coeff[3]);
2088 (void) FormatLocaleFile(stderr,
" j = %+lf*x %+lf*y %+lf*x*y %+lf;\n",
2089 coeff[4],coeff[5],coeff[6],coeff[7]);
2092 (void) FormatLocaleFile(stderr,
" c8 = %+lf c9 = 2*a = %+lf;\n",
2093 coeff[8], coeff[9]);
2095 (void) FormatLocaleFile(stderr,
2096 "BilinearForward Distort, FX Equivalent:\n");
2097 (void) FormatLocaleFile(stderr,
"%s", image_gen);
2098 (void) FormatLocaleFile(stderr,
2099 " -fx 'ii=i+page.x%+lf; jj=j+page.y%+lf;\n",0.5-coeff[3],0.5-
2101 (void) FormatLocaleFile(stderr,
" bb=%lf*ii %+lf*jj %+lf;\n",
2102 coeff[6], -coeff[2], coeff[8]);
2106 (void) FormatLocaleFile(stderr,
2107 " rt=bb*bb %+lf*(%lf*ii%+lf*jj);\n",-2*coeff[9],coeff[4],
2109 (void) FormatLocaleFile(stderr,
2110 " yy=( -bb + sqrt(rt) ) / %lf;\n",coeff[9]);
2113 (
void) FormatLocaleFile(stderr,
" yy=(%lf*ii%+lf*jj)/bb;\n",
2114 -coeff[4],coeff[0]);
2115 (void) FormatLocaleFile(stderr,
2116 " xx=(ii %+lf*yy)/(%lf %+lf*yy);\n",-coeff[1],coeff[0],
2118 if ( coeff[9] != 0 )
2119 (void) FormatLocaleFile(stderr,
" (rt < 0 ) ? red : %s'\n",
2122 (
void) FormatLocaleFile(stderr,
" %s' \\\n", lookup);
2125 case BilinearReverseDistortion:
2128 (void) FormatLocaleFile(stderr,
"Polynomial Projection Distort:\n");
2129 (void) FormatLocaleFile(stderr,
" -distort PolynomialProjection \\\n");
2130 (void) FormatLocaleFile(stderr,
" '1.5, %lf, %lf, %lf, %lf,\n",
2131 coeff[3], coeff[0], coeff[1], coeff[2]);
2132 (void) FormatLocaleFile(stderr,
" %lf, %lf, %lf, %lf'\n",
2133 coeff[7], coeff[4], coeff[5], coeff[6]);
2135 (void) FormatLocaleFile(stderr,
2136 "BilinearReverse Distort, FX Equivalent:\n");
2137 (void) FormatLocaleFile(stderr,
"%s", image_gen);
2138 (void) FormatLocaleFile(stderr,
2139 " -fx 'ii=i+page.x+0.5; jj=j+page.y+0.5;\n");
2140 (void) FormatLocaleFile(stderr,
2141 " xx=%+lf*ii %+lf*jj %+lf*ii*jj %+lf;\n",coeff[0],coeff[1],
2142 coeff[2], coeff[3]);
2143 (void) FormatLocaleFile(stderr,
2144 " yy=%+lf*ii %+lf*jj %+lf*ii*jj %+lf;\n",coeff[4],coeff[5],
2145 coeff[6], coeff[7]);
2146 (void) FormatLocaleFile(stderr,
" %s' \\\n", lookup);
2149 case PolynomialDistortion:
2151 size_t nterms = CastDoubleToSizeT(coeff[1]);
2152 (void) FormatLocaleFile(stderr,
2153 "Polynomial (order %lg, terms %lu), FX Equivalent\n",coeff[0],
2154 (
unsigned long) nterms);
2155 (void) FormatLocaleFile(stderr,
"%s", image_gen);
2156 (void) FormatLocaleFile(stderr,
2157 " -fx 'ii=i+page.x+0.5; jj=j+page.y+0.5;\n");
2158 (void) FormatLocaleFile(stderr,
" xx =");
2159 for (i=0; i < (ssize_t) nterms; i++)
2161 if ((i != 0) && (i%4 == 0))
2162 (void) FormatLocaleFile(stderr,
"\n ");
2163 (void) FormatLocaleFile(stderr,
" %+lf%s",coeff[2+i],
2166 (void) FormatLocaleFile(stderr,
";\n yy =");
2167 for (i=0; i < (ssize_t) nterms; i++)
2169 if ((i != 0) && (i%4 == 0))
2170 (void) FormatLocaleFile(stderr,
"\n ");
2171 (void) FormatLocaleFile(stderr,
" %+lf%s",coeff[2+i+nterms],
2174 (void) FormatLocaleFile(stderr,
";\n %s' \\\n", lookup);
2179 (void) FormatLocaleFile(stderr,
"Arc Distort, Internal Coefficients:\n");
2180 for (i=0; i < 5; i++)
2181 (
void) FormatLocaleFile(stderr,
2182 " c%.20g = %+lf\n",(
double) i,coeff[i]);
2183 (void) FormatLocaleFile(stderr,
"Arc Distort, FX Equivalent:\n");
2184 (void) FormatLocaleFile(stderr,
"%s", image_gen);
2185 (void) FormatLocaleFile(stderr,
" -fx 'ii=i+page.x; jj=j+page.y;\n");
2186 (void) FormatLocaleFile(stderr,
" xx=(atan2(jj,ii)%+lf)/(2*pi);\n",
2188 (void) FormatLocaleFile(stderr,
" xx=xx-round(xx);\n");
2189 (void) FormatLocaleFile(stderr,
" xx=xx*%lf %+lf;\n",coeff[1],
2191 (void) FormatLocaleFile(stderr,
2192 " yy=(%lf - hypot(ii,jj)) * %lf;\n",coeff[2],coeff[3]);
2193 (void) FormatLocaleFile(stderr,
" v.p{xx-.5,yy-.5}' \\\n");
2196 case PolarDistortion:
2198 (void) FormatLocaleFile(stderr,
"Polar Distort, Internal Coefficients\n");
2199 for (i=0; i < 8; i++)
2200 (
void) FormatLocaleFile(stderr,
" c%.20g = %+lf\n",(
double) i,
2202 (void) FormatLocaleFile(stderr,
"Polar Distort, FX Equivalent:\n");
2203 (void) FormatLocaleFile(stderr,
"%s", image_gen);
2204 (void) FormatLocaleFile(stderr,
2205 " -fx 'ii=i+page.x%+lf; jj=j+page.y%+lf;\n",-coeff[2],-coeff[3]);
2206 (void) FormatLocaleFile(stderr,
" xx=(atan2(ii,jj)%+lf)/(2*pi);\n",
2207 -(coeff[4]+coeff[5])/2 );
2208 (void) FormatLocaleFile(stderr,
" xx=xx-round(xx);\n");
2209 (void) FormatLocaleFile(stderr,
" xx=xx*2*pi*%lf + v.w/2;\n",
2211 (void) FormatLocaleFile(stderr,
" yy=(hypot(ii,jj)%+lf)*%lf;\n",
2212 -coeff[1],coeff[7] );
2213 (void) FormatLocaleFile(stderr,
" v.p{xx-.5,yy-.5}' \\\n");
2216 case DePolarDistortion:
2218 (void) FormatLocaleFile(stderr,
2219 "DePolar Distort, Internal Coefficients\n");
2220 for (i=0; i < 8; i++)
2221 (
void) FormatLocaleFile(stderr,
" c%.20g = %+lf\n",(
double) i,
2223 (void) FormatLocaleFile(stderr,
"DePolar Distort, FX Equivalent:\n");
2224 (void) FormatLocaleFile(stderr,
"%s", image_gen);
2225 (void) FormatLocaleFile(stderr,
" -fx 'aa=(i+.5)*%lf %+lf;\n",
2226 coeff[6],+coeff[4]);
2227 (void) FormatLocaleFile(stderr,
" rr=(j+.5)*%lf %+lf;\n",
2228 coeff[7],+coeff[1]);
2229 (void) FormatLocaleFile(stderr,
" xx=rr*sin(aa) %+lf;\n",
2231 (void) FormatLocaleFile(stderr,
" yy=rr*cos(aa) %+lf;\n",
2233 (void) FormatLocaleFile(stderr,
" v.p{xx-.5,yy-.5}' \\\n");
2236 case Cylinder2PlaneDistortion:
2238 (void) FormatLocaleFile(stderr,
2239 "Cylinder to Plane Distort, Internal Coefficients\n");
2240 (void) FormatLocaleFile(stderr,
" cylinder_radius = %+lf\n",coeff[1]);
2241 (void) FormatLocaleFile(stderr,
2242 "Cylinder to Plane Distort, FX Equivalent:\n");
2243 (void) FormatLocaleFile(stderr,
"%s", image_gen);
2244 (void) FormatLocaleFile(stderr,
2245 " -fx 'ii=i+page.x%+lf+0.5; jj=j+page.y%+lf+0.5;\n",-coeff[4],
2247 (void) FormatLocaleFile(stderr,
" aa=atan(ii/%+lf);\n",coeff[1]);
2248 (void) FormatLocaleFile(stderr,
" xx=%lf*aa%+lf;\n",
2250 (void) FormatLocaleFile(stderr,
" yy=jj*cos(aa)%+lf;\n",coeff[3]);
2251 (void) FormatLocaleFile(stderr,
" %s' \\\n", lookup);
2254 case Plane2CylinderDistortion:
2256 (void) FormatLocaleFile(stderr,
2257 "Plane to Cylinder Distort, Internal Coefficients\n");
2258 (void) FormatLocaleFile(stderr,
" cylinder_radius = %+lf\n",coeff[1]);
2259 (void) FormatLocaleFile(stderr,
2260 "Plane to Cylinder Distort, FX Equivalent:\n");
2261 (void) FormatLocaleFile(stderr,
"%s", image_gen);
2262 (void) FormatLocaleFile(stderr,
2263 " -fx 'ii=i+page.x%+lf+0.5; jj=j+page.y%+lf+0.5;\n",-coeff[4],
2265 (void) FormatLocaleFile(stderr,
" ii=ii/%+lf;\n",coeff[1]);
2266 (void) FormatLocaleFile(stderr,
" xx=%lf*tan(ii)%+lf;\n",coeff[1],
2268 (void) FormatLocaleFile(stderr,
" yy=jj/cos(ii)%+lf;\n",coeff[3]);
2269 (void) FormatLocaleFile(stderr,
" %s' \\\n", lookup);
2272 case BarrelDistortion:
2273 case BarrelInverseDistortion:
2284 xc=((double)image->columns-1.0)/2.0+image->page.x;
2285 yc=((double)image->rows-1.0)/2.0+image->page.y;
2286 (void) FormatLocaleFile(stderr,
"Barrel%s Distort, FX Equivalent:\n",
2287 method == BarrelDistortion ?
"" :
"Inv");
2288 (void) FormatLocaleFile(stderr,
"%s", image_gen);
2289 if ( fabs(coeff[8]-xc-0.5) < 0.1 && fabs(coeff[9]-yc-0.5) < 0.1 )
2290 (void) FormatLocaleFile(stderr,
" -fx 'xc=(w-1)/2; yc=(h-1)/2;\n");
2292 (
void) FormatLocaleFile(stderr,
" -fx 'xc=%lf; yc=%lf;\n",coeff[8]-
2294 (void) FormatLocaleFile(stderr,
2295 " ii=i-xc; jj=j-yc; rr=hypot(ii,jj);\n");
2296 (void) FormatLocaleFile(stderr,
2297 " ii=ii%s(%lf*rr*rr*rr %+lf*rr*rr %+lf*rr %+lf);\n",
2298 method == BarrelDistortion ?
"*" :
"/",coeff[0],coeff[1],coeff[2],
2300 (void) FormatLocaleFile(stderr,
2301 " jj=jj%s(%lf*rr*rr*rr %+lf*rr*rr %+lf*rr %+lf);\n",
2302 method == BarrelDistortion ?
"*" :
"/",coeff[4],coeff[5],coeff[6],
2304 (void) FormatLocaleFile(stderr,
" p{ii+xc,jj+yc}' \\\n");
2315 {
const char *artifact;
2316 artifact=GetImageArtifact(image,
"distort:scale");
2317 output_scaling = 1.0;
2318 if (artifact != (
const char *) NULL) {
2319 output_scaling = fabs(StringToDouble(artifact,(
char **) NULL));
2320 geometry.width=CastDoubleToSizeT(output_scaling*geometry.width+0.5);
2321 geometry.height=CastDoubleToSizeT(output_scaling*geometry.height+0.5);
2322 geometry.x=CastDoubleToSsizeT(output_scaling*geometry.x+0.5);
2323 geometry.y=CastDoubleToSsizeT(output_scaling*geometry.y+0.5);
2324 if ( output_scaling < 0.1 ) {
2325 coeff = (
double *) RelinquishMagickMemory(coeff);
2326 (void) ThrowMagickException(exception,GetMagickModule(),
2327 OptionError,
"InvalidArgument",
"%s",
"-define distort:scale" );
2328 return((Image *) NULL);
2330 output_scaling = 1/output_scaling;
2333#define ScaleFilter(F,A,B,C,D) \
2334 ScaleResampleFilter( (F), \
2335 output_scaling*(A), output_scaling*(B), \
2336 output_scaling*(C), output_scaling*(D) )
2341 distort_image=CloneImage(image,geometry.width,geometry.height,MagickTrue,
2343 if (distort_image == (Image *) NULL)
2345 coeff=(
double *) RelinquishMagickMemory(coeff);
2346 return((Image *) NULL);
2349 if (SetImageStorageClass(distort_image,DirectClass) == MagickFalse)
2351 coeff=(
double *) RelinquishMagickMemory(coeff);
2352 InheritException(exception,&distort_image->exception);
2353 distort_image=DestroyImage(distort_image);
2354 return((Image *) NULL);
2356 if ((IsPixelGray(&distort_image->background_color) == MagickFalse) &&
2357 (IsGrayColorspace(distort_image->colorspace) != MagickFalse))
2358 (void) SetImageColorspace(distort_image,sRGBColorspace);
2359 if (distort_image->background_color.opacity != OpaqueOpacity)
2360 distort_image->matte=MagickTrue;
2361 distort_image->page.x=geometry.x;
2362 distort_image->page.y=geometry.y;
2380 **magick_restrict resample_filter;
2387 GetMagickPixelPacket(distort_image,&zero);
2388 resample_filter=AcquireResampleFilterTLS(image,UndefinedVirtualPixelMethod,
2389 MagickFalse,exception);
2390 distort_view=AcquireAuthenticCacheView(distort_image,exception);
2391#if defined(MAGICKCORE_OPENMP_SUPPORT)
2392 #pragma omp parallel for schedule(static) shared(progress,status) \
2393 magick_number_threads(image,distort_image,distort_image->rows,1)
2395 for (j=0; j < (ssize_t) distort_image->rows; j++)
2398 id = GetOpenMPThreadId();
2415 *magick_restrict indexes;
2423 q=QueueCacheViewAuthenticPixels(distort_view,0,j,distort_image->columns,1,
2425 if (q == (PixelPacket *) NULL)
2430 indexes=GetCacheViewAuthenticIndexQueue(distort_view);
2438 case AffineDistortion:
2439 ScaleFilter( resample_filter[
id],
2441 coeff[3], coeff[4] );
2454 GetMagickPixelPacket(distort_image,&invalid);
2455 SetMagickPixelPacket(distort_image,&distort_image->matte_color,
2456 (IndexPacket *) NULL, &invalid);
2457 if (distort_image->colorspace == CMYKColorspace)
2458 ConvertRGBToCMYK(&invalid);
2460 for (i=0; i < (ssize_t) distort_image->columns; i++)
2463 d.x = (double) (geometry.x+i+0.5)*output_scaling;
2464 d.y = (double) (geometry.y+j+0.5)*output_scaling;
2468 case AffineDistortion:
2470 s.x=coeff[0]*d.x+coeff[1]*d.y+coeff[2];
2471 s.y=coeff[3]*d.x+coeff[4]*d.y+coeff[5];
2475 case PerspectiveDistortion:
2478 p,q,r,abs_r,abs_c6,abs_c7,scale;
2480 p=coeff[0]*d.x+coeff[1]*d.y+coeff[2];
2481 q=coeff[3]*d.x+coeff[4]*d.y+coeff[5];
2482 r=coeff[6]*d.x+coeff[7]*d.y+1.0;
2484 validity = (r*coeff[8] < 0.0) ? 0.0 : 1.0;
2487 abs_c6 = fabs(coeff[6]);
2488 abs_c7 = fabs(coeff[7]);
2489 if ( abs_c6 > abs_c7 ) {
2490 if ( abs_r < abs_c6*output_scaling )
2491 validity = 0.5 - coeff[8]*r/(coeff[6]*output_scaling);
2493 else if ( abs_r < abs_c7*output_scaling )
2494 validity = 0.5 - coeff[8]*r/(coeff[7]*output_scaling);
2496 if ( validity > 0.0 ) {
2503 ScaleFilter( resample_filter[
id],
2504 (r*coeff[0] - p*coeff[6])*scale,
2505 (r*coeff[1] - p*coeff[7])*scale,
2506 (r*coeff[3] - q*coeff[6])*scale,
2507 (r*coeff[4] - q*coeff[7])*scale );
2511 case BilinearReverseDistortion:
2514 s.x=coeff[0]*d.x+coeff[1]*d.y+coeff[2]*d.x*d.y+coeff[3];
2515 s.y=coeff[4]*d.x+coeff[5]*d.y
2516 +coeff[6]*d.x*d.y+coeff[7];
2518 ScaleFilter( resample_filter[
id],
2519 coeff[0] + coeff[2]*d.y,
2520 coeff[1] + coeff[2]*d.x,
2521 coeff[4] + coeff[6]*d.y,
2522 coeff[5] + coeff[6]*d.x );
2525 case BilinearForwardDistortion:
2530 d.x -= coeff[3]; d.y -= coeff[7];
2531 b = coeff[6]*d.x - coeff[2]*d.y + coeff[8];
2532 c = coeff[4]*d.x - coeff[0]*d.y;
2537 if ( fabs(coeff[9]) < MagickEpsilon )
2540 c = b*b - 2*coeff[9]*c;
2544 s.y = ( -b + sqrt(c) )/coeff[9];
2546 if ( validity > 0.0 )
2547 s.x = ( d.x - coeff[1]*s.y) / ( coeff[0] + coeff[2]*s.y );
2557 case BilinearDistortion:
2562 case PolynomialDistortion:
2569 nterms=(ssize_t)coeff[1];
2574 s.x=s.y=du.x=du.y=dv.x=dv.y=0.0;
2575 for(k=0; k < nterms; k++) {
2576 s.x += poly_basis_fn(k,d.x,d.y)*coeff[2+k];
2577 du.x += poly_basis_dx(k,d.x,d.y)*coeff[2+k];
2578 du.y += poly_basis_dy(k,d.x,d.y)*coeff[2+k];
2579 s.y += poly_basis_fn(k,d.x,d.y)*coeff[2+k+nterms];
2580 dv.x += poly_basis_dx(k,d.x,d.y)*coeff[2+k+nterms];
2581 dv.y += poly_basis_dy(k,d.x,d.y)*coeff[2+k+nterms];
2583 ScaleFilter( resample_filter[
id], du.x,du.y,dv.x,dv.y );
2589 s.x = (double) ((atan2(d.y,d.x) - coeff[0])/Magick2PI);
2590 s.x -= MagickRound(s.x);
2591 s.y = hypot(d.x,d.y);
2598 if ( s.y > MagickEpsilon )
2599 ScaleFilter( resample_filter[
id],
2600 (
double) (coeff[1]/(Magick2PI*s.y)), 0, 0, coeff[3] );
2602 ScaleFilter( resample_filter[
id],
2603 distort_image->columns*2, 0, 0, coeff[3] );
2606 s.x = s.x*coeff[1] + coeff[4] + image->page.x +0.5;
2607 s.y = (coeff[2] - s.y) * coeff[3] + image->page.y;
2610 case PolarDistortion:
2614 s.x = atan2(d.x,d.y) - (coeff[4]+coeff[5])/2;
2616 s.x -= MagickRound(s.x);
2618 s.y = hypot(d.x,d.y);
2623 if ( s.y > MagickEpsilon )
2624 ScaleFilter( resample_filter[
id],
2625 (
double) (coeff[6]/(Magick2PI*s.y)), 0, 0, coeff[7] );
2627 ScaleFilter( resample_filter[
id],
2628 distort_image->columns*2, 0, 0, coeff[7] );
2631 s.x = s.x*coeff[6] + (double)image->columns/2.0 + image->page.x;
2632 s.y = (s.y-coeff[1])*coeff[7] + image->page.y;
2635 case DePolarDistortion:
2638 d.x = ((double)i+0.5)*output_scaling*coeff[6]+coeff[4];
2639 d.y = ((double)j+0.5)*output_scaling*coeff[7]+coeff[1];
2640 s.x = d.y*sin(d.x) + coeff[2];
2641 s.y = d.y*cos(d.x) + coeff[3];
2645 case Cylinder2PlaneDistortion:
2649 d.x -= coeff[4]; d.y -= coeff[5];
2656 ScaleFilter( resample_filter[
id],
2657 1.0/(1.0+d.x*d.x), 0.0, -d.x*s.y*cx*cx/coeff[1], s.y/d.y );
2659if ( i == 0 && j == 0 ) {
2660 fprintf(stderr,
"x=%lf y=%lf u=%lf v=%lf\n", d.x*coeff[1], d.y, s.x, s.y);
2661 fprintf(stderr,
"phi = %lf\n", (
double)(ax * 180.0/MagickPI) );
2662 fprintf(stderr,
"du/dx=%lf du/dx=%lf dv/dx=%lf dv/dy=%lf\n",
2663 1.0/(1.0+d.x*d.x), 0.0, -d.x*s.y*cx*cx/coeff[1], s.y/d.y );
2667 s.x += coeff[2]; s.y += coeff[3];
2670 case Plane2CylinderDistortion:
2673 d.x -= coeff[4]; d.y -= coeff[5];
2677 validity = (double) ((coeff[1]*MagickPI2 - fabs(d.x))/output_scaling + 0.5);
2679 if ( validity > 0.0 ) {
2687 ScaleFilter( resample_filter[
id],
2688 cx*cx, 0.0, s.y*cx/coeff[1], cx );
2691if ( d.x == 0.5 && d.y == 0.5 ) {
2692 fprintf(stderr,
"x=%lf y=%lf u=%lf v=%lf\n", d.x*coeff[1], d.y, s.x, s.y);
2693 fprintf(stderr,
"radius = %lf phi = %lf validity = %lf\n",
2694 coeff[1], (
double)(d.x * 180.0/MagickPI), validity );
2695 fprintf(stderr,
"du/dx=%lf du/dx=%lf dv/dx=%lf dv/dy=%lf\n",
2696 cx*cx, 0.0, s.y*cx/coeff[1], cx);
2701 s.x += coeff[2]; s.y += coeff[3];
2704 case BarrelDistortion:
2705 case BarrelInverseDistortion:
2707 double r,fx,fy,gx,gy;
2711 r = sqrt(d.x*d.x+d.y*d.y);
2712 if ( r > MagickEpsilon ) {
2713 fx = ((coeff[0]*r + coeff[1])*r + coeff[2])*r + coeff[3];
2714 fy = ((coeff[4]*r + coeff[5])*r + coeff[6])*r + coeff[7];
2715 gx = ((3*coeff[0]*r + 2*coeff[1])*r + coeff[2])/r;
2716 gy = ((3*coeff[4]*r + 2*coeff[5])*r + coeff[6])/r;
2718 if ( method == BarrelInverseDistortion ) {
2719 fx = 1/fx; fy = 1/fy;
2720 gx *= -fx*fx; gy *= -fy*fy;
2723 s.x = d.x*fx + coeff[8];
2724 s.y = d.y*fy + coeff[9];
2725 ScaleFilter( resample_filter[
id],
2726 gx*d.x*d.x + fx, gx*d.x*d.y,
2727 gy*d.x*d.y, gy*d.y*d.y + fy );
2736 if ( method == BarrelDistortion )
2737 ScaleFilter( resample_filter[
id],
2738 coeff[3], 0, 0, coeff[7] );
2741 ScaleFilter( resample_filter[
id],
2742 1.0/coeff[3], 0, 0, 1.0/coeff[7] );
2746 case ShepardsDistortion:
2761 denominator = s.x = s.y = 0;
2762 for(i=0; i<number_arguments; i+=4) {
2764 ((double)d.x-arguments[i+2])*((
double)d.x-arguments[i+2])
2765 + ((double)d.y-arguments[i+3])*((
double)d.y-arguments[i+3]);
2766 weight = pow(weight,coeff[0]);
2767 weight = ( weight < 1.0 ) ? 1.0 : 1.0/weight;
2769 s.x += (arguments[ i ]-arguments[i+2])*weight;
2770 s.y += (arguments[i+1]-arguments[i+3])*weight;
2771 denominator += weight;
2783 if ( bestfit && method != ArcDistortion ) {
2784 s.x -= image->page.x;
2785 s.y -= image->page.y;
2790 if ( validity <= 0.0 ) {
2792 SetPixelPacket(distort_image,&invalid,q,indexes);
2796 status=ResamplePixelColor(resample_filter[
id],s.x,s.y,&pixel);
2797 if (status == MagickFalse)
2798 SetPixelPacket(distort_image,&invalid,q,indexes);
2802 if ( validity < 1.0 ) {
2805 MagickPixelCompositeBlend(&pixel,validity,&invalid,(1.0-validity),
2809 SetPixelPacket(distort_image,&pixel,q,indexes);
2814 sync=SyncCacheViewAuthenticPixels(distort_view,exception);
2815 if (sync == MagickFalse)
2817 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2822#if defined(MAGICKCORE_OPENMP_SUPPORT)
2826 proceed=SetImageProgress(image,DistortImageTag,progress,image->rows);
2827 if (proceed == MagickFalse)
2831 distort_view=DestroyCacheView(distort_view);
2832 resample_filter=DestroyResampleFilterTLS(resample_filter);
2834 if (status == MagickFalse)
2835 distort_image=DestroyImage(distort_image);
2841 if ( method == ArcDistortion && !bestfit && !viewport_given ) {
2842 distort_image->page.x = 0;
2843 distort_image->page.y = 0;
2845 coeff=(
double *) RelinquishMagickMemory(coeff);
2846 return(distort_image);
2883MagickExport Image *RotateImage(
const Image *image,
const double degrees,
2884 ExceptionInfo *exception)
2902 assert(image != (Image *) NULL);
2903 assert(image->signature == MagickCoreSignature);
2904 assert(exception != (ExceptionInfo *) NULL);
2905 assert(exception->signature == MagickCoreSignature);
2906 if (IsEventLogging() != MagickFalse)
2907 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
2908 angle=fmod(degrees,360.0);
2909 while (angle < -45.0)
2911 for (rotations=0; angle > 45.0; rotations++)
2914 shear.x=(-tan((
double) DegreesToRadians(angle)/2.0));
2915 shear.y=sin((
double) DegreesToRadians(angle));
2916 if ((fabs(shear.x) < MagickEpsilon) && (fabs(shear.y) < MagickEpsilon))
2917 return(IntegralRotateImage(image,rotations,exception));
2918 distort_image=CloneImage(image,0,0,MagickTrue,exception);
2919 if (distort_image == (Image *) NULL)
2920 return((Image *) NULL);
2921 (void) SetImageVirtualPixelMethod(distort_image,BackgroundVirtualPixelMethod);
2922 rotate_image=DistortImage(distort_image,ScaleRotateTranslateDistortion,1,
2923 °rees,MagickTrue,exception);
2924 distort_image=DestroyImage(distort_image);
2925 return(rotate_image);
2970MagickExport Image *SparseColorImage(
const Image *image,
2971 const ChannelType channel,
const SparseColorMethod method,
2972 const size_t number_arguments,
const double *arguments,
2973 ExceptionInfo *exception)
2975#define SparseColorTag "Distort/SparseColor"
2989 assert(image != (Image *) NULL);
2990 assert(image->signature == MagickCoreSignature);
2991 assert(exception != (ExceptionInfo *) NULL);
2992 assert(exception->signature == MagickCoreSignature);
2993 if (IsEventLogging() != MagickFalse)
2994 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
2999 if ( channel & RedChannel ) number_colors++;
3000 if ( channel & GreenChannel ) number_colors++;
3001 if ( channel & BlueChannel ) number_colors++;
3002 if ( channel & IndexChannel ) number_colors++;
3003 if ( channel & OpacityChannel ) number_colors++;
3009 { DistortImageMethod
3012 distort_method=(DistortImageMethod) method;
3013 if ( distort_method >= SentinelDistortion )
3014 distort_method = ShepardsDistortion;
3015 coeff = GenerateCoefficients(image, &distort_method, number_arguments,
3016 arguments, number_colors, exception);
3017 if ( coeff == (
double *) NULL )
3018 return((Image *) NULL);
3025 sparse_method = (SparseColorMethod) distort_method;
3026 if ( distort_method == ShepardsDistortion )
3027 sparse_method = method;
3028 if ( sparse_method == InverseColorInterpolate )
3033 if ( GetImageArtifact(image,
"verbose") != (
const char *) NULL ) {
3035 switch (sparse_method) {
3036 case BarycentricColorInterpolate:
3039 (void) FormatLocaleFile(stderr,
"Barycentric Sparse Color:\n");
3040 if ( channel & RedChannel )
3041 (void) FormatLocaleFile(stderr,
" -channel R -fx '%+lf*i %+lf*j %+lf' \\\n",
3042 coeff[x], coeff[x+1], coeff[x+2]),x+=3;
3043 if ( channel & GreenChannel )
3044 (void) FormatLocaleFile(stderr,
" -channel G -fx '%+lf*i %+lf*j %+lf' \\\n",
3045 coeff[x], coeff[x+1], coeff[x+2]),x+=3;
3046 if ( channel & BlueChannel )
3047 (void) FormatLocaleFile(stderr,
" -channel B -fx '%+lf*i %+lf*j %+lf' \\\n",
3048 coeff[x], coeff[x+1], coeff[x+2]),x+=3;
3049 if ( channel & IndexChannel )
3050 (void) FormatLocaleFile(stderr,
" -channel K -fx '%+lf*i %+lf*j %+lf' \\\n",
3051 coeff[x], coeff[x+1], coeff[x+2]),x+=3;
3052 if ( channel & OpacityChannel )
3053 (void) FormatLocaleFile(stderr,
" -channel A -fx '%+lf*i %+lf*j %+lf' \\\n",
3054 coeff[x], coeff[x+1], coeff[x+2]),x+=3;
3057 case BilinearColorInterpolate:
3060 (void) FormatLocaleFile(stderr,
"Bilinear Sparse Color\n");
3061 if ( channel & RedChannel )
3062 (void) FormatLocaleFile(stderr,
" -channel R -fx '%+lf*i %+lf*j %+lf*i*j %+lf;\n",
3063 coeff[ x ], coeff[x+1],
3064 coeff[x+2], coeff[x+3]),x+=4;
3065 if ( channel & GreenChannel )
3066 (void) FormatLocaleFile(stderr,
" -channel G -fx '%+lf*i %+lf*j %+lf*i*j %+lf;\n",
3067 coeff[ x ], coeff[x+1],
3068 coeff[x+2], coeff[x+3]),x+=4;
3069 if ( channel & BlueChannel )
3070 (void) FormatLocaleFile(stderr,
" -channel B -fx '%+lf*i %+lf*j %+lf*i*j %+lf;\n",
3071 coeff[ x ], coeff[x+1],
3072 coeff[x+2], coeff[x+3]),x+=4;
3073 if ( channel & IndexChannel )
3074 (void) FormatLocaleFile(stderr,
" -channel K -fx '%+lf*i %+lf*j %+lf*i*j %+lf;\n",
3075 coeff[ x ], coeff[x+1],
3076 coeff[x+2], coeff[x+3]),x+=4;
3077 if ( channel & OpacityChannel )
3078 (void) FormatLocaleFile(stderr,
" -channel A -fx '%+lf*i %+lf*j %+lf*i*j %+lf;\n",
3079 coeff[ x ], coeff[x+1],
3080 coeff[x+2], coeff[x+3]),x+=4;
3095 sparse_image=CloneImage(image,0,0,MagickTrue,exception);
3096 if (sparse_image == (Image *) NULL)
3097 return((Image *) NULL);
3098 if (SetImageStorageClass(sparse_image,DirectClass) == MagickFalse)
3100 InheritException(exception,&image->exception);
3101 sparse_image=DestroyImage(sparse_image);
3102 return((Image *) NULL);
3119 sparse_view=AcquireAuthenticCacheView(sparse_image,exception);
3120#if defined(MAGICKCORE_OPENMP_SUPPORT)
3121 #pragma omp parallel for schedule(static) shared(progress,status) \
3122 magick_number_threads(image,sparse_image,sparse_image->rows,1)
3124 for (j=0; j < (ssize_t) sparse_image->rows; j++)
3133 *magick_restrict indexes;
3141 q=GetCacheViewAuthenticPixels(sparse_view,0,j,sparse_image->columns,
3143 if (q == (PixelPacket *) NULL)
3148 indexes=GetCacheViewAuthenticIndexQueue(sparse_view);
3149 GetMagickPixelPacket(sparse_image,&pixel);
3150 for (i=0; i < (ssize_t) image->columns; i++)
3152 SetMagickPixelPacket(image,q,indexes,&pixel);
3153 switch (sparse_method)
3155 case BarycentricColorInterpolate:
3158 if ( channel & RedChannel )
3159 pixel.red = coeff[x]*i +coeff[x+1]*j
3161 if ( channel & GreenChannel )
3162 pixel.green = coeff[x]*i +coeff[x+1]*j
3164 if ( channel & BlueChannel )
3165 pixel.blue = coeff[x]*i +coeff[x+1]*j
3167 if ( channel & IndexChannel )
3168 pixel.index = coeff[x]*i +coeff[x+1]*j
3170 if ( channel & OpacityChannel )
3171 pixel.opacity = coeff[x]*i +coeff[x+1]*j
3175 case BilinearColorInterpolate:
3178 if ( channel & RedChannel )
3179 pixel.red = coeff[x]*i + coeff[x+1]*j +
3180 coeff[x+2]*i*j + coeff[x+3], x+=4;
3181 if ( channel & GreenChannel )
3182 pixel.green = coeff[x]*i + coeff[x+1]*j +
3183 coeff[x+2]*i*j + coeff[x+3], x+=4;
3184 if ( channel & BlueChannel )
3185 pixel.blue = coeff[x]*i + coeff[x+1]*j +
3186 coeff[x+2]*i*j + coeff[x+3], x+=4;
3187 if ( channel & IndexChannel )
3188 pixel.index = coeff[x]*i + coeff[x+1]*j +
3189 coeff[x+2]*i*j + coeff[x+3], x+=4;
3190 if ( channel & OpacityChannel )
3191 pixel.opacity = coeff[x]*i + coeff[x+1]*j +
3192 coeff[x+2]*i*j + coeff[x+3], x+=4;
3195 case InverseColorInterpolate:
3196 case ShepardsColorInterpolate:
3203 if ( channel & RedChannel ) pixel.red = 0.0;
3204 if ( channel & GreenChannel ) pixel.green = 0.0;
3205 if ( channel & BlueChannel ) pixel.blue = 0.0;
3206 if ( channel & IndexChannel ) pixel.index = 0.0;
3207 if ( channel & OpacityChannel ) pixel.opacity = 0.0;
3209 for(k=0; k<number_arguments; k+=2+number_colors) {
3210 ssize_t x=(ssize_t) k+2;
3212 ((double)i-arguments[ k ])*((
double)i-arguments[ k ])
3213 + ((double)j-arguments[k+1])*((
double)j-arguments[k+1]);
3214 weight = pow(weight,coeff[0]);
3215 weight = ( weight < 1.0 ) ? 1.0 : 1.0/weight;
3216 if ( channel & RedChannel )
3217 pixel.red += arguments[x++]*weight;
3218 if ( channel & GreenChannel )
3219 pixel.green += arguments[x++]*weight;
3220 if ( channel & BlueChannel )
3221 pixel.blue += arguments[x++]*weight;
3222 if ( channel & IndexChannel )
3223 pixel.index += arguments[x++]*weight;
3224 if ( channel & OpacityChannel )
3225 pixel.opacity += arguments[x++]*weight;
3226 denominator += weight;
3228 if ( channel & RedChannel ) pixel.red /= denominator;
3229 if ( channel & GreenChannel ) pixel.green /= denominator;
3230 if ( channel & BlueChannel ) pixel.blue /= denominator;
3231 if ( channel & IndexChannel ) pixel.index /= denominator;
3232 if ( channel & OpacityChannel ) pixel.opacity /= denominator;
3235 case ManhattanColorInterpolate:
3241 minimum = MagickMaximumValue;
3246 for(k=0; k<number_arguments; k+=2+number_colors) {
3248 fabs((
double)i-arguments[ k ])
3249 + fabs((
double)j-arguments[k+1]);
3250 if ( distance < minimum ) {
3251 ssize_t x=(ssize_t) k+2;
3252 if ( channel & RedChannel ) pixel.red = arguments[x++];
3253 if ( channel & GreenChannel ) pixel.green = arguments[x++];
3254 if ( channel & BlueChannel ) pixel.blue = arguments[x++];
3255 if ( channel & IndexChannel ) pixel.index = arguments[x++];
3256 if ( channel & OpacityChannel ) pixel.opacity = arguments[x++];
3262 case VoronoiColorInterpolate:
3269 minimum = MagickMaximumValue;
3274 for(k=0; k<number_arguments; k+=2+number_colors) {
3276 ((double)i-arguments[ k ])*((
double)i-arguments[ k ])
3277 + ((double)j-arguments[k+1])*((
double)j-arguments[k+1]);
3278 if ( distance < minimum ) {
3279 ssize_t x=(ssize_t) k+2;
3280 if ( channel & RedChannel ) pixel.red = arguments[x++];
3281 if ( channel & GreenChannel ) pixel.green = arguments[x++];
3282 if ( channel & BlueChannel ) pixel.blue = arguments[x++];
3283 if ( channel & IndexChannel ) pixel.index = arguments[x++];
3284 if ( channel & OpacityChannel ) pixel.opacity = arguments[x++];
3292 if ( channel & RedChannel )
3293 pixel.red=ClampPixel((MagickRealType) QuantumRange*pixel.red);
3294 if ( channel & GreenChannel )
3295 pixel.green=ClampPixel((MagickRealType) QuantumRange*pixel.green);
3296 if ( channel & BlueChannel )
3297 pixel.blue=ClampPixel((MagickRealType) QuantumRange*pixel.blue);
3298 if ( channel & IndexChannel )
3299 pixel.index=ClampPixel((MagickRealType) QuantumRange*pixel.index);
3300 if ( channel & OpacityChannel )
3301 pixel.opacity=ClampPixel((MagickRealType) QuantumRange*pixel.opacity);
3302 SetPixelPacket(sparse_image,&pixel,q,indexes);
3306 sync=SyncCacheViewAuthenticPixels(sparse_view,exception);
3307 if (sync == MagickFalse)
3309 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3314#if defined(MAGICKCORE_OPENMP_SUPPORT)
3318 proceed=SetImageProgress(image,SparseColorTag,progress,image->rows);
3319 if (proceed == MagickFalse)
3323 sparse_view=DestroyCacheView(sparse_view);
3324 if (status == MagickFalse)
3325 sparse_image=DestroyImage(sparse_image);
3327 coeff = (
double *) RelinquishMagickMemory(coeff);
3328 return(sparse_image);