2
% Copyright (C) 2003 GraphicsMagick Group
3
% Copyright (C) 2002 ImageMagick Studio
4
% Copyright 1991-1999 E. I. du Pont de Nemours and Company
6
% This program is covered by multiple licenses, which are described in
7
% Copyright.txt. You should have received a copy of Copyright.txt with this
8
% package; otherwise see http://www.graphicsmagick.org/www/Copyright.html.
10
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14
% SSSSS H H EEEEE AAA RRRR %
16
% SSS HHHHH EEE AAAAA RRRR %
18
% SSSSS H H EEEEE A A R R %
21
% Methods to Shear or Rotate an Image by an Arbitrary Angle %
30
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
32
% Method RotateImage, XShearImage, and YShearImage is based on the paper
33
% "A Fast Algorithm for General Raster Rotatation" by Alan W. Paeth,
34
% Graphics Interface '86 (Vancouver). RotateImage is adapted from a similar
35
% method based on the Paeth paper written by Michael Halle of the Spatial
36
% Imaging Group, MIT Media Lab.
44
#include "magick/studio.h"
45
#include "magick/cache.h"
46
#include "magick/decorate.h"
47
#include "magick/monitor.h"
48
#include "magick/render.h"
49
#include "magick/shear.h"
50
#include "magick/transform.h"
53
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
56
% A f f i n e T r a n s f o r m I m a g e %
60
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
62
% AffineTransformImage() transforms an image as dictated by the affine matrix.
63
% It allocates the memory necessary for the new Image structure and returns
64
% a pointer to the new image.
66
% The format of the AffineTransformImage method is:
68
% Image *AffineTransformImage(const Image *image,
69
% AffineMatrix *affine,ExceptionInfo *exception)
71
% A description of each parameter follows:
75
% o affine: The affine transform.
77
% o exception: Return any errors or warnings in this structure.
81
MagickExport Image *AffineTransformImage(const Image *image,
82
const AffineMatrix *affine,ExceptionInfo *exception)
103
Determine bounding box.
105
assert(image != (const Image *) NULL);
106
assert(image->signature == MagickSignature);
107
assert(affine != (AffineMatrix *) NULL);
108
assert(exception != (ExceptionInfo *) NULL);
109
assert(exception->signature == MagickSignature);
112
extent[1].x=image->columns;
114
extent[2].x=image->columns;
115
extent[2].y=image->rows;
117
extent[3].y=image->rows;
118
for (i=0; i < 4; i++)
120
x=(long) (extent[i].x+0.5);
121
y=(long) (extent[i].y+0.5);
122
extent[i].x=x*affine->sx+y*affine->ry+affine->tx;
123
extent[i].y=x*affine->rx+y*affine->sy+affine->ty;
127
for (i=1; i < 4; i++)
129
if (min.x > extent[i].x)
131
if (min.y > extent[i].y)
133
if (max.x < extent[i].x)
135
if (max.y < extent[i].y)
139
Affine transform image.
141
affine_image=CloneImage(image,(unsigned long) ceil(max.x-min.x-0.5),
142
(unsigned long) ceil(max.y-min.y-0.5),True,exception);
143
if (affine_image == (Image *) NULL)
144
return((Image *) NULL);
145
SetImage(affine_image,TransparentOpacity);
146
transform.sx=affine->sx;
147
transform.rx=affine->rx;
148
transform.ry=affine->ry;
149
transform.sy=affine->sy;
150
transform.tx=(-min.x);
151
transform.ty=(-min.y);
152
DrawAffineImage(affine_image,image,&transform);
153
return(affine_image);
157
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
161
+ C r o p T o F i t I m a g e %
165
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
167
% Method CropToFitImage crops the sheared image as determined by the bounding
168
% box as defined by width and height and shearing angles.
170
% The format of the CropToFitImage method is:
172
% Image *CropToFitImage(Image **image,const double x_shear,
173
% const double x_shear,const double width,const double height,
174
% const unsigne int rotate,ExceptionInfo *exception)
176
% A description of each parameter follows.
178
% o image: The address of a structure of type Image.
180
% o x_shear, y_shear, width, height: Defines a region of the image to crop.
182
% o exception: Return any errors or warnings in this structure.
186
static void CropToFitImage(Image **image,const double x_shear,
187
const double y_shear,const double width,const double height,
188
const unsigned int rotate,ExceptionInfo *exception)
205
Calculate the rotated image size.
207
extent[0].x=(-width/2.0);
208
extent[0].y=(-height/2.0);
209
extent[1].x=width/2.0;
210
extent[1].y=(-height/2.0);
211
extent[2].x=(-width/2.0);
212
extent[2].y=height/2.0;
213
extent[3].x=width/2.0;
214
extent[3].y=height/2.0;
215
for (i=0; i < 4; i++)
217
extent[i].x+=x_shear*extent[i].y;
218
extent[i].y+=y_shear*extent[i].x;
220
extent[i].x+=x_shear*extent[i].y;
221
extent[i].x+=(double) (*image)->columns/2.0;
222
extent[i].y+=(double) (*image)->rows/2.0;
226
for (i=1; i < 4; i++)
228
if (min.x > extent[i].x)
230
if (min.y > extent[i].y)
232
if (max.x < extent[i].x)
234
if (max.y < extent[i].y)
237
geometry.width=(unsigned long) floor(max.x-min.x+0.5);
238
geometry.height=(unsigned long) floor(max.y-min.y+0.5);
239
geometry.x=(long) ceil(min.x-0.5);
240
geometry.y=(long) ceil(min.y-0.5);
241
crop_image=CropImage(*image,&geometry,exception);
242
if (crop_image != (Image *) NULL)
244
crop_image->page=(*image)->page;
245
DestroyImage(*image);
251
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
255
+ I n t e g r a l R o t a t e I m a g e %
259
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
261
% Method IntegralRotateImage rotates the image an integral of 90 degrees.
262
% It allocates the memory necessary for the new Image structure and returns
263
% a pointer to the rotated image.
265
% The format of the IntegralRotateImage method is:
267
% Image *IntegralRotateImage(const Image *image,unsigned int rotations,
268
% ExceptionInfo *exception)
270
% A description of each parameter follows.
272
% o rotate_image: Method IntegralRotateImage returns a pointer to the
273
% rotated image. A null image is returned if there is a a memory shortage.
275
% o image: The image.
277
% o rotations: Specifies the number of 90 degree rotations.
281
static Image *IntegralRotateImage(const Image *image,unsigned int rotations,
282
ExceptionInfo *exception)
284
#define RotateImageText " Rotate image... "
299
register const PixelPacket
309
Initialize rotated image attributes.
311
assert(image != (Image *) NULL);
314
if ((rotations == 1) || (rotations == 3))
315
rotate_image=CloneImage(image,image->rows,image->columns,True,exception);
317
rotate_image=CloneImage(image,image->columns,image->rows,True,exception);
318
if (rotate_image == (Image *) NULL)
319
return((Image *) NULL);
321
Integral rotate the image.
330
for (y=0; y < (long) image->rows; y++)
332
p=AcquireImagePixels(image,0,y,image->columns,1,exception);
333
q=SetImagePixels(rotate_image,0,y,rotate_image->columns,1);
334
if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
336
(void) memcpy(q,p,image->columns*sizeof(PixelPacket));
337
indexes=GetIndexes(image);
338
rotate_indexes=GetIndexes(rotate_image);
339
if ((indexes != (IndexPacket *) NULL) &&
340
(rotate_indexes != (IndexPacket *) NULL))
341
(void) memcpy(rotate_indexes,indexes,image->columns*
342
sizeof(IndexPacket));
343
if (!SyncImagePixels(rotate_image))
345
if (QuantumTick(y,image->rows))
346
if (!MagickMonitor(RotateImageText,y,image->rows,exception))
356
for (y=0; y < (long) image->rows; y++)
358
p=AcquireImagePixels(image,0,y,image->columns,1,exception);
359
q=SetImagePixels(rotate_image,(long) (image->rows-y-1),0,1,
361
if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
363
(void) memcpy(q,p,image->columns*sizeof(PixelPacket));
364
indexes=GetIndexes(image);
365
rotate_indexes=GetIndexes(rotate_image);
366
if ((indexes != (IndexPacket *) NULL) &&
367
(rotate_indexes != (IndexPacket *) NULL))
368
(void) memcpy(rotate_indexes,indexes,image->columns*
369
sizeof(IndexPacket));
370
if (!SyncImagePixels(rotate_image))
372
if (QuantumTick(y,image->rows))
373
if (!MagickMonitor(RotateImageText,y,image->rows,exception))
376
Swap(page.width,page.height);
378
page.x=(long) (page.width-rotate_image->columns-page.x);
386
for (y=0; y < (long) image->rows; y++)
388
p=AcquireImagePixels(image,0,y,image->columns,1,exception);
389
q=SetImagePixels(rotate_image,0,(long) (image->rows-y-1),
391
if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
394
indexes=GetIndexes(image);
395
rotate_indexes=GetIndexes(rotate_image);
396
if ((indexes != (IndexPacket *) NULL) &&
397
(rotate_indexes != (IndexPacket *) NULL))
398
for (x=0; x < (long) image->columns; x++)
399
rotate_indexes[image->columns-x-1]=indexes[x];
400
for (x=0; x < (long) image->columns; x++)
402
if (!SyncImagePixels(rotate_image))
404
if (QuantumTick(y,image->rows))
405
if (!MagickMonitor(RotateImageText,y,image->rows,exception))
408
page.x=(long) (page.width-rotate_image->columns-page.x);
409
page.y=(long) (page.height-rotate_image->rows-page.y);
417
for (y=0; y < (long) image->rows; y++)
419
p=AcquireImagePixels(image,0,y,image->columns,1,exception);
420
q=SetImagePixels(rotate_image,y,0,1,rotate_image->rows);
421
if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
424
for (x=0; x < (long) image->columns; x++)
426
indexes=GetIndexes(image);
427
rotate_indexes=GetIndexes(rotate_image);
428
if ((indexes != (IndexPacket *) NULL) &&
429
(rotate_indexes != (IndexPacket *) NULL))
430
for (x=0; x < (long) image->columns; x++)
431
rotate_indexes[image->columns-x-1]=indexes[x];
432
if (!SyncImagePixels(rotate_image))
434
if (QuantumTick(y,image->rows))
435
if (!MagickMonitor(RotateImageText,y,image->rows,exception))
438
Swap(page.width,page.height);
440
page.y=(long) (page.height-rotate_image->rows-page.y);
444
rotate_image->page=page;
445
rotate_image->is_grayscale=image->is_grayscale;
446
return(rotate_image);
450
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
454
+ X S h e a r I m a g e %
458
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
460
% Procedure XShearImage shears the image in the X direction with a shear angle
461
% of 'degrees'. Positive angles shear counter-clockwise (right-hand rule),
462
% and negative angles shear clockwise. Angles are measured relative to a
463
% vertical Y-axis. X shears will widen an image creating 'empty' triangles
464
% on the left and right sides of the source image.
466
% The format of the XShearImage method is:
468
% void XShearImage(Image *image,const double degrees,
469
% const unsigned long width,const unsigned long height,
470
% const long x_offset,long y_offset)
472
% A description of each parameter follows.
474
% o image: The image.
476
% o degrees: A double representing the shearing angle along the X axis.
478
% o width, height, x_offset, y_offset: Defines a region of the image
483
static inline PixelPacket BlendComposite(const PixelPacket *p,
484
const PixelPacket *q,const double alpha)
492
color=((double) p->red*(MaxRGB-alpha)+q->red*alpha)/MaxRGB;
493
composite.red=(Quantum)
494
((color < 0) ? 0 : (color > MaxRGB) ? MaxRGB : color+0.5);
495
color=((double) p->green*(MaxRGB-alpha)+q->green*alpha)/MaxRGB;
496
composite.green=(Quantum)
497
((color < 0) ? 0 : (color > MaxRGB) ? MaxRGB : color+0.5);
498
color=((double) p->blue*(MaxRGB-alpha)+q->blue*alpha)/MaxRGB;
499
composite.blue=(Quantum)
500
((color < 0) ? 0 : (color > MaxRGB) ? MaxRGB : color+0.5);
501
composite.opacity=p->opacity;
505
static void XShearImage(Image *image,const double degrees,
506
const unsigned long width,const unsigned long height,const long x_offset,
509
#define XShearImageText " X Shear image... "
535
assert(image != (Image *) NULL);
536
is_grayscale=image->is_grayscale;
539
for (y=0; y < (long) height; y++)
542
displacement=degrees*(y-height/2.0);
543
if (displacement == 0.0)
545
if (displacement > 0.0)
549
displacement*=(-1.0);
552
step=(long) floor(displacement);
553
alpha=(double) MaxRGB*(displacement-step);
557
No fractional displacement-- just copy.
564
Transfer pixels left-to-right.
568
p=GetImagePixels(image,0,y_offset,image->columns,1);
569
if (p == (PixelPacket *) NULL)
573
(void) memcpy(q,p,width*sizeof(PixelPacket));
575
for (i=0; i < (long) step; i++)
576
*q++=image->background_color;
582
Transfer pixels right-to-left.
584
p=GetImagePixels(image,0,y_offset,image->columns,1);
585
if (p == (PixelPacket *) NULL)
589
for (i=0; i < (long) width; i++)
591
for (i=0; i < (long) step; i++)
592
*--q=image->background_color;
596
if (!SyncImagePixels(image))
601
Fractional displacement.
604
pixel=image->background_color;
610
Transfer pixels left-to-right.
614
p=GetImagePixels(image,0,y_offset,image->columns,1);
615
if (p == (PixelPacket *) NULL)
619
for (i=0; i < (long) width; i++)
621
if ((x_offset+i) < step)
627
*q++=BlendComposite(&pixel,p,alpha);
630
*q++=BlendComposite(&pixel,&image->background_color,alpha);
631
for (i=0; i < (step-1); i++)
632
*q++=image->background_color;
638
Transfer pixels right-to-left.
640
p=GetImagePixels(image,0,y_offset,image->columns,1);
641
if (p == (PixelPacket *) NULL)
645
for (i=0; i < (long) width; i++)
649
if ((x_offset+width+step-i) >= image->columns)
651
*q=BlendComposite(&pixel,p,alpha);
654
*--q=BlendComposite(&pixel,&image->background_color,alpha);
655
for (i=0; i < (step-1); i++)
656
*--q=image->background_color;
660
if (!SyncImagePixels(image))
662
if (QuantumTick(y,height))
663
if (!MagickMonitor(XShearImageText,y,height,&image->exception))
666
if (is_grayscale && IsGray(image->background_color))
667
image->is_grayscale=True;
671
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
675
+ Y S h e a r I m a g e %
679
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
681
% Procedure YShearImage shears the image in the Y direction with a shear
682
% angle of 'degrees'. Positive angles shear counter-clockwise (right-hand
683
% rule), and negative angles shear clockwise. Angles are measured relative
684
% to a horizontal X-axis. Y shears will increase the height of an image
685
% creating 'empty' triangles on the top and bottom of the source image.
687
% The format of the YShearImage method is:
689
% void YShearImage(Image *image,const double degrees,
690
% const unsigned long width,const unsigned long height,long x_offset,
691
% const long y_offset)
693
% A description of each parameter follows.
695
% o image: The image.
697
% o degrees: A double representing the shearing angle along the Y axis.
699
% o width, height, x_offset, y_offset: Defines a region of the image
704
static void YShearImage(Image *image,const double degrees,
705
const unsigned long width,const unsigned long height,long x_offset,
708
#define YShearImageText " Y Shear image... "
734
assert(image != (Image *) NULL);
735
is_grayscale=image->is_grayscale;
737
for (y=0; y < (long) width; y++)
740
displacement=degrees*(y-width/2.0);
741
if (displacement == 0.0)
743
if (displacement > 0.0)
747
displacement*=(-1.0);
750
step=(long) floor(displacement);
751
alpha=(double) MaxRGB*(displacement-step);
755
No fractional displacement-- just copy the pixels.
762
Transfer pixels top-to-bottom.
766
p=GetImagePixels(image,x_offset,0,1,image->rows);
767
if (p == (PixelPacket *) NULL)
771
(void) memcpy(q,p,height*sizeof(PixelPacket));
773
for (i=0; i < (long) step; i++)
774
*q++=image->background_color;
780
Transfer pixels bottom-to-top.
782
p=GetImagePixels(image,x_offset,0,1,image->rows);
783
if (p == (PixelPacket *) NULL)
787
for (i=0; i < (long) height; i++)
789
for (i=0; i < (long) step; i++)
790
*--q=image->background_color;
794
if (!SyncImagePixels(image))
799
Fractional displacment.
802
pixel=image->background_color;
808
Transfer pixels top-to-bottom.
812
p=GetImagePixels(image,x_offset,0,1,image->rows);
813
if (p == (PixelPacket *) NULL)
817
for (i=0; i < (long) height; i++)
819
if ((y_offset+i) < step)
825
*q++=BlendComposite(&pixel,p,alpha);
828
*q++=BlendComposite(&pixel,&image->background_color,alpha);
829
for (i=0; i < (step-1); i++)
830
*q++=image->background_color;
836
Transfer pixels bottom-to-top.
838
p=GetImagePixels(image,x_offset,0,1,image->rows);
839
if (p == (PixelPacket *) NULL)
843
for (i=0; i < (long) height; i++)
847
if ((y_offset+height+step-i) >= image->rows)
849
*q=BlendComposite(&pixel,p,alpha);
852
*--q=BlendComposite(&pixel,&image->background_color,alpha);
853
for (i=0; i < (step-1); i++)
854
*--q=image->background_color;
858
if (!SyncImagePixels(image))
860
if (QuantumTick(y,width))
861
if (!MagickMonitor(YShearImageText,y,width,&image->exception))
864
if (is_grayscale && IsGray(image->background_color))
865
image->is_grayscale=True;
869
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
873
% R o t a t e I m a g e %
877
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
879
% Method RotateImage creates a new image that is a rotated copy of an
880
% existing one. Positive angles rotate counter-clockwise (right-hand rule),
881
% while negative angles rotate clockwise. Rotated images are usually larger
882
% than the originals and have 'empty' triangular corners. X axis. Empty
883
% triangles left over from shearing the image are filled with the color
884
% specified by the image background_color. RotateImage allocates the memory
885
% necessary for the new Image structure and returns a pointer to the new
888
% Method RotateImage is based on the paper "A Fast Algorithm for General
889
% Raster Rotatation" by Alan W. Paeth. RotateImage is adapted from a similar
890
% method based on the Paeth paper written by Michael Halle of the Spatial
891
% Imaging Group, MIT Media Lab.
893
% The format of the RotateImage method is:
895
% Image *RotateImage(const Image *image,const double degrees,
896
% ExceptionInfo *exception)
898
% A description of each parameter follows.
900
% o status: Method RotateImage returns a pointer to the image after
901
% rotating. A null image is returned if there is a memory shortage.
903
% o image: The image; returned from
906
% o degrees: Specifies the number of degrees to rotate the image.
908
% o exception: Return any errors or warnings in this structure.
912
MagickExport Image *RotateImage(const Image *image,const double degrees,
913
ExceptionInfo *exception)
939
Adjust rotation angle.
941
assert(image != (Image *) NULL);
942
assert(image->signature == MagickSignature);
943
assert(exception != (ExceptionInfo *) NULL);
944
assert(exception->signature == MagickSignature);
946
while (angle < -45.0)
948
for (rotations=0; angle > 45.0; rotations++)
952
Calculate shear equations.
954
integral_image=IntegralRotateImage(image,rotations,exception);
955
if (integral_image == (Image *) NULL)
956
ThrowImageException3(ResourceLimitError,MemoryAllocationFailed,
957
UnableToRotateImage);
958
shear.x=(-tan(DegreesToRadians(angle)/2.0));
959
shear.y=sin(DegreesToRadians(angle));
960
if ((shear.x == 0.0) || (shear.y == 0.0))
961
return(integral_image);
965
width=image->columns;
967
if ((rotations == 1) || (rotations == 3))
970
height=image->columns;
972
x_offset=(long) ceil(fabs(2.0*height*shear.y)-0.5);
973
y_width=(unsigned long) floor(fabs(height*shear.x)+width+0.5);
974
y_offset=(long) ceil(fabs(y_width*shear.y)-0.5);
976
Surround image with a border.
978
integral_image->border_color=integral_image->background_color;
979
border_info.width=x_offset;
980
border_info.height=y_offset;
981
rotate_image=BorderImage(integral_image,&border_info,exception);
982
DestroyImage(integral_image);
983
if (rotate_image == (Image *) NULL)
984
ThrowImageException3(ResourceLimitError,MemoryAllocationFailed,
985
UnableToRotateImage);
989
rotate_image->storage_class=DirectClass;
990
rotate_image->matte|=rotate_image->background_color.opacity != OpaqueOpacity;
991
XShearImage(rotate_image,shear.x,width,height,x_offset,
992
(long) (rotate_image->rows-height)/2);
993
YShearImage(rotate_image,shear.y,y_width,height,
994
(long) (rotate_image->columns-y_width)/2,y_offset);
995
XShearImage(rotate_image,shear.x,y_width,rotate_image->rows,
996
(long) (rotate_image->columns-y_width)/2,0);
997
CropToFitImage(&rotate_image,shear.x,shear.y,width,height,True,exception);
998
rotate_image->page.width=0;
999
rotate_image->page.height=0;
1000
return(rotate_image);
1004
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1008
% S h e a r I m a g e %
1012
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1014
% Method ShearImage creates a new image that is a shear_image copy of an
1015
% existing one. Shearing slides one edge of an image along the X or Y
1016
% axis, creating a parallelogram. An X direction shear slides an edge
1017
% along the X axis, while a Y direction shear slides an edge along the Y
1018
% axis. The amount of the shear is controlled by a shear angle. For X
1019
% direction shears, x_shear is measured relative to the Y axis, and
1020
% similarly, for Y direction shears y_shear is measured relative to the
1021
% X axis. Empty triangles left over from shearing the image are filled
1022
% with the color defined by the pixel at location (0,0). ShearImage
1023
% allocates the memory necessary for the new Image structure and returns
1024
% a pointer to the new image.
1026
% Method ShearImage is based on the paper "A Fast Algorithm for General
1027
% Raster Rotatation" by Alan W. Paeth.
1029
% The format of the ShearImage method is:
1031
% Image *ShearImage(const Image *image,const double x_shear,
1032
% const double y_shear,ExceptionInfo *exception)
1034
% A description of each parameter follows.
1036
% o status: Method ShearImage returns a pointer to the image after
1037
% rotating. A null image is returned if there is a memory shortage.
1039
% o image: The image; returned from
1042
% o x_shear, y_shear: Specifies the number of degrees to shear the image.
1044
% o exception: Return any errors or warnings in this structure.
1048
MagickExport Image *ShearImage(const Image *image,const double x_shear,
1049
const double y_shear,ExceptionInfo *exception)
1068
assert(image != (Image *) NULL);
1069
assert(image->signature == MagickSignature);
1070
assert(exception != (ExceptionInfo *) NULL);
1071
assert(exception->signature == MagickSignature);
1072
if ((x_shear == 180.0) || (y_shear == 180.0))
1073
ThrowImageException3(ImageError,UnableToShearImage,AngleIsDiscontinuous);
1076
Initialize shear angle.
1078
integral_image=IntegralRotateImage(image,0,exception);
1079
if (integral_image == (Image *) NULL)
1080
ThrowImageException3(ResourceLimitError,MemoryAllocationFailed,
1081
UnableToShearImage);
1082
shear.x=(-tan(DegreesToRadians(x_shear)/2.0));
1083
shear.y=sin(DegreesToRadians(y_shear));
1084
if ((shear.x == 0.0) || (shear.y == 0.0))
1085
return(integral_image);
1090
x_offset=(long) ceil(fabs(2.0*image->rows*shear.x)-0.5);
1091
y_width=(unsigned long) floor(fabs(image->rows*shear.x)+image->columns+0.5);
1092
y_offset=(long) ceil(fabs(y_width*shear.y)-0.5);
1094
Surround image with border.
1096
integral_image->border_color=integral_image->background_color;
1097
border_info.width=x_offset;
1098
border_info.height=y_offset;
1099
shear_image=BorderImage(integral_image,&border_info,exception);
1100
if (shear_image == (Image *) NULL)
1101
ThrowImageException3(ResourceLimitError,MemoryAllocationFailed,
1102
UnableToShearImage);
1103
DestroyImage(integral_image);
1107
shear_image->storage_class=DirectClass;
1108
shear_image->matte|=shear_image->background_color.opacity != OpaqueOpacity;
1109
XShearImage(shear_image,shear.x,image->columns,image->rows,x_offset,
1110
(long) (shear_image->rows-image->rows)/2);
1111
YShearImage(shear_image,shear.y,y_width,image->rows,
1112
(long) (shear_image->columns-y_width)/2,y_offset);
1113
CropToFitImage(&shear_image,shear.x,shear.y,image->columns,image->rows,
1115
shear_image->page.width=0;
1116
shear_image->page.height=0;
1117
return(shear_image);