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/quantum.h"
74#include "MagickCore/resource_.h"
75#include "MagickCore/shear.h"
76#include "MagickCore/statistic.h"
77#include "MagickCore/string_.h"
78#include "MagickCore/string-private.h"
79#include "MagickCore/thread-private.h"
80#include "MagickCore/threshold.h"
81#include "MagickCore/transform.h"
113static MagickBooleanType CropToFitImage(
Image **image,
114 const double x_shear,
const double y_shear,
115 const double width,
const double height,
136 extent[0].x=(double) (-width/2.0);
137 extent[0].y=(double) (-height/2.0);
138 extent[1].x=(double) width/2.0;
139 extent[1].y=(double) (-height/2.0);
140 extent[2].x=(double) (-width/2.0);
141 extent[2].y=(double) height/2.0;
142 extent[3].x=(double) width/2.0;
143 extent[3].y=(double) height/2.0;
144 for (i=3; i >= 0; i--)
146 extent[i].x+=x_shear*extent[i].y;
147 extent[i].y+=y_shear*extent[i].x;
148 if (rotate != MagickFalse)
149 extent[i].x+=x_shear*extent[i].y;
150 extent[i].x+=(double) (*image)->columns/2.0;
151 extent[i].y+=(double) (*image)->rows/2.0;
155 for (i=1; i < 4; i++)
157 if (min.x > extent[i].x)
159 if (min.y > extent[i].y)
161 if (max.x < extent[i].x)
163 if (max.y < extent[i].y)
166 geometry.x=CastDoubleToLong(ceil(min.x-0.5));
167 geometry.y=CastDoubleToLong(ceil(min.y-0.5));
168 geometry.width=(size_t) CastDoubleToLong(floor(max.x-min.x+0.5));
169 geometry.height=(size_t) CastDoubleToLong(floor(max.y-min.y+0.5));
171 (void) ParseAbsoluteGeometry(
"0x0+0+0",&(*image)->page);
172 crop_image=CropImage(*image,&geometry,exception);
173 if (crop_image == (
Image *) NULL)
175 crop_image->page=page;
176 *image=DestroyImage(*image);
216static void RadonProjection(
MatrixInfo *source_matrices,
217 MatrixInfo *destination_matrices,
const ssize_t sign,
size_t *projection)
231 q=destination_matrices;
232 for (step=1; step < GetMatrixColumns(p); step*=2)
234 for (x=0; x < (ssize_t) GetMatrixColumns(p); x+=2*(ssize_t) step)
244 for (i=0; i < (ssize_t) step; i++)
246 for (y=0; y < ((ssize_t) GetMatrixRows(p)-i-1); y++)
248 if (GetMatrixElement(p,x+i,y,&element) == MagickFalse)
250 if (GetMatrixElement(p,x+i+(ssize_t) step,y+i,&neighbor) == MagickFalse)
253 if (SetMatrixElement(q,x+2*i,y,&neighbor) == MagickFalse)
255 if (GetMatrixElement(p,x+i+(ssize_t) step,y+i+1,&neighbor) == MagickFalse)
258 if (SetMatrixElement(q,x+2*i+1,y,&neighbor) == MagickFalse)
261 for ( ; y < ((ssize_t) GetMatrixRows(p)-i); y++)
263 if (GetMatrixElement(p,x+i,y,&element) == MagickFalse)
265 if (GetMatrixElement(p,x+i+(ssize_t) step,y+i,&neighbor) == MagickFalse)
268 if (SetMatrixElement(q,x+2*i,y,&neighbor) == MagickFalse)
270 if (SetMatrixElement(q,x+2*i+1,y,&element) == MagickFalse)
273 for ( ; y < (ssize_t) GetMatrixRows(p); y++)
275 if (GetMatrixElement(p,x+i,y,&element) == MagickFalse)
277 if (SetMatrixElement(q,x+2*i,y,&element) == MagickFalse)
279 if (SetMatrixElement(q,x+2*i+1,y,&element) == MagickFalse)
288#if defined(MAGICKCORE_OPENMP_SUPPORT)
289 #pragma omp parallel for schedule(static) \
290 num_threads(GetMagickResourceLimit(ThreadResource))
292 for (x=0; x < (ssize_t) GetMatrixColumns(p); x++)
301 for (y=0; y < (ssize_t) (GetMatrixRows(p)-1); y++)
310 if (GetMatrixElement(p,x,y,&element) == MagickFalse)
312 if (GetMatrixElement(p,x,y+1,&neighbor) == MagickFalse)
314 delta=(ssize_t) element-(ssize_t) neighbor;
315 sum+=(size_t) (delta*delta);
317 projection[(ssize_t) GetMatrixColumns(p)+sign*x-1]=sum;
321static MagickBooleanType RadonTransform(
const Image *image,
322 const double threshold,
size_t *projection,
ExceptionInfo *exception)
328 *destination_matrices,
348 for (width=1; width < ((image->columns+7)/8); width<<=1) ;
349 source_matrices=AcquireMatrixInfo(width,image->rows,
sizeof(
unsigned short),
351 destination_matrices=AcquireMatrixInfo(width,image->rows,
352 sizeof(
unsigned short),exception);
353 if ((source_matrices == (
MatrixInfo *) NULL) ||
354 (destination_matrices == (
MatrixInfo *) NULL))
356 if (destination_matrices != (
MatrixInfo *) NULL)
357 destination_matrices=DestroyMatrixInfo(destination_matrices);
359 source_matrices=DestroyMatrixInfo(source_matrices);
362 if (NullMatrix(source_matrices) == MagickFalse)
364 destination_matrices=DestroyMatrixInfo(destination_matrices);
365 source_matrices=DestroyMatrixInfo(source_matrices);
368 for (j=0; j < 256; j++)
371 for (count=0; c != 0; c>>=1)
373 bits[j]=(
unsigned short) count;
376 image_view=AcquireVirtualCacheView(image,exception);
377#if defined(MAGICKCORE_OPENMP_SUPPORT)
378 #pragma omp parallel for schedule(static) shared(status) \
379 magick_number_threads(image,image,image->rows,2)
381 for (y=0; y < (ssize_t) image->rows; y++)
397 if (status == MagickFalse)
399 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
400 if (p == (
const Quantum *) NULL)
407 i=(ssize_t) (image->columns+7)/8;
408 for (x=0; x < (ssize_t) image->columns; x++)
411 if (((MagickRealType) GetPixelRed(image,p) < threshold) ||
412 ((MagickRealType) GetPixelGreen(image,p) < threshold) ||
413 ((MagickRealType) GetPixelBlue(image,p) < threshold))
419 (void) SetMatrixElement(source_matrices,--i,y,&value);
423 p+=GetPixelChannels(image);
429 (void) SetMatrixElement(source_matrices,--i,y,&value);
432 RadonProjection(source_matrices,destination_matrices,-1,projection);
433 (void) NullMatrix(source_matrices);
434#if defined(MAGICKCORE_OPENMP_SUPPORT)
435 #pragma omp parallel for schedule(static) shared(status) \
436 magick_number_threads(image,image,image->rows,2)
438 for (y=0; y < (ssize_t) image->rows; y++)
454 if (status == MagickFalse)
456 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
457 if (p == (
const Quantum *) NULL)
465 for (x=0; x < (ssize_t) image->columns; x++)
468 if (((MagickRealType) GetPixelRed(image,p) < threshold) ||
469 ((MagickRealType) GetPixelGreen(image,p) < threshold) ||
470 ((MagickRealType) GetPixelBlue(image,p) < threshold))
476 (void) SetMatrixElement(source_matrices,i++,y,&value);
480 p+=GetPixelChannels(image);
486 (void) SetMatrixElement(source_matrices,i++,y,&value);
489 RadonProjection(source_matrices,destination_matrices,1,projection);
490 image_view=DestroyCacheView(image_view);
491 destination_matrices=DestroyMatrixInfo(destination_matrices);
492 source_matrices=DestroyMatrixInfo(source_matrices);
496static void GetImageBackgroundColor(
Image *image,
const ssize_t offset,
516 GetPixelInfo(image,&background);
518 image_view=AcquireVirtualCacheView(image,exception);
519 for (y=0; y < (ssize_t) image->rows; y++)
527 if ((y >= offset) && (y < ((ssize_t) image->rows-offset)))
529 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
530 if (p == (
const Quantum *) NULL)
532 for (x=0; x < (ssize_t) image->columns; x++)
534 if ((x >= offset) && (x < ((ssize_t) image->columns-offset)))
536 background.red+=QuantumScale*(double) GetPixelRed(image,p);
537 background.green+=QuantumScale*(double) GetPixelGreen(image,p);
538 background.blue+=QuantumScale*(double) GetPixelBlue(image,p);
539 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
540 background.alpha+=QuantumScale*(double) GetPixelAlpha(image,p);
542 p+=GetPixelChannels(image);
545 image_view=DestroyCacheView(image_view);
546 image->background_color.red=(double) ClampToQuantum((
double) QuantumRange*
547 (
double) background.red/count);
548 image->background_color.green=(double) ClampToQuantum((
double) QuantumRange*
549 (
double) background.green/count);
550 image->background_color.blue=(double) ClampToQuantum((
double) QuantumRange*
551 (
double) background.blue/count);
552 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
553 image->background_color.alpha=(double) ClampToQuantum((
double) QuantumRange*
554 (
double) background.alpha/count);
557MagickExport
Image *DeskewImage(
const Image *image,
const double threshold,
593 for (width=1; width < ((image->columns+7)/8); width<<=1) ;
594 projection=(
size_t *) AcquireQuantumMemory((
size_t) (2*width-1),
595 sizeof(*projection));
596 if (projection == (
size_t *) NULL)
597 ThrowImageException(ResourceLimitError,
"MemoryAllocationFailed");
598 status=RadonTransform(image,threshold,projection,exception);
599 if (status == MagickFalse)
601 projection=(
size_t *) RelinquishMagickMemory(projection);
602 ThrowImageException(ResourceLimitError,
"MemoryAllocationFailed");
606 for (i=0; i < (ssize_t) (2*width-1); i++)
608 if (projection[i] > max_projection)
610 skew=i-(ssize_t) width+1;
611 max_projection=projection[i];
614 projection=(
size_t *) RelinquishMagickMemory(projection);
615 degrees=RadiansToDegrees(-atan((
double) skew/width/8));
616 if (image->debug != MagickFalse)
617 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
618 " Deskew angle: %g",degrees);
622 clone_image=CloneImage(image,0,0,MagickTrue,exception);
623 if (clone_image == (
Image *) NULL)
624 return((
Image *) NULL);
627 angle[MagickPathExtent];
629 (void) FormatLocaleString(angle,MagickPathExtent,
"%.20g",degrees);
630 (void) SetImageArtifact(clone_image,
"deskew:angle",angle);
632 (void) SetImageVirtualPixelMethod(clone_image,BackgroundVirtualPixelMethod,
634 affine_matrix.sx=cos(DegreesToRadians(fmod((
double) degrees,360.0)));
635 affine_matrix.rx=sin(DegreesToRadians(fmod((
double) degrees,360.0)));
636 affine_matrix.ry=(-sin(DegreesToRadians(fmod((
double) degrees,360.0))));
637 affine_matrix.sy=cos(DegreesToRadians(fmod((
double) degrees,360.0)));
638 affine_matrix.tx=0.0;
639 affine_matrix.ty=0.0;
640 artifact=GetImageArtifact(image,
"deskew:auto-crop");
641 if (IsStringTrue(artifact) == MagickFalse)
643 deskew_image=AffineTransformImage(clone_image,&affine_matrix,exception);
644 clone_image=DestroyImage(clone_image);
645 return(deskew_image);
650 GetImageBackgroundColor(clone_image,(ssize_t) StringToLong(artifact),
652 deskew_image=AffineTransformImage(clone_image,&affine_matrix,exception);
653 clone_image=DestroyImage(clone_image);
654 if (deskew_image == (
Image *) NULL)
655 return((
Image *) NULL);
656 median_image=StatisticImage(deskew_image,MedianStatistic,3,3,exception);
657 if (median_image == (
Image *) NULL)
659 deskew_image=DestroyImage(deskew_image);
660 return((
Image *) NULL);
662 geometry=GetImageBoundingBox(median_image,exception);
663 median_image=DestroyImage(median_image);
664 if (image->debug != MagickFalse)
665 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
" Deskew geometry: "
666 "%.20gx%.20g%+.20g%+.20g",(
double) geometry.width,(
double)
667 geometry.height,(
double) geometry.x,(
double) geometry.y);
668 crop_image=CropImage(deskew_image,&geometry,exception);
669 deskew_image=DestroyImage(deskew_image);
700MagickExport
Image *IntegralRotateImage(
const Image *image,
size_t rotations,
703#define RotateImageTag "Rotate/Image"
724 assert(image != (
Image *) NULL);
732 rotate_image=CloneImage(image,0,0,MagickTrue,exception);
737 rotate_image=CloneImage(image,image->columns,image->rows,MagickTrue,
744 rotate_image=CloneImage(image,image->rows,image->columns,MagickTrue,
749 if (rotate_image == (
Image *) NULL)
750 return((
Image *) NULL);
752 return(rotate_image);
758 image_view=AcquireVirtualCacheView(image,exception);
759 rotate_view=AcquireAuthenticCacheView(rotate_image,exception);
774 GetPixelCacheTileSize(image,&tile_width,&tile_height);
775 tile_width=image->columns;
776#if defined(MAGICKCORE_OPENMP_SUPPORT)
777 #pragma omp parallel for schedule(static) shared(status) \
778 magick_number_threads(image,rotate_image,image->rows/tile_height,2)
780 for (tile_y=0; tile_y < (ssize_t) image->rows; tile_y+=(ssize_t) tile_height)
785 if (status == MagickFalse)
788 for ( ; tile_x < (ssize_t) image->columns; tile_x+=(ssize_t) tile_width)
807 if ((tile_width+(
size_t) tile_x) > image->columns)
808 width=(size_t) ((ssize_t) tile_width-(tile_x+(ssize_t) tile_width-
809 (ssize_t) image->columns));
811 if ((tile_height+(
size_t) tile_y) > image->rows)
812 height=(size_t) ((ssize_t) tile_height-(tile_y+(ssize_t)
813 tile_height-(ssize_t) image->rows));
814 p=GetCacheViewVirtualPixels(image_view,tile_x,tile_y,width,height,
816 if (p == (
const Quantum *) NULL)
821 for (y=0; y < (ssize_t) width; y++)
824 *magick_restrict tile_pixels;
829 if (status == MagickFalse)
831 q=QueueCacheViewAuthenticPixels(rotate_view,(ssize_t)
832 rotate_image->columns-(tile_y+(ssize_t) height),y+tile_x,height,
834 if (q == (Quantum *) NULL)
839 tile_pixels=p+(((ssize_t) height-1)*(ssize_t) width+y)*(ssize_t)
840 GetPixelChannels(image);
841 for (x=0; x < (ssize_t) height; x++)
846 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
848 PixelChannel channel = GetPixelChannelChannel(image,i);
849 PixelTrait traits = GetPixelChannelTraits(image,channel);
850 PixelTrait rotate_traits = GetPixelChannelTraits(rotate_image,
852 if ((traits == UndefinedPixelTrait) ||
853 (rotate_traits == UndefinedPixelTrait))
855 SetPixelChannel(rotate_image,channel,tile_pixels[i],q);
857 tile_pixels-=width*GetPixelChannels(image);
858 q+=GetPixelChannels(rotate_image);
860 sync=SyncCacheViewAuthenticPixels(rotate_view,exception);
861 if (sync == MagickFalse)
865 if (image->progress_monitor != (MagickProgressMonitor) NULL)
870 proceed=SetImageProgress(image,RotateImageTag,
871 progress+=(MagickOffsetType) tile_height,image->rows);
872 if (proceed == MagickFalse)
876 (void) SetImageProgress(image,RotateImageTag,(MagickOffsetType)
877 image->rows-1,image->rows);
878 Swap(page.width,page.height);
881 page.x=(ssize_t) page.width-(ssize_t) rotate_image->columns-page.x;
892#if defined(MAGICKCORE_OPENMP_SUPPORT)
893 #pragma omp parallel for schedule(static) shared(status) \
894 magick_number_threads(image,rotate_image,image->rows,2)
896 for (y=0; y < (ssize_t) image->rows; y++)
910 if (status == MagickFalse)
912 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
913 q=QueueCacheViewAuthenticPixels(rotate_view,0,(ssize_t) image->rows-y-1,
914 image->columns,1,exception);
915 if ((p == (
const Quantum *) NULL) || (q == (Quantum *) NULL))
920 q+=GetPixelChannels(rotate_image)*image->columns;
921 for (x=0; x < (ssize_t) image->columns; x++)
926 q-=GetPixelChannels(rotate_image);
927 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
929 PixelChannel channel = GetPixelChannelChannel(image,i);
930 PixelTrait traits = GetPixelChannelTraits(image,channel);
931 PixelTrait rotate_traits = GetPixelChannelTraits(rotate_image,
933 if ((traits == UndefinedPixelTrait) ||
934 (rotate_traits == UndefinedPixelTrait))
936 SetPixelChannel(rotate_image,channel,p[i],q);
938 p+=GetPixelChannels(image);
940 sync=SyncCacheViewAuthenticPixels(rotate_view,exception);
941 if (sync == MagickFalse)
943 if (image->progress_monitor != (MagickProgressMonitor) NULL)
948 proceed=SetImageProgress(image,RotateImageTag,progress++,
950 if (proceed == MagickFalse)
954 (void) SetImageProgress(image,RotateImageTag,(MagickOffsetType)
955 image->rows-1,image->rows);
957 page.x=(ssize_t) page.width-(ssize_t) rotate_image->columns-page.x;
958 if (page.height != 0)
959 page.y=(ssize_t) page.height-(ssize_t) rotate_image->rows-page.y;
974 GetPixelCacheTileSize(image,&tile_width,&tile_height);
975 tile_width=image->columns;
976#if defined(MAGICKCORE_OPENMP_SUPPORT)
977 #pragma omp parallel for schedule(static) shared(status) \
978 magick_number_threads(image,rotate_image,image->rows/tile_height,2)
980 for (tile_y=0; tile_y < (ssize_t) image->rows; tile_y+=(ssize_t) tile_height)
985 if (status == MagickFalse)
988 for ( ; tile_x < (ssize_t) image->columns; tile_x+=(ssize_t) tile_width)
1007 if ((tile_width+(
size_t) tile_x) > image->columns)
1008 width=(size_t) ((ssize_t) tile_width-(tile_x+(ssize_t) tile_width-
1009 (ssize_t) image->columns));
1011 if ((tile_height+(
size_t) tile_y) > image->rows)
1012 height=(size_t) ((ssize_t) tile_height-(tile_y+(ssize_t)
1013 tile_height-(ssize_t) image->rows));
1014 p=GetCacheViewVirtualPixels(image_view,tile_x,tile_y,width,height,
1016 if (p == (
const Quantum *) NULL)
1021 for (y=0; y < (ssize_t) width; y++)
1024 *magick_restrict tile_pixels;
1029 if (status == MagickFalse)
1031 q=QueueCacheViewAuthenticPixels(rotate_view,tile_y,y+(ssize_t)
1032 rotate_image->rows-(tile_x+(ssize_t) width),height,1,exception);
1033 if (q == (Quantum *) NULL)
1038 tile_pixels=p+(((ssize_t) width-1)-y)*(ssize_t)
1039 GetPixelChannels(image);
1040 for (x=0; x < (ssize_t) height; x++)
1045 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1047 PixelChannel channel = GetPixelChannelChannel(image,i);
1048 PixelTrait traits = GetPixelChannelTraits(image,channel);
1049 PixelTrait rotate_traits = GetPixelChannelTraits(rotate_image,
1051 if ((traits == UndefinedPixelTrait) ||
1052 (rotate_traits == UndefinedPixelTrait))
1054 SetPixelChannel(rotate_image,channel,tile_pixels[i],q);
1056 tile_pixels+=width*GetPixelChannels(image);
1057 q+=GetPixelChannels(rotate_image);
1059#if defined(MAGICKCORE_OPENMP_SUPPORT)
1060 #pragma omp critical (MagickCore_IntegralRotateImage)
1062 sync=SyncCacheViewAuthenticPixels(rotate_view,exception);
1063 if (sync == MagickFalse)
1067 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1072 proceed=SetImageProgress(image,RotateImageTag,
1073 progress+=(MagickOffsetType) tile_height,image->rows);
1074 if (proceed == MagickFalse)
1078 (void) SetImageProgress(image,RotateImageTag,(MagickOffsetType)
1079 image->rows-1,image->rows);
1080 Swap(page.width,page.height);
1081 Swap(page.x,page.y);
1082 if (page.height != 0)
1083 page.y=(ssize_t) page.height-(ssize_t) rotate_image->rows-page.y;
1089 rotate_view=DestroyCacheView(rotate_view);
1090 image_view=DestroyCacheView(image_view);
1091 rotate_image->type=image->type;
1092 rotate_image->page=page;
1093 if (status == MagickFalse)
1094 rotate_image=DestroyImage(rotate_image);
1095 return(rotate_image);
1134static MagickBooleanType XShearImage(
Image *image,
const double degrees,
1135 const size_t width,
const size_t height,
const ssize_t x_offset,
1138#define XShearImageTag "XShear/Image"
1164 assert(image != (
Image *) NULL);
1165 assert(image->signature == MagickCoreSignature);
1166 if (IsEventLogging() != MagickFalse)
1167 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
1169 background=image->background_color;
1171 image_view=AcquireAuthenticCacheView(image,exception);
1172#if defined(MAGICKCORE_OPENMP_SUPPORT)
1173 #pragma omp parallel for schedule(static) shared(progress,status) \
1174 magick_number_threads(image,image,height,1)
1176 for (y=0; y < (ssize_t) height; y++)
1198 if (status == MagickFalse)
1200 p=GetCacheViewAuthenticPixels(image_view,0,y_offset+y,image->columns,1,
1202 if (p == (Quantum *) NULL)
1207 p+=x_offset*(ssize_t) GetPixelChannels(image);
1208 displacement=degrees*(double) (y-height/2.0);
1209 if (displacement == 0.0)
1211 if (displacement > 0.0)
1215 displacement*=(-1.0);
1218 step=CastDoubleToLong(floor((
double) displacement));
1219 area=(double) (displacement-step);
1222 GetPixelInfo(image,&source);
1223 GetPixelInfo(image,&destination);
1231 if (step > x_offset)
1233 q=p-step*(ssize_t) GetPixelChannels(image);
1234 for (i=0; i < (ssize_t) width; i++)
1236 if ((x_offset+i) < step)
1238 p+=GetPixelChannels(image);
1239 GetPixelInfoPixel(image,p,&pixel);
1240 q+=GetPixelChannels(image);
1243 GetPixelInfoPixel(image,p,&source);
1244 CompositePixelInfoAreaBlend(&pixel,(
double) pixel.alpha,
1245 &source,(
double) GetPixelAlpha(image,p),area,&destination);
1246 SetPixelViaPixelInfo(image,&destination,q);
1247 GetPixelInfoPixel(image,p,&pixel);
1248 p+=GetPixelChannels(image);
1249 q+=GetPixelChannels(image);
1251 CompositePixelInfoAreaBlend(&pixel,(
double) pixel.alpha,
1252 &background,(
double) background.alpha,area,&destination);
1253 SetPixelViaPixelInfo(image,&destination,q);
1254 q+=GetPixelChannels(image);
1255 for (i=0; i < (step-1); i++)
1257 SetPixelViaPixelInfo(image,&background,q);
1258 q+=GetPixelChannels(image);
1267 p+=width*GetPixelChannels(image);
1268 q=p+step*(ssize_t) GetPixelChannels(image);
1269 for (i=0; i < (ssize_t) width; i++)
1271 p-=GetPixelChannels(image);
1272 q-=GetPixelChannels(image);
1273 if ((
size_t) (x_offset+(ssize_t) width+step-i) > image->columns)
1275 GetPixelInfoPixel(image,p,&source);
1276 CompositePixelInfoAreaBlend(&pixel,(
double) pixel.alpha,
1277 &source,(
double) GetPixelAlpha(image,p),area,&destination);
1278 SetPixelViaPixelInfo(image,&destination,q);
1279 GetPixelInfoPixel(image,p,&pixel);
1281 CompositePixelInfoAreaBlend(&pixel,(
double) pixel.alpha,
1282 &background,(
double) background.alpha,area,&destination);
1283 q-=GetPixelChannels(image);
1284 SetPixelViaPixelInfo(image,&destination,q);
1285 for (i=0; i < (step-1); i++)
1287 q-=GetPixelChannels(image);
1288 SetPixelViaPixelInfo(image,&background,q);
1293 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1295 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1300#if defined(MAGICKCORE_OPENMP_SUPPORT)
1304 proceed=SetImageProgress(image,XShearImageTag,progress,height);
1305 if (proceed == MagickFalse)
1309 image_view=DestroyCacheView(image_view);
1349static MagickBooleanType YShearImage(
Image *image,
const double degrees,
1350 const size_t width,
const size_t height,
const ssize_t x_offset,
1353#define YShearImageTag "YShear/Image"
1379 assert(image != (
Image *) NULL);
1380 assert(image->signature == MagickCoreSignature);
1381 if (IsEventLogging() != MagickFalse)
1382 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
1385 background=image->background_color;
1386 image_view=AcquireAuthenticCacheView(image,exception);
1387#if defined(MAGICKCORE_OPENMP_SUPPORT)
1388 #pragma omp parallel for schedule(static) shared(progress,status) \
1389 magick_number_threads(image,image,width,1)
1391 for (x=0; x < (ssize_t) width; x++)
1413 if (status == MagickFalse)
1415 p=GetCacheViewAuthenticPixels(image_view,x_offset+x,0,1,image->rows,
1417 if (p == (Quantum *) NULL)
1422 p+=y_offset*(ssize_t) GetPixelChannels(image);
1423 displacement=degrees*(double) (x-width/2.0);
1424 if (displacement == 0.0)
1426 if (displacement > 0.0)
1430 displacement*=(-1.0);
1433 step=CastDoubleToLong(floor((
double) displacement));
1434 area=(double) (displacement-step);
1437 GetPixelInfo(image,&source);
1438 GetPixelInfo(image,&destination);
1446 if (step > y_offset)
1448 q=p-step*(ssize_t) GetPixelChannels(image);
1449 for (i=0; i < (ssize_t) height; i++)
1451 if ((y_offset+i) < step)
1453 p+=GetPixelChannels(image);
1454 GetPixelInfoPixel(image,p,&pixel);
1455 q+=GetPixelChannels(image);
1458 GetPixelInfoPixel(image,p,&source);
1459 CompositePixelInfoAreaBlend(&pixel,(
double) pixel.alpha,
1460 &source,(
double) GetPixelAlpha(image,p),area,
1462 SetPixelViaPixelInfo(image,&destination,q);
1463 GetPixelInfoPixel(image,p,&pixel);
1464 p+=GetPixelChannels(image);
1465 q+=GetPixelChannels(image);
1467 CompositePixelInfoAreaBlend(&pixel,(
double) pixel.alpha,
1468 &background,(
double) background.alpha,area,&destination);
1469 SetPixelViaPixelInfo(image,&destination,q);
1470 q+=GetPixelChannels(image);
1471 for (i=0; i < (step-1); i++)
1473 SetPixelViaPixelInfo(image,&background,q);
1474 q+=GetPixelChannels(image);
1483 p+=height*GetPixelChannels(image);
1484 q=p+step*(ssize_t) GetPixelChannels(image);
1485 for (i=0; i < (ssize_t) height; i++)
1487 p-=GetPixelChannels(image);
1488 q-=GetPixelChannels(image);
1489 if ((
size_t) (y_offset+(ssize_t) height+step-i) > image->rows)
1491 GetPixelInfoPixel(image,p,&source);
1492 CompositePixelInfoAreaBlend(&pixel,(
double) pixel.alpha,
1493 &source,(
double) GetPixelAlpha(image,p),area,
1495 SetPixelViaPixelInfo(image,&destination,q);
1496 GetPixelInfoPixel(image,p,&pixel);
1498 CompositePixelInfoAreaBlend(&pixel,(
double) pixel.alpha,
1499 &background,(
double) background.alpha,area,&destination);
1500 q-=GetPixelChannels(image);
1501 SetPixelViaPixelInfo(image,&destination,q);
1502 for (i=0; i < (step-1); i++)
1504 q-=GetPixelChannels(image);
1505 SetPixelViaPixelInfo(image,&background,q);
1510 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1512 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1517#if defined(MAGICKCORE_OPENMP_SUPPORT)
1521 proceed=SetImageProgress(image,YShearImageTag,progress,image->rows);
1522 if (proceed == MagickFalse)
1526 image_view=DestroyCacheView(image_view);
1569MagickExport
Image *ShearImage(
const Image *image,
const double x_shear,
1586 assert(image != (
Image *) NULL);
1587 assert(image->signature == MagickCoreSignature);
1589 assert(exception->signature == MagickCoreSignature);
1590 if (IsEventLogging() != MagickFalse)
1591 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
1592 if ((x_shear != 0.0) && (fmod(x_shear,90.0) == 0.0))
1593 ThrowImageException(ImageError,
"AngleIsDiscontinuous");
1594 if ((y_shear != 0.0) && (fmod(y_shear,90.0) == 0.0))
1595 ThrowImageException(ImageError,
"AngleIsDiscontinuous");
1599 integral_image=CloneImage(image,0,0,MagickTrue,exception);
1600 if (integral_image == (
Image *) NULL)
1601 ThrowImageException(ResourceLimitError,
"MemoryAllocationFailed");
1602 shear.x=(-tan(DegreesToRadians(fmod(x_shear,360.0))));
1603 shear.y=tan(DegreesToRadians(fmod(y_shear,360.0)));
1604 if ((shear.x == 0.0) && (shear.y == 0.0))
1605 return(integral_image);
1606 if (SetImageStorageClass(integral_image,DirectClass,exception) == MagickFalse)
1608 integral_image=DestroyImage(integral_image);
1609 return(integral_image);
1611 if (integral_image->alpha_trait == UndefinedPixelTrait)
1612 (void) SetImageAlphaChannel(integral_image,OpaqueAlphaChannel,exception);
1616 bounds.width=(size_t) ((ssize_t) image->columns+
1617 CastDoubleToLong(floor(fabs(shear.x)*image->rows+0.5)));
1618 bounds.x=CastDoubleToLong(ceil((
double) image->columns+((fabs(shear.x)*
1619 image->rows)-image->columns)/2.0-0.5));
1620 bounds.y=CastDoubleToLong(ceil((
double) image->rows+((fabs(shear.y)*
1621 bounds.width)-image->rows)/2.0-0.5));
1625 integral_image->border_color=integral_image->background_color;
1626 integral_image->compose=CopyCompositeOp;
1627 border_info.width=(size_t) bounds.x;
1628 border_info.height=(size_t) bounds.y;
1629 shear_image=BorderImage(integral_image,&border_info,image->compose,exception);
1630 integral_image=DestroyImage(integral_image);
1631 if (shear_image == (
Image *) NULL)
1632 ThrowImageException(ResourceLimitError,
"MemoryAllocationFailed");
1636 if (shear_image->alpha_trait == UndefinedPixelTrait)
1637 (void) SetImageAlphaChannel(shear_image,OpaqueAlphaChannel,exception);
1638 status=XShearImage(shear_image,shear.x,image->columns,image->rows,bounds.x,
1639 (ssize_t) (shear_image->rows-image->rows)/2,exception);
1640 if (status == MagickFalse)
1642 shear_image=DestroyImage(shear_image);
1643 return((
Image *) NULL);
1645 status=YShearImage(shear_image,shear.y,bounds.width,image->rows,(ssize_t)
1646 (shear_image->columns-bounds.width)/2,bounds.y,exception);
1647 if (status == MagickFalse)
1649 shear_image=DestroyImage(shear_image);
1650 return((
Image *) NULL);
1652 status=CropToFitImage(&shear_image,shear.x,shear.y,(MagickRealType)
1653 image->columns,(MagickRealType) image->rows,MagickFalse,exception);
1654 shear_image->alpha_trait=image->alpha_trait;
1655 shear_image->compose=image->compose;
1656 shear_image->page.width=0;
1657 shear_image->page.height=0;
1658 if (status == MagickFalse)
1659 shear_image=DestroyImage(shear_image);
1660 return(shear_image);
1702MagickExport
Image *ShearRotateImage(
const Image *image,
const double degrees,
1731 assert(image != (
Image *) NULL);
1732 assert(image->signature == MagickCoreSignature);
1734 assert(exception->signature == MagickCoreSignature);
1735 if (IsEventLogging() != MagickFalse)
1736 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
1737 angle=fmod(degrees,360.0);
1740 for (rotations=0; angle > 45.0; rotations++)
1746 integral_image=IntegralRotateImage(image,rotations,exception);
1747 if (integral_image == (
Image *) NULL)
1748 ThrowImageException(ResourceLimitError,
"MemoryAllocationFailed");
1749 shear.x=(-tan((
double) DegreesToRadians(angle)/2.0));
1750 shear.y=sin((
double) DegreesToRadians(angle));
1751 if ((shear.x == 0.0) && (shear.y == 0.0))
1752 return(integral_image);
1753 if (SetImageStorageClass(integral_image,DirectClass,exception) == MagickFalse)
1755 integral_image=DestroyImage(integral_image);
1756 return(integral_image);
1758 if (integral_image->alpha_trait == UndefinedPixelTrait)
1759 (void) SetImageAlphaChannel(integral_image,OpaqueAlphaChannel,exception);
1763 width=integral_image->columns;
1764 height=integral_image->rows;
1765 bounds.width=CastDoubleToUnsigned(fabs((
double) height*shear.x)+width+0.5);
1766 bounds.height=CastDoubleToUnsigned(fabs((
double) bounds.width*shear.y)+
1768 shear_width=CastDoubleToUnsigned(fabs((
double) bounds.height*shear.x)+
1770 bounds.x=CastDoubleToLong(floor((
double) ((shear_width > bounds.width) ?
1771 width : bounds.width-shear_width+2)/2.0+0.5));
1772 bounds.y=CastDoubleToLong(floor(((
double) bounds.height-height+2)/2.0+0.5));
1776 integral_image->border_color=integral_image->background_color;
1777 integral_image->compose=CopyCompositeOp;
1778 border_info.width=(size_t) bounds.x;
1779 border_info.height=(size_t) bounds.y;
1780 rotate_image=BorderImage(integral_image,&border_info,image->compose,
1782 integral_image=DestroyImage(integral_image);
1783 if (rotate_image == (
Image *) NULL)
1784 ThrowImageException(ResourceLimitError,
"MemoryAllocationFailed");
1788 status=XShearImage(rotate_image,shear.x,width,height,bounds.x,(ssize_t)
1789 (rotate_image->rows-height)/2,exception);
1790 if (status == MagickFalse)
1792 rotate_image=DestroyImage(rotate_image);
1793 return((
Image *) NULL);
1795 status=YShearImage(rotate_image,shear.y,bounds.width,height,(ssize_t)
1796 (rotate_image->columns-bounds.width)/2,bounds.y,exception);
1797 if (status == MagickFalse)
1799 rotate_image=DestroyImage(rotate_image);
1800 return((
Image *) NULL);
1802 status=XShearImage(rotate_image,shear.x,bounds.width,bounds.height,(ssize_t)
1803 (rotate_image->columns-bounds.width)/2,(ssize_t) (rotate_image->rows-
1804 bounds.height)/2,exception);
1805 if (status == MagickFalse)
1807 rotate_image=DestroyImage(rotate_image);
1808 return((
Image *) NULL);
1810 status=CropToFitImage(&rotate_image,shear.x,shear.y,(MagickRealType) width,
1811 (MagickRealType) height,MagickTrue,exception);
1812 rotate_image->alpha_trait=image->alpha_trait;
1813 rotate_image->compose=image->compose;
1814 rotate_image->page.width=0;
1815 rotate_image->page.height=0;
1816 if (status == MagickFalse)
1817 rotate_image=DestroyImage(rotate_image);
1818 return(rotate_image);