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