% MagickCore Methods to Shear or Rotate an Image by an Arbitrary Angle %
% %
% Software Design %
-% John Cristy %
+% Cristy %
% July 1992 %
% %
% %
-% Copyright 1999-2011 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 %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
-% The RotateImage, XShearImage, and YShearImage methods are based on the
-% paper "A Fast Algorithm for General Raster Rotatation" by Alan W. Paeth,
-% Graphics Interface '86 (Vancouver). RotateImage is adapted from a similar
+% The XShearImage() and YShearImage() methods are based on the paper "A Fast
+% Algorithm for General Raster Rotatation" by Alan W. Paeth, Graphics
+% Interface '86 (Vancouver). ShearRotateImage() is adapted from a similar
% method based on the Paeth paper written by Michael Halle of the Spatial
% Imaging Group, MIT Media Lab.
%
-%
*/
\f
/*
#include "MagickCore/attribute.h"
#include "MagickCore/blob-private.h"
#include "MagickCore/cache-private.h"
+#include "MagickCore/channel.h"
#include "MagickCore/color-private.h"
#include "MagickCore/colorspace-private.h"
#include "MagickCore/composite.h"
#include "MagickCore/geometry.h"
#include "MagickCore/image.h"
#include "MagickCore/image-private.h"
+#include "MagickCore/matrix.h"
#include "MagickCore/memory_.h"
#include "MagickCore/list.h"
#include "MagickCore/monitor.h"
#include "MagickCore/monitor-private.h"
+#include "MagickCore/nt-base-private.h"
#include "MagickCore/pixel-accessor.h"
#include "MagickCore/quantum.h"
#include "MagickCore/resource_.h"
% %
% %
% %
-% A f f i n e T r a n s f o r m I m a g e %
-% %
-% %
-% %
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%
-% AffineTransformImage() transforms an image as dictated by the affine matrix.
-% It allocates the memory necessary for the new Image structure and returns
-% a pointer to the new image.
-%
-% The format of the AffineTransformImage method is:
-%
-% Image *AffineTransformImage(const Image *image,
-% AffineMatrix *affine_matrix,ExceptionInfo *exception)
-%
-% A description of each parameter follows:
-%
-% o image: the image.
-%
-% o affine_matrix: the affine matrix.
-%
-% o exception: return any errors or warnings in this structure.
-%
-*/
-MagickExport Image *AffineTransformImage(const Image *image,
- const AffineMatrix *affine_matrix,ExceptionInfo *exception)
-{
- double
- distort[6];
-
- Image
- *deskew_image;
-
- /*
- Affine transform image.
- */
- assert(image->signature == MagickSignature);
- if (image->debug != MagickFalse)
- (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
- assert(affine_matrix != (AffineMatrix *) NULL);
- assert(exception != (ExceptionInfo *) NULL);
- assert(exception->signature == MagickSignature);
- distort[0]=affine_matrix->sx;
- distort[1]=affine_matrix->rx;
- distort[2]=affine_matrix->ry;
- distort[3]=affine_matrix->sy;
- distort[4]=affine_matrix->tx;
- distort[5]=affine_matrix->ty;
- deskew_image=DistortImage(image,AffineProjectionDistortion,6,distort,
- MagickTrue,exception);
- return(deskew_image);
-}
-\f
-/*
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-% %
-% %
-% %
+ C r o p T o F i t I m a g e %
% %
% %
% The format of the CropToFitImage method is:
%
% MagickBooleanType CropToFitImage(Image **image,
-% const MagickRealType x_shear,const MagickRealType x_shear,
-% const MagickRealType width,const MagickRealType height,
+% const double x_shear,const double x_shear,
+% const double width,const double height,
% const MagickBooleanType rotate,ExceptionInfo *exception)
%
% A description of each parameter follows.
%
*/
static MagickBooleanType CropToFitImage(Image **image,
- const MagickRealType x_shear,const MagickRealType y_shear,
- const MagickRealType width,const MagickRealType height,
+ const double x_shear,const double y_shear,
+ const double width,const double height,
const MagickBooleanType rotate,ExceptionInfo *exception)
{
Image
% imperfections in the scanning or surface, or simply because the paper was
% not placed completely flat when scanned.
%
+% The result will be auto-croped if the artifact "deskew:auto-crop" is
+% defined, while the amount the image is to be deskewed, in degrees is also
+% saved as the artifact "deskew:angle".
+%
+% If the artifact "deskew:auto-crop" is given the image will be automatically
+% cropped of the excess background. The value is the border width of all
+% pixels around the edge that will be used to determine an average border
+% color for the automatic trim.
+%
% The format of the DeskewImage method is:
%
% Image *DeskewImage(const Image *image,const double threshold,
%
*/
-typedef struct _RadonInfo
-{
- CacheType
- type;
-
- size_t
- width,
- height;
-
- MagickSizeType
- length;
-
- MagickBooleanType
- mapped;
-
- char
- path[MaxTextExtent];
-
- int
- file;
-
- unsigned short
- *cells;
-} RadonInfo;
-
-static RadonInfo *DestroyRadonInfo(RadonInfo *radon_info)
-{
- assert(radon_info != (RadonInfo *) NULL);
- switch (radon_info->type)
- {
- case MemoryCache:
- {
- if (radon_info->mapped == MagickFalse)
- radon_info->cells=(unsigned short *) RelinquishMagickMemory(
- radon_info->cells);
- else
- radon_info->cells=(unsigned short *) UnmapBlob(radon_info->cells,
- (size_t) radon_info->length);
- RelinquishMagickResource(MemoryResource,radon_info->length);
- break;
- }
- case MapCache:
- {
- radon_info->cells=(unsigned short *) UnmapBlob(radon_info->cells,(size_t)
- radon_info->length);
- RelinquishMagickResource(MapResource,radon_info->length);
- }
- case DiskCache:
- {
- if (radon_info->file != -1)
- (void) close(radon_info->file);
- (void) RelinquishUniqueFileResource(radon_info->path);
- RelinquishMagickResource(DiskResource,radon_info->length);
- break;
- }
- default:
- break;
- }
- return((RadonInfo *) RelinquishMagickMemory(radon_info));
-}
-
-static MagickBooleanType ResetRadonCells(RadonInfo *radon_info)
-{
- register ssize_t
- x;
-
- ssize_t
- count,
- y;
-
- unsigned short
- value;
-
- if (radon_info->type != DiskCache)
- {
- (void) ResetMagickMemory(radon_info->cells,0,(size_t) radon_info->length);
- return(MagickTrue);
- }
- value=0;
- (void) lseek(radon_info->file,0,SEEK_SET);
- for (y=0; y < (ssize_t) radon_info->height; y++)
- {
- for (x=0; x < (ssize_t) radon_info->width; x++)
- {
- count=write(radon_info->file,&value,sizeof(*radon_info->cells));
- if (count != (ssize_t) sizeof(*radon_info->cells))
- break;
- }
- if (x < (ssize_t) radon_info->width)
- break;
- }
- return(y < (ssize_t) radon_info->height ? MagickFalse : MagickTrue);
-}
-
-static RadonInfo *AcquireRadonInfo(const Image *image,const size_t width,
- const size_t height,ExceptionInfo *exception)
-{
- MagickBooleanType
- status;
-
- RadonInfo
- *radon_info;
-
- radon_info=(RadonInfo *) AcquireMagickMemory(sizeof(*radon_info));
- if (radon_info == (RadonInfo *) NULL)
- return((RadonInfo *) NULL);
- (void) ResetMagickMemory(radon_info,0,sizeof(*radon_info));
- radon_info->width=width;
- radon_info->height=height;
- radon_info->length=(MagickSizeType) width*height*sizeof(*radon_info->cells);
- radon_info->type=MemoryCache;
- status=AcquireMagickResource(AreaResource,radon_info->length);
- if ((status != MagickFalse) &&
- (radon_info->length == (MagickSizeType) ((size_t) radon_info->length)))
- {
- status=AcquireMagickResource(MemoryResource,radon_info->length);
- if (status != MagickFalse)
- {
- radon_info->mapped=MagickFalse;
- radon_info->cells=(unsigned short *) AcquireMagickMemory((size_t)
- radon_info->length);
- if (radon_info->cells == (unsigned short *) NULL)
- {
- radon_info->mapped=MagickTrue;
- radon_info->cells=(unsigned short *) MapBlob(-1,IOMode,0,(size_t)
- radon_info->length);
- }
- if (radon_info->cells == (unsigned short *) NULL)
- RelinquishMagickResource(MemoryResource,radon_info->length);
- }
- }
- radon_info->file=(-1);
- if (radon_info->cells == (unsigned short *) NULL)
- {
- status=AcquireMagickResource(DiskResource,radon_info->length);
- if (status == MagickFalse)
- {
- (void) ThrowMagickException(exception,GetMagickModule(),CacheError,
- "CacheResourcesExhausted","`%s'",image->filename);
- return(DestroyRadonInfo(radon_info));
- }
- radon_info->type=DiskCache;
- (void) AcquireMagickResource(MemoryResource,radon_info->length);
- radon_info->file=AcquireUniqueFileResource(radon_info->path);
- if (radon_info->file == -1)
- return(DestroyRadonInfo(radon_info));
- status=AcquireMagickResource(MapResource,radon_info->length);
- if (status != MagickFalse)
- {
- status=ResetRadonCells(radon_info);
- if (status != MagickFalse)
- {
- radon_info->cells=(unsigned short *) MapBlob(radon_info->file,
- IOMode,0,(size_t) radon_info->length);
- if (radon_info->cells != (unsigned short *) NULL)
- radon_info->type=MapCache;
- else
- RelinquishMagickResource(MapResource,radon_info->length);
- }
- }
- }
- return(radon_info);
-}
-
-static inline size_t MagickMin(const size_t x,const size_t y)
-{
- if (x < y)
- return(x);
- return(y);
-}
-
-static inline ssize_t ReadRadonCell(const RadonInfo *radon_info,
- const MagickOffsetType offset,const size_t length,unsigned char *buffer)
-{
- register ssize_t
- i;
-
- ssize_t
- count;
-
-#if !defined(MAGICKCORE_HAVE_PPREAD)
-#if defined(MAGICKCORE_OPENMP_SUPPORT)
- #pragma omp critical (MagickCore_ReadRadonCell)
-#endif
- {
- i=(-1);
- if (lseek(radon_info->file,offset,SEEK_SET) >= 0)
- {
-#endif
- count=0;
- for (i=0; i < (ssize_t) length; i+=count)
- {
-#if !defined(MAGICKCORE_HAVE_PPREAD)
- count=read(radon_info->file,buffer+i,MagickMin(length-i,(size_t)
- SSIZE_MAX));
-#else
- count=pread(radon_info->file,buffer+i,MagickMin(length-i,(size_t)
- SSIZE_MAX),offset+i);
-#endif
- if (count > 0)
- continue;
- count=0;
- if (errno != EINTR)
- {
- i=(-1);
- break;
- }
- }
-#if !defined(MAGICKCORE_HAVE_PPREAD)
- }
- }
-#endif
- return(i);
-}
-
-static inline ssize_t WriteRadonCell(const RadonInfo *radon_info,
- const MagickOffsetType offset,const size_t length,const unsigned char *buffer)
-{
- register ssize_t
- i;
-
- ssize_t
- count;
-
-#if !defined(MAGICKCORE_HAVE_PWRITE)
-#if defined(MAGICKCORE_OPENMP_SUPPORT)
- #pragma omp critical (MagickCore_WriteRadonCell)
-#endif
- {
- if (lseek(radon_info->file,offset,SEEK_SET) >= 0)
- {
-#endif
- count=0;
- for (i=0; i < (ssize_t) length; i+=count)
- {
-#if !defined(MAGICKCORE_HAVE_PWRITE)
- count=write(radon_info->file,buffer+i,MagickMin(length-i,(size_t)
- SSIZE_MAX));
-#else
- count=pwrite(radon_info->file,buffer+i,MagickMin(length-i,(size_t)
- SSIZE_MAX),offset+i);
-#endif
- if (count > 0)
- continue;
- count=0;
- if (errno != EINTR)
- {
- i=(-1);
- break;
- }
- }
-#if !defined(MAGICKCORE_HAVE_PWRITE)
- }
- }
-#endif
- return(i);
-}
-
-static inline unsigned short GetRadonCell(const RadonInfo *radon_info,
- const ssize_t x,const ssize_t y)
-{
- MagickOffsetType
- i;
-
- unsigned short
- value;
-
- i=(MagickOffsetType) radon_info->height*x+y;
- if ((i < 0) ||
- ((MagickSizeType) (i*sizeof(*radon_info->cells)) >= radon_info->length))
- return(0);
- if (radon_info->type != DiskCache)
- return(radon_info->cells[i]);
- value=0;
- (void) ReadRadonCell(radon_info,i*sizeof(*radon_info->cells),
- sizeof(*radon_info->cells),(unsigned char *) &value);
- return(value);
-}
-
-static inline MagickBooleanType SetRadonCell(const RadonInfo *radon_info,
- const ssize_t x,const ssize_t y,const unsigned short value)
+static void RadonProjection(const Image *image,MatrixInfo *source_matrixs,
+ MatrixInfo *destination_matrixs,const ssize_t sign,size_t *projection)
{
- MagickOffsetType
- i;
-
- ssize_t
- count;
-
- i=(MagickOffsetType) radon_info->height*x+y;
- if ((i < 0) ||
- ((MagickSizeType) (i*sizeof(*radon_info->cells)) >= radon_info->length))
- return(MagickFalse);
- if (radon_info->type != DiskCache)
- {
- radon_info->cells[i]=value;
- return(MagickTrue);
- }
- count=WriteRadonCell(radon_info,i*sizeof(*radon_info->cells),
- sizeof(*radon_info->cells),(const unsigned char *) &value);
- if (count != (ssize_t) sizeof(*radon_info->cells))
- return(MagickFalse);
- return(MagickTrue);
-}
-
-static void RadonProjection(RadonInfo *source_cells,
- RadonInfo *destination_cells,const ssize_t sign,size_t *projection)
-{
- RadonInfo
+ MatrixInfo
*swap;
- register ssize_t
- x;
-
- register RadonInfo
+ register MatrixInfo
*p,
*q;
+ register ssize_t
+ x;
+
size_t
step;
- p=source_cells;
- q=destination_cells;
- for (step=1; step < p->width; step*=2)
+ p=source_matrixs;
+ q=destination_matrixs;
+ for (step=1; step < GetMatrixColumns(p); step*=2)
{
- for (x=0; x < (ssize_t) p->width; x+=2*(ssize_t) step)
+ for (x=0; x < (ssize_t) GetMatrixColumns(p); x+=2*(ssize_t) step)
{
register ssize_t
i;
y;
unsigned short
- cell;
+ element,
+ neighbor;
for (i=0; i < (ssize_t) step; i++)
{
- for (y=0; y < (ssize_t) (p->height-i-1); y++)
+ for (y=0; y < (ssize_t) (GetMatrixRows(p)-i-1); y++)
{
- cell=GetRadonCell(p,x+i,y);
- (void) SetRadonCell(q,x+2*i,y,cell+GetRadonCell(p,x+i+(ssize_t)
- step,y+i));
- (void) SetRadonCell(q,x+2*i+1,y,cell+GetRadonCell(p,x+i+(ssize_t)
- step,y+i+1));
+ if (GetMatrixElement(p,x+i,y,&element) == MagickFalse)
+ continue;
+ if (GetMatrixElement(p,x+i+step,y+i,&neighbor) == MagickFalse)
+ continue;
+ neighbor+=element;
+ if (SetMatrixElement(q,x+2*i,y,&neighbor) == MagickFalse)
+ continue;
+ if (GetMatrixElement(p,x+i+step,y+i+1,&neighbor) == MagickFalse)
+ continue;
+ neighbor+=element;
+ if (SetMatrixElement(q,x+2*i+1,y,&neighbor) == MagickFalse)
+ continue;
}
- for ( ; y < (ssize_t) (p->height-i); y++)
+ for ( ; y < (ssize_t) (GetMatrixRows(p)-i); y++)
{
- cell=GetRadonCell(p,x+i,y);
- (void) SetRadonCell(q,x+2*i,y,cell+GetRadonCell(p,x+i+(ssize_t) step,
- y+i));
- (void) SetRadonCell(q,x+2*i+1,y,cell);
+ if (GetMatrixElement(p,x+i,y,&element) == MagickFalse)
+ continue;
+ if (GetMatrixElement(p,x+i+step,y+i,&neighbor) == MagickFalse)
+ continue;
+ neighbor+=element;
+ if (SetMatrixElement(q,x+2*i,y,&neighbor) == MagickFalse)
+ continue;
+ if (SetMatrixElement(q,x+2*i+1,y,&element) == MagickFalse)
+ continue;
}
- for ( ; y < (ssize_t) p->height; y++)
+ for ( ; y < (ssize_t) GetMatrixRows(p); y++)
{
- cell=GetRadonCell(p,x+i,y);
- (void) SetRadonCell(q,x+2*i,y,cell);
- (void) SetRadonCell(q,x+2*i+1,y,cell);
+ if (GetMatrixElement(p,x+i,y,&element) == MagickFalse)
+ continue;
+ if (SetMatrixElement(q,x+2*i,y,&element) == MagickFalse)
+ continue;
+ if (SetMatrixElement(q,x+2*i+1,y,&element) == MagickFalse)
+ continue;
}
}
}
q=swap;
}
#if defined(MAGICKCORE_OPENMP_SUPPORT)
- #pragma omp parallel for schedule(dynamic,4)
+ #pragma omp parallel for schedule(static,4) \
+ magick_threads(image,image,1,1)
#endif
- for (x=0; x < (ssize_t) p->width; x++)
+ for (x=0; x < (ssize_t) GetMatrixColumns(p); x++)
{
register ssize_t
y;
sum;
sum=0;
- for (y=0; y < (ssize_t) (p->height-1); y++)
+ for (y=0; y < (ssize_t) (GetMatrixRows(p)-1); y++)
{
ssize_t
delta;
- delta=GetRadonCell(p,x,y)-(ssize_t) GetRadonCell(p,x,y+1);
+ unsigned short
+ element,
+ neighbor;
+
+ if (GetMatrixElement(p,x,y,&element) == MagickFalse)
+ continue;
+ if (GetMatrixElement(p,x,y+1,&neighbor) == MagickFalse)
+ continue;
+ delta=(ssize_t) element-(ssize_t) neighbor;
sum+=delta*delta;
}
- projection[p->width+sign*x-1]=sum;
+ projection[GetMatrixColumns(p)+sign*x-1]=sum;
}
}
CacheView
*image_view;
+ MatrixInfo
+ *destination_matrixs,
+ *source_matrixs;
+
MagickBooleanType
status;
- RadonInfo
- *destination_cells,
- *source_cells;
-
register ssize_t
i;
bits[256];
for (width=1; width < ((image->columns+7)/8); width<<=1) ;
- source_cells=AcquireRadonInfo(image,width,image->rows,exception);
- destination_cells=AcquireRadonInfo(image,width,image->rows,exception);
- if ((source_cells == (RadonInfo *) NULL) ||
- (destination_cells == (RadonInfo *) NULL))
+ source_matrixs=AcquireMatrixInfo(width,image->rows,sizeof(unsigned short),
+ exception);
+ destination_matrixs=AcquireMatrixInfo(width,image->rows,sizeof(unsigned short),
+ exception);
+ if ((source_matrixs == (MatrixInfo *) NULL) ||
+ (destination_matrixs == (MatrixInfo *) NULL))
{
- if (destination_cells != (RadonInfo *) NULL)
- destination_cells=DestroyRadonInfo(destination_cells);
- if (source_cells != (RadonInfo *) NULL)
- source_cells=DestroyRadonInfo(source_cells);
+ if (destination_matrixs != (MatrixInfo *) NULL)
+ destination_matrixs=DestroyMatrixInfo(destination_matrixs);
+ if (source_matrixs != (MatrixInfo *) NULL)
+ source_matrixs=DestroyMatrixInfo(source_matrixs);
return(MagickFalse);
}
- if (ResetRadonCells(source_cells) == MagickFalse)
+ if (NullMatrix(source_matrixs) == MagickFalse)
{
- destination_cells=DestroyRadonInfo(destination_cells);
- source_cells=DestroyRadonInfo(source_cells);
+ destination_matrixs=DestroyMatrixInfo(destination_matrixs);
+ source_matrixs=DestroyMatrixInfo(source_matrixs);
return(MagickFalse);
}
for (i=0; i < 256; i++)
bits[i]=(unsigned short) count;
}
status=MagickTrue;
- image_view=AcquireCacheView(image);
+ image_view=AcquireVirtualCacheView(image,exception);
#if defined(MAGICKCORE_OPENMP_SUPPORT)
- #pragma omp parallel for schedule(dynamic,4) shared(status)
+ #pragma omp parallel for schedule(static,4) shared(status) \
+ magick_threads(image,image,1,1)
#endif
for (y=0; y < (ssize_t) image->rows; y++)
{
bit,
byte;
+ unsigned short
+ value;
+
if (status == MagickFalse)
continue;
p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
bit++;
if (bit == 8)
{
- (void) SetRadonCell(source_cells,--i,y,bits[byte]);
+ value=bits[byte];
+ (void) SetMatrixElement(source_matrixs,--i,y,&value);
bit=0;
byte=0;
}
if (bit != 0)
{
byte<<=(8-bit);
- (void) SetRadonCell(source_cells,--i,y,bits[byte]);
+ value=bits[byte];
+ (void) SetMatrixElement(source_matrixs,--i,y,&value);
}
}
- RadonProjection(source_cells,destination_cells,-1,projection);
- (void) ResetRadonCells(source_cells);
+ RadonProjection(image,source_matrixs,destination_matrixs,-1,projection);
+ (void) NullMatrix(source_matrixs);
#if defined(MAGICKCORE_OPENMP_SUPPORT)
- #pragma omp parallel for schedule(dynamic,4) shared(status)
+ #pragma omp parallel for schedule(static,4) shared(status) \
+ magick_threads(image,image,image->rows,1)
#endif
for (y=0; y < (ssize_t) image->rows; y++)
{
bit,
byte;
+ unsigned short
+ value;
+
if (status == MagickFalse)
continue;
p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
bit++;
if (bit == 8)
{
- (void) SetRadonCell(source_cells,i++,y,bits[byte]);
+ value=bits[byte];
+ (void) SetMatrixElement(source_matrixs,i++,y,&value);
bit=0;
byte=0;
}
if (bit != 0)
{
byte<<=(8-bit);
- (void) SetRadonCell(source_cells,i++,y,bits[byte]);
+ value=bits[byte];
+ (void) SetMatrixElement(source_matrixs,i++,y,&value);
}
}
- RadonProjection(source_cells,destination_cells,1,projection);
+ RadonProjection(image,source_matrixs,destination_matrixs,1,projection);
image_view=DestroyCacheView(image_view);
- destination_cells=DestroyRadonInfo(destination_cells);
- source_cells=DestroyRadonInfo(source_cells);
+ destination_matrixs=DestroyMatrixInfo(destination_matrixs);
+ source_matrixs=DestroyMatrixInfo(source_matrixs);
return(MagickTrue);
}
PixelInfo
background;
- MagickRealType
+ double
count;
ssize_t
return;
GetPixelInfo(image,&background);
count=0.0;
- image_view=AcquireCacheView(image);
+ image_view=AcquireVirtualCacheView(image,exception);
for (y=0; y < (ssize_t) image->rows; y++)
{
register const Quantum
background.red+=QuantumScale*GetPixelRed(image,p);
background.green+=QuantumScale*GetPixelGreen(image,p);
background.blue+=QuantumScale*GetPixelBlue(image,p);
- background.alpha+=QuantumScale*GetPixelAlpha(image,p);
+ if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
+ background.alpha+=QuantumScale*GetPixelAlpha(image,p);
count++;
p+=GetPixelChannels(image);
}
}
image_view=DestroyCacheView(image_view);
- image->background_color.red=ClampToQuantum((MagickRealType) QuantumRange*
+ image->background_color.red=(double) ClampToQuantum(QuantumRange*
background.red/count);
- image->background_color.green=ClampToQuantum((MagickRealType) QuantumRange*
+ image->background_color.green=(double) ClampToQuantum(QuantumRange*
background.green/count);
- image->background_color.blue=ClampToQuantum((MagickRealType) QuantumRange*
+ image->background_color.blue=(double) ClampToQuantum(QuantumRange*
background.blue/count);
- image->background_color.alpha=ClampToQuantum((MagickRealType) QuantumRange*
- background.alpha/count);
+ if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
+ image->background_color.alpha=(double) ClampToQuantum(QuantumRange*
+ background.alpha/count);
}
MagickExport Image *DeskewImage(const Image *image,const double threshold,
}
}
projection=(size_t *) RelinquishMagickMemory(projection);
+ degrees=RadiansToDegrees(-atan((double) skew/width/8));
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TransformEvent,GetMagickModule(),
+ " Deskew angle: %g",degrees);
/*
Deskew image.
*/
clone_image=CloneImage(image,0,0,MagickTrue,exception);
if (clone_image == (Image *) NULL)
return((Image *) NULL);
- (void) SetImageVirtualPixelMethod(clone_image,BackgroundVirtualPixelMethod);
- degrees=RadiansToDegrees(-atan((double) skew/width/8));
- if (image->debug != MagickFalse)
- (void) LogMagickEvent(TransformEvent,GetMagickModule(),
- " Deskew angle: %g",degrees);
+ {
+ char
+ angle[MaxTextExtent];
+
+ (void) FormatLocaleString(angle,MaxTextExtent,"%.20g",degrees);
+ (void) SetImageArtifact(clone_image,"deskew:angle",angle);
+ }
+ (void) SetImageVirtualPixelMethod(clone_image,BackgroundVirtualPixelMethod,
+ exception);
affine_matrix.sx=cos(DegreesToRadians(fmod((double) degrees,360.0)));
affine_matrix.rx=sin(DegreesToRadians(fmod((double) degrees,360.0)));
affine_matrix.ry=(-sin(DegreesToRadians(fmod((double) degrees,360.0))));
% %
% %
% %
-+ I n t e g r a l R o t a t e I m a g e %
+% I n t e g r a l R o t a t e I m a g e %
% %
% %
% %
% o rotations: Specifies the number of 90 degree rotations.
%
*/
-static Image *IntegralRotateImage(const Image *image,size_t rotations,
+MagickExport Image *IntegralRotateImage(const Image *image,size_t rotations,
ExceptionInfo *exception)
{
#define RotateImageTag "Rotate/Image"
*/
status=MagickTrue;
progress=0;
- image_view=AcquireCacheView(image);
- rotate_view=AcquireCacheView(rotate_image);
+ image_view=AcquireVirtualCacheView(image,exception);
+ rotate_view=AcquireAuthenticCacheView(rotate_image,exception);
switch (rotations)
{
- case 0:
- {
- /*
- Rotate 0 degrees.
- */
- break;
- }
case 1:
{
size_t
Rotate 90 degrees.
*/
GetPixelCacheTileSize(image,&tile_width,&tile_height);
+ tile_width=image->columns;
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(static,4) shared(status) \
+ magick_threads(image,image,1,1)
+#endif
for (tile_y=0; tile_y < (ssize_t) image->rows; tile_y+=(ssize_t) tile_height)
{
register ssize_t
if (status == MagickFalse)
continue;
- for (tile_x=0; tile_x < (ssize_t) image->columns; tile_x+=(ssize_t) tile_width)
+ tile_x=0;
+ for ( ; tile_x < (ssize_t) image->columns; tile_x+=(ssize_t) tile_width)
{
MagickBooleanType
sync;
register const Quantum
*restrict p;
- register ssize_t
- y;
-
register Quantum
*restrict q;
+ register ssize_t
+ y;
+
size_t
height,
width;
status=MagickFalse;
break;
}
-#if defined(MAGICKCORE_OPENMP_SUPPORT)
- #pragma omp parallel for schedule(static,1) shared(progress, status)
-#endif
for (y=0; y < (ssize_t) width; y++)
{
register const Quantum
register ssize_t
i;
+ if (GetPixelReadMask(image,tile_pixels) == 0)
+ {
+ tile_pixels-=width*GetPixelChannels(image);
+ q+=GetPixelChannels(rotate_image);
+ continue;
+ }
for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
{
- PixelChannel
- channel;
-
- PixelTrait
- rotate_traits,
- traits;
-
- traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
- channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
- rotate_traits=GetPixelChannelMapTraits(rotate_image,channel);
+ PixelChannel channel=GetPixelChannelChannel(image,i);
+ PixelTrait traits=GetPixelChannelTraits(image,channel);
+ PixelTrait rotate_traits=GetPixelChannelTraits(rotate_image,
+ channel);
if ((traits == UndefinedPixelTrait) ||
(rotate_traits == UndefinedPixelTrait))
continue;
- if ((rotate_traits & CopyPixelTrait) != 0)
- {
- q[channel]=tile_pixels[i];
- continue;
- }
- q[channel]=tile_pixels[i];
+ SetPixelChannel(rotate_image,channel,tile_pixels[i],q);
}
tile_pixels-=width*GetPixelChannels(image);
q+=GetPixelChannels(rotate_image);
MagickBooleanType
proceed;
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_IntegralRotateImage)
+#endif
proceed=SetImageProgress(image,RotateImageTag,progress+=tile_height,
image->rows);
if (proceed == MagickFalse)
/*
Rotate 180 degrees.
*/
-#if defined(MAGICKCORE_OPENMP_SUPPORT)
- #pragma omp parallel for schedule(static,8) shared(progress,status)
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(static,4) shared(status) \
+ magick_threads(image,image,1,1)
#endif
for (y=0; y < (ssize_t) image->rows; y++)
{
register const Quantum
*restrict p;
- register ssize_t
- x;
-
register Quantum
*restrict q;
+ register ssize_t
+ x;
+
if (status == MagickFalse)
continue;
p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
i;
q-=GetPixelChannels(rotate_image);
+ if (GetPixelReadMask(image,p) == 0)
+ {
+ p+=GetPixelChannels(image);
+ continue;
+ }
for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
{
- PixelChannel
- channel;
-
- PixelTrait
- rotate_traits,
- traits;
-
- traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
- channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
- rotate_traits=GetPixelChannelMapTraits(rotate_image,channel);
+ PixelChannel channel=GetPixelChannelChannel(image,i);
+ PixelTrait traits=GetPixelChannelTraits(image,channel);
+ PixelTrait rotate_traits=GetPixelChannelTraits(rotate_image,
+ channel);
if ((traits == UndefinedPixelTrait) ||
(rotate_traits == UndefinedPixelTrait))
continue;
- if ((rotate_traits & CopyPixelTrait) != 0)
- {
- q[channel]=p[i];
- continue;
- }
- q[channel]=p[i];
+ SetPixelChannel(rotate_image,channel,p[i],q);
}
p+=GetPixelChannels(image);
}
MagickBooleanType
proceed;
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_IntegralRotateImage)
+#endif
proceed=SetImageProgress(image,RotateImageTag,progress++,
image->rows);
if (proceed == MagickFalse)
status=MagickFalse;
}
}
+ (void) SetImageProgress(image,RotateImageTag,(MagickOffsetType)
+ image->rows-1,image->rows);
+ Swap(page.width,page.height);
+ Swap(page.x,page.y);
if (page.width != 0)
page.x=(ssize_t) (page.width-rotate_image->columns-page.x);
- if (page.height != 0)
- page.y=(ssize_t) (page.height-rotate_image->rows-page.y);
break;
}
case 3:
Rotate 270 degrees.
*/
GetPixelCacheTileSize(image,&tile_width,&tile_height);
+ tile_width=image->columns;
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(static,4) shared(status) \
+ magick_threads(image,image,1,1)
+#endif
for (tile_y=0; tile_y < (ssize_t) image->rows; tile_y+=(ssize_t) tile_height)
{
register ssize_t
if (status == MagickFalse)
continue;
- for (tile_x=0; tile_x < (ssize_t) image->columns; tile_x+=(ssize_t) tile_width)
+ tile_x=0;
+ for ( ; tile_x < (ssize_t) image->columns; tile_x+=(ssize_t) tile_width)
{
MagickBooleanType
sync;
register const Quantum
*restrict p;
- register ssize_t
- y;
-
register Quantum
*restrict q;
+ register ssize_t
+ y;
+
size_t
height,
width;
status=MagickFalse;
break;
}
-#if defined(MAGICKCORE_OPENMP_SUPPORT)
- #pragma omp parallel for schedule(static,1) shared(progress,status)
-#endif
for (y=0; y < (ssize_t) width; y++)
{
register const Quantum
register ssize_t
i;
+ if (GetPixelReadMask(image,tile_pixels) == 0)
+ {
+ tile_pixels+=width*GetPixelChannels(image);
+ q+=GetPixelChannels(rotate_image);
+ continue;
+ }
for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
{
- PixelChannel
- channel;
-
- PixelTrait
- rotate_traits,
- traits;
-
- traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
- channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
- rotate_traits=GetPixelChannelMapTraits(rotate_image,channel);
+ PixelChannel channel=GetPixelChannelChannel(image,i);
+ PixelTrait traits=GetPixelChannelTraits(image,channel);
+ PixelTrait rotate_traits=GetPixelChannelTraits(rotate_image,
+ channel);
if ((traits == UndefinedPixelTrait) ||
(rotate_traits == UndefinedPixelTrait))
continue;
- if ((rotate_traits & CopyPixelTrait) != 0)
- {
- q[channel]=tile_pixels[i];
- continue;
- }
- q[channel]=tile_pixels[i];
+ SetPixelChannel(rotate_image,channel,tile_pixels[i],q);
}
tile_pixels+=width*GetPixelChannels(image);
q+=GetPixelChannels(rotate_image);
}
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_IntegralRotateImage)
+#endif
sync=SyncCacheViewAuthenticPixels(rotate_view,exception);
if (sync == MagickFalse)
status=MagickFalse;
image->rows-1,image->rows);
Swap(page.width,page.height);
Swap(page.x,page.y);
- if (page.height != 0)
- page.y=(ssize_t) (page.height-rotate_image->rows-page.y);
+ if (page.width != 0)
+ page.x=(ssize_t) (page.width-rotate_image->columns-page.x);
break;
}
+ default:
+ break;
}
rotate_view=DestroyCacheView(rotate_view);
image_view=DestroyCacheView(image_view);
%
% The format of the XShearImage method is:
%
-% MagickBooleanType XShearImage(Image *image,const MagickRealType degrees,
+% MagickBooleanType XShearImage(Image *image,const double degrees,
% const size_t width,const size_t height,
% const ssize_t x_offset,const ssize_t y_offset,ExceptionInfo *exception)
%
%
% o image: the image.
%
-% o degrees: A MagickRealType representing the shearing angle along the X
+% o degrees: A double representing the shearing angle along the X
% axis.
%
% o width, height, x_offset, y_offset: Defines a region of the image
% o exception: return any errors or warnings in this structure.
%
*/
-static MagickBooleanType XShearImage(Image *image,const MagickRealType degrees,
+static MagickBooleanType XShearImage(Image *image,const double degrees,
const size_t width,const size_t height,const ssize_t x_offset,
const ssize_t y_offset,ExceptionInfo *exception)
{
ssize_t
y;
+ /*
+ X shear image.
+ */
assert(image != (Image *) NULL);
assert(image->signature == MagickSignature);
if (image->debug != MagickFalse)
(void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
- GetPixelInfo(image,&background);
- SetPixelInfoPacket(image,&image->background_color,&background);
- if (image->colorspace == CMYKColorspace)
- ConvertRGBToCMYK(&background);
- /*
- X shear image.
- */
status=MagickTrue;
+ background=image->background_color;
progress=0;
- image_view=AcquireCacheView(image);
+ image_view=AcquireAuthenticCacheView(image,exception);
#if defined(MAGICKCORE_OPENMP_SUPPORT)
- #pragma omp parallel for schedule(dynamic,4) shared(progress, status)
+ #pragma omp parallel for schedule(static,4) shared(progress,status) \
+ magick_threads(image,image,height,1)
#endif
for (y=0; y < (ssize_t) height; y++)
{
source,
destination;
- MagickRealType
+ double
area,
displacement;
continue;
}
p+=x_offset*GetPixelChannels(image);
- displacement=degrees*(MagickRealType) (y-height/2.0);
+ displacement=degrees*(double) (y-height/2.0);
if (displacement == 0.0)
continue;
if (displacement > 0.0)
direction=LEFT;
}
step=(ssize_t) floor((double) displacement);
- area=(MagickRealType) (displacement-step);
+ area=(double) (displacement-step);
step++;
pixel=background;
GetPixelInfo(image,&source);
if ((x_offset+i) < step)
{
p+=GetPixelChannels(image);
- SetPixelInfo(image,p,&pixel);
+ GetPixelInfoPixel(image,p,&pixel);
q+=GetPixelChannels(image);
continue;
}
- SetPixelInfo(image,p,&source);
- CompositePixelInfoAreaBlend(&pixel,(MagickRealType) pixel.alpha,
- &source,(MagickRealType) GetPixelAlpha(image,p),area,
- &destination);
- SetPixelPixelInfo(image,&destination,q);
- SetPixelInfo(image,p,&pixel);
+ GetPixelInfoPixel(image,p,&source);
+ CompositePixelInfoAreaBlend(&pixel,(double) pixel.alpha,
+ &source,(double) GetPixelAlpha(image,p),area,&destination);
+ SetPixelInfoPixel(image,&destination,q);
+ GetPixelInfoPixel(image,p,&pixel);
p+=GetPixelChannels(image);
q+=GetPixelChannels(image);
}
- CompositePixelInfoAreaBlend(&pixel,(MagickRealType) pixel.alpha,
- &background,(MagickRealType) background.alpha,area,&destination);
- SetPixelPixelInfo(image,&destination,q);
+ CompositePixelInfoAreaBlend(&pixel,(double) pixel.alpha,
+ &background,(double) background.alpha,area,&destination);
+ SetPixelInfoPixel(image,&destination,q);
q+=GetPixelChannels(image);
for (i=0; i < (step-1); i++)
{
- SetPixelPixelInfo(image,&background,q);
+ SetPixelInfoPixel(image,&background,q);
q+=GetPixelChannels(image);
}
break;
{
p-=GetPixelChannels(image);
q-=GetPixelChannels(image);
- if ((size_t) (x_offset+width+step-i) >= image->columns)
+ if ((size_t) (x_offset+width+step-i) > image->columns)
continue;
- SetPixelInfo(image,p,&source);
- CompositePixelInfoAreaBlend(&pixel,(MagickRealType) pixel.alpha,
- &source,(MagickRealType) GetPixelAlpha(image,p),area,
- &destination);
- SetPixelPixelInfo(image,&destination,q);
- SetPixelInfo(image,p,&pixel);
+ GetPixelInfoPixel(image,p,&source);
+ CompositePixelInfoAreaBlend(&pixel,(double) pixel.alpha,
+ &source,(double) GetPixelAlpha(image,p),area,&destination);
+ SetPixelInfoPixel(image,&destination,q);
+ GetPixelInfoPixel(image,p,&pixel);
}
- CompositePixelInfoAreaBlend(&pixel,(MagickRealType) pixel.alpha,
- &background,(MagickRealType) background.alpha,area,&destination);
+ CompositePixelInfoAreaBlend(&pixel,(double) pixel.alpha,
+ &background,(double) background.alpha,area,&destination);
q-=GetPixelChannels(image);
- SetPixelPixelInfo(image,&destination,q);
+ SetPixelInfoPixel(image,&destination,q);
for (i=0; i < (step-1); i++)
{
q-=GetPixelChannels(image);
- SetPixelPixelInfo(image,&background,q);
+ SetPixelInfoPixel(image,&background,q);
}
break;
}
proceed;
#if defined(MAGICKCORE_OPENMP_SUPPORT)
- #pragma omp critical (MagickCore_XShearImage)
+ #pragma omp critical (MagickCore_XShearImage)
#endif
proceed=SetImageProgress(image,XShearImageTag,progress++,height);
if (proceed == MagickFalse)
%
% The format of the YShearImage method is:
%
-% MagickBooleanType YShearImage(Image *image,const MagickRealType degrees,
+% MagickBooleanType YShearImage(Image *image,const double degrees,
% const size_t width,const size_t height,
% const ssize_t x_offset,const ssize_t y_offset,ExceptionInfo *exception)
%
%
% o image: the image.
%
-% o degrees: A MagickRealType representing the shearing angle along the Y
+% o degrees: A double representing the shearing angle along the Y
% axis.
%
% o width, height, x_offset, y_offset: Defines a region of the image
% o exception: return any errors or warnings in this structure.
%
*/
-static MagickBooleanType YShearImage(Image *image,const MagickRealType degrees,
+static MagickBooleanType YShearImage(Image *image,const double degrees,
const size_t width,const size_t height,const ssize_t x_offset,
const ssize_t y_offset,ExceptionInfo *exception)
{
ssize_t
x;
+ /*
+ Y Shear image.
+ */
assert(image != (Image *) NULL);
assert(image->signature == MagickSignature);
if (image->debug != MagickFalse)
(void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
- GetPixelInfo(image,&background);
- SetPixelInfoPacket(image,&image->background_color,&background);
- if (image->colorspace == CMYKColorspace)
- ConvertRGBToCMYK(&background);
- /*
- Y Shear image.
- */
status=MagickTrue;
progress=0;
- image_view=AcquireCacheView(image);
+ background=image->background_color;
+ image_view=AcquireAuthenticCacheView(image,exception);
#if defined(MAGICKCORE_OPENMP_SUPPORT)
- #pragma omp parallel for schedule(dynamic,4) shared(progress, status)
+ #pragma omp parallel for schedule(static,4) shared(progress,status) \
+ magick_threads(image,image,width,1)
#endif
for (x=0; x < (ssize_t) width; x++)
{
ssize_t
step;
- MagickRealType
+ double
area,
displacement;
continue;
}
p+=y_offset*GetPixelChannels(image);
- displacement=degrees*(MagickRealType) (x-width/2.0);
+ displacement=degrees*(double) (x-width/2.0);
if (displacement == 0.0)
continue;
if (displacement > 0.0)
direction=UP;
}
step=(ssize_t) floor((double) displacement);
- area=(MagickRealType) (displacement-step);
+ area=(double) (displacement-step);
step++;
pixel=background;
GetPixelInfo(image,&source);
if ((y_offset+i) < step)
{
p+=GetPixelChannels(image);
- SetPixelInfo(image,p,&pixel);
+ GetPixelInfoPixel(image,p,&pixel);
q+=GetPixelChannels(image);
continue;
}
- SetPixelInfo(image,p,&source);
- CompositePixelInfoAreaBlend(&pixel,(MagickRealType) pixel.alpha,
- &source,(MagickRealType) GetPixelAlpha(image,p),area,
+ GetPixelInfoPixel(image,p,&source);
+ CompositePixelInfoAreaBlend(&pixel,(double) pixel.alpha,
+ &source,(double) GetPixelAlpha(image,p),area,
&destination);
- SetPixelPixelInfo(image,&destination,q);
- SetPixelInfo(image,p,&pixel);
+ SetPixelInfoPixel(image,&destination,q);
+ GetPixelInfoPixel(image,p,&pixel);
p+=GetPixelChannels(image);
q+=GetPixelChannels(image);
}
- CompositePixelInfoAreaBlend(&pixel,(MagickRealType) pixel.alpha,
- &background,(MagickRealType) background.alpha,area,&destination);
- SetPixelPixelInfo(image,&destination,q);
+ CompositePixelInfoAreaBlend(&pixel,(double) pixel.alpha,
+ &background,(double) background.alpha,area,&destination);
+ SetPixelInfoPixel(image,&destination,q);
q+=GetPixelChannels(image);
for (i=0; i < (step-1); i++)
{
- SetPixelPixelInfo(image,&background,q);
+ SetPixelInfoPixel(image,&background,q);
q+=GetPixelChannels(image);
}
break;
{
p-=GetPixelChannels(image);
q-=GetPixelChannels(image);
- if ((size_t) (y_offset+height+step-i) >= image->rows)
+ if ((size_t) (y_offset+height+step-i) > image->rows)
continue;
- SetPixelInfo(image,p,&source);
- CompositePixelInfoAreaBlend(&pixel,(MagickRealType) pixel.alpha,
- &source,(MagickRealType) GetPixelAlpha(image,p),area,
+ GetPixelInfoPixel(image,p,&source);
+ CompositePixelInfoAreaBlend(&pixel,(double) pixel.alpha,
+ &source,(double) GetPixelAlpha(image,p),area,
&destination);
- SetPixelPixelInfo(image,&destination,q);
- SetPixelInfo(image,p,&pixel);
+ SetPixelInfoPixel(image,&destination,q);
+ GetPixelInfoPixel(image,p,&pixel);
}
- CompositePixelInfoAreaBlend(&pixel,(MagickRealType) pixel.alpha,
- &background,(MagickRealType) background.alpha,area,&destination);
+ CompositePixelInfoAreaBlend(&pixel,(double) pixel.alpha,
+ &background,(double) background.alpha,area,&destination);
q-=GetPixelChannels(image);
- SetPixelPixelInfo(image,&destination,q);
+ SetPixelInfoPixel(image,&destination,q);
for (i=0; i < (step-1); i++)
{
q-=GetPixelChannels(image);
- SetPixelPixelInfo(image,&background,q);
+ SetPixelInfoPixel(image,&background,q);
}
break;
}
proceed;
#if defined(MAGICKCORE_OPENMP_SUPPORT)
- #pragma omp critical (MagickCore_YShearImage)
+ #pragma omp critical (MagickCore_YShearImage)
#endif
proceed=SetImageProgress(image,YShearImageTag,progress++,image->rows);
if (proceed == MagickFalse)
% %
% %
% %
-% R o t a t e I m a g e %
+% S h e a r I m a g e %
% %
% %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
-% RotateImage() creates a new image that is a rotated copy of an existing
-% one. Positive angles rotate counter-clockwise (right-hand rule), while
-% negative angles rotate clockwise. Rotated images are usually larger than
-% the originals and have 'empty' triangular corners. X axis. Empty
-% triangles left over from shearing the image are filled with the background
-% color defined by member 'background_color' of the image. RotateImage
-% allocates the memory necessary for the new Image structure and returns a
-% pointer to the new image.
+% ShearImage() creates a new image that is a shear_image copy of an existing
+% one. Shearing slides one edge of an image along the X or Y axis, creating
+% a parallelogram. An X direction shear slides an edge along the X axis,
+% while a Y direction shear slides an edge along the Y axis. The amount of
+% the shear is controlled by a shear angle. For X direction shears, x_shear
+% is measured relative to the Y axis, and similarly, for Y direction shears
+% y_shear is measured relative to the X axis. Empty triangles left over from
+% shearing the image are filled with the background color defined by member
+% 'background_color' of the image.. ShearImage() allocates the memory
+% necessary for the new Image structure and returns a pointer to the new image.
%
-% RotateImage() is based on the paper "A Fast Algorithm for General
-% Raster Rotatation" by Alan W. Paeth. RotateImage is adapted from a similar
-% method based on the Paeth paper written by Michael Halle of the Spatial
-% Imaging Group, MIT Media Lab.
+% ShearImage() is based on the paper "A Fast Algorithm for General Raster
+% Rotatation" by Alan W. Paeth.
%
-% The format of the RotateImage method is:
+% The format of the ShearImage method is:
%
-% Image *RotateImage(const Image *image,const double degrees,
-% ExceptionInfo *exception)
+% Image *ShearImage(const Image *image,const double x_shear,
+% const double y_shear,ExceptionInfo *exception)
%
% A description of each parameter follows.
%
% o image: the image.
%
-% o degrees: Specifies the number of degrees to rotate the image.
+% o x_shear, y_shear: Specifies the number of degrees to shear the image.
%
% o exception: return any errors or warnings in this structure.
%
*/
-MagickExport Image *RotateImage(const Image *image,const double degrees,
- ExceptionInfo *exception)
+MagickExport Image *ShearImage(const Image *image,const double x_shear,
+ const double y_shear,ExceptionInfo *exception)
{
Image
*integral_image,
- *rotate_image;
+ *shear_image;
MagickBooleanType
status;
- MagickRealType
- angle;
-
PointInfo
shear;
RectangleInfo
- border_info;
-
- size_t
- height,
- rotations,
- width,
- y_width;
+ border_info,
+ bounds;
- ssize_t
- x_offset,
- y_offset;
-
- /*
- Adjust rotation angle.
- */
assert(image != (Image *) NULL);
assert(image->signature == MagickSignature);
if (image->debug != MagickFalse)
(void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
assert(exception != (ExceptionInfo *) NULL);
assert(exception->signature == MagickSignature);
- angle=degrees;
- while (angle < -45.0)
- angle+=360.0;
- for (rotations=0; angle > 45.0; rotations++)
- angle-=90.0;
- rotations%=4;
+ if ((x_shear != 0.0) && (fmod(x_shear,90.0) == 0.0))
+ ThrowImageException(ImageError,"AngleIsDiscontinuous");
+ if ((y_shear != 0.0) && (fmod(y_shear,90.0) == 0.0))
+ ThrowImageException(ImageError,"AngleIsDiscontinuous");
/*
- Calculate shear equations.
+ Initialize shear angle.
*/
- integral_image=IntegralRotateImage(image,rotations,exception);
+ integral_image=CloneImage(image,0,0,MagickTrue,exception);
if (integral_image == (Image *) NULL)
ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
- shear.x=(-tan((double) DegreesToRadians(angle)/2.0));
- shear.y=sin((double) DegreesToRadians(angle));
+ shear.x=(-tan(DegreesToRadians(fmod(x_shear,360.0))));
+ shear.y=tan(DegreesToRadians(fmod(y_shear,360.0)));
if ((shear.x == 0.0) && (shear.y == 0.0))
return(integral_image);
if (SetImageStorageClass(integral_image,DirectClass,exception) == MagickFalse)
integral_image=DestroyImage(integral_image);
return(integral_image);
}
- if (integral_image->matte == MagickFalse)
+ if (integral_image->alpha_trait != BlendPixelTrait)
(void) SetImageAlphaChannel(integral_image,OpaqueAlphaChannel,exception);
/*
Compute image size.
*/
- width=image->columns;
- height=image->rows;
- if ((rotations == 1) || (rotations == 3))
- {
- width=image->rows;
- height=image->columns;
- }
- y_width=width+(ssize_t) floor(fabs(shear.x)*height+0.5);
- x_offset=(ssize_t) ceil((double) width+((fabs(shear.y)*height)-width)/2.0-
- 0.5);
- y_offset=(ssize_t) ceil((double) height+((fabs(shear.y)*y_width)-height)/2.0-
- 0.5);
+ bounds.width=image->columns+(ssize_t) floor(fabs(shear.x)*image->rows+0.5);
+ bounds.x=(ssize_t) ceil((double) image->columns+((fabs(shear.x)*image->rows)-
+ image->columns)/2.0-0.5);
+ bounds.y=(ssize_t) ceil((double) image->rows+((fabs(shear.y)*bounds.width)-
+ image->rows)/2.0-0.5);
/*
- Surround image with a border.
+ Surround image with border.
*/
integral_image->border_color=integral_image->background_color;
integral_image->compose=CopyCompositeOp;
- border_info.width=(size_t) x_offset;
- border_info.height=(size_t) y_offset;
- rotate_image=BorderImage(integral_image,&border_info,image->compose,
- exception);
+ border_info.width=(size_t) bounds.x;
+ border_info.height=(size_t) bounds.y;
+ shear_image=BorderImage(integral_image,&border_info,image->compose,exception);
integral_image=DestroyImage(integral_image);
- if (rotate_image == (Image *) NULL)
+ if (shear_image == (Image *) NULL)
ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
/*
- Rotate the image.
+ Shear the image.
*/
- status=XShearImage(rotate_image,shear.x,width,height,x_offset,(ssize_t)
- (rotate_image->rows-height)/2,exception);
- if (status == MagickFalse)
- {
- rotate_image=DestroyImage(rotate_image);
- return((Image *) NULL);
- }
- status=YShearImage(rotate_image,shear.y,y_width,height,(ssize_t)
- (rotate_image->columns-y_width)/2,y_offset,exception);
+ if (shear_image->alpha_trait != BlendPixelTrait)
+ (void) SetImageAlphaChannel(shear_image,OpaqueAlphaChannel,exception);
+ status=XShearImage(shear_image,shear.x,image->columns,image->rows,bounds.x,
+ (ssize_t) (shear_image->rows-image->rows)/2,exception);
if (status == MagickFalse)
{
- rotate_image=DestroyImage(rotate_image);
+ shear_image=DestroyImage(shear_image);
return((Image *) NULL);
}
- status=XShearImage(rotate_image,shear.x,y_width,rotate_image->rows,(ssize_t)
- (rotate_image->columns-y_width)/2,0,exception);
+ status=YShearImage(shear_image,shear.y,bounds.width,image->rows,(ssize_t)
+ (shear_image->columns-bounds.width)/2,bounds.y,exception);
if (status == MagickFalse)
{
- rotate_image=DestroyImage(rotate_image);
+ shear_image=DestroyImage(shear_image);
return((Image *) NULL);
}
- status=CropToFitImage(&rotate_image,shear.x,shear.y,(MagickRealType) width,
- (MagickRealType) height,MagickTrue,exception);
+ status=CropToFitImage(&shear_image,shear.x,shear.y,(MagickRealType)
+ image->columns,(MagickRealType) image->rows,MagickFalse,exception);
+ shear_image->compose=image->compose;
+ shear_image->page.width=0;
+ shear_image->page.height=0;
if (status == MagickFalse)
- {
- rotate_image=DestroyImage(rotate_image);
- return((Image *) NULL);
- }
- rotate_image->compose=image->compose;
- rotate_image->page.width=0;
- rotate_image->page.height=0;
- return(rotate_image);
+ shear_image=DestroyImage(shear_image);
+ return(shear_image);
}
\f
/*
% %
% %
% %
-% S h e a r I m a g e %
+% S h e a r R o t a t e I m a g e %
% %
% %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
-% ShearImage() creates a new image that is a shear_image copy of an existing
-% one. Shearing slides one edge of an image along the X or Y axis, creating
-% a parallelogram. An X direction shear slides an edge along the X axis,
-% while a Y direction shear slides an edge along the Y axis. The amount of
-% the shear is controlled by a shear angle. For X direction shears, x_shear
-% is measured relative to the Y axis, and similarly, for Y direction shears
-% y_shear is measured relative to the X axis. Empty triangles left over from
-% shearing the image are filled with the background color defined by member
-% 'background_color' of the image.. ShearImage() allocates the memory
-% necessary for the new Image structure and returns a pointer to the new image.
+% ShearRotateImage() creates a new image that is a rotated copy of an existing
+% one. Positive angles rotate counter-clockwise (right-hand rule), while
+% negative angles rotate clockwise. Rotated images are usually larger than
+% the originals and have 'empty' triangular corners. X axis. Empty
+% triangles left over from shearing the image are filled with the background
+% color defined by member 'background_color' of the image. ShearRotateImage
+% allocates the memory necessary for the new Image structure and returns a
+% pointer to the new image.
%
-% ShearImage() is based on the paper "A Fast Algorithm for General Raster
-% Rotatation" by Alan W. Paeth.
+% ShearRotateImage() is based on the paper "A Fast Algorithm for General
+% Raster Rotatation" by Alan W. Paeth. ShearRotateImage is adapted from a
+% similar method based on the Paeth paper written by Michael Halle of the
+% Spatial Imaging Group, MIT Media Lab.
%
-% The format of the ShearImage method is:
+% The format of the ShearRotateImage method is:
%
-% Image *ShearImage(const Image *image,const double x_shear,
-% const double y_shear,ExceptionInfo *exception)
+% Image *ShearRotateImage(const Image *image,const double degrees,
+% ExceptionInfo *exception)
%
% A description of each parameter follows.
%
% o image: the image.
%
-% o x_shear, y_shear: Specifies the number of degrees to shear the image.
+% o degrees: Specifies the number of degrees to rotate the image.
%
% o exception: return any errors or warnings in this structure.
%
*/
-MagickExport Image *ShearImage(const Image *image,const double x_shear,
- const double y_shear,ExceptionInfo *exception)
+MagickExport Image *ShearRotateImage(const Image *image,const double degrees,
+ ExceptionInfo *exception)
{
Image
*integral_image,
- *shear_image;
-
- ssize_t
- x_offset,
- y_offset;
+ *rotate_image;
MagickBooleanType
status;
+ MagickRealType
+ angle;
+
PointInfo
shear;
RectangleInfo
- border_info;
+ border_info,
+ bounds;
size_t
- y_width;
+ height,
+ rotations,
+ shear_width,
+ width;
+ /*
+ Adjust rotation angle.
+ */
assert(image != (Image *) NULL);
assert(image->signature == MagickSignature);
if (image->debug != MagickFalse)
(void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
assert(exception != (ExceptionInfo *) NULL);
assert(exception->signature == MagickSignature);
- if ((x_shear != 0.0) && (fmod(x_shear,90.0) == 0.0))
- ThrowImageException(ImageError,"AngleIsDiscontinuous");
- if ((y_shear != 0.0) && (fmod(y_shear,90.0) == 0.0))
- ThrowImageException(ImageError,"AngleIsDiscontinuous");
+ angle=degrees;
+ while (angle < -45.0)
+ angle+=360.0;
+ for (rotations=0; angle > 45.0; rotations++)
+ angle-=90.0;
+ rotations%=4;
/*
- Initialize shear angle.
+ Calculate shear equations.
*/
- integral_image=CloneImage(image,0,0,MagickTrue,exception);
+ integral_image=IntegralRotateImage(image,rotations,exception);
if (integral_image == (Image *) NULL)
ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
- shear.x=(-tan(DegreesToRadians(fmod(x_shear,360.0))));
- shear.y=tan(DegreesToRadians(fmod(y_shear,360.0)));
+ shear.x=(-tan((double) DegreesToRadians(angle)/2.0));
+ shear.y=sin((double) DegreesToRadians(angle));
if ((shear.x == 0.0) && (shear.y == 0.0))
return(integral_image);
if (SetImageStorageClass(integral_image,DirectClass,exception) == MagickFalse)
integral_image=DestroyImage(integral_image);
return(integral_image);
}
- if (integral_image->matte == MagickFalse)
+ if (integral_image->alpha_trait != BlendPixelTrait)
(void) SetImageAlphaChannel(integral_image,OpaqueAlphaChannel,exception);
/*
- Compute image size.
+ Compute maximum bounds for 3 shear operations.
*/
- y_width=image->columns+(ssize_t) floor(fabs(shear.x)*image->rows+0.5);
- x_offset=(ssize_t) ceil((double) image->columns+((fabs(shear.x)*image->rows)-
- image->columns)/2.0-0.5);
- y_offset=(ssize_t) ceil((double) image->rows+((fabs(shear.y)*y_width)-
- image->rows)/2.0-0.5);
+ width=integral_image->columns;
+ height=integral_image->rows;
+ bounds.width=(size_t) floor(fabs((double) height*shear.x)+width+0.5);
+ bounds.height=(size_t) floor(fabs((double) bounds.width*shear.y)+height+0.5);
+ shear_width=(size_t) floor(fabs((double) bounds.height*shear.x)+
+ bounds.width+0.5);
+ bounds.x=(ssize_t) floor((double) ((shear_width > bounds.width) ? width :
+ bounds.width-shear_width+2)/2.0+0.5);
+ bounds.y=(ssize_t) floor(((double) bounds.height-height+2)/2.0+0.5);
/*
- Surround image with border.
+ Surround image with a border.
*/
integral_image->border_color=integral_image->background_color;
integral_image->compose=CopyCompositeOp;
- border_info.width=(size_t) x_offset;
- border_info.height=(size_t) y_offset;
- shear_image=BorderImage(integral_image,&border_info,image->compose,exception);
+ border_info.width=(size_t) bounds.x;
+ border_info.height=(size_t) bounds.y;
+ rotate_image=BorderImage(integral_image,&border_info,image->compose,
+ exception);
integral_image=DestroyImage(integral_image);
- if (shear_image == (Image *) NULL)
+ if (rotate_image == (Image *) NULL)
ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
/*
- Shear the image.
+ Rotate the image.
*/
- if (shear_image->matte == MagickFalse)
- (void) SetImageAlphaChannel(shear_image,OpaqueAlphaChannel,exception);
- status=XShearImage(shear_image,shear.x,image->columns,image->rows,x_offset,
- (ssize_t) (shear_image->rows-image->rows)/2,exception);
+ status=XShearImage(rotate_image,shear.x,width,height,bounds.x,(ssize_t)
+ (rotate_image->rows-height)/2,exception);
if (status == MagickFalse)
{
- shear_image=DestroyImage(shear_image);
+ rotate_image=DestroyImage(rotate_image);
return((Image *) NULL);
}
- status=YShearImage(shear_image,shear.y,y_width,image->rows,(ssize_t)
- (shear_image->columns-y_width)/2,y_offset,exception);
+ status=YShearImage(rotate_image,shear.y,bounds.width,height,(ssize_t)
+ (rotate_image->columns-bounds.width)/2,bounds.y,exception);
if (status == MagickFalse)
{
- shear_image=DestroyImage(shear_image);
+ rotate_image=DestroyImage(rotate_image);
return((Image *) NULL);
}
- status=CropToFitImage(&shear_image,shear.x,shear.y,(MagickRealType)
- image->columns,(MagickRealType) image->rows,MagickFalse,exception);
+ status=XShearImage(rotate_image,shear.x,bounds.width,bounds.height,(ssize_t)
+ (rotate_image->columns-bounds.width)/2,(ssize_t) (rotate_image->rows-
+ bounds.height)/2,exception);
if (status == MagickFalse)
{
- shear_image=DestroyImage(shear_image);
+ rotate_image=DestroyImage(rotate_image);
return((Image *) NULL);
}
- shear_image->compose=image->compose;
- shear_image->page.width=0;
- shear_image->page.height=0;
- return(shear_image);
+ status=CropToFitImage(&rotate_image,shear.x,shear.y,(MagickRealType) width,
+ (MagickRealType) height,MagickTrue,exception);
+ rotate_image->compose=image->compose;
+ rotate_image->page.width=0;
+ rotate_image->page.height=0;
+ if (status == MagickFalse)
+ rotate_image=DestroyImage(rotate_image);
+ return(rotate_image);
}