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