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