47#include "MagickCore/studio.h"
48#include "MagickCore/artifact.h"
49#include "MagickCore/attribute.h"
50#include "MagickCore/blob-private.h"
51#include "MagickCore/cache-private.h"
52#include "MagickCore/channel.h"
53#include "MagickCore/color-private.h"
54#include "MagickCore/colorspace-private.h"
55#include "MagickCore/composite.h"
56#include "MagickCore/composite-private.h"
57#include "MagickCore/decorate.h"
58#include "MagickCore/distort.h"
59#include "MagickCore/draw.h"
60#include "MagickCore/exception.h"
61#include "MagickCore/exception-private.h"
62#include "MagickCore/gem.h"
63#include "MagickCore/geometry.h"
64#include "MagickCore/image.h"
65#include "MagickCore/image-private.h"
66#include "MagickCore/matrix.h"
67#include "MagickCore/memory_.h"
68#include "MagickCore/list.h"
69#include "MagickCore/monitor.h"
70#include "MagickCore/monitor-private.h"
71#include "MagickCore/nt-base-private.h"
72#include "MagickCore/pixel-accessor.h"
73#include "MagickCore/profile-private.h"
74#include "MagickCore/quantum.h"
75#include "MagickCore/resource_.h"
76#include "MagickCore/shear.h"
77#include "MagickCore/statistic.h"
78#include "MagickCore/string_.h"
79#include "MagickCore/string-private.h"
80#include "MagickCore/thread-private.h"
81#include "MagickCore/threshold.h"
82#include "MagickCore/transform.h"
114static MagickBooleanType CropToFitImage(Image **image,
115 const double x_shear,
const double y_shear,
116 const double width,
const double height,
117 const MagickBooleanType rotate,ExceptionInfo *exception)
137 extent[0].x=(double) (-width/2.0);
138 extent[0].y=(double) (-height/2.0);
139 extent[1].x=(double) width/2.0;
140 extent[1].y=(double) (-height/2.0);
141 extent[2].x=(double) (-width/2.0);
142 extent[2].y=(double) height/2.0;
143 extent[3].x=(double) width/2.0;
144 extent[3].y=(double) height/2.0;
145 for (i=3; i >= 0; i--)
147 extent[i].x+=x_shear*extent[i].y;
148 extent[i].y+=y_shear*extent[i].x;
149 if (rotate != MagickFalse)
150 extent[i].x+=x_shear*extent[i].y;
151 extent[i].x+=(double) (*image)->columns/2.0;
152 extent[i].y+=(double) (*image)->rows/2.0;
156 for (i=1; i < 4; i++)
158 if (min.x > extent[i].x)
160 if (min.y > extent[i].y)
162 if (max.x < extent[i].x)
164 if (max.y < extent[i].y)
167 geometry.x=CastDoubleToSsizeT(ceil(min.x-0.5));
168 geometry.y=CastDoubleToSsizeT(ceil(min.y-0.5));
169 geometry.width=(size_t) CastDoubleToSsizeT(floor(max.x-min.x+0.5));
170 geometry.height=(size_t) CastDoubleToSsizeT(floor(max.y-min.y+0.5));
172 (void) ParseAbsoluteGeometry(
"0x0+0+0",&(*image)->page);
173 crop_image=CropImage(*image,&geometry,exception);
174 if (crop_image == (Image *) NULL)
176 crop_image->page=page;
177 *image=DestroyImage(*image);
217static void RadonProjection(MatrixInfo *source_matrices,
218 MatrixInfo *destination_matrices,
const ssize_t sign,
size_t *projection)
232 q=destination_matrices;
233 for (step=1; step < GetMatrixColumns(p); step*=2)
235 for (x=0; x < (ssize_t) GetMatrixColumns(p); x+=2*(ssize_t) step)
245 for (i=0; i < (ssize_t) step; i++)
247 for (y=0; y < ((ssize_t) GetMatrixRows(p)-i-1); y++)
249 if (GetMatrixElement(p,x+i,y,&element) == MagickFalse)
251 if (GetMatrixElement(p,x+i+(ssize_t) step,y+i,&neighbor) == MagickFalse)
254 if (SetMatrixElement(q,x+2*i,y,&neighbor) == MagickFalse)
256 if (GetMatrixElement(p,x+i+(ssize_t) step,y+i+1,&neighbor) == MagickFalse)
259 if (SetMatrixElement(q,x+2*i+1,y,&neighbor) == MagickFalse)
262 for ( ; y < ((ssize_t) GetMatrixRows(p)-i); y++)
264 if (GetMatrixElement(p,x+i,y,&element) == MagickFalse)
266 if (GetMatrixElement(p,x+i+(ssize_t) step,y+i,&neighbor) == MagickFalse)
269 if (SetMatrixElement(q,x+2*i,y,&neighbor) == MagickFalse)
271 if (SetMatrixElement(q,x+2*i+1,y,&element) == MagickFalse)
274 for ( ; y < (ssize_t) GetMatrixRows(p); y++)
276 if (GetMatrixElement(p,x+i,y,&element) == MagickFalse)
278 if (SetMatrixElement(q,x+2*i,y,&element) == MagickFalse)
280 if (SetMatrixElement(q,x+2*i+1,y,&element) == MagickFalse)
289#if defined(MAGICKCORE_OPENMP_SUPPORT)
290 #pragma omp parallel for schedule(static) \
291 num_threads((int) GetMagickResourceLimit(ThreadResource))
293 for (x=0; x < (ssize_t) GetMatrixColumns(p); x++)
302 for (y=0; y < (ssize_t) (GetMatrixRows(p)-1); y++)
311 if (GetMatrixElement(p,x,y,&element) == MagickFalse)
313 if (GetMatrixElement(p,x,y+1,&neighbor) == MagickFalse)
315 delta=(ssize_t) element-(ssize_t) neighbor;
316 sum+=(size_t) (delta*delta);
318 projection[(ssize_t) GetMatrixColumns(p)+sign*x-1]=sum;
322static MagickBooleanType RadonTransform(
const Image *image,
323 const double threshold,
size_t *projection,ExceptionInfo *exception)
329 *destination_matrices,
349 for (width=1; width < ((image->columns+7)/8); width<<=1) ;
350 source_matrices=AcquireMatrixInfo(width,image->rows,
sizeof(
unsigned short),
352 destination_matrices=AcquireMatrixInfo(width,image->rows,
353 sizeof(
unsigned short),exception);
354 if ((source_matrices == (MatrixInfo *) NULL) ||
355 (destination_matrices == (MatrixInfo *) NULL))
357 if (destination_matrices != (MatrixInfo *) NULL)
358 destination_matrices=DestroyMatrixInfo(destination_matrices);
359 if (source_matrices != (MatrixInfo *) NULL)
360 source_matrices=DestroyMatrixInfo(source_matrices);
363 if (NullMatrix(source_matrices) == MagickFalse)
365 destination_matrices=DestroyMatrixInfo(destination_matrices);
366 source_matrices=DestroyMatrixInfo(source_matrices);
369 for (j=0; j < 256; j++)
372 for (count=0; c != 0; c>>=1)
374 bits[j]=(
unsigned short) count;
377 image_view=AcquireVirtualCacheView(image,exception);
378#if defined(MAGICKCORE_OPENMP_SUPPORT)
379 #pragma omp parallel for schedule(static) shared(status) \
380 magick_number_threads(image,image,image->rows,2)
382 for (y=0; y < (ssize_t) image->rows; y++)
398 if (status == MagickFalse)
400 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
401 if (p == (
const Quantum *) NULL)
408 i=(ssize_t) (image->columns+7)/8;
409 for (x=0; x < (ssize_t) image->columns; x++)
412 if (((MagickRealType) GetPixelRed(image,p) < threshold) ||
413 ((MagickRealType) GetPixelGreen(image,p) < threshold) ||
414 ((MagickRealType) GetPixelBlue(image,p) < threshold))
420 (void) SetMatrixElement(source_matrices,--i,y,&value);
424 p+=(ptrdiff_t) GetPixelChannels(image);
430 (void) SetMatrixElement(source_matrices,--i,y,&value);
433 RadonProjection(source_matrices,destination_matrices,-1,projection);
434 (void) NullMatrix(source_matrices);
435#if defined(MAGICKCORE_OPENMP_SUPPORT)
436 #pragma omp parallel for schedule(static) shared(status) \
437 magick_number_threads(image,image,image->rows,2)
439 for (y=0; y < (ssize_t) image->rows; y++)
455 if (status == MagickFalse)
457 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
458 if (p == (
const Quantum *) NULL)
466 for (x=0; x < (ssize_t) image->columns; x++)
469 if (((MagickRealType) GetPixelRed(image,p) < threshold) ||
470 ((MagickRealType) GetPixelGreen(image,p) < threshold) ||
471 ((MagickRealType) GetPixelBlue(image,p) < threshold))
477 (void) SetMatrixElement(source_matrices,i++,y,&value);
481 p+=(ptrdiff_t) GetPixelChannels(image);
487 (void) SetMatrixElement(source_matrices,i++,y,&value);
490 RadonProjection(source_matrices,destination_matrices,1,projection);
491 image_view=DestroyCacheView(image_view);
492 destination_matrices=DestroyMatrixInfo(destination_matrices);
493 source_matrices=DestroyMatrixInfo(source_matrices);
497static void GetImageBackgroundColor(Image *image,
const ssize_t offset,
498 ExceptionInfo *exception)
517 GetPixelInfo(image,&background);
519 image_view=AcquireVirtualCacheView(image,exception);
520 for (y=0; y < (ssize_t) image->rows; y++)
528 if ((y >= offset) && (y < ((ssize_t) image->rows-offset)))
530 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
531 if (p == (
const Quantum *) NULL)
533 for (x=0; x < (ssize_t) image->columns; x++)
535 if ((x >= offset) && (x < ((ssize_t) image->columns-offset)))
537 background.red+=QuantumScale*(double) GetPixelRed(image,p);
538 background.green+=QuantumScale*(double) GetPixelGreen(image,p);
539 background.blue+=QuantumScale*(double) GetPixelBlue(image,p);
540 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
541 background.alpha+=QuantumScale*(double) GetPixelAlpha(image,p);
543 p+=(ptrdiff_t) GetPixelChannels(image);
546 image_view=DestroyCacheView(image_view);
547 image->background_color.red=(double) ClampToQuantum((
double) QuantumRange*
548 (
double) background.red/count);
549 image->background_color.green=(double) ClampToQuantum((
double) QuantumRange*
550 (
double) background.green/count);
551 image->background_color.blue=(double) ClampToQuantum((
double) QuantumRange*
552 (
double) background.blue/count);
553 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
554 image->background_color.alpha=(double) ClampToQuantum((
double) QuantumRange*
555 (
double) background.alpha/count);
558MagickExport Image *DeskewImage(
const Image *image,
const double threshold,
559 ExceptionInfo *exception)
594 for (width=1; width < ((image->columns+7)/8); width<<=1) ;
595 projection=(
size_t *) AcquireQuantumMemory((
size_t) (2*width-1),
596 sizeof(*projection));
597 if (projection == (
size_t *) NULL)
598 ThrowImageException(ResourceLimitError,
"MemoryAllocationFailed");
599 status=RadonTransform(image,threshold,projection,exception);
600 if (status == MagickFalse)
602 projection=(
size_t *) RelinquishMagickMemory(projection);
603 ThrowImageException(ResourceLimitError,
"MemoryAllocationFailed");
607 for (i=0; i < (ssize_t) (2*width-1); i++)
609 if (projection[i] > max_projection)
611 skew=i-(ssize_t) width+1;
612 max_projection=projection[i];
615 projection=(
size_t *) RelinquishMagickMemory(projection);
616 degrees=RadiansToDegrees(-atan((
double) skew/width/8));
617 if (image->debug != MagickFalse)
618 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
619 " Deskew angle: %g",degrees);
623 clone_image=CloneImage(image,0,0,MagickTrue,exception);
624 if (clone_image == (Image *) NULL)
625 return((Image *) NULL);
628 angle[MagickPathExtent];
630 (void) FormatLocaleString(angle,MagickPathExtent,
"%.20g",degrees);
631 (void) SetImageArtifact(clone_image,
"deskew:angle",angle);
633 (void) SetImageVirtualPixelMethod(clone_image,BackgroundVirtualPixelMethod,
635 affine_matrix.sx=cos(DegreesToRadians(fmod((
double) degrees,360.0)));
636 affine_matrix.rx=sin(DegreesToRadians(fmod((
double) degrees,360.0)));
637 affine_matrix.ry=(-sin(DegreesToRadians(fmod((
double) degrees,360.0))));
638 affine_matrix.sy=cos(DegreesToRadians(fmod((
double) degrees,360.0)));
639 affine_matrix.tx=0.0;
640 affine_matrix.ty=0.0;
641 artifact=GetImageArtifact(image,
"deskew:auto-crop");
642 if (IsStringTrue(artifact) == MagickFalse)
644 deskew_image=AffineTransformImage(clone_image,&affine_matrix,exception);
645 clone_image=DestroyImage(clone_image);
646 return(deskew_image);
651 GetImageBackgroundColor(clone_image,(ssize_t) StringToLong(artifact),
653 deskew_image=AffineTransformImage(clone_image,&affine_matrix,exception);
654 clone_image=DestroyImage(clone_image);
655 if (deskew_image == (Image *) NULL)
656 return((Image *) NULL);
657 median_image=StatisticImage(deskew_image,MedianStatistic,3,3,exception);
658 if (median_image == (Image *) NULL)
660 deskew_image=DestroyImage(deskew_image);
661 return((Image *) NULL);
663 geometry=GetImageBoundingBox(median_image,exception);
664 median_image=DestroyImage(median_image);
665 if (image->debug != MagickFalse)
666 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
" Deskew geometry: "
667 "%.20gx%.20g%+.20g%+.20g",(
double) geometry.width,(
double)
668 geometry.height,(
double) geometry.x,(
double) geometry.y);
669 crop_image=CropImage(deskew_image,&geometry,exception);
670 deskew_image=DestroyImage(deskew_image);
701MagickExport Image *IntegralRotateImage(
const Image *image,
size_t rotations,
702 ExceptionInfo *exception)
704#define RotateImageTag "Rotate/Image"
725 assert(image != (Image *) NULL);
733 rotate_image=CloneImage(image,0,0,MagickTrue,exception);
738 rotate_image=CloneImage(image,image->columns,image->rows,MagickTrue,
745 rotate_image=CloneImage(image,image->rows,image->columns,MagickTrue,
750 if (rotate_image == (Image *) NULL)
751 return((Image *) NULL);
753 return(rotate_image);
759 image_view=AcquireVirtualCacheView(image,exception);
760 rotate_view=AcquireAuthenticCacheView(rotate_image,exception);
775 GetPixelCacheTileSize(image,&tile_width,&tile_height);
776 tile_width=image->columns;
777#if defined(MAGICKCORE_OPENMP_SUPPORT)
778 #pragma omp parallel for schedule(static) shared(status) \
779 magick_number_threads(image,rotate_image,image->rows/tile_height,2)
781 for (tile_y=0; tile_y < (ssize_t) image->rows; tile_y+=(ssize_t) tile_height)
786 if (status == MagickFalse)
789 for ( ; tile_x < (ssize_t) image->columns; tile_x+=(ssize_t) tile_width)
808 if ((tile_width+(
size_t) tile_x) > image->columns)
809 width=(size_t) ((ssize_t) tile_width-(tile_x+(ssize_t) tile_width-
810 (ssize_t) image->columns));
812 if ((tile_height+(
size_t) tile_y) > image->rows)
813 height=(size_t) ((ssize_t) tile_height-(tile_y+(ssize_t)
814 tile_height-(ssize_t) image->rows));
815 p=GetCacheViewVirtualPixels(image_view,tile_x,tile_y,width,height,
817 if (p == (
const Quantum *) NULL)
822 for (y=0; y < (ssize_t) width; y++)
825 *magick_restrict tile_pixels;
830 if (status == MagickFalse)
832 q=QueueCacheViewAuthenticPixels(rotate_view,(ssize_t)
833 rotate_image->columns-(tile_y+(ssize_t) height),y+tile_x,height,
835 if (q == (Quantum *) NULL)
840 tile_pixels=p+(((ssize_t) height-1)*(ssize_t) width+y)*(ssize_t)
841 GetPixelChannels(image);
842 for (x=0; x < (ssize_t) height; x++)
847 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
849 PixelChannel channel = GetPixelChannelChannel(image,i);
850 PixelTrait traits = GetPixelChannelTraits(image,channel);
851 PixelTrait rotate_traits = GetPixelChannelTraits(rotate_image,
853 if ((traits == UndefinedPixelTrait) ||
854 (rotate_traits == UndefinedPixelTrait))
856 SetPixelChannel(rotate_image,channel,tile_pixels[i],q);
858 tile_pixels-=width*GetPixelChannels(image);
859 q+=(ptrdiff_t) GetPixelChannels(rotate_image);
861 sync=SyncCacheViewAuthenticPixels(rotate_view,exception);
862 if (sync == MagickFalse)
866 if (image->progress_monitor != (MagickProgressMonitor) NULL)
871 proceed=SetImageProgress(image,RotateImageTag,
872 progress+=(MagickOffsetType) tile_height,image->rows);
873 if (proceed == MagickFalse)
877 (void) SetImageProgress(image,RotateImageTag,(MagickOffsetType)
878 image->rows-1,image->rows);
879 Swap(page.width,page.height);
882 page.x=(ssize_t) page.width-(ssize_t) rotate_image->columns-page.x;
893#if defined(MAGICKCORE_OPENMP_SUPPORT)
894 #pragma omp parallel for schedule(static) shared(status) \
895 magick_number_threads(image,rotate_image,image->rows,2)
897 for (y=0; y < (ssize_t) image->rows; y++)
911 if (status == MagickFalse)
913 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
914 q=QueueCacheViewAuthenticPixels(rotate_view,0,(ssize_t) image->rows-y-1,
915 image->columns,1,exception);
916 if ((p == (
const Quantum *) NULL) || (q == (Quantum *) NULL))
921 q+=(ptrdiff_t) GetPixelChannels(rotate_image)*image->columns;
922 for (x=0; x < (ssize_t) image->columns; x++)
927 q-=GetPixelChannels(rotate_image);
928 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
930 PixelChannel channel = GetPixelChannelChannel(image,i);
931 PixelTrait traits = GetPixelChannelTraits(image,channel);
932 PixelTrait rotate_traits = GetPixelChannelTraits(rotate_image,
934 if ((traits == UndefinedPixelTrait) ||
935 (rotate_traits == UndefinedPixelTrait))
937 SetPixelChannel(rotate_image,channel,p[i],q);
939 p+=(ptrdiff_t) GetPixelChannels(image);
941 sync=SyncCacheViewAuthenticPixels(rotate_view,exception);
942 if (sync == MagickFalse)
944 if (image->progress_monitor != (MagickProgressMonitor) NULL)
949 proceed=SetImageProgress(image,RotateImageTag,progress++,
951 if (proceed == MagickFalse)
955 (void) SetImageProgress(image,RotateImageTag,(MagickOffsetType)
956 image->rows-1,image->rows);
958 page.x=(ssize_t) page.width-(ssize_t) rotate_image->columns-page.x;
959 if (page.height != 0)
960 page.y=(ssize_t) page.height-(ssize_t) rotate_image->rows-page.y;
975 GetPixelCacheTileSize(image,&tile_width,&tile_height);
976 tile_width=image->columns;
977#if defined(MAGICKCORE_OPENMP_SUPPORT)
978 #pragma omp parallel for schedule(static) shared(status) \
979 magick_number_threads(image,rotate_image,image->rows/tile_height,2)
981 for (tile_y=0; tile_y < (ssize_t) image->rows; tile_y+=(ssize_t) tile_height)
986 if (status == MagickFalse)
989 for ( ; tile_x < (ssize_t) image->columns; tile_x+=(ssize_t) tile_width)
1008 if ((tile_width+(
size_t) tile_x) > image->columns)
1009 width=(size_t) ((ssize_t) tile_width-(tile_x+(ssize_t) tile_width-
1010 (ssize_t) image->columns));
1012 if ((tile_height+(
size_t) tile_y) > image->rows)
1013 height=(size_t) ((ssize_t) tile_height-(tile_y+(ssize_t)
1014 tile_height-(ssize_t) image->rows));
1015 p=GetCacheViewVirtualPixels(image_view,tile_x,tile_y,width,height,
1017 if (p == (
const Quantum *) NULL)
1022 for (y=0; y < (ssize_t) width; y++)
1025 *magick_restrict tile_pixels;
1030 if (status == MagickFalse)
1032 q=QueueCacheViewAuthenticPixels(rotate_view,tile_y,y+(ssize_t)
1033 rotate_image->rows-(tile_x+(ssize_t) width),height,1,exception);
1034 if (q == (Quantum *) NULL)
1039 tile_pixels=p+(((ssize_t) width-1)-y)*(ssize_t)
1040 GetPixelChannels(image);
1041 for (x=0; x < (ssize_t) height; x++)
1046 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1048 PixelChannel channel = GetPixelChannelChannel(image,i);
1049 PixelTrait traits = GetPixelChannelTraits(image,channel);
1050 PixelTrait rotate_traits = GetPixelChannelTraits(rotate_image,
1052 if ((traits == UndefinedPixelTrait) ||
1053 (rotate_traits == UndefinedPixelTrait))
1055 SetPixelChannel(rotate_image,channel,tile_pixels[i],q);
1057 tile_pixels+=width*GetPixelChannels(image);
1058 q+=(ptrdiff_t) GetPixelChannels(rotate_image);
1060#if defined(MAGICKCORE_OPENMP_SUPPORT)
1061 #pragma omp critical (MagickCore_IntegralRotateImage)
1063 sync=SyncCacheViewAuthenticPixels(rotate_view,exception);
1064 if (sync == MagickFalse)
1068 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1073 proceed=SetImageProgress(image,RotateImageTag,
1074 progress+=(MagickOffsetType) tile_height,image->rows);
1075 if (proceed == MagickFalse)
1079 (void) SetImageProgress(image,RotateImageTag,(MagickOffsetType)
1080 image->rows-1,image->rows);
1081 Swap(page.width,page.height);
1082 Swap(page.x,page.y);
1083 if (page.height != 0)
1084 page.y=(ssize_t) page.height-(ssize_t) rotate_image->rows-page.y;
1090 rotate_view=DestroyCacheView(rotate_view);
1091 image_view=DestroyCacheView(image_view);
1092 rotate_image->type=image->type;
1093 rotate_image->page=page;
1094 if (status != MagickFalse)
1097 transform[MagickPathExtent];
1099 (void) FormatLocaleString(transform,MagickPathExtent,
1100 "rotate %.20gx%.20g %.20g",(
double) image->columns,
1101 (
double) image->rows,(
double) rotations);
1102 AppendImageProfileProperty(rotate_image,
"hdrgm",
"hdrgm:Transform",
1103 transform,exception);
1105 if (status == MagickFalse)
1106 rotate_image=DestroyImage(rotate_image);
1107 return(rotate_image);
1146static MagickBooleanType XShearImage(Image *image,
const double degrees,
1147 const size_t width,
const size_t height,
const ssize_t x_offset,
1148 const ssize_t y_offset,ExceptionInfo *exception)
1150#define XShearImageTag "XShear/Image"
1176 assert(image != (Image *) NULL);
1177 assert(image->signature == MagickCoreSignature);
1178 if (IsEventLogging() != MagickFalse)
1179 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
1181 background=image->background_color;
1183 image_view=AcquireAuthenticCacheView(image,exception);
1184#if defined(MAGICKCORE_OPENMP_SUPPORT)
1185 #pragma omp parallel for schedule(static) shared(progress,status) \
1186 magick_number_threads(image,image,height,1)
1188 for (y=0; y < (ssize_t) height; y++)
1210 if (status == MagickFalse)
1212 p=GetCacheViewAuthenticPixels(image_view,0,y_offset+y,image->columns,1,
1214 if (p == (Quantum *) NULL)
1219 p+=(ptrdiff_t) x_offset*(ssize_t) GetPixelChannels(image);
1220 displacement=degrees*(double) (y-height/2.0);
1221 if (displacement == 0.0)
1223 if (displacement > 0.0)
1227 displacement*=(-1.0);
1230 step=CastDoubleToSsizeT(floor((
double) displacement));
1231 area=(double) (displacement-step);
1234 GetPixelInfo(image,&source);
1235 GetPixelInfo(image,&destination);
1243 if (step > x_offset)
1245 q=p-step*(ssize_t) GetPixelChannels(image);
1246 for (i=0; i < (ssize_t) width; i++)
1248 if ((x_offset+i) < step)
1250 p+=(ptrdiff_t) GetPixelChannels(image);
1251 GetPixelInfoPixel(image,p,&pixel);
1252 q+=(ptrdiff_t) GetPixelChannels(image);
1255 GetPixelInfoPixel(image,p,&source);
1256 CompositePixelInfoAreaBlend(&pixel,(
double) pixel.alpha,
1257 &source,(
double) GetPixelAlpha(image,p),area,&destination);
1258 SetPixelViaPixelInfo(image,&destination,q);
1259 GetPixelInfoPixel(image,p,&pixel);
1260 p+=(ptrdiff_t) GetPixelChannels(image);
1261 q+=(ptrdiff_t) GetPixelChannels(image);
1263 CompositePixelInfoAreaBlend(&pixel,(
double) pixel.alpha,
1264 &background,(
double) background.alpha,area,&destination);
1265 SetPixelViaPixelInfo(image,&destination,q);
1266 q+=(ptrdiff_t) GetPixelChannels(image);
1267 for (i=0; i < (step-1); i++)
1269 SetPixelViaPixelInfo(image,&background,q);
1270 q+=(ptrdiff_t) GetPixelChannels(image);
1279 p+=(ptrdiff_t) width*GetPixelChannels(image);
1280 q=p+step*(ssize_t) GetPixelChannels(image);
1281 for (i=0; i < (ssize_t) width; i++)
1283 p-=(ptrdiff_t)GetPixelChannels(image);
1284 q-=GetPixelChannels(image);
1285 if ((
size_t) (x_offset+(ssize_t) width+step-i) > image->columns)
1287 GetPixelInfoPixel(image,p,&source);
1288 CompositePixelInfoAreaBlend(&pixel,(
double) pixel.alpha,
1289 &source,(
double) GetPixelAlpha(image,p),area,&destination);
1290 SetPixelViaPixelInfo(image,&destination,q);
1291 GetPixelInfoPixel(image,p,&pixel);
1293 CompositePixelInfoAreaBlend(&pixel,(
double) pixel.alpha,
1294 &background,(
double) background.alpha,area,&destination);
1295 q-=GetPixelChannels(image);
1296 SetPixelViaPixelInfo(image,&destination,q);
1297 for (i=0; i < (step-1); i++)
1299 q-=GetPixelChannels(image);
1300 SetPixelViaPixelInfo(image,&background,q);
1305 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1307 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1312#if defined(MAGICKCORE_OPENMP_SUPPORT)
1316 proceed=SetImageProgress(image,XShearImageTag,progress,height);
1317 if (proceed == MagickFalse)
1321 image_view=DestroyCacheView(image_view);
1361static MagickBooleanType YShearImage(Image *image,
const double degrees,
1362 const size_t width,
const size_t height,
const ssize_t x_offset,
1363 const ssize_t y_offset,ExceptionInfo *exception)
1365#define YShearImageTag "YShear/Image"
1391 assert(image != (Image *) NULL);
1392 assert(image->signature == MagickCoreSignature);
1393 if (IsEventLogging() != MagickFalse)
1394 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
1397 background=image->background_color;
1398 image_view=AcquireAuthenticCacheView(image,exception);
1399#if defined(MAGICKCORE_OPENMP_SUPPORT)
1400 #pragma omp parallel for schedule(static) shared(progress,status) \
1401 magick_number_threads(image,image,width,1)
1403 for (x=0; x < (ssize_t) width; x++)
1425 if (status == MagickFalse)
1427 p=GetCacheViewAuthenticPixels(image_view,x_offset+x,0,1,image->rows,
1429 if (p == (Quantum *) NULL)
1434 p+=(ptrdiff_t) y_offset*(ssize_t) GetPixelChannels(image);
1435 displacement=degrees*(double) (x-width/2.0);
1436 if (displacement == 0.0)
1438 if (displacement > 0.0)
1442 displacement*=(-1.0);
1445 step=CastDoubleToSsizeT(floor((
double) displacement));
1446 area=(double) (displacement-step);
1449 GetPixelInfo(image,&source);
1450 GetPixelInfo(image,&destination);
1458 if (step > y_offset)
1460 q=p-step*(ssize_t) GetPixelChannels(image);
1461 for (i=0; i < (ssize_t) height; i++)
1463 if ((y_offset+i) < step)
1465 p+=(ptrdiff_t) GetPixelChannels(image);
1466 GetPixelInfoPixel(image,p,&pixel);
1467 q+=(ptrdiff_t) GetPixelChannels(image);
1470 GetPixelInfoPixel(image,p,&source);
1471 CompositePixelInfoAreaBlend(&pixel,(
double) pixel.alpha,
1472 &source,(
double) GetPixelAlpha(image,p),area,
1474 SetPixelViaPixelInfo(image,&destination,q);
1475 GetPixelInfoPixel(image,p,&pixel);
1476 p+=(ptrdiff_t) GetPixelChannels(image);
1477 q+=(ptrdiff_t) GetPixelChannels(image);
1479 CompositePixelInfoAreaBlend(&pixel,(
double) pixel.alpha,
1480 &background,(
double) background.alpha,area,&destination);
1481 SetPixelViaPixelInfo(image,&destination,q);
1482 q+=(ptrdiff_t) GetPixelChannels(image);
1483 for (i=0; i < (step-1); i++)
1485 SetPixelViaPixelInfo(image,&background,q);
1486 q+=(ptrdiff_t) GetPixelChannels(image);
1495 p+=(ptrdiff_t) height*GetPixelChannels(image);
1496 q=p+step*(ssize_t) GetPixelChannels(image);
1497 for (i=0; i < (ssize_t) height; i++)
1499 p-=(ptrdiff_t)GetPixelChannels(image);
1500 q-=GetPixelChannels(image);
1501 if ((
size_t) (y_offset+(ssize_t) height+step-i) > image->rows)
1503 GetPixelInfoPixel(image,p,&source);
1504 CompositePixelInfoAreaBlend(&pixel,(
double) pixel.alpha,
1505 &source,(
double) GetPixelAlpha(image,p),area,
1507 SetPixelViaPixelInfo(image,&destination,q);
1508 GetPixelInfoPixel(image,p,&pixel);
1510 CompositePixelInfoAreaBlend(&pixel,(
double) pixel.alpha,
1511 &background,(
double) background.alpha,area,&destination);
1512 q-=GetPixelChannels(image);
1513 SetPixelViaPixelInfo(image,&destination,q);
1514 for (i=0; i < (step-1); i++)
1516 q-=GetPixelChannels(image);
1517 SetPixelViaPixelInfo(image,&background,q);
1522 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1524 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1529#if defined(MAGICKCORE_OPENMP_SUPPORT)
1533 proceed=SetImageProgress(image,YShearImageTag,progress,image->rows);
1534 if (proceed == MagickFalse)
1538 image_view=DestroyCacheView(image_view);
1581MagickExport Image *ShearImage(
const Image *image,
const double x_shear,
1582 const double y_shear,ExceptionInfo *exception)
1598 assert(image != (Image *) NULL);
1599 assert(image->signature == MagickCoreSignature);
1600 assert(exception != (ExceptionInfo *) NULL);
1601 assert(exception->signature == MagickCoreSignature);
1602 if (IsEventLogging() != MagickFalse)
1603 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
1604 if ((x_shear != 0.0) && (fmod(x_shear,90.0) == 0.0))
1605 ThrowImageException(ImageError,
"AngleIsDiscontinuous");
1606 if ((y_shear != 0.0) && (fmod(y_shear,90.0) == 0.0))
1607 ThrowImageException(ImageError,
"AngleIsDiscontinuous");
1611 integral_image=CloneImage(image,0,0,MagickTrue,exception);
1612 if (integral_image == (Image *) NULL)
1613 ThrowImageException(ResourceLimitError,
"MemoryAllocationFailed");
1614 shear.x=(-tan(DegreesToRadians(fmod(x_shear,360.0))));
1615 shear.y=tan(DegreesToRadians(fmod(y_shear,360.0)));
1616 if ((shear.x == 0.0) && (shear.y == 0.0))
1617 return(integral_image);
1618 if (SetImageStorageClass(integral_image,DirectClass,exception) == MagickFalse)
1620 integral_image=DestroyImage(integral_image);
1621 return(integral_image);
1623 if (integral_image->alpha_trait == UndefinedPixelTrait)
1624 (void) SetImageAlphaChannel(integral_image,OpaqueAlphaChannel,exception);
1628 bounds.width=(size_t) ((ssize_t) image->columns+CastDoubleToSsizeT(floor(
1629 fabs(shear.x)*image->rows+0.5)));
1630 bounds.x=CastDoubleToSsizeT(ceil((
double) image->columns+((fabs(shear.x)*
1631 image->rows)-image->columns)/2.0-0.5));
1632 bounds.y=CastDoubleToSsizeT(ceil((
double) image->rows+((fabs(shear.y)*
1633 bounds.width)-image->rows)/2.0-0.5));
1637 integral_image->border_color=integral_image->background_color;
1638 integral_image->compose=CopyCompositeOp;
1639 border_info.width=(size_t) bounds.x;
1640 border_info.height=(size_t) bounds.y;
1641 shear_image=BorderImage(integral_image,&border_info,image->compose,exception);
1642 integral_image=DestroyImage(integral_image);
1643 if (shear_image == (Image *) NULL)
1644 ThrowImageException(ResourceLimitError,
"MemoryAllocationFailed");
1648 if (shear_image->alpha_trait == UndefinedPixelTrait)
1649 (void) SetImageAlphaChannel(shear_image,OpaqueAlphaChannel,exception);
1650 status=XShearImage(shear_image,shear.x,image->columns,image->rows,bounds.x,
1651 (ssize_t) (shear_image->rows-image->rows)/2,exception);
1652 if (status == MagickFalse)
1654 shear_image=DestroyImage(shear_image);
1655 return((Image *) NULL);
1657 status=YShearImage(shear_image,shear.y,bounds.width,image->rows,(ssize_t)
1658 (shear_image->columns-bounds.width)/2,bounds.y,exception);
1659 if (status == MagickFalse)
1661 shear_image=DestroyImage(shear_image);
1662 return((Image *) NULL);
1664 status=CropToFitImage(&shear_image,shear.x,shear.y,(MagickRealType)
1665 image->columns,(MagickRealType) image->rows,MagickFalse,exception);
1666 shear_image->alpha_trait=image->alpha_trait;
1667 shear_image->compose=image->compose;
1668 shear_image->page.width=0;
1669 shear_image->page.height=0;
1670 if (status == MagickFalse)
1671 shear_image=DestroyImage(shear_image);
1672 return(shear_image);
1714MagickExport Image *ShearRotateImage(
const Image *image,
const double degrees,
1715 ExceptionInfo *exception)
1743 assert(image != (Image *) NULL);
1744 assert(image->signature == MagickCoreSignature);
1745 assert(exception != (ExceptionInfo *) NULL);
1746 assert(exception->signature == MagickCoreSignature);
1747 if (IsEventLogging() != MagickFalse)
1748 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
1749 angle=fmod(degrees,360.0);
1752 for (rotations=0; angle > 45.0; rotations++)
1758 integral_image=IntegralRotateImage(image,rotations,exception);
1759 if (integral_image == (Image *) NULL)
1760 ThrowImageException(ResourceLimitError,
"MemoryAllocationFailed");
1761 shear.x=(-tan((
double) DegreesToRadians(angle)/2.0));
1762 shear.y=sin((
double) DegreesToRadians(angle));
1763 if ((shear.x == 0.0) && (shear.y == 0.0))
1764 return(integral_image);
1765 if (SetImageStorageClass(integral_image,DirectClass,exception) == MagickFalse)
1767 integral_image=DestroyImage(integral_image);
1768 return(integral_image);
1770 if (integral_image->alpha_trait == UndefinedPixelTrait)
1771 (void) SetImageAlphaChannel(integral_image,OpaqueAlphaChannel,exception);
1775 width=integral_image->columns;
1776 height=integral_image->rows;
1777 bounds.width=CastDoubleToSizeT(fabs((
double) height*shear.x)+width+0.5);
1778 bounds.height=CastDoubleToSizeT(fabs((
double) bounds.width*shear.y)+height+0.5);
1779 shear_width=CastDoubleToSizeT(fabs((
double) bounds.height*shear.x)+
1781 bounds.x=CastDoubleToSsizeT(floor((
double) ((shear_width > bounds.width) ?
1782 width : bounds.width-shear_width+2)/2.0+0.5));
1783 bounds.y=CastDoubleToSsizeT(floor(((
double) bounds.height-height+2)/2.0+0.5));
1787 integral_image->border_color=integral_image->background_color;
1788 integral_image->compose=CopyCompositeOp;
1789 border_info.width=(size_t) bounds.x;
1790 border_info.height=(size_t) bounds.y;
1791 rotate_image=BorderImage(integral_image,&border_info,image->compose,
1793 integral_image=DestroyImage(integral_image);
1794 if (rotate_image == (Image *) NULL)
1795 ThrowImageException(ResourceLimitError,
"MemoryAllocationFailed");
1799 status=XShearImage(rotate_image,shear.x,width,height,bounds.x,(ssize_t)
1800 (rotate_image->rows-height)/2,exception);
1801 if (status == MagickFalse)
1803 rotate_image=DestroyImage(rotate_image);
1804 return((Image *) NULL);
1806 status=YShearImage(rotate_image,shear.y,bounds.width,height,(ssize_t)
1807 (rotate_image->columns-bounds.width)/2,bounds.y,exception);
1808 if (status == MagickFalse)
1810 rotate_image=DestroyImage(rotate_image);
1811 return((Image *) NULL);
1813 status=XShearImage(rotate_image,shear.x,bounds.width,bounds.height,(ssize_t)
1814 (rotate_image->columns-bounds.width)/2,(ssize_t) (rotate_image->rows-
1815 bounds.height)/2,exception);
1816 if (status == MagickFalse)
1818 rotate_image=DestroyImage(rotate_image);
1819 return((Image *) NULL);
1821 status=CropToFitImage(&rotate_image,shear.x,shear.y,(MagickRealType) width,
1822 (MagickRealType) height,MagickTrue,exception);
1823 rotate_image->alpha_trait=image->alpha_trait;
1824 rotate_image->compose=image->compose;
1825 rotate_image->page.width=0;
1826 rotate_image->page.height=0;
1827 if (status == MagickFalse)
1828 rotate_image=DestroyImage(rotate_image);
1829 return(rotate_image);