shear.c revision 17f11b056210f082a6d0e54ac5d68e6d72fa76b2
1/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3%                                                                             %
4%                                                                             %
5%                                                                             %
6%                      SSSSS  H   H  EEEEE   AAA    RRRR                      %
7%                      SS     H   H  E      A   A   R   R                     %
8%                       SSS   HHHHH  EEE    AAAAA   RRRR                      %
9%                         SS  H   H  E      A   A   R R                       %
10%                      SSSSS  H   H  EEEEE  A   A   R  R                      %
11%                                                                             %
12%                                                                             %
13%    MagickCore Methods to Shear or Rotate an Image by an Arbitrary Angle     %
14%                                                                             %
15%                               Software Design                               %
16%                                    Cristy                                   %
17%                                  July 1992                                  %
18%                                                                             %
19%                                                                             %
20%  Copyright 1999-2015 ImageMagick Studio LLC, a non-profit organization      %
21%  dedicated to making software imaging solutions freely available.           %
22%                                                                             %
23%  You may not use this file except in compliance with the License.  You may  %
24%  obtain a copy of the License at                                            %
25%                                                                             %
26%    http://www.imagemagick.org/script/license.php                            %
27%                                                                             %
28%  Unless required by applicable law or agreed to in writing, software        %
29%  distributed under the License is distributed on an "AS IS" BASIS,          %
30%  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31%  See the License for the specific language governing permissions and        %
32%  limitations under the License.                                             %
33%                                                                             %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36%  The XShearImage() and YShearImage() methods are based on the paper "A Fast
37%  Algorithm for General Raster Rotatation" by Alan W. Paeth, Graphics
38%  Interface '86 (Vancouver).  ShearRotateImage() is adapted from a similar
39%  method based on the Paeth paper written by Michael Halle of the Spatial
40%  Imaging Group, MIT Media Lab.
41%
42*/
43
44/*
45  Include declarations.
46*/
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"
82
83/*
84%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
85%                                                                             %
86%                                                                             %
87%                                                                             %
88+   C r o p T o F i t I m a g e                                               %
89%                                                                             %
90%                                                                             %
91%                                                                             %
92%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
93%
94%  CropToFitImage() crops the sheared image as determined by the bounding box
95%  as defined by width and height and shearing angles.
96%
97%  The format of the CropToFitImage method is:
98%
99%      MagickBooleanType CropToFitImage(Image **image,
100%        const double x_shear,const double x_shear,
101%        const double width,const double height,
102%        const MagickBooleanType rotate,ExceptionInfo *exception)
103%
104%  A description of each parameter follows.
105%
106%    o image: the image.
107%
108%    o x_shear, y_shear, width, height: Defines a region of the image to crop.
109%
110%    o exception: return any errors or warnings in this structure.
111%
112*/
113static MagickBooleanType CropToFitImage(Image **image,
114  const double x_shear,const double y_shear,
115  const double width,const double height,
116  const MagickBooleanType rotate,ExceptionInfo *exception)
117{
118  Image
119    *crop_image;
120
121  PointInfo
122    extent[4],
123    min,
124    max;
125
126  RectangleInfo
127    geometry,
128    page;
129
130  register ssize_t
131    i;
132
133  /*
134    Calculate the rotated image size.
135  */
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=0; i < 4; i++)
145  {
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;
152  }
153  min=extent[0];
154  max=extent[0];
155  for (i=1; i < 4; i++)
156  {
157    if (min.x > extent[i].x)
158      min.x=extent[i].x;
159    if (min.y > extent[i].y)
160      min.y=extent[i].y;
161    if (max.x < extent[i].x)
162      max.x=extent[i].x;
163    if (max.y < extent[i].y)
164      max.y=extent[i].y;
165  }
166  geometry.x=(ssize_t) ceil(min.x-0.5);
167  geometry.y=(ssize_t) ceil(min.y-0.5);
168  geometry.width=(size_t) floor(max.x-min.x+0.5);
169  geometry.height=(size_t) floor(max.y-min.y+0.5);
170  page=(*image)->page;
171  (void) ParseAbsoluteGeometry("0x0+0+0",&(*image)->page);
172  crop_image=CropImage(*image,&geometry,exception);
173  if (crop_image == (Image *) NULL)
174    return(MagickFalse);
175  crop_image->page=page;
176  *image=DestroyImage(*image);
177  *image=crop_image;
178  return(MagickTrue);
179}
180
181/*
182%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
183%                                                                             %
184%                                                                             %
185%                                                                             %
186%     D e s k e w I m a g e                                                   %
187%                                                                             %
188%                                                                             %
189%                                                                             %
190%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
191%
192%  DeskewImage() removes skew from the image.  Skew is an artifact that
193%  occurs in scanned images because of the camera being misaligned,
194%  imperfections in the scanning or surface, or simply because the paper was
195%  not placed completely flat when scanned.
196%
197%  The result will be auto-croped if the artifact "deskew:auto-crop" is
198%  defined, while the amount the image is to be deskewed, in degrees is also
199%  saved as the artifact "deskew:angle".
200%
201%  If the artifact "deskew:auto-crop" is given the image will be automatically
202%  cropped of the excess background.  The value is the border width of all
203%  pixels around the edge that will be used to determine an average border
204%  color for the automatic trim.
205%
206%  The format of the DeskewImage method is:
207%
208%      Image *DeskewImage(const Image *image,const double threshold,
209%        ExceptionInfo *exception)
210%
211%  A description of each parameter follows:
212%
213%    o image: the image.
214%
215%    o threshold: separate background from foreground.
216%
217%    o exception: return any errors or warnings in this structure.
218%
219*/
220
221static void RadonProjection(const Image *image,MatrixInfo *source_matrixs,
222  MatrixInfo *destination_matrixs,const ssize_t sign,size_t *projection)
223{
224  MatrixInfo
225    *swap;
226
227  register MatrixInfo
228    *p,
229    *q;
230
231  register ssize_t
232    x;
233
234  size_t
235    step;
236
237  p=source_matrixs;
238  q=destination_matrixs;
239  for (step=1; step < GetMatrixColumns(p); step*=2)
240  {
241    for (x=0; x < (ssize_t) GetMatrixColumns(p); x+=2*(ssize_t) step)
242    {
243      register ssize_t
244        i;
245
246      ssize_t
247        y;
248
249      unsigned short
250        element,
251        neighbor;
252
253      for (i=0; i < (ssize_t) step; i++)
254      {
255        for (y=0; y < (ssize_t) (GetMatrixRows(p)-i-1); y++)
256        {
257          if (GetMatrixElement(p,x+i,y,&element) == MagickFalse)
258            continue;
259          if (GetMatrixElement(p,x+i+step,y+i,&neighbor) == MagickFalse)
260            continue;
261          neighbor+=element;
262          if (SetMatrixElement(q,x+2*i,y,&neighbor) == MagickFalse)
263            continue;
264          if (GetMatrixElement(p,x+i+step,y+i+1,&neighbor) == MagickFalse)
265            continue;
266          neighbor+=element;
267          if (SetMatrixElement(q,x+2*i+1,y,&neighbor) == MagickFalse)
268            continue;
269        }
270        for ( ; y < (ssize_t) (GetMatrixRows(p)-i); y++)
271        {
272          if (GetMatrixElement(p,x+i,y,&element) == MagickFalse)
273            continue;
274          if (GetMatrixElement(p,x+i+step,y+i,&neighbor) == MagickFalse)
275            continue;
276          neighbor+=element;
277          if (SetMatrixElement(q,x+2*i,y,&neighbor) == MagickFalse)
278            continue;
279          if (SetMatrixElement(q,x+2*i+1,y,&element) == MagickFalse)
280            continue;
281        }
282        for ( ; y < (ssize_t) GetMatrixRows(p); y++)
283        {
284          if (GetMatrixElement(p,x+i,y,&element) == MagickFalse)
285            continue;
286          if (SetMatrixElement(q,x+2*i,y,&element) == MagickFalse)
287            continue;
288          if (SetMatrixElement(q,x+2*i+1,y,&element) == MagickFalse)
289            continue;
290        }
291      }
292    }
293    swap=p;
294    p=q;
295    q=swap;
296  }
297#if defined(MAGICKCORE_OPENMP_SUPPORT)
298  #pragma omp parallel for schedule(static,4) \
299    magick_threads(image,image,1,1)
300#endif
301  for (x=0; x < (ssize_t) GetMatrixColumns(p); x++)
302  {
303    register ssize_t
304      y;
305
306    size_t
307      sum;
308
309    sum=0;
310    for (y=0; y < (ssize_t) (GetMatrixRows(p)-1); y++)
311    {
312      ssize_t
313        delta;
314
315      unsigned short
316        element,
317        neighbor;
318
319      if (GetMatrixElement(p,x,y,&element) == MagickFalse)
320        continue;
321      if (GetMatrixElement(p,x,y+1,&neighbor) == MagickFalse)
322        continue;
323      delta=(ssize_t) element-(ssize_t) neighbor;
324      sum+=delta*delta;
325    }
326    projection[GetMatrixColumns(p)+sign*x-1]=sum;
327  }
328}
329
330static MagickBooleanType RadonTransform(const Image *image,
331  const double threshold,size_t *projection,ExceptionInfo *exception)
332{
333  CacheView
334    *image_view;
335
336  MatrixInfo
337    *destination_matrixs,
338    *source_matrixs;
339
340  MagickBooleanType
341    status;
342
343  register ssize_t
344    i;
345
346  size_t
347    count,
348    width;
349
350  ssize_t
351    y;
352
353  unsigned char
354    byte;
355
356  unsigned short
357    bits[256];
358
359  for (width=1; width < ((image->columns+7)/8); width<<=1) ;
360  source_matrixs=AcquireMatrixInfo(width,image->rows,sizeof(unsigned short),
361    exception);
362  destination_matrixs=AcquireMatrixInfo(width,image->rows,sizeof(unsigned short),
363    exception);
364  if ((source_matrixs == (MatrixInfo *) NULL) ||
365      (destination_matrixs == (MatrixInfo *) NULL))
366    {
367      if (destination_matrixs != (MatrixInfo *) NULL)
368        destination_matrixs=DestroyMatrixInfo(destination_matrixs);
369      if (source_matrixs != (MatrixInfo *) NULL)
370        source_matrixs=DestroyMatrixInfo(source_matrixs);
371      return(MagickFalse);
372    }
373  if (NullMatrix(source_matrixs) == MagickFalse)
374    {
375      destination_matrixs=DestroyMatrixInfo(destination_matrixs);
376      source_matrixs=DestroyMatrixInfo(source_matrixs);
377      return(MagickFalse);
378    }
379  for (i=0; i < 256; i++)
380  {
381    byte=(unsigned char) i;
382    for (count=0; byte != 0; byte>>=1)
383      count+=byte & 0x01;
384    bits[i]=(unsigned short) count;
385  }
386  status=MagickTrue;
387  image_view=AcquireVirtualCacheView(image,exception);
388#if defined(MAGICKCORE_OPENMP_SUPPORT)
389  #pragma omp parallel for schedule(static,4) shared(status) \
390    magick_threads(image,image,1,1)
391#endif
392  for (y=0; y < (ssize_t) image->rows; y++)
393  {
394    register const Quantum
395      *restrict p;
396
397    register ssize_t
398      i,
399      x;
400
401    size_t
402      bit,
403      byte;
404
405    unsigned short
406      value;
407
408    if (status == MagickFalse)
409      continue;
410    p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
411    if (p == (const Quantum *) NULL)
412      {
413        status=MagickFalse;
414        continue;
415      }
416    bit=0;
417    byte=0;
418    i=(ssize_t) (image->columns+7)/8;
419    for (x=0; x < (ssize_t) image->columns; x++)
420    {
421      byte<<=1;
422      if (((MagickRealType) GetPixelRed(image,p) < threshold) ||
423          ((MagickRealType) GetPixelGreen(image,p) < threshold) ||
424          ((MagickRealType) GetPixelBlue(image,p) < threshold))
425        byte|=0x01;
426      bit++;
427      if (bit == 8)
428        {
429          value=bits[byte];
430          (void) SetMatrixElement(source_matrixs,--i,y,&value);
431          bit=0;
432          byte=0;
433        }
434      p+=GetPixelChannels(image);
435    }
436    if (bit != 0)
437      {
438        byte<<=(8-bit);
439        value=bits[byte];
440        (void) SetMatrixElement(source_matrixs,--i,y,&value);
441      }
442  }
443  RadonProjection(image,source_matrixs,destination_matrixs,-1,projection);
444  (void) NullMatrix(source_matrixs);
445#if defined(MAGICKCORE_OPENMP_SUPPORT)
446  #pragma omp parallel for schedule(static,4) shared(status) \
447    magick_threads(image,image,image->rows,1)
448#endif
449  for (y=0; y < (ssize_t) image->rows; y++)
450  {
451    register const Quantum
452      *restrict p;
453
454    register ssize_t
455      i,
456      x;
457
458    size_t
459      bit,
460      byte;
461
462    unsigned short
463     value;
464
465    if (status == MagickFalse)
466      continue;
467    p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
468    if (p == (const Quantum *) NULL)
469      {
470        status=MagickFalse;
471        continue;
472      }
473    bit=0;
474    byte=0;
475    i=0;
476    for (x=0; x < (ssize_t) image->columns; x++)
477    {
478      byte<<=1;
479      if (((MagickRealType) GetPixelRed(image,p) < threshold) ||
480          ((MagickRealType) GetPixelGreen(image,p) < threshold) ||
481          ((MagickRealType) GetPixelBlue(image,p) < threshold))
482        byte|=0x01;
483      bit++;
484      if (bit == 8)
485        {
486          value=bits[byte];
487          (void) SetMatrixElement(source_matrixs,i++,y,&value);
488          bit=0;
489          byte=0;
490        }
491      p+=GetPixelChannels(image);
492    }
493    if (bit != 0)
494      {
495        byte<<=(8-bit);
496        value=bits[byte];
497        (void) SetMatrixElement(source_matrixs,i++,y,&value);
498      }
499  }
500  RadonProjection(image,source_matrixs,destination_matrixs,1,projection);
501  image_view=DestroyCacheView(image_view);
502  destination_matrixs=DestroyMatrixInfo(destination_matrixs);
503  source_matrixs=DestroyMatrixInfo(source_matrixs);
504  return(MagickTrue);
505}
506
507static void GetImageBackgroundColor(Image *image,const ssize_t offset,
508  ExceptionInfo *exception)
509{
510  CacheView
511    *image_view;
512
513  PixelInfo
514    background;
515
516  double
517    count;
518
519  ssize_t
520    y;
521
522  /*
523    Compute average background color.
524  */
525  if (offset <= 0)
526    return;
527  GetPixelInfo(image,&background);
528  count=0.0;
529  image_view=AcquireVirtualCacheView(image,exception);
530  for (y=0; y < (ssize_t) image->rows; y++)
531  {
532    register const Quantum
533      *restrict p;
534
535    register ssize_t
536      x;
537
538    if ((y >= offset) && (y < ((ssize_t) image->rows-offset)))
539      continue;
540    p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
541    if (p == (const Quantum *) NULL)
542      continue;
543    for (x=0; x < (ssize_t) image->columns; x++)
544    {
545      if ((x >= offset) && (x < ((ssize_t) image->columns-offset)))
546        continue;
547      background.red+=QuantumScale*GetPixelRed(image,p);
548      background.green+=QuantumScale*GetPixelGreen(image,p);
549      background.blue+=QuantumScale*GetPixelBlue(image,p);
550      if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
551        background.alpha+=QuantumScale*GetPixelAlpha(image,p);
552      count++;
553      p+=GetPixelChannels(image);
554    }
555  }
556  image_view=DestroyCacheView(image_view);
557  image->background_color.red=(double) ClampToQuantum(QuantumRange*
558    background.red/count);
559  image->background_color.green=(double) ClampToQuantum(QuantumRange*
560    background.green/count);
561  image->background_color.blue=(double) ClampToQuantum(QuantumRange*
562    background.blue/count);
563  if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
564    image->background_color.alpha=(double) ClampToQuantum(QuantumRange*
565      background.alpha/count);
566}
567
568MagickExport Image *DeskewImage(const Image *image,const double threshold,
569  ExceptionInfo *exception)
570{
571  AffineMatrix
572    affine_matrix;
573
574  const char
575    *artifact;
576
577  double
578    degrees;
579
580  Image
581    *clone_image,
582    *crop_image,
583    *deskew_image,
584    *median_image;
585
586  MagickBooleanType
587    status;
588
589  RectangleInfo
590    geometry;
591
592  register ssize_t
593    i;
594
595  size_t
596    max_projection,
597    *projection,
598    width;
599
600  ssize_t
601    skew;
602
603  /*
604    Compute deskew angle.
605  */
606  for (width=1; width < ((image->columns+7)/8); width<<=1) ;
607  projection=(size_t *) AcquireQuantumMemory((size_t) (2*width-1),
608    sizeof(*projection));
609  if (projection == (size_t *) NULL)
610    ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
611  status=RadonTransform(image,threshold,projection,exception);
612  if (status == MagickFalse)
613    {
614      projection=(size_t *) RelinquishMagickMemory(projection);
615      ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
616    }
617  max_projection=0;
618  skew=0;
619  for (i=0; i < (ssize_t) (2*width-1); i++)
620  {
621    if (projection[i] > max_projection)
622      {
623        skew=i-(ssize_t) width+1;
624        max_projection=projection[i];
625      }
626  }
627  projection=(size_t *) RelinquishMagickMemory(projection);
628  degrees=RadiansToDegrees(-atan((double) skew/width/8));
629  if (image->debug != MagickFalse)
630    (void) LogMagickEvent(TransformEvent,GetMagickModule(),
631      "  Deskew angle: %g",degrees);
632  /*
633    Deskew image.
634  */
635  clone_image=CloneImage(image,0,0,MagickTrue,exception);
636  if (clone_image == (Image *) NULL)
637    return((Image *) NULL);
638  {
639    char
640      angle[MaxTextExtent];
641
642    (void) FormatLocaleString(angle,MaxTextExtent,"%.20g",degrees);
643    (void) SetImageArtifact(clone_image,"deskew:angle",angle);
644  }
645  (void) SetImageVirtualPixelMethod(clone_image,BackgroundVirtualPixelMethod,
646    exception);
647  affine_matrix.sx=cos(DegreesToRadians(fmod((double) degrees,360.0)));
648  affine_matrix.rx=sin(DegreesToRadians(fmod((double) degrees,360.0)));
649  affine_matrix.ry=(-sin(DegreesToRadians(fmod((double) degrees,360.0))));
650  affine_matrix.sy=cos(DegreesToRadians(fmod((double) degrees,360.0)));
651  affine_matrix.tx=0.0;
652  affine_matrix.ty=0.0;
653  artifact=GetImageArtifact(image,"deskew:auto-crop");
654  if (artifact == (const char *) NULL)
655    {
656      deskew_image=AffineTransformImage(clone_image,&affine_matrix,exception);
657      clone_image=DestroyImage(clone_image);
658      return(deskew_image);
659    }
660  /*
661    Auto-crop image.
662  */
663  GetImageBackgroundColor(clone_image,(ssize_t) StringToLong(artifact),
664    exception);
665  deskew_image=AffineTransformImage(clone_image,&affine_matrix,exception);
666  clone_image=DestroyImage(clone_image);
667  if (deskew_image == (Image *) NULL)
668    return((Image *) NULL);
669  median_image=StatisticImage(deskew_image,MedianStatistic,3,3,exception);
670  if (median_image == (Image *) NULL)
671    {
672      deskew_image=DestroyImage(deskew_image);
673      return((Image *) NULL);
674    }
675  geometry=GetImageBoundingBox(median_image,exception);
676  median_image=DestroyImage(median_image);
677  if (image->debug != MagickFalse)
678    (void) LogMagickEvent(TransformEvent,GetMagickModule(),"  Deskew geometry: "
679      "%.20gx%.20g%+.20g%+.20g",(double) geometry.width,(double)
680      geometry.height,(double) geometry.x,(double) geometry.y);
681  crop_image=CropImage(deskew_image,&geometry,exception);
682  deskew_image=DestroyImage(deskew_image);
683  return(crop_image);
684}
685
686/*
687%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
688%                                                                             %
689%                                                                             %
690%                                                                             %
691%   I n t e g r a l R o t a t e I m a g e                                     %
692%                                                                             %
693%                                                                             %
694%                                                                             %
695%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
696%
697%  IntegralRotateImage() rotates the image an integral of 90 degrees.  It
698%  allocates the memory necessary for the new Image structure and returns a
699%  pointer to the rotated image.
700%
701%  The format of the IntegralRotateImage method is:
702%
703%      Image *IntegralRotateImage(const Image *image,size_t rotations,
704%        ExceptionInfo *exception)
705%
706%  A description of each parameter follows.
707%
708%    o image: the image.
709%
710%    o rotations: Specifies the number of 90 degree rotations.
711%
712*/
713MagickExport Image *IntegralRotateImage(const Image *image,size_t rotations,
714  ExceptionInfo *exception)
715{
716#define RotateImageTag  "Rotate/Image"
717
718  CacheView
719    *image_view,
720    *rotate_view;
721
722  Image
723    *rotate_image;
724
725  MagickBooleanType
726    status;
727
728  MagickOffsetType
729    progress;
730
731  RectangleInfo
732    page;
733
734  ssize_t
735    y;
736
737  /*
738    Initialize rotated image attributes.
739  */
740  assert(image != (Image *) NULL);
741  page=image->page;
742  rotations%=4;
743  if (rotations == 0)
744    return(CloneImage(image,0,0,MagickTrue,exception));
745  if ((rotations == 1) || (rotations == 3))
746    rotate_image=CloneImage(image,image->rows,image->columns,MagickTrue,
747      exception);
748  else
749    rotate_image=CloneImage(image,image->columns,image->rows,MagickTrue,
750      exception);
751  if (rotate_image == (Image *) NULL)
752    return((Image *) NULL);
753  /*
754    Integral rotate the image.
755  */
756  status=MagickTrue;
757  progress=0;
758  image_view=AcquireVirtualCacheView(image,exception);
759  rotate_view=AcquireAuthenticCacheView(rotate_image,exception);
760  switch (rotations)
761  {
762    case 1:
763    {
764      size_t
765        tile_height,
766        tile_width;
767
768      ssize_t
769        tile_y;
770
771      /*
772        Rotate 90 degrees.
773      */
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,4) shared(status) \
778        magick_threads(image,image,1,1)
779#endif
780      for (tile_y=0; tile_y < (ssize_t) image->rows; tile_y+=(ssize_t) tile_height)
781      {
782        register ssize_t
783          tile_x;
784
785        if (status == MagickFalse)
786          continue;
787        tile_x=0;
788        for ( ; tile_x < (ssize_t) image->columns; tile_x+=(ssize_t) tile_width)
789        {
790          MagickBooleanType
791            sync;
792
793          register const Quantum
794            *restrict p;
795
796          register Quantum
797            *restrict q;
798
799          register ssize_t
800            y;
801
802          size_t
803            height,
804            width;
805
806          width=tile_width;
807          if ((tile_x+(ssize_t) tile_width) > (ssize_t) image->columns)
808            width=(size_t) (tile_width-(tile_x+tile_width-image->columns));
809          height=tile_height;
810          if ((tile_y+(ssize_t) tile_height) > (ssize_t) image->rows)
811            height=(size_t) (tile_height-(tile_y+tile_height-image->rows));
812          p=GetCacheViewVirtualPixels(image_view,tile_x,tile_y,width,height,
813            exception);
814          if (p == (const Quantum *) NULL)
815            {
816              status=MagickFalse;
817              break;
818            }
819          for (y=0; y < (ssize_t) width; y++)
820          {
821            register const Quantum
822              *restrict tile_pixels;
823
824            register ssize_t
825              x;
826
827            if (status == MagickFalse)
828              continue;
829            q=QueueCacheViewAuthenticPixels(rotate_view,(ssize_t)
830              (rotate_image->columns-(tile_y+height)),y+tile_x,height,1,
831              exception);
832            if (q == (Quantum *) NULL)
833              {
834                status=MagickFalse;
835                continue;
836              }
837            tile_pixels=p+((height-1)*width+y)*GetPixelChannels(image);
838            for (x=0; x < (ssize_t) height; x++)
839            {
840              register ssize_t
841                i;
842
843              if (GetPixelReadMask(image,tile_pixels) == 0)
844                {
845                  tile_pixels-=width*GetPixelChannels(image);
846                  q+=GetPixelChannels(rotate_image);
847                  continue;
848                }
849              for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
850              {
851                PixelChannel channel=GetPixelChannelChannel(image,i);
852                PixelTrait traits=GetPixelChannelTraits(image,channel);
853                PixelTrait rotate_traits=GetPixelChannelTraits(rotate_image,
854                  channel);
855                if ((traits == UndefinedPixelTrait) ||
856                    (rotate_traits == UndefinedPixelTrait))
857                  continue;
858                SetPixelChannel(rotate_image,channel,tile_pixels[i],q);
859              }
860              tile_pixels-=width*GetPixelChannels(image);
861              q+=GetPixelChannels(rotate_image);
862            }
863            sync=SyncCacheViewAuthenticPixels(rotate_view,exception);
864            if (sync == MagickFalse)
865              status=MagickFalse;
866          }
867        }
868        if (image->progress_monitor != (MagickProgressMonitor) NULL)
869          {
870            MagickBooleanType
871              proceed;
872
873#if defined(MAGICKCORE_OPENMP_SUPPORT)
874            #pragma omp critical (MagickCore_IntegralRotateImage)
875#endif
876            proceed=SetImageProgress(image,RotateImageTag,progress+=tile_height,
877              image->rows);
878            if (proceed == MagickFalse)
879              status=MagickFalse;
880          }
881      }
882      (void) SetImageProgress(image,RotateImageTag,(MagickOffsetType)
883        image->rows-1,image->rows);
884      Swap(page.width,page.height);
885      Swap(page.x,page.y);
886      if (page.width != 0)
887        page.x=(ssize_t) (page.width-rotate_image->columns-page.x);
888      break;
889    }
890    case 2:
891    {
892      /*
893        Rotate 180 degrees.
894      */
895#if defined(MAGICKCORE_OPENMP_SUPPORT)
896      #pragma omp parallel for schedule(static,4) shared(status) \
897        magick_threads(image,image,1,1)
898#endif
899      for (y=0; y < (ssize_t) image->rows; y++)
900      {
901        MagickBooleanType
902          sync;
903
904        register const Quantum
905          *restrict p;
906
907        register Quantum
908          *restrict q;
909
910        register ssize_t
911          x;
912
913        if (status == MagickFalse)
914          continue;
915        p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
916        q=QueueCacheViewAuthenticPixels(rotate_view,0,(ssize_t) (image->rows-y-
917          1),image->columns,1,exception);
918        if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
919          {
920            status=MagickFalse;
921            continue;
922          }
923        q+=GetPixelChannels(rotate_image)*image->columns;
924        for (x=0; x < (ssize_t) image->columns; x++)
925        {
926          register ssize_t
927            i;
928
929          q-=GetPixelChannels(rotate_image);
930          if (GetPixelReadMask(image,p) == 0)
931            {
932              p+=GetPixelChannels(image);
933              continue;
934            }
935          for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
936          {
937            PixelChannel channel=GetPixelChannelChannel(image,i);
938            PixelTrait traits=GetPixelChannelTraits(image,channel);
939            PixelTrait rotate_traits=GetPixelChannelTraits(rotate_image,
940              channel);
941            if ((traits == UndefinedPixelTrait) ||
942                (rotate_traits == UndefinedPixelTrait))
943              continue;
944            SetPixelChannel(rotate_image,channel,p[i],q);
945          }
946          p+=GetPixelChannels(image);
947        }
948        sync=SyncCacheViewAuthenticPixels(rotate_view,exception);
949        if (sync == MagickFalse)
950          status=MagickFalse;
951        if (image->progress_monitor != (MagickProgressMonitor) NULL)
952          {
953            MagickBooleanType
954              proceed;
955
956#if defined(MAGICKCORE_OPENMP_SUPPORT)
957            #pragma omp critical (MagickCore_IntegralRotateImage)
958#endif
959            proceed=SetImageProgress(image,RotateImageTag,progress++,
960              image->rows);
961            if (proceed == MagickFalse)
962              status=MagickFalse;
963          }
964      }
965      (void) SetImageProgress(image,RotateImageTag,(MagickOffsetType)
966        image->rows-1,image->rows);
967      Swap(page.width,page.height);
968      Swap(page.x,page.y);
969      if (page.width != 0)
970        page.x=(ssize_t) (page.width-rotate_image->columns-page.x);
971      break;
972    }
973    case 3:
974    {
975      size_t
976        tile_height,
977        tile_width;
978
979      ssize_t
980        tile_y;
981
982      /*
983        Rotate 270 degrees.
984      */
985      GetPixelCacheTileSize(image,&tile_width,&tile_height);
986      tile_width=image->columns;
987#if defined(MAGICKCORE_OPENMP_SUPPORT)
988      #pragma omp parallel for schedule(static,4) shared(status) \
989        magick_threads(image,image,1,1)
990#endif
991      for (tile_y=0; tile_y < (ssize_t) image->rows; tile_y+=(ssize_t) tile_height)
992      {
993        register ssize_t
994          tile_x;
995
996        if (status == MagickFalse)
997          continue;
998        tile_x=0;
999        for ( ; tile_x < (ssize_t) image->columns; tile_x+=(ssize_t) tile_width)
1000        {
1001          MagickBooleanType
1002            sync;
1003
1004          register const Quantum
1005            *restrict p;
1006
1007          register Quantum
1008            *restrict q;
1009
1010          register ssize_t
1011            y;
1012
1013          size_t
1014            height,
1015            width;
1016
1017          width=tile_width;
1018          if ((tile_x+(ssize_t) tile_width) > (ssize_t) image->columns)
1019            width=(size_t) (tile_width-(tile_x+tile_width-image->columns));
1020          height=tile_height;
1021          if ((tile_y+(ssize_t) tile_height) > (ssize_t) image->rows)
1022            height=(size_t) (tile_height-(tile_y+tile_height-image->rows));
1023          p=GetCacheViewVirtualPixels(image_view,tile_x,tile_y,width,height,
1024            exception);
1025          if (p == (const Quantum *) NULL)
1026            {
1027              status=MagickFalse;
1028              break;
1029            }
1030          for (y=0; y < (ssize_t) width; y++)
1031          {
1032            register const Quantum
1033              *restrict tile_pixels;
1034
1035            register ssize_t
1036              x;
1037
1038            if (status == MagickFalse)
1039              continue;
1040            q=QueueCacheViewAuthenticPixels(rotate_view,tile_y,(ssize_t) (y+
1041              rotate_image->rows-(tile_x+width)),height,1,exception);
1042            if (q == (Quantum *) NULL)
1043              {
1044                status=MagickFalse;
1045                continue;
1046              }
1047            tile_pixels=p+((width-1)-y)*GetPixelChannels(image);
1048            for (x=0; x < (ssize_t) height; x++)
1049            {
1050              register ssize_t
1051                i;
1052
1053              if (GetPixelReadMask(image,tile_pixels) == 0)
1054                {
1055                  tile_pixels+=width*GetPixelChannels(image);
1056                  q+=GetPixelChannels(rotate_image);
1057                  continue;
1058                }
1059              for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1060              {
1061                PixelChannel channel=GetPixelChannelChannel(image,i);
1062                PixelTrait traits=GetPixelChannelTraits(image,channel);
1063                PixelTrait rotate_traits=GetPixelChannelTraits(rotate_image,
1064                  channel);
1065                if ((traits == UndefinedPixelTrait) ||
1066                    (rotate_traits == UndefinedPixelTrait))
1067                  continue;
1068                SetPixelChannel(rotate_image,channel,tile_pixels[i],q);
1069              }
1070              tile_pixels+=width*GetPixelChannels(image);
1071              q+=GetPixelChannels(rotate_image);
1072            }
1073#if defined(MAGICKCORE_OPENMP_SUPPORT)
1074            #pragma omp critical (MagickCore_IntegralRotateImage)
1075#endif
1076            sync=SyncCacheViewAuthenticPixels(rotate_view,exception);
1077            if (sync == MagickFalse)
1078              status=MagickFalse;
1079          }
1080        }
1081        if (image->progress_monitor != (MagickProgressMonitor) NULL)
1082          {
1083            MagickBooleanType
1084              proceed;
1085
1086            proceed=SetImageProgress(image,RotateImageTag,progress+=tile_height,
1087              image->rows);
1088            if (proceed == MagickFalse)
1089              status=MagickFalse;
1090          }
1091      }
1092      (void) SetImageProgress(image,RotateImageTag,(MagickOffsetType)
1093        image->rows-1,image->rows);
1094      Swap(page.width,page.height);
1095      Swap(page.x,page.y);
1096      if (page.width != 0)
1097        page.x=(ssize_t) (page.width-rotate_image->columns-page.x);
1098      break;
1099    }
1100    default:
1101      break;
1102  }
1103  rotate_view=DestroyCacheView(rotate_view);
1104  image_view=DestroyCacheView(image_view);
1105  rotate_image->type=image->type;
1106  rotate_image->page=page;
1107  if (status == MagickFalse)
1108    rotate_image=DestroyImage(rotate_image);
1109  return(rotate_image);
1110}
1111
1112/*
1113%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1114%                                                                             %
1115%                                                                             %
1116%                                                                             %
1117+   X S h e a r I m a g e                                                     %
1118%                                                                             %
1119%                                                                             %
1120%                                                                             %
1121%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1122%
1123%  XShearImage() shears the image in the X direction with a shear angle of
1124%  'degrees'.  Positive angles shear counter-clockwise (right-hand rule), and
1125%  negative angles shear clockwise.  Angles are measured relative to a vertical
1126%  Y-axis.  X shears will widen an image creating 'empty' triangles on the left
1127%  and right sides of the source image.
1128%
1129%  The format of the XShearImage method is:
1130%
1131%      MagickBooleanType XShearImage(Image *image,const double degrees,
1132%        const size_t width,const size_t height,
1133%        const ssize_t x_offset,const ssize_t y_offset,ExceptionInfo *exception)
1134%
1135%  A description of each parameter follows.
1136%
1137%    o image: the image.
1138%
1139%    o degrees: A double representing the shearing angle along the X
1140%      axis.
1141%
1142%    o width, height, x_offset, y_offset: Defines a region of the image
1143%      to shear.
1144%
1145%    o exception: return any errors or warnings in this structure.
1146%
1147*/
1148static MagickBooleanType XShearImage(Image *image,const double degrees,
1149  const size_t width,const size_t height,const ssize_t x_offset,
1150  const ssize_t y_offset,ExceptionInfo *exception)
1151{
1152#define XShearImageTag  "XShear/Image"
1153
1154  typedef enum
1155  {
1156    LEFT,
1157    RIGHT
1158  } ShearDirection;
1159
1160  CacheView
1161    *image_view;
1162
1163  MagickBooleanType
1164    status;
1165
1166  MagickOffsetType
1167    progress;
1168
1169  PixelInfo
1170    background;
1171
1172  ssize_t
1173    y;
1174
1175  /*
1176    X shear image.
1177  */
1178  assert(image != (Image *) NULL);
1179  assert(image->signature == MagickSignature);
1180  if (image->debug != MagickFalse)
1181    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1182  status=MagickTrue;
1183  background=image->background_color;
1184  progress=0;
1185  image_view=AcquireAuthenticCacheView(image,exception);
1186#if defined(MAGICKCORE_OPENMP_SUPPORT)
1187  #pragma omp parallel for schedule(static,4) shared(progress,status) \
1188    magick_threads(image,image,height,1)
1189#endif
1190  for (y=0; y < (ssize_t) height; y++)
1191  {
1192    PixelInfo
1193      pixel,
1194      source,
1195      destination;
1196
1197    double
1198      area,
1199      displacement;
1200
1201    register Quantum
1202      *restrict p,
1203      *restrict q;
1204
1205    register ssize_t
1206      i;
1207
1208    ShearDirection
1209      direction;
1210
1211    ssize_t
1212      step;
1213
1214    if (status == MagickFalse)
1215      continue;
1216    p=GetCacheViewAuthenticPixels(image_view,0,y_offset+y,image->columns,1,
1217      exception);
1218    if (p == (Quantum *) NULL)
1219      {
1220        status=MagickFalse;
1221        continue;
1222      }
1223    p+=x_offset*GetPixelChannels(image);
1224    displacement=degrees*(double) (y-height/2.0);
1225    if (displacement == 0.0)
1226      continue;
1227    if (displacement > 0.0)
1228      direction=RIGHT;
1229    else
1230      {
1231        displacement*=(-1.0);
1232        direction=LEFT;
1233      }
1234    step=(ssize_t) floor((double) displacement);
1235    area=(double) (displacement-step);
1236    step++;
1237    pixel=background;
1238    GetPixelInfo(image,&source);
1239    GetPixelInfo(image,&destination);
1240    switch (direction)
1241    {
1242      case LEFT:
1243      {
1244        /*
1245          Transfer pixels left-to-right.
1246        */
1247        if (step > x_offset)
1248          break;
1249        q=p-step*GetPixelChannels(image);
1250        for (i=0; i < (ssize_t) width; i++)
1251        {
1252          if ((x_offset+i) < step)
1253            {
1254              p+=GetPixelChannels(image);
1255              GetPixelInfoPixel(image,p,&pixel);
1256              q+=GetPixelChannels(image);
1257              continue;
1258            }
1259          GetPixelInfoPixel(image,p,&source);
1260          CompositePixelInfoAreaBlend(&pixel,(double) pixel.alpha,
1261            &source,(double) GetPixelAlpha(image,p),area,&destination);
1262          SetPixelInfoPixel(image,&destination,q);
1263          GetPixelInfoPixel(image,p,&pixel);
1264          p+=GetPixelChannels(image);
1265          q+=GetPixelChannels(image);
1266        }
1267        CompositePixelInfoAreaBlend(&pixel,(double) pixel.alpha,
1268          &background,(double) background.alpha,area,&destination);
1269        SetPixelInfoPixel(image,&destination,q);
1270        q+=GetPixelChannels(image);
1271        for (i=0; i < (step-1); i++)
1272        {
1273          SetPixelInfoPixel(image,&background,q);
1274          q+=GetPixelChannels(image);
1275        }
1276        break;
1277      }
1278      case RIGHT:
1279      {
1280        /*
1281          Transfer pixels right-to-left.
1282        */
1283        p+=width*GetPixelChannels(image);
1284        q=p+step*GetPixelChannels(image);
1285        for (i=0; i < (ssize_t) width; i++)
1286        {
1287          p-=GetPixelChannels(image);
1288          q-=GetPixelChannels(image);
1289          if ((size_t) (x_offset+width+step-i) > image->columns)
1290            continue;
1291          GetPixelInfoPixel(image,p,&source);
1292          CompositePixelInfoAreaBlend(&pixel,(double) pixel.alpha,
1293            &source,(double) GetPixelAlpha(image,p),area,&destination);
1294          SetPixelInfoPixel(image,&destination,q);
1295          GetPixelInfoPixel(image,p,&pixel);
1296        }
1297        CompositePixelInfoAreaBlend(&pixel,(double) pixel.alpha,
1298          &background,(double) background.alpha,area,&destination);
1299        q-=GetPixelChannels(image);
1300        SetPixelInfoPixel(image,&destination,q);
1301        for (i=0; i < (step-1); i++)
1302        {
1303          q-=GetPixelChannels(image);
1304          SetPixelInfoPixel(image,&background,q);
1305        }
1306        break;
1307      }
1308    }
1309    if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1310      status=MagickFalse;
1311    if (image->progress_monitor != (MagickProgressMonitor) NULL)
1312      {
1313        MagickBooleanType
1314          proceed;
1315
1316#if defined(MAGICKCORE_OPENMP_SUPPORT)
1317        #pragma omp critical (MagickCore_XShearImage)
1318#endif
1319        proceed=SetImageProgress(image,XShearImageTag,progress++,height);
1320        if (proceed == MagickFalse)
1321          status=MagickFalse;
1322      }
1323  }
1324  image_view=DestroyCacheView(image_view);
1325  return(status);
1326}
1327
1328/*
1329%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1330%                                                                             %
1331%                                                                             %
1332%                                                                             %
1333+   Y S h e a r I m a g e                                                     %
1334%                                                                             %
1335%                                                                             %
1336%                                                                             %
1337%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1338%
1339%  YShearImage shears the image in the Y direction with a shear angle of
1340%  'degrees'.  Positive angles shear counter-clockwise (right-hand rule), and
1341%  negative angles shear clockwise.  Angles are measured relative to a
1342%  horizontal X-axis.  Y shears will increase the height of an image creating
1343%  'empty' triangles on the top and bottom of the source image.
1344%
1345%  The format of the YShearImage method is:
1346%
1347%      MagickBooleanType YShearImage(Image *image,const double degrees,
1348%        const size_t width,const size_t height,
1349%        const ssize_t x_offset,const ssize_t y_offset,ExceptionInfo *exception)
1350%
1351%  A description of each parameter follows.
1352%
1353%    o image: the image.
1354%
1355%    o degrees: A double representing the shearing angle along the Y
1356%      axis.
1357%
1358%    o width, height, x_offset, y_offset: Defines a region of the image
1359%      to shear.
1360%
1361%    o exception: return any errors or warnings in this structure.
1362%
1363*/
1364static MagickBooleanType YShearImage(Image *image,const double degrees,
1365  const size_t width,const size_t height,const ssize_t x_offset,
1366  const ssize_t y_offset,ExceptionInfo *exception)
1367{
1368#define YShearImageTag  "YShear/Image"
1369
1370  typedef enum
1371  {
1372    UP,
1373    DOWN
1374  } ShearDirection;
1375
1376  CacheView
1377    *image_view;
1378
1379  MagickBooleanType
1380    status;
1381
1382  MagickOffsetType
1383    progress;
1384
1385  PixelInfo
1386    background;
1387
1388  ssize_t
1389    x;
1390
1391  /*
1392    Y Shear image.
1393  */
1394  assert(image != (Image *) NULL);
1395  assert(image->signature == MagickSignature);
1396  if (image->debug != MagickFalse)
1397    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1398  status=MagickTrue;
1399  progress=0;
1400  background=image->background_color;
1401  image_view=AcquireAuthenticCacheView(image,exception);
1402#if defined(MAGICKCORE_OPENMP_SUPPORT)
1403  #pragma omp parallel for schedule(static,4) shared(progress,status) \
1404    magick_threads(image,image,width,1)
1405#endif
1406  for (x=0; x < (ssize_t) width; x++)
1407  {
1408    ssize_t
1409      step;
1410
1411    double
1412      area,
1413      displacement;
1414
1415    PixelInfo
1416      pixel,
1417      source,
1418      destination;
1419
1420    register Quantum
1421      *restrict p,
1422      *restrict q;
1423
1424    register ssize_t
1425      i;
1426
1427    ShearDirection
1428      direction;
1429
1430    if (status == MagickFalse)
1431      continue;
1432    p=GetCacheViewAuthenticPixels(image_view,x_offset+x,0,1,image->rows,
1433      exception);
1434    if (p == (Quantum *) NULL)
1435      {
1436        status=MagickFalse;
1437        continue;
1438      }
1439    p+=y_offset*GetPixelChannels(image);
1440    displacement=degrees*(double) (x-width/2.0);
1441    if (displacement == 0.0)
1442      continue;
1443    if (displacement > 0.0)
1444      direction=DOWN;
1445    else
1446      {
1447        displacement*=(-1.0);
1448        direction=UP;
1449      }
1450    step=(ssize_t) floor((double) displacement);
1451    area=(double) (displacement-step);
1452    step++;
1453    pixel=background;
1454    GetPixelInfo(image,&source);
1455    GetPixelInfo(image,&destination);
1456    switch (direction)
1457    {
1458      case UP:
1459      {
1460        /*
1461          Transfer pixels top-to-bottom.
1462        */
1463        if (step > y_offset)
1464          break;
1465        q=p-step*GetPixelChannels(image);
1466        for (i=0; i < (ssize_t) height; i++)
1467        {
1468          if ((y_offset+i) < step)
1469            {
1470              p+=GetPixelChannels(image);
1471              GetPixelInfoPixel(image,p,&pixel);
1472              q+=GetPixelChannels(image);
1473              continue;
1474            }
1475          GetPixelInfoPixel(image,p,&source);
1476          CompositePixelInfoAreaBlend(&pixel,(double) pixel.alpha,
1477            &source,(double) GetPixelAlpha(image,p),area,
1478            &destination);
1479          SetPixelInfoPixel(image,&destination,q);
1480          GetPixelInfoPixel(image,p,&pixel);
1481          p+=GetPixelChannels(image);
1482          q+=GetPixelChannels(image);
1483        }
1484        CompositePixelInfoAreaBlend(&pixel,(double) pixel.alpha,
1485          &background,(double) background.alpha,area,&destination);
1486        SetPixelInfoPixel(image,&destination,q);
1487        q+=GetPixelChannels(image);
1488        for (i=0; i < (step-1); i++)
1489        {
1490          SetPixelInfoPixel(image,&background,q);
1491          q+=GetPixelChannels(image);
1492        }
1493        break;
1494      }
1495      case DOWN:
1496      {
1497        /*
1498          Transfer pixels bottom-to-top.
1499        */
1500        p+=height*GetPixelChannels(image);
1501        q=p+step*GetPixelChannels(image);
1502        for (i=0; i < (ssize_t) height; i++)
1503        {
1504          p-=GetPixelChannels(image);
1505          q-=GetPixelChannels(image);
1506          if ((size_t) (y_offset+height+step-i) > image->rows)
1507            continue;
1508          GetPixelInfoPixel(image,p,&source);
1509          CompositePixelInfoAreaBlend(&pixel,(double) pixel.alpha,
1510            &source,(double) GetPixelAlpha(image,p),area,
1511            &destination);
1512          SetPixelInfoPixel(image,&destination,q);
1513          GetPixelInfoPixel(image,p,&pixel);
1514        }
1515        CompositePixelInfoAreaBlend(&pixel,(double) pixel.alpha,
1516          &background,(double) background.alpha,area,&destination);
1517        q-=GetPixelChannels(image);
1518        SetPixelInfoPixel(image,&destination,q);
1519        for (i=0; i < (step-1); i++)
1520        {
1521          q-=GetPixelChannels(image);
1522          SetPixelInfoPixel(image,&background,q);
1523        }
1524        break;
1525      }
1526    }
1527    if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1528      status=MagickFalse;
1529    if (image->progress_monitor != (MagickProgressMonitor) NULL)
1530      {
1531        MagickBooleanType
1532          proceed;
1533
1534#if defined(MAGICKCORE_OPENMP_SUPPORT)
1535        #pragma omp critical (MagickCore_YShearImage)
1536#endif
1537        proceed=SetImageProgress(image,YShearImageTag,progress++,image->rows);
1538        if (proceed == MagickFalse)
1539          status=MagickFalse;
1540      }
1541  }
1542  image_view=DestroyCacheView(image_view);
1543  return(status);
1544}
1545
1546/*
1547%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1548%                                                                             %
1549%                                                                             %
1550%                                                                             %
1551%   S h e a r I m a g e                                                       %
1552%                                                                             %
1553%                                                                             %
1554%                                                                             %
1555%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1556%
1557%  ShearImage() creates a new image that is a shear_image copy of an existing
1558%  one.  Shearing slides one edge of an image along the X or Y axis, creating
1559%  a parallelogram.  An X direction shear slides an edge along the X axis,
1560%  while a Y direction shear slides an edge along the Y axis.  The amount of
1561%  the shear is controlled by a shear angle.  For X direction shears, x_shear
1562%  is measured relative to the Y axis, and similarly, for Y direction shears
1563%  y_shear is measured relative to the X axis.  Empty triangles left over from
1564%  shearing the image are filled with the background color defined by member
1565%  'background_color' of the image..  ShearImage() allocates the memory
1566%  necessary for the new Image structure and returns a pointer to the new image.
1567%
1568%  ShearImage() is based on the paper "A Fast Algorithm for General Raster
1569%  Rotatation" by Alan W. Paeth.
1570%
1571%  The format of the ShearImage method is:
1572%
1573%      Image *ShearImage(const Image *image,const double x_shear,
1574%        const double y_shear,ExceptionInfo *exception)
1575%
1576%  A description of each parameter follows.
1577%
1578%    o image: the image.
1579%
1580%    o x_shear, y_shear: Specifies the number of degrees to shear the image.
1581%
1582%    o exception: return any errors or warnings in this structure.
1583%
1584*/
1585MagickExport Image *ShearImage(const Image *image,const double x_shear,
1586  const double y_shear,ExceptionInfo *exception)
1587{
1588  Image
1589    *integral_image,
1590    *shear_image;
1591
1592  MagickBooleanType
1593    status;
1594
1595  PointInfo
1596    shear;
1597
1598  RectangleInfo
1599    border_info,
1600    bounds;
1601
1602  assert(image != (Image *) NULL);
1603  assert(image->signature == MagickSignature);
1604  if (image->debug != MagickFalse)
1605    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1606  assert(exception != (ExceptionInfo *) NULL);
1607  assert(exception->signature == MagickSignature);
1608  if ((x_shear != 0.0) && (fmod(x_shear,90.0) == 0.0))
1609    ThrowImageException(ImageError,"AngleIsDiscontinuous");
1610  if ((y_shear != 0.0) && (fmod(y_shear,90.0) == 0.0))
1611    ThrowImageException(ImageError,"AngleIsDiscontinuous");
1612  /*
1613    Initialize shear angle.
1614  */
1615  integral_image=CloneImage(image,0,0,MagickTrue,exception);
1616  if (integral_image == (Image *) NULL)
1617    ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1618  shear.x=(-tan(DegreesToRadians(fmod(x_shear,360.0))));
1619  shear.y=tan(DegreesToRadians(fmod(y_shear,360.0)));
1620  if ((shear.x == 0.0) && (shear.y == 0.0))
1621    return(integral_image);
1622  if (SetImageStorageClass(integral_image,DirectClass,exception) == MagickFalse)
1623    {
1624      integral_image=DestroyImage(integral_image);
1625      return(integral_image);
1626    }
1627  if (integral_image->alpha_trait == UndefinedPixelTrait)
1628    (void) SetImageAlphaChannel(integral_image,OpaqueAlphaChannel,exception);
1629  /*
1630    Compute image size.
1631  */
1632  bounds.width=image->columns+(ssize_t) floor(fabs(shear.x)*image->rows+0.5);
1633  bounds.x=(ssize_t) ceil((double) image->columns+((fabs(shear.x)*image->rows)-
1634    image->columns)/2.0-0.5);
1635  bounds.y=(ssize_t) ceil((double) image->rows+((fabs(shear.y)*bounds.width)-
1636    image->rows)/2.0-0.5);
1637  /*
1638    Surround image with border.
1639  */
1640  integral_image->border_color=integral_image->background_color;
1641  integral_image->compose=CopyCompositeOp;
1642  border_info.width=(size_t) bounds.x;
1643  border_info.height=(size_t) bounds.y;
1644  shear_image=BorderImage(integral_image,&border_info,image->compose,exception);
1645  integral_image=DestroyImage(integral_image);
1646  if (shear_image == (Image *) NULL)
1647    ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1648  /*
1649    Shear the image.
1650  */
1651  if (shear_image->alpha_trait == UndefinedPixelTrait)
1652    (void) SetImageAlphaChannel(shear_image,OpaqueAlphaChannel,exception);
1653  status=XShearImage(shear_image,shear.x,image->columns,image->rows,bounds.x,
1654    (ssize_t) (shear_image->rows-image->rows)/2,exception);
1655  if (status == MagickFalse)
1656    {
1657      shear_image=DestroyImage(shear_image);
1658      return((Image *) NULL);
1659    }
1660  status=YShearImage(shear_image,shear.y,bounds.width,image->rows,(ssize_t)
1661    (shear_image->columns-bounds.width)/2,bounds.y,exception);
1662  if (status == MagickFalse)
1663    {
1664      shear_image=DestroyImage(shear_image);
1665      return((Image *) NULL);
1666    }
1667  status=CropToFitImage(&shear_image,shear.x,shear.y,(MagickRealType)
1668    image->columns,(MagickRealType) image->rows,MagickFalse,exception);
1669  shear_image->compose=image->compose;
1670  shear_image->page.width=0;
1671  shear_image->page.height=0;
1672  if (status == MagickFalse)
1673    shear_image=DestroyImage(shear_image);
1674  return(shear_image);
1675}
1676
1677/*
1678%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1679%                                                                             %
1680%                                                                             %
1681%                                                                             %
1682%   S h e a r R o t a t e I m a g e                                           %
1683%                                                                             %
1684%                                                                             %
1685%                                                                             %
1686%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1687%
1688%  ShearRotateImage() creates a new image that is a rotated copy of an existing
1689%  one.  Positive angles rotate counter-clockwise (right-hand rule), while
1690%  negative angles rotate clockwise.  Rotated images are usually larger than
1691%  the originals and have 'empty' triangular corners.  X axis.  Empty
1692%  triangles left over from shearing the image are filled with the background
1693%  color defined by member 'background_color' of the image.  ShearRotateImage
1694%  allocates the memory necessary for the new Image structure and returns a
1695%  pointer to the new image.
1696%
1697%  ShearRotateImage() is based on the paper "A Fast Algorithm for General
1698%  Raster Rotatation" by Alan W. Paeth.  ShearRotateImage is adapted from a
1699%  similar method based on the Paeth paper written by Michael Halle of the
1700%  Spatial Imaging Group, MIT Media Lab.
1701%
1702%  The format of the ShearRotateImage method is:
1703%
1704%      Image *ShearRotateImage(const Image *image,const double degrees,
1705%        ExceptionInfo *exception)
1706%
1707%  A description of each parameter follows.
1708%
1709%    o image: the image.
1710%
1711%    o degrees: Specifies the number of degrees to rotate the image.
1712%
1713%    o exception: return any errors or warnings in this structure.
1714%
1715*/
1716MagickExport Image *ShearRotateImage(const Image *image,const double degrees,
1717  ExceptionInfo *exception)
1718{
1719  Image
1720    *integral_image,
1721    *rotate_image;
1722
1723  MagickBooleanType
1724    status;
1725
1726  MagickRealType
1727    angle;
1728
1729  PointInfo
1730    shear;
1731
1732  RectangleInfo
1733    border_info,
1734    bounds;
1735
1736  size_t
1737    height,
1738    rotations,
1739    shear_width,
1740    width;
1741
1742  /*
1743    Adjust rotation angle.
1744  */
1745  assert(image != (Image *) NULL);
1746  assert(image->signature == MagickSignature);
1747  if (image->debug != MagickFalse)
1748    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1749  assert(exception != (ExceptionInfo *) NULL);
1750  assert(exception->signature == MagickSignature);
1751  angle=degrees;
1752  while (angle < -45.0)
1753    angle+=360.0;
1754  for (rotations=0; angle > 45.0; rotations++)
1755    angle-=90.0;
1756  rotations%=4;
1757  /*
1758    Calculate shear equations.
1759  */
1760  integral_image=IntegralRotateImage(image,rotations,exception);
1761  if (integral_image == (Image *) NULL)
1762    ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1763  shear.x=(-tan((double) DegreesToRadians(angle)/2.0));
1764  shear.y=sin((double) DegreesToRadians(angle));
1765  if ((shear.x == 0.0) && (shear.y == 0.0))
1766    return(integral_image);
1767  if (SetImageStorageClass(integral_image,DirectClass,exception) == MagickFalse)
1768    {
1769      integral_image=DestroyImage(integral_image);
1770      return(integral_image);
1771    }
1772  if (integral_image->alpha_trait == UndefinedPixelTrait)
1773    (void) SetImageAlphaChannel(integral_image,OpaqueAlphaChannel,exception);
1774  /*
1775    Compute maximum bounds for 3 shear operations.
1776  */
1777  width=integral_image->columns;
1778  height=integral_image->rows;
1779  bounds.width=(size_t) floor(fabs((double) height*shear.x)+width+0.5);
1780  bounds.height=(size_t) floor(fabs((double) bounds.width*shear.y)+height+0.5);
1781  shear_width=(size_t) floor(fabs((double) bounds.height*shear.x)+
1782    bounds.width+0.5);
1783  bounds.x=(ssize_t) floor((double) ((shear_width > bounds.width) ? width :
1784    bounds.width-shear_width+2)/2.0+0.5);
1785  bounds.y=(ssize_t) floor(((double) bounds.height-height+2)/2.0+0.5);
1786  /*
1787    Surround image with a border.
1788  */
1789  integral_image->border_color=integral_image->background_color;
1790  integral_image->compose=CopyCompositeOp;
1791  border_info.width=(size_t) bounds.x;
1792  border_info.height=(size_t) bounds.y;
1793  rotate_image=BorderImage(integral_image,&border_info,image->compose,
1794    exception);
1795  integral_image=DestroyImage(integral_image);
1796  if (rotate_image == (Image *) NULL)
1797    ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1798  /*
1799    Rotate the image.
1800  */
1801  status=XShearImage(rotate_image,shear.x,width,height,bounds.x,(ssize_t)
1802    (rotate_image->rows-height)/2,exception);
1803  if (status == MagickFalse)
1804    {
1805      rotate_image=DestroyImage(rotate_image);
1806      return((Image *) NULL);
1807    }
1808  status=YShearImage(rotate_image,shear.y,bounds.width,height,(ssize_t)
1809    (rotate_image->columns-bounds.width)/2,bounds.y,exception);
1810  if (status == MagickFalse)
1811    {
1812      rotate_image=DestroyImage(rotate_image);
1813      return((Image *) NULL);
1814    }
1815  status=XShearImage(rotate_image,shear.x,bounds.width,bounds.height,(ssize_t)
1816    (rotate_image->columns-bounds.width)/2,(ssize_t) (rotate_image->rows-
1817    bounds.height)/2,exception);
1818  if (status == MagickFalse)
1819    {
1820      rotate_image=DestroyImage(rotate_image);
1821      return((Image *) NULL);
1822    }
1823  status=CropToFitImage(&rotate_image,shear.x,shear.y,(MagickRealType) width,
1824    (MagickRealType) height,MagickTrue,exception);
1825  rotate_image->compose=image->compose;
1826  rotate_image->page.width=0;
1827  rotate_image->page.height=0;
1828  if (status == MagickFalse)
1829    rotate_image=DestroyImage(rotate_image);
1830  return(rotate_image);
1831}
1832