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