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