source: ImageMagick/branches/ImageMagick-6/magick/shear.c @ 8450

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