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