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=(size_t) ((ssize_t) page.width+page.x-bounding_box.x);
619 page.x=0;
620 }
621 else
622 {
623 page.width=(size_t) ((ssize_t) page.width-(bounding_box.x-page.x));
624 page.x-=bounding_box.x;
625 if (page.x < 0)
626 page.x=0;
627 }
628 if ((page.y < 0) && (bounding_box.y >= 0))
629 {
630 page.height=(size_t) ((ssize_t) page.height+page.y-bounding_box.y);
631 page.y=0;
632 }
633 else
634 {
635 page.height=(size_t) ((ssize_t) page.height-(bounding_box.y-page.y));
636 page.y-=bounding_box.y;
637 if (page.y < 0)
638 page.y=0;
639 }
640 if ((page.x+(ssize_t) page.width) > (ssize_t) image->columns)
641 page.width=(size_t) ((ssize_t) image->columns-page.x);
642 if ((geometry->width != 0) && (page.width > geometry->width))
643 page.width=geometry->width;
644 if ((page.y+(ssize_t) page.height) > (ssize_t) image->rows)
645 page.height=(size_t) ((ssize_t) image->rows-page.y);
646 if ((geometry->height != 0) && (page.height > geometry->height))
647 page.height=geometry->height;
648 bounding_box.x+=page.x;
649 bounding_box.y+=page.y;
650 if ((page.width == 0) || (page.height == 0))
651 {
652 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
653 "GeometryDoesNotContainImage","`%s'",image->filename);
654 return((Image *) NULL);
655 }
656 /*
657 Initialize crop image attributes.
658 */
659 crop_image=CloneImage(image,page.width,page.height,MagickTrue,exception);
660 if (crop_image == (Image *) NULL)
661 return((Image *) NULL);
662 crop_image->page.width=image->page.width;
663 crop_image->page.height=image->page.height;
664 offset.x=bounding_box.x+(ssize_t) bounding_box.width;
665 offset.y=bounding_box.y+(ssize_t) bounding_box.height;
666 if ((offset.x > (ssize_t) image->page.width) ||
667 (offset.y > (ssize_t) image->page.height))
668 {
669 crop_image->page.width=bounding_box.width;
670 crop_image->page.height=bounding_box.height;
671 }
672 crop_image->page.x=bounding_box.x;
673 crop_image->page.y=bounding_box.y;
674 /*
675 Crop image.
676 */
677 status=MagickTrue;
678 progress=0;
679 image_view=AcquireVirtualCacheView(image,exception);
680 crop_view=AcquireAuthenticCacheView(crop_image,exception);
681#if defined(MAGICKCORE_OPENMP_SUPPORT)
682 #pragma omp parallel for schedule(static) shared(status) \
683 magick_number_threads(image,crop_image,crop_image->rows,2)
684#endif
685 for (y=0; y < (ssize_t) crop_image->rows; y++)
686 {
687 const Quantum
688 *magick_restrict p;
689
690 Quantum
691 *magick_restrict q;
692
693 ssize_t
694 x;
695
696 if (status == MagickFalse)
697 continue;
698 p=GetCacheViewVirtualPixels(image_view,page.x,page.y+y,crop_image->columns,
699 1,exception);
700 q=QueueCacheViewAuthenticPixels(crop_view,0,y,crop_image->columns,1,
701 exception);
702 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
703 {
704 status=MagickFalse;
705 continue;
706 }
707 for (x=0; x < (ssize_t) crop_image->columns; x++)
708 {
709 ssize_t
710 i;
711
712 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
713 {
714 PixelChannel channel = GetPixelChannelChannel(image,i);
715 PixelTrait traits = GetPixelChannelTraits(image,channel);
716 PixelTrait crop_traits=GetPixelChannelTraits(crop_image,channel);
717 if ((traits == UndefinedPixelTrait) ||
718 (crop_traits == UndefinedPixelTrait))
719 continue;
720 SetPixelChannel(crop_image,channel,p[i],q);
721 }
722 p+=(ptrdiff_t) GetPixelChannels(image);
723 q+=(ptrdiff_t) GetPixelChannels(crop_image);
724 }
725 if (SyncCacheViewAuthenticPixels(crop_view,exception) == MagickFalse)
726 status=MagickFalse;
727 if (image->progress_monitor != (MagickProgressMonitor) NULL)
728 {
729 MagickBooleanType
730 proceed;
731
732#if defined(MAGICKCORE_OPENMP_SUPPORT)
733 #pragma omp atomic
734#endif
735 progress++;
736 proceed=SetImageProgress(image,CropImageTag,progress,image->rows);
737 if (proceed == MagickFalse)
738 status=MagickFalse;
739 }
740 }
741 crop_view=DestroyCacheView(crop_view);
742 image_view=DestroyCacheView(image_view);
743 crop_image->type=image->type;
744 if (status == MagickFalse)
745 crop_image=DestroyImage(crop_image);
746 return(crop_image);
747}
748
749/*
750%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
751% %
752% %
753% %
754% C r o p I m a g e T o T i l e s %
755% %
756% %
757% %
758%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
759%
760% CropImageToTiles() crops a single image, into a possible list of tiles.
761% This may include a single sub-region of the image. This basically applies
762% all the normal geometry flags for Crop.
763%
764% Image *CropImageToTiles(const Image *image,
765% const RectangleInfo *crop_geometry, ExceptionInfo *exception)
766%
767% A description of each parameter follows:
768%
769% o image: the image The transformed image is returned as this parameter.
770%
771% o crop_geometry: A crop geometry string.
772%
773% o exception: return any errors or warnings in this structure.
774%
775*/
776
777static inline ssize_t PixelRoundOffset(double x)
778{
779 /*
780 Round the fraction to nearest integer.
781 */
782 if ((x-floor(x)) < (ceil(x)-x))
783 return(CastDoubleToLong(floor(x)));
784 return(CastDoubleToLong(ceil(x)));
785}
786
787MagickExport Image *CropImageToTiles(const Image *image,
788 const char *crop_geometry,ExceptionInfo *exception)
789{
790 Image
791 *next,
792 *crop_image;
793
794 MagickStatusType
795 flags;
796
798 geometry;
799
800 assert(image != (Image *) NULL);
801 assert(image->signature == MagickCoreSignature);
802 if (IsEventLogging() != MagickFalse)
803 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
804 flags=ParseGravityGeometry(image,crop_geometry,&geometry,exception);
805 if ((flags & AreaValue) != 0)
806 {
808 delta,
809 offset;
810
812 crop;
813
814 size_t
815 height,
816 width;
817
818 /*
819 Crop into NxM tiles (@ flag).
820 */
821 crop_image=NewImageList();
822 width=image->columns;
823 height=image->rows;
824 if (geometry.width == 0)
825 geometry.width=1;
826 if (geometry.height == 0)
827 geometry.height=1;
828 if ((flags & AspectValue) == 0)
829 {
830 width=(size_t) ((ssize_t) width-(geometry.x < 0 ? -1 : 1)*geometry.x);
831 height=(size_t) ((ssize_t) height-(geometry.y < 0 ? -1 : 1)*
832 geometry.y);
833 }
834 else
835 {
836 width=(size_t) ((ssize_t) width+(geometry.x < 0 ? -1 : 1)*geometry.x);
837 height=(size_t) ((ssize_t) height+(geometry.y < 0 ? -1 : 1)*
838 geometry.y);
839 }
840 delta.x=(double) width/geometry.width;
841 delta.y=(double) height/geometry.height;
842 if (delta.x < 1.0)
843 delta.x=1.0;
844 if (delta.y < 1.0)
845 delta.y=1.0;
846 for (offset.y=0; offset.y < (double) height; )
847 {
848 if ((flags & AspectValue) == 0)
849 {
850 crop.y=PixelRoundOffset((double) (offset.y-
851 (geometry.y > 0 ? 0 : geometry.y)));
852 offset.y+=delta.y; /* increment now to find width */
853 crop.height=(size_t) PixelRoundOffset((double) (offset.y+
854 (geometry.y < 0 ? 0 : geometry.y)));
855 }
856 else
857 {
858 crop.y=PixelRoundOffset((double) (offset.y-
859 (geometry.y > 0 ? geometry.y : 0)));
860 offset.y+=delta.y; /* increment now to find width */
861 crop.height=(size_t) PixelRoundOffset((double)
862 (offset.y+(geometry.y < -1 ? geometry.y : 0)));
863 }
864 crop.height=(size_t) ((ssize_t) crop.height-crop.y);
865 crop.y+=image->page.y;
866 for (offset.x=0; offset.x < (double) width; )
867 {
868 if ((flags & AspectValue) == 0)
869 {
870 crop.x=PixelRoundOffset((double) (offset.x-
871 (geometry.x > 0 ? 0 : geometry.x)));
872 offset.x+=delta.x; /* increment now to find height */
873 crop.width=(size_t) PixelRoundOffset((double) (offset.x+
874 (geometry.x < 0 ? 0 : geometry.x)));
875 }
876 else
877 {
878 crop.x=PixelRoundOffset((double) (offset.x-
879 (geometry.x > 0 ? geometry.x : 0)));
880 offset.x+=delta.x; /* increment now to find height */
881 crop.width=(size_t) PixelRoundOffset((double) (offset.x+
882 (geometry.x < 0 ? geometry.x : 0)));
883 }
884 crop.width=(size_t) ((ssize_t) crop.width-crop.x);
885 crop.x+=image->page.x;
886 next=CropImage(image,&crop,exception);
887 if (next != (Image *) NULL)
888 AppendImageToList(&crop_image,next);
889 }
890 }
891 ClearMagickException(exception);
892 return(crop_image);
893 }
894 if (((geometry.width == 0) && (geometry.height == 0)) ||
895 ((flags & XValue) != 0) || ((flags & YValue) != 0))
896 {
897 /*
898 Crop a single region at +X+Y.
899 */
900 crop_image=CropImage(image,&geometry,exception);
901 if ((crop_image != (Image *) NULL) && ((flags & AspectValue) != 0))
902 {
903 crop_image->page.width=geometry.width;
904 crop_image->page.height=geometry.height;
905 crop_image->page.x-=geometry.x;
906 crop_image->page.y-=geometry.y;
907 }
908 return(crop_image);
909 }
910 if ((image->columns > geometry.width) || (image->rows > geometry.height))
911 {
913 page;
914
915 size_t
916 height,
917 width;
918
919 ssize_t
920 x,
921 y;
922
923 /*
924 Crop into tiles of fixed size WxH.
925 */
926 page=image->page;
927 if (page.width == 0)
928 page.width=image->columns;
929 if (page.height == 0)
930 page.height=image->rows;
931 width=geometry.width;
932 if (width == 0)
933 width=page.width;
934 height=geometry.height;
935 if (height == 0)
936 height=page.height;
937 next=(Image *) NULL;
938 crop_image=NewImageList();
939 for (y=0; y < (ssize_t) page.height; y+=(ssize_t) height)
940 {
941 for (x=0; x < (ssize_t) page.width; x+=(ssize_t) width)
942 {
943 geometry.width=width;
944 geometry.height=height;
945 geometry.x=x;
946 geometry.y=y;
947 next=CropImage(image,&geometry,exception);
948 if (next == (Image *) NULL)
949 break;
950 AppendImageToList(&crop_image,next);
951 }
952 if (next == (Image *) NULL)
953 break;
954 }
955 return(crop_image);
956 }
957 return(CloneImage(image,0,0,MagickTrue,exception));
958}
959
960/*
961%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
962% %
963% %
964% %
965% E x c e r p t I m a g e %
966% %
967% %
968% %
969%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
970%
971% ExcerptImage() returns a excerpt of the image as defined by the geometry.
972%
973% The format of the ExcerptImage method is:
974%
975% Image *ExcerptImage(const Image *image,const RectangleInfo *geometry,
976% ExceptionInfo *exception)
977%
978% A description of each parameter follows:
979%
980% o image: the image.
981%
982% o geometry: Define the region of the image to extend with members
983% x, y, width, and height.
984%
985% o exception: return any errors or warnings in this structure.
986%
987*/
988MagickExport Image *ExcerptImage(const Image *image,
989 const RectangleInfo *geometry,ExceptionInfo *exception)
990{
991#define ExcerptImageTag "Excerpt/Image"
992
994 *excerpt_view,
995 *image_view;
996
997 Image
998 *excerpt_image;
999
1000 MagickBooleanType
1001 status;
1002
1003 MagickOffsetType
1004 progress;
1005
1006 ssize_t
1007 y;
1008
1009 /*
1010 Allocate excerpt image.
1011 */
1012 assert(image != (const Image *) NULL);
1013 assert(image->signature == MagickCoreSignature);
1014 assert(geometry != (const RectangleInfo *) NULL);
1015 assert(exception != (ExceptionInfo *) NULL);
1016 assert(exception->signature == MagickCoreSignature);
1017 if (IsEventLogging() != MagickFalse)
1018 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1019 excerpt_image=CloneImage(image,geometry->width,geometry->height,MagickTrue,
1020 exception);
1021 if (excerpt_image == (Image *) NULL)
1022 return((Image *) NULL);
1023 /*
1024 Excerpt each row.
1025 */
1026 status=MagickTrue;
1027 progress=0;
1028 image_view=AcquireVirtualCacheView(image,exception);
1029 excerpt_view=AcquireAuthenticCacheView(excerpt_image,exception);
1030#if defined(MAGICKCORE_OPENMP_SUPPORT)
1031 #pragma omp parallel for schedule(static) shared(progress,status) \
1032 magick_number_threads(image,excerpt_image,excerpt_image->rows,2)
1033#endif
1034 for (y=0; y < (ssize_t) excerpt_image->rows; y++)
1035 {
1036 const Quantum
1037 *magick_restrict p;
1038
1039 Quantum
1040 *magick_restrict q;
1041
1042 ssize_t
1043 x;
1044
1045 if (status == MagickFalse)
1046 continue;
1047 p=GetCacheViewVirtualPixels(image_view,geometry->x,geometry->y+y,
1048 geometry->width,1,exception);
1049 q=GetCacheViewAuthenticPixels(excerpt_view,0,y,excerpt_image->columns,1,
1050 exception);
1051 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1052 {
1053 status=MagickFalse;
1054 continue;
1055 }
1056 for (x=0; x < (ssize_t) excerpt_image->columns; x++)
1057 {
1058 ssize_t
1059 i;
1060
1061 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1062 {
1063 PixelChannel channel = GetPixelChannelChannel(image,i);
1064 PixelTrait traits = GetPixelChannelTraits(image,channel);
1065 PixelTrait excerpt_traits=GetPixelChannelTraits(excerpt_image,channel);
1066 if ((traits == UndefinedPixelTrait) ||
1067 (excerpt_traits == UndefinedPixelTrait))
1068 continue;
1069 SetPixelChannel(excerpt_image,channel,p[i],q);
1070 }
1071 p+=(ptrdiff_t) GetPixelChannels(image);
1072 q+=(ptrdiff_t) GetPixelChannels(excerpt_image);
1073 }
1074 if (SyncCacheViewAuthenticPixels(excerpt_view,exception) == MagickFalse)
1075 status=MagickFalse;
1076 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1077 {
1078 MagickBooleanType
1079 proceed;
1080
1081#if defined(MAGICKCORE_OPENMP_SUPPORT)
1082 #pragma omp atomic
1083#endif
1084 progress++;
1085 proceed=SetImageProgress(image,ExcerptImageTag,progress,image->rows);
1086 if (proceed == MagickFalse)
1087 status=MagickFalse;
1088 }
1089 }
1090 excerpt_view=DestroyCacheView(excerpt_view);
1091 image_view=DestroyCacheView(image_view);
1092 excerpt_image->type=image->type;
1093 if (status == MagickFalse)
1094 excerpt_image=DestroyImage(excerpt_image);
1095 return(excerpt_image);
1096}
1097
1098/*
1099%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1100% %
1101% %
1102% %
1103% E x t e n t I m a g e %
1104% %
1105% %
1106% %
1107%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1108%
1109% ExtentImage() extends the image as defined by the geometry, gravity, and
1110% image background color. Set the (x,y) offset of the geometry to move the
1111% original image relative to the extended image.
1112%
1113% The format of the ExtentImage method is:
1114%
1115% Image *ExtentImage(const Image *image,const RectangleInfo *geometry,
1116% ExceptionInfo *exception)
1117%
1118% A description of each parameter follows:
1119%
1120% o image: the image.
1121%
1122% o geometry: Define the region of the image to extend with members
1123% x, y, width, and height.
1124%
1125% o exception: return any errors or warnings in this structure.
1126%
1127*/
1128MagickExport Image *ExtentImage(const Image *image,
1129 const RectangleInfo *geometry,ExceptionInfo *exception)
1130{
1131 Image
1132 *extent_image;
1133
1134 MagickBooleanType
1135 status;
1136
1137 /*
1138 Allocate extent image.
1139 */
1140 assert(image != (const Image *) NULL);
1141 assert(image->signature == MagickCoreSignature);
1142 assert(geometry != (const RectangleInfo *) NULL);
1143 assert(exception != (ExceptionInfo *) NULL);
1144 assert(exception->signature == MagickCoreSignature);
1145 if (IsEventLogging() != MagickFalse)
1146 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1147 extent_image=CloneImage(image,geometry->width,geometry->height,MagickTrue,
1148 exception);
1149 if (extent_image == (Image *) NULL)
1150 return((Image *) NULL);
1151 status=SetImageBackgroundColor(extent_image,exception);
1152 if (status == MagickFalse)
1153 {
1154 extent_image=DestroyImage(extent_image);
1155 return((Image *) NULL);
1156 }
1157 DisableCompositeClampUnlessSpecified(extent_image);
1158 status=CompositeImage(extent_image,image,image->compose,MagickTrue,
1159 -geometry->x,-geometry->y,exception);
1160 if (status != MagickFalse)
1161 Update8BIMClipPath(extent_image,image->columns,image->rows,geometry);
1162 return(extent_image);
1163}
1164
1165/*
1166%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1167% %
1168% %
1169% %
1170% F l i p I m a g e %
1171% %
1172% %
1173% %
1174%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1175%
1176% FlipImage() creates a vertical mirror image by reflecting the pixels
1177% around the central x-axis.
1178%
1179% The format of the FlipImage method is:
1180%
1181% Image *FlipImage(const Image *image,ExceptionInfo *exception)
1182%
1183% A description of each parameter follows:
1184%
1185% o image: the image.
1186%
1187% o exception: return any errors or warnings in this structure.
1188%
1189*/
1190MagickExport Image *FlipImage(const Image *image,ExceptionInfo *exception)
1191{
1192#define FlipImageTag "Flip/Image"
1193
1194 CacheView
1195 *flip_view,
1196 *image_view;
1197
1198 Image
1199 *flip_image;
1200
1201 MagickBooleanType
1202 status;
1203
1204 MagickOffsetType
1205 progress;
1206
1208 page;
1209
1210 ssize_t
1211 y;
1212
1213 assert(image != (const Image *) NULL);
1214 assert(image->signature == MagickCoreSignature);
1215 assert(exception != (ExceptionInfo *) NULL);
1216 assert(exception->signature == MagickCoreSignature);
1217 if (IsEventLogging() != MagickFalse)
1218 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1219 flip_image=CloneImage(image,0,0,MagickTrue,exception);
1220 if (flip_image == (Image *) NULL)
1221 return((Image *) NULL);
1222 /*
1223 Flip image.
1224 */
1225 status=MagickTrue;
1226 progress=0;
1227 page=image->page;
1228 image_view=AcquireVirtualCacheView(image,exception);
1229 flip_view=AcquireAuthenticCacheView(flip_image,exception);
1230#if defined(MAGICKCORE_OPENMP_SUPPORT)
1231 #pragma omp parallel for schedule(static) shared(status) \
1232 magick_number_threads(image,flip_image,flip_image->rows,2)
1233#endif
1234 for (y=0; y < (ssize_t) flip_image->rows; y++)
1235 {
1236 const Quantum
1237 *magick_restrict p;
1238
1239 Quantum
1240 *magick_restrict q;
1241
1242 ssize_t
1243 x;
1244
1245 if (status == MagickFalse)
1246 continue;
1247 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1248 q=QueueCacheViewAuthenticPixels(flip_view,0,((ssize_t) flip_image->rows-y-
1249 1),flip_image->columns,1,exception);
1250 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1251 {
1252 status=MagickFalse;
1253 continue;
1254 }
1255 for (x=0; x < (ssize_t) flip_image->columns; x++)
1256 {
1257 ssize_t
1258 i;
1259
1260 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1261 {
1262 PixelChannel channel = GetPixelChannelChannel(image,i);
1263 PixelTrait traits = GetPixelChannelTraits(image,channel);
1264 PixelTrait flip_traits=GetPixelChannelTraits(flip_image,channel);
1265 if ((traits == UndefinedPixelTrait) ||
1266 (flip_traits == UndefinedPixelTrait))
1267 continue;
1268 SetPixelChannel(flip_image,channel,p[i],q);
1269 }
1270 p+=(ptrdiff_t) GetPixelChannels(image);
1271 q+=(ptrdiff_t) GetPixelChannels(flip_image);
1272 }
1273 if (SyncCacheViewAuthenticPixels(flip_view,exception) == MagickFalse)
1274 status=MagickFalse;
1275 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1276 {
1277 MagickBooleanType
1278 proceed;
1279
1280#if defined(MAGICKCORE_OPENMP_SUPPORT)
1281 #pragma omp atomic
1282#endif
1283 progress++;
1284 proceed=SetImageProgress(image,FlipImageTag,progress,image->rows);
1285 if (proceed == MagickFalse)
1286 status=MagickFalse;
1287 }
1288 }
1289 flip_view=DestroyCacheView(flip_view);
1290 image_view=DestroyCacheView(image_view);
1291 flip_image->type=image->type;
1292 if (page.height != 0)
1293 page.y=((ssize_t) page.height-(ssize_t) flip_image->rows-page.y);
1294 flip_image->page=page;
1295 if (status == MagickFalse)
1296 flip_image=DestroyImage(flip_image);
1297 return(flip_image);
1298}
1299
1300/*
1301%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1302% %
1303% %
1304% %
1305% F l o p I m a g e %
1306% %
1307% %
1308% %
1309%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1310%
1311% FlopImage() creates a horizontal mirror image by reflecting the pixels
1312% around the central y-axis.
1313%
1314% The format of the FlopImage method is:
1315%
1316% Image *FlopImage(const Image *image,ExceptionInfo *exception)
1317%
1318% A description of each parameter follows:
1319%
1320% o image: the image.
1321%
1322% o exception: return any errors or warnings in this structure.
1323%
1324*/
1325MagickExport Image *FlopImage(const Image *image,ExceptionInfo *exception)
1326{
1327#define FlopImageTag "Flop/Image"
1328
1329 CacheView
1330 *flop_view,
1331 *image_view;
1332
1333 Image
1334 *flop_image;
1335
1336 MagickBooleanType
1337 status;
1338
1339 MagickOffsetType
1340 progress;
1341
1343 page;
1344
1345 ssize_t
1346 y;
1347
1348 assert(image != (const Image *) NULL);
1349 assert(image->signature == MagickCoreSignature);
1350 assert(exception != (ExceptionInfo *) NULL);
1351 assert(exception->signature == MagickCoreSignature);
1352 if (IsEventLogging() != MagickFalse)
1353 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1354 flop_image=CloneImage(image,0,0,MagickTrue,exception);
1355 if (flop_image == (Image *) NULL)
1356 return((Image *) NULL);
1357 /*
1358 Flop each row.
1359 */
1360 status=MagickTrue;
1361 progress=0;
1362 page=image->page;
1363 image_view=AcquireVirtualCacheView(image,exception);
1364 flop_view=AcquireAuthenticCacheView(flop_image,exception);
1365#if defined(MAGICKCORE_OPENMP_SUPPORT)
1366 #pragma omp parallel for schedule(static) shared(status) \
1367 magick_number_threads(image,flop_image,flop_image->rows,2)
1368#endif
1369 for (y=0; y < (ssize_t) flop_image->rows; y++)
1370 {
1371 const Quantum
1372 *magick_restrict p;
1373
1374 ssize_t
1375 x;
1376
1377 Quantum
1378 *magick_restrict q;
1379
1380 if (status == MagickFalse)
1381 continue;
1382 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1383 q=QueueCacheViewAuthenticPixels(flop_view,0,y,flop_image->columns,1,
1384 exception);
1385 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1386 {
1387 status=MagickFalse;
1388 continue;
1389 }
1390 q+=(ptrdiff_t) GetPixelChannels(flop_image)*flop_image->columns;
1391 for (x=0; x < (ssize_t) flop_image->columns; x++)
1392 {
1393 ssize_t
1394 i;
1395
1396 q-=GetPixelChannels(flop_image);
1397 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1398 {
1399 PixelChannel channel = GetPixelChannelChannel(image,i);
1400 PixelTrait traits = GetPixelChannelTraits(image,channel);
1401 PixelTrait flop_traits=GetPixelChannelTraits(flop_image,channel);
1402 if ((traits == UndefinedPixelTrait) ||
1403 (flop_traits == UndefinedPixelTrait))
1404 continue;
1405 SetPixelChannel(flop_image,channel,p[i],q);
1406 }
1407 p+=(ptrdiff_t) GetPixelChannels(image);
1408 }
1409 if (SyncCacheViewAuthenticPixels(flop_view,exception) == MagickFalse)
1410 status=MagickFalse;
1411 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1412 {
1413 MagickBooleanType
1414 proceed;
1415
1416#if defined(MAGICKCORE_OPENMP_SUPPORT)
1417 #pragma omp atomic
1418#endif
1419 progress++;
1420 proceed=SetImageProgress(image,FlopImageTag,progress,image->rows);
1421 if (proceed == MagickFalse)
1422 status=MagickFalse;
1423 }
1424 }
1425 flop_view=DestroyCacheView(flop_view);
1426 image_view=DestroyCacheView(image_view);
1427 flop_image->type=image->type;
1428 if (page.width != 0)
1429 page.x=((ssize_t) page.width-(ssize_t) flop_image->columns-page.x);
1430 flop_image->page=page;
1431 if (status == MagickFalse)
1432 flop_image=DestroyImage(flop_image);
1433 return(flop_image);
1434}
1435
1436/*
1437%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1438% %
1439% %
1440% %
1441% R o l l I m a g e %
1442% %
1443% %
1444% %
1445%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1446%
1447% RollImage() offsets an image as defined by x_offset and y_offset.
1448%
1449% The format of the RollImage method is:
1450%
1451% Image *RollImage(const Image *image,const ssize_t x_offset,
1452% const ssize_t y_offset,ExceptionInfo *exception)
1453%
1454% A description of each parameter follows:
1455%
1456% o image: the image.
1457%
1458% o x_offset: the number of columns to roll in the horizontal direction.
1459%
1460% o y_offset: the number of rows to roll in the vertical direction.
1461%
1462% o exception: return any errors or warnings in this structure.
1463%
1464*/
1465
1466static MagickBooleanType CopyImageRegion(Image *destination,const Image *source, const size_t columns,const size_t rows,const ssize_t sx,const ssize_t sy,
1467 const ssize_t dx,const ssize_t dy,ExceptionInfo *exception)
1468{
1469 CacheView
1470 *source_view,
1471 *destination_view;
1472
1473 MagickBooleanType
1474 status;
1475
1476 ssize_t
1477 y;
1478
1479 if (columns == 0)
1480 return(MagickTrue);
1481 status=MagickTrue;
1482 source_view=AcquireVirtualCacheView(source,exception);
1483 destination_view=AcquireAuthenticCacheView(destination,exception);
1484#if defined(MAGICKCORE_OPENMP_SUPPORT)
1485 #pragma omp parallel for schedule(static) shared(status) \
1486 magick_number_threads(source,destination,rows,2)
1487#endif
1488 for (y=0; y < (ssize_t) rows; y++)
1489 {
1490 MagickBooleanType
1491 sync;
1492
1493 const Quantum
1494 *magick_restrict p;
1495
1496 Quantum
1497 *magick_restrict q;
1498
1499 ssize_t
1500 x;
1501
1502 /*
1503 Transfer scanline.
1504 */
1505 if (status == MagickFalse)
1506 continue;
1507 p=GetCacheViewVirtualPixels(source_view,sx,sy+y,columns,1,exception);
1508 q=GetCacheViewAuthenticPixels(destination_view,dx,dy+y,columns,1,exception);
1509 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1510 {
1511 status=MagickFalse;
1512 continue;
1513 }
1514 for (x=0; x < (ssize_t) columns; x++)
1515 {
1516 ssize_t
1517 i;
1518
1519 for (i=0; i < (ssize_t) GetPixelChannels(source); i++)
1520 {
1521 PixelChannel channel = GetPixelChannelChannel(source,i);
1522 PixelTrait source_traits=GetPixelChannelTraits(source,channel);
1523 PixelTrait destination_traits=GetPixelChannelTraits(destination,
1524 channel);
1525 if ((source_traits == UndefinedPixelTrait) ||
1526 (destination_traits == UndefinedPixelTrait))
1527 continue;
1528 SetPixelChannel(destination,channel,p[i],q);
1529 }
1530 p+=(ptrdiff_t) GetPixelChannels(source);
1531 q+=(ptrdiff_t) GetPixelChannels(destination);
1532 }
1533 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
1534 if (sync == MagickFalse)
1535 status=MagickFalse;
1536 }
1537 destination_view=DestroyCacheView(destination_view);
1538 source_view=DestroyCacheView(source_view);
1539 return(status);
1540}
1541
1542MagickExport Image *RollImage(const Image *image,const ssize_t x_offset,
1543 const ssize_t y_offset,ExceptionInfo *exception)
1544{
1545#define RollImageTag "Roll/Image"
1546
1547 Image
1548 *roll_image;
1549
1550 MagickStatusType
1551 status;
1552
1554 offset;
1555
1556 /*
1557 Initialize roll image attributes.
1558 */
1559 assert(image != (const Image *) NULL);
1560 assert(image->signature == MagickCoreSignature);
1561 assert(exception != (ExceptionInfo *) NULL);
1562 assert(exception->signature == MagickCoreSignature);
1563 if (IsEventLogging() != MagickFalse)
1564 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1565 roll_image=CloneImage(image,0,0,MagickTrue,exception);
1566 if (roll_image == (Image *) NULL)
1567 return((Image *) NULL);
1568 offset.x=x_offset;
1569 offset.y=y_offset;
1570 while (offset.x < 0)
1571 offset.x+=(ssize_t) image->columns;
1572 while (offset.x >= (ssize_t) image->columns)
1573 offset.x-=(ssize_t) image->columns;
1574 while (offset.y < 0)
1575 offset.y+=(ssize_t) image->rows;
1576 while (offset.y >= (ssize_t) image->rows)
1577 offset.y-=(ssize_t) image->rows;
1578 /*
1579 Roll image.
1580 */
1581 status=CopyImageRegion(roll_image,image,(size_t) offset.x,
1582 (size_t) offset.y,(ssize_t) image->columns-offset.x,(ssize_t) image->rows-
1583 offset.y,0,0,exception);
1584 (void) SetImageProgress(image,RollImageTag,0,3);
1585 status&=(MagickStatusType) CopyImageRegion(roll_image,image,(size_t)
1586 ((ssize_t) image->columns-offset.x),(size_t) offset.y,0,(ssize_t)
1587 image->rows-offset.y,offset.x,0,exception);
1588 (void) SetImageProgress(image,RollImageTag,1,3);
1589 status&=(MagickStatusType) CopyImageRegion(roll_image,image,(size_t)
1590 offset.x,(size_t) ((ssize_t) image->rows-offset.y),(ssize_t)
1591 image->columns-offset.x,0,0,offset.y,exception);
1592 (void) SetImageProgress(image,RollImageTag,2,3);
1593 status&=(MagickStatusType) CopyImageRegion(roll_image,image,(size_t)
1594 ((ssize_t) image->columns-offset.x),(size_t) ((ssize_t) image->rows-
1595 offset.y),0,0,offset.x,offset.y,exception);
1596 (void) SetImageProgress(image,RollImageTag,3,3);
1597 roll_image->type=image->type;
1598 if (status == MagickFalse)
1599 roll_image=DestroyImage(roll_image);
1600 return(roll_image);
1601}
1602
1603/*
1604%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1605% %
1606% %
1607% %
1608% S h a v e I m a g e %
1609% %
1610% %
1611% %
1612%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1613%
1614% ShaveImage() shaves pixels from the image edges. It allocates the memory
1615% necessary for the new Image structure and returns a pointer to the new
1616% image.
1617%
1618% The format of the ShaveImage method is:
1619%
1620% Image *ShaveImage(const Image *image,const RectangleInfo *shave_info,
1621% ExceptionInfo *exception)
1622%
1623% A description of each parameter follows:
1624%
1625% o shave_image: Method ShaveImage returns a pointer to the shaved
1626% image. A null image is returned if there is a memory shortage or
1627% if the image width or height is zero.
1628%
1629% o image: the image.
1630%
1631% o shave_info: Specifies a pointer to a RectangleInfo which defines the
1632% region of the image to crop.
1633%
1634% o exception: return any errors or warnings in this structure.
1635%
1636*/
1637MagickExport Image *ShaveImage(const Image *image,
1638 const RectangleInfo *shave_info,ExceptionInfo *exception)
1639{
1640 Image
1641 *shave_image;
1642
1644 geometry;
1645
1646 assert(image != (const Image *) NULL);
1647 assert(image->signature == MagickCoreSignature);
1648 if (IsEventLogging() != MagickFalse)
1649 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1650 if (((2*shave_info->width) >= image->columns) ||
1651 ((2*shave_info->height) >= image->rows))
1652 ThrowImageException(OptionWarning,"GeometryDoesNotContainImage");
1653 SetGeometry(image,&geometry);
1654 geometry.width-=2*shave_info->width;
1655 geometry.height-=2*shave_info->height;
1656 geometry.x=(ssize_t) shave_info->width+image->page.x;
1657 geometry.y=(ssize_t) shave_info->height+image->page.y;
1658 shave_image=CropImage(image,&geometry,exception);
1659 if (shave_image == (Image *) NULL)
1660 return((Image *) NULL);
1661 shave_image->page.width-=2*shave_info->width;
1662 shave_image->page.height-=2*shave_info->height;
1663 shave_image->page.x-=(ssize_t) shave_info->width;
1664 shave_image->page.y-=(ssize_t) shave_info->height;
1665 return(shave_image);
1666}
1667
1668/*
1669%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1670% %
1671% %
1672% %
1673% S p l i c e I m a g e %
1674% %
1675% %
1676% %
1677%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1678%
1679% SpliceImage() splices a solid color into the image as defined by the
1680% geometry.
1681%
1682% The format of the SpliceImage method is:
1683%
1684% Image *SpliceImage(const Image *image,const RectangleInfo *geometry,
1685% ExceptionInfo *exception)
1686%
1687% A description of each parameter follows:
1688%
1689% o image: the image.
1690%
1691% o geometry: Define the region of the image to splice with members
1692% x, y, width, and height.
1693%
1694% o exception: return any errors or warnings in this structure.
1695%
1696*/
1697MagickExport Image *SpliceImage(const Image *image,
1698 const RectangleInfo *geometry,ExceptionInfo *exception)
1699{
1700#define SpliceImageTag "Splice/Image"
1701
1702 CacheView
1703 *image_view,
1704 *splice_view;
1705
1706 Image
1707 *splice_image;
1708
1709 MagickBooleanType
1710 status;
1711
1712 MagickOffsetType
1713 progress;
1714
1716 splice_geometry;
1717
1718 ssize_t
1719 columns,
1720 y;
1721
1722 /*
1723 Allocate splice image.
1724 */
1725 assert(image != (const Image *) NULL);
1726 assert(image->signature == MagickCoreSignature);
1727 if (IsEventLogging() != MagickFalse)
1728 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1729 assert(geometry != (const RectangleInfo *) NULL);
1730 assert(exception != (ExceptionInfo *) NULL);
1731 assert(exception->signature == MagickCoreSignature);
1732 splice_geometry=(*geometry);
1733 splice_image=CloneImage(image,image->columns+splice_geometry.width,
1734 image->rows+splice_geometry.height,MagickTrue,exception);
1735 if (splice_image == (Image *) NULL)
1736 return((Image *) NULL);
1737 if (SetImageStorageClass(splice_image,DirectClass,exception) == MagickFalse)
1738 {
1739 splice_image=DestroyImage(splice_image);
1740 return((Image *) NULL);
1741 }
1742 if ((IsPixelInfoGray(&splice_image->background_color) == MagickFalse) &&
1743 (IsGrayColorspace(splice_image->colorspace) != MagickFalse))
1744 (void) SetImageColorspace(splice_image,sRGBColorspace,exception);
1745 if ((splice_image->background_color.alpha_trait != UndefinedPixelTrait) &&
1746 (splice_image->alpha_trait == UndefinedPixelTrait))
1747 (void) SetImageAlpha(splice_image,OpaqueAlpha,exception);
1748 (void) SetImageBackgroundColor(splice_image,exception);
1749 /*
1750 Respect image geometry.
1751 */
1752 switch (image->gravity)
1753 {
1754 default:
1755 case UndefinedGravity:
1756 case NorthWestGravity:
1757 break;
1758 case NorthGravity:
1759 {
1760 splice_geometry.x+=(ssize_t) splice_geometry.width/2;
1761 break;
1762 }
1763 case NorthEastGravity:
1764 {
1765 splice_geometry.x+=(ssize_t) splice_geometry.width;
1766 break;
1767 }
1768 case WestGravity:
1769 {
1770 splice_geometry.y+=(ssize_t) splice_geometry.width/2;
1771 break;
1772 }
1773 case CenterGravity:
1774 {
1775 splice_geometry.x+=(ssize_t) splice_geometry.width/2;
1776 splice_geometry.y+=(ssize_t) splice_geometry.height/2;
1777 break;
1778 }
1779 case EastGravity:
1780 {
1781 splice_geometry.x+=(ssize_t) splice_geometry.width;
1782 splice_geometry.y+=(ssize_t) splice_geometry.height/2;
1783 break;
1784 }
1785 case SouthWestGravity:
1786 {
1787 splice_geometry.y+=(ssize_t) splice_geometry.height;
1788 break;
1789 }
1790 case SouthGravity:
1791 {
1792 splice_geometry.x+=(ssize_t) splice_geometry.width/2;
1793 splice_geometry.y+=(ssize_t) splice_geometry.height;
1794 break;
1795 }
1796 case SouthEastGravity:
1797 {
1798 splice_geometry.x+=(ssize_t) splice_geometry.width;
1799 splice_geometry.y+=(ssize_t) splice_geometry.height;
1800 break;
1801 }
1802 }
1803 /*
1804 Splice image.
1805 */
1806 status=MagickTrue;
1807 progress=0;
1808 columns=MagickMin(splice_geometry.x,(ssize_t) splice_image->columns);
1809 image_view=AcquireVirtualCacheView(image,exception);
1810 splice_view=AcquireAuthenticCacheView(splice_image,exception);
1811#if defined(MAGICKCORE_OPENMP_SUPPORT)
1812 #pragma omp parallel for schedule(static) shared(progress,status) \
1813 magick_number_threads(image,splice_image,(size_t) splice_geometry.y,2)
1814#endif
1815 for (y=0; y < (ssize_t) splice_geometry.y; y++)
1816 {
1817 const Quantum
1818 *magick_restrict p;
1819
1820 ssize_t
1821 x;
1822
1823 Quantum
1824 *magick_restrict q;
1825
1826 if (status == MagickFalse)
1827 continue;
1828 p=GetCacheViewVirtualPixels(image_view,0,y,splice_image->columns,1,
1829 exception);
1830 q=QueueCacheViewAuthenticPixels(splice_view,0,y,splice_image->columns,1,
1831 exception);
1832 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1833 {
1834 status=MagickFalse;
1835 continue;
1836 }
1837 for (x=0; x < columns; x++)
1838 {
1839 ssize_t
1840 i;
1841
1842 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1843 {
1844 PixelChannel channel = GetPixelChannelChannel(image,i);
1845 PixelTrait traits = GetPixelChannelTraits(image,channel);
1846 PixelTrait splice_traits=GetPixelChannelTraits(splice_image,channel);
1847 if ((traits == UndefinedPixelTrait) ||
1848 (splice_traits == UndefinedPixelTrait))
1849 continue;
1850 SetPixelChannel(splice_image,channel,p[i],q);
1851 }
1852 SetPixelRed(splice_image,GetPixelRed(image,p),q);
1853 SetPixelGreen(splice_image,GetPixelGreen(image,p),q);
1854 SetPixelBlue(splice_image,GetPixelBlue(image,p),q);
1855 SetPixelAlpha(splice_image,GetPixelAlpha(image,p),q);
1856 p+=(ptrdiff_t) GetPixelChannels(image);
1857 q+=(ptrdiff_t) GetPixelChannels(splice_image);
1858 }
1859 for ( ; x < (splice_geometry.x+(ssize_t) splice_geometry.width); x++)
1860 q+=(ptrdiff_t) GetPixelChannels(splice_image);
1861 for ( ; x < (ssize_t) splice_image->columns; x++)
1862 {
1863 ssize_t
1864 i;
1865
1866 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1867 {
1868 PixelChannel channel = GetPixelChannelChannel(image,i);
1869 PixelTrait traits = GetPixelChannelTraits(image,channel);
1870 PixelTrait splice_traits=GetPixelChannelTraits(splice_image,channel);
1871 if ((traits == UndefinedPixelTrait) ||
1872 (splice_traits == UndefinedPixelTrait))
1873 continue;
1874 SetPixelChannel(splice_image,channel,p[i],q);
1875 }
1876 SetPixelRed(splice_image,GetPixelRed(image,p),q);
1877 SetPixelGreen(splice_image,GetPixelGreen(image,p),q);
1878 SetPixelBlue(splice_image,GetPixelBlue(image,p),q);
1879 SetPixelAlpha(splice_image,GetPixelAlpha(image,p),q);
1880 p+=(ptrdiff_t) GetPixelChannels(image);
1881 q+=(ptrdiff_t) GetPixelChannels(splice_image);
1882 }
1883 if (SyncCacheViewAuthenticPixels(splice_view,exception) == MagickFalse)
1884 status=MagickFalse;
1885 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1886 {
1887 MagickBooleanType
1888 proceed;
1889
1890#if defined(MAGICKCORE_OPENMP_SUPPORT)
1891 #pragma omp atomic
1892#endif
1893 progress++;
1894 proceed=SetImageProgress(image,SpliceImageTag,progress,
1895 splice_image->rows);
1896 if (proceed == MagickFalse)
1897 status=MagickFalse;
1898 }
1899 }
1900#if defined(MAGICKCORE_OPENMP_SUPPORT)
1901 #pragma omp parallel for schedule(static) shared(progress,status) \
1902 magick_number_threads(image,splice_image,splice_image->rows,2)
1903#endif
1904 for (y=splice_geometry.y+(ssize_t) splice_geometry.height; y < (ssize_t) splice_image->rows; y++)
1905 {
1906 const Quantum
1907 *magick_restrict p;
1908
1909 ssize_t
1910 x;
1911
1912 Quantum
1913 *magick_restrict q;
1914
1915 if (status == MagickFalse)
1916 continue;
1917 if ((y < 0) || (y >= (ssize_t) splice_image->rows))
1918 continue;
1919 p=GetCacheViewVirtualPixels(image_view,0,y-(ssize_t) splice_geometry.height,
1920 splice_image->columns,1,exception);
1921 q=QueueCacheViewAuthenticPixels(splice_view,0,y,splice_image->columns,1,
1922 exception);
1923 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1924 {
1925 status=MagickFalse;
1926 continue;
1927 }
1928 for (x=0; x < columns; x++)
1929 {
1930 ssize_t
1931 i;
1932
1933 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1934 {
1935 PixelChannel channel = GetPixelChannelChannel(image,i);
1936 PixelTrait traits = GetPixelChannelTraits(image,channel);
1937 PixelTrait splice_traits=GetPixelChannelTraits(splice_image,channel);
1938 if ((traits == UndefinedPixelTrait) ||
1939 (splice_traits == UndefinedPixelTrait))
1940 continue;
1941 SetPixelChannel(splice_image,channel,p[i],q);
1942 }
1943 SetPixelRed(splice_image,GetPixelRed(image,p),q);
1944 SetPixelGreen(splice_image,GetPixelGreen(image,p),q);
1945 SetPixelBlue(splice_image,GetPixelBlue(image,p),q);
1946 SetPixelAlpha(splice_image,GetPixelAlpha(image,p),q);
1947 p+=(ptrdiff_t) GetPixelChannels(image);
1948 q+=(ptrdiff_t) GetPixelChannels(splice_image);
1949 }
1950 for ( ; x < (splice_geometry.x+(ssize_t) splice_geometry.width); x++)
1951 q+=(ptrdiff_t) GetPixelChannels(splice_image);
1952 for ( ; x < (ssize_t) splice_image->columns; x++)
1953 {
1954 ssize_t
1955 i;
1956
1957 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1958 {
1959 PixelChannel channel = GetPixelChannelChannel(image,i);
1960 PixelTrait traits = GetPixelChannelTraits(image,channel);
1961 PixelTrait splice_traits=GetPixelChannelTraits(splice_image,channel);
1962 if ((traits == UndefinedPixelTrait) ||
1963 (splice_traits == UndefinedPixelTrait))
1964 continue;
1965 SetPixelChannel(splice_image,channel,p[i],q);
1966 }
1967 SetPixelRed(splice_image,GetPixelRed(image,p),q);
1968 SetPixelGreen(splice_image,GetPixelGreen(image,p),q);
1969 SetPixelBlue(splice_image,GetPixelBlue(image,p),q);
1970 SetPixelAlpha(splice_image,GetPixelAlpha(image,p),q);
1971 p+=(ptrdiff_t) GetPixelChannels(image);
1972 q+=(ptrdiff_t) GetPixelChannels(splice_image);
1973 }
1974 if (SyncCacheViewAuthenticPixels(splice_view,exception) == MagickFalse)
1975 status=MagickFalse;
1976 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1977 {
1978 MagickBooleanType
1979 proceed;
1980
1981#if defined(MAGICKCORE_OPENMP_SUPPORT)
1982 #pragma omp atomic
1983#endif
1984 progress++;
1985 proceed=SetImageProgress(image,SpliceImageTag,progress,
1986 splice_image->rows);
1987 if (proceed == MagickFalse)
1988 status=MagickFalse;
1989 }
1990 }
1991 splice_view=DestroyCacheView(splice_view);
1992 image_view=DestroyCacheView(image_view);
1993 if (status == MagickFalse)
1994 splice_image=DestroyImage(splice_image);
1995 return(splice_image);
1996}
1997
1998/*
1999%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2000% %
2001% %
2002% %
2003% T r a n s f o r m I m a g e %
2004% %
2005% %
2006% %
2007%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2008%
2009% TransformImage() is a convenience method that behaves like ResizeImage() or
2010% CropImage() but accepts scaling and/or cropping information as a region
2011% geometry specification. If the operation fails, the original image handle
2012% is left as is.
2013%
2014% This should only be used for single images.
2015%
2016% This function destroys what it assumes to be a single image list.
2017% If the input image is part of a larger list, all other images in that list
2018% will be simply 'lost', not destroyed.
2019%
2020% Also if the crop generates a list of images only the first image is resized.
2021% And finally if the crop succeeds and the resize failed, you will get a
2022% cropped image, as well as a 'false' or 'failed' report.
2023%
2024% This function and should probably be deprecated in favor of direct calls
2025% to CropImageToTiles() or ResizeImage(), as appropriate.
2026%
2027% The format of the TransformImage method is:
2028%
2029% MagickBooleanType TransformImage(Image **image,const char *crop_geometry,
2030% const char *image_geometry,ExceptionInfo *exception)
2031%
2032% A description of each parameter follows:
2033%
2034% o image: the image The transformed image is returned as this parameter.
2035%
2036% o crop_geometry: A crop geometry string. This geometry defines a
2037% subregion of the image to crop.
2038%
2039% o image_geometry: An image geometry string. This geometry defines the
2040% final size of the image.
2041%
2042% o exception: return any errors or warnings in this structure.
2043%
2044*/
2045MagickPrivate MagickBooleanType TransformImage(Image **image,
2046 const char *crop_geometry,const char *image_geometry,ExceptionInfo *exception)
2047{
2048 Image
2049 *resize_image,
2050 *transform_image;
2051
2053 geometry;
2054
2055 assert(image != (Image **) NULL);
2056 assert((*image)->signature == MagickCoreSignature);
2057 if (IsEventLogging() != MagickFalse)
2058 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
2059 transform_image=(*image);
2060 if (crop_geometry != (const char *) NULL)
2061 {
2062 Image
2063 *crop_image;
2064
2065 /*
2066 Crop image to a user specified size.
2067 */
2068 crop_image=CropImageToTiles(*image,crop_geometry,exception);
2069 if (crop_image == (Image *) NULL)
2070 transform_image=CloneImage(*image,0,0,MagickTrue,exception);
2071 else
2072 {
2073 transform_image=DestroyImage(transform_image);
2074 transform_image=GetFirstImageInList(crop_image);
2075 }
2076 *image=transform_image;
2077 }
2078 if (image_geometry == (const char *) NULL)
2079 return(MagickTrue);
2080 /*
2081 Scale image to a user specified size.
2082 */
2083 (void) ParseRegionGeometry(transform_image,image_geometry,&geometry,
2084 exception);
2085 if ((transform_image->columns == geometry.width) &&
2086 (transform_image->rows == geometry.height))
2087 return(MagickTrue);
2088 resize_image=ResizeImage(transform_image,geometry.width,geometry.height,
2089 transform_image->filter,exception);
2090 if (resize_image == (Image *) NULL)
2091 return(MagickFalse);
2092 transform_image=DestroyImage(transform_image);
2093 transform_image=resize_image;
2094 *image=transform_image;
2095 return(MagickTrue);
2096}
2097
2098/*
2099%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2100% %
2101% %
2102% %
2103% T r a n s p o s e I m a g e %
2104% %
2105% %
2106% %
2107%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2108%
2109% TransposeImage() creates a horizontal mirror image by reflecting the pixels
2110% around the central y-axis while rotating them by 90 degrees.
2111%
2112% The format of the TransposeImage method is:
2113%
2114% Image *TransposeImage(const Image *image,ExceptionInfo *exception)
2115%
2116% A description of each parameter follows:
2117%
2118% o image: the image.
2119%
2120% o exception: return any errors or warnings in this structure.
2121%
2122*/
2123MagickExport Image *TransposeImage(const Image *image,ExceptionInfo *exception)
2124{
2125#define TransposeImageTag "Transpose/Image"
2126
2127 CacheView
2128 *image_view,
2129 *transpose_view;
2130
2131 Image
2132 *transpose_image;
2133
2134 MagickBooleanType
2135 status;
2136
2137 MagickOffsetType
2138 progress;
2139
2141 page;
2142
2143 ssize_t
2144 y;
2145
2146 assert(image != (const Image *) NULL);
2147 assert(image->signature == MagickCoreSignature);
2148 assert(exception != (ExceptionInfo *) NULL);
2149 assert(exception->signature == MagickCoreSignature);
2150 if (IsEventLogging() != MagickFalse)
2151 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2152 transpose_image=CloneImage(image,image->rows,image->columns,MagickTrue,
2153 exception);
2154 if (transpose_image == (Image *) NULL)
2155 return((Image *) NULL);
2156 /*
2157 Transpose image.
2158 */
2159 status=MagickTrue;
2160 progress=0;
2161 image_view=AcquireVirtualCacheView(image,exception);
2162 transpose_view=AcquireAuthenticCacheView(transpose_image,exception);
2163#if defined(MAGICKCORE_OPENMP_SUPPORT)
2164 #pragma omp parallel for schedule(static) shared(progress,status) \
2165 magick_number_threads(image,transpose_image,image->rows,2)
2166#endif
2167 for (y=0; y < (ssize_t) image->rows; y++)
2168 {
2169 const Quantum
2170 *magick_restrict p;
2171
2172 Quantum
2173 *magick_restrict q;
2174
2175 ssize_t
2176 x;
2177
2178 if (status == MagickFalse)
2179 continue;
2180 p=GetCacheViewVirtualPixels(image_view,0,(ssize_t) image->rows-y-1,
2181 image->columns,1,exception);
2182 q=QueueCacheViewAuthenticPixels(transpose_view,(ssize_t) image->rows-y-1,
2183 0,1,transpose_image->rows,exception);
2184 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2185 {
2186 status=MagickFalse;
2187 continue;
2188 }
2189 for (x=0; x < (ssize_t) image->columns; x++)
2190 {
2191 ssize_t
2192 i;
2193
2194 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2195 {
2196 PixelChannel channel = GetPixelChannelChannel(image,i);
2197 PixelTrait traits = GetPixelChannelTraits(image,channel);
2198 PixelTrait transpose_traits=GetPixelChannelTraits(transpose_image,
2199 channel);
2200 if ((traits == UndefinedPixelTrait) ||
2201 (transpose_traits == UndefinedPixelTrait))
2202 continue;
2203 SetPixelChannel(transpose_image,channel,p[i],q);
2204 }
2205 p+=(ptrdiff_t) GetPixelChannels(image);
2206 q+=(ptrdiff_t) GetPixelChannels(transpose_image);
2207 }
2208 if (SyncCacheViewAuthenticPixels(transpose_view,exception) == MagickFalse)
2209 status=MagickFalse;
2210 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2211 {
2212 MagickBooleanType
2213 proceed;
2214
2215#if defined(MAGICKCORE_OPENMP_SUPPORT)
2216 #pragma omp atomic
2217#endif
2218 progress++;
2219 proceed=SetImageProgress(image,TransposeImageTag,progress,image->rows);
2220 if (proceed == MagickFalse)
2221 status=MagickFalse;
2222 }
2223 }
2224 transpose_view=DestroyCacheView(transpose_view);
2225 image_view=DestroyCacheView(image_view);
2226 transpose_image->type=image->type;
2227 page=transpose_image->page;
2228 Swap(page.width,page.height);
2229 Swap(page.x,page.y);
2230 transpose_image->page=page;
2231 if (status == MagickFalse)
2232 transpose_image=DestroyImage(transpose_image);
2233 return(transpose_image);
2234}
2235
2236/*
2237%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2238% %
2239% %
2240% %
2241% T r a n s v e r s e I m a g e %
2242% %
2243% %
2244% %
2245%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2246%
2247% TransverseImage() creates a vertical mirror image by reflecting the pixels
2248% around the central x-axis while rotating them by 270 degrees.
2249%
2250% The format of the TransverseImage method is:
2251%
2252% Image *TransverseImage(const Image *image,ExceptionInfo *exception)
2253%
2254% A description of each parameter follows:
2255%
2256% o image: the image.
2257%
2258% o exception: return any errors or warnings in this structure.
2259%
2260*/
2261MagickExport Image *TransverseImage(const Image *image,ExceptionInfo *exception)
2262{
2263#define TransverseImageTag "Transverse/Image"
2264
2265 CacheView
2266 *image_view,
2267 *transverse_view;
2268
2269 Image
2270 *transverse_image;
2271
2272 MagickBooleanType
2273 status;
2274
2275 MagickOffsetType
2276 progress;
2277
2279 page;
2280
2281 ssize_t
2282 y;
2283
2284 assert(image != (const Image *) NULL);
2285 assert(image->signature == MagickCoreSignature);
2286 assert(exception != (ExceptionInfo *) NULL);
2287 assert(exception->signature == MagickCoreSignature);
2288 if (IsEventLogging() != MagickFalse)
2289 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2290 transverse_image=CloneImage(image,image->rows,image->columns,MagickTrue,
2291 exception);
2292 if (transverse_image == (Image *) NULL)
2293 return((Image *) NULL);
2294 /*
2295 Transverse image.
2296 */
2297 status=MagickTrue;
2298 progress=0;
2299 image_view=AcquireVirtualCacheView(image,exception);
2300 transverse_view=AcquireAuthenticCacheView(transverse_image,exception);
2301#if defined(MAGICKCORE_OPENMP_SUPPORT)
2302 #pragma omp parallel for schedule(static) shared(progress,status) \
2303 magick_number_threads(image,transverse_image,image->rows,2)
2304#endif
2305 for (y=0; y < (ssize_t) image->rows; y++)
2306 {
2307 MagickBooleanType
2308 sync;
2309
2310 const Quantum
2311 *magick_restrict p;
2312
2313 Quantum
2314 *magick_restrict q;
2315
2316 ssize_t
2317 x;
2318
2319 if (status == MagickFalse)
2320 continue;
2321 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
2322 q=QueueCacheViewAuthenticPixels(transverse_view,(ssize_t) image->rows-y-1,
2323 0,1,transverse_image->rows,exception);
2324 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2325 {
2326 status=MagickFalse;
2327 continue;
2328 }
2329 q+=(ptrdiff_t) GetPixelChannels(transverse_image)*image->columns;
2330 for (x=0; x < (ssize_t) image->columns; x++)
2331 {
2332 ssize_t
2333 i;
2334
2335 q-=GetPixelChannels(transverse_image);
2336 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2337 {
2338 PixelChannel channel = GetPixelChannelChannel(image,i);
2339 PixelTrait traits = GetPixelChannelTraits(image,channel);
2340 PixelTrait transverse_traits=GetPixelChannelTraits(transverse_image,
2341 channel);
2342 if ((traits == UndefinedPixelTrait) ||
2343 (transverse_traits == UndefinedPixelTrait))
2344 continue;
2345 SetPixelChannel(transverse_image,channel,p[i],q);
2346 }
2347 p+=(ptrdiff_t) GetPixelChannels(image);
2348 }
2349 sync=SyncCacheViewAuthenticPixels(transverse_view,exception);
2350 if (sync == MagickFalse)
2351 status=MagickFalse;
2352 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2353 {
2354 MagickBooleanType
2355 proceed;
2356
2357#if defined(MAGICKCORE_OPENMP_SUPPORT)
2358 #pragma omp atomic
2359#endif
2360 progress++;
2361 proceed=SetImageProgress(image,TransverseImageTag,progress,image->rows);
2362 if (proceed == MagickFalse)
2363 status=MagickFalse;
2364 }
2365 }
2366 transverse_view=DestroyCacheView(transverse_view);
2367 image_view=DestroyCacheView(image_view);
2368 transverse_image->type=image->type;
2369 page=transverse_image->page;
2370 Swap(page.width,page.height);
2371 Swap(page.x,page.y);
2372 if (page.width != 0)
2373 page.x=(ssize_t) page.width-(ssize_t) transverse_image->columns-page.x;
2374 if (page.height != 0)
2375 page.y=(ssize_t) page.height-(ssize_t) transverse_image->rows-page.y;
2376 transverse_image->page=page;
2377 if (status == MagickFalse)
2378 transverse_image=DestroyImage(transverse_image);
2379 return(transverse_image);
2380}
2381
2382/*
2383%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2384% %
2385% %
2386% %
2387% T r i m I m a g e %
2388% %
2389% %
2390% %
2391%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2392%
2393% TrimImage() trims pixels from the image edges. It allocates the memory
2394% necessary for the new Image structure and returns a pointer to the new
2395% image.
2396%
2397% The format of the TrimImage method is:
2398%
2399% Image *TrimImage(const Image *image,ExceptionInfo *exception)
2400%
2401% A description of each parameter follows:
2402%
2403% o image: the image.
2404%
2405% o exception: return any errors or warnings in this structure.
2406%
2407*/
2408MagickExport Image *TrimImage(const Image *image,ExceptionInfo *exception)
2409{
2410 const char
2411 *artifact;
2412
2413 Image
2414 *trim_image;
2415
2417 geometry,
2418 page;
2419
2420 assert(image != (const Image *) NULL);
2421 assert(image->signature == MagickCoreSignature);
2422 if (IsEventLogging() != MagickFalse)
2423 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2424 geometry=GetImageBoundingBox(image,exception);
2425 if ((geometry.width == 0) || (geometry.height == 0))
2426 {
2427 Image
2428 *crop_image;
2429
2430 crop_image=CloneImage(image,1,1,MagickTrue,exception);
2431 if (crop_image == (Image *) NULL)
2432 return((Image *) NULL);
2433 crop_image->background_color.alpha_trait=BlendPixelTrait;
2434 crop_image->background_color.alpha=(MagickRealType) TransparentAlpha;
2435 (void) SetImageBackgroundColor(crop_image,exception);
2436 crop_image->page=image->page;
2437 crop_image->page.x=(-1);
2438 crop_image->page.y=(-1);
2439 return(crop_image);
2440 }
2441 page=geometry;
2442 artifact=GetImageArtifact(image,"trim:minSize");
2443 if (artifact != (const char *) NULL)
2444 (void) ParseAbsoluteGeometry(artifact,&page);
2445 if ((geometry.width < page.width) && (geometry.height < page.height))
2446 {
2447 /*
2448 Limit trim to a minimum size.
2449 */
2450 switch (image->gravity)
2451 {
2452 case CenterGravity:
2453 {
2454 geometry.x-=((ssize_t) page.width-(ssize_t) geometry.width)/2;
2455 geometry.y-=((ssize_t) page.height-(ssize_t) geometry.height)/2;
2456 break;
2457 }
2458 case NorthWestGravity:
2459 {
2460 geometry.x-=((ssize_t) page.width-(ssize_t) geometry.width);
2461 geometry.y-=((ssize_t) page.height-(ssize_t) geometry.height);
2462 break;
2463 }
2464 case NorthGravity:
2465 {
2466 geometry.x-=((ssize_t) page.width-(ssize_t) geometry.width)/2;
2467 geometry.y-=((ssize_t) page.height-(ssize_t) geometry.height);
2468 break;
2469 }
2470 case NorthEastGravity:
2471 {
2472 geometry.y-=((ssize_t) page.height-(ssize_t) geometry.height);
2473 break;
2474 }
2475 case EastGravity:
2476 {
2477 geometry.y-=((ssize_t) page.height-(ssize_t) geometry.height)/2;
2478 break;
2479 }
2480 case SouthEastGravity:
2481 break;
2482 case SouthGravity:
2483 {
2484 geometry.x-=((ssize_t) page.width-(ssize_t) geometry.width)/2;
2485 break;
2486 }
2487 case SouthWestGravity:
2488 {
2489 geometry.x-=((ssize_t) page.width-(ssize_t) geometry.width);
2490 break;
2491 }
2492 case WestGravity:
2493 {
2494 geometry.x-=((ssize_t) page.width-(ssize_t) geometry.width);
2495 geometry.y-=((ssize_t) page.height-(ssize_t) geometry.height)/2;
2496 break;
2497 }
2498 default:
2499 break;
2500 }
2501 geometry.width=page.width;
2502 geometry.height=page.height;
2503 }
2504 geometry.x+=image->page.x;
2505 geometry.y+=image->page.y;
2506 trim_image=CropImage(image,&geometry,exception);
2507 if (trim_image != (Image *) NULL)
2508 Update8BIMClipPath(trim_image,image->columns,image->rows,&geometry);
2509 return(trim_image);
2510}