43#include "MagickCore/studio.h"
44#include "MagickCore/artifact.h"
45#include "MagickCore/color-private.h"
46#include "MagickCore/cache.h"
47#include "MagickCore/draw.h"
48#include "MagickCore/exception-private.h"
49#include "MagickCore/gem.h"
50#include "MagickCore/image.h"
51#include "MagickCore/image-private.h"
52#include "MagickCore/log.h"
53#include "MagickCore/magick.h"
54#include "MagickCore/memory_.h"
55#include "MagickCore/memory-private.h"
56#include "MagickCore/pixel.h"
57#include "MagickCore/pixel-accessor.h"
58#include "MagickCore/quantum.h"
59#include "MagickCore/random_.h"
60#include "MagickCore/resample.h"
61#include "MagickCore/resize.h"
62#include "MagickCore/resize-private.h"
63#include "MagickCore/resource_.h"
64#include "MagickCore/token.h"
65#include "MagickCore/transform.h"
66#include "MagickCore/signature-private.h"
67#include "MagickCore/utility.h"
68#include "MagickCore/utility-private.h"
69#include "MagickCore/option.h"
82#define DEBUG_ELLIPSE 0
83#define DEBUG_HIT_MISS 0
84#define DEBUG_NO_PIXEL_HIT 0
87#define WLUT_WIDTH 1024
111 PixelInterpolateMethod
132 Vlimit, Ulimit, Uwidth, slope;
137 filter_lut[WLUT_WIDTH];
214 assert(image != (
Image *) NULL);
215 assert(image->signature == MagickCoreSignature);
217 assert(exception->signature == MagickCoreSignature);
218 if (IsEventLogging() != MagickFalse)
219 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
222 (void) memset(resample_filter,0,
sizeof(*resample_filter));
223 resample_filter->exception=exception;
224 resample_filter->image=ReferenceImage((
Image *) image);
225 resample_filter->view=AcquireVirtualCacheView(resample_filter->image,
227 resample_filter->debug=IsEventLogging();
228 resample_filter->image_area=(ssize_t) (image->columns*image->rows);
229 resample_filter->average_defined=MagickFalse;
230 resample_filter->signature=MagickCoreSignature;
231 SetResampleFilter(resample_filter,image->filter);
232 (void) SetResampleFilterInterpolateMethod(resample_filter,image->interpolate);
233 (void) SetResampleFilterVirtualPixelMethod(resample_filter,
234 GetImageVirtualPixelMethod(image));
235 return(resample_filter);
266 assert(resample_filter->signature == MagickCoreSignature);
267 assert(resample_filter->image != (
Image *) NULL);
268 if (IsEventLogging() != MagickFalse)
269 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",
270 resample_filter->image->filename);
271 resample_filter->view=DestroyCacheView(resample_filter->view);
272 resample_filter->image=DestroyImage(resample_filter->image);
274 resample_filter->filter_def=DestroyResizeFilter(resample_filter->filter_def);
276 resample_filter->signature=(~MagickCoreSignature);
277 resample_filter=(
ResampleFilter *) RelinquishMagickMemory(resample_filter);
278 return(resample_filter);
315MagickExport MagickBooleanType ResamplePixelColor(
322 ssize_t u,v, v1, v2, uw, hit;
325 double divisor_c,divisor_m;
327 const Quantum *pixels;
329 assert(resample_filter->signature == MagickCoreSignature);
333 if ( resample_filter->do_interpolate ) {
334 status=InterpolatePixelInfo(resample_filter->image,resample_filter->view,
335 resample_filter->interpolate,u0,v0,pixel,resample_filter->exception);
340 (void) FormatLocaleFile(stderr,
"u0=%lf; v0=%lf;\n", u0, v0);
353 switch ( resample_filter->virtual_pixel ) {
354 case BackgroundVirtualPixelMethod:
355 case TransparentVirtualPixelMethod:
356 case BlackVirtualPixelMethod:
357 case GrayVirtualPixelMethod:
358 case WhiteVirtualPixelMethod:
359 case MaskVirtualPixelMethod:
360 if ( resample_filter->limit_reached
361 || u0 + resample_filter->Ulimit < 0.0
362 || u0 - resample_filter->Ulimit > (
double) resample_filter->image->columns-1.0
363 || v0 + resample_filter->Vlimit < 0.0
364 || v0 - resample_filter->Vlimit > (
double) resample_filter->image->rows-1.0
369 case UndefinedVirtualPixelMethod:
370 case EdgeVirtualPixelMethod:
371 if ( ( u0 + resample_filter->Ulimit < 0.0 && v0 + resample_filter->Vlimit < 0.0 )
372 || ( u0 + resample_filter->Ulimit < 0.0
373 && v0 - resample_filter->Vlimit > (
double) resample_filter->image->rows-1.0 )
374 || ( u0 - resample_filter->Ulimit > (
double) resample_filter->image->columns-1.0
375 && v0 + resample_filter->Vlimit < 0.0 )
376 || ( u0 - resample_filter->Ulimit > (
double) resample_filter->image->columns-1.0
377 && v0 - resample_filter->Vlimit > (
double) resample_filter->image->rows-1.0 )
381 case HorizontalTileVirtualPixelMethod:
382 if ( v0 + resample_filter->Vlimit < 0.0
383 || v0 - resample_filter->Vlimit > (
double) resample_filter->image->rows-1.0
387 case VerticalTileVirtualPixelMethod:
388 if ( u0 + resample_filter->Ulimit < 0.0
389 || u0 - resample_filter->Ulimit > (
double) resample_filter->image->columns-1.0
393 case DitherVirtualPixelMethod:
394 if ( ( u0 + resample_filter->Ulimit < -32.0 && v0 + resample_filter->Vlimit < -32.0 )
395 || ( u0 + resample_filter->Ulimit < -32.0
396 && v0 - resample_filter->Vlimit > (
double) resample_filter->image->rows+31.0 )
397 || ( u0 - resample_filter->Ulimit > (
double) resample_filter->image->columns+31.0
398 && v0 + resample_filter->Vlimit < -32.0 )
399 || ( u0 - resample_filter->Ulimit > (
double) resample_filter->image->columns+31.0
400 && v0 - resample_filter->Vlimit > (
double) resample_filter->image->rows+31.0 )
404 case TileVirtualPixelMethod:
405 case MirrorVirtualPixelMethod:
406 case RandomVirtualPixelMethod:
407 case HorizontalTileEdgeVirtualPixelMethod:
408 case VerticalTileEdgeVirtualPixelMethod:
409 case CheckerTileVirtualPixelMethod:
419 status=InterpolatePixelInfo(resample_filter->image,resample_filter->view,
420 IntegerInterpolatePixel,u0,v0,pixel,resample_filter->exception);
427 if ( resample_filter->limit_reached ) {
428 switch ( resample_filter->virtual_pixel ) {
437 case UndefinedVirtualPixelMethod:
438 case EdgeVirtualPixelMethod:
439 case DitherVirtualPixelMethod:
440 case HorizontalTileEdgeVirtualPixelMethod:
441 case VerticalTileEdgeVirtualPixelMethod:
448 status=InterpolatePixelInfo(resample_filter->image,
449 resample_filter->view,AverageInterpolatePixel,u0,v0,pixel,
450 resample_filter->exception);
452 case HorizontalTileVirtualPixelMethod:
453 case VerticalTileVirtualPixelMethod:
455 status=InterpolatePixelInfo(resample_filter->image,
456 resample_filter->view,IntegerInterpolatePixel,-1.0,-1.0,pixel,
457 resample_filter->exception);
459 case TileVirtualPixelMethod:
460 case MirrorVirtualPixelMethod:
461 case RandomVirtualPixelMethod:
462 case CheckerTileVirtualPixelMethod:
465 if ( resample_filter->average_defined == MagickFalse ) {
472 GetPixelInfo(resample_filter->image,(
PixelInfo *)
473 &resample_filter->average_pixel);
474 resample_filter->average_defined=MagickTrue;
477 average_image=ResizeImage(resample_filter->image,1,1,BoxFilter,
478 resample_filter->exception);
479 if (average_image == (
Image *) NULL)
481 *pixel=resample_filter->average_pixel;
484 average_view=AcquireVirtualCacheView(average_image,exception);
485 pixels=GetCacheViewVirtualPixels(average_view,0,0,1,1,
486 resample_filter->exception);
487 if (pixels == (
const Quantum *) NULL) {
488 average_view=DestroyCacheView(average_view);
489 average_image=DestroyImage(average_image);
490 *pixel=resample_filter->average_pixel;
493 GetPixelInfoPixel(resample_filter->image,pixels,
494 &(resample_filter->average_pixel));
495 average_view=DestroyCacheView(average_view);
496 average_image=DestroyImage(average_image);
498 if ( resample_filter->virtual_pixel == CheckerTileVirtualPixelMethod )
504 weight = QuantumScale*((double)
505 resample_filter->average_pixel.alpha);
506 resample_filter->average_pixel.red *= weight;
507 resample_filter->average_pixel.green *= weight;
508 resample_filter->average_pixel.blue *= weight;
512 weight = QuantumScale*((double)
513 resample_filter->image->background_color.alpha);
514 resample_filter->average_pixel.red +=
515 weight*resample_filter->image->background_color.red;
516 resample_filter->average_pixel.green +=
517 weight*resample_filter->image->background_color.green;
518 resample_filter->average_pixel.blue +=
519 weight*resample_filter->image->background_color.blue;
520 resample_filter->average_pixel.alpha +=
521 resample_filter->image->background_color.alpha;
525 resample_filter->average_pixel.red /= divisor_c;
526 resample_filter->average_pixel.green /= divisor_c;
527 resample_filter->average_pixel.blue /= divisor_c;
528 resample_filter->average_pixel.alpha /= 2;
532 *pixel=resample_filter->average_pixel;
544 pixel->red = pixel->green = pixel->blue = 0.0;
545 if (pixel->colorspace == CMYKColorspace)
547 if (pixel->alpha_trait != UndefinedPixelTrait)
554 v1 = (ssize_t)ceil(v0 - resample_filter->Vlimit);
555 v2 = (ssize_t)floor(v0 + resample_filter->Vlimit);
558 u1 = u0 + (v1-v0)*resample_filter->slope - resample_filter->Uwidth;
559 uw = (ssize_t)(2.0*resample_filter->Uwidth)+1;
562 (void) FormatLocaleFile(stderr,
"v1=%ld; v2=%ld\n", (
long)v1, (
long)v2);
563 (void) FormatLocaleFile(stderr,
"u1=%ld; uw=%ld\n", (
long)u1, (
long)uw);
565# define DEBUG_HIT_MISS 0
572 DDQ = 2*resample_filter->A;
573 for( v=v1; v<=v2; v++ ) {
576 (void) FormatLocaleFile(stderr,
"# scan line from pixel %ld, %ld\n", (
long)uu, (
long)v);
578 u = (ssize_t)ceil(u1);
579 u1 += resample_filter->slope;
587 Q = (resample_filter->A*U + resample_filter->B*V)*U + resample_filter->C*V*V;
588 DQ = resample_filter->A*(2.0*U+1) + resample_filter->B*V;
591 pixels=GetCacheViewVirtualPixels(resample_filter->view,u,v,(
size_t) uw,
592 1,resample_filter->exception);
593 if (pixels == (
const Quantum *) NULL)
597 for( u=0; u<uw; u++ ) {
601 if (((
int) Q >= 0) && ((
int) Q < WLUT_WIDTH)) {
602 weight = resample_filter->filter_lut[(int) Q];
605 if ((Q >= 0.0) && (Q < resample_filter->F)) {
606 weight = GetResizeFilterWeight(resample_filter->filter_def,
610 pixel->alpha+=weight*(double)
611 GetPixelAlpha(resample_filter->image,pixels);
614 if (pixel->alpha_trait != UndefinedPixelTrait)
615 weight*=QuantumScale*((double)
616 GetPixelAlpha(resample_filter->image,pixels));
617 pixel->red+=weight*(double)
618 GetPixelRed(resample_filter->image,pixels);
619 pixel->green+=weight*(double)
620 GetPixelGreen(resample_filter->image,pixels);
621 pixel->blue+=weight*(double)
622 GetPixelBlue(resample_filter->image,pixels);
623 if (pixel->colorspace == CMYKColorspace)
624 pixel->black+=weight*(double)
625 GetPixelBlack(resample_filter->image,pixels);
631 (void) FormatLocaleFile(stderr,
"set arrow from %lf,%lf to %lf,%lf nohead ls 3\n",
632 (
long)uu-.1,(
double)v-.1,(
long)uu+.1,(
long)v+.1);
633 (void) FormatLocaleFile(stderr,
"set arrow from %lf,%lf to %lf,%lf nohead ls 3\n",
634 (
long)uu+.1,(
double)v-.1,(
long)uu-.1,(
long)v+.1);
636 (void) FormatLocaleFile(stderr,
"set arrow from %lf,%lf to %lf,%lf nohead ls 1\n",
637 (
long)uu-.1,(
double)v-.1,(
long)uu+.1,(
long)v+.1);
638 (void) FormatLocaleFile(stderr,
"set arrow from %lf,%lf to %lf,%lf nohead ls 1\n",
639 (
long)uu+.1,(
double)v-.1,(
long)uu-.1,(
long)v+.1);
645 pixels+=GetPixelChannels(resample_filter->image);
651 (void) FormatLocaleFile(stderr,
"Hit=%ld; Total=%ld;\n", (
long)hit, (
long)uw*(v2-v1) );
657 if ( hit == 0 || divisor_m <= MagickEpsilon || divisor_c <= MagickEpsilon ) {
660#if DEBUG_NO_PIXEL_HIT
661 pixel->alpha = pixel->red = pixel->green = pixel->blue = 0;
662 pixel->red = QuantumRange;
664 status=InterpolatePixelInfo(resample_filter->image,
665 resample_filter->view,resample_filter->interpolate,u0,v0,pixel,
666 resample_filter->exception);
674 divisor_m = 1.0/divisor_m;
675 if (pixel->alpha_trait != UndefinedPixelTrait)
676 pixel->alpha = (double) ClampToQuantum(divisor_m*pixel->alpha);
677 divisor_c = 1.0/divisor_c;
678 pixel->red = (double) ClampToQuantum(divisor_c*pixel->red);
679 pixel->green = (double) ClampToQuantum(divisor_c*pixel->green);
680 pixel->blue = (double) ClampToQuantum(divisor_c*pixel->blue);
681 if (pixel->colorspace == CMYKColorspace)
682 pixel->black = (double) ClampToQuantum(divisor_c*pixel->black);
716static inline void ClampUpAxes(
const double dux,
722 double *major_unit_x,
723 double *major_unit_y,
724 double *minor_unit_x,
725 double *minor_unit_y)
889 const double a = dux;
890 const double b = duy;
891 const double c = dvx;
892 const double d = dvy;
897 const double aa = a*a;
898 const double bb = b*b;
899 const double cc = c*c;
900 const double dd = d*d;
904 const double n11 = aa+bb;
905 const double n12 = a*c+b*d;
906 const double n21 = n12;
907 const double n22 = cc+dd;
908 const double det = a*d-b*c;
909 const double twice_det = det+det;
910 const double frobenius_squared = n11+n22;
911 const double discriminant =
912 (frobenius_squared+twice_det)*(frobenius_squared-twice_det);
918 const double sqrt_discriminant =
919 sqrt(discriminant > 0.0 ? discriminant : 0.0);
930 const double s1s1 = 0.5*(frobenius_squared+sqrt_discriminant);
936 const double s2s2 = 0.5*(frobenius_squared-sqrt_discriminant);
937 const double s1s1minusn11 = s1s1-n11;
938 const double s1s1minusn22 = s1s1-n22;
946 const double s1s1minusn11_squared = s1s1minusn11*s1s1minusn11;
947 const double s1s1minusn22_squared = s1s1minusn22*s1s1minusn22;
957 const double temp_u11 =
958 ( (s1s1minusn11_squared>=s1s1minusn22_squared) ? n12 : s1s1minusn22 );
959 const double temp_u21 =
960 ( (s1s1minusn11_squared>=s1s1minusn22_squared) ? s1s1minusn11 : n21 );
961 const double norm = sqrt(temp_u11*temp_u11+temp_u21*temp_u21);
966 const double u11 = ( (norm>0.0) ? temp_u11/norm : 1.0 );
967 const double u21 = ( (norm>0.0) ? temp_u21/norm : 0.0 );
971 *major_mag = ( (s1s1<=1.0) ? 1.0 : sqrt(s1s1) );
972 *minor_mag = ( (s2s2<=1.0) ? 1.0 : sqrt(s2s2) );
978 *minor_unit_x = -u21;
1045MagickExport
void ScaleResampleFilter(
ResampleFilter *resample_filter,
1046 const double dux,
const double duy,
const double dvx,
const double dvy)
1051 assert(resample_filter->signature == MagickCoreSignature);
1053 resample_filter->limit_reached = MagickFalse;
1056 if ( resample_filter->filter == PointFilter )
1060 (void) FormatLocaleFile(stderr,
"# -----\n" );
1061 (void) FormatLocaleFile(stderr,
"dux=%lf; dvx=%lf; duy=%lf; dvy=%lf;\n",
1062 dux, dvx, duy, dvy);
1086 ClampUpAxes(dux,dvx,duy,dvy, &major_mag, &minor_mag,
1087 &major_x, &major_y, &minor_x, &minor_y);
1088 major_x *= major_mag; major_y *= major_mag;
1089 minor_x *= minor_mag; minor_y *= minor_mag;
1091 (void) FormatLocaleFile(stderr,
"major_x=%lf; major_y=%lf; minor_x=%lf; minor_y=%lf;\n",
1092 major_x, major_y, minor_x, minor_y);
1094 A = major_y*major_y+minor_y*minor_y;
1095 B = -2.0*(major_x*major_y+minor_x*minor_y);
1096 C = major_x*major_x+minor_x*minor_x;
1097 F = major_mag*minor_mag;
1101 A = dvx*dvx+dvy*dvy;
1102 B = -2.0*(dux*dvx+duy*dvy);
1103 C = dux*dux+duy*duy;
1104 F = dux*dvy-duy*dvx;
1123 A = dvx*dvx+dvy*dvy+1;
1124 B = -2.0*(dux*dvx+duy*dvy);
1125 C = dux*dux+duy*duy+1;
1130 (void) FormatLocaleFile(stderr,
"A=%lf; B=%lf; C=%lf; F=%lf\n", A,B,C,F);
1138 {
double alpha, beta, gamma, Major, Minor;
1139 double Eccentricity, Ellipse_Area, Ellipse_Angle;
1143 gamma = sqrt(beta*beta + B*B );
1145 if ( alpha - gamma <= MagickEpsilon )
1146 Major=MagickMaximumValue;
1148 Major=sqrt(2*F/(alpha - gamma));
1149 Minor = sqrt(2*F/(alpha + gamma));
1151 (void) FormatLocaleFile(stderr,
"# Major=%lf; Minor=%lf\n", Major, Minor );
1154 Eccentricity = Major/Minor;
1155 Ellipse_Area = MagickPI*Major*Minor;
1156 Ellipse_Angle = atan2(B, A-C);
1158 (void) FormatLocaleFile(stderr,
"# Angle=%lf Area=%lf\n",
1159 (
double) RadiansToDegrees(Ellipse_Angle), Ellipse_Area);
1170 if ( (4*A*C - B*B) > MagickMaximumValue ) {
1171 resample_filter->limit_reached = MagickTrue;
1179 F *= resample_filter->support;
1180 F *= resample_filter->support;
1183 resample_filter->Ulimit = sqrt(C*F/(A*C-0.25*B*B));
1184 resample_filter->Vlimit = sqrt(A*F/(A*C-0.25*B*B));
1187 resample_filter->Uwidth = sqrt(F/A);
1188 resample_filter->slope = -B/(2.0*A);
1191 (void) FormatLocaleFile(stderr,
"Ulimit=%lf; Vlimit=%lf; UWidth=%lf; Slope=%lf;\n",
1192 resample_filter->Ulimit, resample_filter->Vlimit,
1193 resample_filter->Uwidth, resample_filter->slope );
1200 if ( (resample_filter->Uwidth * resample_filter->Vlimit)
1201 > (4.0*resample_filter->image_area)) {
1202 resample_filter->limit_reached = MagickTrue;
1210 scale=(double) WLUT_WIDTH*PerceptibleReciprocal(F);
1213 scale=resample_filter->F*PerceptibleReciprocal(F);
1215 resample_filter->A = A*scale;
1216 resample_filter->B = B*scale;
1217 resample_filter->C = C*scale;
1248MagickExport
void SetResampleFilter(
ResampleFilter *resample_filter,
1249 const FilterType filter)
1255 assert(resample_filter->signature == MagickCoreSignature);
1257 resample_filter->do_interpolate = MagickFalse;
1258 resample_filter->filter = filter;
1261 if ( filter == UndefinedFilter )
1262 resample_filter->filter = RobidouxFilter;
1264 if ( resample_filter->filter == PointFilter ) {
1265 resample_filter->do_interpolate = MagickTrue;
1269 resize_filter = AcquireResizeFilter(resample_filter->image,
1270 resample_filter->filter,MagickTrue,resample_filter->exception);
1272 (void) ThrowMagickException(resample_filter->exception,GetMagickModule(),
1273 ModuleError,
"UnableToSetFilteringValue",
1274 "Fall back to Interpolated 'Point' filter");
1275 resample_filter->filter = PointFilter;
1276 resample_filter->do_interpolate = MagickTrue;
1284 resample_filter->support = GetResizeFilterSupport(resize_filter);
1286 resample_filter->support = 2.0;
1297 r_scale = resample_filter->support*sqrt(1.0/(
double)WLUT_WIDTH);
1298 for(Q=0; Q<WLUT_WIDTH; Q++)
1299 resample_filter->filter_lut[Q] = (
double)
1300 GetResizeFilterWeight(resize_filter,sqrt((
double)Q)*r_scale);
1303 resize_filter = DestroyResizeFilter(resize_filter);
1307 resample_filter->filter_def = resize_filter;
1308 resample_filter->F = resample_filter->support*resample_filter->support;
1316 ScaleResampleFilter(resample_filter, 1.0, 0.0, 0.0, 1.0);
1332 r_scale = -2.77258872223978123767/(WLUT_WIDTH*blur*blur);
1333 for(Q=0; Q<WLUT_WIDTH; Q++)
1334 resample_filter->filter_lut[Q] = exp((
double)Q*r_scale);
1335 resample_filter->support = WLUT_WIDTH;
1339#if defined(MAGICKCORE_OPENMP_SUPPORT)
1343 if (IsStringTrue(GetImageArtifact(resample_filter->image,
1344 "resample:verbose")) != MagickFalse)
1357 printf(
"# Resampling Filter LUT (%d values) for '%s' filter\n",
1358 WLUT_WIDTH, CommandOptionToMnemonic(MagickFilterOptions,
1359 resample_filter->filter) );
1361 printf(
"# Note: values in table are using a squared radius lookup.\n");
1362 printf(
"# As such its distribution is not uniform.\n");
1364 printf(
"# The X value is the support distance for the Y weight\n");
1365 printf(
"# so you can use gnuplot to plot this cylindrical filter\n");
1366 printf(
"# plot [0:2][-.2:1] \"lut.dat\" with lines\n");
1370 r_scale = resample_filter->support*sqrt(1.0/(
double)WLUT_WIDTH);
1371 for(Q=0; Q<WLUT_WIDTH; Q++)
1372 printf(
"%8.*g %.*g\n",
1373 GetMagickPrecision(),sqrt((
double)Q)*r_scale,
1374 GetMagickPrecision(),resample_filter->filter_lut[Q] );
1411MagickExport MagickBooleanType SetResampleFilterInterpolateMethod(
1412 ResampleFilter *resample_filter,
const PixelInterpolateMethod method)
1415 assert(resample_filter->signature == MagickCoreSignature);
1416 assert(resample_filter->image != (
Image *) NULL);
1417 if (IsEventLogging() != MagickFalse)
1418 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",
1419 resample_filter->image->filename);
1420 resample_filter->interpolate=method;
1450MagickExport MagickBooleanType SetResampleFilterVirtualPixelMethod(
1454 assert(resample_filter->signature == MagickCoreSignature);
1455 assert(resample_filter->image != (
Image *) NULL);
1456 if (IsEventLogging() != MagickFalse)
1457 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",
1458 resample_filter->image->filename);
1459 resample_filter->virtual_pixel=method;
1460 if (method != UndefinedVirtualPixelMethod)
1461 (void) SetCacheViewVirtualPixelMethod(resample_filter->view,method);