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