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