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