]> 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     if ((p->width*p->height) > 8192) \
577       num_threads(GetMagickResourceLimit(ThreadResource))
578 #endif
579   for (x=0; x < (ssize_t) p->width; x++)
580   {
581     register ssize_t
582       y;
583
584     size_t
585       sum;
586
587     sum=0;
588     for (y=0; y < (ssize_t) (p->height-1); y++)
589     {
590       ssize_t
591         delta;
592
593       delta=GetRadonCell(p,x,y)-(ssize_t) GetRadonCell(p,x,y+1);
594       sum+=delta*delta;
595     }
596     projection[p->width+sign*x-1]=sum;
597   }
598 }
599
600 static MagickBooleanType RadonTransform(const Image *image,
601   const double threshold,size_t *projection,ExceptionInfo *exception)
602 {
603   CacheView
604     *image_view;
605
606   MagickBooleanType
607     status;
608
609   RadonInfo
610     *destination_cells,
611     *source_cells;
612
613   register ssize_t
614     i;
615
616   size_t
617     count,
618     width;
619
620   ssize_t
621     y;
622
623   unsigned char
624     byte;
625
626   unsigned short
627     bits[256];
628
629   for (width=1; width < ((image->columns+7)/8); width<<=1) ;
630   source_cells=AcquireRadonInfo(image,width,image->rows,exception);
631   destination_cells=AcquireRadonInfo(image,width,image->rows,exception);
632   if ((source_cells == (RadonInfo *) NULL) ||
633       (destination_cells == (RadonInfo *) NULL))
634     {
635       if (destination_cells != (RadonInfo *) NULL)
636         destination_cells=DestroyRadonInfo(destination_cells);
637       if (source_cells != (RadonInfo *) NULL)
638         source_cells=DestroyRadonInfo(source_cells);
639       return(MagickFalse);
640     }
641   if (ResetRadonCells(source_cells) == MagickFalse)
642     {
643       destination_cells=DestroyRadonInfo(destination_cells);
644       source_cells=DestroyRadonInfo(source_cells);
645       return(MagickFalse);
646     }
647   for (i=0; i < 256; i++)
648   {
649     byte=(unsigned char) i;
650     for (count=0; byte != 0; byte>>=1)
651       count+=byte & 0x01;
652     bits[i]=(unsigned short) count;
653   }
654   status=MagickTrue;
655   image_view=AcquireVirtualCacheView(image,exception);
656 #if defined(MAGICKCORE_OPENMP_SUPPORT)
657   #pragma omp parallel for schedule(static,4) shared(status) \
658     if ((image->rows*image->columns) > 8192) \
659       num_threads(GetMagickResourceLimit(ThreadResource))
660 #endif
661   for (y=0; y < (ssize_t) image->rows; y++)
662   {
663     register const Quantum
664       *restrict p;
665
666     register ssize_t
667       i,
668       x;
669
670     size_t
671       bit,
672       byte;
673
674     if (status == MagickFalse)
675       continue;
676     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
677     if (p == (const Quantum *) NULL)
678       {
679         status=MagickFalse;
680         continue;
681       }
682     bit=0;
683     byte=0;
684     i=(ssize_t) (image->columns+7)/8;
685     for (x=0; x < (ssize_t) image->columns; x++)
686     {
687       byte<<=1;
688       if ((double) GetPixelIntensity(image,p) < threshold)
689         byte|=0x01;
690       bit++;
691       if (bit == 8)
692         {
693           (void) SetRadonCell(source_cells,--i,y,bits[byte]);
694           bit=0;
695           byte=0;
696         }
697       p+=GetPixelChannels(image);
698     }
699     if (bit != 0)
700       {
701         byte<<=(8-bit);
702         (void) SetRadonCell(source_cells,--i,y,bits[byte]);
703       }
704   }
705   RadonProjection(source_cells,destination_cells,-1,projection);
706   (void) ResetRadonCells(source_cells);
707 #if defined(MAGICKCORE_OPENMP_SUPPORT)
708   #pragma omp parallel for schedule(static,4) shared(status) \
709     if ((image->rows*image->columns) > 8192) \
710       num_threads(GetMagickResourceLimit(ThreadResource))
711 #endif
712   for (y=0; y < (ssize_t) image->rows; y++)
713   {
714     register const Quantum
715       *restrict p;
716
717     register ssize_t
718       i,
719       x;
720
721     size_t
722       bit,
723       byte;
724
725     if (status == MagickFalse)
726       continue;
727     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
728     if (p == (const Quantum *) NULL)
729       {
730         status=MagickFalse;
731         continue;
732       }
733     bit=0;
734     byte=0;
735     i=0;
736     for (x=0; x < (ssize_t) image->columns; x++)
737     {
738       byte<<=1;
739       if ((double) GetPixelIntensity(image,p) < threshold)
740         byte|=0x01;
741       bit++;
742       if (bit == 8)
743         {
744           (void) SetRadonCell(source_cells,i++,y,bits[byte]);
745           bit=0;
746           byte=0;
747         }
748       p+=GetPixelChannels(image);
749     }
750     if (bit != 0)
751       {
752         byte<<=(8-bit);
753         (void) SetRadonCell(source_cells,i++,y,bits[byte]);
754       }
755   }
756   RadonProjection(source_cells,destination_cells,1,projection);
757   image_view=DestroyCacheView(image_view);
758   destination_cells=DestroyRadonInfo(destination_cells);
759   source_cells=DestroyRadonInfo(source_cells);
760   return(MagickTrue);
761 }
762
763 static void GetImageBackgroundColor(Image *image,const ssize_t offset,
764   ExceptionInfo *exception)
765 {
766   CacheView
767     *image_view;
768
769   PixelInfo
770     background;
771
772   MagickRealType
773     count;
774
775   ssize_t
776     y;
777
778   /*
779     Compute average background color.
780   */
781   if (offset <= 0)
782     return;
783   GetPixelInfo(image,&background);
784   count=0.0;
785   image_view=AcquireVirtualCacheView(image,exception);
786   for (y=0; y < (ssize_t) image->rows; y++)
787   {
788     register const Quantum
789       *restrict p;
790
791     register ssize_t
792       x;
793
794     if ((y >= offset) && (y < ((ssize_t) image->rows-offset)))
795       continue;
796     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
797     if (p == (const Quantum *) NULL)
798       continue;
799     for (x=0; x < (ssize_t) image->columns; x++)
800     {
801       if ((x >= offset) && (x < ((ssize_t) image->columns-offset)))
802         continue;
803       background.red+=QuantumScale*GetPixelRed(image,p);
804       background.green+=QuantumScale*GetPixelGreen(image,p);
805       background.blue+=QuantumScale*GetPixelBlue(image,p);
806       if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
807         background.alpha+=QuantumScale*GetPixelAlpha(image,p);
808       count++;
809       p+=GetPixelChannels(image);
810     }
811   }
812   image_view=DestroyCacheView(image_view);
813   image->background_color.red=(double) ClampToQuantum((MagickRealType)
814     QuantumRange*background.red/count);
815   image->background_color.green=(double) ClampToQuantum((MagickRealType)
816     QuantumRange*background.green/count);
817   image->background_color.blue=(double) ClampToQuantum((MagickRealType)
818     QuantumRange*background.blue/count);
819   if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
820     image->background_color.alpha=(double) ClampToQuantum((MagickRealType)
821       QuantumRange*background.alpha/count);
822 }
823
824 MagickExport Image *DeskewImage(const Image *image,const double threshold,
825   ExceptionInfo *exception)
826 {
827   AffineMatrix
828     affine_matrix;
829
830   const char
831     *artifact;
832
833   double
834     degrees;
835
836   Image
837     *clone_image,
838     *crop_image,
839     *deskew_image,
840     *median_image;
841
842   MagickBooleanType
843     status;
844
845   RectangleInfo
846     geometry;
847
848   register ssize_t
849     i;
850
851   size_t
852     max_projection,
853     *projection,
854     width;
855
856   ssize_t
857     skew;
858
859   /*
860     Compute deskew angle.
861   */
862   for (width=1; width < ((image->columns+7)/8); width<<=1) ;
863   projection=(size_t *) AcquireQuantumMemory((size_t) (2*width-1),
864     sizeof(*projection));
865   if (projection == (size_t *) NULL)
866     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
867   status=RadonTransform(image,threshold,projection,exception);
868   if (status == MagickFalse)
869     {
870       projection=(size_t *) RelinquishMagickMemory(projection);
871       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
872     }
873   max_projection=0;
874   skew=0;
875   for (i=0; i < (ssize_t) (2*width-1); i++)
876   {
877     if (projection[i] > max_projection)
878       {
879         skew=i-(ssize_t) width+1;
880         max_projection=projection[i];
881       }
882   }
883   projection=(size_t *) RelinquishMagickMemory(projection);
884   /*
885     Deskew image.
886   */
887   clone_image=CloneImage(image,0,0,MagickTrue,exception);
888   if (clone_image == (Image *) NULL)
889     return((Image *) NULL);
890   (void) SetImageVirtualPixelMethod(clone_image,BackgroundVirtualPixelMethod,
891     exception);
892   degrees=RadiansToDegrees(-atan((double) skew/width/8));
893   if (image->debug != MagickFalse)
894     (void) LogMagickEvent(TransformEvent,GetMagickModule(),
895       "  Deskew angle: %g",degrees);
896   affine_matrix.sx=cos(DegreesToRadians(fmod((double) degrees,360.0)));
897   affine_matrix.rx=sin(DegreesToRadians(fmod((double) degrees,360.0)));
898   affine_matrix.ry=(-sin(DegreesToRadians(fmod((double) degrees,360.0))));
899   affine_matrix.sy=cos(DegreesToRadians(fmod((double) degrees,360.0)));
900   affine_matrix.tx=0.0;
901   affine_matrix.ty=0.0;
902   artifact=GetImageArtifact(image,"deskew:auto-crop");
903   if (artifact == (const char *) NULL)
904     {
905       deskew_image=AffineTransformImage(clone_image,&affine_matrix,exception);
906       clone_image=DestroyImage(clone_image);
907       return(deskew_image);
908     }
909   /*
910     Auto-crop image.
911   */
912   GetImageBackgroundColor(clone_image,(ssize_t) StringToLong(artifact),
913     exception);
914   deskew_image=AffineTransformImage(clone_image,&affine_matrix,exception);
915   clone_image=DestroyImage(clone_image);
916   if (deskew_image == (Image *) NULL)
917     return((Image *) NULL);
918   median_image=StatisticImage(deskew_image,MedianStatistic,3,3,exception);
919   if (median_image == (Image *) NULL)
920     {
921       deskew_image=DestroyImage(deskew_image);
922       return((Image *) NULL);
923     }
924   geometry=GetImageBoundingBox(median_image,exception);
925   median_image=DestroyImage(median_image);
926   if (image->debug != MagickFalse)
927     (void) LogMagickEvent(TransformEvent,GetMagickModule(),"  Deskew geometry: "
928       "%.20gx%.20g%+.20g%+.20g",(double) geometry.width,(double)
929       geometry.height,(double) geometry.x,(double) geometry.y);
930   crop_image=CropImage(deskew_image,&geometry,exception);
931   deskew_image=DestroyImage(deskew_image);
932   return(crop_image);
933 }
934 \f
935 /*
936 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
937 %                                                                             %
938 %                                                                             %
939 %                                                                             %
940 %   I n t e g r a l R o t a t e I m a g e                                     %
941 %                                                                             %
942 %                                                                             %
943 %                                                                             %
944 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
945 %
946 %  IntegralRotateImage() rotates the image an integral of 90 degrees.  It
947 %  allocates the memory necessary for the new Image structure and returns a
948 %  pointer to the rotated image.
949 %
950 %  The format of the IntegralRotateImage method is:
951 %
952 %      Image *IntegralRotateImage(const Image *image,size_t rotations,
953 %        ExceptionInfo *exception)
954 %
955 %  A description of each parameter follows.
956 %
957 %    o image: the image.
958 %
959 %    o rotations: Specifies the number of 90 degree rotations.
960 %
961 */
962 MagickExport Image *IntegralRotateImage(const Image *image,size_t rotations,
963   ExceptionInfo *exception)
964 {
965 #define RotateImageTag  "Rotate/Image"
966
967   CacheView
968     *image_view,
969     *rotate_view;
970
971   Image
972     *rotate_image;
973
974   MagickBooleanType
975     status;
976
977   MagickOffsetType
978     progress;
979
980   RectangleInfo
981     page;
982
983   ssize_t
984     y;
985
986   /*
987     Initialize rotated image attributes.
988   */
989   assert(image != (Image *) NULL);
990   page=image->page;
991   rotations%=4;
992   if (rotations == 0)
993     return(CloneImage(image,0,0,MagickTrue,exception));
994   if ((rotations == 1) || (rotations == 3))
995     rotate_image=CloneImage(image,image->rows,image->columns,MagickTrue,
996       exception);
997   else
998     rotate_image=CloneImage(image,image->columns,image->rows,MagickTrue,
999       exception);
1000   if (rotate_image == (Image *) NULL)
1001     return((Image *) NULL);
1002   /*
1003     Integral rotate the image.
1004   */
1005   status=MagickTrue;
1006   progress=0;
1007   image_view=AcquireVirtualCacheView(image,exception);
1008   rotate_view=AcquireAuthenticCacheView(rotate_image,exception);
1009   switch (rotations)
1010   {
1011     case 0:
1012     {
1013       /*
1014         Rotate 0 degrees.
1015       */
1016       break;
1017     }
1018     case 1:
1019     {
1020       size_t
1021         tile_height,
1022         tile_width;
1023
1024       ssize_t
1025         tile_y;
1026
1027       /*
1028         Rotate 90 degrees.
1029       */
1030       GetPixelCacheTileSize(image,&tile_width,&tile_height);
1031 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1032       #pragma omp parallel for schedule(static,4) shared(progress,status) \
1033         if ((image->rows*image->columns) > 8192) \
1034           num_threads(GetMagickResourceLimit(ThreadResource))
1035 #endif
1036       for (tile_y=0; tile_y < (ssize_t) image->rows; tile_y+=(ssize_t) tile_height)
1037       {
1038         register ssize_t
1039           tile_x;
1040
1041         if (status == MagickFalse)
1042           continue;
1043         tile_x=0;
1044         for ( ; tile_x < (ssize_t) image->columns; tile_x+=(ssize_t) tile_width)
1045         {
1046           MagickBooleanType
1047             sync;
1048
1049           register const Quantum
1050             *restrict p;
1051
1052           register Quantum
1053             *restrict q;
1054
1055           register ssize_t
1056             y;
1057
1058           size_t
1059             height,
1060             width;
1061
1062           width=tile_width;
1063           if ((tile_x+(ssize_t) tile_width) > (ssize_t) image->columns)
1064             width=(size_t) (tile_width-(tile_x+tile_width-image->columns));
1065           height=tile_height;
1066           if ((tile_y+(ssize_t) tile_height) > (ssize_t) image->rows)
1067             height=(size_t) (tile_height-(tile_y+tile_height-image->rows));
1068           p=GetCacheViewVirtualPixels(image_view,tile_x,tile_y,width,height,
1069             exception);
1070           if (p == (const Quantum *) NULL)
1071             {
1072               status=MagickFalse;
1073               break;
1074             }
1075           for (y=0; y < (ssize_t) width; y++)
1076           {
1077             register const Quantum
1078               *restrict tile_pixels;
1079
1080             register ssize_t
1081               x;
1082
1083             if (status == MagickFalse)
1084               continue;
1085             q=QueueCacheViewAuthenticPixels(rotate_view,(ssize_t)
1086               (rotate_image->columns-(tile_y+height)),y+tile_x,height,1,
1087               exception);
1088             if (q == (Quantum *) NULL)
1089               {
1090                 status=MagickFalse;
1091                 continue;
1092               }
1093             tile_pixels=p+((height-1)*width+y)*GetPixelChannels(image);
1094             for (x=0; x < (ssize_t) height; x++)
1095             {
1096               register ssize_t
1097                 i;
1098
1099               if (GetPixelMask(image,tile_pixels) != 0)
1100                 {
1101                   tile_pixels-=width*GetPixelChannels(image);
1102                   q+=GetPixelChannels(rotate_image);
1103                   continue;
1104                 }
1105               for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1106               {
1107                 PixelChannel
1108                   channel;
1109
1110                 PixelTrait
1111                   rotate_traits,
1112                   traits;
1113
1114                 channel=GetPixelChannelMapChannel(image,i);
1115                 traits=GetPixelChannelMapTraits(image,channel);
1116                 rotate_traits=GetPixelChannelMapTraits(rotate_image,channel);
1117                 if ((traits == UndefinedPixelTrait) ||
1118                     (rotate_traits == UndefinedPixelTrait))
1119                   continue;
1120                 SetPixelChannel(rotate_image,channel,tile_pixels[i],q);
1121               }
1122               tile_pixels-=width*GetPixelChannels(image);
1123               q+=GetPixelChannels(rotate_image);
1124             }
1125             sync=SyncCacheViewAuthenticPixels(rotate_view,exception);
1126             if (sync == MagickFalse)
1127               status=MagickFalse;
1128           }
1129         }
1130         if (image->progress_monitor != (MagickProgressMonitor) NULL)
1131           {
1132             MagickBooleanType
1133               proceed;
1134
1135 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1136             #pragma omp critical (MagickCore_IntegralRotateImage)
1137 #endif
1138             proceed=SetImageProgress(image,RotateImageTag,progress+=tile_height,
1139               image->rows);
1140             if (proceed == MagickFalse)
1141               status=MagickFalse;
1142           }
1143       }
1144       (void) SetImageProgress(image,RotateImageTag,(MagickOffsetType)
1145         image->rows-1,image->rows);
1146       Swap(page.width,page.height);
1147       Swap(page.x,page.y);
1148       if (page.width != 0)
1149         page.x=(ssize_t) (page.width-rotate_image->columns-page.x);
1150       break;
1151     }
1152     case 2:
1153     {
1154       /*
1155         Rotate 180 degrees.
1156       */
1157 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1158       #pragma omp parallel for schedule(static) shared(progress,status) \
1159         if ((image->rows*image->columns) > 8192) \
1160           num_threads(GetMagickResourceLimit(ThreadResource))
1161 #endif
1162       for (y=0; y < (ssize_t) image->rows; y++)
1163       {
1164         MagickBooleanType
1165           sync;
1166
1167         register const Quantum
1168           *restrict p;
1169
1170         register Quantum
1171           *restrict q;
1172
1173         register ssize_t
1174           x;
1175
1176         if (status == MagickFalse)
1177           continue;
1178         p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1179         q=QueueCacheViewAuthenticPixels(rotate_view,0,(ssize_t) (image->rows-y-
1180           1),image->columns,1,exception);
1181         if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1182           {
1183             status=MagickFalse;
1184             continue;
1185           }
1186         q+=GetPixelChannels(rotate_image)*image->columns;
1187         for (x=0; x < (ssize_t) image->columns; x++)
1188         {
1189           register ssize_t
1190             i;
1191
1192           q-=GetPixelChannels(rotate_image);
1193           if (GetPixelMask(image,p) != 0)
1194             {
1195               p+=GetPixelChannels(image);
1196               continue;
1197             }
1198           for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1199           {
1200             PixelChannel
1201               channel;
1202
1203             PixelTrait
1204               rotate_traits,
1205               traits;
1206
1207             channel=GetPixelChannelMapChannel(image,i);
1208             traits=GetPixelChannelMapTraits(image,channel);
1209             rotate_traits=GetPixelChannelMapTraits(rotate_image,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 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1256       #pragma omp parallel for schedule(static,4) shared(progress,status) \
1257         if ((image->rows*image->columns) > 8192) \
1258           num_threads(GetMagickResourceLimit(ThreadResource))
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 (GetPixelMask(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
1331                   channel;
1332
1333                 PixelTrait
1334                   rotate_traits,
1335                   traits;
1336
1337                 channel=GetPixelChannelMapChannel(image,i);
1338                 traits=GetPixelChannelMapTraits(image,channel);
1339                 rotate_traits=GetPixelChannelMapTraits(rotate_image,channel);
1340                 if ((traits == UndefinedPixelTrait) ||
1341                     (rotate_traits == UndefinedPixelTrait))
1342                   continue;
1343                 SetPixelChannel(rotate_image,channel,tile_pixels[i],q);
1344               }
1345               tile_pixels+=width*GetPixelChannels(image);
1346               q+=GetPixelChannels(rotate_image);
1347             }
1348             sync=SyncCacheViewAuthenticPixels(rotate_view,exception);
1349             if (sync == MagickFalse)
1350               status=MagickFalse;
1351           }
1352         }
1353         if (image->progress_monitor != (MagickProgressMonitor) NULL)
1354           {
1355             MagickBooleanType
1356               proceed;
1357
1358 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1359             #pragma omp critical (MagickCore_IntegralRotateImage)
1360 #endif
1361             proceed=SetImageProgress(image,RotateImageTag,progress+=tile_height,
1362               image->rows);
1363             if (proceed == MagickFalse)
1364               status=MagickFalse;
1365           }
1366       }
1367       (void) SetImageProgress(image,RotateImageTag,(MagickOffsetType)
1368         image->rows-1,image->rows);
1369       Swap(page.width,page.height);
1370       Swap(page.x,page.y);
1371       if (page.width != 0)
1372         page.x=(ssize_t) (page.width-rotate_image->columns-page.x);
1373       break;
1374     }
1375   }
1376   rotate_view=DestroyCacheView(rotate_view);
1377   image_view=DestroyCacheView(image_view);
1378   rotate_image->type=image->type;
1379   rotate_image->page=page;
1380   if (status == MagickFalse)
1381     rotate_image=DestroyImage(rotate_image);
1382   return(rotate_image);
1383 }
1384 \f
1385 /*
1386 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1387 %                                                                             %
1388 %                                                                             %
1389 %                                                                             %
1390 +   X S h e a r I m a g e                                                     %
1391 %                                                                             %
1392 %                                                                             %
1393 %                                                                             %
1394 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1395 %
1396 %  XShearImage() shears the image in the X direction with a shear angle of
1397 %  'degrees'.  Positive angles shear counter-clockwise (right-hand rule), and
1398 %  negative angles shear clockwise.  Angles are measured relative to a vertical
1399 %  Y-axis.  X shears will widen an image creating 'empty' triangles on the left
1400 %  and right sides of the source image.
1401 %
1402 %  The format of the XShearImage method is:
1403 %
1404 %      MagickBooleanType XShearImage(Image *image,const MagickRealType degrees,
1405 %        const size_t width,const size_t height,
1406 %        const ssize_t x_offset,const ssize_t y_offset,ExceptionInfo *exception)
1407 %
1408 %  A description of each parameter follows.
1409 %
1410 %    o image: the image.
1411 %
1412 %    o degrees: A MagickRealType representing the shearing angle along the X
1413 %      axis.
1414 %
1415 %    o width, height, x_offset, y_offset: Defines a region of the image
1416 %      to shear.
1417 %
1418 %    o exception: return any errors or warnings in this structure.
1419 %
1420 */
1421 static MagickBooleanType XShearImage(Image *image,const MagickRealType degrees,
1422   const size_t width,const size_t height,const ssize_t x_offset,
1423   const ssize_t y_offset,ExceptionInfo *exception)
1424 {
1425 #define XShearImageTag  "XShear/Image"
1426
1427   typedef enum
1428   {
1429     LEFT,
1430     RIGHT
1431   } ShearDirection;
1432
1433   CacheView
1434     *image_view;
1435
1436   MagickBooleanType
1437     status;
1438
1439   MagickOffsetType
1440     progress;
1441
1442   PixelInfo
1443     background;
1444
1445   ssize_t
1446     y;
1447
1448   /*
1449     X shear image.
1450   */
1451   assert(image != (Image *) NULL);
1452   assert(image->signature == MagickSignature);
1453   if (image->debug != MagickFalse)
1454     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1455   status=MagickTrue;
1456   background=image->background_color;
1457   progress=0;
1458   image_view=AcquireAuthenticCacheView(image,exception);
1459 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1460   #pragma omp parallel for schedule(static,4) shared(progress,status) \
1461     if ((height*width) > 8192) \
1462       num_threads(GetMagickResourceLimit(ThreadResource))
1463 #endif
1464   for (y=0; y < (ssize_t) height; y++)
1465   {
1466     PixelInfo
1467       pixel,
1468       source,
1469       destination;
1470
1471     MagickRealType
1472       area,
1473       displacement;
1474
1475     register Quantum
1476       *restrict p,
1477       *restrict q;
1478
1479     register ssize_t
1480       i;
1481
1482     ShearDirection
1483       direction;
1484
1485     ssize_t
1486       step;
1487
1488     if (status == MagickFalse)
1489       continue;
1490     p=GetCacheViewAuthenticPixels(image_view,0,y_offset+y,image->columns,1,
1491       exception);
1492     if (p == (Quantum *) NULL)
1493       {
1494         status=MagickFalse;
1495         continue;
1496       }
1497     p+=x_offset*GetPixelChannels(image);
1498     displacement=degrees*(MagickRealType) (y-height/2.0);
1499     if (displacement == 0.0)
1500       continue;
1501     if (displacement > 0.0)
1502       direction=RIGHT;
1503     else
1504       {
1505         displacement*=(-1.0);
1506         direction=LEFT;
1507       }
1508     step=(ssize_t) floor((double) displacement);
1509     area=(MagickRealType) (displacement-step);
1510     step++;
1511     pixel=background;
1512     GetPixelInfo(image,&source);
1513     GetPixelInfo(image,&destination);
1514     switch (direction)
1515     {
1516       case LEFT:
1517       {
1518         /*
1519           Transfer pixels left-to-right.
1520         */
1521         if (step > x_offset)
1522           break;
1523         q=p-step*GetPixelChannels(image);
1524         for (i=0; i < (ssize_t) width; i++)
1525         {
1526           if ((x_offset+i) < step)
1527             {
1528               p+=GetPixelChannels(image);
1529               GetPixelInfoPixel(image,p,&pixel);
1530               q+=GetPixelChannels(image);
1531               continue;
1532             }
1533           GetPixelInfoPixel(image,p,&source);
1534           CompositePixelInfoAreaBlend(&pixel,(MagickRealType) pixel.alpha,
1535             &source,(MagickRealType) GetPixelAlpha(image,p),area,&destination);
1536           SetPixelInfoPixel(image,&destination,q);
1537           GetPixelInfoPixel(image,p,&pixel);
1538           p+=GetPixelChannels(image);
1539           q+=GetPixelChannels(image);
1540         }
1541         CompositePixelInfoAreaBlend(&pixel,(MagickRealType) pixel.alpha,
1542           &background,(MagickRealType) background.alpha,area,&destination);
1543         SetPixelInfoPixel(image,&destination,q);
1544         q+=GetPixelChannels(image);
1545         for (i=0; i < (step-1); i++)
1546         {
1547           SetPixelInfoPixel(image,&background,q);
1548           q+=GetPixelChannels(image);
1549         }
1550         break;
1551       }
1552       case RIGHT:
1553       {
1554         /*
1555           Transfer pixels right-to-left.
1556         */
1557         p+=width*GetPixelChannels(image);
1558         q=p+step*GetPixelChannels(image);
1559         for (i=0; i < (ssize_t) width; i++)
1560         {
1561           p-=GetPixelChannels(image);
1562           q-=GetPixelChannels(image);
1563           if ((size_t) (x_offset+width+step-i) >= image->columns)
1564             continue;
1565           GetPixelInfoPixel(image,p,&source);
1566           CompositePixelInfoAreaBlend(&pixel,(MagickRealType) pixel.alpha,
1567             &source,(MagickRealType) GetPixelAlpha(image,p),area,&destination);
1568           SetPixelInfoPixel(image,&destination,q);
1569           GetPixelInfoPixel(image,p,&pixel);
1570         }
1571         CompositePixelInfoAreaBlend(&pixel,(MagickRealType) pixel.alpha,
1572           &background,(MagickRealType) background.alpha,area,&destination);
1573         q-=GetPixelChannels(image);
1574         SetPixelInfoPixel(image,&destination,q);
1575         for (i=0; i < (step-1); i++)
1576         {
1577           q-=GetPixelChannels(image);
1578           SetPixelInfoPixel(image,&background,q);
1579         }
1580         break;
1581       }
1582     }
1583     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1584       status=MagickFalse;
1585     if (image->progress_monitor != (MagickProgressMonitor) NULL)
1586       {
1587         MagickBooleanType
1588           proceed;
1589
1590 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1591         #pragma omp critical (MagickCore_XShearImage)
1592 #endif
1593         proceed=SetImageProgress(image,XShearImageTag,progress++,height);
1594         if (proceed == MagickFalse)
1595           status=MagickFalse;
1596       }
1597   }
1598   image_view=DestroyCacheView(image_view);
1599   return(status);
1600 }
1601 \f
1602 /*
1603 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1604 %                                                                             %
1605 %                                                                             %
1606 %                                                                             %
1607 +   Y S h e a r I m a g e                                                     %
1608 %                                                                             %
1609 %                                                                             %
1610 %                                                                             %
1611 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1612 %
1613 %  YShearImage shears the image in the Y direction with a shear angle of
1614 %  'degrees'.  Positive angles shear counter-clockwise (right-hand rule), and
1615 %  negative angles shear clockwise.  Angles are measured relative to a
1616 %  horizontal X-axis.  Y shears will increase the height of an image creating
1617 %  'empty' triangles on the top and bottom of the source image.
1618 %
1619 %  The format of the YShearImage method is:
1620 %
1621 %      MagickBooleanType YShearImage(Image *image,const MagickRealType degrees,
1622 %        const size_t width,const size_t height,
1623 %        const ssize_t x_offset,const ssize_t y_offset,ExceptionInfo *exception)
1624 %
1625 %  A description of each parameter follows.
1626 %
1627 %    o image: the image.
1628 %
1629 %    o degrees: A MagickRealType representing the shearing angle along the Y
1630 %      axis.
1631 %
1632 %    o width, height, x_offset, y_offset: Defines a region of the image
1633 %      to shear.
1634 %
1635 %    o exception: return any errors or warnings in this structure.
1636 %
1637 */
1638 static MagickBooleanType YShearImage(Image *image,const MagickRealType degrees,
1639   const size_t width,const size_t height,const ssize_t x_offset,
1640   const ssize_t y_offset,ExceptionInfo *exception)
1641 {
1642 #define YShearImageTag  "YShear/Image"
1643
1644   typedef enum
1645   {
1646     UP,
1647     DOWN
1648   } ShearDirection;
1649
1650   CacheView
1651     *image_view;
1652
1653   MagickBooleanType
1654     status;
1655
1656   MagickOffsetType
1657     progress;
1658
1659   PixelInfo
1660     background;
1661
1662   ssize_t
1663     x;
1664
1665   /*
1666     Y Shear image.
1667   */
1668   assert(image != (Image *) NULL);
1669   assert(image->signature == MagickSignature);
1670   if (image->debug != MagickFalse)
1671     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1672   status=MagickTrue;
1673   progress=0;
1674   background=image->background_color;
1675   image_view=AcquireAuthenticCacheView(image,exception);
1676 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1677   #pragma omp parallel for schedule(static,4) shared(progress,status) \
1678     if ((height*width) > 8192) \
1679       num_threads(GetMagickResourceLimit(ThreadResource))
1680 #endif
1681   for (x=0; x < (ssize_t) width; x++)
1682   {
1683     ssize_t
1684       step;
1685
1686     MagickRealType
1687       area,
1688       displacement;
1689
1690     PixelInfo
1691       pixel,
1692       source,
1693       destination;
1694
1695     register Quantum
1696       *restrict p,
1697       *restrict q;
1698
1699     register ssize_t
1700       i;
1701
1702     ShearDirection
1703       direction;
1704
1705     if (status == MagickFalse)
1706       continue;
1707     p=GetCacheViewAuthenticPixels(image_view,x_offset+x,0,1,image->rows,
1708       exception);
1709     if (p == (Quantum *) NULL)
1710       {
1711         status=MagickFalse;
1712         continue;
1713       }
1714     p+=y_offset*GetPixelChannels(image);
1715     displacement=degrees*(MagickRealType) (x-width/2.0);
1716     if (displacement == 0.0)
1717       continue;
1718     if (displacement > 0.0)
1719       direction=DOWN;
1720     else
1721       {
1722         displacement*=(-1.0);
1723         direction=UP;
1724       }
1725     step=(ssize_t) floor((double) displacement);
1726     area=(MagickRealType) (displacement-step);
1727     step++;
1728     pixel=background;
1729     GetPixelInfo(image,&source);
1730     GetPixelInfo(image,&destination);
1731     switch (direction)
1732     {
1733       case UP:
1734       {
1735         /*
1736           Transfer pixels top-to-bottom.
1737         */
1738         if (step > y_offset)
1739           break;
1740         q=p-step*GetPixelChannels(image);
1741         for (i=0; i < (ssize_t) height; i++)
1742         {
1743           if ((y_offset+i) < step)
1744             {
1745               p+=GetPixelChannels(image);
1746               GetPixelInfoPixel(image,p,&pixel);
1747               q+=GetPixelChannels(image);
1748               continue;
1749             }
1750           GetPixelInfoPixel(image,p,&source);
1751           CompositePixelInfoAreaBlend(&pixel,(MagickRealType) pixel.alpha,
1752             &source,(MagickRealType) GetPixelAlpha(image,p),area,
1753             &destination);
1754           SetPixelInfoPixel(image,&destination,q);
1755           GetPixelInfoPixel(image,p,&pixel);
1756           p+=GetPixelChannels(image);
1757           q+=GetPixelChannels(image);
1758         }
1759         CompositePixelInfoAreaBlend(&pixel,(MagickRealType) pixel.alpha,
1760           &background,(MagickRealType) background.alpha,area,&destination);
1761         SetPixelInfoPixel(image,&destination,q);
1762         q+=GetPixelChannels(image);
1763         for (i=0; i < (step-1); i++)
1764         {
1765           SetPixelInfoPixel(image,&background,q);
1766           q+=GetPixelChannels(image);
1767         }
1768         break;
1769       }
1770       case DOWN:
1771       {
1772         /*
1773           Transfer pixels bottom-to-top.
1774         */
1775         p+=height*GetPixelChannels(image);
1776         q=p+step*GetPixelChannels(image);
1777         for (i=0; i < (ssize_t) height; i++)
1778         {
1779           p-=GetPixelChannels(image);
1780           q-=GetPixelChannels(image);
1781           if ((size_t) (y_offset+height+step-i) >= image->rows)
1782             continue;
1783           GetPixelInfoPixel(image,p,&source);
1784           CompositePixelInfoAreaBlend(&pixel,(MagickRealType) pixel.alpha,
1785             &source,(MagickRealType) GetPixelAlpha(image,p),area,
1786             &destination);
1787           SetPixelInfoPixel(image,&destination,q);
1788           GetPixelInfoPixel(image,p,&pixel);
1789         }
1790         CompositePixelInfoAreaBlend(&pixel,(MagickRealType) pixel.alpha,
1791           &background,(MagickRealType) background.alpha,area,&destination);
1792         q-=GetPixelChannels(image);
1793         SetPixelInfoPixel(image,&destination,q);
1794         for (i=0; i < (step-1); i++)
1795         {
1796           q-=GetPixelChannels(image);
1797           SetPixelInfoPixel(image,&background,q);
1798         }
1799         break;
1800       }
1801     }
1802     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1803       status=MagickFalse;
1804     if (image->progress_monitor != (MagickProgressMonitor) NULL)
1805       {
1806         MagickBooleanType
1807           proceed;
1808
1809 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1810         #pragma omp critical (MagickCore_YShearImage)
1811 #endif
1812         proceed=SetImageProgress(image,YShearImageTag,progress++,image->rows);
1813         if (proceed == MagickFalse)
1814           status=MagickFalse;
1815       }
1816   }
1817   image_view=DestroyCacheView(image_view);
1818   return(status);
1819 }
1820 \f
1821 /*
1822 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1823 %                                                                             %
1824 %                                                                             %
1825 %                                                                             %
1826 %   S h e a r I m a g e                                                       %
1827 %                                                                             %
1828 %                                                                             %
1829 %                                                                             %
1830 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1831 %
1832 %  ShearImage() creates a new image that is a shear_image copy of an existing
1833 %  one.  Shearing slides one edge of an image along the X or Y axis, creating
1834 %  a parallelogram.  An X direction shear slides an edge along the X axis,
1835 %  while a Y direction shear slides an edge along the Y axis.  The amount of
1836 %  the shear is controlled by a shear angle.  For X direction shears, x_shear
1837 %  is measured relative to the Y axis, and similarly, for Y direction shears
1838 %  y_shear is measured relative to the X axis.  Empty triangles left over from
1839 %  shearing the image are filled with the background color defined by member
1840 %  'background_color' of the image..  ShearImage() allocates the memory
1841 %  necessary for the new Image structure and returns a pointer to the new image.
1842 %
1843 %  ShearImage() is based on the paper "A Fast Algorithm for General Raster
1844 %  Rotatation" by Alan W. Paeth.
1845 %
1846 %  The format of the ShearImage method is:
1847 %
1848 %      Image *ShearImage(const Image *image,const double x_shear,
1849 %        const double y_shear,ExceptionInfo *exception)
1850 %
1851 %  A description of each parameter follows.
1852 %
1853 %    o image: the image.
1854 %
1855 %    o x_shear, y_shear: Specifies the number of degrees to shear the image.
1856 %
1857 %    o exception: return any errors or warnings in this structure.
1858 %
1859 */
1860 MagickExport Image *ShearImage(const Image *image,const double x_shear,
1861   const double y_shear,ExceptionInfo *exception)
1862 {
1863   Image
1864     *integral_image,
1865     *shear_image;
1866
1867   ssize_t
1868     x_offset,
1869     y_offset;
1870
1871   MagickBooleanType
1872     status;
1873
1874   PointInfo
1875     shear;
1876
1877   RectangleInfo
1878     border_info;
1879
1880   size_t
1881     y_width;
1882
1883   assert(image != (Image *) NULL);
1884   assert(image->signature == MagickSignature);
1885   if (image->debug != MagickFalse)
1886     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1887   assert(exception != (ExceptionInfo *) NULL);
1888   assert(exception->signature == MagickSignature);
1889   if ((x_shear != 0.0) && (fmod(x_shear,90.0) == 0.0))
1890     ThrowImageException(ImageError,"AngleIsDiscontinuous");
1891   if ((y_shear != 0.0) && (fmod(y_shear,90.0) == 0.0))
1892     ThrowImageException(ImageError,"AngleIsDiscontinuous");
1893   /*
1894     Initialize shear angle.
1895   */
1896   integral_image=CloneImage(image,0,0,MagickTrue,exception);
1897   if (integral_image == (Image *) NULL)
1898     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1899   shear.x=(-tan(DegreesToRadians(fmod(x_shear,360.0))));
1900   shear.y=tan(DegreesToRadians(fmod(y_shear,360.0)));
1901   if ((shear.x == 0.0) && (shear.y == 0.0))
1902     return(integral_image);
1903   if (SetImageStorageClass(integral_image,DirectClass,exception) == MagickFalse)
1904     {
1905       integral_image=DestroyImage(integral_image);
1906       return(integral_image);
1907     }
1908   if (integral_image->matte == MagickFalse)
1909     (void) SetImageAlphaChannel(integral_image,OpaqueAlphaChannel,exception);
1910   /*
1911     Compute image size.
1912   */
1913   y_width=image->columns+(ssize_t) floor(fabs(shear.x)*image->rows+0.5);
1914   x_offset=(ssize_t) ceil((double) image->columns+((fabs(shear.x)*image->rows)-
1915     image->columns)/2.0-0.5);
1916   y_offset=(ssize_t) ceil((double) image->rows+((fabs(shear.y)*y_width)-
1917     image->rows)/2.0-0.5);
1918   /*
1919     Surround image with border.
1920   */
1921   integral_image->border_color=integral_image->background_color;
1922   integral_image->compose=CopyCompositeOp;
1923   border_info.width=(size_t) x_offset;
1924   border_info.height=(size_t) y_offset;
1925   shear_image=BorderImage(integral_image,&border_info,image->compose,exception);
1926   integral_image=DestroyImage(integral_image);
1927   if (shear_image == (Image *) NULL)
1928     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1929   /*
1930     Shear the image.
1931   */
1932   if (shear_image->matte == MagickFalse)
1933     (void) SetImageAlphaChannel(shear_image,OpaqueAlphaChannel,exception);
1934   status=XShearImage(shear_image,shear.x,image->columns,image->rows,x_offset,
1935     (ssize_t) (shear_image->rows-image->rows)/2,exception);
1936   if (status == MagickFalse)
1937     {
1938       shear_image=DestroyImage(shear_image);
1939       return((Image *) NULL);
1940     }
1941   status=YShearImage(shear_image,shear.y,y_width,image->rows,(ssize_t)
1942     (shear_image->columns-y_width)/2,y_offset,exception);
1943   if (status == MagickFalse)
1944     {
1945       shear_image=DestroyImage(shear_image);
1946       return((Image *) NULL);
1947     }
1948   status=CropToFitImage(&shear_image,shear.x,shear.y,(MagickRealType)
1949     image->columns,(MagickRealType) image->rows,MagickFalse,exception);
1950   if (status == MagickFalse)
1951     {
1952       shear_image=DestroyImage(shear_image);
1953       return((Image *) NULL);
1954     }
1955   shear_image->compose=image->compose;
1956   shear_image->page.width=0;
1957   shear_image->page.height=0;
1958   return(shear_image);
1959 }
1960 \f
1961 /*
1962 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1963 %                                                                             %
1964 %                                                                             %
1965 %                                                                             %
1966 %   S h e a r R o t a t e I m a g e                                           %
1967 %                                                                             %
1968 %                                                                             %
1969 %                                                                             %
1970 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1971 %
1972 %  ShearRotateImage() creates a new image that is a rotated copy of an existing
1973 %  one.  Positive angles rotate counter-clockwise (right-hand rule), while
1974 %  negative angles rotate clockwise.  Rotated images are usually larger than
1975 %  the originals and have 'empty' triangular corners.  X axis.  Empty
1976 %  triangles left over from shearing the image are filled with the background
1977 %  color defined by member 'background_color' of the image.  ShearRotateImage
1978 %  allocates the memory necessary for the new Image structure and returns a
1979 %  pointer to the new image.
1980 %
1981 %  ShearRotateImage() is based on the paper "A Fast Algorithm for General
1982 %  Raster Rotatation" by Alan W. Paeth.  ShearRotateImage is adapted from a
1983 %  similar method based on the Paeth paper written by Michael Halle of the
1984 %  Spatial Imaging Group, MIT Media Lab.
1985 %
1986 %  The format of the ShearRotateImage method is:
1987 %
1988 %      Image *ShearRotateImage(const Image *image,const double degrees,
1989 %        ExceptionInfo *exception)
1990 %
1991 %  A description of each parameter follows.
1992 %
1993 %    o image: the image.
1994 %
1995 %    o degrees: Specifies the number of degrees to rotate the image.
1996 %
1997 %    o exception: return any errors or warnings in this structure.
1998 %
1999 */
2000 MagickExport Image *ShearRotateImage(const Image *image,const double degrees,
2001   ExceptionInfo *exception)
2002 {
2003   Image
2004     *integral_image,
2005     *rotate_image;
2006
2007   MagickBooleanType
2008     status;
2009
2010   MagickRealType
2011     angle;
2012
2013   PointInfo
2014     shear;
2015
2016   RectangleInfo
2017     border_info;
2018
2019   size_t
2020     height,
2021     rotations,
2022     width,
2023     y_width;
2024
2025   ssize_t
2026     x_offset,
2027     y_offset;
2028
2029   /*
2030     Adjust rotation angle.
2031   */
2032   assert(image != (Image *) NULL);
2033   assert(image->signature == MagickSignature);
2034   if (image->debug != MagickFalse)
2035     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2036   assert(exception != (ExceptionInfo *) NULL);
2037   assert(exception->signature == MagickSignature);
2038   angle=degrees;
2039   while (angle < -45.0)
2040     angle+=360.0;
2041   for (rotations=0; angle > 45.0; rotations++)
2042     angle-=90.0;
2043   rotations%=4;
2044   /*
2045     Calculate shear equations.
2046   */
2047   integral_image=IntegralRotateImage(image,rotations,exception);
2048   if (integral_image == (Image *) NULL)
2049     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2050   shear.x=(-tan((double) DegreesToRadians(angle)/2.0));
2051   shear.y=sin((double) DegreesToRadians(angle));
2052   if ((shear.x == 0.0) && (shear.y == 0.0))
2053     return(integral_image);
2054   if (SetImageStorageClass(integral_image,DirectClass,exception) == MagickFalse)
2055     {
2056       integral_image=DestroyImage(integral_image);
2057       return(integral_image);
2058     }
2059   if (integral_image->matte == MagickFalse)
2060     (void) SetImageAlphaChannel(integral_image,OpaqueAlphaChannel,exception);
2061   /*
2062     Compute image size.
2063   */
2064   width=image->columns;
2065   height=image->rows;
2066   if ((rotations == 1) || (rotations == 3))
2067     {
2068       width=image->rows;
2069       height=image->columns;
2070     }
2071   y_width=width+(ssize_t) floor(fabs(shear.x)*height+0.5);
2072   x_offset=(ssize_t) ceil((double) width+((fabs(shear.y)*height)-width)/2.0-
2073     0.5);
2074   y_offset=(ssize_t) ceil((double) height+((fabs(shear.y)*y_width)-height)/2.0-
2075     0.5);
2076   /*
2077     Surround image with a border.
2078   */
2079   integral_image->border_color=integral_image->background_color;
2080   integral_image->compose=CopyCompositeOp;
2081   border_info.width=(size_t) x_offset;
2082   border_info.height=(size_t) y_offset;
2083   rotate_image=BorderImage(integral_image,&border_info,image->compose,
2084     exception);
2085   integral_image=DestroyImage(integral_image);
2086   if (rotate_image == (Image *) NULL)
2087     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2088   /*
2089     Rotate the image.
2090   */
2091   status=XShearImage(rotate_image,shear.x,width,height,x_offset,(ssize_t)
2092     (rotate_image->rows-height)/2,exception);
2093   if (status == MagickFalse)
2094     {
2095       rotate_image=DestroyImage(rotate_image);
2096       return((Image *) NULL);
2097     }
2098   status=YShearImage(rotate_image,shear.y,y_width,height,(ssize_t)
2099     (rotate_image->columns-y_width)/2,y_offset,exception);
2100   if (status == MagickFalse)
2101     {
2102       rotate_image=DestroyImage(rotate_image);
2103       return((Image *) NULL);
2104     }
2105   status=XShearImage(rotate_image,shear.x,y_width,rotate_image->rows,(ssize_t)
2106     (rotate_image->columns-y_width)/2,0,exception);
2107   if (status == MagickFalse)
2108     {
2109       rotate_image=DestroyImage(rotate_image);
2110       return((Image *) NULL);
2111     }
2112   status=CropToFitImage(&rotate_image,shear.x,shear.y,(MagickRealType) width,
2113     (MagickRealType) height,MagickTrue,exception);
2114   if (status == MagickFalse)
2115     {
2116       rotate_image=DestroyImage(rotate_image);
2117       return((Image *) NULL);
2118     }
2119   rotate_image->compose=image->compose;
2120   rotate_image->page.width=0;
2121   rotate_image->page.height=0;
2122   return(rotate_image);
2123 }