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