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