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