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