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