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