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