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