1 /*
2 % Copyright (C) 2003 - 2020 GraphicsMagick Group
3 % Copyright (C) 2002 ImageMagick Studio
4 % Copyright 1991-1999 E. I. du Pont de Nemours and Company
5 %
6 % This program is covered by multiple licenses, which are described in
7 % Copyright.txt. You should have received a copy of Copyright.txt with this
8 % package; otherwise see http://www.graphicsmagick.org/www/Copyright.html.
9 %
10 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11 % %
12 % %
13 % %
14 % TTTTT RRRR AAA N N SSSSS FFFFF OOO RRRR M M %
15 % T R R A A NN N SS F O O R R MM MM %
16 % T RRRR AAAAA N N N SSS FFF O O RRRR M M M %
17 % T R R A A N NN SS F O O R R M M %
18 % T R R A A N N SSSSS F OOO R R M M %
19 % %
20 % %
21 % GraphicsMagick Image Transform Methods %
22 % %
23 % %
24 % Software Design %
25 % John Cristy %
26 % July 1992 %
27 % %
28 % %
29 % %
30 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
31 %
32 %
33 %
34 */
35
36 /*
37 Include declarations.
38 */
39 #include "magick/studio.h"
40 #include "magick/analyze.h"
41 #include "magick/color.h"
42 #include "magick/composite.h"
43 #include "magick/monitor.h"
44 #include "magick/pixel_cache.h"
45 #include "magick/resize.h"
46 #include "magick/texture.h"
47 #include "magick/transform.h"
48 #include "magick/utility.h"
49 #include "magick/log.h"
50
51 /*
52 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
53 % %
54 % %
55 % %
56 % C h o p I m a g e %
57 % %
58 % %
59 % %
60 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
61 %
62 % Chop() removes a region of an image and collapses the image to occupy the
63 % removed portion.
64 %
65 % The format of the ChopImage method is:
66 %
67 % Image *ChopImage(const Image *image,const RectangleInfo *chop_info
68 % ExceptionInfo *exception)
69 %
70 % A description of each parameter follows:
71 %
72 % o image: The image.
73 %
74 % o chop_info: Define the region of the image to chop.
75 %
76 % o exception: Return any errors or warnings in this structure.
77 %
78 %
79 */
ChopImage(const Image * image,const RectangleInfo * chop_info,ExceptionInfo * exception)80 MagickExport Image *ChopImage(const Image *image,const RectangleInfo *chop_info,
81 ExceptionInfo *exception)
82 {
83 #define ChopImageText "[%s] Chop..."
84
85 Image
86 *chop_image;
87
88 unsigned long
89 row_count=0;
90
91 MagickBool
92 monitor_active;
93
94 long
95 y;
96
97 RectangleInfo
98 clone_info;
99
100 MagickPassFail
101 status=MagickPass;
102
103 /*
104 Check chop geometry.
105 */
106 assert(image != (const Image *) NULL);
107 assert(image->signature == MagickSignature);
108 assert(exception != (ExceptionInfo *) NULL);
109 assert(exception->signature == MagickSignature);
110 assert(chop_info != (RectangleInfo *) NULL);
111 if (((chop_info->x+(long) chop_info->width) < 0) ||
112 ((chop_info->y+(long) chop_info->height) < 0) ||
113 (chop_info->x > (long) image->columns) ||
114 (chop_info->y > (long) image->rows))
115 ThrowImageException3(OptionError,GeometryDoesNotContainImage,
116 UnableToChopImage);
117 clone_info=(*chop_info);
118 if ((clone_info.x+(long) clone_info.width) > (long) image->columns)
119 clone_info.width=(unsigned long) ((long) image->columns-clone_info.x);
120 if ((clone_info.y+(long) clone_info.height) > (long) image->rows)
121 clone_info.height=(unsigned long) ((long) image->rows-clone_info.y);
122 if (clone_info.x < 0)
123 {
124 clone_info.width-=(unsigned long) (-clone_info.x);
125 clone_info.x=0;
126 }
127 if (clone_info.y < 0)
128 {
129 clone_info.height-=(unsigned long) (-clone_info.y);
130 clone_info.y=0;
131 }
132 /*
133 Initialize chop image attributes.
134 */
135 chop_image=CloneImage(image,image->columns-clone_info.width,
136 image->rows-clone_info.height,False,exception);
137 if (chop_image == (Image *) NULL)
138 return((Image *) NULL);
139
140 /*
141 Extract chop image.
142 */
143 monitor_active=MagickMonitorActive();
144
145 #if defined(HAVE_OPENMP) && !defined(DisableSlowOpenMP)
146 # if defined(TUNE_OPENMP)
147 # pragma omp parallel for schedule(runtime) shared(row_count, status)
148 # else
149 # pragma omp parallel for schedule(static,4) shared(row_count, status)
150 # endif
151 #endif
152 for (y=0; y < (long) clone_info.y; y++)
153 {
154 register const PixelPacket
155 *p;
156
157 register const IndexPacket
158 *indexes;
159
160 register IndexPacket
161 *chop_indexes;
162
163 register long
164 x;
165
166 register PixelPacket
167 *q;
168
169 MagickBool
170 thread_status;
171
172 thread_status=status;
173 if (thread_status == MagickFail)
174 continue;
175
176 p=AcquireImagePixels(image,0,y,image->columns,1,exception);
177 q=SetImagePixelsEx(chop_image,0,y,chop_image->columns,1,exception);
178 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
179 thread_status=MagickFail;
180
181 if (thread_status != MagickFail)
182 {
183 indexes=AccessImmutableIndexes(image);
184 chop_indexes=AccessMutableIndexes(chop_image);
185 for (x=0; x < (long) image->columns; x++)
186 {
187 if ((x < clone_info.x) || (x >= (long) (clone_info.x+clone_info.width)))
188 {
189 if ((indexes != (const IndexPacket *) NULL) &&
190 (chop_indexes != (IndexPacket *) NULL))
191 *chop_indexes++=indexes[x];
192 *q=(*p);
193 q++;
194 }
195 p++;
196 }
197 if (!SyncImagePixelsEx(chop_image,exception))
198 thread_status=MagickFail;
199 }
200
201 #if defined(HAVE_OPENMP) && !defined(DisableSlowOpenMP)
202 # pragma omp atomic
203 #endif
204 row_count++;
205
206 if (monitor_active)
207 {
208 unsigned long
209 thread_row_count;
210
211 #if defined(HAVE_OPENMP)
212 # pragma omp flush (row_count)
213 #endif
214 thread_row_count=row_count;
215 if (QuantumTick(thread_row_count,chop_image->rows))
216 if (!MagickMonitorFormatted(thread_row_count,chop_image->rows,exception,
217 ChopImageText,image->filename))
218 thread_status=MagickFail;
219 }
220
221 if (thread_status == MagickFail)
222 {
223 status=MagickFail;
224 #if defined(HAVE_OPENMP) && !defined(DisableSlowOpenMP)
225 # pragma omp flush (status)
226 #endif
227 }
228 }
229 /*
230 Extract chop image.
231 */
232 #if defined(HAVE_OPENMP) && !defined(DisableSlowOpenMP)
233 # if defined(TUNE_OPENMP)
234 # pragma omp parallel for schedule(runtime) shared(row_count, status)
235 # else
236 # pragma omp parallel for schedule(static,4) shared(row_count, status)
237 # endif
238 #endif
239 for (y=0; y < (long) (image->rows-(clone_info.y+clone_info.height)); y++)
240 {
241 register const PixelPacket
242 *p;
243
244 register const IndexPacket
245 *indexes;
246
247 register IndexPacket
248 *chop_indexes;
249
250 register long
251 x;
252
253 register PixelPacket
254 *q;
255
256 MagickBool
257 thread_status;
258
259 thread_status=status;
260 if (thread_status == MagickFail)
261 continue;
262
263 p=AcquireImagePixels(image,0,clone_info.y+clone_info.height+y,image->columns,1,exception);
264 q=SetImagePixelsEx(chop_image,0,clone_info.y+y,chop_image->columns,1,exception);
265 if ((p == (PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
266 thread_status=MagickFail;
267
268 if (thread_status != MagickFail)
269 {
270 indexes=AccessImmutableIndexes(image);
271 chop_indexes=AccessMutableIndexes(chop_image);
272 for (x=0; x < (long) image->columns; x++)
273 {
274 if ((x < clone_info.x) || (x >= (long) (clone_info.x+clone_info.width)))
275 {
276 if ((indexes != (const IndexPacket *) NULL) &&
277 (chop_indexes != (IndexPacket *) NULL))
278 *chop_indexes++=indexes[x];
279 *q=(*p);
280 q++;
281 }
282 p++;
283 }
284 if (!SyncImagePixelsEx(chop_image,exception))
285 thread_status=MagickFail;
286 }
287
288 #if defined(HAVE_OPENMP) && !defined(DisableSlowOpenMP)
289 # pragma omp atomic
290 #endif
291 row_count++;
292
293 if (monitor_active)
294 {
295 unsigned long
296 thread_row_count;
297
298 #if defined(HAVE_OPENMP)
299 # pragma omp flush (row_count)
300 #endif
301 thread_row_count=row_count;
302 if (QuantumTick(thread_row_count,chop_image->rows))
303 if (!MagickMonitorFormatted(thread_row_count,chop_image->rows,exception,
304 ChopImageText,image->filename))
305 thread_status=MagickFail;
306 }
307
308 if (thread_status == MagickFail)
309 {
310 status=MagickFail;
311 #if defined(HAVE_OPENMP) && !defined(DisableSlowOpenMP)
312 # pragma omp flush (status)
313 #endif
314 }
315 }
316 if (row_count < chop_image->rows)
317 {
318 DestroyImage(chop_image);
319 return((Image *) NULL);
320 }
321 chop_image->is_grayscale=image->is_grayscale;
322 return(chop_image);
323 }
324
325 /*
326 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
327 % %
328 % %
329 % %
330 % C o a l e s c e I m a g e s %
331 % %
332 % %
333 % %
334 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
335 %
336 % CoalesceImages() composites a set of images while respecting any page
337 % offsets and disposal methods. GIF, MIFF, and MNG animation sequences
338 % typically start with an image background and each subsequent image
339 % varies in size and offset. CoalesceImages() returns a new sequence
340 % where each image in the sequence is the same size as the first and
341 % composited with the next image in the sequence.
342 %
343 % The format of the CoalesceImages method is:
344 %
345 % Image *CoalesceImages(const Image *image,ExceptionInfo *exception)
346 %
347 % A description of each parameter follows:
348 %
349 % o image: The image sequence.
350 %
351 % o exception: Return any errors or warnings in this structure.
352 %
353 */
CoalesceImages(const Image * image,ExceptionInfo * exception)354 MagickExport Image *CoalesceImages(const Image *image,ExceptionInfo *exception)
355 {
356 Image
357 *coalesce_image,
358 *previous_image;
359
360 register const Image
361 *next;
362
363 register long
364 i;
365
366 MagickBool
367 found_transparency=False;
368
369 /*
370 Coalesce the image sequence.
371 */
372 assert(image != (Image *) NULL);
373 assert(image->signature == MagickSignature);
374 assert(exception != (ExceptionInfo *) NULL);
375 assert(exception->signature == MagickSignature);
376 if (image->next == (Image *) NULL)
377 ThrowImageException3(ImageError,ImageSequenceIsRequired,
378 UnableToCoalesceImage);
379 /*
380 Clone first image in sequence.
381 */
382 coalesce_image=CloneImage(image,0,0,True,exception);
383 if (coalesce_image == (Image *) NULL)
384 return((Image *) NULL);
385 (void) memset(&coalesce_image->page,0,sizeof(RectangleInfo));
386 previous_image=coalesce_image;
387 /*
388 Coalesce image.
389 */
390 for (next=image->next; next != (Image *) NULL; next=next->next)
391 {
392 switch (next->dispose)
393 {
394 case UndefinedDispose:
395 case NoneDispose:
396 {
397 coalesce_image->next=CloneImage(coalesce_image,0,0,True,exception);
398 if (coalesce_image->next != (Image *) NULL)
399 previous_image=coalesce_image->next;
400 break;
401 }
402 case BackgroundDispose:
403 {
404 /*
405 Fill image with transparent color, if one exists.
406 */
407 coalesce_image->next=CloneImage(coalesce_image,0,0,True,exception);
408 if (coalesce_image->next != (Image *) NULL) {
409 for (i = 0; i < (long) coalesce_image->colors; i++) {
410 if (coalesce_image->colormap[i].opacity == TransparentOpacity) {
411 found_transparency = True;
412 (void) SetImageColor(coalesce_image->next,&coalesce_image->colormap[i]);
413 break;
414 }
415 }
416 if (!found_transparency)
417 (void) SetImage(coalesce_image->next,OpaqueOpacity);
418 }
419 break;
420 }
421 case PreviousDispose:
422 default:
423 {
424 coalesce_image->next=CloneImage(previous_image,0,0,True,exception);
425 break;
426 }
427 }
428 if (coalesce_image->next == (Image *) NULL)
429 {
430 DestroyImageList(coalesce_image);
431 return((Image *) NULL);
432 }
433 coalesce_image->next->previous=coalesce_image;
434 coalesce_image=coalesce_image->next;
435 coalesce_image->delay=next->delay;
436 coalesce_image->start_loop=next->start_loop;
437 (void) CompositeImage(coalesce_image,next->matte ? OverCompositeOp :
438 CopyCompositeOp,next,next->page.x,next->page.y);
439 }
440
441 while (coalesce_image->previous != (Image *) NULL)
442 coalesce_image=coalesce_image->previous;
443 return(coalesce_image);
444 }
445
446 /*
447 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
448 % %
449 % %
450 % %
451 % C r o p I m a g e %
452 % %
453 % %
454 % %
455 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
456 %
457 % Use CropImage() to extract a region of the image starting at the offset
458 % defined by geometry. As a special feature, if the geometry "0x0" is
459 % is passed, GetImageBoundingBox() is used to locate the edges of the
460 % image and the image is cropped ("trimmed") to that boundary.
461 %
462 % The format of the CropImage method is:
463 %
464 % Image *CropImage(const Image *image,const RectangleInfo *geometry,
465 % ExceptionInfo *exception)
466 %
467 % A description of each parameter follows:
468 %
469 % o image: The image.
470 %
471 % o geometry: Define the region of the image to crop with members
472 % x, y, width, and height.
473 %
474 % o exception: Return any errors or warnings in this structure.
475 %
476 %
477 */
CropImage(const Image * image,const RectangleInfo * geometry,ExceptionInfo * exception)478 MagickExport Image *CropImage(const Image *image,const RectangleInfo *geometry,
479 ExceptionInfo *exception)
480 {
481 Image
482 *crop_image;
483
484 unsigned long
485 row_count=0;
486
487 MagickBool
488 monitor_active;
489
490 long
491 y;
492
493 RectangleInfo
494 page;
495
496 MagickPassFail
497 status=MagickPass;
498
499 /*
500 Check crop geometry.
501 */
502 assert(image != (const Image *) NULL);
503 assert(image->signature == MagickSignature);
504 assert(geometry != (const RectangleInfo *) NULL);
505 assert(exception != (ExceptionInfo *) NULL);
506 assert(exception->signature == MagickSignature);
507 if ((geometry->width != 0) || (geometry->height != 0))
508 {
509 if (((geometry->x+(long) geometry->width) < 0) ||
510 ((geometry->y+(long) geometry->height) < 0) ||
511 (geometry->x >= (long) image->columns) ||
512 (geometry->y >= (long) image->rows))
513 ThrowImageException(OptionError,GeometryDoesNotContainImage,
514 MagickMsg(ResourceLimitError,UnableToCropImage));
515 }
516 page=(*geometry);
517 if ((page.width != 0) || (page.height != 0))
518 {
519 if ((page.x+(long) page.width) > (long) image->columns)
520 page.width=image->columns-page.x;
521 if ((page.y+(long) page.height) > (long) image->rows)
522 page.height=image->rows-page.y;
523 if (page.x < 0)
524 {
525 page.width+=page.x;
526 page.x=0;
527 }
528 if (page.y < 0)
529 {
530 page.height+=page.y;
531 page.y=0;
532 }
533 }
534 else
535 {
536 /*
537 Set bounding box to the image dimensions.
538 */
539 page=GetImageBoundingBox(image,exception);
540 page.width+=geometry->x*2;
541 page.height+=geometry->y*2;
542 page.x-=geometry->x;
543 if (page.x < 0)
544 page.x=0;
545 page.y-=geometry->y;
546 if (page.y < 0)
547 page.y=0;
548 if ((((long) page.width+page.x) > (long) image->columns) ||
549 (((long) page.height+page.y) > (long) image->rows))
550 ThrowImageException(OptionError,GeometryDoesNotContainImage,
551 MagickMsg(ResourceLimitError,UnableToCropImage));
552 }
553 if ((page.width == 0) || (page.height == 0))
554 ThrowImageException(OptionError,GeometryDimensionsAreZero,
555 MagickMsg(ResourceLimitError,UnableToCropImage));
556 if ((page.width == image->columns) && (page.height == image->rows) &&
557 (page.x == 0) && (page.y == 0))
558 return(CloneImage(image,0,0,True,exception));
559 /*
560 Initialize crop image attributes.
561 */
562 crop_image=CloneImage(image,page.width,page.height,True,exception);
563 if (crop_image == (Image *) NULL)
564 return((Image *) NULL);
565 /*
566 Extract crop image.
567 */
568 crop_image->page=page;
569 if ((geometry->width == 0) || (geometry->height == 0))
570 (void) memset(&crop_image->page,0,sizeof(RectangleInfo));
571
572 monitor_active=MagickMonitorActive();
573
574 #if defined(HAVE_OPENMP) && !defined(DisableSlowOpenMP)
575 # if defined(TUNE_OPENMP)
576 # pragma omp parallel for schedule(runtime) shared(row_count, status)
577 # else
578 # pragma omp parallel for schedule(static,4) shared(row_count, status)
579 # endif
580 #endif
581 for (y=0; y < (long) crop_image->rows; y++)
582 {
583 const PixelPacket
584 *p;
585
586 const IndexPacket
587 *indexes;
588
589 IndexPacket
590 *crop_indexes;
591
592 PixelPacket
593 *q;
594
595 MagickBool
596 thread_status;
597
598 thread_status=status;
599 if (thread_status == MagickFail)
600 continue;
601
602 p=AcquireImagePixels(image,page.x,page.y+y,crop_image->columns,1,exception);
603 q=SetImagePixelsEx(crop_image,0,y,crop_image->columns,1,exception);
604 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
605 thread_status=MagickFail;
606
607 if (thread_status != MagickFail)
608 {
609 (void) memcpy(q,p,crop_image->columns*sizeof(PixelPacket));
610 indexes=AccessImmutableIndexes(image);
611 crop_indexes=AccessMutableIndexes(crop_image);
612 if ((indexes != (const IndexPacket *) NULL) &&
613 (crop_indexes != (IndexPacket *) NULL))
614 (void) memcpy(crop_indexes,indexes,crop_image->columns*
615 sizeof(IndexPacket));
616 if (!SyncImagePixelsEx(crop_image,exception))
617 thread_status=MagickFail;
618 }
619
620 #if defined(HAVE_OPENMP) && !defined(DisableSlowOpenMP)
621 # pragma omp atomic
622 #endif
623 row_count++;
624
625 if (monitor_active)
626 {
627 unsigned long
628 thread_row_count;
629
630 #if defined(HAVE_OPENMP)
631 # pragma omp flush (row_count)
632 #endif
633 thread_row_count=row_count;
634 if (QuantumTick(thread_row_count,crop_image->rows))
635 if (!MagickMonitorFormatted(thread_row_count,crop_image->rows,exception,
636 "[%s] Crop: %lux%lu+%ld+%ld...",
637 crop_image->filename,
638 crop_image->columns,crop_image->rows,
639 page.x,page.y))
640 thread_status=MagickFail;
641 }
642
643 if (thread_status == MagickFail)
644 {
645 status=MagickFail;
646 #if defined(HAVE_OPENMP) && !defined(DisableSlowOpenMP)
647 # pragma omp flush (status)
648 #endif
649 }
650 }
651 if (row_count < crop_image->rows)
652 {
653 DestroyImage(crop_image);
654 return((Image *) NULL);
655 }
656 crop_image->is_grayscale=image->is_grayscale;
657 return(crop_image);
658 }
659
660 /*
661 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
662 % %
663 % %
664 % %
665 % D e c o n s t r u c t I m a g e s %
666 % %
667 % %
668 % %
669 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
670 %
671 % DeconstructImages() compares each image with the next in a sequence and
672 % returns the maximum bounding region of any pixel differences it discovers.
673 %
674 % The format of the DeconstructImages method is:
675 %
676 % Image *DeconstructImages(const Image *image,ExceptionInfo *exception)
677 %
678 % A description of each parameter follows:
679 %
680 % o image: The image.
681 %
682 % o exception: Return any errors or warnings in this structure.
683 %
684 %
685 */
DeconstructImages(const Image * image,ExceptionInfo * exception)686 MagickExport Image *DeconstructImages(const Image *image,
687 ExceptionInfo *exception)
688 {
689 Image
690 *crop_image,
691 *crop_next,
692 *deconstruct_image;
693
694 long
695 y;
696
697 RectangleInfo
698 *bounds;
699
700 register const Image
701 *next;
702
703 register const PixelPacket
704 *p;
705
706 register long
707 i,
708 x;
709
710 register PixelPacket
711 *q;
712
713 assert(image != (const Image *) NULL);
714 assert(image->signature == MagickSignature);
715 assert(exception != (ExceptionInfo *) NULL);
716 assert(exception->signature == MagickSignature);
717 if (image->next == (Image *) NULL)
718 ThrowImageException3(ImageError,ImageSequenceIsRequired,
719 UnableToDeconstructImageSequence);
720 /*
721 Ensure the image are the same size.
722 */
723 for (next=image; next != (Image *) NULL; next=next->next)
724 {
725 if ((next->columns != image->columns) || (next->rows != image->rows))
726 ThrowImageException(OptionError,ImagesAreNotTheSameSize,
727 MagickMsg(ImageError,UnableToDeconstructImageSequence));
728 }
729 /*
730 Allocate memory.
731 */
732 bounds=MagickAllocateMemory(RectangleInfo *,
733 GetImageListLength(image)*sizeof(RectangleInfo));
734 if (bounds == (RectangleInfo *) NULL)
735 ThrowImageException(ResourceLimitError,MemoryAllocationFailed,
736 MagickMsg(ImageError,UnableToDeconstructImageSequence));
737 /*
738 Compute the bounding box for each next in the sequence.
739 */
740 i=0;
741 for (next=image->next; next != (const Image *) NULL; next=next->next)
742 {
743 /*
744 Set bounding box to the next dimensions.
745 */
746 for (x=0; x < (long) next->columns; x++)
747 {
748 p=AcquireImagePixels(next,x,0,1,next->rows,exception);
749 q=GetImagePixels(next->previous,x,0,1,next->previous->rows);
750 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
751 break;
752 for (y=0; y < (long) next->rows; y++)
753 {
754 if (!FuzzyColorMatch(p,q,next->fuzz))
755 break;
756 p++;
757 q++;
758 }
759 if (y < (long) next->rows)
760 break;
761 }
762 bounds[i].x=x;
763 for (y=0; y < (long) next->rows; y++)
764 {
765 p=AcquireImagePixels(next,0,y,next->columns,1,exception);
766 q=GetImagePixels(next->previous,0,y,next->previous->columns,1);
767 if ((p == (PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
768 break;
769 for (x=0; x < (long) next->columns; x++)
770 {
771 if (!FuzzyColorMatch(p,q,next->fuzz))
772 break;
773 p++;
774 q++;
775 }
776 if (x < (long) next->columns)
777 break;
778 }
779 bounds[i].y=y;
780 for (x=(long) next->columns-1; x >= 0; x--)
781 {
782 p=AcquireImagePixels(next,x,0,1,next->rows,exception);
783 q=GetImagePixels(next->previous,x,0,1,next->previous->rows);
784 if ((p == (PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
785 break;
786 for (y=0; y < (long) next->rows; y++)
787 {
788 if (!FuzzyColorMatch(p,q,next->fuzz))
789 break;
790 p++;
791 q++;
792 }
793 if (y < (long) next->rows)
794 break;
795 }
796 bounds[i].width=x-bounds[i].x+1;
797 for (y=(long) next->rows-1; y >= 0; y--)
798 {
799 p=AcquireImagePixels(next,0,y,next->columns,1,exception);
800 q=GetImagePixels(next->previous,0,y,next->previous->columns,1);
801 if ((p == (PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
802 break;
803 for (x=0; x < (long) next->columns; x++)
804 {
805 if (!FuzzyColorMatch(p,q,next->fuzz))
806 break;
807 p++;
808 q++;
809 }
810 if (x < (long) next->columns)
811 break;
812 }
813 bounds[i].height=y-bounds[i].y+1;
814 i++;
815 }
816 /*
817 Clone first image in sequence.
818 */
819 deconstruct_image=CloneImage(image,0,0,True,exception);
820 if (deconstruct_image == (Image *) NULL)
821 {
822 MagickFreeMemory(bounds);
823 return((Image *) NULL);
824 }
825 /*
826 Deconstruct the image sequence.
827 */
828 i=0;
829 for (next=image->next; next != (Image *) NULL; next=next->next)
830 {
831 crop_image=CloneImage(next,0,0,True,exception);
832 if (crop_image == (Image *) NULL)
833 break;
834 crop_next=CropImage(crop_image,&bounds[i++],exception);
835 DestroyImage(crop_image);
836 if (crop_next == (Image *) NULL)
837 break;
838 deconstruct_image->next=crop_next;
839 crop_next->previous=deconstruct_image;
840 deconstruct_image=deconstruct_image->next;
841 }
842 MagickFreeMemory(bounds);
843 while (deconstruct_image->previous != (Image *) NULL)
844 deconstruct_image=deconstruct_image->previous;
845 if (next != (Image *) NULL)
846 {
847 DestroyImageList(deconstruct_image);
848 return((Image *) NULL);
849 }
850 return(deconstruct_image);
851 }
852
853 /*
854 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
855 % %
856 % %
857 % %
858 % E x t e n t I m a g e %
859 % %
860 % %
861 % %
862 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
863 %
864 % Use ExtentImage() to change the image dimensions as specified by geometry
865 % width and height. The existing image content is composited at the position
866 % specified by geometry x and y using the image compose method. Existing
867 % image content which falls outside the bounds of the new image dimensions
868 % is discarded.
869 %
870 % The format of the ExtentImage method is:
871 %
872 % Image *ExtentImage(const Image *image,const RectangleInfo *geometry,
873 % ExceptionInfo *exception)
874 %
875 % A description of each parameter follows:
876 %
877 % o image: The image.
878 %
879 % o geometry: Define the new image dimension with width and height, and
880 % the top left coordinate to place the existing image content with
881 % x and y.
882 %
883 % o exception: Return any errors or warnings in this structure.
884 %
885 %
886 */
ExtentImage(const Image * image,const RectangleInfo * geometry,ExceptionInfo * exception)887 MagickExport Image *ExtentImage(const Image *image,const RectangleInfo *geometry,
888 ExceptionInfo *exception)
889 {
890 Image
891 *extent_image;
892
893 assert(image != (const Image *) NULL);
894 assert(image->signature == MagickSignature);
895 assert(geometry != (const RectangleInfo *) NULL);
896 assert(exception != (ExceptionInfo *) NULL);
897 assert(exception->signature == MagickSignature);
898
899 /*
900 Allocate canvas image
901 */
902 if ((extent_image=CloneImage(image,geometry->width,geometry->height,
903 MagickTrue,exception)) == (Image *) NULL)
904 return((Image *) NULL);
905
906 /*
907 Set canvas image color to background color
908 */
909 if ((SetImage(extent_image,image->background_color.opacity)) == MagickFail)
910 {
911 CopyException(exception,&extent_image->exception);
912 DestroyImage(extent_image);
913 return((Image *) NULL);
914 }
915
916 /*
917 Composite existing image at position using requested composition
918 operator.
919 */
920 if ((CompositeImage(extent_image,image->compose,image,geometry->x,
921 geometry->y)) == MagickFail)
922 {
923 CopyException(exception,&extent_image->exception);
924 DestroyImage(extent_image);
925 return((Image *) NULL);
926 }
927
928 return(extent_image);
929 }
930
931 /*
932 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
933 % %
934 % %
935 % %
936 % F l a t t e n I m a g e %
937 % %
938 % %
939 % %
940 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
941 %
942 % Method FlattenImage merges a sequence of images. This is useful for
943 % combining Photoshop layers into a single image.
944 %
945 % The format of the FlattenImage method is:
946 %
947 % Image *FlattenImage(const Image *image,ExceptionInfo *exception)
948 %
949 % A description of each parameter follows:
950 %
951 % o image: The image sequence.
952 %
953 % o exception: Return any errors or warnings in this structure.
954 %
955 */
FlattenImages(const Image * image,ExceptionInfo * exception)956 MagickExport Image *FlattenImages(const Image *image,ExceptionInfo *exception)
957 {
958 Image
959 *flatten_image;
960
961 register const Image
962 *next;
963
964 /*
965 Flatten the image sequence.
966 */
967 assert(image != (Image *) NULL);
968 assert(image->signature == MagickSignature);
969 assert(exception != (ExceptionInfo *) NULL);
970 assert(exception->signature == MagickSignature);
971
972 /*
973 Clone first image in sequence to serve as canvas image
974 */
975 flatten_image=CloneImage(image,0,0,True,exception);
976
977 /*
978 Apply background color under image if it has a matte channel.
979 */
980 if ((flatten_image != (Image *) NULL) && (flatten_image->matte))
981 (void) MagickCompositeImageUnderColor(flatten_image,
982 &flatten_image->background_color,
983 exception);
984
985 if ((flatten_image != (Image *) NULL) &&
986 (image->next != (Image *) NULL))
987 {
988 /*
989 Flatten remaining images onto canvas
990 */
991 for (next=image->next; next != (Image *) NULL; next=next->next)
992 (void) CompositeImage(flatten_image,next->compose,next,next->page.x,
993 next->page.y);
994 }
995 return(flatten_image);
996 }
997
998 /*
999 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1000 % %
1001 % %
1002 % %
1003 % F l i p I m a g e %
1004 % %
1005 % %
1006 % %
1007 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1008 %
1009 % FlipImage() creates a vertical mirror image by reflecting the pixels
1010 % around the central x-axis.
1011 %
1012 % The format of the FlipImage method is:
1013 %
1014 % Image *FlipImage(const Image *image,ExceptionInfo *exception)
1015 %
1016 % A description of each parameter follows:
1017 %
1018 % o image: The image.
1019 %
1020 % o exception: Return any errors or warnings in this structure.
1021 %
1022 %
1023 */
FlipImage(const Image * image,ExceptionInfo * exception)1024 MagickExport Image *FlipImage(const Image *image,ExceptionInfo *exception)
1025 {
1026 #define FlipImageText "[%s] Flip..."
1027
1028 Image
1029 *flip_image;
1030
1031 unsigned long
1032 row_count=0;
1033
1034 MagickBool
1035 monitor_active;
1036
1037 long
1038 y;
1039
1040 MagickPassFail
1041 status=MagickPass;
1042
1043 /*
1044 Initialize flip image attributes.
1045 */
1046 assert(image != (const Image *) NULL);
1047 assert(image->signature == MagickSignature);
1048 assert(exception != (ExceptionInfo *) NULL);
1049 assert(exception->signature == MagickSignature);
1050
1051 if ((image->columns == 0UL) || (image->rows == 0UL))
1052 ThrowImageException(ImageError,UnableToResizeImage,
1053 MagickMsg(OptionError,NonzeroWidthAndHeightRequired));
1054
1055 if (((((size_t) Max(sizeof(PixelPacket),sizeof(IndexPacket)))*image->columns)/
1056 image->columns) != Max(sizeof(PixelPacket),sizeof(IndexPacket)))
1057 ThrowImageException(ImageError,WidthOrHeightExceedsLimit,image->filename);
1058
1059 flip_image=CloneImage(image,image->columns,image->rows,True,exception);
1060 if (flip_image == (Image *) NULL)
1061 return((Image *) NULL);
1062 /*
1063 Flip each row.
1064 */
1065
1066 monitor_active=MagickMonitorActive();
1067
1068 #if defined(HAVE_OPENMP) && !defined(DisableSlowOpenMP)
1069 # if defined(TUNE_OPENMP)
1070 # pragma omp parallel for schedule(runtime) shared(row_count, status)
1071 # else
1072 # pragma omp parallel for schedule(static,4) shared(row_count, status)
1073 # endif
1074 #endif
1075 for (y=0; y < (long) flip_image->rows; y++)
1076 {
1077 const PixelPacket
1078 *p;
1079
1080 const IndexPacket
1081 *indexes;
1082
1083 IndexPacket
1084 *flip_indexes;
1085
1086 PixelPacket
1087 *q;
1088
1089 MagickBool
1090 thread_status;
1091
1092 thread_status=status;
1093 if (thread_status == MagickFail)
1094 continue;
1095
1096 p=AcquireImagePixels(image,0,y,image->columns,1,exception);
1097 q=SetImagePixelsEx(flip_image,0,(long) (flip_image->rows-y-1),
1098 flip_image->columns,1,exception);
1099 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
1100 thread_status=MagickFail;
1101
1102 if (thread_status != MagickFail)
1103 {
1104 (void) memcpy(q,p,flip_image->columns*sizeof(PixelPacket));
1105 indexes=AccessImmutableIndexes(image);
1106 flip_indexes=AccessMutableIndexes(flip_image);
1107 if ((indexes != (IndexPacket *) NULL) &&
1108 (flip_indexes != (IndexPacket *) NULL))
1109 (void) memcpy(flip_indexes,indexes,image->columns*sizeof(IndexPacket));
1110 if (!SyncImagePixelsEx(flip_image,exception))
1111 thread_status=MagickFail;
1112 }
1113 #if defined(HAVE_OPENMP) && !defined(DisableSlowOpenMP)
1114 # pragma omp atomic
1115 #endif
1116 row_count++;
1117
1118 if (monitor_active)
1119 {
1120 unsigned long
1121 thread_row_count;
1122
1123 #if defined(HAVE_OPENMP)
1124 # pragma omp flush (row_count)
1125 #endif
1126 thread_row_count=row_count;
1127 if (QuantumTick(thread_row_count,flip_image->rows))
1128 if (!MagickMonitorFormatted(thread_row_count,flip_image->rows,exception,
1129 FlipImageText,image->filename))
1130 thread_status=MagickFail;
1131 }
1132
1133 if (thread_status == MagickFail)
1134 {
1135 status=MagickFail;
1136 #if defined(HAVE_OPENMP) && !defined(DisableSlowOpenMP)
1137 # pragma omp flush (status)
1138 #endif
1139 }
1140 }
1141 if (row_count < flip_image->rows)
1142 {
1143 DestroyImage(flip_image);
1144 return((Image *) NULL);
1145 }
1146 flip_image->is_grayscale=image->is_grayscale;
1147 return(flip_image);
1148 }
1149
1150 /*
1151 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1152 % %
1153 % %
1154 % %
1155 % F l o p I m a g e %
1156 % %
1157 % %
1158 % %
1159 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1160 %
1161 % FlopImage() creates a horizontal mirror image by reflecting the pixels
1162 % around the central y-axis.
1163 %
1164 % The format of the FlopImage method is:
1165 %
1166 % Image *FlopImage(const Image *image,ExceptionInfo *exception)
1167 %
1168 % A description of each parameter follows:
1169 %
1170 % o image: The image.
1171 %
1172 % o exception: Return any errors or warnings in this structure.
1173 %
1174 %
1175 */
FlopImage(const Image * image,ExceptionInfo * exception)1176 MagickExport Image *FlopImage(const Image *image,ExceptionInfo *exception)
1177 {
1178 #define FlopImageText "[%s] Flop..."
1179
1180 Image
1181 *flop_image;
1182
1183 unsigned long
1184 row_count=0;
1185
1186 MagickBool
1187 monitor_active;
1188
1189 long
1190 y;
1191
1192 MagickPassFail
1193 status=MagickPass;
1194
1195 /*
1196 Initialize flop image attributes.
1197 */
1198 assert(image != (const Image *) NULL);
1199 assert(image->signature == MagickSignature);
1200 assert(exception != (ExceptionInfo *) NULL);
1201 assert(exception->signature == MagickSignature);
1202 flop_image=CloneImage(image,image->columns,image->rows,True,exception);
1203 if (flop_image == (Image *) NULL)
1204 return((Image *) NULL);
1205 /*
1206 Flop each row.
1207 */
1208
1209 monitor_active=MagickMonitorActive();
1210
1211 #if defined(HAVE_OPENMP) && !defined(DisableSlowOpenMP)
1212 # if defined(TUNE_OPENMP)
1213 # pragma omp parallel for schedule(runtime) shared(row_count, status)
1214 # else
1215 # pragma omp parallel for schedule(static,4) shared(row_count, status)
1216 # endif
1217 #endif
1218 for (y=0; y < (long) flop_image->rows; y++)
1219 {
1220 register const IndexPacket
1221 *indexes;
1222
1223 register IndexPacket
1224 *flop_indexes;
1225
1226 register const PixelPacket
1227 *p;
1228
1229 register long
1230 x;
1231
1232 register PixelPacket
1233 *q;
1234
1235 MagickBool
1236 thread_status;
1237
1238 thread_status=status;
1239 if (thread_status == MagickFail)
1240 continue;
1241
1242 p=AcquireImagePixels(image,0,y,image->columns,1,exception);
1243 q=SetImagePixelsEx(flop_image,0,y,flop_image->columns,1,exception);
1244 if ((p == (PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
1245 thread_status=MagickFail;
1246
1247 if (thread_status != MagickFail)
1248 {
1249 indexes=AccessImmutableIndexes(image);
1250 flop_indexes=AccessMutableIndexes(flop_image);
1251 q+=flop_image->columns;
1252 for (x=0; x < (long) flop_image->columns; x++)
1253 {
1254 if ((indexes != (const IndexPacket *) NULL) &&
1255 (flop_indexes != (IndexPacket *) NULL))
1256 flop_indexes[flop_image->columns-x-1]=indexes[x];
1257 q--;
1258 *q=(*p);
1259 p++;
1260 }
1261 if (!SyncImagePixelsEx(flop_image,exception))
1262 thread_status=MagickFail;
1263 }
1264 #if defined(HAVE_OPENMP) && !defined(DisableSlowOpenMP)
1265 # pragma omp atomic
1266 #endif
1267 row_count++;
1268
1269 if (monitor_active)
1270 {
1271 unsigned long
1272 thread_row_count;
1273
1274 #if defined(HAVE_OPENMP)
1275 # pragma omp flush (row_count)
1276 #endif
1277 thread_row_count=row_count;
1278 if (QuantumTick(thread_row_count,flop_image->rows))
1279 if (!MagickMonitorFormatted(thread_row_count,flop_image->rows,exception,
1280 FlopImageText,image->filename))
1281 thread_status=MagickFail;
1282 }
1283
1284 if (thread_status == MagickFail)
1285 {
1286 status=MagickFail;
1287 #if defined(HAVE_OPENMP) && !defined(DisableSlowOpenMP)
1288 # pragma omp flush (status)
1289 #endif
1290 }
1291 }
1292 if (row_count < flop_image->rows)
1293 {
1294 DestroyImage(flop_image);
1295 return((Image *) NULL);
1296 }
1297 flop_image->is_grayscale=image->is_grayscale;
1298 return(flop_image);
1299 }
1300
1301 /*
1302 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1303 % %
1304 % %
1305 % %
1306 + G e t I m a g e M o s a i c D i m e n s i o n s %
1307 % %
1308 % %
1309 % %
1310 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1311 %
1312 % Method GetImageMosaicDimensions returns the bounding region of the canvas
1313 % which supports the images in the list as would be returned by
1314 % MosaicImages(). The bounding region is computed based on the image size
1315 % and page offsets of each image in the image list.
1316 %
1317 % The format of the GetImageMosaicDimensions method is:
1318 %
1319 % RectangleInfo GetImageMosaicDimensions(const Image *image)
1320 %
1321 % A description of each parameter follows:
1322 %
1323 % o bounds: Method GetImageMosaicDimensions returns the bounding box of
1324 % the image canvas.
1325 %
1326 % o image: The image.
1327 %
1328 % o exception: Return any errors or warnings in this structure.
1329 %
1330 %
1331 */
1332
1333 static RectangleInfo GetImageMosaicDimensions(const Image *image) MAGICK_FUNC_PURE;
1334
GetImageMosaicDimensions(const Image * image)1335 static RectangleInfo GetImageMosaicDimensions(const Image *image)
1336 {
1337 RectangleInfo
1338 page;
1339
1340 register const Image
1341 *next;
1342
1343 page.width=image->columns;
1344 page.height=image->rows;
1345 page.x=0;
1346 page.y=0;
1347 for (next=image; next != (Image *) NULL; next=next->next)
1348 {
1349 page.x=next->page.x;
1350 page.y=next->page.y;
1351 /*
1352 Without casts, unsigned underflow can occur here if page offset
1353 is negative and has greater magnitude than image size.
1354 */
1355 if (((long) next->columns+page.x) > (long) page.width)
1356 page.width=(long) next->columns+page.x;
1357 if (next->page.width > page.width)
1358 page.width=next->page.width;
1359 if (((long) next->rows+page.y) > (long) page.height)
1360 page.height=(long) next->rows+page.y;
1361 if (next->page.height > page.height)
1362 page.height=next->page.height;
1363 }
1364
1365 return page;
1366 }
1367
1368 /*
1369 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1370 % %
1371 % %
1372 % %
1373 % M o s a i c I m a g e s %
1374 % %
1375 % %
1376 % %
1377 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1378 %
1379 % MosaicImages() inlays an image sequence to form a single coherent picture.
1380 % It returns a single image with each image in the sequence composited at
1381 % the location defined by the page member of the image structure.
1382 %
1383 % The format of the MosaicImage method is:
1384 %
1385 % Image *MosaicImages(const Image *image,ExceptionInfo *exception)
1386 %
1387 % A description of each parameter follows:
1388 %
1389 % o image: The image.
1390 %
1391 % o exception: Return any errors or warnings in this structure.
1392 %
1393 %
1394 */
MosaicImages(const Image * image,ExceptionInfo * exception)1395 MagickExport Image *MosaicImages(const Image *image,ExceptionInfo *exception)
1396 {
1397 #define MosaicImageText "[%s] Create mosaic..."
1398
1399 Image
1400 *mosaic_image;
1401
1402 RectangleInfo
1403 page;
1404
1405 register const Image
1406 *next;
1407
1408 unsigned int
1409 scene;
1410
1411 MagickBool
1412 matte;
1413
1414 MagickPassFail
1415 status;
1416
1417 size_t
1418 image_list_length;
1419
1420 assert(image != (Image *) NULL);
1421 assert(image->signature == MagickSignature);
1422 assert(exception != (ExceptionInfo *) NULL);
1423 assert(exception->signature == MagickSignature);
1424
1425 image_list_length=GetImageListLength(image);
1426
1427 /*
1428 Determine mosaic bounding box.
1429 */
1430 page=GetImageMosaicDimensions(image);
1431
1432 /*
1433 Allocate canvas image.
1434 */
1435 mosaic_image=AllocateImage((ImageInfo *) NULL);
1436 if (mosaic_image == (Image *) NULL)
1437 return((Image *) NULL);
1438 mosaic_image->columns=page.width;
1439 mosaic_image->rows=page.height;
1440
1441 /*
1442 Canvas image supports transparency if any subordinate image uses
1443 transparency.
1444 */
1445 matte=MagickTrue;
1446 for (next=image; next != (Image *) NULL; next=next->next)
1447 matte &= next->matte;
1448 mosaic_image->matte=matte;
1449
1450 /*
1451 Canvas color is copied from background color of first image in
1452 list. Default canvas color is 'white' but opaque 'black' or
1453 'transparent' is often best for composition.
1454 */
1455 mosaic_image->background_color=image->background_color;
1456 (void) SetImage(mosaic_image,OpaqueOpacity);
1457
1458 /*
1459 Composite mosaic.
1460 */
1461 scene=0;
1462 for (next=image; next != (Image *) NULL; next=next->next)
1463 {
1464 (void) CompositeImage(mosaic_image,next->compose,next,next->page.x,
1465 next->page.y);
1466 status=MagickMonitorFormatted(scene++,image_list_length,
1467 exception,MosaicImageText,image->filename);
1468 if (status == MagickFail)
1469 break;
1470 }
1471 return(mosaic_image);
1472 }
1473
1474 /*
1475 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1476 % %
1477 % %
1478 % %
1479 % R o l l I m a g e %
1480 % %
1481 % %
1482 % %
1483 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1484 %
1485 % RollImage() offsets an image as defined by x_offset and y_offset.
1486 %
1487 % The format of the RollImage method is:
1488 %
1489 % Image *RollImage(const Image *image,const long x_offset,
1490 % const long y_offset,ExceptionInfo *exception)
1491 %
1492 % A description of each parameter follows:
1493 %
1494 % o image: The image.
1495 %
1496 % o x_offset: The number of columns to roll in the horizontal direction.
1497 %
1498 % o y_offset: The number of rows to roll in the vertical direction.
1499 %
1500 % o exception: Return any errors or warnings in this structure.
1501 %
1502 %
1503 */
RollImage(const Image * image,const long x_offset,const long y_offset,ExceptionInfo * exception)1504 MagickExport Image *RollImage(const Image *image,const long x_offset,
1505 const long y_offset,ExceptionInfo *exception)
1506 {
1507 Image
1508 *roll_image;
1509
1510 RectangleInfo
1511 offset;
1512
1513 /*
1514 Initialize roll image attributes.
1515 */
1516 assert(image != (const Image *) NULL);
1517 assert(image->signature == MagickSignature);
1518 assert(exception != (ExceptionInfo *) NULL);
1519 assert(exception->signature == MagickSignature);
1520 roll_image=CloneImage(image,image->columns,image->rows,True,exception);
1521 if (roll_image == (Image *) NULL)
1522 return((Image *) NULL);
1523 /*
1524 Roll image.
1525 */
1526 offset.x=x_offset;
1527 offset.y=y_offset;
1528 while (offset.x < 0)
1529 offset.x+=image->columns;
1530 while (offset.x >= (long) image->columns)
1531 offset.x-=image->columns;
1532 while (offset.y < 0)
1533 offset.y+=image->rows;
1534 while (offset.y >= (long) image->rows)
1535 offset.y-=image->rows;
1536
1537 /* Top left quadrant */
1538 (void) CompositeImageRegion(CopyCompositeOp,0,offset.x,offset.y,image,
1539 image->columns-offset.x,image->rows-offset.y,
1540 roll_image,0,0,exception);
1541
1542 /* Top right quadrant */
1543 (void) CompositeImageRegion(CopyCompositeOp,0,image->columns-offset.x,offset.y,image,
1544 0,image->rows-offset.y,
1545 roll_image,offset.x,0,exception);
1546
1547 /* Bottom left quadrant */
1548 (void) CompositeImageRegion(CopyCompositeOp,0,offset.x,image->rows-offset.y,image,
1549 image->columns-offset.x,0,
1550 roll_image,0,offset.y,exception);
1551
1552 /* Bottom right quadrant */
1553 (void) CompositeImageRegion(CopyCompositeOp,0,image->columns-offset.x,image->rows-offset.y,image,
1554 0,0,
1555 roll_image,offset.x,offset.y,exception);
1556
1557 roll_image->is_grayscale=image->is_grayscale;
1558 return(roll_image);
1559 }
1560
1561 /*
1562 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1563 % %
1564 % %
1565 % %
1566 % S h a v e I m a g e %
1567 % %
1568 % %
1569 % %
1570 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1571 %
1572 % Method ShaveImage shaves pixels from the image edges. It allocates the
1573 % memory necessary for the new Image structure and returns a pointer to the
1574 % new image.
1575 %
1576 % The format of the ShaveImage method is:
1577 %
1578 % Image *ShaveImage(const Image *image,const RectangleInfo *shave_info,
1579 % ExceptionInfo *exception)
1580 %
1581 % A description of each parameter follows:
1582 %
1583 % o shave_image: Method ShaveImage returns a pointer to the shaved
1584 % image. A null image is returned if there is a memory shortage or
1585 % if the image width or height is zero.
1586 %
1587 % o image: The image.
1588 %
1589 % o shave_info: Specifies a pointer to a RectangleInfo which defines the
1590 % region of the image to shave.
1591 %
1592 % o exception: Return any errors or warnings in this structure.
1593 %
1594 %
1595 */
ShaveImage(const Image * image,const RectangleInfo * shave_info,ExceptionInfo * exception)1596 MagickExport Image *ShaveImage(const Image *image,
1597 const RectangleInfo *shave_info,ExceptionInfo *exception)
1598 {
1599 RectangleInfo
1600 geometry;
1601
1602 if (((2*shave_info->width) >= image->columns) ||
1603 ((2*shave_info->height) >= image->rows))
1604 ThrowImageException(OptionError,GeometryDoesNotContainImage,
1605 MagickMsg(ResourceLimitError,UnableToShaveImage));
1606 SetGeometry(image,&geometry);
1607 geometry.width-=2*shave_info->width;
1608 geometry.height-=2*shave_info->height;
1609 geometry.x=(long) shave_info->width;
1610 geometry.y=(long) shave_info->height;
1611 return(CropImage(image,&geometry,exception));
1612 }
1613
1614 /*
1615 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1616 % %
1617 % %
1618 % %
1619 % T r a n s f o r m I m a g e %
1620 % %
1621 % %
1622 % %
1623 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1624 %
1625 % TransformImage() is a convenience method that behaves like ResizeImage() or
1626 % CropImage() but accepts scaling and/or cropping information as a region
1627 % geometry specification. If the operation fails, the original image handle
1628 % is returned.
1629 %
1630 % The format of the TransformImage method is:
1631 %
1632 % MagickPassFail TransformImage(Image **image,const char *crop_geometry,
1633 % const char *image_geometry)
1634 %
1635 % A description of each parameter follows:
1636 %
1637 % o image: The image to transform. The resulting transformed image is
1638 % returned via this parameter.
1639 %
1640 % o crop_geometry: A crop geometry string. This geometry defines a
1641 % subregion of the image to crop.
1642 %
1643 % o image_geometry: An image geometry string. This geometry defines the
1644 % final size of the image.
1645 %
1646 %
1647 */
TransformImage(Image ** image,const char * crop_geometry,const char * image_geometry)1648 MagickExport MagickPassFail TransformImage(Image **image,const char *crop_geometry,
1649 const char *image_geometry)
1650 {
1651 Image
1652 *previous,
1653 *resize_image,
1654 *transform_image;
1655
1656 RectangleInfo
1657 geometry;
1658
1659 int
1660 flags;
1661
1662 MagickPassFail
1663 status = MagickPass;
1664
1665 assert(image != (Image **) NULL);
1666 assert((*image)->signature == MagickSignature);
1667 transform_image=(*image);
1668 if (crop_geometry != (const char *) NULL)
1669 {
1670 Image
1671 *crop_image;
1672
1673 /*
1674 Crop image to a user specified size.
1675 */
1676 crop_image=(Image *) NULL;
1677 flags=GetImageGeometry(transform_image,crop_geometry,False,&geometry);
1678 if ((geometry.width == 0) || (geometry.height == 0) ||
1679 ((flags & XValue) != 0) || ((flags & YValue) != 0) ||
1680 (flags & PercentValue))
1681 {
1682 crop_image=CropImage(transform_image,&geometry,&(*image)->exception);
1683 if (crop_image == (Image *) NULL)
1684 status = MagickFail;
1685 }
1686 else
1687 if ((transform_image->columns > geometry.width) ||
1688 (transform_image->rows > geometry.height))
1689 {
1690 Image
1691 *next;
1692
1693 long
1694 x,
1695 y;
1696
1697 unsigned long
1698 height,
1699 width;
1700
1701 /*
1702 Crop repeatedly to create uniform subimages.
1703 */
1704 width=geometry.width;
1705 height=geometry.height;
1706 next=(Image *) NULL;
1707 for (y=0; y < (long) transform_image->rows; y+=height)
1708 {
1709 for (x=0; x < (long) transform_image->columns; x+=width)
1710 {
1711 geometry.width=width;
1712 geometry.height=height;
1713 geometry.x=x;
1714 geometry.y=y;
1715 next=CropImage(transform_image,&geometry,&(*image)->exception);
1716 if (next == (Image *) NULL)
1717 break;
1718 if (crop_image == (Image *) NULL)
1719 crop_image=next;
1720 else
1721 {
1722 next->previous=crop_image;
1723 crop_image->next=next;
1724 crop_image=crop_image->next;
1725 }
1726 }
1727 if (next == (Image *) NULL)
1728 {
1729 status=MagickFail;
1730 break;
1731 }
1732 }
1733 }
1734 if (crop_image != (Image *) NULL)
1735 {
1736 previous=transform_image->previous;
1737 crop_image->next=transform_image->next;
1738 DestroyImage(transform_image);
1739 transform_image=(Image *) NULL;
1740 while (crop_image->previous != (Image *) NULL)
1741 crop_image=crop_image->previous;
1742 crop_image->previous=previous;
1743 transform_image=crop_image;
1744 }
1745 *image=transform_image;
1746 }
1747 if (image_geometry == (const char *) NULL)
1748 return status;
1749
1750 /*
1751 Scale image to a user specified size.
1752 */
1753 SetGeometry(transform_image,&geometry);
1754 flags=GetMagickGeometry(image_geometry,&geometry.x,&geometry.y,
1755 &geometry.width,&geometry.height);
1756 if ((transform_image->columns == geometry.width) &&
1757 (transform_image->rows == geometry.height))
1758 return status;
1759
1760 /*
1761 Resize image.
1762 */
1763 resize_image=ZoomImage(transform_image,geometry.width,geometry.height,
1764 &(*image)->exception);
1765 if (resize_image == (Image *) NULL)
1766 {
1767 status=MagickFail;
1768 return status;
1769 }
1770
1771 ReplaceImageInList(&transform_image,resize_image);
1772 *image=transform_image;
1773 return status;
1774 }
1775