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