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