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