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