00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043 #include "magick/studio.h"
00044 #include "magick/artifact.h"
00045 #include "magick/cache.h"
00046 #include "magick/cache-view.h"
00047 #include "magick/colorspace-private.h"
00048 #include "magick/composite-private.h"
00049 #include "magick/distort.h"
00050 #include "magick/exception.h"
00051 #include "magick/exception-private.h"
00052 #include "magick/gem.h"
00053 #include "magick/hashmap.h"
00054 #include "magick/image.h"
00055 #include "magick/list.h"
00056 #include "magick/matrix.h"
00057 #include "magick/memory_.h"
00058 #include "magick/monitor-private.h"
00059 #include "magick/pixel.h"
00060 #include "magick/pixel-private.h"
00061 #include "magick/resample.h"
00062 #include "magick/resample-private.h"
00063 #include "magick/registry.h"
00064 #include "magick/semaphore.h"
00065 #include "magick/string_.h"
00066 #include "magick/thread-private.h"
00067 #include "magick/token.h"
00068
00069
00070
00071
00072 static inline double MagickMin(const double x,const double y)
00073 {
00074 return( x < y ? x : y);
00075 }
00076 static inline double MagickMax(const double x,const double y)
00077 {
00078 return( x > y ? x : y);
00079 }
00080
00081 static inline void AffineArgsToCoefficients(double *affine)
00082 {
00083
00084 double tmp[4];
00085 tmp[0]=affine[1]; tmp[1]=affine[2]; tmp[2]=affine[3]; tmp[3]=affine[4];
00086 affine[3]=tmp[0]; affine[1]=tmp[1]; affine[4]=tmp[2]; affine[2]=tmp[3];
00087 }
00088 static inline void CoefficientsToAffineArgs(double *coeff)
00089 {
00090
00091 double tmp[4];
00092 tmp[0]=coeff[3]; tmp[1]=coeff[1]; tmp[2]=coeff[4]; tmp[3]=coeff[2];
00093 coeff[1]=tmp[0]; coeff[2]=tmp[1]; coeff[3]=tmp[2]; coeff[4]=tmp[3];
00094 }
00095 static void InvertAffineCoefficients(const double *coeff,double *inverse)
00096 {
00097
00098 double determinant;
00099
00100 determinant=1.0/(coeff[0]*coeff[4]-coeff[1]*coeff[3]);
00101 inverse[0]=determinant*coeff[4];
00102 inverse[1]=determinant*(-coeff[1]);
00103 inverse[2]=determinant*(coeff[1]*coeff[5]-coeff[2]*coeff[4]);
00104 inverse[3]=determinant*(-coeff[3]);
00105 inverse[4]=determinant*coeff[0];
00106 inverse[5]=determinant*(coeff[2]*coeff[3]-coeff[0]*coeff[5]);
00107 }
00108
00109 static void InvertPerspectiveCoefficients(const double *coeff,
00110 double *inverse)
00111 {
00112
00113 double determinant;
00114
00115 determinant=1.0/(coeff[0]*coeff[4]-coeff[3]*coeff[1]);
00116 inverse[0]=determinant*(coeff[4]-coeff[7]*coeff[5]);
00117 inverse[1]=determinant*(coeff[7]*coeff[2]-coeff[1]);
00118 inverse[2]=determinant*(coeff[1]*coeff[5]-coeff[4]*coeff[2]);
00119 inverse[3]=determinant*(coeff[6]*coeff[5]-coeff[3]);
00120 inverse[4]=determinant*(coeff[0]-coeff[6]*coeff[2]);
00121 inverse[5]=determinant*(coeff[3]*coeff[2]-coeff[0]*coeff[5]);
00122 inverse[6]=determinant*(coeff[3]*coeff[7]-coeff[6]*coeff[4]);
00123 inverse[7]=determinant*(coeff[6]*coeff[1]-coeff[0]*coeff[7]);
00124 }
00125
00126 static inline double MagickRound(double x)
00127 {
00128
00129 if (x >= 0.0)
00130 return((double) ((long) (x+0.5)));
00131 return((double) ((long) (x-0.5)));
00132 }
00133
00134
00135
00136
00137
00138
00139
00140
00141
00142
00143
00144
00145
00146
00147
00148
00149
00150
00151
00152
00153 static unsigned long poly_number_terms(double order)
00154 {
00155
00156 if ( order < 1 || order > 5 ||
00157 ( order != floor(order) && (order-1.5) > MagickEpsilon) )
00158 return 0;
00159 return((unsigned long) floor((order+1)*(order+2)/2));
00160 }
00161
00162 static double poly_basis_fn(long n, double x, double y)
00163 {
00164
00165 switch(n) {
00166 case 0: return( 1.0 );
00167 case 1: return( x );
00168 case 2: return( y );
00169 case 3: return( x*y );
00170 case 4: return( x*x );
00171 case 5: return( y*y );
00172 case 6: return( x*x*x );
00173 case 7: return( x*x*y );
00174 case 8: return( x*y*y );
00175 case 9: return( y*y*y );
00176 case 10: return( x*x*x*x );
00177 case 11: return( x*x*x*y );
00178 case 12: return( x*x*y*y );
00179 case 13: return( x*y*y*y );
00180 case 14: return( y*y*y*y );
00181 case 15: return( x*x*x*x*x );
00182 case 16: return( x*x*x*x*y );
00183 case 17: return( x*x*x*y*y );
00184 case 18: return( x*x*y*y*y );
00185 case 19: return( x*y*y*y*y );
00186 case 20: return( y*y*y*y*y );
00187 }
00188 return( 0 );
00189 }
00190 static const char *poly_basis_str(long n)
00191 {
00192
00193 switch(n) {
00194 case 0: return("");
00195 case 1: return("*ii");
00196 case 2: return("*jj");
00197 case 3: return("*ii*jj");
00198 case 4: return("*ii*ii");
00199 case 5: return("*jj*jj");
00200 case 6: return("*ii*ii*ii");
00201 case 7: return("*ii*ii*jj");
00202 case 8: return("*ii*jj*jj");
00203 case 9: return("*jj*jj*jj");
00204 case 10: return("*ii*ii*ii*ii");
00205 case 11: return("*ii*ii*ii*jj");
00206 case 12: return("*ii*ii*jj*jj");
00207 case 13: return("*ii*jj*jj*jj");
00208 case 14: return("*jj*jj*jj*jj");
00209 case 15: return("*ii*ii*ii*ii*ii");
00210 case 16: return("*ii*ii*ii*ii*jj");
00211 case 17: return("*ii*ii*ii*jj*jj");
00212 case 18: return("*ii*ii*jj*jj*jj");
00213 case 19: return("*ii*jj*jj*jj*jj");
00214 case 20: return("*jj*jj*jj*jj*jj");
00215 }
00216 return( "UNKNOWN" );
00217 }
00218 static double poly_basis_dx(long n, double x, double y)
00219 {
00220
00221 switch(n) {
00222 case 0: return( 0.0 );
00223 case 1: return( 1.0 );
00224 case 2: return( 0.0 );
00225 case 3: return( y );
00226 case 4: return( x );
00227 case 5: return( 0.0 );
00228 case 6: return( x*x );
00229 case 7: return( x*y );
00230 case 8: return( y*y );
00231 case 9: return( 0.0 );
00232 case 10: return( x*x*x );
00233 case 11: return( x*x*y );
00234 case 12: return( x*y*y );
00235 case 13: return( y*y*y );
00236 case 14: return( 0.0 );
00237 case 15: return( x*x*x*x );
00238 case 16: return( x*x*x*y );
00239 case 17: return( x*x*y*y );
00240 case 18: return( x*y*y*y );
00241 case 19: return( y*y*y*y );
00242 case 20: return( 0.0 );
00243 }
00244 return( 0.0 );
00245 }
00246 static double poly_basis_dy(long n, double x, double y)
00247 {
00248
00249 switch(n) {
00250 case 0: return( 0.0 );
00251 case 1: return( 0.0 );
00252 case 2: return( 1.0 );
00253 case 3: return( x );
00254 case 4: return( 0.0 );
00255 case 5: return( y );
00256 default: return( poly_basis_dx(n-1,x,y) );
00257 }
00258
00259
00260
00261 }
00262
00263
00264
00265
00266
00267
00268
00269
00270
00271
00272
00273
00274
00275
00276
00277
00278
00279
00280
00281
00282
00283
00284
00285
00286
00287
00288
00289
00290
00291
00292
00293
00294
00295
00296
00297
00298
00299
00300
00301
00302
00303
00304
00305
00306
00307
00308
00309
00310
00311
00312
00313 static double *GenerateCoefficients(const Image *image,
00314 DistortImageMethod *method,const unsigned long number_arguments,
00315 const double *arguments,unsigned long number_values,ExceptionInfo *exception)
00316 {
00317 double
00318 *coeff;
00319
00320 register unsigned long
00321 i;
00322
00323 unsigned long
00324 number_coeff,
00325 cp_size,
00326 cp_x,cp_y,
00327 cp_values;
00328
00329
00330 if ( number_values == 0 ) {
00331
00332
00333
00334 number_values = 2;
00335 cp_values = 0;
00336 cp_x = 2;
00337 cp_y = 3;
00338
00339 }
00340 else {
00341 cp_x = 0;
00342 cp_y = 1;
00343 cp_values = 2;
00344
00345 }
00346 cp_size = number_values+2;
00347
00348
00349
00350
00351 if ( number_arguments < 4*cp_size &&
00352 ( *method == BilinearForwardDistortion
00353 || *method == BilinearReverseDistortion
00354 || *method == PerspectiveDistortion
00355 ) )
00356 *method = AffineDistortion;
00357
00358 number_coeff=0;
00359 switch (*method) {
00360 case AffineDistortion:
00361
00362 number_coeff=3*number_values;
00363 break;
00364 case PolynomialDistortion:
00365
00366 if ( number_arguments <= 1 && (number_arguments-1)%cp_size != 0)
00367 {
00368 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
00369 "InvalidArgument","%s : '%s'","Polynomial",
00370 "Invalid number of args: order [CPs]...");
00371 return((double *) NULL);
00372 }
00373 i = poly_number_terms(arguments[0]);
00374 number_coeff = 2 + i*number_values;
00375 if ( i == 0 ) {
00376 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
00377 "InvalidArgument","%s : '%s'","Polynomial",
00378 "Invalid order, should be interger 1 to 5, or 1.5");
00379 return((double *) NULL);
00380 }
00381 if ( number_arguments < 1+i*cp_size ) {
00382 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
00383 "InvalidArgument", "%s : 'require at least %ld CPs'",
00384 "Polynomial", i);
00385 return((double *) NULL);
00386 }
00387 break;
00388 case BilinearReverseDistortion:
00389 number_coeff=4*number_values;
00390 break;
00391
00392
00393
00394 case BilinearForwardDistortion:
00395 number_coeff=10;
00396 cp_x = 0;
00397 cp_y = 1;
00398 cp_values = 2;
00399 break;
00400 #if 0
00401 case QuadraterialDistortion:
00402 number_coeff=19;
00403 #endif
00404 break;
00405 case ShepardsDistortion:
00406 case VoronoiColorInterpolate:
00407 number_coeff=1;
00408 break;
00409 case ArcDistortion:
00410 number_coeff=5;
00411 break;
00412 case ScaleRotateTranslateDistortion:
00413 case AffineProjectionDistortion:
00414 number_coeff=6;
00415 break;
00416 case PolarDistortion:
00417 case DePolarDistortion:
00418 number_coeff=8;
00419 break;
00420 case PerspectiveDistortion:
00421 case PerspectiveProjectionDistortion:
00422 number_coeff=9;
00423 break;
00424 case BarrelDistortion:
00425 case BarrelInverseDistortion:
00426 number_coeff=10;
00427 break;
00428 case UndefinedDistortion:
00429 default:
00430 assert(! "Unknown Method Given");
00431 }
00432
00433
00434 coeff = (double *) AcquireQuantumMemory(number_coeff,sizeof(*coeff));
00435 if (coeff == (double *) NULL) {
00436 (void) ThrowMagickException(exception,GetMagickModule(),
00437 ResourceLimitError,"MemoryAllocationFailed",
00438 "%s", "GenerateCoefficients");
00439 return((double *) NULL);
00440 }
00441
00442
00443 for (i=0; i < number_coeff; i++)
00444 coeff[i] = 0.0;
00445
00446 switch (*method)
00447 {
00448 case AffineDistortion:
00449 {
00450
00451
00452
00453
00454
00455
00456
00457
00458 if ( number_arguments%cp_size != 0 ||
00459 number_arguments < cp_size ) {
00460 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
00461 "InvalidArgument", "%s : 'require at least %ld CPs'",
00462 "Affine", 1L);
00463 coeff=(double *) RelinquishMagickMemory(coeff);
00464 return((double *) NULL);
00465 }
00466
00467 if ( number_arguments == cp_size ) {
00468
00469 if ( cp_values == 0 ) {
00470
00471 coeff[0] = 1.0;
00472 coeff[2] = arguments[0] - arguments[2];
00473 coeff[4] = 1.0;
00474 coeff[5] = arguments[1] - arguments[3];
00475 }
00476 else {
00477
00478 for (i=0; i<number_values; i++)
00479 coeff[i*3+2] = arguments[cp_values+i];
00480 }
00481 }
00482 else {
00483
00484
00485
00486 double
00487 **matrix,
00488 **vectors,
00489 terms[3];
00490
00491 MagickBooleanType
00492 status;
00493
00494
00495 matrix = AcquireMagickMatrix(3UL,3UL);
00496 vectors = (double **) AcquireQuantumMemory(number_values,sizeof(*vectors));
00497 if (matrix == (double **) NULL || vectors == (double **) NULL)
00498 {
00499 matrix = RelinquishMagickMatrix(matrix, 3UL);
00500 vectors = (double **) RelinquishMagickMemory(vectors);
00501 coeff = (double *) RelinquishMagickMemory(coeff);
00502 (void) ThrowMagickException(exception,GetMagickModule(),
00503 ResourceLimitError,"MemoryAllocationFailed",
00504 "%s", "DistortCoefficients");
00505 return((double *) NULL);
00506 }
00507
00508 for (i=0; i < number_values; i++)
00509 vectors[i] = &(coeff[i*3]);
00510
00511 for (i=0; i < number_arguments; i+=cp_size) {
00512 terms[0] = arguments[i+cp_x];
00513 terms[1] = arguments[i+cp_y];
00514 terms[2] = 1;
00515 LeastSquaresAddTerms(matrix,vectors,terms,
00516 &(arguments[i+cp_values]),3UL,number_values);
00517 }
00518 if ( number_arguments == 2*cp_size ) {
00519
00520
00521
00522
00523 terms[0] = arguments[cp_x]
00524 - ( arguments[cp_size+cp_y] - arguments[cp_y] );
00525 terms[1] = arguments[cp_y] +
00526 + ( arguments[cp_size+cp_x] - arguments[cp_x] );
00527 terms[2] = 1;
00528 if ( cp_values == 0 ) {
00529
00530 double
00531 uv2[2];
00532 uv2[0] = arguments[0] - arguments[5] + arguments[1];
00533 uv2[1] = arguments[1] + arguments[4] - arguments[0];
00534 LeastSquaresAddTerms(matrix,vectors,terms,uv2,3UL,2UL);
00535 }
00536 else {
00537
00538 LeastSquaresAddTerms(matrix,vectors,terms,
00539 &(arguments[cp_values]),3UL,number_values);
00540 }
00541 }
00542
00543 status=GaussJordanElimination(matrix,vectors,3UL,number_values);
00544 matrix = RelinquishMagickMatrix(matrix, 3UL);
00545 vectors = (double **) RelinquishMagickMemory(vectors);
00546 if ( status == MagickFalse ) {
00547 coeff = (double *) RelinquishMagickMemory(coeff);
00548 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
00549 "InvalidArgument","%s : '%s'","Affine",
00550 "Unsolvable Matrix");
00551 return((double *) NULL);
00552 }
00553 }
00554 return(coeff);
00555 }
00556 case AffineProjectionDistortion:
00557 {
00558
00559
00560
00561
00562
00563
00564
00565
00566
00567
00568
00569
00570
00571 double inverse[8];
00572 if (number_arguments != 6) {
00573 coeff = (double *) RelinquishMagickMemory(coeff);
00574 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
00575 "InvalidArgument","%s : '%s'","AffineProjection",
00576 "Needs 6 coeff values");
00577 return((double *) NULL);
00578 }
00579
00580 for(i=0; i<6UL; i++ )
00581 inverse[i] = arguments[i];
00582 AffineArgsToCoefficients(inverse);
00583 InvertAffineCoefficients(inverse, coeff);
00584 *method = AffineDistortion;
00585
00586 return(coeff);
00587 }
00588 case ScaleRotateTranslateDistortion:
00589 {
00590
00591
00592
00593
00594
00595
00596
00597
00598
00599
00600
00601
00602
00603
00604
00605
00606
00607
00608
00609
00610
00611
00612 double
00613 cosine, sine,
00614 x,y,sx,sy,a,nx,ny;
00615
00616
00617 x = nx = (double)(image->columns)/2.0 + (double)image->page.x;
00618 y = ny = (double)(image->rows)/2.0 + (double)image->page.y;
00619 sx = sy = 1.0;
00620 switch ( number_arguments ) {
00621 case 0:
00622 coeff = (double *) RelinquishMagickMemory(coeff);
00623 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
00624 "InvalidArgument","%s : '%s'", "ScaleTranslateRotate",
00625 "Needs at least 1 argument");
00626 return((double *) NULL);
00627 case 1:
00628 a = arguments[0];
00629 break;
00630 case 2:
00631 sx = sy = arguments[0];
00632 a = arguments[1];
00633 break;
00634 default:
00635 x = nx = arguments[0];
00636 y = ny = arguments[1];
00637 switch ( number_arguments ) {
00638 case 3:
00639 a = arguments[2];
00640 break;
00641 case 4:
00642 sx = sy = arguments[2];
00643 a = arguments[3];
00644 break;
00645 case 5:
00646 sx = arguments[2];
00647 sy = arguments[3];
00648 a = arguments[4];
00649 break;
00650 case 6:
00651 sx = sy = arguments[2];
00652 a = arguments[3];
00653 nx = arguments[4];
00654 ny = arguments[5];
00655 break;
00656 case 7:
00657 sx = arguments[2];
00658 sy = arguments[3];
00659 a = arguments[4];
00660 nx = arguments[5];
00661 ny = arguments[6];
00662 break;
00663 default:
00664 coeff = (double *) RelinquishMagickMemory(coeff);
00665 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
00666 "InvalidArgument","%s : '%s'", "ScaleTranslateRotate",
00667 "Too Many Arguments (7 or less)");
00668 return((double *) NULL);
00669 }
00670 break;
00671 }
00672
00673 if ( fabs(sx) < MagickEpsilon || fabs(sy) < MagickEpsilon ) {
00674 coeff = (double *) RelinquishMagickMemory(coeff);
00675 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
00676 "InvalidArgument","%s : '%s'", "ScaleTranslateRotate",
00677 "Zero Scale Given");
00678 return((double *) NULL);
00679 }
00680
00681 a=DegreesToRadians(a); cosine=cos(a); sine=sin(a);
00682
00683 *method = AffineDistortion;
00684 coeff[0]=cosine/sx;
00685 coeff[1]=sine/sx;
00686 coeff[2]=x-nx*coeff[0]-ny*coeff[1];
00687 coeff[3]=(-sine)/sy;
00688 coeff[4]=cosine/sy;
00689 coeff[5]=y-nx*coeff[3]-ny*coeff[4];
00690 return(coeff);
00691 }
00692 case PerspectiveDistortion:
00693 {
00694
00695
00696
00697
00698
00699
00700
00701
00702
00703
00704
00705
00706
00707
00708
00709
00710
00711
00712
00713
00714
00715
00716
00717
00718
00719
00720
00721
00722
00723 double
00724 **matrix,
00725 *vectors[1],
00726 terms[8];
00727
00728 unsigned long
00729 cp_u = cp_values,
00730 cp_v = cp_values+1;
00731
00732 MagickBooleanType
00733 status;
00734
00735 if ( number_arguments%cp_size != 0 ||
00736 number_arguments < cp_size*4 ) {
00737 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
00738 "InvalidArgument", "%s : 'require at least %ld CPs'",
00739 "Perspective", 4L);
00740 coeff=(double *) RelinquishMagickMemory(coeff);
00741 return((double *) NULL);
00742 }
00743
00744 vectors[0] = &(coeff[0]);
00745
00746 matrix = AcquireMagickMatrix(8UL,8UL);
00747 if (matrix == (double **) NULL) {
00748 (void) ThrowMagickException(exception,GetMagickModule(),
00749 ResourceLimitError,"MemoryAllocationFailed",
00750 "%s", "DistortCoefficients");
00751 return((double *) NULL);
00752 }
00753
00754 for (i=0; i < number_arguments; i+=4) {
00755 terms[0]=arguments[i+cp_x];
00756 terms[1]=arguments[i+cp_y];
00757 terms[2]=1.0;
00758 terms[3]=0.0;
00759 terms[4]=0.0;
00760 terms[5]=0.0;
00761 terms[6]=-terms[0]*arguments[i+cp_u];
00762 terms[7]=-terms[1]*arguments[i+cp_u];
00763 LeastSquaresAddTerms(matrix,vectors,terms,&(arguments[i+cp_u]),
00764 8UL,1UL);
00765
00766 terms[0]=0.0;
00767 terms[1]=0.0;
00768 terms[2]=0.0;
00769 terms[3]=arguments[i+cp_x];
00770 terms[4]=arguments[i+cp_y];
00771 terms[5]=1.0;
00772 terms[6]=-terms[3]*arguments[i+cp_v];
00773 terms[7]=-terms[4]*arguments[i+cp_v];
00774 LeastSquaresAddTerms(matrix,vectors,terms,&(arguments[i+cp_v]),
00775 8UL,1UL);
00776 }
00777
00778 status=GaussJordanElimination(matrix,vectors,8UL,1UL);
00779 matrix = RelinquishMagickMatrix(matrix, 8UL);
00780 if ( status == MagickFalse ) {
00781 coeff = (double *) RelinquishMagickMemory(coeff);
00782 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
00783 "InvalidArgument","%s : '%s'","Perspective",
00784 "Unsolvable Matrix");
00785 return((double *) NULL);
00786 }
00787
00788
00789
00790
00791
00792
00793 coeff[8] = coeff[6]*arguments[cp_x]
00794 + coeff[7]*arguments[cp_y] + 1.0;
00795 coeff[8] = (coeff[8] < 0.0) ? -1.0 : +1.0;
00796
00797 return(coeff);
00798 }
00799 case PerspectiveProjectionDistortion:
00800 {
00801
00802
00803
00804 if (number_arguments != 8) {
00805 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
00806 "InvalidArgument","%s : '%s'","PerspectiveProjection",
00807 "Needs 8 coefficient values");
00808 return((double *) NULL);
00809 }
00810
00811 InvertPerspectiveCoefficients(arguments, coeff);
00812
00813
00814
00815
00816
00817
00818
00819 coeff[8] = coeff[6]*arguments[2]
00820 + coeff[7]*arguments[5] + 1.0;
00821 coeff[8] = (coeff[8] < 0.0) ? -1.0 : +1.0;
00822 *method = PerspectiveDistortion;
00823
00824 return(coeff);
00825 }
00826 case BilinearForwardDistortion:
00827 case BilinearReverseDistortion:
00828 {
00829
00830
00831
00832
00833
00834
00835
00836
00837
00838
00839
00840
00841
00842 double
00843 **matrix,
00844 **vectors,
00845 terms[4];
00846
00847 MagickBooleanType
00848 status;
00849
00850
00851 if ( number_arguments%cp_size != 0 ||
00852 number_arguments < cp_size*4 ) {
00853 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
00854 "InvalidArgument", "%s : 'require at least %ld CPs'",
00855 *method == BilinearForwardDistortion ? "BilinearForward" :
00856 "BilinearReverse", 4L);
00857 coeff=(double *) RelinquishMagickMemory(coeff);
00858 return((double *) NULL);
00859 }
00860
00861 matrix = AcquireMagickMatrix(4UL,4UL);
00862 vectors = (double **) AcquireQuantumMemory(number_values,sizeof(*vectors));
00863 if (matrix == (double **) NULL || vectors == (double **) NULL)
00864 {
00865 matrix = RelinquishMagickMatrix(matrix, 4UL);
00866 vectors = (double **) RelinquishMagickMemory(vectors);
00867 coeff = (double *) RelinquishMagickMemory(coeff);
00868 (void) ThrowMagickException(exception,GetMagickModule(),
00869 ResourceLimitError,"MemoryAllocationFailed",
00870 "%s", "DistortCoefficients");
00871 return((double *) NULL);
00872 }
00873
00874 for (i=0; i < number_values; i++)
00875 vectors[i] = &(coeff[i*4]);
00876
00877 for (i=0; i < number_arguments; i+=cp_size) {
00878 terms[0] = arguments[i+cp_x];
00879 terms[1] = arguments[i+cp_y];
00880 terms[2] = terms[0]*terms[1];
00881 terms[3] = 1;
00882 LeastSquaresAddTerms(matrix,vectors,terms,
00883 &(arguments[i+cp_values]),4UL,number_values);
00884 }
00885
00886 status=GaussJordanElimination(matrix,vectors,4UL,number_values);
00887 matrix = RelinquishMagickMatrix(matrix, 4UL);
00888 vectors = (double **) RelinquishMagickMemory(vectors);
00889 if ( status == MagickFalse ) {
00890 coeff = (double *) RelinquishMagickMemory(coeff);
00891 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
00892 "InvalidArgument","%s : '%s'",
00893 *method == BilinearForwardDistortion ?
00894 "BilinearForward" : "BilinearReverse",
00895 "Unsolvable Matrix");
00896 return((double *) NULL);
00897 }
00898 if ( *method == BilinearForwardDistortion ) {
00899
00900
00901
00902
00903
00904
00905
00906
00907
00908
00909
00910
00911
00912
00913
00914
00915
00916
00917
00918
00919
00920
00921
00922
00923
00924
00925
00926
00927
00928
00929
00930
00931
00932
00933
00934
00935
00936
00937
00938 coeff[8] = coeff[0]*coeff[5] - coeff[1]*coeff[4];
00939 coeff[9] = 2*(coeff[2]*coeff[5] - coeff[1]*coeff[6]);
00940 }
00941 return(coeff);
00942 }
00943 #if 0
00944 case QuadrilateralDistortion:
00945 {
00946
00947
00948
00949
00950
00951
00952
00953
00954
00955
00956 return(coeff);
00957 }
00958 #endif
00959
00960 case PolynomialDistortion:
00961 {
00962
00963
00964
00965
00966
00967
00968
00969
00970
00971
00972
00973
00974
00975
00976
00977
00978
00979
00980
00981
00982
00983 double
00984 **matrix,
00985 **vectors,
00986 *terms;
00987
00988 unsigned long
00989 nterms;
00990
00991 register long
00992 j;
00993
00994 MagickBooleanType
00995 status;
00996
00997
00998 coeff[0] = arguments[0];
00999 coeff[1] = (double) poly_number_terms(arguments[0]);
01000 nterms = (unsigned long) coeff[1];
01001
01002
01003 matrix = AcquireMagickMatrix(nterms,nterms);
01004 vectors = (double **) AcquireQuantumMemory(number_values,sizeof(*vectors));
01005 terms = (double *) AcquireQuantumMemory(nterms, sizeof(*terms));
01006 if (matrix == (double **) NULL ||
01007 vectors == (double **) NULL ||
01008 terms == (double *) NULL )
01009 {
01010 matrix = RelinquishMagickMatrix(matrix, nterms);
01011 vectors = (double **) RelinquishMagickMemory(vectors);
01012 terms = (double *) RelinquishMagickMemory(terms);
01013 coeff = (double *) RelinquishMagickMemory(coeff);
01014 (void) ThrowMagickException(exception,GetMagickModule(),
01015 ResourceLimitError,"MemoryAllocationFailed",
01016 "%s", "DistortCoefficients");
01017 return((double *) NULL);
01018 }
01019
01020 for (i=0; i < number_values; i++)
01021 vectors[i] = &(coeff[2+i*nterms]);
01022
01023 for (i=1; i < number_arguments; i+=cp_size) {
01024 for (j=0; j < (long) nterms; j++)
01025 terms[j] = poly_basis_fn(j,arguments[i+cp_x],arguments[i+cp_y]);
01026 LeastSquaresAddTerms(matrix,vectors,terms,
01027 &(arguments[i+cp_values]),nterms,number_values);
01028 }
01029 terms = (double *) RelinquishMagickMemory(terms);
01030
01031 status=GaussJordanElimination(matrix,vectors,nterms,number_values);
01032 matrix = RelinquishMagickMatrix(matrix, nterms);
01033 vectors = (double **) RelinquishMagickMemory(vectors);
01034 if ( status == MagickFalse ) {
01035 coeff = (double *) RelinquishMagickMemory(coeff);
01036 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
01037 "InvalidArgument","%s : '%s'","Polynomial",
01038 "Unsolvable Matrix");
01039 return((double *) NULL);
01040 }
01041 return(coeff);
01042 }
01043 case ArcDistortion:
01044 {
01045
01046
01047
01048
01049
01050
01051
01052
01053
01054
01055
01056
01057
01058
01059
01060
01061
01062
01063
01064
01065
01066
01067
01068
01069
01070
01071
01072
01073
01074
01075
01076
01077
01078
01079 if ( number_arguments >= 1 && arguments[0] < MagickEpsilon ) {
01080 coeff = (double *) RelinquishMagickMemory(coeff);
01081 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
01082 "InvalidArgument","%s : '%s'", "Arc",
01083 "Arc Angle Too Small");
01084 return((double *) NULL);
01085 }
01086 if ( number_arguments >= 3 && arguments[2] < MagickEpsilon ) {
01087 coeff = (double *) RelinquishMagickMemory(coeff);
01088 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
01089 "InvalidArgument","%s : '%s'", "Arc",
01090 "Outer Radius Too Small");
01091 return((double *) NULL);
01092 }
01093 coeff[0] = -MagickPI2;
01094 if ( number_arguments >= 1 )
01095 coeff[1] = DegreesToRadians(arguments[0]);
01096 else
01097 coeff[1] = MagickPI2;
01098 if ( number_arguments >= 2 )
01099 coeff[0] += DegreesToRadians(arguments[1]);
01100 coeff[0] /= Magick2PI;
01101 coeff[0] -= MagickRound(coeff[0]);
01102 coeff[0] *= Magick2PI;
01103 coeff[3] = (double)image->rows-1;
01104 coeff[2] = (double)image->columns/coeff[1] + coeff[3]/2.0;
01105 if ( number_arguments >= 3 ) {
01106 if ( number_arguments >= 4 )
01107 coeff[3] = arguments[2] - arguments[3];
01108 else
01109 coeff[3] *= arguments[2]/coeff[2];
01110 coeff[2] = arguments[2];
01111 }
01112 coeff[4] = ((double)image->columns-1.0)/2.0;
01113
01114 return(coeff);
01115 }
01116 case PolarDistortion:
01117 case DePolarDistortion:
01118 {
01119
01120
01121
01122
01123
01124
01125
01126
01127
01128
01129 if ( number_arguments == 3
01130 || ( number_arguments > 6 && *method == PolarDistortion )
01131 || number_arguments > 8 ) {
01132 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
01133 "InvalidArgument", "%s : number of arguments",
01134 *method == PolarDistortion ? "Polar" : "DePolar");
01135 coeff=(double *) RelinquishMagickMemory(coeff);
01136 return((double *) NULL);
01137 }
01138
01139 if ( number_arguments >= 1 )
01140 coeff[0] = arguments[0];
01141 else
01142 coeff[0] = 0.0;
01143
01144 coeff[1] = number_arguments >= 2 ? arguments[1] : 0.0;
01145
01146 if ( number_arguments >= 4 ) {
01147 coeff[2] = arguments[2];
01148 coeff[3] = arguments[3];
01149 }
01150 else {
01151 coeff[2] = (double)(image->columns)/2.0+image->page.x;
01152 coeff[3] = (double)(image->rows)/2.0+image->page.y;
01153 }
01154
01155 coeff[4] = -MagickPI;
01156 if ( number_arguments >= 5 )
01157 coeff[4] = DegreesToRadians(arguments[4]);
01158 coeff[5] = coeff[4];
01159 if ( number_arguments >= 6 )
01160 coeff[5] = DegreesToRadians(arguments[5]);
01161 if ( fabs(coeff[4]-coeff[5]) < MagickEpsilon )
01162 coeff[5] += Magick2PI;
01163
01164 if ( coeff[0] < MagickEpsilon ) {
01165
01166 if ( fabs(coeff[0]) < MagickEpsilon ) {
01167 coeff[0]=MagickMin(fabs(coeff[2]-image->page.x),
01168 fabs(coeff[3]-image->page.y));
01169 coeff[0]=MagickMin(coeff[0],
01170 fabs(coeff[2]-image->page.x-image->columns));
01171 coeff[0]=MagickMin(coeff[0],
01172 fabs(coeff[3]-image->page.y-image->rows));
01173 }
01174
01175 if ( fabs(-1.0-coeff[0]) < MagickEpsilon ) {
01176 double rx,ry;
01177 rx = coeff[2]-image->page.x;
01178 ry = coeff[3]-image->page.y;
01179 coeff[0] = rx*rx+ry*ry;
01180 ry = coeff[3]-image->page.y-image->rows;
01181 coeff[0] = MagickMax(coeff[0],rx*rx+ry*ry);
01182 rx = coeff[2]-image->page.x-image->columns;
01183 coeff[0] = MagickMax(coeff[0],rx*rx+ry*ry);
01184 ry = coeff[3]-image->page.y;
01185 coeff[0] = MagickMax(coeff[0],rx*rx+ry*ry);
01186 coeff[0] = sqrt(coeff[0]);
01187 }
01188 }
01189
01190 if ( coeff[0] < MagickEpsilon || coeff[1] < -MagickEpsilon
01191 || (coeff[0]-coeff[1]) < MagickEpsilon ) {
01192 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
01193 "InvalidArgument", "%s : Invalid Radius",
01194 *method == PolarDistortion ? "Polar" : "DePolar");
01195 coeff=(double *) RelinquishMagickMemory(coeff);
01196 return((double *) NULL);
01197 }
01198
01199 if ( *method == PolarDistortion ) {
01200 coeff[6]=(double) image->columns/(coeff[5]-coeff[4]);
01201 coeff[7]=(double) image->rows/(coeff[0]-coeff[1]);
01202 }
01203 else {
01204 coeff[6]=(coeff[5]-coeff[4])/image->columns;
01205 coeff[7]=(coeff[0]-coeff[1])/image->rows;
01206 }
01207 return(coeff);
01208 }
01209 case BarrelDistortion:
01210 case BarrelInverseDistortion:
01211 {
01212
01213
01214
01215
01216
01217
01218
01219
01220
01221
01222
01223
01224
01225
01226
01227
01228
01229
01230 double
01231 rscale = 2.0/MagickMin((double) image->columns,(double) image->rows);
01232
01233 if ( number_arguments != 4 && number_arguments != 6 &&
01234 number_arguments != 8 && number_arguments != 10 ) {
01235 coeff=(double *) RelinquishMagickMemory(coeff);
01236 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
01237 "InvalidArgument", "%s : '%s'", "Barrel(Inv)",
01238 "number of arguments" );
01239 return((double *) NULL);
01240 }
01241
01242 coeff[0] = arguments[0];
01243 coeff[1] = arguments[1];
01244 coeff[2] = arguments[2];
01245 if ( number_arguments == 3 || number_arguments == 5 )
01246 coeff[3] = 1 - arguments[0] - arguments[1] - arguments[2];
01247 else
01248 coeff[3] = arguments[3];
01249
01250 coeff[0] *= pow(rscale,3.0);
01251 coeff[1] *= rscale*rscale;
01252 coeff[2] *= rscale;
01253
01254 if ( number_arguments >= 8 ) {
01255 coeff[4] = arguments[4] * pow(rscale,3.0);
01256 coeff[5] = arguments[5] * rscale*rscale;
01257 coeff[6] = arguments[6] * rscale;
01258 coeff[7] = arguments[7];
01259 }
01260 else {
01261 coeff[4] = coeff[0];
01262 coeff[5] = coeff[1];
01263 coeff[6] = coeff[2];
01264 coeff[7] = coeff[3];
01265 }
01266
01267 coeff[8] = ((double)image->columns-1)/2.0 + image->page.x;
01268 coeff[9] = ((double)image->rows-1)/2.0 + image->page.y;
01269 if ( number_arguments == 5 ) {
01270 coeff[8] = arguments[3];
01271 coeff[9] = arguments[4];
01272 }
01273 if ( number_arguments == 6 ) {
01274 coeff[8] = arguments[4];
01275 coeff[9] = arguments[5];
01276 }
01277 if ( number_arguments == 10 ) {
01278 coeff[8] = arguments[8];
01279 coeff[9] = arguments[9];
01280 }
01281 return(coeff);
01282 }
01283 case ShepardsDistortion:
01284 case VoronoiColorInterpolate:
01285 {
01286
01287
01288
01289
01290
01291 if ( number_arguments%cp_size != 0 ||
01292 number_arguments < cp_size ) {
01293 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
01294 "InvalidArgument", "%s : 'require at least %ld CPs'",
01295 "Shepards", 1UL);
01296 coeff=(double *) RelinquishMagickMemory(coeff);
01297 return((double *) NULL);
01298 }
01299 return(coeff);
01300 }
01301 default:
01302 break;
01303 }
01304
01305 assert(! "No Method Handler");
01306 return((double *) NULL);
01307 }
01308
01309
01310
01311
01312
01313
01314
01315
01316
01317
01318
01319
01320
01321
01322
01323
01324
01325
01326
01327
01328
01329
01330
01331
01332
01333
01334
01335
01336
01337
01338
01339
01340
01341
01342
01343
01344
01345
01346
01347
01348
01349
01350
01351
01352
01353
01354
01355
01356
01357
01358
01359
01360
01361
01362
01363
01364
01365
01366
01367
01368
01369
01370
01371
01372
01373
01374
01375
01376
01377
01378
01379
01380
01381
01382
01383
01384
01385
01386
01387
01388
01389
01390
01391
01392
01393
01394
01395
01396
01397 MagickExport Image *DistortImage(const Image *image,DistortImageMethod method,
01398 const unsigned long number_arguments,const double *arguments,
01399 MagickBooleanType bestfit,ExceptionInfo *exception)
01400 {
01401 #define DistortImageTag "Distort/Image"
01402
01403 double
01404 *coeff,
01405 output_scaling;
01406
01407 Image
01408 *distort_image;
01409
01410 RectangleInfo
01411 geometry;
01412
01413 MagickBooleanType
01414 viewport_given;
01415
01416 assert(image != (Image *) NULL);
01417 assert(image->signature == MagickSignature);
01418 if (image->debug != MagickFalse)
01419 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
01420 assert(exception != (ExceptionInfo *) NULL);
01421 assert(exception->signature == MagickSignature);
01422
01423
01424
01425
01426
01427
01428
01429
01430 coeff = GenerateCoefficients(image, &method, number_arguments,
01431 arguments, 0, exception);
01432 if ( coeff == (double *) NULL )
01433 return((Image *) NULL);
01434
01435
01436
01437
01438
01439
01440
01441 geometry.width=image->columns;
01442 geometry.height=image->rows;
01443 geometry.x=0;
01444 geometry.y=0;
01445
01446 if ( method == ArcDistortion ) {
01447
01448 bestfit = MagickTrue;
01449 }
01450
01451
01452 if ( bestfit ) {
01453 PointInfo
01454 s,d,min,max;
01455
01456 s.x=s.y=min.x=max.x=min.y=max.y=0.0;
01457
01458
01459 #define InitalBounds(p) \
01460 { \
01461 \
01462 min.x = max.x = p.x; \
01463 min.y = max.y = p.y; \
01464 }
01465 #define ExpandBounds(p) \
01466 { \
01467 \
01468 min.x = MagickMin(min.x,p.x); \
01469 max.x = MagickMax(max.x,p.x); \
01470 min.y = MagickMin(min.y,p.y); \
01471 max.y = MagickMax(max.y,p.y); \
01472 }
01473
01474 switch (method)
01475 {
01476 case AffineDistortion:
01477 { double inverse[6];
01478 InvertAffineCoefficients(coeff, inverse);
01479 s.x = (double) image->page.x;
01480 s.y = (double) image->page.y;
01481 d.x = inverse[0]*s.x+inverse[1]*s.y+inverse[2];
01482 d.y = inverse[3]*s.x+inverse[4]*s.y+inverse[5];
01483 InitalBounds(d);
01484 s.x = (double) image->page.x+image->columns;
01485 s.y = (double) image->page.y;
01486 d.x = inverse[0]*s.x+inverse[1]*s.y+inverse[2];
01487 d.y = inverse[3]*s.x+inverse[4]*s.y+inverse[5];
01488 ExpandBounds(d);
01489 s.x = (double) image->page.x;
01490 s.y = (double) image->page.y+image->rows;
01491 d.x = inverse[0]*s.x+inverse[1]*s.y+inverse[2];
01492 d.y = inverse[3]*s.x+inverse[4]*s.y+inverse[5];
01493 ExpandBounds(d);
01494 s.x = (double) image->page.x+image->columns;
01495 s.y = (double) image->page.y+image->rows;
01496 d.x = inverse[0]*s.x+inverse[1]*s.y+inverse[2];
01497 d.y = inverse[3]*s.x+inverse[4]*s.y+inverse[5];
01498 ExpandBounds(d);
01499 break;
01500 }
01501 case PerspectiveDistortion:
01502 { double inverse[8], scale;
01503 InvertPerspectiveCoefficients(coeff, inverse);
01504 s.x = (double) image->page.x;
01505 s.y = (double) image->page.y;
01506 scale=inverse[6]*s.x+inverse[7]*s.y+1.0;
01507 scale=1.0/( (fabs(scale) <= MagickEpsilon) ? 1.0 : scale );
01508 d.x = scale*(inverse[0]*s.x+inverse[1]*s.y+inverse[2]);
01509 d.y = scale*(inverse[3]*s.x+inverse[4]*s.y+inverse[5]);
01510 InitalBounds(d);
01511 s.x = (double) image->page.x+image->columns;
01512 s.y = (double) image->page.y;
01513 scale=inverse[6]*s.x+inverse[7]*s.y+1.0;
01514 scale=1.0/( (fabs(scale) <= MagickEpsilon) ? 1.0 : scale );
01515 d.x = scale*(inverse[0]*s.x+inverse[1]*s.y+inverse[2]);
01516 d.y = scale*(inverse[3]*s.x+inverse[4]*s.y+inverse[5]);
01517 ExpandBounds(d);
01518 s.x = (double) image->page.x;
01519 s.y = (double) image->page.y+image->rows;
01520 scale=inverse[6]*s.x+inverse[7]*s.y+1.0;
01521 scale=1.0/( (fabs(scale) <= MagickEpsilon) ? 1.0 : scale );
01522 d.x = scale*(inverse[0]*s.x+inverse[1]*s.y+inverse[2]);
01523 d.y = scale*(inverse[3]*s.x+inverse[4]*s.y+inverse[5]);
01524 ExpandBounds(d);
01525 s.x = (double) image->page.x+image->columns;
01526 s.y = (double) image->page.y+image->rows;
01527 scale=inverse[6]*s.x+inverse[7]*s.y+1.0;
01528 scale=1.0/( (fabs(scale) <= MagickEpsilon) ? 1.0 : scale );
01529 d.x = scale*(inverse[0]*s.x+inverse[1]*s.y+inverse[2]);
01530 d.y = scale*(inverse[3]*s.x+inverse[4]*s.y+inverse[5]);
01531 ExpandBounds(d);
01532 break;
01533 }
01534 case ArcDistortion:
01535 { double a, ca, sa;
01536
01537 a = coeff[0]-coeff[1]/2; ca = cos(a); sa = sin(a);
01538 d.x = coeff[2]*ca;
01539 d.y = coeff[2]*sa;
01540 InitalBounds(d);
01541 d.x = (coeff[2]-coeff[3])*ca;
01542 d.y = (coeff[2]-coeff[3])*sa;
01543 ExpandBounds(d);
01544 a = coeff[0]+coeff[1]/2; ca = cos(a); sa = sin(a);
01545 d.x = coeff[2]*ca;
01546 d.y = coeff[2]*sa;
01547 ExpandBounds(d);
01548 d.x = (coeff[2]-coeff[3])*ca;
01549 d.y = (coeff[2]-coeff[3])*sa;
01550 ExpandBounds(d);
01551
01552 for( a=ceil((coeff[0]-coeff[1]/2.0)/MagickPI2)*MagickPI2;
01553 a<(coeff[0]+coeff[1]/2.0); a+=MagickPI2 ) {
01554 ca = cos(a); sa = sin(a);
01555 d.x = coeff[2]*ca;
01556 d.y = coeff[2]*sa;
01557 ExpandBounds(d);
01558 }
01559
01560
01561
01562
01563
01564 coeff[1] = Magick2PI*image->columns/coeff[1];
01565 coeff[3] = (double)image->rows/coeff[3];
01566 break;
01567 }
01568 case PolarDistortion:
01569 {
01570 if (number_arguments < 2)
01571 coeff[2] = coeff[3] = 0.0;
01572 min.x = coeff[2]-coeff[0];
01573 max.x = coeff[2]+coeff[0];
01574 min.y = coeff[3]-coeff[0];
01575 max.y = coeff[3]+coeff[0];
01576
01577 coeff[7]=(double) geometry.height/(coeff[0]-coeff[1]);
01578 break;
01579 }
01580 case DePolarDistortion:
01581 {
01582
01583
01584 geometry.x = geometry.y = 0;
01585 geometry.height = (unsigned long) ceil(coeff[0]-coeff[1]);
01586 geometry.width = (unsigned long)
01587 ceil((coeff[0]-coeff[1])*(coeff[5]-coeff[4])*0.5);
01588 break;
01589 }
01590 case ShepardsDistortion:
01591 case BilinearForwardDistortion:
01592 case BilinearReverseDistortion:
01593 #if 0
01594 case QuadrilateralDistortion:
01595 #endif
01596 case PolynomialDistortion:
01597 case BarrelDistortion:
01598 case BarrelInverseDistortion:
01599 default:
01600
01601 bestfit = MagickFalse;
01602 break;
01603 }
01604
01605
01606
01607 if ( bestfit && method != DePolarDistortion ) {
01608 geometry.x = (long) floor(min.x-0.5);
01609 geometry.y = (long) floor(min.y-0.5);
01610 geometry.width=(unsigned long) ceil(max.x-geometry.x+0.5);
01611 geometry.height=(unsigned long) ceil(max.y-geometry.y+0.5);
01612 }
01613
01614 if ( method == DePolarDistortion ) {
01615 coeff[6]=(coeff[5]-coeff[4])/geometry.width;
01616 coeff[7]=(coeff[0]-coeff[1])/geometry.height;
01617 }
01618 }
01619
01620
01621
01622
01623
01624 { const char *artifact=GetImageArtifact(image,"distort:viewport");
01625 viewport_given = MagickFalse;
01626 if ( artifact != (const char *) NULL ) {
01627 (void) ParseAbsoluteGeometry(artifact,&geometry);
01628 viewport_given = MagickTrue;
01629 }
01630 }
01631
01632
01633 if ( GetImageArtifact(image,"verbose") != (const char *) NULL ) {
01634 register long
01635 i;
01636 char image_gen[MaxTextExtent];
01637 const char *lookup;
01638
01639
01640 if ( bestfit || viewport_given ) {
01641 (void) FormatMagickString(image_gen, MaxTextExtent," -size %lux%lu "
01642 "-page %+ld%+ld xc: +insert \\\n",geometry.width,geometry.height,
01643 geometry.x,geometry.y);
01644 lookup="v.p{ xx-v.page.x-.5, yy-v.page.x-.5 }";
01645 }
01646 else {
01647 image_gen[0] = '\0';
01648 lookup = "p{ xx-page.x-.5, yy-page.x-.5 }";
01649 }
01650
01651 switch (method) {
01652 case AffineDistortion:
01653 {
01654 double *inverse;
01655
01656 inverse = (double *) AcquireQuantumMemory(6,sizeof(*inverse));
01657 if (inverse == (double *) NULL) {
01658 coeff = (double *) RelinquishMagickMemory(coeff);
01659 (void) ThrowMagickException(exception,GetMagickModule(),
01660 ResourceLimitError,"MemoryAllocationFailed",
01661 "%s", "DistortImages");
01662 return((Image *) NULL);
01663 }
01664 InvertAffineCoefficients(coeff, inverse);
01665 CoefficientsToAffineArgs(inverse);
01666 fprintf(stderr, "Affine Projection:\n");
01667 fprintf(stderr, " -distort AffineProjection \\\n '");
01668 for (i=0; i<5; i++)
01669 fprintf(stderr, "%lf,", inverse[i]);
01670 fprintf(stderr, "%lf'\n", inverse[5]);
01671 inverse = (double *) RelinquishMagickMemory(inverse);
01672
01673 fprintf(stderr, "Affine Distort, FX Equivelent:\n");
01674 fprintf(stderr, "%s", image_gen);
01675 fprintf(stderr, " -fx 'ii=i+page.x+0.5; jj=j+page.y+0.5;\n");
01676 fprintf(stderr, " xx=%+lf*ii %+lf*jj %+lf;\n",
01677 coeff[0], coeff[1], coeff[2]);
01678 fprintf(stderr, " yy=%+lf*ii %+lf*jj %+lf;\n",
01679 coeff[3], coeff[4], coeff[5]);
01680 fprintf(stderr, " %s'\n", lookup);
01681
01682 break;
01683 }
01684
01685 case PerspectiveDistortion:
01686 {
01687 double *inverse;
01688
01689 inverse = (double *) AcquireQuantumMemory(8,sizeof(*inverse));
01690 if (inverse == (double *) NULL) {
01691 coeff = (double *) RelinquishMagickMemory(coeff);
01692 (void) ThrowMagickException(exception,GetMagickModule(),
01693 ResourceLimitError,"MemoryAllocationFailed",
01694 "%s", "DistortCoefficients");
01695 return((Image *) NULL);
01696 }
01697 InvertPerspectiveCoefficients(coeff, inverse);
01698 fprintf(stderr, "Perspective Projection:\n");
01699 fprintf(stderr, " -distort PerspectiveProjection \\\n '");
01700 for (i=0; i<4; i++)
01701 fprintf(stderr, "%lf, ", inverse[i]);
01702 fprintf(stderr, "\n ");
01703 for (; i<7; i++)
01704 fprintf(stderr, "%lf, ", inverse[i]);
01705 fprintf(stderr, "%lf'\n", inverse[7]);
01706 inverse = (double *) RelinquishMagickMemory(inverse);
01707
01708 fprintf(stderr, "Perspective Distort, FX Equivelent:\n");
01709 fprintf(stderr, "%s", image_gen);
01710 fprintf(stderr, " -fx 'ii=i+page.x+0.5; jj=j+page.y+0.5;\n");
01711 fprintf(stderr, " rr=%+lf*ii %+lf*jj + 1;\n",
01712 coeff[6], coeff[7]);
01713 fprintf(stderr, " xx=(%+lf*ii %+lf*jj %+lf)/rr;\n",
01714 coeff[0], coeff[1], coeff[2]);
01715 fprintf(stderr, " yy=(%+lf*ii %+lf*jj %+lf)/rr;\n",
01716 coeff[3], coeff[4], coeff[5]);
01717 fprintf(stderr, " rr%s0 ? %s : blue'\n",
01718 coeff[8] < 0 ? "<" : ">", lookup);
01719 break;
01720 }
01721
01722 case BilinearForwardDistortion:
01723 fprintf(stderr, "BilinearForward Mapping Equations:\n");
01724 fprintf(stderr, "%s", image_gen);
01725 fprintf(stderr, " i = %+lf*x %+lf*y %+lf*x*y %+lf;\n",
01726 coeff[0], coeff[1], coeff[2], coeff[3]);
01727 fprintf(stderr, " j = %+lf*x %+lf*y %+lf*x*y %+lf;\n",
01728 coeff[4], coeff[5], coeff[6], coeff[7]);
01729 #if 0
01730
01731 fprintf(stderr, " c8 = %+lf c9 = 2*a = %+lf;\n",
01732 coeff[8], coeff[9]);
01733 #endif
01734 fprintf(stderr, "BilinearForward Distort, FX Equivelent:\n");
01735 fprintf(stderr, "%s", image_gen);
01736 fprintf(stderr, " -fx 'ii=i+page.x%+lf; jj=j+page.y%+lf;\n",
01737 0.5-coeff[3], 0.5-coeff[7]);
01738 fprintf(stderr, " bb=%lf*ii %+lf*jj %+lf;\n",
01739 coeff[6], -coeff[2], coeff[8]);
01740
01741 if ( coeff[9] != 0 ) {
01742 fprintf(stderr, " rt=bb*bb %+lf*(%lf*ii%+lf*jj);\n",
01743 -2*coeff[9], coeff[4], -coeff[0]);
01744 fprintf(stderr, " yy=( -bb + sqrt(rt) ) / %lf;\n",
01745 coeff[9]);
01746 } else
01747 fprintf(stderr, " yy=(%lf*ii%+lf*jj)/bb;\n",
01748 -coeff[4], coeff[0]);
01749 fprintf(stderr, " xx=(ii %+lf*yy)/(%lf %+lf*yy);\n",
01750 -coeff[1], coeff[0], coeff[2]);
01751 if ( coeff[9] != 0 )
01752 fprintf(stderr, " (rt < 0 ) ? red : %s'\n", lookup);
01753 else
01754 fprintf(stderr, " %s'\n", lookup);
01755 break;
01756
01757 case BilinearReverseDistortion:
01758 #if 0
01759 fprintf(stderr, "Polynomial Projection Distort:\n");
01760 fprintf(stderr, " -distort PolynomialProjection \\\n");
01761 fprintf(stderr, " '1.5, %lf, %lf, %lf, %lf,\n",
01762 coeff[3], coeff[0], coeff[1], coeff[2]);
01763 fprintf(stderr, " %lf, %lf, %lf, %lf'\n",
01764 coeff[7], coeff[4], coeff[5], coeff[6]);
01765 #endif
01766 fprintf(stderr, "BilinearReverse Distort, FX Equivelent:\n");
01767 fprintf(stderr, "%s", image_gen);
01768 fprintf(stderr, " -fx 'ii=i+page.x+0.5; jj=j+page.y+0.5;\n");
01769 fprintf(stderr, " xx=%+lf*ii %+lf*jj %+lf*ii*jj %+lf;\n",
01770 coeff[0], coeff[1], coeff[2], coeff[3]);
01771 fprintf(stderr, " yy=%+lf*ii %+lf*jj %+lf*ii*jj %+lf;\n",
01772 coeff[4], coeff[5], coeff[6], coeff[7]);
01773 fprintf(stderr, " %s'\n", lookup);
01774 break;
01775
01776 case PolynomialDistortion:
01777 {
01778 unsigned long nterms = (unsigned long) coeff[1];
01779 fprintf(stderr, "Polynomial (order %lg, terms %lu), FX Equivelent\n",
01780 coeff[0], nterms);
01781 fprintf(stderr, "%s", image_gen);
01782 fprintf(stderr, " -fx 'ii=i+page.x+0.5; jj=j+page.y+0.5;\n");
01783 fprintf(stderr, " xx =");
01784 for (i=0; i<(long) nterms; i++) {
01785 if ( i != 0 && i%4 == 0 ) fprintf(stderr, "\n ");
01786 fprintf(stderr, " %+lf%s", coeff[2+i],
01787 poly_basis_str(i));
01788 }
01789 fprintf(stderr, ";\n yy =");
01790 for (i=0; i<(long) nterms; i++) {
01791 if ( i != 0 && i%4 == 0 ) fprintf(stderr, "\n ");
01792 fprintf(stderr, " %+lf%s", coeff[2+i+nterms],
01793 poly_basis_str(i));
01794 }
01795 fprintf(stderr, ";\n %s'\n", lookup);
01796 break;
01797 }
01798 case ArcDistortion:
01799 {
01800 fprintf(stderr, "Arc Distort, Internal Coefficients:\n");
01801 for ( i=0; i<5; i++ )
01802 fprintf(stderr, " c%ld = %+lf\n", i, coeff[i]);
01803 fprintf(stderr, "Arc Distort, FX Equivelent:\n");
01804 fprintf(stderr, "%s", image_gen);
01805 fprintf(stderr, " -fx 'ii=i+page.x; jj=j+page.y;\n");
01806 fprintf(stderr, " xx=(atan2(jj,ii)%+lf)/(2*pi);\n",
01807 -coeff[0]);
01808 fprintf(stderr, " xx=xx-round(xx);\n");
01809 fprintf(stderr, " xx=xx*%lf %+lf;\n",
01810 coeff[1], coeff[4]);
01811 fprintf(stderr, " yy=(%lf - hypot(ii,jj)) * %lf;\n",
01812 coeff[2], coeff[3]);
01813 fprintf(stderr, " v.p{xx-.5,yy-.5}'\n");
01814 break;
01815 }
01816 case PolarDistortion:
01817 {
01818 fprintf(stderr, "Polar Distort, Internal Coefficents\n");
01819 for ( i=0; i<8; i++ )
01820 fprintf(stderr, " c%ld = %+lf\n", i, coeff[i]);
01821 fprintf(stderr, "Polar Distort, FX Equivelent:\n");
01822 fprintf(stderr, "%s", image_gen);
01823 fprintf(stderr, " -fx 'ii=i+page.x%+lf; jj=j+page.y%+lf;\n",
01824 -coeff[2], -coeff[3]);
01825 fprintf(stderr, " xx=(atan2(ii,jj)%+lf)/(2*pi);\n",
01826 -(coeff[4]+coeff[5])/2 );
01827 fprintf(stderr, " xx=xx-round(xx);\n");
01828 fprintf(stderr, " xx=xx*2*pi*%lf + v.w/2;\n",
01829 coeff[6] );
01830 fprintf(stderr, " yy=(hypot(ii,jj)%+lf)*%lf;\n",
01831 -coeff[1], coeff[7] );
01832 fprintf(stderr, " v.p{xx-.5,yy-.5}'\n");
01833 break;
01834 }
01835 case DePolarDistortion:
01836 {
01837 fprintf(stderr, "DePolar Distort, Internal Coefficents\n");
01838 for ( i=0; i<8; i++ )
01839 fprintf(stderr, " c%ld = %+lf\n", i, coeff[i]);
01840 fprintf(stderr, "DePolar Distort, FX Equivelent:\n");
01841 fprintf(stderr, "%s", image_gen);
01842 fprintf(stderr, " -fx 'aa=(i+.5)*%lf %+lf;\n", coeff[6], -coeff[4] );
01843 fprintf(stderr, " rr=(j+.5)*%lf %+lf;\n", coeff[7], +coeff[1] );
01844 fprintf(stderr, " xx=rr*sin(aa) %+lf;\n", coeff[2] );
01845 fprintf(stderr, " yy=rr*cos(aa) %+lf;\n", coeff[3] );
01846 fprintf(stderr, " v.p{xx-.5,yy-.5}'\n");
01847 break;
01848 }
01849 case BarrelDistortion:
01850 case BarrelInverseDistortion:
01851 { double xc,yc;
01852 xc = ((double)image->columns-1.0)/2.0 + image->page.x;
01853 yc = ((double)image->rows-1.0)/2.0 + image->page.y;
01854 fprintf(stderr, "Barrel%s Distort, FX Equivelent:\n",
01855 method == BarrelDistortion ? "" : "Inv");
01856 fprintf(stderr, "%s", image_gen);
01857 if ( fabs(coeff[8]-xc) < 0.1 && fabs(coeff[9]-yc) < 0.1 )
01858 fprintf(stderr, " -fx 'xc=(w-1)/2; yc=(h-1)/2;\n");
01859 else
01860 fprintf(stderr, " -fx 'xc=%lf; yc=%lf;\n",
01861 coeff[8], coeff[9]);
01862 fprintf(stderr,
01863 " ii=i-xc; jj=j-yc; rr=hypot(ii,jj);\n");
01864 fprintf(stderr, " ii=ii%s(%lf*rr*rr*rr %+lf*rr*rr %+lf*rr %+lf);\n",
01865 method == BarrelDistortion ? "*" : "/",
01866 coeff[0],coeff[1],coeff[2],coeff[3]);
01867 fprintf(stderr, " jj=jj%s(%lf*rr*rr*rr %+lf*rr*rr %+lf*rr %+lf);\n",
01868 method == BarrelDistortion ? "*" : "/",
01869 coeff[4],coeff[5],coeff[6],coeff[7]);
01870 fprintf(stderr, " v.p{fx*ii+xc,fy*jj+yc}'\n");
01871 }
01872 default:
01873 break;
01874 }
01875 }
01876
01877
01878
01879
01880
01881
01882 { const char *artifact;
01883 artifact=GetImageArtifact(image,"distort:scale");
01884 output_scaling = 1.0;
01885 if (artifact != (const char *) NULL) {
01886 output_scaling = fabs(atof(artifact));
01887 geometry.width *= output_scaling;
01888 geometry.height *= output_scaling;
01889 geometry.x *= output_scaling;
01890 geometry.y *= output_scaling;
01891 if ( output_scaling < 0.1 ) {
01892 coeff = (double *) RelinquishMagickMemory(coeff);
01893 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
01894 "InvalidArgument","%s", "-set option:distort:scale" );
01895 return((Image *) NULL);
01896 }
01897 output_scaling = 1/output_scaling;
01898 }
01899 }
01900 #define ScaleFilter(F,A,B,C,D) \
01901 ScaleResampleFilter( (F), \
01902 output_scaling*(A), output_scaling*(B), \
01903 output_scaling*(C), output_scaling*(D) )
01904
01905
01906
01907
01908 distort_image=CloneImage(image,geometry.width,geometry.height,MagickTrue,
01909 exception);
01910 if (distort_image == (Image *) NULL)
01911 return((Image *) NULL);
01912 if (SetImageStorageClass(distort_image,DirectClass) == MagickFalse)
01913 {
01914 InheritException(exception,&distort_image->exception);
01915 distort_image=DestroyImage(distort_image);
01916 return((Image *) NULL);
01917 }
01918 distort_image->page.x=geometry.x;
01919 distort_image->page.y=geometry.y;
01920 if (distort_image->background_color.opacity != OpaqueOpacity)
01921 distort_image->matte=MagickTrue;
01922
01923 {
01924
01925
01926 long
01927 j,
01928 progress,
01929 status;
01930
01931 MagickPixelPacket
01932 zero;
01933
01934 ResampleFilter
01935 **resample_filter;
01936
01937 CacheView
01938 *distort_view;
01939
01940 status=MagickTrue;
01941 progress=0;
01942 GetMagickPixelPacket(distort_image,&zero);
01943 resample_filter=AcquireResampleFilterThreadSet(image,MagickFalse,exception);
01944 distort_view=AcquireCacheView(distort_image);
01945 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01946 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
01947 #endif
01948 for (j=0; j < (long) distort_image->rows; j++)
01949 {
01950 double
01951 validity;
01952
01953 MagickBooleanType
01954 sync;
01955
01956 MagickPixelPacket
01957 pixel,
01958 invalid;
01959
01960 PointInfo
01961 d,s;
01962
01963 register IndexPacket
01964 *__restrict indexes;
01965
01966 register long
01967 i,
01968 id;
01969
01970 register PixelPacket
01971 *__restrict q;
01972
01973 q=QueueCacheViewAuthenticPixels(distort_view,0,j,distort_image->columns,1,
01974 exception);
01975 if (q == (PixelPacket *) NULL)
01976 {
01977 status=MagickFalse;
01978 continue;
01979 }
01980 indexes=GetCacheViewAuthenticIndexQueue(distort_view);
01981 pixel=zero;
01982
01983
01984
01985
01986 id=GetOpenMPThreadId();
01987 switch (method)
01988 {
01989 case AffineDistortion:
01990 ScaleFilter( resample_filter[id],
01991 coeff[0], coeff[1],
01992 coeff[3], coeff[4] );
01993 break;
01994 default:
01995 break;
01996 }
01997
01998
01999
02000
02001
02002
02003 validity = 1.0;
02004
02005 GetMagickPixelPacket(distort_image,&invalid);
02006 SetMagickPixelPacket(distort_image,&distort_image->matte_color,
02007 (IndexPacket *) NULL, &invalid);
02008 if (distort_image->colorspace == CMYKColorspace)
02009 ConvertRGBToCMYK(&invalid);
02010
02011 for (i=0; i < (long) distort_image->columns; i++)
02012 {
02013
02014 d.x = (double) (geometry.x+i+0.5)*output_scaling;
02015 d.y = (double) (geometry.y+j+0.5)*output_scaling;
02016 s = d;
02017 switch (method)
02018 {
02019 case AffineDistortion:
02020 {
02021 s.x=coeff[0]*d.x+coeff[1]*d.y+coeff[2];
02022 s.y=coeff[3]*d.x+coeff[4]*d.y+coeff[5];
02023
02024 break;
02025 }
02026 case PerspectiveDistortion:
02027 {
02028 double
02029 p,q,r,abs_r,abs_c6,abs_c7,scale;
02030
02031 p=coeff[0]*d.x+coeff[1]*d.y+coeff[2];
02032 q=coeff[3]*d.x+coeff[4]*d.y+coeff[5];
02033 r=coeff[6]*d.x+coeff[7]*d.y+1.0;
02034
02035 validity = (r*coeff[8] < 0.0) ? 0.0 : 1.0;
02036
02037 abs_r = fabs(r)*2;
02038 abs_c6 = fabs(coeff[6]);
02039 abs_c7 = fabs(coeff[7]);
02040 if ( abs_c6 > abs_c7 ) {
02041 if ( abs_r < abs_c6 )
02042 validity = 0.5 - coeff[8]*r/coeff[6];
02043 }
02044 else if ( abs_r < abs_c7 )
02045 validity = 0.5 - coeff[8]*r/coeff[7];
02046
02047 if ( validity > 0.0 ) {
02048
02049 scale = 1.0/r;
02050 s.x = p*scale;
02051 s.y = q*scale;
02052
02053 scale *= scale;
02054 ScaleFilter( resample_filter[id],
02055 (r*coeff[0] - p*coeff[6])*scale,
02056 (r*coeff[1] - p*coeff[7])*scale,
02057 (r*coeff[3] - q*coeff[6])*scale,
02058 (r*coeff[4] - q*coeff[7])*scale );
02059 }
02060 break;
02061 }
02062 case BilinearReverseDistortion:
02063 {
02064
02065 s.x=coeff[0]*d.x+coeff[1]*d.y+coeff[2]*d.x*d.y+coeff[3];
02066 s.y=coeff[4]*d.x+coeff[5]*d.y
02067 +coeff[6]*d.x*d.y+coeff[7];
02068
02069 ScaleFilter( resample_filter[id],
02070 coeff[0] + coeff[2]*d.y,
02071 coeff[1] + coeff[2]*d.x,
02072 coeff[4] + coeff[6]*d.y,
02073 coeff[5] + coeff[6]*d.x );
02074 break;
02075 }
02076 case BilinearForwardDistortion:
02077 {
02078
02079
02080 double b,c;
02081 d.x -= coeff[3]; d.y -= coeff[7];
02082 b = coeff[6]*d.x - coeff[2]*d.y + coeff[8];
02083 c = coeff[4]*d.x - coeff[0]*d.y;
02084
02085 validity = 1.0;
02086
02087 if ( fabs(coeff[9]) < MagickEpsilon )
02088 s.y = -c/b;
02089 else {
02090 c = b*b - 2*coeff[9]*c;
02091 if ( c < 0.0 )
02092 validity = 0.0;
02093 else
02094 s.y = ( -b + sqrt(c) )/coeff[9];
02095 }
02096 if ( validity > 0.0 )
02097 s.x = ( d.x - coeff[1]*s.y) / ( coeff[0] + coeff[2]*s.y );
02098
02099
02100
02101
02102
02103
02104 break;
02105 }
02106 #if 0
02107 case QuadrilateralDistortion:
02108
02109
02110 break;
02111 #endif
02112 case PolynomialDistortion:
02113 {
02114
02115 register long
02116 k;
02117 long
02118 nterms=(long)coeff[1];
02119
02120 PointInfo
02121 du,dv;
02122
02123 s.x=s.y=du.x=du.y=dv.x=dv.y=0.0;
02124 for(k=0; k < nterms; k++) {
02125 s.x += poly_basis_fn(k,d.x,d.y)*coeff[2+k];
02126 du.x += poly_basis_dx(k,d.x,d.y)*coeff[2+k];
02127 du.y += poly_basis_dy(k,d.x,d.y)*coeff[2+k];
02128 s.y += poly_basis_fn(k,d.x,d.y)*coeff[2+k+nterms];
02129 dv.x += poly_basis_dx(k,d.x,d.y)*coeff[2+k+nterms];
02130 dv.y += poly_basis_dy(k,d.x,d.y)*coeff[2+k+nterms];
02131 }
02132 ScaleFilter( resample_filter[id], du.x,du.y,dv.x,dv.y );
02133 break;
02134 }
02135 case ArcDistortion:
02136 {
02137
02138 s.x = (atan2(d.y,d.x) - coeff[0])/Magick2PI;
02139 s.x -= MagickRound(s.x);
02140 s.y = hypot(d.x,d.y);
02141
02142
02143
02144
02145
02146
02147 if ( s.y > MagickEpsilon )
02148 ScaleFilter( resample_filter[id],
02149 coeff[1]/(Magick2PI*s.y), 0, 0, coeff[3] );
02150 else
02151 ScaleFilter( resample_filter[id],
02152 distort_image->columns*2, 0, 0, coeff[3] );
02153
02154
02155 s.x = s.x*coeff[1] + coeff[4] + image->page.x +0.5;
02156 s.y = (coeff[2] - s.y) * coeff[3] + image->page.y;
02157 break;
02158 }
02159 case PolarDistortion:
02160 {
02161 d.x -= coeff[2];
02162 d.y -= coeff[3];
02163 s.x = atan2(d.x,d.y) - (coeff[4]+coeff[5])/2;
02164 s.x /= Magick2PI;
02165 s.x -= MagickRound(s.x);
02166 s.x *= Magick2PI;
02167 s.y = hypot(d.x,d.y);
02168
02169
02170
02171
02172 if ( s.y > MagickEpsilon )
02173 ScaleFilter( resample_filter[id],
02174 coeff[6]/(Magick2PI*s.y), 0, 0, coeff[7] );
02175 else
02176 ScaleFilter( resample_filter[id],
02177 distort_image->columns*2, 0, 0, coeff[7] );
02178
02179
02180 s.x = s.x*coeff[6] + (double)image->columns/2.0 + image->page.x;
02181 s.y = (s.y-coeff[1])*coeff[7] + image->page.y;
02182 break;
02183 }
02184 case DePolarDistortion:
02185 {
02186
02187 d.x = ((double)i+0.5)*output_scaling*coeff[6]-coeff[4];
02188 d.y = ((double)j+0.5)*output_scaling*coeff[7]+coeff[1];
02189 s.x = d.y*sin(d.x) + coeff[2];
02190 s.y = d.y*cos(d.x) + coeff[3];
02191
02192 break;
02193 }
02194 case BarrelDistortion:
02195 case BarrelInverseDistortion:
02196 {
02197 double r,fx,fy,gx,gy;
02198
02199 d.x -= coeff[8];
02200 d.y -= coeff[9];
02201 r = sqrt(d.x*d.x+d.y*d.y);
02202 if ( r > MagickEpsilon ) {
02203 fx = ((coeff[0]*r + coeff[1])*r + coeff[2])*r + coeff[3];
02204 fy = ((coeff[4]*r + coeff[5])*r + coeff[6])*r + coeff[7];
02205 gx = ((3*coeff[0]*r + 2*coeff[1])*r + coeff[2])/r;
02206 gy = ((3*coeff[4]*r + 2*coeff[5])*r + coeff[6])/r;
02207
02208 if ( method == BarrelInverseDistortion ) {
02209 fx = 1/fx; fy = 1/fy;
02210 gx *= -fx*fx; gy *= -fy*fy;
02211 }
02212
02213 s.x = d.x*fx + coeff[8];
02214 s.y = d.y*fy + coeff[9];
02215 ScaleFilter( resample_filter[id],
02216 gx*d.x*d.x + fx, gx*d.x*d.y,
02217 gy*d.x*d.y, gy*d.y*d.y + fy );
02218 }
02219 else {
02220 s.x=s.y=0.0;
02221 if ( method == BarrelDistortion )
02222 ScaleFilter( resample_filter[id],
02223 coeff[3], 0, 0, coeff[7] );
02224 else
02225
02226 ScaleFilter( resample_filter[id],
02227 1.0/coeff[3], 0, 0, 1.0/coeff[7] );
02228 }
02229 break;
02230 }
02231 case ShepardsDistortion:
02232 {
02233
02234
02235
02236
02237
02238 unsigned long
02239 i;
02240 double
02241 denominator;
02242
02243 denominator = s.x = s.y = 0;
02244 for(i=0; i<number_arguments; i+=4) {
02245 double weight =
02246 ((double)d.x-arguments[i+2])*((double)d.x-arguments[i+2])
02247 + ((double)d.y-arguments[i+3])*((double)d.y-arguments[i+3]);
02248 if ( weight != 0 )
02249 weight = 1/weight;
02250 else
02251 weight = 1;
02252
02253 s.x += (arguments[ i ]-arguments[i+2])*weight;
02254 s.y += (arguments[i+1]-arguments[i+3])*weight;
02255 denominator += weight;
02256 }
02257 s.x /= denominator;
02258 s.y /= denominator;
02259 s.x += d.x;
02260 s.y += d.y;
02261
02262
02263
02264 break;
02265 }
02266 default:
02267 break;
02268 }
02269
02270 if ( bestfit && method != ArcDistortion ) {
02271 s.x -= image->page.x;
02272 s.y -= image->page.y;
02273 }
02274 s.x -= 0.5;
02275 s.y -= 0.5;
02276
02277 if ( validity <= 0.0 ) {
02278
02279 SetPixelPacket(distort_image,&invalid,q,indexes);
02280 }
02281 else {
02282
02283 (void) ResamplePixelColor(resample_filter[id],s.x,s.y,&pixel);
02284
02285 if ( validity < 1.0 ) {
02286
02287
02288 MagickPixelCompositeBlend(&pixel,validity,&invalid,(1.0-validity),
02289 &pixel);
02290 }
02291 SetPixelPacket(distort_image,&pixel,q,indexes);
02292 }
02293 q++;
02294 indexes++;
02295 }
02296 sync=SyncCacheViewAuthenticPixels(distort_view,exception);
02297 if (sync == MagickFalse)
02298 status=MagickFalse;
02299 if (image->progress_monitor != (MagickProgressMonitor) NULL)
02300 {
02301 MagickBooleanType
02302 proceed;
02303
02304 #if defined(MAGICKCORE_OPENMP_SUPPORT)
02305 #pragma omp critical (MagickCore_DistortImage)
02306 #endif
02307 proceed=SetImageProgress(image,DistortImageTag,progress++,
02308 image->rows);
02309 if (proceed == MagickFalse)
02310 status=MagickFalse;
02311 }
02312 #if 0
02313 fprintf(stderr, "\n");
02314 #endif
02315 }
02316 distort_view=DestroyCacheView(distort_view);
02317 resample_filter=DestroyResampleFilterThreadSet(resample_filter);
02318
02319 if (status == MagickFalse)
02320 distort_image=DestroyImage(distort_image);
02321 }
02322
02323
02324
02325
02326 if ( method == ArcDistortion && !bestfit && !viewport_given ) {
02327 distort_image->page.x = 0;
02328 distort_image->page.y = 0;
02329 }
02330 coeff = (double *) RelinquishMagickMemory(coeff);
02331 return(distort_image);
02332 }
02333
02334
02335
02336
02337
02338
02339
02340
02341
02342
02343
02344
02345
02346
02347
02348
02349
02350
02351
02352
02353
02354
02355
02356
02357
02358
02359
02360
02361
02362
02363
02364
02365
02366
02367
02368
02369
02370
02371
02372
02373
02374
02375
02376 MagickExport Image *SparseColorImage(const Image *image,
02377 const ChannelType channel,SparseColorMethod method,
02378 const unsigned long number_arguments,const double *arguments,
02379 ExceptionInfo *exception)
02380 {
02381 #define SparseColorTag "Distort/SparseColor"
02382
02383 DistortImageMethod
02384 *distort_method;
02385
02386 double
02387 *coeff;
02388
02389 Image
02390 *sparse_image;
02391
02392 MagickPixelPacket
02393 zero;
02394
02395 unsigned long
02396 number_colors;
02397
02398 assert(image != (Image *) NULL);
02399 assert(image->signature == MagickSignature);
02400 if (image->debug != MagickFalse)
02401 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
02402 assert(exception != (ExceptionInfo *) NULL);
02403 assert(exception->signature == MagickSignature);
02404
02405
02406 number_colors=0;
02407 if ( channel & RedChannel ) number_colors++;
02408 if ( channel & GreenChannel ) number_colors++;
02409 if ( channel & BlueChannel ) number_colors++;
02410 if ( channel & IndexChannel ) number_colors++;
02411 if ( channel & OpacityChannel ) number_colors++;
02412
02413
02414
02415
02416
02417 distort_method=(DistortImageMethod *) &method;
02418 coeff = GenerateCoefficients(image, distort_method, number_arguments,
02419 arguments, number_colors, exception);
02420 if ( coeff == (double *) NULL )
02421 return((Image *) NULL);
02422
02423
02424 if ( GetImageArtifact(image,"verbose") != (const char *) NULL ) {
02425
02426 switch (method) {
02427 case BarycentricColorInterpolate:
02428 {
02429 register long x=0;
02430 fprintf(stderr, "Barycentric Sparse Color:\n");
02431 if ( channel & RedChannel )
02432 fprintf(stderr, " -channel R -fx '%+lf*i %+lf*j %+lf' \\\n",
02433 coeff[x], coeff[x+1], coeff[x+2]),x+=3;
02434 if ( channel & GreenChannel )
02435 fprintf(stderr, " -channel G -fx '%+lf*i %+lf*j %+lf' \\\n",
02436 coeff[x], coeff[x+1], coeff[x+2]),x+=3;
02437 if ( channel & BlueChannel )
02438 fprintf(stderr, " -channel B -fx '%+lf*i %+lf*j %+lf' \\\n",
02439 coeff[x], coeff[x+1], coeff[x+2]),x+=3;
02440 if ( channel & IndexChannel )
02441 fprintf(stderr, " -channel K -fx '%+lf*i %+lf*j %+lf' \\\n",
02442 coeff[x], coeff[x+1], coeff[x+2]),x+=3;
02443 if ( channel & OpacityChannel )
02444 fprintf(stderr, " -channel A -fx '%+lf*i %+lf*j %+lf' \\\n",
02445 coeff[x], coeff[x+1], coeff[x+2]),x+=3;
02446 break;
02447 }
02448 case BilinearColorInterpolate:
02449 {
02450 register long x=0;
02451 fprintf(stderr, "Bilinear Sparse Color\n");
02452 if ( channel & RedChannel )
02453 fprintf(stderr, " -channel R -fx '%+lf*i %+lf*j %+lf*i*j %+lf;\n",
02454 coeff[ x ], coeff[x+1],
02455 coeff[x+2], coeff[x+3]),x+=4;
02456 if ( channel & GreenChannel )
02457 fprintf(stderr, " -channel G -fx '%+lf*i %+lf*j %+lf*i*j %+lf;\n",
02458 coeff[ x ], coeff[x+1],
02459 coeff[x+2], coeff[x+3]),x+=4;
02460 if ( channel & BlueChannel )
02461 fprintf(stderr, " -channel B -fx '%+lf*i %+lf*j %+lf*i*j %+lf;\n",
02462 coeff[ x ], coeff[x+1],
02463 coeff[x+2], coeff[x+3]),x+=4;
02464 if ( channel & IndexChannel )
02465 fprintf(stderr, " -channel K -fx '%+lf*i %+lf*j %+lf*i*j %+lf;\n",
02466 coeff[ x ], coeff[x+1],
02467 coeff[x+2], coeff[x+3]),x+=4;
02468 if ( channel & OpacityChannel )
02469 fprintf(stderr, " -channel A -fx '%+lf*i %+lf*j %+lf*i*j %+lf;\n",
02470 coeff[ x ], coeff[x+1],
02471 coeff[x+2], coeff[x+3]),x+=4;
02472 break;
02473 }
02474 default:
02475
02476 break;
02477 }
02478 }
02479
02480
02481
02482
02483
02484
02485
02486 sparse_image=CloneImage(image,image->columns,image->rows,MagickTrue,
02487 exception);
02488 if (sparse_image == (Image *) NULL)
02489 return((Image *) NULL);
02490 if (SetImageStorageClass(sparse_image,DirectClass) == MagickFalse)
02491 {
02492 InheritException(exception,&image->exception);
02493 sparse_image=DestroyImage(sparse_image);
02494 return((Image *) NULL);
02495 }
02496 {
02497 long
02498 j,
02499 progress;
02500
02501 MagickBooleanType
02502 status;
02503
02504 CacheView
02505 *sparse_view;
02506
02507 status=MagickTrue;
02508 progress=0;
02509 GetMagickPixelPacket(sparse_image,&zero);
02510 sparse_view=AcquireCacheView(sparse_image);
02511 #if defined(MAGICKCORE_OPENMP_SUPPORT)
02512 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
02513 #endif
02514 for (j=0; j < (long) sparse_image->rows; j++)
02515 {
02516 MagickBooleanType
02517 sync;
02518
02519 MagickPixelPacket
02520 pixel;
02521
02522 register IndexPacket
02523 *__restrict indexes;
02524
02525 register long
02526 i;
02527
02528 register PixelPacket
02529 *__restrict q;
02530
02531 q=QueueCacheViewAuthenticPixels(sparse_view,0,j,sparse_image->columns,
02532 1,exception);
02533 if (q == (PixelPacket *) NULL)
02534 {
02535 status=MagickFalse;
02536 continue;
02537 }
02538
02539 indexes=GetCacheViewAuthenticIndexQueue(sparse_view);
02540 pixel=zero;
02541 for (i=0; i < (long) sparse_image->columns; i++)
02542 {
02543 switch (method)
02544 {
02545 case BarycentricColorInterpolate:
02546 {
02547 register long x=0;
02548 if ( channel & RedChannel )
02549 pixel.red = coeff[x]*i +coeff[x+1]*j
02550 +coeff[x+2], x+=3;
02551 if ( channel & GreenChannel )
02552 pixel.green = coeff[x]*i +coeff[x+1]*j
02553 +coeff[x+2], x+=3;
02554 if ( channel & BlueChannel )
02555 pixel.blue = coeff[x]*i +coeff[x+1]*j
02556 +coeff[x+2], x+=3;
02557 if ( channel & IndexChannel )
02558 pixel.index = coeff[x]*i +coeff[x+1]*j
02559 +coeff[x+2], x+=3;
02560 if ( channel & OpacityChannel )
02561 pixel.opacity = coeff[x]*i +coeff[x+1]*j
02562 +coeff[x+2], x+=3;
02563 break;
02564 }
02565 case BilinearColorInterpolate:
02566 {
02567 register long x=0;
02568 if ( channel & RedChannel )
02569 pixel.red = coeff[x]*i + coeff[x+1]*j +
02570 coeff[x+2]*i*j + coeff[x+3], x+=4;
02571 if ( channel & GreenChannel )
02572 pixel.green = coeff[x]*i + coeff[x+1]*j +
02573 coeff[x+2]*i*j + coeff[x+3], x+=4;
02574 if ( channel & BlueChannel )
02575 pixel.blue = coeff[x]*i + coeff[x+1]*j +
02576 coeff[x+2]*i*j + coeff[x+3], x+=4;
02577 if ( channel & IndexChannel )
02578 pixel.index = coeff[x]*i + coeff[x+1]*j +
02579 coeff[x+2]*i*j + coeff[x+3], x+=4;
02580 if ( channel & OpacityChannel )
02581 pixel.opacity = coeff[x]*i + coeff[x+1]*j +
02582 coeff[x+2]*i*j + coeff[x+3], x+=4;
02583 break;
02584 }
02585 case ShepardsColorInterpolate:
02586 {
02587
02588 unsigned long
02589 k;
02590 double
02591 denominator;
02592
02593 if ( channel & RedChannel ) pixel.red = 0.0;
02594 if ( channel & GreenChannel ) pixel.green = 0.0;
02595 if ( channel & BlueChannel ) pixel.blue = 0.0;
02596 if ( channel & IndexChannel ) pixel.index = 0.0;
02597 if ( channel & OpacityChannel ) pixel.opacity = 0.0;
02598 denominator = 0.0;
02599 for(k=0; k<number_arguments; k+=2+number_colors) {
02600 register long x=(long) k+2;
02601 double weight =
02602 ((double)i-arguments[ k ])*((double)i-arguments[ k ])
02603 + ((double)j-arguments[k+1])*((double)j-arguments[k+1]);
02604 if ( weight != 0 )
02605 weight = 1/weight;
02606 else
02607 weight = 1;
02608 if ( channel & RedChannel )
02609 pixel.red += arguments[x++]*weight;
02610 if ( channel & GreenChannel )
02611 pixel.green += arguments[x++]*weight;
02612 if ( channel & BlueChannel )
02613 pixel.blue += arguments[x++]*weight;
02614 if ( channel & IndexChannel )
02615 pixel.index += arguments[x++]*weight;
02616 if ( channel & OpacityChannel )
02617 pixel.opacity += arguments[x++]*weight;
02618 denominator += weight;
02619 }
02620 if ( channel & RedChannel ) pixel.red /= denominator;
02621 if ( channel & GreenChannel ) pixel.green /= denominator;
02622 if ( channel & BlueChannel ) pixel.blue /= denominator;
02623 if ( channel & IndexChannel ) pixel.index /= denominator;
02624 if ( channel & OpacityChannel ) pixel.opacity /= denominator;
02625 break;
02626 }
02627 case VoronoiColorInterpolate:
02628 default:
02629 {
02630 unsigned long
02631 k;
02632 double
02633 minimum = MagickHuge;
02634
02635 for(k=0; k<number_arguments; k+=2+number_colors) {
02636 double distance =
02637 ((double)i-arguments[ k ])*((double)i-arguments[ k ])
02638 + ((double)j-arguments[k+1])*((double)j-arguments[k+1]);
02639 if ( distance < minimum ) {
02640 register long x=(long) k+2;
02641 if ( channel & RedChannel ) pixel.red = arguments[x++];
02642 if ( channel & GreenChannel ) pixel.green = arguments[x++];
02643 if ( channel & BlueChannel ) pixel.blue = arguments[x++];
02644 if ( channel & IndexChannel ) pixel.index = arguments[x++];
02645 if ( channel & OpacityChannel ) pixel.opacity = arguments[x++];
02646 minimum = distance;
02647 }
02648 }
02649 break;
02650 }
02651 }
02652
02653 if ( channel & RedChannel ) pixel.red *= QuantumRange;
02654 if ( channel & GreenChannel ) pixel.green *= QuantumRange;
02655 if ( channel & BlueChannel ) pixel.blue *= QuantumRange;
02656 if ( channel & IndexChannel ) pixel.index *= QuantumRange;
02657 if ( channel & OpacityChannel ) pixel.opacity *= QuantumRange;
02658 SetPixelPacket(sparse_image,&pixel,q,indexes);
02659 q++;
02660 indexes++;
02661 }
02662 sync=SyncCacheViewAuthenticPixels(sparse_view,exception);
02663 if (sync == MagickFalse)
02664 status=MagickFalse;
02665 if (image->progress_monitor != (MagickProgressMonitor) NULL)
02666 {
02667 MagickBooleanType
02668 proceed;
02669
02670 #if defined(MAGICKCORE_OPENMP_SUPPORT)
02671 #pragma omp critical (MagickCore_SparseColorImage)
02672 #endif
02673 proceed=SetImageProgress(image,SparseColorTag,progress++,image->rows);
02674 if (proceed == MagickFalse)
02675 status=MagickFalse;
02676 }
02677 }
02678 sparse_view=DestroyCacheView(sparse_view);
02679 if (status == MagickFalse)
02680 sparse_image=DestroyImage(sparse_image);
02681 }
02682 coeff = (double *) RelinquishMagickMemory(coeff);
02683 return(sparse_image);
02684 }