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