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