MagickCore 7.1.1
Convert, Edit, Or Compose Bitmap Images
Loading...
Searching...
No Matches
transform.c
1/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% TTTTT RRRR AAA N N SSSSS FFFFF OOO RRRR M M %
7% T R R A A NN N SS F O O R R MM MM %
8% T RRRR AAAAA N N N SSS FFF O O RRRR M M M %
9% T R R A A N NN SS F O O R R M M %
10% T R R A A N N SSSSS F OOO R R M M %
11% %
12% %
13% MagickCore Image Transform Methods %
14% %
15% Software Design %
16% Cristy %
17% July 1992 %
18% %
19% %
20% Copyright @ 1999 ImageMagick Studio LLC, a non-profit organization %
21% dedicated to making software imaging solutions freely available. %
22% %
23% You may not use this file except in compliance with the License. You may %
24% obtain a copy of the License at %
25% %
26% https://imagemagick.org/script/license.php %
27% %
28% Unless required by applicable law or agreed to in writing, software %
29% distributed under the License is distributed on an "AS IS" BASIS, %
30% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31% See the License for the specific language governing permissions and %
32% limitations under the License. %
33% %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36%
37*/
38
39/*
40 Include declarations.
41*/
42#include "MagickCore/studio.h"
43#include "MagickCore/attribute.h"
44#include "MagickCore/artifact.h"
45#include "MagickCore/cache.h"
46#include "MagickCore/cache-view.h"
47#include "MagickCore/color.h"
48#include "MagickCore/color-private.h"
49#include "MagickCore/colorspace-private.h"
50#include "MagickCore/composite.h"
51#include "MagickCore/composite-private.h"
52#include "MagickCore/distort.h"
53#include "MagickCore/draw.h"
54#include "MagickCore/effect.h"
55#include "MagickCore/exception.h"
56#include "MagickCore/exception-private.h"
57#include "MagickCore/geometry.h"
58#include "MagickCore/image.h"
59#include "MagickCore/memory_.h"
60#include "MagickCore/layer.h"
61#include "MagickCore/list.h"
62#include "MagickCore/monitor.h"
63#include "MagickCore/monitor-private.h"
64#include "MagickCore/pixel-accessor.h"
65#include "MagickCore/profile-private.h"
66#include "MagickCore/property.h"
67#include "MagickCore/resource_.h"
68#include "MagickCore/resize.h"
69#include "MagickCore/statistic.h"
70#include "MagickCore/string_.h"
71#include "MagickCore/thread-private.h"
72#include "MagickCore/transform.h"
73#include "MagickCore/transform-private.h"
74
75/*
76%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
77% %
78% %
79% %
80% A u t o O r i e n t I m a g e %
81% %
82% %
83% %
84%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
85%
86% AutoOrientImage() adjusts an image so that its orientation is suitable for
87% viewing (i.e. top-left orientation).
88%
89% The format of the AutoOrientImage method is:
90%
91% Image *AutoOrientImage(const Image *image,
92% const OrientationType orientation,ExceptionInfo *exception)
93%
94% A description of each parameter follows:
95%
96% o image: The image.
97%
98% o orientation: Current image orientation.
99%
100% o exception: Return any errors or warnings in this structure.
101%
102*/
103MagickExport Image *AutoOrientImage(const Image *image,
104 const OrientationType orientation,ExceptionInfo *exception)
105{
106 Image
107 *orient_image;
108
109 assert(image != (const Image *) NULL);
110 assert(image->signature == MagickCoreSignature);
111 assert(exception != (ExceptionInfo *) NULL);
112 assert(exception->signature == MagickCoreSignature);
113 orient_image=(Image *) NULL;
114 switch (orientation)
115 {
116 case UndefinedOrientation:
117 case TopLeftOrientation:
118 default:
119 {
120 orient_image=CloneImage(image,0,0,MagickTrue,exception);
121 break;
122 }
123 case TopRightOrientation:
124 {
125 orient_image=FlopImage(image,exception);
126 break;
127 }
128 case BottomRightOrientation:
129 {
130 orient_image=RotateImage(image,180.0,exception);
131 break;
132 }
133 case BottomLeftOrientation:
134 {
135 orient_image=FlipImage(image,exception);
136 break;
137 }
138 case LeftTopOrientation:
139 {
140 orient_image=TransposeImage(image,exception);
141 break;
142 }
143 case RightTopOrientation:
144 {
145 orient_image=RotateImage(image,90.0,exception);
146 break;
147 }
148 case RightBottomOrientation:
149 {
150 orient_image=TransverseImage(image,exception);
151 break;
152 }
153 case LeftBottomOrientation:
154 {
155 orient_image=RotateImage(image,270.0,exception);
156 break;
157 }
158 }
159 if (orient_image != (Image *) NULL)
160 orient_image->orientation=TopLeftOrientation;
161 return(orient_image);
162}
163
164/*
165%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
166% %
167% %
168% %
169% C h o p I m a g e %
170% %
171% %
172% %
173%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
174%
175% ChopImage() removes a region of an image and collapses the image to occupy
176% the removed portion.
177%
178% The format of the ChopImage method is:
179%
180% Image *ChopImage(const Image *image,const RectangleInfo *chop_info)
181% ExceptionInfo *exception)
182%
183% A description of each parameter follows:
184%
185% o image: the image.
186%
187% o chop_info: Define the region of the image to chop.
188%
189% o exception: return any errors or warnings in this structure.
190%
191*/
192MagickExport Image *ChopImage(const Image *image,const RectangleInfo *chop_info,
193 ExceptionInfo *exception)
194{
195#define ChopImageTag "Chop/Image"
196
198 *chop_view,
199 *image_view;
200
201 Image
202 *chop_image;
203
204 MagickBooleanType
205 status;
206
207 MagickOffsetType
208 progress;
209
211 extent;
212
213 ssize_t
214 y;
215
216 /*
217 Check chop geometry.
218 */
219 assert(image != (const Image *) NULL);
220 assert(image->signature == MagickCoreSignature);
221 assert(exception != (ExceptionInfo *) NULL);
222 assert(exception->signature == MagickCoreSignature);
223 assert(chop_info != (RectangleInfo *) NULL);
224 if (IsEventLogging() != MagickFalse)
225 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
226 if (((chop_info->x+(ssize_t) chop_info->width) < 0) ||
227 ((chop_info->y+(ssize_t) chop_info->height) < 0) ||
228 (chop_info->x > (ssize_t) image->columns) ||
229 (chop_info->y > (ssize_t) image->rows))
230 ThrowImageException(OptionWarning,"GeometryDoesNotContainImage");
231 extent=(*chop_info);
232 if ((extent.x+(ssize_t) extent.width) > (ssize_t) image->columns)
233 extent.width=(size_t) ((ssize_t) image->columns-extent.x);
234 if ((extent.y+(ssize_t) extent.height) > (ssize_t) image->rows)
235 extent.height=(size_t) ((ssize_t) image->rows-extent.y);
236 if (extent.x < 0)
237 {
238 extent.width-=(size_t) (-extent.x);
239 extent.x=0;
240 }
241 if (extent.y < 0)
242 {
243 extent.height-=(size_t) (-extent.y);
244 extent.y=0;
245 }
246 if ((extent.width >= image->columns) || (extent.height >= image->rows))
247 ThrowImageException(OptionWarning,"GeometryDoesNotContainImage");
248 chop_image=CloneImage(image,image->columns-extent.width,image->rows-
249 extent.height,MagickTrue,exception);
250 if (chop_image == (Image *) NULL)
251 return((Image *) NULL);
252 /*
253 Extract chop image.
254 */
255 status=MagickTrue;
256 progress=0;
257 image_view=AcquireVirtualCacheView(image,exception);
258 chop_view=AcquireAuthenticCacheView(chop_image,exception);
259#if defined(MAGICKCORE_OPENMP_SUPPORT)
260 #pragma omp parallel for schedule(static) shared(status) \
261 magick_number_threads(image,chop_image,(size_t) extent.y,2)
262#endif
263 for (y=0; y < (ssize_t) extent.y; y++)
264 {
265 const Quantum
266 *magick_restrict p;
267
268 ssize_t
269 x;
270
271 Quantum
272 *magick_restrict q;
273
274 if (status == MagickFalse)
275 continue;
276 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
277 q=QueueCacheViewAuthenticPixels(chop_view,0,y,chop_image->columns,1,
278 exception);
279 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
280 {
281 status=MagickFalse;
282 continue;
283 }
284 for (x=0; x < (ssize_t) image->columns; x++)
285 {
286 if ((x < extent.x) || (x >= (extent.x+(ssize_t) extent.width)))
287 {
288 ssize_t
289 i;
290
291 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
292 {
293 PixelChannel channel = GetPixelChannelChannel(image,i);
294 PixelTrait traits = GetPixelChannelTraits(image,channel);
295 PixelTrait chop_traits=GetPixelChannelTraits(chop_image,channel);
296 if ((traits == UndefinedPixelTrait) ||
297 (chop_traits == UndefinedPixelTrait))
298 continue;
299 SetPixelChannel(chop_image,channel,p[i],q);
300 }
301 q+=(ptrdiff_t) GetPixelChannels(chop_image);
302 }
303 p+=(ptrdiff_t) GetPixelChannels(image);
304 }
305 if (SyncCacheViewAuthenticPixels(chop_view,exception) == MagickFalse)
306 status=MagickFalse;
307 if (image->progress_monitor != (MagickProgressMonitor) NULL)
308 {
309 MagickBooleanType
310 proceed;
311
312#if defined(MAGICKCORE_OPENMP_SUPPORT)
313 #pragma omp atomic
314#endif
315 progress++;
316 proceed=SetImageProgress(image,ChopImageTag,progress,image->rows);
317 if (proceed == MagickFalse)
318 status=MagickFalse;
319 }
320 }
321 /*
322 Extract chop image.
323 */
324#if defined(MAGICKCORE_OPENMP_SUPPORT)
325 #pragma omp parallel for schedule(static) shared(progress,status) \
326 magick_number_threads(image,chop_image,image->rows-((size_t) extent.y+extent.height),2)
327#endif
328 for (y=0; y < (ssize_t) (image->rows-((size_t) extent.y+extent.height)); y++)
329 {
330 const Quantum
331 *magick_restrict p;
332
333 ssize_t
334 x;
335
336 Quantum
337 *magick_restrict q;
338
339 if (status == MagickFalse)
340 continue;
341 p=GetCacheViewVirtualPixels(image_view,0,extent.y+(ssize_t) extent.height+y,
342 image->columns,1,exception);
343 q=QueueCacheViewAuthenticPixels(chop_view,0,extent.y+y,chop_image->columns,
344 1,exception);
345 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
346 {
347 status=MagickFalse;
348 continue;
349 }
350 for (x=0; x < (ssize_t) image->columns; x++)
351 {
352 if ((x < extent.x) || (x >= (extent.x+(ssize_t) extent.width)))
353 {
354 ssize_t
355 i;
356
357 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
358 {
359 PixelChannel channel = GetPixelChannelChannel(image,i);
360 PixelTrait traits = GetPixelChannelTraits(image,channel);
361 PixelTrait chop_traits=GetPixelChannelTraits(chop_image,channel);
362 if ((traits == UndefinedPixelTrait) ||
363 (chop_traits == UndefinedPixelTrait))
364 continue;
365 SetPixelChannel(chop_image,channel,p[i],q);
366 }
367 q+=(ptrdiff_t) GetPixelChannels(chop_image);
368 }
369 p+=(ptrdiff_t) GetPixelChannels(image);
370 }
371 if (SyncCacheViewAuthenticPixels(chop_view,exception) == MagickFalse)
372 status=MagickFalse;
373 if (image->progress_monitor != (MagickProgressMonitor) NULL)
374 {
375 MagickBooleanType
376 proceed;
377
378#if defined(MAGICKCORE_OPENMP_SUPPORT)
379 #pragma omp atomic
380#endif
381 progress++;
382 proceed=SetImageProgress(image,ChopImageTag,progress,image->rows);
383 if (proceed == MagickFalse)
384 status=MagickFalse;
385 }
386 }
387 chop_view=DestroyCacheView(chop_view);
388 image_view=DestroyCacheView(image_view);
389 chop_image->type=image->type;
390 if (status == MagickFalse)
391 chop_image=DestroyImage(chop_image);
392 return(chop_image);
393}
394
395/*
396%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
397% %
398% %
399% %
400+ C o n s o l i d a t e C M Y K I m a g e %
401% %
402% %
403% %
404%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
405%
406% ConsolidateCMYKImage() consolidates separate C, M, Y, and K planes into a
407% single image.
408%
409% The format of the ConsolidateCMYKImage method is:
410%
411% Image *ConsolidateCMYKImage(const Image *image,ExceptionInfo *exception)
412%
413% A description of each parameter follows:
414%
415% o image: the image sequence.
416%
417% o exception: return any errors or warnings in this structure.
418%
419*/
420MagickExport Image *ConsolidateCMYKImages(const Image *images,
421 ExceptionInfo *exception)
422{
424 *cmyk_view,
425 *image_view;
426
427 Image
428 *cmyk_image,
429 *cmyk_images;
430
431 ssize_t
432 j;
433
434 ssize_t
435 y;
436
437 /*
438 Consolidate separate C, M, Y, and K planes into a single image.
439 */
440 assert(images != (Image *) NULL);
441 assert(images->signature == MagickCoreSignature);
442 assert(exception != (ExceptionInfo *) NULL);
443 assert(exception->signature == MagickCoreSignature);
444 if (IsEventLogging() != MagickFalse)
445 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
446 cmyk_images=NewImageList();
447 for (j=0; j < (ssize_t) GetImageListLength(images); j+=4)
448 {
449 ssize_t
450 i;
451
452 assert(images != (Image *) NULL);
453 cmyk_image=CloneImage(images,0,0,MagickTrue,
454 exception);
455 if (cmyk_image == (Image *) NULL)
456 break;
457 if (SetImageStorageClass(cmyk_image,DirectClass,exception) == MagickFalse)
458 break;
459 (void) SetImageColorspace(cmyk_image,CMYKColorspace,exception);
460 for (i=0; i < 4; i++)
461 {
462 image_view=AcquireVirtualCacheView(images,exception);
463 cmyk_view=AcquireAuthenticCacheView(cmyk_image,exception);
464 for (y=0; y < (ssize_t) images->rows; y++)
465 {
466 const Quantum
467 *magick_restrict p;
468
469 ssize_t
470 x;
471
472 Quantum
473 *magick_restrict q;
474
475 p=GetCacheViewVirtualPixels(image_view,0,y,images->columns,1,exception);
476 q=QueueCacheViewAuthenticPixels(cmyk_view,0,y,cmyk_image->columns,1,
477 exception);
478 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
479 break;
480 for (x=0; x < (ssize_t) images->columns; x++)
481 {
482 Quantum
483 pixel;
484
485 pixel=ClampToQuantum((double) QuantumRange-
486 GetPixelIntensity(images,p));
487 switch (i)
488 {
489 case 0: SetPixelCyan(cmyk_image,pixel,q); break;
490 case 1: SetPixelMagenta(cmyk_image,pixel,q); break;
491 case 2: SetPixelYellow(cmyk_image,pixel,q); break;
492 case 3: SetPixelBlack(cmyk_image,pixel,q); break;
493 default: break;
494 }
495 p+=(ptrdiff_t) GetPixelChannels(images);
496 q+=(ptrdiff_t) GetPixelChannels(cmyk_image);
497 }
498 if (SyncCacheViewAuthenticPixels(cmyk_view,exception) == MagickFalse)
499 break;
500 }
501 cmyk_view=DestroyCacheView(cmyk_view);
502 image_view=DestroyCacheView(image_view);
503 images=GetNextImageInList(images);
504 if (images == (Image *) NULL)
505 break;
506 }
507 AppendImageToList(&cmyk_images,cmyk_image);
508 }
509 return(cmyk_images);
510}
511
512/*
513%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
514% %
515% %
516% %
517% C r o p I m a g e %
518% %
519% %
520% %
521%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
522%
523% CropImage() extracts a region of the image starting at the offset defined
524% by geometry. Region must be fully defined, and no special handling of
525% geometry flags is performed.
526%
527% The format of the CropImage method is:
528%
529% Image *CropImage(const Image *image,const RectangleInfo *geometry,
530% ExceptionInfo *exception)
531%
532% A description of each parameter follows:
533%
534% o image: the image.
535%
536% o geometry: Define the region of the image to crop with members
537% x, y, width, and height.
538%
539% o exception: return any errors or warnings in this structure.
540%
541*/
542MagickExport Image *CropImage(const Image *image,const RectangleInfo *geometry,
543 ExceptionInfo *exception)
544{
545#define CropImageTag "Crop/Image"
546
548 *crop_view,
549 *image_view;
550
551 Image
552 *crop_image;
553
554 MagickBooleanType
555 status;
556
557 MagickOffsetType
558 progress;
559
561 offset;
562
564 bounding_box,
565 page;
566
567 ssize_t
568 y;
569
570 /*
571 Check crop geometry.
572 */
573 assert(image != (const Image *) NULL);
574 assert(image->signature == MagickCoreSignature);
575 assert(geometry != (const RectangleInfo *) NULL);
576 assert(exception != (ExceptionInfo *) NULL);
577 assert(exception->signature == MagickCoreSignature);
578 if (IsEventLogging() != MagickFalse)
579 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
580 bounding_box=image->page;
581 if ((bounding_box.width == 0) || (bounding_box.height == 0))
582 {
583 bounding_box.width=image->columns;
584 bounding_box.height=image->rows;
585 }
586 page=(*geometry);
587 if (page.width == 0)
588 page.width=bounding_box.width;
589 if (page.height == 0)
590 page.height=bounding_box.height;
591 if ((((double) bounding_box.x-page.x) >= (double) page.width) ||
592 (((double) bounding_box.y-page.y) >= (double) page.height) ||
593 (((double) page.x-bounding_box.x) > (double) image->columns) ||
594 (((double) page.y-bounding_box.y) > (double) image->rows))
595 {
596 /*
597 Crop is not within virtual canvas, return 1 pixel transparent image.
598 */
599 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
600 "GeometryDoesNotContainImage","(\"%.20gx%.20g%+.20g%+.20g\") `%s'",
601 (double) geometry->width,(double) geometry->height,
602 (double) geometry->x,(double) geometry->y,image->filename);
603 crop_image=CloneImage(image,1,1,MagickTrue,exception);
604 if (crop_image == (Image *) NULL)
605 return((Image *) NULL);
606 crop_image->background_color.alpha_trait=BlendPixelTrait;
607 crop_image->background_color.alpha=(MagickRealType) TransparentAlpha;
608 (void) SetImageBackgroundColor(crop_image,exception);
609 crop_image->page=bounding_box;
610 crop_image->page.x=(-1);
611 crop_image->page.y=(-1);
612 if (crop_image->dispose == BackgroundDispose)
613 crop_image->dispose=NoneDispose;
614 return(crop_image);
615 }
616 if ((page.x < 0) && (bounding_box.x >= 0))
617 {
618 page.width=CastDoubleToUnsigned((double) page.width+page.x-
619 bounding_box.x);
620 page.x=0;
621 }
622 else
623 {
624 page.width=CastDoubleToUnsigned((double) page.width-(bounding_box.x-
625 page.x));
626 page.x-=bounding_box.x;
627 if (page.x < 0)
628 page.x=0;
629 }
630 if ((page.y < 0) && (bounding_box.y >= 0))
631 {
632 page.height=CastDoubleToUnsigned((double) page.height+page.y-
633 bounding_box.y);
634 page.y=0;
635 }
636 else
637 {
638 page.height=CastDoubleToUnsigned((double) page.height-(bounding_box.y-
639 page.y));
640 page.y-=bounding_box.y;
641 if (page.y < 0)
642 page.y=0;
643 }
644 if ((page.x+(ssize_t) page.width) > (ssize_t) image->columns)
645 page.width=(size_t) ((ssize_t) image->columns-page.x);
646 if ((geometry->width != 0) && (page.width > geometry->width))
647 page.width=geometry->width;
648 if ((page.y+(ssize_t) page.height) > (ssize_t) image->rows)
649 page.height=(size_t) ((ssize_t) image->rows-page.y);
650 if ((geometry->height != 0) && (page.height > geometry->height))
651 page.height=geometry->height;
652 bounding_box.x+=page.x;
653 bounding_box.y+=page.y;
654 if ((page.width == 0) || (page.height == 0))
655 {
656 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
657 "GeometryDoesNotContainImage","`%s'",image->filename);
658 return((Image *) NULL);
659 }
660 /*
661 Initialize crop image attributes.
662 */
663 crop_image=CloneImage(image,page.width,page.height,MagickTrue,exception);
664 if (crop_image == (Image *) NULL)
665 return((Image *) NULL);
666 crop_image->page.width=image->page.width;
667 crop_image->page.height=image->page.height;
668 offset.x=bounding_box.x+(ssize_t) bounding_box.width;
669 offset.y=bounding_box.y+(ssize_t) bounding_box.height;
670 if ((offset.x > (ssize_t) image->page.width) ||
671 (offset.y > (ssize_t) image->page.height))
672 {
673 crop_image->page.width=bounding_box.width;
674 crop_image->page.height=bounding_box.height;
675 }
676 crop_image->page.x=bounding_box.x;
677 crop_image->page.y=bounding_box.y;
678 /*
679 Crop image.
680 */
681 status=MagickTrue;
682 progress=0;
683 image_view=AcquireVirtualCacheView(image,exception);
684 crop_view=AcquireAuthenticCacheView(crop_image,exception);
685#if defined(MAGICKCORE_OPENMP_SUPPORT)
686 #pragma omp parallel for schedule(static) shared(status) \
687 magick_number_threads(image,crop_image,crop_image->rows,2)
688#endif
689 for (y=0; y < (ssize_t) crop_image->rows; y++)
690 {
691 const Quantum
692 *magick_restrict p;
693
694 Quantum
695 *magick_restrict q;
696
697 ssize_t
698 x;
699
700 if (status == MagickFalse)
701 continue;
702 p=GetCacheViewVirtualPixels(image_view,page.x,page.y+y,crop_image->columns,
703 1,exception);
704 q=QueueCacheViewAuthenticPixels(crop_view,0,y,crop_image->columns,1,
705 exception);
706 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
707 {
708 status=MagickFalse;
709 continue;
710 }
711 for (x=0; x < (ssize_t) crop_image->columns; x++)
712 {
713 ssize_t
714 i;
715
716 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
717 {
718 PixelChannel channel = GetPixelChannelChannel(image,i);
719 PixelTrait traits = GetPixelChannelTraits(image,channel);
720 PixelTrait crop_traits=GetPixelChannelTraits(crop_image,channel);
721 if ((traits == UndefinedPixelTrait) ||
722 (crop_traits == UndefinedPixelTrait))
723 continue;
724 SetPixelChannel(crop_image,channel,p[i],q);
725 }
726 p+=(ptrdiff_t) GetPixelChannels(image);
727 q+=(ptrdiff_t) GetPixelChannels(crop_image);
728 }
729 if (SyncCacheViewAuthenticPixels(crop_view,exception) == MagickFalse)
730 status=MagickFalse;
731 if (image->progress_monitor != (MagickProgressMonitor) NULL)
732 {
733 MagickBooleanType
734 proceed;
735
736#if defined(MAGICKCORE_OPENMP_SUPPORT)
737 #pragma omp atomic
738#endif
739 progress++;
740 proceed=SetImageProgress(image,CropImageTag,progress,image->rows);
741 if (proceed == MagickFalse)
742 status=MagickFalse;
743 }
744 }
745 crop_view=DestroyCacheView(crop_view);
746 image_view=DestroyCacheView(image_view);
747 crop_image->type=image->type;
748 if (status == MagickFalse)
749 crop_image=DestroyImage(crop_image);
750 return(crop_image);
751}
752
753/*
754%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
755% %
756% %
757% %
758% C r o p I m a g e T o T i l e s %
759% %
760% %
761% %
762%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
763%
764% CropImageToTiles() crops a single image, into a possible list of tiles.
765% This may include a single sub-region of the image. This basically applies
766% all the normal geometry flags for Crop.
767%
768% Image *CropImageToTiles(const Image *image,
769% const RectangleInfo *crop_geometry, ExceptionInfo *exception)
770%
771% A description of each parameter follows:
772%
773% o image: the image The transformed image is returned as this parameter.
774%
775% o crop_geometry: A crop geometry string.
776%
777% o exception: return any errors or warnings in this structure.
778%
779*/
780
781static inline ssize_t PixelRoundOffset(double x)
782{
783 /*
784 Round the fraction to nearest integer.
785 */
786 if ((x-floor(x)) < (ceil(x)-x))
787 return(CastDoubleToLong(floor(x)));
788 return(CastDoubleToLong(ceil(x)));
789}
790
791MagickExport Image *CropImageToTiles(const Image *image,
792 const char *crop_geometry,ExceptionInfo *exception)
793{
794 Image
795 *next,
796 *crop_image;
797
798 MagickStatusType
799 flags;
800
802 geometry;
803
804 assert(image != (Image *) NULL);
805 assert(image->signature == MagickCoreSignature);
806 if (IsEventLogging() != MagickFalse)
807 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
808 flags=ParseGravityGeometry(image,crop_geometry,&geometry,exception);
809 if ((flags & AreaValue) != 0)
810 {
812 delta,
813 offset;
814
816 crop;
817
818 size_t
819 height,
820 width;
821
822 /*
823 Crop into NxM tiles (@ flag).
824 */
825 crop_image=NewImageList();
826 width=image->columns;
827 height=image->rows;
828 if (geometry.width == 0)
829 geometry.width=1;
830 if (geometry.height == 0)
831 geometry.height=1;
832 if ((flags & AspectValue) == 0)
833 {
834 width=(size_t) ((ssize_t) width-(geometry.x < 0 ? -1 : 1)*geometry.x);
835 height=(size_t) ((ssize_t) height-(geometry.y < 0 ? -1 : 1)*
836 geometry.y);
837 }
838 else
839 {
840 width=(size_t) ((ssize_t) width+(geometry.x < 0 ? -1 : 1)*geometry.x);
841 height=(size_t) ((ssize_t) height+(geometry.y < 0 ? -1 : 1)*
842 geometry.y);
843 }
844 delta.x=(double) width/geometry.width;
845 delta.y=(double) height/geometry.height;
846 if (delta.x < 1.0)
847 delta.x=1.0;
848 if (delta.y < 1.0)
849 delta.y=1.0;
850 for (offset.y=0; offset.y < (double) height; )
851 {
852 if ((flags & AspectValue) == 0)
853 {
854 crop.y=PixelRoundOffset((double) (offset.y-
855 (geometry.y > 0 ? 0 : geometry.y)));
856 offset.y+=delta.y; /* increment now to find width */
857 crop.height=(size_t) PixelRoundOffset((double) (offset.y+
858 (geometry.y < 0 ? 0 : geometry.y)));
859 }
860 else
861 {
862 crop.y=PixelRoundOffset((double) (offset.y-
863 (geometry.y > 0 ? geometry.y : 0)));
864 offset.y+=delta.y; /* increment now to find width */
865 crop.height=(size_t) PixelRoundOffset((double)
866 (offset.y+(geometry.y < -1 ? geometry.y : 0)));
867 }
868 crop.height=(size_t) ((ssize_t) crop.height-crop.y);
869 crop.y+=image->page.y;
870 for (offset.x=0; offset.x < (double) width; )
871 {
872 if ((flags & AspectValue) == 0)
873 {
874 crop.x=PixelRoundOffset((double) (offset.x-
875 (geometry.x > 0 ? 0 : geometry.x)));
876 offset.x+=delta.x; /* increment now to find height */
877 crop.width=(size_t) PixelRoundOffset((double) (offset.x+
878 (geometry.x < 0 ? 0 : geometry.x)));
879 }
880 else
881 {
882 crop.x=PixelRoundOffset((double) (offset.x-
883 (geometry.x > 0 ? geometry.x : 0)));
884 offset.x+=delta.x; /* increment now to find height */
885 crop.width=(size_t) PixelRoundOffset((double) (offset.x+
886 (geometry.x < 0 ? geometry.x : 0)));
887 }
888 crop.width=(size_t) ((ssize_t) crop.width-crop.x);
889 crop.x+=image->page.x;
890 next=CropImage(image,&crop,exception);
891 if (next != (Image *) NULL)
892 AppendImageToList(&crop_image,next);
893 }
894 }
895 ClearMagickException(exception);
896 return(crop_image);
897 }
898 if (((geometry.width == 0) && (geometry.height == 0)) ||
899 ((flags & XValue) != 0) || ((flags & YValue) != 0))
900 {
901 /*
902 Crop a single region at +X+Y.
903 */
904 crop_image=CropImage(image,&geometry,exception);
905 if ((crop_image != (Image *) NULL) && ((flags & AspectValue) != 0))
906 {
907 crop_image->page.width=geometry.width;
908 crop_image->page.height=geometry.height;
909 crop_image->page.x-=geometry.x;
910 crop_image->page.y-=geometry.y;
911 }
912 return(crop_image);
913 }
914 if ((image->columns > geometry.width) || (image->rows > geometry.height))
915 {
917 page;
918
919 size_t
920 height,
921 width;
922
923 ssize_t
924 x,
925 y;
926
927 /*
928 Crop into tiles of fixed size WxH.
929 */
930 page=image->page;
931 if (page.width == 0)
932 page.width=image->columns;
933 if (page.height == 0)
934 page.height=image->rows;
935 width=geometry.width;
936 if (width == 0)
937 width=page.width;
938 height=geometry.height;
939 if (height == 0)
940 height=page.height;
941 next=(Image *) NULL;
942 crop_image=NewImageList();
943 for (y=0; y < (ssize_t) page.height; y+=(ssize_t) height)
944 {
945 for (x=0; x < (ssize_t) page.width; x+=(ssize_t) width)
946 {
947 geometry.width=width;
948 geometry.height=height;
949 geometry.x=x;
950 geometry.y=y;
951 next=CropImage(image,&geometry,exception);
952 if (next == (Image *) NULL)
953 break;
954 AppendImageToList(&crop_image,next);
955 }
956 if (next == (Image *) NULL)
957 break;
958 }
959 return(crop_image);
960 }
961 return(CloneImage(image,0,0,MagickTrue,exception));
962}
963
964/*
965%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
966% %
967% %
968% %
969% E x c e r p t I m a g e %
970% %
971% %
972% %
973%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
974%
975% ExcerptImage() returns a excerpt of the image as defined by the geometry.
976%
977% The format of the ExcerptImage method is:
978%
979% Image *ExcerptImage(const Image *image,const RectangleInfo *geometry,
980% ExceptionInfo *exception)
981%
982% A description of each parameter follows:
983%
984% o image: the image.
985%
986% o geometry: Define the region of the image to extend with members
987% x, y, width, and height.
988%
989% o exception: return any errors or warnings in this structure.
990%
991*/
992MagickExport Image *ExcerptImage(const Image *image,
993 const RectangleInfo *geometry,ExceptionInfo *exception)
994{
995#define ExcerptImageTag "Excerpt/Image"
996
998 *excerpt_view,
999 *image_view;
1000
1001 Image
1002 *excerpt_image;
1003
1004 MagickBooleanType
1005 status;
1006
1007 MagickOffsetType
1008 progress;
1009
1010 ssize_t
1011 y;
1012
1013 /*
1014 Allocate excerpt image.
1015 */
1016 assert(image != (const Image *) NULL);
1017 assert(image->signature == MagickCoreSignature);
1018 assert(geometry != (const RectangleInfo *) NULL);
1019 assert(exception != (ExceptionInfo *) NULL);
1020 assert(exception->signature == MagickCoreSignature);
1021 if (IsEventLogging() != MagickFalse)
1022 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1023 excerpt_image=CloneImage(image,geometry->width,geometry->height,MagickTrue,
1024 exception);
1025 if (excerpt_image == (Image *) NULL)
1026 return((Image *) NULL);
1027 /*
1028 Excerpt each row.
1029 */
1030 status=MagickTrue;
1031 progress=0;
1032 image_view=AcquireVirtualCacheView(image,exception);
1033 excerpt_view=AcquireAuthenticCacheView(excerpt_image,exception);
1034#if defined(MAGICKCORE_OPENMP_SUPPORT)
1035 #pragma omp parallel for schedule(static) shared(progress,status) \
1036 magick_number_threads(image,excerpt_image,excerpt_image->rows,2)
1037#endif
1038 for (y=0; y < (ssize_t) excerpt_image->rows; y++)
1039 {
1040 const Quantum
1041 *magick_restrict p;
1042
1043 Quantum
1044 *magick_restrict q;
1045
1046 ssize_t
1047 x;
1048
1049 if (status == MagickFalse)
1050 continue;
1051 p=GetCacheViewVirtualPixels(image_view,geometry->x,geometry->y+y,
1052 geometry->width,1,exception);
1053 q=GetCacheViewAuthenticPixels(excerpt_view,0,y,excerpt_image->columns,1,
1054 exception);
1055 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1056 {
1057 status=MagickFalse;
1058 continue;
1059 }
1060 for (x=0; x < (ssize_t) excerpt_image->columns; x++)
1061 {
1062 ssize_t
1063 i;
1064
1065 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1066 {
1067 PixelChannel channel = GetPixelChannelChannel(image,i);
1068 PixelTrait traits = GetPixelChannelTraits(image,channel);
1069 PixelTrait excerpt_traits=GetPixelChannelTraits(excerpt_image,channel);
1070 if ((traits == UndefinedPixelTrait) ||
1071 (excerpt_traits == UndefinedPixelTrait))
1072 continue;
1073 SetPixelChannel(excerpt_image,channel,p[i],q);
1074 }
1075 p+=(ptrdiff_t) GetPixelChannels(image);
1076 q+=(ptrdiff_t) GetPixelChannels(excerpt_image);
1077 }
1078 if (SyncCacheViewAuthenticPixels(excerpt_view,exception) == MagickFalse)
1079 status=MagickFalse;
1080 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1081 {
1082 MagickBooleanType
1083 proceed;
1084
1085#if defined(MAGICKCORE_OPENMP_SUPPORT)
1086 #pragma omp atomic
1087#endif
1088 progress++;
1089 proceed=SetImageProgress(image,ExcerptImageTag,progress,image->rows);
1090 if (proceed == MagickFalse)
1091 status=MagickFalse;
1092 }
1093 }
1094 excerpt_view=DestroyCacheView(excerpt_view);
1095 image_view=DestroyCacheView(image_view);
1096 excerpt_image->type=image->type;
1097 if (status == MagickFalse)
1098 excerpt_image=DestroyImage(excerpt_image);
1099 return(excerpt_image);
1100}
1101
1102/*
1103%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1104% %
1105% %
1106% %
1107% E x t e n t I m a g e %
1108% %
1109% %
1110% %
1111%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1112%
1113% ExtentImage() extends the image as defined by the geometry, gravity, and
1114% image background color. Set the (x,y) offset of the geometry to move the
1115% original image relative to the extended image.
1116%
1117% The format of the ExtentImage method is:
1118%
1119% Image *ExtentImage(const Image *image,const RectangleInfo *geometry,
1120% ExceptionInfo *exception)
1121%
1122% A description of each parameter follows:
1123%
1124% o image: the image.
1125%
1126% o geometry: Define the region of the image to extend with members
1127% x, y, width, and height.
1128%
1129% o exception: return any errors or warnings in this structure.
1130%
1131*/
1132MagickExport Image *ExtentImage(const Image *image,
1133 const RectangleInfo *geometry,ExceptionInfo *exception)
1134{
1135 Image
1136 *extent_image;
1137
1138 MagickBooleanType
1139 status;
1140
1141 /*
1142 Allocate extent image.
1143 */
1144 assert(image != (const Image *) NULL);
1145 assert(image->signature == MagickCoreSignature);
1146 assert(geometry != (const RectangleInfo *) NULL);
1147 assert(exception != (ExceptionInfo *) NULL);
1148 assert(exception->signature == MagickCoreSignature);
1149 if (IsEventLogging() != MagickFalse)
1150 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1151 extent_image=CloneImage(image,geometry->width,geometry->height,MagickTrue,
1152 exception);
1153 if (extent_image == (Image *) NULL)
1154 return((Image *) NULL);
1155 status=SetImageBackgroundColor(extent_image,exception);
1156 if (status == MagickFalse)
1157 {
1158 extent_image=DestroyImage(extent_image);
1159 return((Image *) NULL);
1160 }
1161 DisableCompositeClampUnlessSpecified(extent_image);
1162 status=CompositeImage(extent_image,image,image->compose,MagickTrue,
1163 -geometry->x,-geometry->y,exception);
1164 if (status != MagickFalse)
1165 Update8BIMClipPath(extent_image,image->columns,image->rows,geometry);
1166 return(extent_image);
1167}
1168
1169/*
1170%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1171% %
1172% %
1173% %
1174% F l i p I m a g e %
1175% %
1176% %
1177% %
1178%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1179%
1180% FlipImage() creates a vertical mirror image by reflecting the pixels
1181% around the central x-axis.
1182%
1183% The format of the FlipImage method is:
1184%
1185% Image *FlipImage(const Image *image,ExceptionInfo *exception)
1186%
1187% A description of each parameter follows:
1188%
1189% o image: the image.
1190%
1191% o exception: return any errors or warnings in this structure.
1192%
1193*/
1194MagickExport Image *FlipImage(const Image *image,ExceptionInfo *exception)
1195{
1196#define FlipImageTag "Flip/Image"
1197
1198 CacheView
1199 *flip_view,
1200 *image_view;
1201
1202 Image
1203 *flip_image;
1204
1205 MagickBooleanType
1206 status;
1207
1208 MagickOffsetType
1209 progress;
1210
1212 page;
1213
1214 ssize_t
1215 y;
1216
1217 assert(image != (const Image *) NULL);
1218 assert(image->signature == MagickCoreSignature);
1219 assert(exception != (ExceptionInfo *) NULL);
1220 assert(exception->signature == MagickCoreSignature);
1221 if (IsEventLogging() != MagickFalse)
1222 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1223 flip_image=CloneImage(image,0,0,MagickTrue,exception);
1224 if (flip_image == (Image *) NULL)
1225 return((Image *) NULL);
1226 /*
1227 Flip image.
1228 */
1229 status=MagickTrue;
1230 progress=0;
1231 page=image->page;
1232 image_view=AcquireVirtualCacheView(image,exception);
1233 flip_view=AcquireAuthenticCacheView(flip_image,exception);
1234#if defined(MAGICKCORE_OPENMP_SUPPORT)
1235 #pragma omp parallel for schedule(static) shared(status) \
1236 magick_number_threads(image,flip_image,flip_image->rows,2)
1237#endif
1238 for (y=0; y < (ssize_t) flip_image->rows; y++)
1239 {
1240 const Quantum
1241 *magick_restrict p;
1242
1243 Quantum
1244 *magick_restrict q;
1245
1246 ssize_t
1247 x;
1248
1249 if (status == MagickFalse)
1250 continue;
1251 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1252 q=QueueCacheViewAuthenticPixels(flip_view,0,((ssize_t) flip_image->rows-y-
1253 1),flip_image->columns,1,exception);
1254 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1255 {
1256 status=MagickFalse;
1257 continue;
1258 }
1259 for (x=0; x < (ssize_t) flip_image->columns; x++)
1260 {
1261 ssize_t
1262 i;
1263
1264 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1265 {
1266 PixelChannel channel = GetPixelChannelChannel(image,i);
1267 PixelTrait traits = GetPixelChannelTraits(image,channel);
1268 PixelTrait flip_traits=GetPixelChannelTraits(flip_image,channel);
1269 if ((traits == UndefinedPixelTrait) ||
1270 (flip_traits == UndefinedPixelTrait))
1271 continue;
1272 SetPixelChannel(flip_image,channel,p[i],q);
1273 }
1274 p+=(ptrdiff_t) GetPixelChannels(image);
1275 q+=(ptrdiff_t) GetPixelChannels(flip_image);
1276 }
1277 if (SyncCacheViewAuthenticPixels(flip_view,exception) == MagickFalse)
1278 status=MagickFalse;
1279 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1280 {
1281 MagickBooleanType
1282 proceed;
1283
1284#if defined(MAGICKCORE_OPENMP_SUPPORT)
1285 #pragma omp atomic
1286#endif
1287 progress++;
1288 proceed=SetImageProgress(image,FlipImageTag,progress,image->rows);
1289 if (proceed == MagickFalse)
1290 status=MagickFalse;
1291 }
1292 }
1293 flip_view=DestroyCacheView(flip_view);
1294 image_view=DestroyCacheView(image_view);
1295 flip_image->type=image->type;
1296 if (page.height != 0)
1297 page.y=((ssize_t) page.height-(ssize_t) flip_image->rows-page.y);
1298 flip_image->page=page;
1299 if (status == MagickFalse)
1300 flip_image=DestroyImage(flip_image);
1301 return(flip_image);
1302}
1303
1304/*
1305%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1306% %
1307% %
1308% %
1309% F l o p I m a g e %
1310% %
1311% %
1312% %
1313%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1314%
1315% FlopImage() creates a horizontal mirror image by reflecting the pixels
1316% around the central y-axis.
1317%
1318% The format of the FlopImage method is:
1319%
1320% Image *FlopImage(const Image *image,ExceptionInfo *exception)
1321%
1322% A description of each parameter follows:
1323%
1324% o image: the image.
1325%
1326% o exception: return any errors or warnings in this structure.
1327%
1328*/
1329MagickExport Image *FlopImage(const Image *image,ExceptionInfo *exception)
1330{
1331#define FlopImageTag "Flop/Image"
1332
1333 CacheView
1334 *flop_view,
1335 *image_view;
1336
1337 Image
1338 *flop_image;
1339
1340 MagickBooleanType
1341 status;
1342
1343 MagickOffsetType
1344 progress;
1345
1347 page;
1348
1349 ssize_t
1350 y;
1351
1352 assert(image != (const Image *) NULL);
1353 assert(image->signature == MagickCoreSignature);
1354 assert(exception != (ExceptionInfo *) NULL);
1355 assert(exception->signature == MagickCoreSignature);
1356 if (IsEventLogging() != MagickFalse)
1357 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1358 flop_image=CloneImage(image,0,0,MagickTrue,exception);
1359 if (flop_image == (Image *) NULL)
1360 return((Image *) NULL);
1361 /*
1362 Flop each row.
1363 */
1364 status=MagickTrue;
1365 progress=0;
1366 page=image->page;
1367 image_view=AcquireVirtualCacheView(image,exception);
1368 flop_view=AcquireAuthenticCacheView(flop_image,exception);
1369#if defined(MAGICKCORE_OPENMP_SUPPORT)
1370 #pragma omp parallel for schedule(static) shared(status) \
1371 magick_number_threads(image,flop_image,flop_image->rows,2)
1372#endif
1373 for (y=0; y < (ssize_t) flop_image->rows; y++)
1374 {
1375 const Quantum
1376 *magick_restrict p;
1377
1378 ssize_t
1379 x;
1380
1381 Quantum
1382 *magick_restrict q;
1383
1384 if (status == MagickFalse)
1385 continue;
1386 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1387 q=QueueCacheViewAuthenticPixels(flop_view,0,y,flop_image->columns,1,
1388 exception);
1389 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1390 {
1391 status=MagickFalse;
1392 continue;
1393 }
1394 q+=(ptrdiff_t) GetPixelChannels(flop_image)*flop_image->columns;
1395 for (x=0; x < (ssize_t) flop_image->columns; x++)
1396 {
1397 ssize_t
1398 i;
1399
1400 q-=GetPixelChannels(flop_image);
1401 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1402 {
1403 PixelChannel channel = GetPixelChannelChannel(image,i);
1404 PixelTrait traits = GetPixelChannelTraits(image,channel);
1405 PixelTrait flop_traits=GetPixelChannelTraits(flop_image,channel);
1406 if ((traits == UndefinedPixelTrait) ||
1407 (flop_traits == UndefinedPixelTrait))
1408 continue;
1409 SetPixelChannel(flop_image,channel,p[i],q);
1410 }
1411 p+=(ptrdiff_t) GetPixelChannels(image);
1412 }
1413 if (SyncCacheViewAuthenticPixels(flop_view,exception) == MagickFalse)
1414 status=MagickFalse;
1415 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1416 {
1417 MagickBooleanType
1418 proceed;
1419
1420#if defined(MAGICKCORE_OPENMP_SUPPORT)
1421 #pragma omp atomic
1422#endif
1423 progress++;
1424 proceed=SetImageProgress(image,FlopImageTag,progress,image->rows);
1425 if (proceed == MagickFalse)
1426 status=MagickFalse;
1427 }
1428 }
1429 flop_view=DestroyCacheView(flop_view);
1430 image_view=DestroyCacheView(image_view);
1431 flop_image->type=image->type;
1432 if (page.width != 0)
1433 page.x=((ssize_t) page.width-(ssize_t) flop_image->columns-page.x);
1434 flop_image->page=page;
1435 if (status == MagickFalse)
1436 flop_image=DestroyImage(flop_image);
1437 return(flop_image);
1438}
1439
1440/*
1441%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1442% %
1443% %
1444% %
1445% R o l l I m a g e %
1446% %
1447% %
1448% %
1449%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1450%
1451% RollImage() offsets an image as defined by x_offset and y_offset.
1452%
1453% The format of the RollImage method is:
1454%
1455% Image *RollImage(const Image *image,const ssize_t x_offset,
1456% const ssize_t y_offset,ExceptionInfo *exception)
1457%
1458% A description of each parameter follows:
1459%
1460% o image: the image.
1461%
1462% o x_offset: the number of columns to roll in the horizontal direction.
1463%
1464% o y_offset: the number of rows to roll in the vertical direction.
1465%
1466% o exception: return any errors or warnings in this structure.
1467%
1468*/
1469
1470static MagickBooleanType CopyImageRegion(Image *destination,const Image *source, const size_t columns,const size_t rows,const ssize_t sx,const ssize_t sy,
1471 const ssize_t dx,const ssize_t dy,ExceptionInfo *exception)
1472{
1473 CacheView
1474 *source_view,
1475 *destination_view;
1476
1477 MagickBooleanType
1478 status;
1479
1480 ssize_t
1481 y;
1482
1483 if (columns == 0)
1484 return(MagickTrue);
1485 status=MagickTrue;
1486 source_view=AcquireVirtualCacheView(source,exception);
1487 destination_view=AcquireAuthenticCacheView(destination,exception);
1488#if defined(MAGICKCORE_OPENMP_SUPPORT)
1489 #pragma omp parallel for schedule(static) shared(status) \
1490 magick_number_threads(source,destination,rows,2)
1491#endif
1492 for (y=0; y < (ssize_t) rows; y++)
1493 {
1494 MagickBooleanType
1495 sync;
1496
1497 const Quantum
1498 *magick_restrict p;
1499
1500 Quantum
1501 *magick_restrict q;
1502
1503 ssize_t
1504 x;
1505
1506 /*
1507 Transfer scanline.
1508 */
1509 if (status == MagickFalse)
1510 continue;
1511 p=GetCacheViewVirtualPixels(source_view,sx,sy+y,columns,1,exception);
1512 q=GetCacheViewAuthenticPixels(destination_view,dx,dy+y,columns,1,exception);
1513 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1514 {
1515 status=MagickFalse;
1516 continue;
1517 }
1518 for (x=0; x < (ssize_t) columns; x++)
1519 {
1520 ssize_t
1521 i;
1522
1523 for (i=0; i < (ssize_t) GetPixelChannels(source); i++)
1524 {
1525 PixelChannel channel = GetPixelChannelChannel(source,i);
1526 PixelTrait source_traits=GetPixelChannelTraits(source,channel);
1527 PixelTrait destination_traits=GetPixelChannelTraits(destination,
1528 channel);
1529 if ((source_traits == UndefinedPixelTrait) ||
1530 (destination_traits == UndefinedPixelTrait))
1531 continue;
1532 SetPixelChannel(destination,channel,p[i],q);
1533 }
1534 p+=(ptrdiff_t) GetPixelChannels(source);
1535 q+=(ptrdiff_t) GetPixelChannels(destination);
1536 }
1537 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
1538 if (sync == MagickFalse)
1539 status=MagickFalse;
1540 }
1541 destination_view=DestroyCacheView(destination_view);
1542 source_view=DestroyCacheView(source_view);
1543 return(status);
1544}
1545
1546MagickExport Image *RollImage(const Image *image,const ssize_t x_offset,
1547 const ssize_t y_offset,ExceptionInfo *exception)
1548{
1549#define RollImageTag "Roll/Image"
1550
1551 Image
1552 *roll_image;
1553
1554 MagickStatusType
1555 status;
1556
1558 offset;
1559
1560 /*
1561 Initialize roll image attributes.
1562 */
1563 assert(image != (const Image *) NULL);
1564 assert(image->signature == MagickCoreSignature);
1565 assert(exception != (ExceptionInfo *) NULL);
1566 assert(exception->signature == MagickCoreSignature);
1567 if (IsEventLogging() != MagickFalse)
1568 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1569 roll_image=CloneImage(image,0,0,MagickTrue,exception);
1570 if (roll_image == (Image *) NULL)
1571 return((Image *) NULL);
1572 offset.x=x_offset;
1573 offset.y=y_offset;
1574 while (offset.x < 0)
1575 offset.x+=(ssize_t) image->columns;
1576 while (offset.x >= (ssize_t) image->columns)
1577 offset.x-=(ssize_t) image->columns;
1578 while (offset.y < 0)
1579 offset.y+=(ssize_t) image->rows;
1580 while (offset.y >= (ssize_t) image->rows)
1581 offset.y-=(ssize_t) image->rows;
1582 /*
1583 Roll image.
1584 */
1585 status=CopyImageRegion(roll_image,image,(size_t) offset.x,
1586 (size_t) offset.y,(ssize_t) image->columns-offset.x,(ssize_t) image->rows-
1587 offset.y,0,0,exception);
1588 (void) SetImageProgress(image,RollImageTag,0,3);
1589 status&=(MagickStatusType) CopyImageRegion(roll_image,image,(size_t)
1590 ((ssize_t) image->columns-offset.x),(size_t) offset.y,0,(ssize_t)
1591 image->rows-offset.y,offset.x,0,exception);
1592 (void) SetImageProgress(image,RollImageTag,1,3);
1593 status&=(MagickStatusType) CopyImageRegion(roll_image,image,(size_t)
1594 offset.x,(size_t) ((ssize_t) image->rows-offset.y),(ssize_t)
1595 image->columns-offset.x,0,0,offset.y,exception);
1596 (void) SetImageProgress(image,RollImageTag,2,3);
1597 status&=(MagickStatusType) CopyImageRegion(roll_image,image,(size_t)
1598 ((ssize_t) image->columns-offset.x),(size_t) ((ssize_t) image->rows-
1599 offset.y),0,0,offset.x,offset.y,exception);
1600 (void) SetImageProgress(image,RollImageTag,3,3);
1601 roll_image->type=image->type;
1602 if (status == MagickFalse)
1603 roll_image=DestroyImage(roll_image);
1604 return(roll_image);
1605}
1606
1607/*
1608%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1609% %
1610% %
1611% %
1612% S h a v e I m a g e %
1613% %
1614% %
1615% %
1616%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1617%
1618% ShaveImage() shaves pixels from the image edges. It allocates the memory
1619% necessary for the new Image structure and returns a pointer to the new
1620% image.
1621%
1622% The format of the ShaveImage method is:
1623%
1624% Image *ShaveImage(const Image *image,const RectangleInfo *shave_info,
1625% ExceptionInfo *exception)
1626%
1627% A description of each parameter follows:
1628%
1629% o shave_image: Method ShaveImage returns a pointer to the shaved
1630% image. A null image is returned if there is a memory shortage or
1631% if the image width or height is zero.
1632%
1633% o image: the image.
1634%
1635% o shave_info: Specifies a pointer to a RectangleInfo which defines the
1636% region of the image to crop.
1637%
1638% o exception: return any errors or warnings in this structure.
1639%
1640*/
1641MagickExport Image *ShaveImage(const Image *image,
1642 const RectangleInfo *shave_info,ExceptionInfo *exception)
1643{
1644 Image
1645 *shave_image;
1646
1648 geometry;
1649
1650 assert(image != (const Image *) NULL);
1651 assert(image->signature == MagickCoreSignature);
1652 if (IsEventLogging() != MagickFalse)
1653 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1654 if (((2*shave_info->width) >= image->columns) ||
1655 ((2*shave_info->height) >= image->rows))
1656 ThrowImageException(OptionWarning,"GeometryDoesNotContainImage");
1657 SetGeometry(image,&geometry);
1658 geometry.width-=2*shave_info->width;
1659 geometry.height-=2*shave_info->height;
1660 geometry.x=(ssize_t) shave_info->width+image->page.x;
1661 geometry.y=(ssize_t) shave_info->height+image->page.y;
1662 shave_image=CropImage(image,&geometry,exception);
1663 if (shave_image == (Image *) NULL)
1664 return((Image *) NULL);
1665 shave_image->page.width-=2*shave_info->width;
1666 shave_image->page.height-=2*shave_info->height;
1667 shave_image->page.x-=(ssize_t) shave_info->width;
1668 shave_image->page.y-=(ssize_t) shave_info->height;
1669 return(shave_image);
1670}
1671
1672/*
1673%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1674% %
1675% %
1676% %
1677% S p l i c e I m a g e %
1678% %
1679% %
1680% %
1681%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1682%
1683% SpliceImage() splices a solid color into the image as defined by the
1684% geometry.
1685%
1686% The format of the SpliceImage method is:
1687%
1688% Image *SpliceImage(const Image *image,const RectangleInfo *geometry,
1689% ExceptionInfo *exception)
1690%
1691% A description of each parameter follows:
1692%
1693% o image: the image.
1694%
1695% o geometry: Define the region of the image to splice with members
1696% x, y, width, and height.
1697%
1698% o exception: return any errors or warnings in this structure.
1699%
1700*/
1701MagickExport Image *SpliceImage(const Image *image,
1702 const RectangleInfo *geometry,ExceptionInfo *exception)
1703{
1704#define SpliceImageTag "Splice/Image"
1705
1706 CacheView
1707 *image_view,
1708 *splice_view;
1709
1710 Image
1711 *splice_image;
1712
1713 MagickBooleanType
1714 status;
1715
1716 MagickOffsetType
1717 progress;
1718
1720 splice_geometry;
1721
1722 ssize_t
1723 columns,
1724 y;
1725
1726 /*
1727 Allocate splice image.
1728 */
1729 assert(image != (const Image *) NULL);
1730 assert(image->signature == MagickCoreSignature);
1731 if (IsEventLogging() != MagickFalse)
1732 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1733 assert(geometry != (const RectangleInfo *) NULL);
1734 assert(exception != (ExceptionInfo *) NULL);
1735 assert(exception->signature == MagickCoreSignature);
1736 splice_geometry=(*geometry);
1737 splice_image=CloneImage(image,image->columns+splice_geometry.width,
1738 image->rows+splice_geometry.height,MagickTrue,exception);
1739 if (splice_image == (Image *) NULL)
1740 return((Image *) NULL);
1741 if (SetImageStorageClass(splice_image,DirectClass,exception) == MagickFalse)
1742 {
1743 splice_image=DestroyImage(splice_image);
1744 return((Image *) NULL);
1745 }
1746 if ((IsPixelInfoGray(&splice_image->background_color) == MagickFalse) &&
1747 (IsGrayColorspace(splice_image->colorspace) != MagickFalse))
1748 (void) SetImageColorspace(splice_image,sRGBColorspace,exception);
1749 if ((splice_image->background_color.alpha_trait != UndefinedPixelTrait) &&
1750 (splice_image->alpha_trait == UndefinedPixelTrait))
1751 (void) SetImageAlpha(splice_image,OpaqueAlpha,exception);
1752 (void) SetImageBackgroundColor(splice_image,exception);
1753 /*
1754 Respect image geometry.
1755 */
1756 switch (image->gravity)
1757 {
1758 default:
1759 case UndefinedGravity:
1760 case NorthWestGravity:
1761 break;
1762 case NorthGravity:
1763 {
1764 splice_geometry.x+=(ssize_t) splice_geometry.width/2;
1765 break;
1766 }
1767 case NorthEastGravity:
1768 {
1769 splice_geometry.x+=(ssize_t) splice_geometry.width;
1770 break;
1771 }
1772 case WestGravity:
1773 {
1774 splice_geometry.y+=(ssize_t) splice_geometry.width/2;
1775 break;
1776 }
1777 case CenterGravity:
1778 {
1779 splice_geometry.x+=(ssize_t) splice_geometry.width/2;
1780 splice_geometry.y+=(ssize_t) splice_geometry.height/2;
1781 break;
1782 }
1783 case EastGravity:
1784 {
1785 splice_geometry.x+=(ssize_t) splice_geometry.width;
1786 splice_geometry.y+=(ssize_t) splice_geometry.height/2;
1787 break;
1788 }
1789 case SouthWestGravity:
1790 {
1791 splice_geometry.y+=(ssize_t) splice_geometry.height;
1792 break;
1793 }
1794 case SouthGravity:
1795 {
1796 splice_geometry.x+=(ssize_t) splice_geometry.width/2;
1797 splice_geometry.y+=(ssize_t) splice_geometry.height;
1798 break;
1799 }
1800 case SouthEastGravity:
1801 {
1802 splice_geometry.x+=(ssize_t) splice_geometry.width;
1803 splice_geometry.y+=(ssize_t) splice_geometry.height;
1804 break;
1805 }
1806 }
1807 /*
1808 Splice image.
1809 */
1810 status=MagickTrue;
1811 progress=0;
1812 columns=MagickMin(splice_geometry.x,(ssize_t) splice_image->columns);
1813 image_view=AcquireVirtualCacheView(image,exception);
1814 splice_view=AcquireAuthenticCacheView(splice_image,exception);
1815#if defined(MAGICKCORE_OPENMP_SUPPORT)
1816 #pragma omp parallel for schedule(static) shared(progress,status) \
1817 magick_number_threads(image,splice_image,(size_t) splice_geometry.y,2)
1818#endif
1819 for (y=0; y < (ssize_t) splice_geometry.y; y++)
1820 {
1821 const Quantum
1822 *magick_restrict p;
1823
1824 ssize_t
1825 x;
1826
1827 Quantum
1828 *magick_restrict q;
1829
1830 if (status == MagickFalse)
1831 continue;
1832 p=GetCacheViewVirtualPixels(image_view,0,y,splice_image->columns,1,
1833 exception);
1834 q=QueueCacheViewAuthenticPixels(splice_view,0,y,splice_image->columns,1,
1835 exception);
1836 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1837 {
1838 status=MagickFalse;
1839 continue;
1840 }
1841 for (x=0; x < columns; x++)
1842 {
1843 ssize_t
1844 i;
1845
1846 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1847 {
1848 PixelChannel channel = GetPixelChannelChannel(image,i);
1849 PixelTrait traits = GetPixelChannelTraits(image,channel);
1850 PixelTrait splice_traits=GetPixelChannelTraits(splice_image,channel);
1851 if ((traits == UndefinedPixelTrait) ||
1852 (splice_traits == UndefinedPixelTrait))
1853 continue;
1854 SetPixelChannel(splice_image,channel,p[i],q);
1855 }
1856 SetPixelRed(splice_image,GetPixelRed(image,p),q);
1857 SetPixelGreen(splice_image,GetPixelGreen(image,p),q);
1858 SetPixelBlue(splice_image,GetPixelBlue(image,p),q);
1859 SetPixelAlpha(splice_image,GetPixelAlpha(image,p),q);
1860 p+=(ptrdiff_t) GetPixelChannels(image);
1861 q+=(ptrdiff_t) GetPixelChannels(splice_image);
1862 }
1863 for ( ; x < (splice_geometry.x+(ssize_t) splice_geometry.width); x++)
1864 q+=(ptrdiff_t) GetPixelChannels(splice_image);
1865 for ( ; x < (ssize_t) splice_image->columns; x++)
1866 {
1867 ssize_t
1868 i;
1869
1870 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1871 {
1872 PixelChannel channel = GetPixelChannelChannel(image,i);
1873 PixelTrait traits = GetPixelChannelTraits(image,channel);
1874 PixelTrait splice_traits=GetPixelChannelTraits(splice_image,channel);
1875 if ((traits == UndefinedPixelTrait) ||
1876 (splice_traits == UndefinedPixelTrait))
1877 continue;
1878 SetPixelChannel(splice_image,channel,p[i],q);
1879 }
1880 SetPixelRed(splice_image,GetPixelRed(image,p),q);
1881 SetPixelGreen(splice_image,GetPixelGreen(image,p),q);
1882 SetPixelBlue(splice_image,GetPixelBlue(image,p),q);
1883 SetPixelAlpha(splice_image,GetPixelAlpha(image,p),q);
1884 p+=(ptrdiff_t) GetPixelChannels(image);
1885 q+=(ptrdiff_t) GetPixelChannels(splice_image);
1886 }
1887 if (SyncCacheViewAuthenticPixels(splice_view,exception) == MagickFalse)
1888 status=MagickFalse;
1889 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1890 {
1891 MagickBooleanType
1892 proceed;
1893
1894#if defined(MAGICKCORE_OPENMP_SUPPORT)
1895 #pragma omp atomic
1896#endif
1897 progress++;
1898 proceed=SetImageProgress(image,SpliceImageTag,progress,
1899 splice_image->rows);
1900 if (proceed == MagickFalse)
1901 status=MagickFalse;
1902 }
1903 }
1904#if defined(MAGICKCORE_OPENMP_SUPPORT)
1905 #pragma omp parallel for schedule(static) shared(progress,status) \
1906 magick_number_threads(image,splice_image,splice_image->rows,2)
1907#endif
1908 for (y=splice_geometry.y+(ssize_t) splice_geometry.height; y < (ssize_t) splice_image->rows; y++)
1909 {
1910 const Quantum
1911 *magick_restrict p;
1912
1913 ssize_t
1914 x;
1915
1916 Quantum
1917 *magick_restrict q;
1918
1919 if (status == MagickFalse)
1920 continue;
1921 if ((y < 0) || (y >= (ssize_t) splice_image->rows))
1922 continue;
1923 p=GetCacheViewVirtualPixels(image_view,0,y-(ssize_t) splice_geometry.height,
1924 splice_image->columns,1,exception);
1925 q=QueueCacheViewAuthenticPixels(splice_view,0,y,splice_image->columns,1,
1926 exception);
1927 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1928 {
1929 status=MagickFalse;
1930 continue;
1931 }
1932 for (x=0; x < columns; x++)
1933 {
1934 ssize_t
1935 i;
1936
1937 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1938 {
1939 PixelChannel channel = GetPixelChannelChannel(image,i);
1940 PixelTrait traits = GetPixelChannelTraits(image,channel);
1941 PixelTrait splice_traits=GetPixelChannelTraits(splice_image,channel);
1942 if ((traits == UndefinedPixelTrait) ||
1943 (splice_traits == UndefinedPixelTrait))
1944 continue;
1945 SetPixelChannel(splice_image,channel,p[i],q);
1946 }
1947 SetPixelRed(splice_image,GetPixelRed(image,p),q);
1948 SetPixelGreen(splice_image,GetPixelGreen(image,p),q);
1949 SetPixelBlue(splice_image,GetPixelBlue(image,p),q);
1950 SetPixelAlpha(splice_image,GetPixelAlpha(image,p),q);
1951 p+=(ptrdiff_t) GetPixelChannels(image);
1952 q+=(ptrdiff_t) GetPixelChannels(splice_image);
1953 }
1954 for ( ; x < (splice_geometry.x+(ssize_t) splice_geometry.width); x++)
1955 q+=(ptrdiff_t) GetPixelChannels(splice_image);
1956 for ( ; x < (ssize_t) splice_image->columns; x++)
1957 {
1958 ssize_t
1959 i;
1960
1961 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1962 {
1963 PixelChannel channel = GetPixelChannelChannel(image,i);
1964 PixelTrait traits = GetPixelChannelTraits(image,channel);
1965 PixelTrait splice_traits=GetPixelChannelTraits(splice_image,channel);
1966 if ((traits == UndefinedPixelTrait) ||
1967 (splice_traits == UndefinedPixelTrait))
1968 continue;
1969 SetPixelChannel(splice_image,channel,p[i],q);
1970 }
1971 SetPixelRed(splice_image,GetPixelRed(image,p),q);
1972 SetPixelGreen(splice_image,GetPixelGreen(image,p),q);
1973 SetPixelBlue(splice_image,GetPixelBlue(image,p),q);
1974 SetPixelAlpha(splice_image,GetPixelAlpha(image,p),q);
1975 p+=(ptrdiff_t) GetPixelChannels(image);
1976 q+=(ptrdiff_t) GetPixelChannels(splice_image);
1977 }
1978 if (SyncCacheViewAuthenticPixels(splice_view,exception) == MagickFalse)
1979 status=MagickFalse;
1980 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1981 {
1982 MagickBooleanType
1983 proceed;
1984
1985#if defined(MAGICKCORE_OPENMP_SUPPORT)
1986 #pragma omp atomic
1987#endif
1988 progress++;
1989 proceed=SetImageProgress(image,SpliceImageTag,progress,
1990 splice_image->rows);
1991 if (proceed == MagickFalse)
1992 status=MagickFalse;
1993 }
1994 }
1995 splice_view=DestroyCacheView(splice_view);
1996 image_view=DestroyCacheView(image_view);
1997 if (status == MagickFalse)
1998 splice_image=DestroyImage(splice_image);
1999 return(splice_image);
2000}
2001
2002/*
2003%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2004% %
2005% %
2006% %
2007% T r a n s f o r m I m a g e %
2008% %
2009% %
2010% %
2011%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2012%
2013% TransformImage() is a convenience method that behaves like ResizeImage() or
2014% CropImage() but accepts scaling and/or cropping information as a region
2015% geometry specification. If the operation fails, the original image handle
2016% is left as is.
2017%
2018% This should only be used for single images.
2019%
2020% This function destroys what it assumes to be a single image list.
2021% If the input image is part of a larger list, all other images in that list
2022% will be simply 'lost', not destroyed.
2023%
2024% Also if the crop generates a list of images only the first image is resized.
2025% And finally if the crop succeeds and the resize failed, you will get a
2026% cropped image, as well as a 'false' or 'failed' report.
2027%
2028% This function and should probably be deprecated in favor of direct calls
2029% to CropImageToTiles() or ResizeImage(), as appropriate.
2030%
2031% The format of the TransformImage method is:
2032%
2033% MagickBooleanType TransformImage(Image **image,const char *crop_geometry,
2034% const char *image_geometry,ExceptionInfo *exception)
2035%
2036% A description of each parameter follows:
2037%
2038% o image: the image The transformed image is returned as this parameter.
2039%
2040% o crop_geometry: A crop geometry string. This geometry defines a
2041% subregion of the image to crop.
2042%
2043% o image_geometry: An image geometry string. This geometry defines the
2044% final size of the image.
2045%
2046% o exception: return any errors or warnings in this structure.
2047%
2048*/
2049MagickPrivate MagickBooleanType TransformImage(Image **image,
2050 const char *crop_geometry,const char *image_geometry,ExceptionInfo *exception)
2051{
2052 Image
2053 *resize_image,
2054 *transform_image;
2055
2057 geometry;
2058
2059 assert(image != (Image **) NULL);
2060 assert((*image)->signature == MagickCoreSignature);
2061 if (IsEventLogging() != MagickFalse)
2062 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
2063 transform_image=(*image);
2064 if (crop_geometry != (const char *) NULL)
2065 {
2066 Image
2067 *crop_image;
2068
2069 /*
2070 Crop image to a user specified size.
2071 */
2072 crop_image=CropImageToTiles(*image,crop_geometry,exception);
2073 if (crop_image == (Image *) NULL)
2074 transform_image=CloneImage(*image,0,0,MagickTrue,exception);
2075 else
2076 {
2077 transform_image=DestroyImage(transform_image);
2078 transform_image=GetFirstImageInList(crop_image);
2079 }
2080 *image=transform_image;
2081 }
2082 if (image_geometry == (const char *) NULL)
2083 return(MagickTrue);
2084 /*
2085 Scale image to a user specified size.
2086 */
2087 (void) ParseRegionGeometry(transform_image,image_geometry,&geometry,
2088 exception);
2089 if ((transform_image->columns == geometry.width) &&
2090 (transform_image->rows == geometry.height))
2091 return(MagickTrue);
2092 resize_image=ResizeImage(transform_image,geometry.width,geometry.height,
2093 transform_image->filter,exception);
2094 if (resize_image == (Image *) NULL)
2095 return(MagickFalse);
2096 transform_image=DestroyImage(transform_image);
2097 transform_image=resize_image;
2098 *image=transform_image;
2099 return(MagickTrue);
2100}
2101
2102/*
2103%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2104% %
2105% %
2106% %
2107% T r a n s p o s e I m a g e %
2108% %
2109% %
2110% %
2111%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2112%
2113% TransposeImage() creates a horizontal mirror image by reflecting the pixels
2114% around the central y-axis while rotating them by 90 degrees.
2115%
2116% The format of the TransposeImage method is:
2117%
2118% Image *TransposeImage(const Image *image,ExceptionInfo *exception)
2119%
2120% A description of each parameter follows:
2121%
2122% o image: the image.
2123%
2124% o exception: return any errors or warnings in this structure.
2125%
2126*/
2127MagickExport Image *TransposeImage(const Image *image,ExceptionInfo *exception)
2128{
2129#define TransposeImageTag "Transpose/Image"
2130
2131 CacheView
2132 *image_view,
2133 *transpose_view;
2134
2135 Image
2136 *transpose_image;
2137
2138 MagickBooleanType
2139 status;
2140
2141 MagickOffsetType
2142 progress;
2143
2145 page;
2146
2147 ssize_t
2148 y;
2149
2150 assert(image != (const Image *) NULL);
2151 assert(image->signature == MagickCoreSignature);
2152 assert(exception != (ExceptionInfo *) NULL);
2153 assert(exception->signature == MagickCoreSignature);
2154 if (IsEventLogging() != MagickFalse)
2155 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2156 transpose_image=CloneImage(image,image->rows,image->columns,MagickTrue,
2157 exception);
2158 if (transpose_image == (Image *) NULL)
2159 return((Image *) NULL);
2160 /*
2161 Transpose image.
2162 */
2163 status=MagickTrue;
2164 progress=0;
2165 image_view=AcquireVirtualCacheView(image,exception);
2166 transpose_view=AcquireAuthenticCacheView(transpose_image,exception);
2167#if defined(MAGICKCORE_OPENMP_SUPPORT)
2168 #pragma omp parallel for schedule(static) shared(progress,status) \
2169 magick_number_threads(image,transpose_image,image->rows,2)
2170#endif
2171 for (y=0; y < (ssize_t) image->rows; y++)
2172 {
2173 const Quantum
2174 *magick_restrict p;
2175
2176 Quantum
2177 *magick_restrict q;
2178
2179 ssize_t
2180 x;
2181
2182 if (status == MagickFalse)
2183 continue;
2184 p=GetCacheViewVirtualPixels(image_view,0,(ssize_t) image->rows-y-1,
2185 image->columns,1,exception);
2186 q=QueueCacheViewAuthenticPixels(transpose_view,(ssize_t) image->rows-y-1,
2187 0,1,transpose_image->rows,exception);
2188 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2189 {
2190 status=MagickFalse;
2191 continue;
2192 }
2193 for (x=0; x < (ssize_t) image->columns; x++)
2194 {
2195 ssize_t
2196 i;
2197
2198 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2199 {
2200 PixelChannel channel = GetPixelChannelChannel(image,i);
2201 PixelTrait traits = GetPixelChannelTraits(image,channel);
2202 PixelTrait transpose_traits=GetPixelChannelTraits(transpose_image,
2203 channel);
2204 if ((traits == UndefinedPixelTrait) ||
2205 (transpose_traits == UndefinedPixelTrait))
2206 continue;
2207 SetPixelChannel(transpose_image,channel,p[i],q);
2208 }
2209 p+=(ptrdiff_t) GetPixelChannels(image);
2210 q+=(ptrdiff_t) GetPixelChannels(transpose_image);
2211 }
2212 if (SyncCacheViewAuthenticPixels(transpose_view,exception) == MagickFalse)
2213 status=MagickFalse;
2214 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2215 {
2216 MagickBooleanType
2217 proceed;
2218
2219#if defined(MAGICKCORE_OPENMP_SUPPORT)
2220 #pragma omp atomic
2221#endif
2222 progress++;
2223 proceed=SetImageProgress(image,TransposeImageTag,progress,image->rows);
2224 if (proceed == MagickFalse)
2225 status=MagickFalse;
2226 }
2227 }
2228 transpose_view=DestroyCacheView(transpose_view);
2229 image_view=DestroyCacheView(image_view);
2230 transpose_image->type=image->type;
2231 page=transpose_image->page;
2232 Swap(page.width,page.height);
2233 Swap(page.x,page.y);
2234 transpose_image->page=page;
2235 if (status == MagickFalse)
2236 transpose_image=DestroyImage(transpose_image);
2237 return(transpose_image);
2238}
2239
2240/*
2241%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2242% %
2243% %
2244% %
2245% T r a n s v e r s e I m a g e %
2246% %
2247% %
2248% %
2249%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2250%
2251% TransverseImage() creates a vertical mirror image by reflecting the pixels
2252% around the central x-axis while rotating them by 270 degrees.
2253%
2254% The format of the TransverseImage method is:
2255%
2256% Image *TransverseImage(const Image *image,ExceptionInfo *exception)
2257%
2258% A description of each parameter follows:
2259%
2260% o image: the image.
2261%
2262% o exception: return any errors or warnings in this structure.
2263%
2264*/
2265MagickExport Image *TransverseImage(const Image *image,ExceptionInfo *exception)
2266{
2267#define TransverseImageTag "Transverse/Image"
2268
2269 CacheView
2270 *image_view,
2271 *transverse_view;
2272
2273 Image
2274 *transverse_image;
2275
2276 MagickBooleanType
2277 status;
2278
2279 MagickOffsetType
2280 progress;
2281
2283 page;
2284
2285 ssize_t
2286 y;
2287
2288 assert(image != (const Image *) NULL);
2289 assert(image->signature == MagickCoreSignature);
2290 assert(exception != (ExceptionInfo *) NULL);
2291 assert(exception->signature == MagickCoreSignature);
2292 if (IsEventLogging() != MagickFalse)
2293 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2294 transverse_image=CloneImage(image,image->rows,image->columns,MagickTrue,
2295 exception);
2296 if (transverse_image == (Image *) NULL)
2297 return((Image *) NULL);
2298 /*
2299 Transverse image.
2300 */
2301 status=MagickTrue;
2302 progress=0;
2303 image_view=AcquireVirtualCacheView(image,exception);
2304 transverse_view=AcquireAuthenticCacheView(transverse_image,exception);
2305#if defined(MAGICKCORE_OPENMP_SUPPORT)
2306 #pragma omp parallel for schedule(static) shared(progress,status) \
2307 magick_number_threads(image,transverse_image,image->rows,2)
2308#endif
2309 for (y=0; y < (ssize_t) image->rows; y++)
2310 {
2311 MagickBooleanType
2312 sync;
2313
2314 const Quantum
2315 *magick_restrict p;
2316
2317 Quantum
2318 *magick_restrict q;
2319
2320 ssize_t
2321 x;
2322
2323 if (status == MagickFalse)
2324 continue;
2325 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
2326 q=QueueCacheViewAuthenticPixels(transverse_view,(ssize_t) image->rows-y-1,
2327 0,1,transverse_image->rows,exception);
2328 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2329 {
2330 status=MagickFalse;
2331 continue;
2332 }
2333 q+=(ptrdiff_t) GetPixelChannels(transverse_image)*image->columns;
2334 for (x=0; x < (ssize_t) image->columns; x++)
2335 {
2336 ssize_t
2337 i;
2338
2339 q-=GetPixelChannels(transverse_image);
2340 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2341 {
2342 PixelChannel channel = GetPixelChannelChannel(image,i);
2343 PixelTrait traits = GetPixelChannelTraits(image,channel);
2344 PixelTrait transverse_traits=GetPixelChannelTraits(transverse_image,
2345 channel);
2346 if ((traits == UndefinedPixelTrait) ||
2347 (transverse_traits == UndefinedPixelTrait))
2348 continue;
2349 SetPixelChannel(transverse_image,channel,p[i],q);
2350 }
2351 p+=(ptrdiff_t) GetPixelChannels(image);
2352 }
2353 sync=SyncCacheViewAuthenticPixels(transverse_view,exception);
2354 if (sync == MagickFalse)
2355 status=MagickFalse;
2356 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2357 {
2358 MagickBooleanType
2359 proceed;
2360
2361#if defined(MAGICKCORE_OPENMP_SUPPORT)
2362 #pragma omp atomic
2363#endif
2364 progress++;
2365 proceed=SetImageProgress(image,TransverseImageTag,progress,image->rows);
2366 if (proceed == MagickFalse)
2367 status=MagickFalse;
2368 }
2369 }
2370 transverse_view=DestroyCacheView(transverse_view);
2371 image_view=DestroyCacheView(image_view);
2372 transverse_image->type=image->type;
2373 page=transverse_image->page;
2374 Swap(page.width,page.height);
2375 Swap(page.x,page.y);
2376 if (page.width != 0)
2377 page.x=(ssize_t) page.width-(ssize_t) transverse_image->columns-page.x;
2378 if (page.height != 0)
2379 page.y=(ssize_t) page.height-(ssize_t) transverse_image->rows-page.y;
2380 transverse_image->page=page;
2381 if (status == MagickFalse)
2382 transverse_image=DestroyImage(transverse_image);
2383 return(transverse_image);
2384}
2385
2386/*
2387%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2388% %
2389% %
2390% %
2391% T r i m I m a g e %
2392% %
2393% %
2394% %
2395%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2396%
2397% TrimImage() trims pixels from the image edges. It allocates the memory
2398% necessary for the new Image structure and returns a pointer to the new
2399% image.
2400%
2401% The format of the TrimImage method is:
2402%
2403% Image *TrimImage(const Image *image,ExceptionInfo *exception)
2404%
2405% A description of each parameter follows:
2406%
2407% o image: the image.
2408%
2409% o exception: return any errors or warnings in this structure.
2410%
2411*/
2412MagickExport Image *TrimImage(const Image *image,ExceptionInfo *exception)
2413{
2414 const char
2415 *artifact;
2416
2417 Image
2418 *trim_image;
2419
2421 geometry,
2422 page;
2423
2424 assert(image != (const Image *) NULL);
2425 assert(image->signature == MagickCoreSignature);
2426 if (IsEventLogging() != MagickFalse)
2427 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2428 geometry=GetImageBoundingBox(image,exception);
2429 if ((geometry.width == 0) || (geometry.height == 0))
2430 {
2431 Image
2432 *crop_image;
2433
2434 crop_image=CloneImage(image,1,1,MagickTrue,exception);
2435 if (crop_image == (Image *) NULL)
2436 return((Image *) NULL);
2437 crop_image->background_color.alpha_trait=BlendPixelTrait;
2438 crop_image->background_color.alpha=(MagickRealType) TransparentAlpha;
2439 (void) SetImageBackgroundColor(crop_image,exception);
2440 crop_image->page=image->page;
2441 crop_image->page.x=(-1);
2442 crop_image->page.y=(-1);
2443 return(crop_image);
2444 }
2445 page=geometry;
2446 artifact=GetImageArtifact(image,"trim:minSize");
2447 if (artifact != (const char *) NULL)
2448 (void) ParseAbsoluteGeometry(artifact,&page);
2449 if ((geometry.width < page.width) && (geometry.height < page.height))
2450 {
2451 /*
2452 Limit trim to a minimum size.
2453 */
2454 switch (image->gravity)
2455 {
2456 case CenterGravity:
2457 {
2458 geometry.x-=((ssize_t) page.width-(ssize_t) geometry.width)/2;
2459 geometry.y-=((ssize_t) page.height-(ssize_t) geometry.height)/2;
2460 break;
2461 }
2462 case NorthWestGravity:
2463 {
2464 geometry.x-=((ssize_t) page.width-(ssize_t) geometry.width);
2465 geometry.y-=((ssize_t) page.height-(ssize_t) geometry.height);
2466 break;
2467 }
2468 case NorthGravity:
2469 {
2470 geometry.x-=((ssize_t) page.width-(ssize_t) geometry.width)/2;
2471 geometry.y-=((ssize_t) page.height-(ssize_t) geometry.height);
2472 break;
2473 }
2474 case NorthEastGravity:
2475 {
2476 geometry.y-=((ssize_t) page.height-(ssize_t) geometry.height);
2477 break;
2478 }
2479 case EastGravity:
2480 {
2481 geometry.y-=((ssize_t) page.height-(ssize_t) geometry.height)/2;
2482 break;
2483 }
2484 case SouthEastGravity:
2485 break;
2486 case SouthGravity:
2487 {
2488 geometry.x-=((ssize_t) page.width-(ssize_t) geometry.width)/2;
2489 break;
2490 }
2491 case SouthWestGravity:
2492 {
2493 geometry.x-=((ssize_t) page.width-(ssize_t) geometry.width);
2494 break;
2495 }
2496 case WestGravity:
2497 {
2498 geometry.x-=((ssize_t) page.width-(ssize_t) geometry.width);
2499 geometry.y-=((ssize_t) page.height-(ssize_t) geometry.height)/2;
2500 break;
2501 }
2502 default:
2503 break;
2504 }
2505 geometry.width=page.width;
2506 geometry.height=page.height;
2507 }
2508 geometry.x+=image->page.x;
2509 geometry.y+=image->page.y;
2510 trim_image=CropImage(image,&geometry,exception);
2511 if (trim_image != (Image *) NULL)
2512 Update8BIMClipPath(trim_image,image->columns,image->rows,&geometry);
2513 return(trim_image);
2514}