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