shear.c revision c8388eb80b4dd8482349c1f6dbadfcd75cd6766e
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 ((double) GetPixelIntensity(image,p) < threshold)
743        byte|=0x01;
744      bit++;
745      if (bit == 8)
746        {
747          (void) SetRadonCell(source_cells,--i,y,bits[byte]);
748          bit=0;
749          byte=0;
750        }
751      p+=GetPixelChannels(image);
752    }
753    if (bit != 0)
754      {
755        byte<<=(8-bit);
756        (void) SetRadonCell(source_cells,--i,y,bits[byte]);
757      }
758  }
759  RadonProjection(source_cells,destination_cells,-1,projection);
760  (void) ResetRadonCells(source_cells);
761#if defined(MAGICKCORE_OPENMP_SUPPORT)
762  #pragma omp parallel for schedule(dynamic,4) shared(status)
763#endif
764  for (y=0; y < (ssize_t) image->rows; y++)
765  {
766    register const Quantum
767      *restrict p;
768
769    register ssize_t
770      i,
771      x;
772
773    size_t
774      bit,
775      byte;
776
777    if (status == MagickFalse)
778      continue;
779    p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
780    if (p == (const Quantum *) NULL)
781      {
782        status=MagickFalse;
783        continue;
784      }
785    bit=0;
786    byte=0;
787    i=0;
788    for (x=0; x < (ssize_t) image->columns; x++)
789    {
790      byte<<=1;
791      if ((double) GetPixelIntensity(image,p) < threshold)
792        byte|=0x01;
793      bit++;
794      if (bit == 8)
795        {
796          (void) SetRadonCell(source_cells,i++,y,bits[byte]);
797          bit=0;
798          byte=0;
799        }
800      p+=GetPixelChannels(image);
801    }
802    if (bit != 0)
803      {
804        byte<<=(8-bit);
805        (void) SetRadonCell(source_cells,i++,y,bits[byte]);
806      }
807  }
808  RadonProjection(source_cells,destination_cells,1,projection);
809  image_view=DestroyCacheView(image_view);
810  destination_cells=DestroyRadonInfo(destination_cells);
811  source_cells=DestroyRadonInfo(source_cells);
812  return(MagickTrue);
813}
814
815static void GetImageBackgroundColor(Image *image,const ssize_t offset,
816  ExceptionInfo *exception)
817{
818  CacheView
819    *image_view;
820
821  PixelInfo
822    background;
823
824  MagickRealType
825    count;
826
827  ssize_t
828    y;
829
830  /*
831    Compute average background color.
832  */
833  if (offset <= 0)
834    return;
835  GetPixelInfo(image,&background);
836  count=0.0;
837  image_view=AcquireCacheView(image);
838  for (y=0; y < (ssize_t) image->rows; y++)
839  {
840    register const Quantum
841      *restrict p;
842
843    register ssize_t
844      x;
845
846    if ((y >= offset) && (y < ((ssize_t) image->rows-offset)))
847      continue;
848    p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
849    if (p == (const Quantum *) NULL)
850      continue;
851    for (x=0; x < (ssize_t) image->columns; x++)
852    {
853      if ((x >= offset) && (x < ((ssize_t) image->columns-offset)))
854        continue;
855      background.red+=QuantumScale*GetPixelRed(image,p);
856      background.green+=QuantumScale*GetPixelGreen(image,p);
857      background.blue+=QuantumScale*GetPixelBlue(image,p);
858      if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
859        background.alpha+=QuantumScale*GetPixelAlpha(image,p);
860      count++;
861      p+=GetPixelChannels(image);
862    }
863  }
864  image_view=DestroyCacheView(image_view);
865  image->background_color.red=(double) ClampToQuantum((MagickRealType)
866    QuantumRange*background.red/count);
867  image->background_color.green=(double) ClampToQuantum((MagickRealType)
868    QuantumRange*background.green/count);
869  image->background_color.blue=(double) ClampToQuantum((MagickRealType)
870    QuantumRange*background.blue/count);
871  if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
872    image->background_color.alpha=(double) ClampToQuantum((MagickRealType)
873      QuantumRange*background.alpha/count);
874}
875
876MagickExport Image *DeskewImage(const Image *image,const double threshold,
877  ExceptionInfo *exception)
878{
879  AffineMatrix
880    affine_matrix;
881
882  const char
883    *artifact;
884
885  double
886    degrees;
887
888  Image
889    *clone_image,
890    *crop_image,
891    *deskew_image,
892    *median_image;
893
894  MagickBooleanType
895    status;
896
897  RectangleInfo
898    geometry;
899
900  register ssize_t
901    i;
902
903  size_t
904    max_projection,
905    *projection,
906    width;
907
908  ssize_t
909    skew;
910
911  /*
912    Compute deskew angle.
913  */
914  for (width=1; width < ((image->columns+7)/8); width<<=1) ;
915  projection=(size_t *) AcquireQuantumMemory((size_t) (2*width-1),
916    sizeof(*projection));
917  if (projection == (size_t *) NULL)
918    ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
919  status=RadonTransform(image,threshold,projection,exception);
920  if (status == MagickFalse)
921    {
922      projection=(size_t *) RelinquishMagickMemory(projection);
923      ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
924    }
925  max_projection=0;
926  skew=0;
927  for (i=0; i < (ssize_t) (2*width-1); i++)
928  {
929    if (projection[i] > max_projection)
930      {
931        skew=i-(ssize_t) width+1;
932        max_projection=projection[i];
933      }
934  }
935  projection=(size_t *) RelinquishMagickMemory(projection);
936  /*
937    Deskew image.
938  */
939  clone_image=CloneImage(image,0,0,MagickTrue,exception);
940  if (clone_image == (Image *) NULL)
941    return((Image *) NULL);
942  (void) SetImageVirtualPixelMethod(clone_image,BackgroundVirtualPixelMethod);
943  degrees=RadiansToDegrees(-atan((double) skew/width/8));
944  if (image->debug != MagickFalse)
945    (void) LogMagickEvent(TransformEvent,GetMagickModule(),
946      "  Deskew angle: %g",degrees);
947  affine_matrix.sx=cos(DegreesToRadians(fmod((double) degrees,360.0)));
948  affine_matrix.rx=sin(DegreesToRadians(fmod((double) degrees,360.0)));
949  affine_matrix.ry=(-sin(DegreesToRadians(fmod((double) degrees,360.0))));
950  affine_matrix.sy=cos(DegreesToRadians(fmod((double) degrees,360.0)));
951  affine_matrix.tx=0.0;
952  affine_matrix.ty=0.0;
953  artifact=GetImageArtifact(image,"deskew:auto-crop");
954  if (artifact == (const char *) NULL)
955    {
956      deskew_image=AffineTransformImage(clone_image,&affine_matrix,exception);
957      clone_image=DestroyImage(clone_image);
958      return(deskew_image);
959    }
960  /*
961    Auto-crop image.
962  */
963  GetImageBackgroundColor(clone_image,(ssize_t) StringToLong(artifact),
964    exception);
965  deskew_image=AffineTransformImage(clone_image,&affine_matrix,exception);
966  clone_image=DestroyImage(clone_image);
967  if (deskew_image == (Image *) NULL)
968    return((Image *) NULL);
969  median_image=StatisticImage(deskew_image,MedianStatistic,3,3,exception);
970  if (median_image == (Image *) NULL)
971    {
972      deskew_image=DestroyImage(deskew_image);
973      return((Image *) NULL);
974    }
975  geometry=GetImageBoundingBox(median_image,exception);
976  median_image=DestroyImage(median_image);
977  if (image->debug != MagickFalse)
978    (void) LogMagickEvent(TransformEvent,GetMagickModule(),"  Deskew geometry: "
979      "%.20gx%.20g%+.20g%+.20g",(double) geometry.width,(double)
980      geometry.height,(double) geometry.x,(double) geometry.y);
981  crop_image=CropImage(deskew_image,&geometry,exception);
982  deskew_image=DestroyImage(deskew_image);
983  return(crop_image);
984}
985
986/*
987%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
988%                                                                             %
989%                                                                             %
990%                                                                             %
991+   I n t e g r a l R o t a t e I m a g e                                     %
992%                                                                             %
993%                                                                             %
994%                                                                             %
995%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
996%
997%  IntegralRotateImage() rotates the image an integral of 90 degrees.  It
998%  allocates the memory necessary for the new Image structure and returns a
999%  pointer to the rotated image.
1000%
1001%  The format of the IntegralRotateImage method is:
1002%
1003%      Image *IntegralRotateImage(const Image *image,size_t rotations,
1004%        ExceptionInfo *exception)
1005%
1006%  A description of each parameter follows.
1007%
1008%    o image: the image.
1009%
1010%    o rotations: Specifies the number of 90 degree rotations.
1011%
1012*/
1013static Image *IntegralRotateImage(const Image *image,size_t rotations,
1014  ExceptionInfo *exception)
1015{
1016#define RotateImageTag  "Rotate/Image"
1017
1018  CacheView
1019    *image_view,
1020    *rotate_view;
1021
1022  Image
1023    *rotate_image;
1024
1025  MagickBooleanType
1026    status;
1027
1028  MagickOffsetType
1029    progress;
1030
1031  RectangleInfo
1032    page;
1033
1034  ssize_t
1035    y;
1036
1037  /*
1038    Initialize rotated image attributes.
1039  */
1040  assert(image != (Image *) NULL);
1041  page=image->page;
1042  rotations%=4;
1043  if (rotations == 0)
1044    return(CloneImage(image,0,0,MagickTrue,exception));
1045  if ((rotations == 1) || (rotations == 3))
1046    rotate_image=CloneImage(image,image->rows,image->columns,MagickTrue,
1047      exception);
1048  else
1049    rotate_image=CloneImage(image,image->columns,image->rows,MagickTrue,
1050      exception);
1051  if (rotate_image == (Image *) NULL)
1052    return((Image *) NULL);
1053  /*
1054    Integral rotate the image.
1055  */
1056  status=MagickTrue;
1057  progress=0;
1058  image_view=AcquireCacheView(image);
1059  rotate_view=AcquireCacheView(rotate_image);
1060  switch (rotations)
1061  {
1062    case 0:
1063    {
1064      /*
1065        Rotate 0 degrees.
1066      */
1067      break;
1068    }
1069    case 1:
1070    {
1071      size_t
1072        tile_height,
1073        tile_width;
1074
1075      ssize_t
1076        tile_y;
1077
1078      /*
1079        Rotate 90 degrees.
1080      */
1081      GetPixelCacheTileSize(image,&tile_width,&tile_height);
1082      tile_width=image->columns;
1083#if defined(MAGICKCORE_OPENMP_SUPPORT)
1084      #pragma omp parallel for schedule(dynamic,4) shared(progress, status) omp_throttle(1)
1085#endif
1086      for (tile_y=0; tile_y < (ssize_t) image->rows; tile_y+=(ssize_t) tile_height)
1087      {
1088        register ssize_t
1089          tile_x;
1090
1091        if (status == MagickFalse)
1092          continue;
1093        tile_x=0;
1094        for ( ; 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 Quantum
1103            *restrict q;
1104
1105          register ssize_t
1106            y;
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-image->columns));
1115          height=tile_height;
1116          if ((tile_y+(ssize_t) tile_height) > (ssize_t) image->rows)
1117            height=(size_t) (tile_height-(tile_y+tile_height-image->rows));
1118          p=GetCacheViewVirtualPixels(image_view,tile_x,tile_y,width,height,
1119            exception);
1120          if (p == (const Quantum *) NULL)
1121            {
1122              status=MagickFalse;
1123              break;
1124            }
1125          for (y=0; y < (ssize_t) width; y++)
1126          {
1127            register const Quantum
1128              *restrict tile_pixels;
1129
1130            register ssize_t
1131              x;
1132
1133            if (status == MagickFalse)
1134              continue;
1135            q=QueueCacheViewAuthenticPixels(rotate_view,(ssize_t)
1136              (rotate_image->columns-(tile_y+height)),y+tile_x,height,1,
1137              exception);
1138            if (q == (Quantum *) NULL)
1139              {
1140                status=MagickFalse;
1141                continue;
1142              }
1143            tile_pixels=p+((height-1)*width+y)*GetPixelChannels(image);
1144            for (x=0; x < (ssize_t) height; x++)
1145            {
1146              register ssize_t
1147                i;
1148
1149              for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1150              {
1151                PixelChannel
1152                  channel;
1153
1154                PixelTrait
1155                  rotate_traits,
1156                  traits;
1157
1158                traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
1159                channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
1160                rotate_traits=GetPixelChannelMapTraits(rotate_image,channel);
1161                if ((traits == UndefinedPixelTrait) ||
1162                    (rotate_traits == UndefinedPixelTrait))
1163                  continue;
1164                SetPixelChannel(rotate_image,channel,tile_pixels[i],q);
1165              }
1166              tile_pixels-=width*GetPixelChannels(image);
1167              q+=GetPixelChannels(rotate_image);
1168            }
1169            sync=SyncCacheViewAuthenticPixels(rotate_view,exception);
1170            if (sync == MagickFalse)
1171              status=MagickFalse;
1172          }
1173        }
1174        if (image->progress_monitor != (MagickProgressMonitor) NULL)
1175          {
1176            MagickBooleanType
1177              proceed;
1178
1179#if defined(MAGICKCORE_OPENMP_SUPPORT)
1180            #pragma omp critical (MagickCore_IntegralRotateImage)
1181#endif
1182            proceed=SetImageProgress(image,RotateImageTag,progress+=tile_height,
1183              image->rows);
1184            if (proceed == MagickFalse)
1185              status=MagickFalse;
1186          }
1187      }
1188      (void) SetImageProgress(image,RotateImageTag,(MagickOffsetType)
1189        image->rows-1,image->rows);
1190      Swap(page.width,page.height);
1191      Swap(page.x,page.y);
1192      if (page.width != 0)
1193        page.x=(ssize_t) (page.width-rotate_image->columns-page.x);
1194      break;
1195    }
1196    case 2:
1197    {
1198      /*
1199        Rotate 180 degrees.
1200      */
1201#if defined(MAGICKCORE_OPENMP_SUPPORT)
1202      #pragma omp parallel for schedule(dynamic,4) shared(progress,status) omp_throttle(1)
1203#endif
1204      for (y=0; y < (ssize_t) image->rows; y++)
1205      {
1206        MagickBooleanType
1207          sync;
1208
1209        register const Quantum
1210          *restrict p;
1211
1212        register Quantum
1213          *restrict q;
1214
1215        register ssize_t
1216          x;
1217
1218        if (status == MagickFalse)
1219          continue;
1220        p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1221        q=QueueCacheViewAuthenticPixels(rotate_view,0,(ssize_t) (image->rows-y-
1222          1),image->columns,1,exception);
1223        if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1224          {
1225            status=MagickFalse;
1226            continue;
1227          }
1228        q+=GetPixelChannels(rotate_image)*image->columns;
1229        for (x=0; x < (ssize_t) image->columns; x++)
1230        {
1231          register ssize_t
1232            i;
1233
1234          q-=GetPixelChannels(rotate_image);
1235          for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1236          {
1237            PixelChannel
1238              channel;
1239
1240            PixelTrait
1241              rotate_traits,
1242              traits;
1243
1244            traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
1245            channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
1246            rotate_traits=GetPixelChannelMapTraits(rotate_image,channel);
1247            if ((traits == UndefinedPixelTrait) ||
1248                (rotate_traits == UndefinedPixelTrait))
1249              continue;
1250            SetPixelChannel(rotate_image,channel,p[i],q);
1251          }
1252          p+=GetPixelChannels(image);
1253        }
1254        sync=SyncCacheViewAuthenticPixels(rotate_view,exception);
1255        if (sync == MagickFalse)
1256          status=MagickFalse;
1257        if (image->progress_monitor != (MagickProgressMonitor) NULL)
1258          {
1259            MagickBooleanType
1260              proceed;
1261
1262#if defined(MAGICKCORE_OPENMP_SUPPORT)
1263            #pragma omp critical (MagickCore_IntegralRotateImage)
1264#endif
1265            proceed=SetImageProgress(image,RotateImageTag,progress++,
1266              image->rows);
1267            if (proceed == MagickFalse)
1268              status=MagickFalse;
1269          }
1270      }
1271      (void) SetImageProgress(image,RotateImageTag,(MagickOffsetType)
1272        image->rows-1,image->rows);
1273      Swap(page.width,page.height);
1274      Swap(page.x,page.y);
1275      if (page.width != 0)
1276        page.x=(ssize_t) (page.width-rotate_image->columns-page.x);
1277      break;
1278    }
1279    case 3:
1280    {
1281      size_t
1282        tile_height,
1283        tile_width;
1284
1285      ssize_t
1286        tile_y;
1287
1288      /*
1289        Rotate 270 degrees.
1290      */
1291      GetPixelCacheTileSize(image,&tile_width,&tile_height);
1292      tile_width=image->columns;
1293#if defined(MAGICKCORE_OPENMP_SUPPORT)
1294      #pragma omp parallel for schedule(dynamic,4) shared(progress,status) omp_throttle(1)
1295#endif
1296      for (tile_y=0; tile_y < (ssize_t) image->rows; tile_y+=(ssize_t) tile_height)
1297      {
1298        register ssize_t
1299          tile_x;
1300
1301        if (status == MagickFalse)
1302          continue;
1303        tile_x=0;
1304        for ( ; tile_x < (ssize_t) image->columns; tile_x+=(ssize_t) tile_width)
1305        {
1306          MagickBooleanType
1307            sync;
1308
1309          register const Quantum
1310            *restrict p;
1311
1312          register Quantum
1313            *restrict q;
1314
1315          register ssize_t
1316            y;
1317
1318          size_t
1319            height,
1320            width;
1321
1322          width=tile_width;
1323          if ((tile_x+(ssize_t) tile_width) > (ssize_t) image->columns)
1324            width=(size_t) (tile_width-(tile_x+tile_width-image->columns));
1325          height=tile_height;
1326          if ((tile_y+(ssize_t) tile_height) > (ssize_t) image->rows)
1327            height=(size_t) (tile_height-(tile_y+tile_height-image->rows));
1328          p=GetCacheViewVirtualPixels(image_view,tile_x,tile_y,width,height,
1329            exception);
1330          if (p == (const Quantum *) NULL)
1331            {
1332              status=MagickFalse;
1333              break;
1334            }
1335          for (y=0; y < (ssize_t) width; y++)
1336          {
1337            register const Quantum
1338              *restrict tile_pixels;
1339
1340            register ssize_t
1341              x;
1342
1343            if (status == MagickFalse)
1344              continue;
1345            q=QueueCacheViewAuthenticPixels(rotate_view,tile_y,(ssize_t) (y+
1346              rotate_image->rows-(tile_x+width)),height,1,exception);
1347            if (q == (Quantum *) NULL)
1348              {
1349                status=MagickFalse;
1350                continue;
1351              }
1352            tile_pixels=p+((width-1)-y)*GetPixelChannels(image);
1353            for (x=0; x < (ssize_t) height; x++)
1354            {
1355              register ssize_t
1356                i;
1357
1358              for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1359              {
1360                PixelChannel
1361                  channel;
1362
1363                PixelTrait
1364                  rotate_traits,
1365                  traits;
1366
1367                traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
1368                channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
1369                rotate_traits=GetPixelChannelMapTraits(rotate_image,channel);
1370                if ((traits == UndefinedPixelTrait) ||
1371                    (rotate_traits == UndefinedPixelTrait))
1372                  continue;
1373                SetPixelChannel(rotate_image,channel,tile_pixels[i],q);
1374              }
1375              tile_pixels+=width*GetPixelChannels(image);
1376              q+=GetPixelChannels(rotate_image);
1377            }
1378            sync=SyncCacheViewAuthenticPixels(rotate_view,exception);
1379            if (sync == MagickFalse)
1380              status=MagickFalse;
1381          }
1382        }
1383        if (image->progress_monitor != (MagickProgressMonitor) NULL)
1384          {
1385            MagickBooleanType
1386              proceed;
1387
1388#if defined(MAGICKCORE_OPENMP_SUPPORT)
1389            #pragma omp critical (MagickCore_IntegralRotateImage)
1390#endif
1391            proceed=SetImageProgress(image,RotateImageTag,progress+=tile_height,
1392              image->rows);
1393            if (proceed == MagickFalse)
1394              status=MagickFalse;
1395          }
1396      }
1397      (void) SetImageProgress(image,RotateImageTag,(MagickOffsetType)
1398        image->rows-1,image->rows);
1399      Swap(page.width,page.height);
1400      Swap(page.x,page.y);
1401      if (page.width != 0)
1402        page.x=(ssize_t) (page.width-rotate_image->columns-page.x);
1403      break;
1404    }
1405  }
1406  rotate_view=DestroyCacheView(rotate_view);
1407  image_view=DestroyCacheView(image_view);
1408  rotate_image->type=image->type;
1409  rotate_image->page=page;
1410  if (status == MagickFalse)
1411    rotate_image=DestroyImage(rotate_image);
1412  return(rotate_image);
1413}
1414
1415/*
1416%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1417%                                                                             %
1418%                                                                             %
1419%                                                                             %
1420+   X S h e a r I m a g e                                                     %
1421%                                                                             %
1422%                                                                             %
1423%                                                                             %
1424%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1425%
1426%  XShearImage() shears the image in the X direction with a shear angle of
1427%  'degrees'.  Positive angles shear counter-clockwise (right-hand rule), and
1428%  negative angles shear clockwise.  Angles are measured relative to a vertical
1429%  Y-axis.  X shears will widen an image creating 'empty' triangles on the left
1430%  and right sides of the source image.
1431%
1432%  The format of the XShearImage method is:
1433%
1434%      MagickBooleanType XShearImage(Image *image,const MagickRealType degrees,
1435%        const size_t width,const size_t height,
1436%        const ssize_t x_offset,const ssize_t y_offset,ExceptionInfo *exception)
1437%
1438%  A description of each parameter follows.
1439%
1440%    o image: the image.
1441%
1442%    o degrees: A MagickRealType representing the shearing angle along the X
1443%      axis.
1444%
1445%    o width, height, x_offset, y_offset: Defines a region of the image
1446%      to shear.
1447%
1448%    o exception: return any errors or warnings in this structure.
1449%
1450*/
1451static MagickBooleanType XShearImage(Image *image,const MagickRealType degrees,
1452  const size_t width,const size_t height,const ssize_t x_offset,
1453  const ssize_t y_offset,ExceptionInfo *exception)
1454{
1455#define XShearImageTag  "XShear/Image"
1456
1457  typedef enum
1458  {
1459    LEFT,
1460    RIGHT
1461  } ShearDirection;
1462
1463  CacheView
1464    *image_view;
1465
1466  MagickBooleanType
1467    status;
1468
1469  MagickOffsetType
1470    progress;
1471
1472  PixelInfo
1473    background;
1474
1475  ssize_t
1476    y;
1477
1478  assert(image != (Image *) NULL);
1479  assert(image->signature == MagickSignature);
1480  if (image->debug != MagickFalse)
1481    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1482  GetPixelInfo(image,&background);
1483  SetPixelInfoPacket(image,&image->background_color,&background);
1484  if (image->colorspace == CMYKColorspace)
1485    ConvertRGBToCMYK(&background);
1486  /*
1487    X shear image.
1488  */
1489  status=MagickTrue;
1490  progress=0;
1491  image_view=AcquireCacheView(image);
1492#if defined(MAGICKCORE_OPENMP_SUPPORT)
1493  #pragma omp parallel for schedule(dynamic,4) shared(progress, status)
1494#endif
1495  for (y=0; y < (ssize_t) height; y++)
1496  {
1497    PixelInfo
1498      pixel,
1499      source,
1500      destination;
1501
1502    MagickRealType
1503      area,
1504      displacement;
1505
1506    register Quantum
1507      *restrict p,
1508      *restrict q;
1509
1510    register ssize_t
1511      i;
1512
1513    ShearDirection
1514      direction;
1515
1516    ssize_t
1517      step;
1518
1519    if (status == MagickFalse)
1520      continue;
1521    p=GetCacheViewAuthenticPixels(image_view,0,y_offset+y,image->columns,1,
1522      exception);
1523    if (p == (Quantum *) NULL)
1524      {
1525        status=MagickFalse;
1526        continue;
1527      }
1528    p+=x_offset*GetPixelChannels(image);
1529    displacement=degrees*(MagickRealType) (y-height/2.0);
1530    if (displacement == 0.0)
1531      continue;
1532    if (displacement > 0.0)
1533      direction=RIGHT;
1534    else
1535      {
1536        displacement*=(-1.0);
1537        direction=LEFT;
1538      }
1539    step=(ssize_t) floor((double) displacement);
1540    area=(MagickRealType) (displacement-step);
1541    step++;
1542    pixel=background;
1543    GetPixelInfo(image,&source);
1544    GetPixelInfo(image,&destination);
1545    switch (direction)
1546    {
1547      case LEFT:
1548      {
1549        /*
1550          Transfer pixels left-to-right.
1551        */
1552        if (step > x_offset)
1553          break;
1554        q=p-step*GetPixelChannels(image);
1555        for (i=0; i < (ssize_t) width; i++)
1556        {
1557          if ((x_offset+i) < step)
1558            {
1559              p+=GetPixelChannels(image);
1560              SetPixelInfo(image,p,&pixel);
1561              q+=GetPixelChannels(image);
1562              continue;
1563            }
1564          SetPixelInfo(image,p,&source);
1565          CompositePixelInfoAreaBlend(&pixel,(MagickRealType) pixel.alpha,
1566            &source,(MagickRealType) GetPixelAlpha(image,p),area,&destination);
1567          SetPixelPixelInfo(image,&destination,q);
1568          SetPixelInfo(image,p,&pixel);
1569          p+=GetPixelChannels(image);
1570          q+=GetPixelChannels(image);
1571        }
1572        CompositePixelInfoAreaBlend(&pixel,(MagickRealType) pixel.alpha,
1573          &background,(MagickRealType) background.alpha,area,&destination);
1574        SetPixelPixelInfo(image,&destination,q);
1575        q+=GetPixelChannels(image);
1576        for (i=0; i < (step-1); i++)
1577        {
1578          SetPixelPixelInfo(image,&background,q);
1579          q+=GetPixelChannels(image);
1580        }
1581        break;
1582      }
1583      case RIGHT:
1584      {
1585        /*
1586          Transfer pixels right-to-left.
1587        */
1588        p+=width*GetPixelChannels(image);
1589        q=p+step*GetPixelChannels(image);
1590        for (i=0; i < (ssize_t) width; i++)
1591        {
1592          p-=GetPixelChannels(image);
1593          q-=GetPixelChannels(image);
1594          if ((size_t) (x_offset+width+step-i) >= image->columns)
1595            continue;
1596          SetPixelInfo(image,p,&source);
1597          CompositePixelInfoAreaBlend(&pixel,(MagickRealType) pixel.alpha,
1598            &source,(MagickRealType) GetPixelAlpha(image,p),area,&destination);
1599          SetPixelPixelInfo(image,&destination,q);
1600          SetPixelInfo(image,p,&pixel);
1601        }
1602        CompositePixelInfoAreaBlend(&pixel,(MagickRealType) pixel.alpha,
1603          &background,(MagickRealType) background.alpha,area,&destination);
1604        q-=GetPixelChannels(image);
1605        SetPixelPixelInfo(image,&destination,q);
1606        for (i=0; i < (step-1); i++)
1607        {
1608          q-=GetPixelChannels(image);
1609          SetPixelPixelInfo(image,&background,q);
1610        }
1611        break;
1612      }
1613    }
1614    if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1615      status=MagickFalse;
1616    if (image->progress_monitor != (MagickProgressMonitor) NULL)
1617      {
1618        MagickBooleanType
1619          proceed;
1620
1621#if defined(MAGICKCORE_OPENMP_SUPPORT)
1622        #pragma omp critical (MagickCore_XShearImage)
1623#endif
1624        proceed=SetImageProgress(image,XShearImageTag,progress++,height);
1625        if (proceed == MagickFalse)
1626          status=MagickFalse;
1627      }
1628  }
1629  image_view=DestroyCacheView(image_view);
1630  return(status);
1631}
1632
1633/*
1634%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1635%                                                                             %
1636%                                                                             %
1637%                                                                             %
1638+   Y S h e a r I m a g e                                                     %
1639%                                                                             %
1640%                                                                             %
1641%                                                                             %
1642%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1643%
1644%  YShearImage shears the image in the Y direction with a shear angle of
1645%  'degrees'.  Positive angles shear counter-clockwise (right-hand rule), and
1646%  negative angles shear clockwise.  Angles are measured relative to a
1647%  horizontal X-axis.  Y shears will increase the height of an image creating
1648%  'empty' triangles on the top and bottom of the source image.
1649%
1650%  The format of the YShearImage method is:
1651%
1652%      MagickBooleanType YShearImage(Image *image,const MagickRealType degrees,
1653%        const size_t width,const size_t height,
1654%        const ssize_t x_offset,const ssize_t y_offset,ExceptionInfo *exception)
1655%
1656%  A description of each parameter follows.
1657%
1658%    o image: the image.
1659%
1660%    o degrees: A MagickRealType representing the shearing angle along the Y
1661%      axis.
1662%
1663%    o width, height, x_offset, y_offset: Defines a region of the image
1664%      to shear.
1665%
1666%    o exception: return any errors or warnings in this structure.
1667%
1668*/
1669static MagickBooleanType YShearImage(Image *image,const MagickRealType degrees,
1670  const size_t width,const size_t height,const ssize_t x_offset,
1671  const ssize_t y_offset,ExceptionInfo *exception)
1672{
1673#define YShearImageTag  "YShear/Image"
1674
1675  typedef enum
1676  {
1677    UP,
1678    DOWN
1679  } ShearDirection;
1680
1681  CacheView
1682    *image_view;
1683
1684  MagickBooleanType
1685    status;
1686
1687  MagickOffsetType
1688    progress;
1689
1690  PixelInfo
1691    background;
1692
1693  ssize_t
1694    x;
1695
1696  assert(image != (Image *) NULL);
1697  assert(image->signature == MagickSignature);
1698  if (image->debug != MagickFalse)
1699    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1700  GetPixelInfo(image,&background);
1701  SetPixelInfoPacket(image,&image->background_color,&background);
1702  if (image->colorspace == CMYKColorspace)
1703    ConvertRGBToCMYK(&background);
1704  /*
1705    Y Shear image.
1706  */
1707  status=MagickTrue;
1708  progress=0;
1709  image_view=AcquireCacheView(image);
1710#if defined(MAGICKCORE_OPENMP_SUPPORT)
1711  #pragma omp parallel for schedule(dynamic,4) shared(progress, status)
1712#endif
1713  for (x=0; x < (ssize_t) width; x++)
1714  {
1715    ssize_t
1716      step;
1717
1718    MagickRealType
1719      area,
1720      displacement;
1721
1722    PixelInfo
1723      pixel,
1724      source,
1725      destination;
1726
1727    register Quantum
1728      *restrict p,
1729      *restrict q;
1730
1731    register ssize_t
1732      i;
1733
1734    ShearDirection
1735      direction;
1736
1737    if (status == MagickFalse)
1738      continue;
1739    p=GetCacheViewAuthenticPixels(image_view,x_offset+x,0,1,image->rows,
1740      exception);
1741    if (p == (Quantum *) NULL)
1742      {
1743        status=MagickFalse;
1744        continue;
1745      }
1746    p+=y_offset*GetPixelChannels(image);
1747    displacement=degrees*(MagickRealType) (x-width/2.0);
1748    if (displacement == 0.0)
1749      continue;
1750    if (displacement > 0.0)
1751      direction=DOWN;
1752    else
1753      {
1754        displacement*=(-1.0);
1755        direction=UP;
1756      }
1757    step=(ssize_t) floor((double) displacement);
1758    area=(MagickRealType) (displacement-step);
1759    step++;
1760    pixel=background;
1761    GetPixelInfo(image,&source);
1762    GetPixelInfo(image,&destination);
1763    switch (direction)
1764    {
1765      case UP:
1766      {
1767        /*
1768          Transfer pixels top-to-bottom.
1769        */
1770        if (step > y_offset)
1771          break;
1772        q=p-step*GetPixelChannels(image);
1773        for (i=0; i < (ssize_t) height; i++)
1774        {
1775          if ((y_offset+i) < step)
1776            {
1777              p+=GetPixelChannels(image);
1778              SetPixelInfo(image,p,&pixel);
1779              q+=GetPixelChannels(image);
1780              continue;
1781            }
1782          SetPixelInfo(image,p,&source);
1783          CompositePixelInfoAreaBlend(&pixel,(MagickRealType) pixel.alpha,
1784            &source,(MagickRealType) GetPixelAlpha(image,p),area,
1785            &destination);
1786          SetPixelPixelInfo(image,&destination,q);
1787          SetPixelInfo(image,p,&pixel);
1788          p+=GetPixelChannels(image);
1789          q+=GetPixelChannels(image);
1790        }
1791        CompositePixelInfoAreaBlend(&pixel,(MagickRealType) pixel.alpha,
1792          &background,(MagickRealType) background.alpha,area,&destination);
1793        SetPixelPixelInfo(image,&destination,q);
1794        q+=GetPixelChannels(image);
1795        for (i=0; i < (step-1); i++)
1796        {
1797          SetPixelPixelInfo(image,&background,q);
1798          q+=GetPixelChannels(image);
1799        }
1800        break;
1801      }
1802      case DOWN:
1803      {
1804        /*
1805          Transfer pixels bottom-to-top.
1806        */
1807        p+=height*GetPixelChannels(image);
1808        q=p+step*GetPixelChannels(image);
1809        for (i=0; i < (ssize_t) height; i++)
1810        {
1811          p-=GetPixelChannels(image);
1812          q-=GetPixelChannels(image);
1813          if ((size_t) (y_offset+height+step-i) >= image->rows)
1814            continue;
1815          SetPixelInfo(image,p,&source);
1816          CompositePixelInfoAreaBlend(&pixel,(MagickRealType) pixel.alpha,
1817            &source,(MagickRealType) GetPixelAlpha(image,p),area,
1818            &destination);
1819          SetPixelPixelInfo(image,&destination,q);
1820          SetPixelInfo(image,p,&pixel);
1821        }
1822        CompositePixelInfoAreaBlend(&pixel,(MagickRealType) pixel.alpha,
1823          &background,(MagickRealType) background.alpha,area,&destination);
1824        q-=GetPixelChannels(image);
1825        SetPixelPixelInfo(image,&destination,q);
1826        for (i=0; i < (step-1); i++)
1827        {
1828          q-=GetPixelChannels(image);
1829          SetPixelPixelInfo(image,&background,q);
1830        }
1831        break;
1832      }
1833    }
1834    if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1835      status=MagickFalse;
1836    if (image->progress_monitor != (MagickProgressMonitor) NULL)
1837      {
1838        MagickBooleanType
1839          proceed;
1840
1841#if defined(MAGICKCORE_OPENMP_SUPPORT)
1842        #pragma omp critical (MagickCore_YShearImage)
1843#endif
1844        proceed=SetImageProgress(image,YShearImageTag,progress++,image->rows);
1845        if (proceed == MagickFalse)
1846          status=MagickFalse;
1847      }
1848  }
1849  image_view=DestroyCacheView(image_view);
1850  return(status);
1851}
1852
1853/*
1854%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1855%                                                                             %
1856%                                                                             %
1857%                                                                             %
1858%   R o t a t e I m a g e                                                     %
1859%                                                                             %
1860%                                                                             %
1861%                                                                             %
1862%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1863%
1864%  RotateImage() creates a new image that is a rotated copy of an existing
1865%  one.  Positive angles rotate counter-clockwise (right-hand rule), while
1866%  negative angles rotate clockwise.  Rotated images are usually larger than
1867%  the originals and have 'empty' triangular corners.  X axis.  Empty
1868%  triangles left over from shearing the image are filled with the background
1869%  color defined by member 'background_color' of the image.  RotateImage
1870%  allocates the memory necessary for the new Image structure and returns a
1871%  pointer to the new image.
1872%
1873%  RotateImage() is based on the paper "A Fast Algorithm for General
1874%  Raster Rotatation" by Alan W. Paeth.  RotateImage is adapted from a similar
1875%  method based on the Paeth paper written by Michael Halle of the Spatial
1876%  Imaging Group, MIT Media Lab.
1877%
1878%  The format of the RotateImage method is:
1879%
1880%      Image *RotateImage(const Image *image,const double degrees,
1881%        ExceptionInfo *exception)
1882%
1883%  A description of each parameter follows.
1884%
1885%    o image: the image.
1886%
1887%    o degrees: Specifies the number of degrees to rotate the image.
1888%
1889%    o exception: return any errors or warnings in this structure.
1890%
1891*/
1892MagickExport Image *RotateImage(const Image *image,const double degrees,
1893  ExceptionInfo *exception)
1894{
1895  Image
1896    *integral_image,
1897    *rotate_image;
1898
1899  MagickBooleanType
1900    status;
1901
1902  MagickRealType
1903    angle;
1904
1905  PointInfo
1906    shear;
1907
1908  RectangleInfo
1909    border_info;
1910
1911  size_t
1912    height,
1913    rotations,
1914    width,
1915    y_width;
1916
1917  ssize_t
1918    x_offset,
1919    y_offset;
1920
1921  /*
1922    Adjust rotation angle.
1923  */
1924  assert(image != (Image *) NULL);
1925  assert(image->signature == MagickSignature);
1926  if (image->debug != MagickFalse)
1927    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1928  assert(exception != (ExceptionInfo *) NULL);
1929  assert(exception->signature == MagickSignature);
1930  angle=degrees;
1931  while (angle < -45.0)
1932    angle+=360.0;
1933  for (rotations=0; angle > 45.0; rotations++)
1934    angle-=90.0;
1935  rotations%=4;
1936  /*
1937    Calculate shear equations.
1938  */
1939  integral_image=IntegralRotateImage(image,rotations,exception);
1940  if (integral_image == (Image *) NULL)
1941    ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1942  shear.x=(-tan((double) DegreesToRadians(angle)/2.0));
1943  shear.y=sin((double) DegreesToRadians(angle));
1944  if ((shear.x == 0.0) && (shear.y == 0.0))
1945    return(integral_image);
1946  if (SetImageStorageClass(integral_image,DirectClass,exception) == MagickFalse)
1947    {
1948      integral_image=DestroyImage(integral_image);
1949      return(integral_image);
1950    }
1951  if (integral_image->matte == MagickFalse)
1952    (void) SetImageAlphaChannel(integral_image,OpaqueAlphaChannel,exception);
1953  /*
1954    Compute image size.
1955  */
1956  width=image->columns;
1957  height=image->rows;
1958  if ((rotations == 1) || (rotations == 3))
1959    {
1960      width=image->rows;
1961      height=image->columns;
1962    }
1963  y_width=width+(ssize_t) floor(fabs(shear.x)*height+0.5);
1964  x_offset=(ssize_t) ceil((double) width+((fabs(shear.y)*height)-width)/2.0-
1965    0.5);
1966  y_offset=(ssize_t) ceil((double) height+((fabs(shear.y)*y_width)-height)/2.0-
1967    0.5);
1968  /*
1969    Surround image with a border.
1970  */
1971  integral_image->border_color=integral_image->background_color;
1972  integral_image->compose=CopyCompositeOp;
1973  border_info.width=(size_t) x_offset;
1974  border_info.height=(size_t) y_offset;
1975  rotate_image=BorderImage(integral_image,&border_info,image->compose,
1976    exception);
1977  integral_image=DestroyImage(integral_image);
1978  if (rotate_image == (Image *) NULL)
1979    ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1980  /*
1981    Rotate the image.
1982  */
1983  status=XShearImage(rotate_image,shear.x,width,height,x_offset,(ssize_t)
1984    (rotate_image->rows-height)/2,exception);
1985  if (status == MagickFalse)
1986    {
1987      rotate_image=DestroyImage(rotate_image);
1988      return((Image *) NULL);
1989    }
1990  status=YShearImage(rotate_image,shear.y,y_width,height,(ssize_t)
1991    (rotate_image->columns-y_width)/2,y_offset,exception);
1992  if (status == MagickFalse)
1993    {
1994      rotate_image=DestroyImage(rotate_image);
1995      return((Image *) NULL);
1996    }
1997  status=XShearImage(rotate_image,shear.x,y_width,rotate_image->rows,(ssize_t)
1998    (rotate_image->columns-y_width)/2,0,exception);
1999  if (status == MagickFalse)
2000    {
2001      rotate_image=DestroyImage(rotate_image);
2002      return((Image *) NULL);
2003    }
2004  status=CropToFitImage(&rotate_image,shear.x,shear.y,(MagickRealType) width,
2005    (MagickRealType) height,MagickTrue,exception);
2006  if (status == MagickFalse)
2007    {
2008      rotate_image=DestroyImage(rotate_image);
2009      return((Image *) NULL);
2010    }
2011  rotate_image->compose=image->compose;
2012  rotate_image->page.width=0;
2013  rotate_image->page.height=0;
2014  return(rotate_image);
2015}
2016
2017/*
2018%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2019%                                                                             %
2020%                                                                             %
2021%                                                                             %
2022%   S h e a r I m a g e                                                       %
2023%                                                                             %
2024%                                                                             %
2025%                                                                             %
2026%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2027%
2028%  ShearImage() creates a new image that is a shear_image copy of an existing
2029%  one.  Shearing slides one edge of an image along the X or Y axis, creating
2030%  a parallelogram.  An X direction shear slides an edge along the X axis,
2031%  while a Y direction shear slides an edge along the Y axis.  The amount of
2032%  the shear is controlled by a shear angle.  For X direction shears, x_shear
2033%  is measured relative to the Y axis, and similarly, for Y direction shears
2034%  y_shear is measured relative to the X axis.  Empty triangles left over from
2035%  shearing the image are filled with the background color defined by member
2036%  'background_color' of the image..  ShearImage() allocates the memory
2037%  necessary for the new Image structure and returns a pointer to the new image.
2038%
2039%  ShearImage() is based on the paper "A Fast Algorithm for General Raster
2040%  Rotatation" by Alan W. Paeth.
2041%
2042%  The format of the ShearImage method is:
2043%
2044%      Image *ShearImage(const Image *image,const double x_shear,
2045%        const double y_shear,ExceptionInfo *exception)
2046%
2047%  A description of each parameter follows.
2048%
2049%    o image: the image.
2050%
2051%    o x_shear, y_shear: Specifies the number of degrees to shear the image.
2052%
2053%    o exception: return any errors or warnings in this structure.
2054%
2055*/
2056MagickExport Image *ShearImage(const Image *image,const double x_shear,
2057  const double y_shear,ExceptionInfo *exception)
2058{
2059  Image
2060    *integral_image,
2061    *shear_image;
2062
2063  ssize_t
2064    x_offset,
2065    y_offset;
2066
2067  MagickBooleanType
2068    status;
2069
2070  PointInfo
2071    shear;
2072
2073  RectangleInfo
2074    border_info;
2075
2076  size_t
2077    y_width;
2078
2079  assert(image != (Image *) NULL);
2080  assert(image->signature == MagickSignature);
2081  if (image->debug != MagickFalse)
2082    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2083  assert(exception != (ExceptionInfo *) NULL);
2084  assert(exception->signature == MagickSignature);
2085  if ((x_shear != 0.0) && (fmod(x_shear,90.0) == 0.0))
2086    ThrowImageException(ImageError,"AngleIsDiscontinuous");
2087  if ((y_shear != 0.0) && (fmod(y_shear,90.0) == 0.0))
2088    ThrowImageException(ImageError,"AngleIsDiscontinuous");
2089  /*
2090    Initialize shear angle.
2091  */
2092  integral_image=CloneImage(image,0,0,MagickTrue,exception);
2093  if (integral_image == (Image *) NULL)
2094    ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2095  shear.x=(-tan(DegreesToRadians(fmod(x_shear,360.0))));
2096  shear.y=tan(DegreesToRadians(fmod(y_shear,360.0)));
2097  if ((shear.x == 0.0) && (shear.y == 0.0))
2098    return(integral_image);
2099  if (SetImageStorageClass(integral_image,DirectClass,exception) == MagickFalse)
2100    {
2101      integral_image=DestroyImage(integral_image);
2102      return(integral_image);
2103    }
2104  if (integral_image->matte == MagickFalse)
2105    (void) SetImageAlphaChannel(integral_image,OpaqueAlphaChannel,exception);
2106  /*
2107    Compute image size.
2108  */
2109  y_width=image->columns+(ssize_t) floor(fabs(shear.x)*image->rows+0.5);
2110  x_offset=(ssize_t) ceil((double) image->columns+((fabs(shear.x)*image->rows)-
2111    image->columns)/2.0-0.5);
2112  y_offset=(ssize_t) ceil((double) image->rows+((fabs(shear.y)*y_width)-
2113    image->rows)/2.0-0.5);
2114  /*
2115    Surround image with border.
2116  */
2117  integral_image->border_color=integral_image->background_color;
2118  integral_image->compose=CopyCompositeOp;
2119  border_info.width=(size_t) x_offset;
2120  border_info.height=(size_t) y_offset;
2121  shear_image=BorderImage(integral_image,&border_info,image->compose,exception);
2122  integral_image=DestroyImage(integral_image);
2123  if (shear_image == (Image *) NULL)
2124    ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2125  /*
2126    Shear the image.
2127  */
2128  if (shear_image->matte == MagickFalse)
2129    (void) SetImageAlphaChannel(shear_image,OpaqueAlphaChannel,exception);
2130  status=XShearImage(shear_image,shear.x,image->columns,image->rows,x_offset,
2131    (ssize_t) (shear_image->rows-image->rows)/2,exception);
2132  if (status == MagickFalse)
2133    {
2134      shear_image=DestroyImage(shear_image);
2135      return((Image *) NULL);
2136    }
2137  status=YShearImage(shear_image,shear.y,y_width,image->rows,(ssize_t)
2138    (shear_image->columns-y_width)/2,y_offset,exception);
2139  if (status == MagickFalse)
2140    {
2141      shear_image=DestroyImage(shear_image);
2142      return((Image *) NULL);
2143    }
2144  status=CropToFitImage(&shear_image,shear.x,shear.y,(MagickRealType)
2145    image->columns,(MagickRealType) image->rows,MagickFalse,exception);
2146  if (status == MagickFalse)
2147    {
2148      shear_image=DestroyImage(shear_image);
2149      return((Image *) NULL);
2150    }
2151  shear_image->compose=image->compose;
2152  shear_image->page.width=0;
2153  shear_image->page.height=0;
2154  return(shear_image);
2155}
2156