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