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