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