% MagickCore Image Distortion Methods %
% %
% Software Design %
-% John Cristy %
+% Cristy %
% Anthony Thyssen %
% June 2007 %
% %
% %
-% Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization %
+% Copyright 1999-2014 ImageMagick Studio LLC, a non-profit organization %
% dedicated to making software imaging solutions freely available. %
% %
% You may not use this file except in compliance with the License. You may %
#include "MagickCore/artifact.h"
#include "MagickCore/cache.h"
#include "MagickCore/cache-view.h"
+#include "MagickCore/channel.h"
#include "MagickCore/colorspace-private.h"
#include "MagickCore/composite-private.h"
#include "MagickCore/distort.h"
#include "MagickCore/option.h"
#include "MagickCore/pixel.h"
#include "MagickCore/pixel-accessor.h"
+#include "MagickCore/pixel-private.h"
#include "MagickCore/resample.h"
#include "MagickCore/resample-private.h"
#include "MagickCore/registry.h"
+#include "MagickCore/resource_.h"
#include "MagickCore/semaphore.h"
#include "MagickCore/shear.h"
#include "MagickCore/string_.h"
/* From "Digital Image Warping" by George Wolberg, page 50 */
double determinant;
- determinant=1.0/(coeff[0]*coeff[4]-coeff[1]*coeff[3]);
+ determinant=PerceptibleReciprocal(coeff[0]*coeff[4]-coeff[1]*coeff[3]);
inverse[0]=determinant*coeff[4];
inverse[1]=determinant*(-coeff[1]);
inverse[2]=determinant*(coeff[1]*coeff[5]-coeff[2]*coeff[4]);
/* From "Digital Image Warping" by George Wolberg, page 53 */
double determinant;
- determinant=1.0/(coeff[0]*coeff[4]-coeff[3]*coeff[1]);
+ determinant=PerceptibleReciprocal(coeff[0]*coeff[4]-coeff[3]*coeff[1]);
inverse[0]=determinant*(coeff[4]-coeff[7]*coeff[5]);
inverse[1]=determinant*(coeff[7]*coeff[2]-coeff[1]);
inverse[2]=determinant*(coeff[1]*coeff[5]-coeff[4]*coeff[2]);
inverse[7]=determinant*(coeff[6]*coeff[1]-coeff[0]*coeff[7]);
}
-static inline double MagickRound(double x)
-{
- /*
- Round the fraction to nearest integer.
- */
- if (x >= 0.0)
- return((double) ((ssize_t) (x+0.5)));
- return((double) ((ssize_t) (x-0.5)));
-}
-
/*
* Polynomial Term Defining Functions
*
% outside other MagickCore library methods.
*/
+static inline double MagickRound(double x)
+{
+ /*
+ Round the fraction to nearest integer.
+ */
+ if ((x-floor(x)) < (ceil(x)-x))
+ return(floor(x));
+ return(ceil(x));
+}
+
static double *GenerateCoefficients(const Image *image,
DistortImageMethod *method,const size_t number_arguments,
const double *arguments,size_t number_values,ExceptionInfo *exception)
#endif
break;
case ShepardsDistortion:
- number_coeff=1; /* not used, but provide some type of return */
+ number_coeff=1; /* The power factor to use */
break;
case ArcDistortion:
number_coeff=5;
number_coeff=10;
break;
default:
- assert(! "Unknown Method Given"); /* just fail assertion */
+ perror("unknown method given"); /* just fail assertion */
}
/* allocate the array of coefficients needed */
if ( number_arguments%cp_size != 0 ||
number_arguments < cp_size ) {
(void) ThrowMagickException(exception,GetMagickModule(),OptionError,
- "InvalidArgument", "%s : 'require at least %.20g CPs'",
- CommandOptionToMnemonic(MagickDistortOptions, *method), 1.0);
+ "InvalidArgument", "%s : 'requires CP's (4 numbers each)'",
+ CommandOptionToMnemonic(MagickDistortOptions, *method));
coeff=(double *) RelinquishMagickMemory(coeff);
return((double *) NULL);
}
+ /* User defined weighting power for Shepard's Method */
+ { const char *artifact=GetImageArtifact(image,"shepards:power");
+ if ( artifact != (const char *) NULL ) {
+ coeff[0]=StringToDouble(artifact,(char **) NULL) / 2.0;
+ if ( coeff[0] < MagickEpsilon ) {
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ OptionError,"InvalidArgument","%s", "-define shepards:power" );
+ coeff=(double *) RelinquishMagickMemory(coeff);
+ return((double *) NULL);
+ }
+ }
+ else
+ coeff[0]=1.0; /* Default power of 2 (Inverse Squared) */
+ }
return(coeff);
}
default:
break;
}
/* you should never reach this point */
- assert(! "No Method Handler"); /* just fail assertion */
+ perror("no method handler"); /* just fail assertion */
return((double *) NULL);
}
\f
return((Image *) NULL);
/* Do not short-circuit this resize if final image size is unchanged */
-
(void) ResetMagickMemory(distort_args,0,12*sizeof(double));
distort_args[4]=(double) image->columns;
distort_args[6]=(double) columns;
return((Image *) NULL);
(void) SetImageVirtualPixelMethod(tmp_image,TransparentVirtualPixelMethod,
exception);
+ tmp_image->image_info=image->image_info; /* preserve global options */
- if (image->matte == MagickFalse)
+ if (image->alpha_trait != BlendPixelTrait)
{
/*
Image has not transparency channel, so we free to use it
}
else
{
- Image
- *resize_alpha;
-
/*
Image has transparency so handle colors and alpha separatly.
Basically we need to separate Virtual-Pixel alpha in the resized
distort alpha channel separately
*/
+ Image
+ *resize_alpha;
+
(void) SetImageAlphaChannel(tmp_image,ExtractAlphaChannel,exception);
(void) SetImageAlphaChannel(tmp_image,OpaqueAlphaChannel,exception);
resize_alpha=DistortImage(tmp_image,AffineDistortion,12,distort_args,
tmp_image=CloneImage(image,0,0,MagickTrue,exception);
if ( tmp_image == (Image *) NULL )
return((Image *) NULL);
+ tmp_image->image_info=image->image_info; /* preserve global options */
(void) SetImageVirtualPixelMethod(tmp_image,
TransparentVirtualPixelMethod,exception);
resize_image=DistortImage(tmp_image,AffineDistortion,12,distort_args,
tmp_image=resize_image;
resize_image=CropImage(tmp_image,&crop_area,exception);
tmp_image=DestroyImage(tmp_image);
-
- if ( resize_image == (Image *) NULL )
- return((Image *) NULL);
-
return(resize_image);
}
\f
assert(exception != (ExceptionInfo *) NULL);
assert(exception->signature == MagickSignature);
-
/*
Handle Special Compound Distortions
*/
s.x = (double) image->page.x;
s.y = (double) image->page.y;
scale=inverse[6]*s.x+inverse[7]*s.y+1.0;
- scale=1.0/( (fabs(scale) <= MagickEpsilon) ? 1.0 : scale );
+ scale=PerceptibleReciprocal(scale);
d.x = scale*(inverse[0]*s.x+inverse[1]*s.y+inverse[2]);
d.y = scale*(inverse[3]*s.x+inverse[4]*s.y+inverse[5]);
InitalBounds(d);
s.x = (double) image->page.x+image->columns;
s.y = (double) image->page.y;
scale=inverse[6]*s.x+inverse[7]*s.y+1.0;
- scale=1.0/( (fabs(scale) <= MagickEpsilon) ? 1.0 : scale );
+ scale=PerceptibleReciprocal(scale);
d.x = scale*(inverse[0]*s.x+inverse[1]*s.y+inverse[2]);
d.y = scale*(inverse[3]*s.x+inverse[4]*s.y+inverse[5]);
ExpandBounds(d);
s.x = (double) image->page.x;
s.y = (double) image->page.y+image->rows;
scale=inverse[6]*s.x+inverse[7]*s.y+1.0;
- scale=1.0/( (fabs(scale) <= MagickEpsilon) ? 1.0 : scale );
+ scale=PerceptibleReciprocal(scale);
d.x = scale*(inverse[0]*s.x+inverse[1]*s.y+inverse[2]);
d.y = scale*(inverse[3]*s.x+inverse[4]*s.y+inverse[5]);
ExpandBounds(d);
s.x = (double) image->page.x+image->columns;
s.y = (double) image->page.y+image->rows;
scale=inverse[6]*s.x+inverse[7]*s.y+1.0;
- scale=1.0/( (fabs(scale) <= MagickEpsilon) ? 1.0 : scale );
+ scale=PerceptibleReciprocal(scale);
d.x = scale*(inverse[0]*s.x+inverse[1]*s.y+inverse[2]);
d.y = scale*(inverse[3]*s.x+inverse[4]*s.y+inverse[5]);
ExpandBounds(d);
{ const char *artifact=GetImageArtifact(image,"distort:viewport");
viewport_given = MagickFalse;
if ( artifact != (const char *) NULL ) {
- if (IfMagickFalse(IsGeometry(artifact)))
+ MagickStatusType flags=ParseAbsoluteGeometry(artifact,&geometry);
+ if (flags==NoValue)
(void) ThrowMagickException(exception,GetMagickModule(),
OptionWarning,"InvalidSetting","'%s' '%s'",
"distort:viewport",artifact);
- else {
- (void) ParseAbsoluteGeometry(artifact,&geometry);
+ else
viewport_given = MagickTrue;
- }
}
}
/* Verbose output */
- if ( IfMagickTrue(GetImageArtifact(image,"verbose")) ) {
+ if ( IfStringTrue(GetImageArtifact(image,"verbose")) ) {
register ssize_t
i;
char image_gen[MaxTextExtent];
(void) FormatLocaleFile(stderr, " c%.20g = %+lf\n", (double) i, coeff[i]);
(void) FormatLocaleFile(stderr, "DePolar Distort, FX Equivelent:\n");
(void) FormatLocaleFile(stderr, "%s", image_gen);
- (void) FormatLocaleFile(stderr, " -fx 'aa=(i+.5)*%lf %+lf;\n", coeff[6], -coeff[4] );
+ (void) FormatLocaleFile(stderr, " -fx 'aa=(i+.5)*%lf %+lf;\n", coeff[6], +coeff[4] );
(void) FormatLocaleFile(stderr, " rr=(j+.5)*%lf %+lf;\n", coeff[7], +coeff[1] );
(void) FormatLocaleFile(stderr, " xx=rr*sin(aa) %+lf;\n", coeff[2] );
(void) FormatLocaleFile(stderr, " yy=rr*cos(aa) %+lf;\n", coeff[3] );
output_scaling = 1.0;
if (artifact != (const char *) NULL) {
output_scaling = fabs(StringToDouble(artifact,(char **) NULL));
- geometry.width *= (size_t) output_scaling;
- geometry.height *= (size_t) output_scaling;
- geometry.x *= (ssize_t) output_scaling;
- geometry.y *= (ssize_t) output_scaling;
+ geometry.width=(size_t) (output_scaling*geometry.width+0.5);
+ geometry.height=(size_t) (output_scaling*geometry.height+0.5);
+ geometry.x=(ssize_t) (output_scaling*geometry.x+0.5);
+ geometry.y=(ssize_t) (output_scaling*geometry.y+0.5);
if ( output_scaling < 0.1 ) {
coeff = (double *) RelinquishMagickMemory(coeff);
(void) ThrowMagickException(exception,GetMagickModule(),OptionError,
distort_image=DestroyImage(distort_image);
return((Image *) NULL);
}
+ if ((IsPixelInfoGray(&distort_image->background_color) == MagickFalse) &&
+ (IsGrayColorspace(distort_image->colorspace) != MagickFalse))
+ (void) SetImageColorspace(distort_image,sRGBColorspace,exception);
+ if (distort_image->background_color.alpha_trait == BlendPixelTrait)
+ distort_image->alpha_trait=BlendPixelTrait;
distort_image->page.x=geometry.x;
distort_image->page.y=geometry.y;
- if (distort_image->background_color.matte != MagickFalse)
- distort_image->matte=MagickTrue;
{ /* ----- MAIN CODE -----
Sample the source image to each pixel in the distort image.
GetPixelInfo(distort_image,&zero);
resample_filter=AcquireResampleFilterThreadSet(image,
UndefinedVirtualPixelMethod,MagickFalse,exception);
- distort_view=AcquireCacheView(distort_image);
+ distort_view=AcquireAuthenticCacheView(distort_image,exception);
#if defined(MAGICKCORE_OPENMP_SUPPORT)
- #pragma omp parallel for schedule(static,4) shared(progress,status)
+ #pragma omp parallel for schedule(static,4) shared(progress,status) \
+ magick_threads(image,distort_image,distort_image->rows,1)
#endif
for (j=0; j < (ssize_t) distort_image->rows; j++)
{
case DePolarDistortion:
{ /* @D Polar to Carteasain */
/* ignore all destination virtual offsets */
- d.x = ((double)i+0.5)*output_scaling*coeff[6]-coeff[4];
+ d.x = ((double)i+0.5)*output_scaling*coeff[6]+coeff[4];
d.y = ((double)j+0.5)*output_scaling*coeff[7]+coeff[1];
s.x = d.y*sin(d.x) + coeff[2];
s.y = d.y*cos(d.x) + coeff[3];
}
case ShepardsDistortion:
{ /* Shepards Method, or Inverse Weighted Distance for
- displacement around the destination image control points
- The input arguments are the coefficents to the function.
- This is more of a 'displacement' function rather than an
- absolute distortion function.
+ displacement around the destination image control points
+ The input arguments are the coefficents to the function.
+ This is more of a 'displacement' function rather than an
+ absolute distortion function.
+
+ Note: We can not determine derivatives using shepards method
+ so only a point sample interpolatation can be used.
*/
size_t
i;
double weight =
((double)d.x-arguments[i+2])*((double)d.x-arguments[i+2])
+ ((double)d.y-arguments[i+3])*((double)d.y-arguments[i+3]);
- if ( weight != 0 )
- weight = 1/weight;
- else
- weight = 1;
+ weight = pow(weight,coeff[0]); /* shepards power factor */
+ weight = ( weight < 1.0 ) ? 1.0 : 1.0/weight;
s.x += (arguments[ i ]-arguments[i+2])*weight;
s.y += (arguments[i+1]-arguments[i+3])*weight;
}
s.x /= denominator;
s.y /= denominator;
- s.x += d.x;
+ s.x += d.x; /* make it as relative displacement */
s.y += d.y;
-
- /* We can not determine derivatives using shepards method
- only color interpolatation, not area-resampling */
break;
}
default:
}
else {
/* resample the source image to find its correct color */
- (void) ResamplePixelColor(resample_filter[id],s.x,s.y,&pixel);
+ (void) ResamplePixelColor(resample_filter[id],s.x,s.y,&pixel,
+ exception);
/* if validity between 0.0 and 1.0 mix result with invalid pixel */
if ( validity < 1.0 ) {
/* Do a blend of sample color and invalid pixel */
proceed;
#if defined(MAGICKCORE_OPENMP_SUPPORT)
- #pragma omp critical (MagickCore_DistortImage)
+ #pragma omp critical (MagickCore_DistortImage)
#endif
proceed=SetImageProgress(image,DistortImageTag,progress++,
image->rows);
*distort_image,
*rotate_image;
- MagickRealType
+ double
angle;
PointInfo
distort_image=CloneImage(image,0,0,MagickTrue,exception);
if (distort_image == (Image *) NULL)
return((Image *) NULL);
- if (IsGrayColorspace(image->colorspace) != MagickFalse)
- (void) TransformImageColorspace(distort_image,sRGBColorspace,exception);
(void) SetImageVirtualPixelMethod(distort_image,BackgroundVirtualPixelMethod,
exception);
rotate_image=DistortImage(distort_image,ScaleRotateTranslateDistortion,1,
(image->colorspace == CMYKColorspace))
number_colors++;
if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
- (image->matte != MagickFalse))
+ (image->alpha_trait == BlendPixelTrait))
number_colors++;
/*
*/
sparse_method = (SparseColorMethod) distort_method;
if ( distort_method == ShepardsDistortion )
- sparse_method = method; /* return non-distiort methods to normal */
+ sparse_method = method; /* return non-distort methods to normal */
+ if ( sparse_method == InverseColorInterpolate )
+ coeff[0]=0.5; /* sqrt() the squared distance for inverse */
}
/* Verbose output */
- if ( IfMagickTrue(GetImageArtifact(image,"verbose")) ) {
+ if ( IfStringTrue(GetImageArtifact(image,"verbose")) ) {
switch (sparse_method) {
case BarycentricColorInterpolate:
(void) FormatLocaleFile(stderr, " -channel K -fx '%+lf*i %+lf*j %+lf' \\\n",
coeff[x], coeff[x+1], coeff[x+2]),x+=3;
if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
- (image->matte != MagickFalse))
+ (image->alpha_trait == BlendPixelTrait))
(void) FormatLocaleFile(stderr, " -channel A -fx '%+lf*i %+lf*j %+lf' \\\n",
coeff[x], coeff[x+1], coeff[x+2]),x+=3;
break;
coeff[ x ], coeff[x+1],
coeff[x+2], coeff[x+3]),x+=4;
if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
- (image->matte != MagickFalse))
+ (image->alpha_trait == BlendPixelTrait))
(void) FormatLocaleFile(stderr, " -channel A -fx '%+lf*i %+lf*j %+lf*i*j %+lf;\n",
coeff[ x ], coeff[x+1],
coeff[x+2], coeff[x+3]),x+=4;
status=MagickTrue;
progress=0;
- sparse_view=AcquireCacheView(sparse_image);
+ sparse_view=AcquireAuthenticCacheView(sparse_image,exception);
#if defined(MAGICKCORE_OPENMP_SUPPORT)
- #pragma omp parallel for schedule(static,4) shared(progress,status)
+ #pragma omp parallel for schedule(static,4) shared(progress,status) \
+ magick_threads(image,sparse_image,sparse_image->rows,1)
#endif
for (j=0; j < (ssize_t) sparse_image->rows; j++)
{
pixel.black = coeff[x]*i +coeff[x+1]*j
+coeff[x+2], x+=3;
if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
- (image->matte != MagickFalse))
+ (image->alpha_trait == BlendPixelTrait))
pixel.alpha = coeff[x]*i +coeff[x+1]*j
+coeff[x+2], x+=3;
break;
pixel.black = coeff[x]*i + coeff[x+1]*j +
coeff[x+2]*i*j + coeff[x+3], x+=4;
if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
- (image->matte != MagickFalse))
+ (image->alpha_trait == BlendPixelTrait))
pixel.alpha = coeff[x]*i + coeff[x+1]*j +
coeff[x+2]*i*j + coeff[x+3], x+=4;
break;
(image->colorspace == CMYKColorspace))
pixel.black=0.0;
if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
- (image->matte != MagickFalse))
+ (image->alpha_trait == BlendPixelTrait))
pixel.alpha=0.0;
denominator = 0.0;
for(k=0; k<number_arguments; k+=2+number_colors) {
double weight =
((double)i-arguments[ k ])*((double)i-arguments[ k ])
+ ((double)j-arguments[k+1])*((double)j-arguments[k+1]);
- if ( method == InverseColorInterpolate )
- weight = sqrt(weight); /* inverse, not inverse squared */
+ weight = pow(weight,coeff[0]); /* inverse of power factor */
weight = ( weight < 1.0 ) ? 1.0 : 1.0/weight;
if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
pixel.red += arguments[x++]*weight;
(image->colorspace == CMYKColorspace))
pixel.black += arguments[x++]*weight;
if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
- (image->matte != MagickFalse))
+ (image->alpha_trait == BlendPixelTrait))
pixel.alpha += arguments[x++]*weight;
denominator += weight;
}
(image->colorspace == CMYKColorspace))
pixel.black/=denominator;
if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
- (image->matte != MagickFalse))
+ (image->alpha_trait == BlendPixelTrait))
pixel.alpha/=denominator;
break;
}
case VoronoiColorInterpolate:
default:
- { /* Just use the closest control point you can find! */
+ {
size_t
k;
+
double
- minimum = MagickHuge;
+ minimum = MagickMaximumValue;
- for(k=0; k<number_arguments; k+=2+number_colors) {
+ /*
+ Just use the closest control point you can find!
+ */
+ for (k=0; k<number_arguments; k+=2+number_colors) {
double distance =
((double)i-arguments[ k ])*((double)i-arguments[ k ])
+ ((double)j-arguments[k+1])*((double)j-arguments[k+1]);
(image->colorspace == CMYKColorspace))
pixel.black=arguments[x++];
if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
- (image->matte != MagickFalse))
+ (image->alpha_trait == BlendPixelTrait))
pixel.alpha=arguments[x++];
minimum = distance;
}
(image->colorspace == CMYKColorspace))
pixel.black*=QuantumRange;
if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
- (image->matte != MagickFalse))
+ (image->alpha_trait == BlendPixelTrait))
pixel.alpha*=QuantumRange;
SetPixelInfoPixel(sparse_image,&pixel,q);
q+=GetPixelChannels(sparse_image);
proceed;
#if defined(MAGICKCORE_OPENMP_SUPPORT)
- #pragma omp critical (MagickCore_SparseColorImage)
+ #pragma omp critical (MagickCore_SparseColorImage)
#endif
proceed=SetImageProgress(image,SparseColorTag,progress++,image->rows);
if (proceed == MagickFalse)