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