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