blob: e38accacaa6e0803273cef7de391978d9acc3ee2 [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
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% John Cristy %
17% July 1992 %
18% %
19% %
cristy45ef08f2012-12-07 13:13:34 +000020% Copyright 1999-2013 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000021% 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% http://www.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*/
cristy4c08aed2011-07-01 19:47:50 +000042#include "MagickCore/studio.h"
43#include "MagickCore/attribute.h"
44#include "MagickCore/cache.h"
45#include "MagickCore/cache-view.h"
46#include "MagickCore/color.h"
47#include "MagickCore/color-private.h"
48#include "MagickCore/colorspace-private.h"
49#include "MagickCore/composite.h"
cristyfa5f6c72013-01-01 14:37:35 +000050#include "MagickCore/distort.h"
cristy4c08aed2011-07-01 19:47:50 +000051#include "MagickCore/draw.h"
52#include "MagickCore/effect.h"
53#include "MagickCore/exception.h"
54#include "MagickCore/exception-private.h"
55#include "MagickCore/geometry.h"
56#include "MagickCore/image.h"
57#include "MagickCore/memory_.h"
58#include "MagickCore/layer.h"
59#include "MagickCore/list.h"
60#include "MagickCore/monitor.h"
61#include "MagickCore/monitor-private.h"
62#include "MagickCore/pixel-accessor.h"
63#include "MagickCore/resource_.h"
64#include "MagickCore/resize.h"
65#include "MagickCore/statistic.h"
66#include "MagickCore/string_.h"
67#include "MagickCore/thread-private.h"
68#include "MagickCore/transform.h"
cristy3ed852e2009-09-05 21:47:34 +000069
70/*
71%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
72% %
73% %
74% %
cristyfa5f6c72013-01-01 14:37:35 +000075% A u t o O r i e n t I m a g e %
76% %
77% %
78% %
79%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
80%
81% AutoOrientImage() adjusts an image so that its orientation is suitable for
82% viewing (i.e. top-left orientation).
83%
84% The format of the AutoOrientImage method is:
85%
86% Image *AutoOrientImage(const Image *image,
87% const OrientationType orientation,ExceptionInfo *exception)
88%
89% A description of each parameter follows:
90%
91% o image: The image.
92%
93% o orientation: Current image orientation.
94%
95% o exception: Return any errors or warnings in this structure.
96%
97*/
98MagickExport Image *AutoOrientImage(const Image *image,
99 const OrientationType orientation,ExceptionInfo *exception)
100{
101 Image
102 *orient_image;
103
104 assert(image != (const Image *) NULL);
105 assert(image->signature == MagickSignature);
106 assert(exception != (ExceptionInfo *) NULL);
107 assert(exception->signature == MagickSignature);
108 orient_image=(Image *) NULL;
109 switch(orientation)
110 {
111 case UndefinedOrientation:
112 case TopLeftOrientation:
113 default:
114 {
115 orient_image=CloneImage(image,0,0,MagickTrue,exception);
116 break;
117 }
118 case TopRightOrientation:
119 {
120 orient_image=FlopImage(image,exception);
121 break;
122 }
123 case BottomRightOrientation:
124 {
125 orient_image=RotateImage(image,180.0,exception);
126 break;
127 }
128 case BottomLeftOrientation:
129 {
130 orient_image=FlipImage(image,exception);
131 break;
132 }
133 case LeftTopOrientation:
134 {
135 orient_image=TransposeImage(image,exception);
136 break;
137 }
138 case RightTopOrientation:
139 {
140 orient_image=RotateImage(image,90.0,exception);
141 break;
142 }
143 case RightBottomOrientation:
144 {
145 orient_image=TransverseImage(image,exception);
146 break;
147 }
148 case LeftBottomOrientation:
149 {
150 orient_image=RotateImage(image,270.0,exception);
151 break;
152 }
153 }
154 if (orient_image != (Image *) NULL)
155 orient_image->orientation=TopLeftOrientation;
156 return(orient_image);
157}
158
159/*
160%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
161% %
162% %
163% %
cristy3ed852e2009-09-05 21:47:34 +0000164% C h o p I m a g e %
165% %
166% %
167% %
168%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
169%
cristy00f95372010-02-13 16:39:29 +0000170% ChopImage() removes a region of an image and collapses the image to occupy
171% the removed portion.
cristy3ed852e2009-09-05 21:47:34 +0000172%
173% The format of the ChopImage method is:
174%
175% Image *ChopImage(const Image *image,const RectangleInfo *chop_info)
176% ExceptionInfo *exception)
177%
178% A description of each parameter follows:
179%
180% o image: the image.
181%
182% o chop_info: Define the region of the image to chop.
183%
184% o exception: return any errors or warnings in this structure.
185%
186*/
187MagickExport Image *ChopImage(const Image *image,const RectangleInfo *chop_info,
188 ExceptionInfo *exception)
189{
190#define ChopImageTag "Chop/Image"
191
cristyc4c8d132010-01-07 01:58:38 +0000192 CacheView
193 *chop_view,
194 *image_view;
195
cristy3ed852e2009-09-05 21:47:34 +0000196 Image
197 *chop_image;
198
cristy3ed852e2009-09-05 21:47:34 +0000199 MagickBooleanType
cristy00f95372010-02-13 16:39:29 +0000200 status;
cristy3ed852e2009-09-05 21:47:34 +0000201
cristyc2b1fb82010-10-25 13:01:28 +0000202 MagickOffsetType
203 progress;
204
cristy3ed852e2009-09-05 21:47:34 +0000205 RectangleInfo
206 extent;
207
cristy9d314ff2011-03-09 01:30:28 +0000208 ssize_t
209 y;
210
cristy3ed852e2009-09-05 21:47:34 +0000211 /*
212 Check chop geometry.
213 */
214 assert(image != (const Image *) NULL);
215 assert(image->signature == MagickSignature);
216 if (image->debug != MagickFalse)
217 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
218 assert(exception != (ExceptionInfo *) NULL);
219 assert(exception->signature == MagickSignature);
220 assert(chop_info != (RectangleInfo *) NULL);
cristybb503372010-05-27 20:51:26 +0000221 if (((chop_info->x+(ssize_t) chop_info->width) < 0) ||
222 ((chop_info->y+(ssize_t) chop_info->height) < 0) ||
223 (chop_info->x > (ssize_t) image->columns) ||
224 (chop_info->y > (ssize_t) image->rows))
cristy3ed852e2009-09-05 21:47:34 +0000225 ThrowImageException(OptionWarning,"GeometryDoesNotContainImage");
226 extent=(*chop_info);
cristybb503372010-05-27 20:51:26 +0000227 if ((extent.x+(ssize_t) extent.width) > (ssize_t) image->columns)
228 extent.width=(size_t) ((ssize_t) image->columns-extent.x);
229 if ((extent.y+(ssize_t) extent.height) > (ssize_t) image->rows)
230 extent.height=(size_t) ((ssize_t) image->rows-extent.y);
cristy3ed852e2009-09-05 21:47:34 +0000231 if (extent.x < 0)
232 {
cristybb503372010-05-27 20:51:26 +0000233 extent.width-=(size_t) (-extent.x);
cristy3ed852e2009-09-05 21:47:34 +0000234 extent.x=0;
235 }
236 if (extent.y < 0)
237 {
cristybb503372010-05-27 20:51:26 +0000238 extent.height-=(size_t) (-extent.y);
cristy3ed852e2009-09-05 21:47:34 +0000239 extent.y=0;
240 }
241 chop_image=CloneImage(image,image->columns-extent.width,image->rows-
242 extent.height,MagickTrue,exception);
243 if (chop_image == (Image *) NULL)
244 return((Image *) NULL);
245 /*
246 Extract chop image.
247 */
cristy00f95372010-02-13 16:39:29 +0000248 status=MagickTrue;
cristyc2b1fb82010-10-25 13:01:28 +0000249 progress=0;
cristy46ff2672012-12-14 15:32:26 +0000250 image_view=AcquireVirtualCacheView(image,exception);
251 chop_view=AcquireAuthenticCacheView(chop_image,exception);
cristy26b64912012-12-16 18:20:09 +0000252#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyd6432472013-01-06 16:56:13 +0000253 #pragma omp parallel for schedule(static,4) shared(status) \
254 magick_threads(image,chop_image,1,1)
cristy09d81172010-10-21 16:15:05 +0000255#endif
cristybb503372010-05-27 20:51:26 +0000256 for (y=0; y < (ssize_t) extent.y; y++)
cristy3ed852e2009-09-05 21:47:34 +0000257 {
cristy4c08aed2011-07-01 19:47:50 +0000258 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +0000259 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000260
cristybb503372010-05-27 20:51:26 +0000261 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000262 x;
263
cristy4c08aed2011-07-01 19:47:50 +0000264 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000265 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000266
cristy00f95372010-02-13 16:39:29 +0000267 if (status == MagickFalse)
268 continue;
cristyc2b1fb82010-10-25 13:01:28 +0000269 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
270 q=QueueCacheViewAuthenticPixels(chop_view,0,y,chop_image->columns,1,
cristy3ed852e2009-09-05 21:47:34 +0000271 exception);
cristy4c08aed2011-07-01 19:47:50 +0000272 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy00f95372010-02-13 16:39:29 +0000273 {
274 status=MagickFalse;
275 continue;
276 }
cristybb503372010-05-27 20:51:26 +0000277 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000278 {
cristybb503372010-05-27 20:51:26 +0000279 if ((x < extent.x) || (x >= (ssize_t) (extent.x+extent.width)))
cristy3ed852e2009-09-05 21:47:34 +0000280 {
cristyd000c802011-09-20 02:03:18 +0000281 register ssize_t
282 i;
283
284 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
285 {
cristy5a23c552013-02-13 14:34:28 +0000286 PixelChannel channel=GetPixelChannelChannel(image,i);
287 PixelTrait traits=GetPixelChannelTraits(image,channel);
288 PixelTrait chop_traits=GetPixelChannelTraits(chop_image,channel);
cristyd000c802011-09-20 02:03:18 +0000289 if ((traits == UndefinedPixelTrait) ||
290 (chop_traits == UndefinedPixelTrait))
291 continue;
cristy0beccfa2011-09-25 20:47:53 +0000292 SetPixelChannel(chop_image,channel,p[i],q);
cristyd000c802011-09-20 02:03:18 +0000293 }
cristyed231572011-07-14 02:18:59 +0000294 q+=GetPixelChannels(chop_image);
cristy3ed852e2009-09-05 21:47:34 +0000295 }
cristyed231572011-07-14 02:18:59 +0000296 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000297 }
298 if (SyncCacheViewAuthenticPixels(chop_view,exception) == MagickFalse)
cristy00f95372010-02-13 16:39:29 +0000299 status=MagickFalse;
cristyc2b1fb82010-10-25 13:01:28 +0000300 if (image->progress_monitor != (MagickProgressMonitor) NULL)
301 {
302 MagickBooleanType
303 proceed;
304
cristy26b64912012-12-16 18:20:09 +0000305#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristya5ab7ad2012-01-21 23:49:58 +0000306 #pragma omp critical (MagickCore_ChopImage)
cristyc2b1fb82010-10-25 13:01:28 +0000307#endif
308 proceed=SetImageProgress(image,ChopImageTag,progress++,image->rows);
309 if (proceed == MagickFalse)
310 status=MagickFalse;
311 }
cristy3ed852e2009-09-05 21:47:34 +0000312 }
313 /*
314 Extract chop image.
315 */
cristy26b64912012-12-16 18:20:09 +0000316#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyd6432472013-01-06 16:56:13 +0000317 #pragma omp parallel for schedule(static,4) shared(progress,status) \
318 magick_threads(image,chop_image,1,1)
cristy09d81172010-10-21 16:15:05 +0000319#endif
cristybb503372010-05-27 20:51:26 +0000320 for (y=0; y < (ssize_t) (image->rows-(extent.y+extent.height)); y++)
cristy3ed852e2009-09-05 21:47:34 +0000321 {
cristy4c08aed2011-07-01 19:47:50 +0000322 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +0000323 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000324
cristybb503372010-05-27 20:51:26 +0000325 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000326 x;
327
cristy4c08aed2011-07-01 19:47:50 +0000328 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000329 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000330
cristy00f95372010-02-13 16:39:29 +0000331 if (status == MagickFalse)
332 continue;
cristyc2b1fb82010-10-25 13:01:28 +0000333 p=GetCacheViewVirtualPixels(image_view,0,extent.y+extent.height+y,
334 image->columns,1,exception);
335 q=QueueCacheViewAuthenticPixels(chop_view,0,extent.y+y,chop_image->columns,
336 1,exception);
cristy4c08aed2011-07-01 19:47:50 +0000337 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy00f95372010-02-13 16:39:29 +0000338 {
339 status=MagickFalse;
340 continue;
341 }
cristybb503372010-05-27 20:51:26 +0000342 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000343 {
cristybb503372010-05-27 20:51:26 +0000344 if ((x < extent.x) || (x >= (ssize_t) (extent.x+extent.width)))
cristy3ed852e2009-09-05 21:47:34 +0000345 {
cristyd000c802011-09-20 02:03:18 +0000346 register ssize_t
347 i;
348
349 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
350 {
cristy5a23c552013-02-13 14:34:28 +0000351 PixelChannel channel=GetPixelChannelChannel(image,i);
352 PixelTrait traits=GetPixelChannelTraits(image,channel);
353 PixelTrait chop_traits=GetPixelChannelTraits(chop_image,channel);
cristyd000c802011-09-20 02:03:18 +0000354 if ((traits == UndefinedPixelTrait) ||
355 (chop_traits == UndefinedPixelTrait))
356 continue;
cristy0beccfa2011-09-25 20:47:53 +0000357 SetPixelChannel(chop_image,channel,p[i],q);
cristyd000c802011-09-20 02:03:18 +0000358 }
cristyed231572011-07-14 02:18:59 +0000359 q+=GetPixelChannels(chop_image);
cristy3ed852e2009-09-05 21:47:34 +0000360 }
cristyed231572011-07-14 02:18:59 +0000361 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000362 }
363 if (SyncCacheViewAuthenticPixels(chop_view,exception) == MagickFalse)
cristy00f95372010-02-13 16:39:29 +0000364 status=MagickFalse;
cristyc2b1fb82010-10-25 13:01:28 +0000365 if (image->progress_monitor != (MagickProgressMonitor) NULL)
366 {
367 MagickBooleanType
368 proceed;
369
cristy26b64912012-12-16 18:20:09 +0000370#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristya5ab7ad2012-01-21 23:49:58 +0000371 #pragma omp critical (MagickCore_ChopImage)
cristyc2b1fb82010-10-25 13:01:28 +0000372#endif
373 proceed=SetImageProgress(image,ChopImageTag,progress++,image->rows);
374 if (proceed == MagickFalse)
375 status=MagickFalse;
376 }
cristy3ed852e2009-09-05 21:47:34 +0000377 }
378 chop_view=DestroyCacheView(chop_view);
379 image_view=DestroyCacheView(image_view);
380 chop_image->type=image->type;
cristy1c2f48d2012-12-14 01:20:55 +0000381 if (status == MagickFalse)
382 chop_image=DestroyImage(chop_image);
cristy3ed852e2009-09-05 21:47:34 +0000383 return(chop_image);
384}
385
386/*
387%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
388% %
389% %
390% %
391+ C o n s o l i d a t e C M Y K I m a g e %
392% %
393% %
394% %
395%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
396%
397% ConsolidateCMYKImage() consolidates separate C, M, Y, and K planes into a
398% single image.
399%
400% The format of the ConsolidateCMYKImage method is:
401%
402% Image *ConsolidateCMYKImage(const Image *image,ExceptionInfo *exception)
403%
404% A description of each parameter follows:
405%
406% o image: the image sequence.
407%
408% o exception: return any errors or warnings in this structure.
409%
410*/
411MagickExport Image *ConsolidateCMYKImages(const Image *images,
412 ExceptionInfo *exception)
413{
cristyc5c6f662010-09-22 14:23:02 +0000414 CacheView
415 *cmyk_view,
416 *image_view;
cristy2224dcd2010-11-15 00:49:30 +0000417
cristy3ed852e2009-09-05 21:47:34 +0000418 Image
419 *cmyk_image,
420 *cmyk_images;
421
cristybb503372010-05-27 20:51:26 +0000422 register ssize_t
cristyd000c802011-09-20 02:03:18 +0000423 j;
cristy3ed852e2009-09-05 21:47:34 +0000424
cristy2224dcd2010-11-15 00:49:30 +0000425 ssize_t
426 y;
427
cristy3ed852e2009-09-05 21:47:34 +0000428 /*
429 Consolidate separate C, M, Y, and K planes into a single image.
430 */
431 assert(images != (Image *) NULL);
432 assert(images->signature == MagickSignature);
433 if (images->debug != MagickFalse)
434 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
435 assert(exception != (ExceptionInfo *) NULL);
436 assert(exception->signature == MagickSignature);
437 cmyk_images=NewImageList();
cristyd000c802011-09-20 02:03:18 +0000438 for (j=0; j < (ssize_t) GetImageListLength(images); j+=4)
cristy3ed852e2009-09-05 21:47:34 +0000439 {
cristyd000c802011-09-20 02:03:18 +0000440 register ssize_t
441 i;
442
cristy3ed852e2009-09-05 21:47:34 +0000443 cmyk_image=CloneImage(images,images->columns,images->rows,MagickTrue,
444 exception);
445 if (cmyk_image == (Image *) NULL)
446 break;
cristy574cc262011-08-05 01:23:58 +0000447 if (SetImageStorageClass(cmyk_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000448 break;
cristy63240882011-08-05 19:05:27 +0000449 (void) SetImageColorspace(cmyk_image,CMYKColorspace,exception);
cristyd000c802011-09-20 02:03:18 +0000450 for (i=0; i < 4; i++)
cristy3ed852e2009-09-05 21:47:34 +0000451 {
cristy46ff2672012-12-14 15:32:26 +0000452 image_view=AcquireVirtualCacheView(images,exception);
453 cmyk_view=AcquireAuthenticCacheView(cmyk_image,exception);
cristyd000c802011-09-20 02:03:18 +0000454 for (y=0; y < (ssize_t) images->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000455 {
cristyd000c802011-09-20 02:03:18 +0000456 register const Quantum
457 *restrict p;
458
459 register ssize_t
460 x;
461
462 register Quantum
463 *restrict q;
464
465 p=GetCacheViewVirtualPixels(image_view,0,y,images->columns,1,exception);
466 q=QueueCacheViewAuthenticPixels(cmyk_view,0,y,cmyk_image->columns,1,
467 exception);
468 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
469 break;
470 for (x=0; x < (ssize_t) images->columns; x++)
471 {
472 Quantum
473 pixel;
474
475 pixel=QuantumRange-GetPixelIntensity(images,p);
476 switch (i)
477 {
478 case 0: SetPixelCyan(cmyk_image,pixel,q); break;
479 case 1: SetPixelMagenta(cmyk_image,pixel,q); break;
480 case 2: SetPixelYellow(cmyk_image,pixel,q); break;
481 case 3: SetPixelBlack(cmyk_image,pixel,q); break;
482 default: break;
483 }
484 p+=GetPixelChannels(images);
485 q+=GetPixelChannels(cmyk_image);
486 }
487 if (SyncCacheViewAuthenticPixels(cmyk_view,exception) == MagickFalse)
488 break;
cristy3ed852e2009-09-05 21:47:34 +0000489 }
cristyd000c802011-09-20 02:03:18 +0000490 cmyk_view=DestroyCacheView(cmyk_view);
491 image_view=DestroyCacheView(image_view);
492 images=GetNextImageInList(images);
493 if (images == (Image *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000494 break;
495 }
cristy3ed852e2009-09-05 21:47:34 +0000496 AppendImageToList(&cmyk_images,cmyk_image);
cristy3ed852e2009-09-05 21:47:34 +0000497 }
498 return(cmyk_images);
499}
500
501/*
502%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
503% %
504% %
505% %
506% C r o p I m a g e %
507% %
508% %
509% %
510%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
511%
512% CropImage() extracts a region of the image starting at the offset defined
anthony9f4f0342011-03-28 11:47:22 +0000513% by geometry. Region must be fully defined, and no special handling of
514% geometry flags is performed.
cristy3ed852e2009-09-05 21:47:34 +0000515%
516% The format of the CropImage method is:
517%
518% Image *CropImage(const Image *image,const RectangleInfo *geometry,
519% ExceptionInfo *exception)
520%
521% A description of each parameter follows:
522%
523% o image: the image.
524%
525% o geometry: Define the region of the image to crop with members
526% x, y, width, and height.
527%
528% o exception: return any errors or warnings in this structure.
529%
530*/
531MagickExport Image *CropImage(const Image *image,const RectangleInfo *geometry,
532 ExceptionInfo *exception)
533{
534#define CropImageTag "Crop/Image"
535
cristyc4c8d132010-01-07 01:58:38 +0000536 CacheView
537 *crop_view,
538 *image_view;
539
cristy3ed852e2009-09-05 21:47:34 +0000540 Image
541 *crop_image;
542
cristy3ed852e2009-09-05 21:47:34 +0000543 MagickBooleanType
544 status;
545
cristybb503372010-05-27 20:51:26 +0000546 MagickOffsetType
547 progress;
548
cristy010d7d12011-08-31 01:02:48 +0000549 OffsetInfo
550 offset;
551
cristy3ed852e2009-09-05 21:47:34 +0000552 RectangleInfo
553 bounding_box,
554 page;
555
cristybb503372010-05-27 20:51:26 +0000556 ssize_t
557 y;
558
cristy3ed852e2009-09-05 21:47:34 +0000559 /*
560 Check crop geometry.
561 */
562 assert(image != (const Image *) NULL);
563 assert(image->signature == MagickSignature);
564 if (image->debug != MagickFalse)
565 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
566 assert(geometry != (const RectangleInfo *) NULL);
567 assert(exception != (ExceptionInfo *) NULL);
568 assert(exception->signature == MagickSignature);
569 bounding_box=image->page;
570 if ((bounding_box.width == 0) || (bounding_box.height == 0))
571 {
572 bounding_box.width=image->columns;
573 bounding_box.height=image->rows;
574 }
575 page=(*geometry);
576 if (page.width == 0)
577 page.width=bounding_box.width;
578 if (page.height == 0)
579 page.height=bounding_box.height;
cristybb503372010-05-27 20:51:26 +0000580 if (((bounding_box.x-page.x) >= (ssize_t) page.width) ||
581 ((bounding_box.y-page.y) >= (ssize_t) page.height) ||
582 ((page.x-bounding_box.x) > (ssize_t) image->columns) ||
583 ((page.y-bounding_box.y) > (ssize_t) image->rows))
cristy3ed852e2009-09-05 21:47:34 +0000584 {
585 /*
586 Crop is not within virtual canvas, return 1 pixel transparent image.
587 */
588 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
cristyefe601c2013-01-05 17:51:12 +0000589 "GeometryDoesNotContainImage","`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000590 crop_image=CloneImage(image,1,1,MagickTrue,exception);
591 if (crop_image == (Image *) NULL)
592 return((Image *) NULL);
cristy4c08aed2011-07-01 19:47:50 +0000593 crop_image->background_color.alpha=(Quantum) TransparentAlpha;
cristy6d94a302013-03-01 00:47:47 +0000594 crop_image->alpha_trait=BlendPixelTrait;
cristyea1a8aa2011-10-20 13:24:06 +0000595 (void) SetImageBackgroundColor(crop_image,exception);
cristy3ed852e2009-09-05 21:47:34 +0000596 crop_image->page=bounding_box;
597 crop_image->page.x=(-1);
598 crop_image->page.y=(-1);
599 if (crop_image->dispose == BackgroundDispose)
600 crop_image->dispose=NoneDispose;
601 return(crop_image);
602 }
603 if ((page.x < 0) && (bounding_box.x >= 0))
604 {
605 page.width+=page.x-bounding_box.x;
606 page.x=0;
607 }
608 else
609 {
610 page.width-=bounding_box.x-page.x;
611 page.x-=bounding_box.x;
612 if (page.x < 0)
613 page.x=0;
614 }
615 if ((page.y < 0) && (bounding_box.y >= 0))
616 {
617 page.height+=page.y-bounding_box.y;
618 page.y=0;
619 }
620 else
621 {
622 page.height-=bounding_box.y-page.y;
623 page.y-=bounding_box.y;
624 if (page.y < 0)
625 page.y=0;
626 }
cristybb503372010-05-27 20:51:26 +0000627 if ((size_t) (page.x+page.width) > image->columns)
cristy3ed852e2009-09-05 21:47:34 +0000628 page.width=image->columns-page.x;
cristy1e4aa462010-02-15 00:04:18 +0000629 if ((geometry->width != 0) && (page.width > geometry->width))
630 page.width=geometry->width;
cristybb503372010-05-27 20:51:26 +0000631 if ((size_t) (page.y+page.height) > image->rows)
cristy3ed852e2009-09-05 21:47:34 +0000632 page.height=image->rows-page.y;
cristy1e4aa462010-02-15 00:04:18 +0000633 if ((geometry->height != 0) && (page.height > geometry->height))
634 page.height=geometry->height;
cristy3ed852e2009-09-05 21:47:34 +0000635 bounding_box.x+=page.x;
636 bounding_box.y+=page.y;
637 if ((page.width == 0) || (page.height == 0))
638 {
639 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
cristyefe601c2013-01-05 17:51:12 +0000640 "GeometryDoesNotContainImage","`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000641 return((Image *) NULL);
642 }
643 /*
644 Initialize crop image attributes.
645 */
646 crop_image=CloneImage(image,page.width,page.height,MagickTrue,exception);
647 if (crop_image == (Image *) NULL)
648 return((Image *) NULL);
649 crop_image->page.width=image->page.width;
650 crop_image->page.height=image->page.height;
cristy010d7d12011-08-31 01:02:48 +0000651 offset.x=(ssize_t) (bounding_box.x+bounding_box.width);
652 offset.y=(ssize_t) (bounding_box.y+bounding_box.height);
653 if ((offset.x > (ssize_t) image->page.width) ||
654 (offset.y > (ssize_t) image->page.height))
cristy3ed852e2009-09-05 21:47:34 +0000655 {
656 crop_image->page.width=bounding_box.width;
657 crop_image->page.height=bounding_box.height;
658 }
659 crop_image->page.x=bounding_box.x;
660 crop_image->page.y=bounding_box.y;
661 /*
662 Crop image.
663 */
664 status=MagickTrue;
665 progress=0;
cristy46ff2672012-12-14 15:32:26 +0000666 image_view=AcquireVirtualCacheView(image,exception);
667 crop_view=AcquireAuthenticCacheView(crop_image,exception);
cristy2224dcd2010-11-15 00:49:30 +0000668#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyd6432472013-01-06 16:56:13 +0000669 #pragma omp parallel for schedule(static,4) shared(status) \
670 magick_threads(image,crop_image,1,1)
cristy3ed852e2009-09-05 21:47:34 +0000671#endif
cristybb503372010-05-27 20:51:26 +0000672 for (y=0; y < (ssize_t) crop_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000673 {
cristy4c08aed2011-07-01 19:47:50 +0000674 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +0000675 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000676
cristy4c08aed2011-07-01 19:47:50 +0000677 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000678 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000679
cristy5ce8df82011-07-07 14:52:23 +0000680 register ssize_t
cristy4c08aed2011-07-01 19:47:50 +0000681 x;
682
cristy3ed852e2009-09-05 21:47:34 +0000683 if (status == MagickFalse)
684 continue;
685 p=GetCacheViewVirtualPixels(image_view,page.x,page.y+y,crop_image->columns,
686 1,exception);
687 q=QueueCacheViewAuthenticPixels(crop_view,0,y,crop_image->columns,1,
688 exception);
cristy4c08aed2011-07-01 19:47:50 +0000689 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000690 {
691 status=MagickFalse;
692 continue;
693 }
cristy4c08aed2011-07-01 19:47:50 +0000694 for (x=0; x < (ssize_t) crop_image->columns; x++)
695 {
cristy010d7d12011-08-31 01:02:48 +0000696 register ssize_t
697 i;
698
cristy883fde12013-04-08 00:50:13 +0000699 if (GetPixelReadMask(image,p) == 0)
cristy10a6c612012-01-29 21:41:05 +0000700 {
cristyc3a58022013-10-09 23:22:42 +0000701 SetPixelBackgoundColor(crop_image,q);
cristy10a6c612012-01-29 21:41:05 +0000702 p+=GetPixelChannels(image);
703 q+=GetPixelChannels(crop_image);
704 continue;
705 }
cristy010d7d12011-08-31 01:02:48 +0000706 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
707 {
cristy5a23c552013-02-13 14:34:28 +0000708 PixelChannel channel=GetPixelChannelChannel(image,i);
709 PixelTrait traits=GetPixelChannelTraits(image,channel);
710 PixelTrait crop_traits=GetPixelChannelTraits(crop_image,channel);
cristy010d7d12011-08-31 01:02:48 +0000711 if ((traits == UndefinedPixelTrait) ||
712 (crop_traits == UndefinedPixelTrait))
713 continue;
cristy0beccfa2011-09-25 20:47:53 +0000714 SetPixelChannel(crop_image,channel,p[i],q);
cristy010d7d12011-08-31 01:02:48 +0000715 }
cristyed231572011-07-14 02:18:59 +0000716 p+=GetPixelChannels(image);
717 q+=GetPixelChannels(crop_image);
cristy4c08aed2011-07-01 19:47:50 +0000718 }
cristy3ed852e2009-09-05 21:47:34 +0000719 if (SyncCacheViewAuthenticPixels(crop_view,exception) == MagickFalse)
720 status=MagickFalse;
721 if (image->progress_monitor != (MagickProgressMonitor) NULL)
722 {
723 MagickBooleanType
724 proceed;
725
cristy2224dcd2010-11-15 00:49:30 +0000726#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristya5ab7ad2012-01-21 23:49:58 +0000727 #pragma omp critical (MagickCore_CropImage)
cristy3ed852e2009-09-05 21:47:34 +0000728#endif
729 proceed=SetImageProgress(image,CropImageTag,progress++,image->rows);
730 if (proceed == MagickFalse)
731 status=MagickFalse;
732 }
733 }
734 crop_view=DestroyCacheView(crop_view);
735 image_view=DestroyCacheView(image_view);
736 crop_image->type=image->type;
737 if (status == MagickFalse)
738 crop_image=DestroyImage(crop_image);
739 return(crop_image);
740}
741
742/*
743%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
744% %
745% %
746% %
anthony9f4f0342011-03-28 11:47:22 +0000747% C r o p I m a g e T o T i l e s %
748% %
749% %
750% %
751%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
752%
cristyfaf49552011-09-16 01:37:07 +0000753% CropImageToTiles() crops a single image, into a possible list of tiles.
anthony9f4f0342011-03-28 11:47:22 +0000754% This may include a single sub-region of the image. This basically applies
755% all the normal geometry flags for Crop.
756%
cristyfaf49552011-09-16 01:37:07 +0000757% Image *CropImageToTiles(const Image *image,
758% const RectangleInfo *crop_geometry, ExceptionInfo *exception)
anthony9f4f0342011-03-28 11:47:22 +0000759%
760% A description of each parameter follows:
761%
762% o image: the image The transformed image is returned as this parameter.
763%
764% o crop_geometry: A crop geometry string.
765%
766% o exception: return any errors or warnings in this structure.
767%
768*/
cristyfde3fa52011-07-22 17:49:35 +0000769
cristy72844f12013-04-28 23:52:36 +0000770static inline double MagickRound(double x)
anthony9f4f0342011-03-28 11:47:22 +0000771{
772 /*
773 Round the fraction to nearest integer.
774 */
cristyae0a3fc2013-04-29 08:29:35 +0000775 if ((x-floor(x)) < (ceil(x)-x))
cristy72844f12013-04-28 23:52:36 +0000776 return(floor(x));
777 return(ceil(x));
anthony9f4f0342011-03-28 11:47:22 +0000778}
779
780MagickExport Image *CropImageToTiles(const Image *image,
cristy9ec43c12012-03-03 15:11:08 +0000781 const char *crop_geometry,ExceptionInfo *exception)
anthony9f4f0342011-03-28 11:47:22 +0000782{
783 Image
784 *next,
785 *crop_image;
786
787 MagickStatusType
788 flags;
789
790 RectangleInfo
791 geometry;
792
793 assert(image != (Image *) NULL);
794 assert(image->signature == MagickSignature);
795 if (image->debug != MagickFalse)
796 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
anthony9f4f0342011-03-28 11:47:22 +0000797 crop_image=NewImageList();
798 next=NewImageList();
799 flags=ParseGravityGeometry(image,crop_geometry,&geometry,exception);
anthony9f4f0342011-03-28 11:47:22 +0000800 if ((flags & AreaValue) != 0)
801 {
anthony9f4f0342011-03-28 11:47:22 +0000802 PointInfo
803 delta,
804 offset;
805
806 RectangleInfo
807 crop;
808
cristyfde3fa52011-07-22 17:49:35 +0000809 size_t
810 height,
811 width;
812
anthony9f4f0342011-03-28 11:47:22 +0000813 /*
cristyfde3fa52011-07-22 17:49:35 +0000814 Crop into NxM tiles (@ flag).
anthony9f4f0342011-03-28 11:47:22 +0000815 */
816 width=image->columns;
817 height=image->rows;
818 if (geometry.width == 0)
819 geometry.width=1;
820 if (geometry.height == 0)
821 geometry.height=1;
822 if ((flags & AspectValue) == 0)
823 {
824 width-=(geometry.x < 0 ? -1 : 1)*geometry.x;
825 height-=(geometry.y < 0 ? -1 : 1)*geometry.y;
826 }
827 else
828 {
829 width+=(geometry.x < 0 ? -1 : 1)*geometry.x;
830 height+=(geometry.y < 0 ? -1 : 1)*geometry.y;
831 }
cristy240ae872011-12-02 12:05:20 +0000832 delta.x=(double) width/geometry.width;
833 delta.y=(double) height/geometry.height;
cristy9ec43c12012-03-03 15:11:08 +0000834 if (delta.x < 1.0)
835 delta.x=1.0;
836 if (delta.y < 1.0)
837 delta.y=1.0;
anthony9f4f0342011-03-28 11:47:22 +0000838 for (offset.y=0; offset.y < (double) height; )
839 {
840 if ((flags & AspectValue) == 0)
841 {
cristya19f1d72012-08-07 18:24:38 +0000842 crop.y=(ssize_t) MagickRound((double) (offset.y-
cristyfde3fa52011-07-22 17:49:35 +0000843 (geometry.y > 0 ? 0 : geometry.y)));
anthony9f4f0342011-03-28 11:47:22 +0000844 offset.y+=delta.y; /* increment now to find width */
cristya19f1d72012-08-07 18:24:38 +0000845 crop.height=(size_t) MagickRound((double) (offset.y+
cristyfde3fa52011-07-22 17:49:35 +0000846 (geometry.y < 0 ? 0 : geometry.y)));
anthony9f4f0342011-03-28 11:47:22 +0000847 }
848 else
849 {
cristya19f1d72012-08-07 18:24:38 +0000850 crop.y=(ssize_t) MagickRound((double) (offset.y-
cristyfde3fa52011-07-22 17:49:35 +0000851 (geometry.y > 0 ? geometry.y : 0)));
anthony9f4f0342011-03-28 11:47:22 +0000852 offset.y+=delta.y; /* increment now to find width */
cristya19f1d72012-08-07 18:24:38 +0000853 crop.height=(size_t) MagickRound((double)
cristyfde3fa52011-07-22 17:49:35 +0000854 (offset.y+(geometry.y < -1 ? geometry.y : 0)));
anthony9f4f0342011-03-28 11:47:22 +0000855 }
856 crop.height-=crop.y;
857 crop.y+=image->page.y;
858 for (offset.x=0; offset.x < (double) width; )
859 {
anthony9f4f0342011-03-28 11:47:22 +0000860 if ((flags & AspectValue) == 0)
861 {
cristya19f1d72012-08-07 18:24:38 +0000862 crop.x=(ssize_t) MagickRound((double) (offset.x-
cristyfde3fa52011-07-22 17:49:35 +0000863 (geometry.x > 0 ? 0 : geometry.x)));
864 offset.x+=delta.x; /* increment now to find height */
cristya19f1d72012-08-07 18:24:38 +0000865 crop.width=(size_t) MagickRound((double) (offset.x+
cristyfde3fa52011-07-22 17:49:35 +0000866 (geometry.x < 0 ? 0 : geometry.x)));
anthony9f4f0342011-03-28 11:47:22 +0000867 }
868 else
869 {
cristya19f1d72012-08-07 18:24:38 +0000870 crop.x=(ssize_t) MagickRound((double) (offset.x-
anthony9f4f0342011-03-28 11:47:22 +0000871 (geometry.x > 0 ? geometry.x : 0)));
cristyfde3fa52011-07-22 17:49:35 +0000872 offset.x+=delta.x; /* increment now to find height */
cristya19f1d72012-08-07 18:24:38 +0000873 crop.width=(size_t) MagickRound((double) (offset.x+
cristyfde3fa52011-07-22 17:49:35 +0000874 (geometry.x < 0 ? geometry.x : 0)));
anthony9f4f0342011-03-28 11:47:22 +0000875 }
876 crop.width-=crop.x;
877 crop.x+=image->page.x;
cristybf46f102011-08-01 18:00:16 +0000878 next=CropImage(image,&crop,exception);
879 if (next == (Image *) NULL)
880 break;
881 AppendImageToList(&crop_image,next);
anthony9f4f0342011-03-28 11:47:22 +0000882 }
883 if (next == (Image *) NULL)
884 break;
anthony9f4f0342011-03-28 11:47:22 +0000885 }
cristy52224bf2011-08-01 18:17:07 +0000886 ClearMagickException(exception);
anthony9f4f0342011-03-28 11:47:22 +0000887 return(crop_image);
888 }
anthony9f4f0342011-03-28 11:47:22 +0000889 if (((geometry.width == 0) && (geometry.height == 0)) ||
890 ((flags & XValue) != 0) || ((flags & YValue) != 0))
891 {
892 /*
893 Crop a single region at +X+Y.
894 */
895 crop_image=CropImage(image,&geometry,exception);
896 if ((crop_image != (Image *) NULL) && ((flags & AspectValue) != 0))
897 {
898 crop_image->page.width=geometry.width;
899 crop_image->page.height=geometry.height;
900 crop_image->page.x-=geometry.x;
901 crop_image->page.y-=geometry.y;
902 }
903 return(crop_image);
cristy9ec43c12012-03-03 15:11:08 +0000904 }
cristyfde3fa52011-07-22 17:49:35 +0000905 if ((image->columns > geometry.width) || (image->rows > geometry.height))
anthony9f4f0342011-03-28 11:47:22 +0000906 {
cristyfde3fa52011-07-22 17:49:35 +0000907 RectangleInfo
908 page;
anthony9f4f0342011-03-28 11:47:22 +0000909
anthony9f4f0342011-03-28 11:47:22 +0000910 size_t
911 height,
912 width;
913
914 ssize_t
915 x,
916 y;
917
anthony9f4f0342011-03-28 11:47:22 +0000918 /*
919 Crop into tiles of fixed size WxH.
920 */
anthony9f4f0342011-03-28 11:47:22 +0000921 page=image->page;
922 if (page.width == 0)
923 page.width=image->columns;
anthony5ea220b2011-04-22 13:15:46 +0000924 if (page.height == 0)
anthony9f4f0342011-03-28 11:47:22 +0000925 page.height=image->rows;
anthony5ea220b2011-04-22 13:15:46 +0000926 width=geometry.width;
927 if (width == 0)
928 width=page.width;
929 height=geometry.height;
930 if (height == 0)
931 height=page.height;
anthony9f4f0342011-03-28 11:47:22 +0000932 next=NewImageList();
anthony9f4f0342011-03-28 11:47:22 +0000933 for (y=0; y < (ssize_t) page.height; y+=(ssize_t) height)
934 {
935 for (x=0; x < (ssize_t) page.width; x+=(ssize_t) width)
936 {
anthony9f4f0342011-03-28 11:47:22 +0000937 geometry.width=width;
938 geometry.height=height;
939 geometry.x=x;
940 geometry.y=y;
941 next=CropImage(image,&geometry,exception);
anthony9f4f0342011-03-28 11:47:22 +0000942 if (next == (Image *) NULL)
943 break;
anthony9f4f0342011-03-28 11:47:22 +0000944 AppendImageToList(&crop_image,next);
945 }
946 if (next == (Image *) NULL)
947 break;
anthony9f4f0342011-03-28 11:47:22 +0000948 }
949 return(crop_image);
950 }
anthony9f4f0342011-03-28 11:47:22 +0000951 return(CloneImage(image,0,0,MagickTrue,exception));
952}
953
954/*
955%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
956% %
957% %
958% %
cristy3ed852e2009-09-05 21:47:34 +0000959% E x c e r p t I m a g e %
960% %
961% %
962% %
963%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
964%
965% ExcerptImage() returns a excerpt of the image as defined by the geometry.
966%
967% The format of the ExcerptImage method is:
968%
969% Image *ExcerptImage(const Image *image,const RectangleInfo *geometry,
970% ExceptionInfo *exception)
971%
972% A description of each parameter follows:
973%
974% o image: the image.
975%
976% o geometry: Define the region of the image to extend with members
977% x, y, width, and height.
978%
979% o exception: return any errors or warnings in this structure.
980%
981*/
982MagickExport Image *ExcerptImage(const Image *image,
983 const RectangleInfo *geometry,ExceptionInfo *exception)
984{
985#define ExcerptImageTag "Excerpt/Image"
986
cristyc4c8d132010-01-07 01:58:38 +0000987 CacheView
988 *excerpt_view,
989 *image_view;
990
cristy3ed852e2009-09-05 21:47:34 +0000991 Image
992 *excerpt_image;
993
cristy3ed852e2009-09-05 21:47:34 +0000994 MagickBooleanType
995 status;
996
cristybb503372010-05-27 20:51:26 +0000997 MagickOffsetType
998 progress;
999
1000 ssize_t
1001 y;
1002
cristy3ed852e2009-09-05 21:47:34 +00001003 /*
1004 Allocate excerpt image.
1005 */
1006 assert(image != (const Image *) NULL);
1007 assert(image->signature == MagickSignature);
1008 if (image->debug != MagickFalse)
1009 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1010 assert(geometry != (const RectangleInfo *) NULL);
1011 assert(exception != (ExceptionInfo *) NULL);
1012 assert(exception->signature == MagickSignature);
1013 excerpt_image=CloneImage(image,geometry->width,geometry->height,MagickTrue,
1014 exception);
1015 if (excerpt_image == (Image *) NULL)
1016 return((Image *) NULL);
1017 /*
1018 Excerpt each row.
1019 */
1020 status=MagickTrue;
1021 progress=0;
cristy46ff2672012-12-14 15:32:26 +00001022 image_view=AcquireVirtualCacheView(image,exception);
1023 excerpt_view=AcquireAuthenticCacheView(excerpt_image,exception);
cristyb5d5f722009-11-04 03:03:49 +00001024#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +00001025 #pragma omp parallel for schedule(static,4) shared(progress,status) \
cristy5e6b2592012-12-19 14:08:11 +00001026 magick_threads(image,excerpt_image,excerpt_image->rows,1)
cristy3ed852e2009-09-05 21:47:34 +00001027#endif
cristybb503372010-05-27 20:51:26 +00001028 for (y=0; y < (ssize_t) excerpt_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001029 {
cristy4c08aed2011-07-01 19:47:50 +00001030 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00001031 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001032
cristy4c08aed2011-07-01 19:47:50 +00001033 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00001034 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001035
cristy4c08aed2011-07-01 19:47:50 +00001036 register ssize_t
1037 x;
1038
cristy3ed852e2009-09-05 21:47:34 +00001039 if (status == MagickFalse)
1040 continue;
1041 p=GetCacheViewVirtualPixels(image_view,geometry->x,geometry->y+y,
1042 geometry->width,1,exception);
1043 q=GetCacheViewAuthenticPixels(excerpt_view,0,y,excerpt_image->columns,1,
1044 exception);
cristy4c08aed2011-07-01 19:47:50 +00001045 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00001046 {
1047 status=MagickFalse;
1048 continue;
1049 }
cristy4c08aed2011-07-01 19:47:50 +00001050 for (x=0; x < (ssize_t) excerpt_image->columns; x++)
1051 {
cristy010d7d12011-08-31 01:02:48 +00001052 register ssize_t
1053 i;
1054
cristy883fde12013-04-08 00:50:13 +00001055 if (GetPixelReadMask(image,p) == 0)
cristy10a6c612012-01-29 21:41:05 +00001056 {
cristyc3a58022013-10-09 23:22:42 +00001057 SetPixelBackgoundColor(excerpt_image,q);
cristy10a6c612012-01-29 21:41:05 +00001058 p+=GetPixelChannels(image);
1059 q+=GetPixelChannels(excerpt_image);
1060 continue;
1061 }
cristy010d7d12011-08-31 01:02:48 +00001062 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1063 {
cristy5a23c552013-02-13 14:34:28 +00001064 PixelChannel channel=GetPixelChannelChannel(image,i);
1065 PixelTrait traits=GetPixelChannelTraits(image,channel);
1066 PixelTrait excerpt_traits=GetPixelChannelTraits(excerpt_image,channel);
cristy010d7d12011-08-31 01:02:48 +00001067 if ((traits == UndefinedPixelTrait) ||
1068 (excerpt_traits == UndefinedPixelTrait))
1069 continue;
cristy0beccfa2011-09-25 20:47:53 +00001070 SetPixelChannel(excerpt_image,channel,p[i],q);
cristy010d7d12011-08-31 01:02:48 +00001071 }
cristyed231572011-07-14 02:18:59 +00001072 p+=GetPixelChannels(image);
1073 q+=GetPixelChannels(excerpt_image);
cristy4c08aed2011-07-01 19:47:50 +00001074 }
cristy3ed852e2009-09-05 21:47:34 +00001075 if (SyncCacheViewAuthenticPixels(excerpt_view,exception) == MagickFalse)
1076 status=MagickFalse;
1077 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1078 {
1079 MagickBooleanType
1080 proceed;
1081
cristyb5d5f722009-11-04 03:03:49 +00001082#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristya5ab7ad2012-01-21 23:49:58 +00001083 #pragma omp critical (MagickCore_ExcerptImage)
cristy3ed852e2009-09-05 21:47:34 +00001084#endif
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*/
1128MagickExport Image *ExtentImage(const Image *image,
1129 const RectangleInfo *geometry,ExceptionInfo *exception)
1130{
1131 Image
1132 *extent_image;
1133
1134 /*
1135 Allocate extent image.
1136 */
1137 assert(image != (const Image *) NULL);
1138 assert(image->signature == MagickSignature);
1139 if (image->debug != MagickFalse)
1140 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1141 assert(geometry != (const RectangleInfo *) NULL);
1142 assert(exception != (ExceptionInfo *) NULL);
1143 assert(exception->signature == MagickSignature);
1144 extent_image=CloneImage(image,geometry->width,geometry->height,MagickTrue,
1145 exception);
1146 if (extent_image == (Image *) NULL)
1147 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00001148 if (SetImageStorageClass(extent_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001149 {
cristy3ed852e2009-09-05 21:47:34 +00001150 extent_image=DestroyImage(extent_image);
1151 return((Image *) NULL);
1152 }
cristy4c08aed2011-07-01 19:47:50 +00001153 if (extent_image->background_color.alpha != OpaqueAlpha)
cristy8a46d822012-08-28 23:32:39 +00001154 extent_image->alpha_trait=BlendPixelTrait;
cristyea1a8aa2011-10-20 13:24:06 +00001155 (void) SetImageBackgroundColor(extent_image,exception);
cristy39172402012-03-30 13:04:39 +00001156 (void) CompositeImage(extent_image,image,image->compose,MagickTrue,
cristyfeb3e962012-03-29 17:25:55 +00001157 -geometry->x,-geometry->y,exception);
cristy3ed852e2009-09-05 21:47:34 +00001158 return(extent_image);
1159}
1160
1161/*
1162%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1163% %
1164% %
1165% %
1166% F l i p I m a g e %
1167% %
1168% %
1169% %
1170%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1171%
1172% FlipImage() creates a vertical mirror image by reflecting the pixels
1173% around the central x-axis.
1174%
1175% The format of the FlipImage method is:
1176%
1177% Image *FlipImage(const Image *image,ExceptionInfo *exception)
1178%
1179% A description of each parameter follows:
1180%
1181% o image: the image.
1182%
1183% o exception: return any errors or warnings in this structure.
1184%
1185*/
1186MagickExport Image *FlipImage(const Image *image,ExceptionInfo *exception)
1187{
1188#define FlipImageTag "Flip/Image"
1189
cristyc4c8d132010-01-07 01:58:38 +00001190 CacheView
1191 *flip_view,
1192 *image_view;
1193
cristy3ed852e2009-09-05 21:47:34 +00001194 Image
1195 *flip_image;
1196
cristy3ed852e2009-09-05 21:47:34 +00001197 MagickBooleanType
1198 status;
1199
cristybb503372010-05-27 20:51:26 +00001200 MagickOffsetType
1201 progress;
1202
cristy74ea2cd2010-12-08 02:54:25 +00001203 RectangleInfo
1204 page;
1205
cristybb503372010-05-27 20:51:26 +00001206 ssize_t
1207 y;
1208
cristy3ed852e2009-09-05 21:47:34 +00001209 assert(image != (const Image *) NULL);
1210 assert(image->signature == MagickSignature);
1211 if (image->debug != MagickFalse)
1212 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1213 assert(exception != (ExceptionInfo *) NULL);
1214 assert(exception->signature == MagickSignature);
1215 flip_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
1216 if (flip_image == (Image *) NULL)
1217 return((Image *) NULL);
1218 /*
1219 Flip image.
1220 */
1221 status=MagickTrue;
1222 progress=0;
cristy74ea2cd2010-12-08 02:54:25 +00001223 page=image->page;
cristy46ff2672012-12-14 15:32:26 +00001224 image_view=AcquireVirtualCacheView(image,exception);
1225 flip_view=AcquireAuthenticCacheView(flip_image,exception);
cristy26b64912012-12-16 18:20:09 +00001226#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyd6432472013-01-06 16:56:13 +00001227 #pragma omp parallel for schedule(static,4) shared(status) \
1228 magick_threads(image,flip_image,1,1)
cristy3ed852e2009-09-05 21:47:34 +00001229#endif
cristybb503372010-05-27 20:51:26 +00001230 for (y=0; y < (ssize_t) flip_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001231 {
cristy4c08aed2011-07-01 19:47:50 +00001232 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00001233 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001234
cristy4c08aed2011-07-01 19:47:50 +00001235 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00001236 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001237
cristy4c08aed2011-07-01 19:47:50 +00001238 register ssize_t
1239 x;
1240
cristy3ed852e2009-09-05 21:47:34 +00001241 if (status == MagickFalse)
1242 continue;
1243 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
cristy5db1f092010-06-07 13:07:26 +00001244 q=QueueCacheViewAuthenticPixels(flip_view,0,(ssize_t) (flip_image->rows-y-
1245 1),flip_image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001246 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00001247 {
1248 status=MagickFalse;
1249 continue;
1250 }
cristy4c08aed2011-07-01 19:47:50 +00001251 for (x=0; x < (ssize_t) flip_image->columns; x++)
1252 {
cristy010d7d12011-08-31 01:02:48 +00001253 register ssize_t
1254 i;
1255
cristy883fde12013-04-08 00:50:13 +00001256 if (GetPixelReadMask(image,p) == 0)
cristy10a6c612012-01-29 21:41:05 +00001257 {
cristyc3a58022013-10-09 23:22:42 +00001258 SetPixelBackgoundColor(flip_image,q);
cristy10a6c612012-01-29 21:41:05 +00001259 p+=GetPixelChannels(image);
1260 q+=GetPixelChannels(flip_image);
1261 continue;
1262 }
cristy010d7d12011-08-31 01:02:48 +00001263 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1264 {
cristy5a23c552013-02-13 14:34:28 +00001265 PixelChannel channel=GetPixelChannelChannel(image,i);
1266 PixelTrait traits=GetPixelChannelTraits(image,channel);
1267 PixelTrait flip_traits=GetPixelChannelTraits(flip_image,channel);
cristy010d7d12011-08-31 01:02:48 +00001268 if ((traits == UndefinedPixelTrait) ||
1269 (flip_traits == UndefinedPixelTrait))
1270 continue;
cristy0beccfa2011-09-25 20:47:53 +00001271 SetPixelChannel(flip_image,channel,p[i],q);
cristy010d7d12011-08-31 01:02:48 +00001272 }
cristyed231572011-07-14 02:18:59 +00001273 p+=GetPixelChannels(image);
1274 q+=GetPixelChannels(flip_image);
cristy4c08aed2011-07-01 19:47:50 +00001275 }
cristy3ed852e2009-09-05 21:47:34 +00001276 if (SyncCacheViewAuthenticPixels(flip_view,exception) == MagickFalse)
1277 status=MagickFalse;
1278 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1279 {
1280 MagickBooleanType
1281 proceed;
1282
cristy26b64912012-12-16 18:20:09 +00001283#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristya5ab7ad2012-01-21 23:49:58 +00001284 #pragma omp critical (MagickCore_FlipImage)
cristy3ed852e2009-09-05 21:47:34 +00001285#endif
1286 proceed=SetImageProgress(image,FlipImageTag,progress++,image->rows);
1287 if (proceed == MagickFalse)
1288 status=MagickFalse;
1289 }
1290 }
1291 flip_view=DestroyCacheView(flip_view);
1292 image_view=DestroyCacheView(image_view);
1293 flip_image->type=image->type;
anthony37a1b912010-12-10 12:45:59 +00001294 if (page.height != 0)
1295 page.y=(ssize_t) (page.height-flip_image->rows-page.y);
cristy74ea2cd2010-12-08 02:54:25 +00001296 flip_image->page=page;
cristy3ed852e2009-09-05 21:47:34 +00001297 if (status == MagickFalse)
1298 flip_image=DestroyImage(flip_image);
1299 return(flip_image);
1300}
1301
1302/*
1303%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1304% %
1305% %
1306% %
1307% F l o p I m a g e %
1308% %
1309% %
1310% %
1311%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1312%
1313% FlopImage() creates a horizontal mirror image by reflecting the pixels
1314% around the central y-axis.
1315%
1316% The format of the FlopImage method is:
1317%
1318% Image *FlopImage(const Image *image,ExceptionInfo *exception)
1319%
1320% A description of each parameter follows:
1321%
1322% o image: the image.
1323%
1324% o exception: return any errors or warnings in this structure.
1325%
1326*/
1327MagickExport Image *FlopImage(const Image *image,ExceptionInfo *exception)
1328{
1329#define FlopImageTag "Flop/Image"
1330
cristyc4c8d132010-01-07 01:58:38 +00001331 CacheView
1332 *flop_view,
1333 *image_view;
1334
cristy3ed852e2009-09-05 21:47:34 +00001335 Image
1336 *flop_image;
1337
cristy3ed852e2009-09-05 21:47:34 +00001338 MagickBooleanType
1339 status;
1340
cristybb503372010-05-27 20:51:26 +00001341 MagickOffsetType
1342 progress;
1343
cristy74ea2cd2010-12-08 02:54:25 +00001344 RectangleInfo
1345 page;
1346
cristybb503372010-05-27 20:51:26 +00001347 ssize_t
1348 y;
1349
cristy3ed852e2009-09-05 21:47:34 +00001350 assert(image != (const Image *) NULL);
1351 assert(image->signature == MagickSignature);
1352 if (image->debug != MagickFalse)
1353 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1354 assert(exception != (ExceptionInfo *) NULL);
1355 assert(exception->signature == MagickSignature);
1356 flop_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
1357 if (flop_image == (Image *) NULL)
1358 return((Image *) NULL);
1359 /*
1360 Flop each row.
1361 */
1362 status=MagickTrue;
1363 progress=0;
cristy74ea2cd2010-12-08 02:54:25 +00001364 page=image->page;
cristy46ff2672012-12-14 15:32:26 +00001365 image_view=AcquireVirtualCacheView(image,exception);
1366 flop_view=AcquireAuthenticCacheView(flop_image,exception);
cristy26b64912012-12-16 18:20:09 +00001367#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyd6432472013-01-06 16:56:13 +00001368 #pragma omp parallel for schedule(static,4) shared(status) \
1369 magick_threads(image,flop_image,1,1)
cristy3ed852e2009-09-05 21:47:34 +00001370#endif
cristybb503372010-05-27 20:51:26 +00001371 for (y=0; y < (ssize_t) flop_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001372 {
cristy4c08aed2011-07-01 19:47:50 +00001373 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00001374 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001375
cristybb503372010-05-27 20:51:26 +00001376 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001377 x;
1378
cristy4c08aed2011-07-01 19:47:50 +00001379 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00001380 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001381
1382 if (status == MagickFalse)
1383 continue;
1384 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1385 q=QueueCacheViewAuthenticPixels(flop_view,0,y,flop_image->columns,1,
1386 exception);
cristy4c08aed2011-07-01 19:47:50 +00001387 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00001388 {
1389 status=MagickFalse;
1390 continue;
1391 }
cristyed231572011-07-14 02:18:59 +00001392 q+=GetPixelChannels(flop_image)*flop_image->columns;
cristybb503372010-05-27 20:51:26 +00001393 for (x=0; x < (ssize_t) flop_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001394 {
cristy010d7d12011-08-31 01:02:48 +00001395 register ssize_t
1396 i;
1397
cristyed231572011-07-14 02:18:59 +00001398 q-=GetPixelChannels(flop_image);
cristy883fde12013-04-08 00:50:13 +00001399 if (GetPixelReadMask(image,p) == 0)
cristy10a6c612012-01-29 21:41:05 +00001400 {
1401 p+=GetPixelChannels(image);
1402 continue;
1403 }
cristy010d7d12011-08-31 01:02:48 +00001404 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1405 {
cristy5a23c552013-02-13 14:34:28 +00001406 PixelChannel channel=GetPixelChannelChannel(image,i);
1407 PixelTrait traits=GetPixelChannelTraits(image,channel);
1408 PixelTrait flop_traits=GetPixelChannelTraits(flop_image,channel);
cristy010d7d12011-08-31 01:02:48 +00001409 if ((traits == UndefinedPixelTrait) ||
1410 (flop_traits == UndefinedPixelTrait))
1411 continue;
cristy0beccfa2011-09-25 20:47:53 +00001412 SetPixelChannel(flop_image,channel,p[i],q);
cristy010d7d12011-08-31 01:02:48 +00001413 }
cristyed231572011-07-14 02:18:59 +00001414 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001415 }
1416 if (SyncCacheViewAuthenticPixels(flop_view,exception) == MagickFalse)
1417 status=MagickFalse;
1418 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1419 {
1420 MagickBooleanType
1421 proceed;
1422
cristy26b64912012-12-16 18:20:09 +00001423#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristya5ab7ad2012-01-21 23:49:58 +00001424 #pragma omp critical (MagickCore_FlopImage)
cristy3ed852e2009-09-05 21:47:34 +00001425#endif
1426 proceed=SetImageProgress(image,FlopImageTag,progress++,image->rows);
1427 if (proceed == MagickFalse)
1428 status=MagickFalse;
1429 }
1430 }
1431 flop_view=DestroyCacheView(flop_view);
1432 image_view=DestroyCacheView(image_view);
1433 flop_image->type=image->type;
anthony37a1b912010-12-10 12:45:59 +00001434 if (page.width != 0)
1435 page.x=(ssize_t) (page.width-flop_image->columns-page.x);
cristy74ea2cd2010-12-08 02:54:25 +00001436 flop_image->page=page;
cristy3ed852e2009-09-05 21:47:34 +00001437 if (status == MagickFalse)
1438 flop_image=DestroyImage(flop_image);
1439 return(flop_image);
1440}
1441
1442/*
1443%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1444% %
1445% %
1446% %
1447% R o l l I m a g e %
1448% %
1449% %
1450% %
1451%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1452%
1453% RollImage() offsets an image as defined by x_offset and y_offset.
1454%
1455% The format of the RollImage method is:
1456%
cristybb503372010-05-27 20:51:26 +00001457% Image *RollImage(const Image *image,const ssize_t x_offset,
1458% const ssize_t y_offset,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001459%
1460% A description of each parameter follows:
1461%
1462% o image: the image.
1463%
1464% o x_offset: the number of columns to roll in the horizontal direction.
1465%
1466% o y_offset: the number of rows to roll in the vertical direction.
1467%
1468% o exception: return any errors or warnings in this structure.
1469%
1470*/
1471
1472static inline MagickBooleanType CopyImageRegion(Image *destination,
cristybb503372010-05-27 20:51:26 +00001473 const Image *source,const size_t columns,const size_t rows,
1474 const ssize_t sx,const ssize_t sy,const ssize_t dx,const ssize_t dy,
cristy3ed852e2009-09-05 21:47:34 +00001475 ExceptionInfo *exception)
1476{
cristyc4c8d132010-01-07 01:58:38 +00001477 CacheView
1478 *source_view,
1479 *destination_view;
1480
cristy3ed852e2009-09-05 21:47:34 +00001481 MagickBooleanType
1482 status;
1483
cristy9d314ff2011-03-09 01:30:28 +00001484 ssize_t
1485 y;
1486
cristy3ed852e2009-09-05 21:47:34 +00001487 status=MagickTrue;
cristy46ff2672012-12-14 15:32:26 +00001488 source_view=AcquireVirtualCacheView(source,exception);
1489 destination_view=AcquireAuthenticCacheView(destination,exception);
cristyb5d5f722009-11-04 03:03:49 +00001490#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy9a5a52f2012-10-09 14:40:31 +00001491 #pragma omp parallel for schedule(static,4) shared(status) \
cristy5e6b2592012-12-19 14:08:11 +00001492 magick_threads(source,destination,rows,1)
cristy3ed852e2009-09-05 21:47:34 +00001493#endif
cristybb503372010-05-27 20:51:26 +00001494 for (y=0; y < (ssize_t) rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001495 {
1496 MagickBooleanType
1497 sync;
1498
cristy4c08aed2011-07-01 19:47:50 +00001499 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00001500 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001501
cristy4c08aed2011-07-01 19:47:50 +00001502 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00001503 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001504
cristy4c08aed2011-07-01 19:47:50 +00001505 register ssize_t
1506 x;
1507
cristy3ed852e2009-09-05 21:47:34 +00001508 /*
1509 Transfer scanline.
1510 */
1511 if (status == MagickFalse)
1512 continue;
1513 p=GetCacheViewVirtualPixels(source_view,sx,sy+y,columns,1,exception);
1514 q=GetCacheViewAuthenticPixels(destination_view,dx,dy+y,columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001515 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00001516 {
1517 status=MagickFalse;
1518 continue;
1519 }
cristy4c08aed2011-07-01 19:47:50 +00001520 for (x=0; x < (ssize_t) columns; x++)
1521 {
cristy010d7d12011-08-31 01:02:48 +00001522 register ssize_t
1523 i;
1524
cristy883fde12013-04-08 00:50:13 +00001525 if (GetPixelReadMask(source,p) == 0)
cristy10a6c612012-01-29 21:41:05 +00001526 {
cristyc3a58022013-10-09 23:22:42 +00001527 SetPixelBackgoundColor(destination,q);
cristy10a6c612012-01-29 21:41:05 +00001528 p+=GetPixelChannels(source);
1529 q+=GetPixelChannels(destination);
1530 continue;
1531 }
cristy010d7d12011-08-31 01:02:48 +00001532 for (i=0; i < (ssize_t) GetPixelChannels(source); i++)
1533 {
cristy5a23c552013-02-13 14:34:28 +00001534 PixelChannel channel=GetPixelChannelChannel(source,i);
1535 PixelTrait source_traits=GetPixelChannelTraits(source,channel);
1536 PixelTrait destination_traits=GetPixelChannelTraits(destination,
1537 channel);
cristy010d7d12011-08-31 01:02:48 +00001538 if ((source_traits == UndefinedPixelTrait) ||
1539 (destination_traits == UndefinedPixelTrait))
1540 continue;
cristy0beccfa2011-09-25 20:47:53 +00001541 SetPixelChannel(destination,channel,p[i],q);
cristy010d7d12011-08-31 01:02:48 +00001542 }
cristyed231572011-07-14 02:18:59 +00001543 p+=GetPixelChannels(source);
1544 q+=GetPixelChannels(destination);
cristy4c08aed2011-07-01 19:47:50 +00001545 }
cristy3ed852e2009-09-05 21:47:34 +00001546 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
1547 if (sync == MagickFalse)
1548 status=MagickFalse;
1549 }
1550 destination_view=DestroyCacheView(destination_view);
1551 source_view=DestroyCacheView(source_view);
1552 return(status);
1553}
1554
cristybb503372010-05-27 20:51:26 +00001555MagickExport Image *RollImage(const Image *image,const ssize_t x_offset,
1556 const ssize_t y_offset,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001557{
1558#define RollImageTag "Roll/Image"
1559
1560 Image
1561 *roll_image;
1562
1563 MagickStatusType
1564 status;
1565
1566 RectangleInfo
1567 offset;
1568
1569 /*
1570 Initialize roll image attributes.
1571 */
1572 assert(image != (const Image *) NULL);
1573 assert(image->signature == MagickSignature);
1574 if (image->debug != MagickFalse)
1575 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1576 assert(exception != (ExceptionInfo *) NULL);
1577 assert(exception->signature == MagickSignature);
1578 roll_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
1579 if (roll_image == (Image *) NULL)
1580 return((Image *) NULL);
1581 offset.x=x_offset;
1582 offset.y=y_offset;
1583 while (offset.x < 0)
cristyeaedf062010-05-29 22:36:02 +00001584 offset.x+=(ssize_t) image->columns;
cristybb503372010-05-27 20:51:26 +00001585 while (offset.x >= (ssize_t) image->columns)
cristyeaedf062010-05-29 22:36:02 +00001586 offset.x-=(ssize_t) image->columns;
cristy3ed852e2009-09-05 21:47:34 +00001587 while (offset.y < 0)
cristyeaedf062010-05-29 22:36:02 +00001588 offset.y+=(ssize_t) image->rows;
cristybb503372010-05-27 20:51:26 +00001589 while (offset.y >= (ssize_t) image->rows)
cristyeaedf062010-05-29 22:36:02 +00001590 offset.y-=(ssize_t) image->rows;
cristy3ed852e2009-09-05 21:47:34 +00001591 /*
1592 Roll image.
1593 */
cristybb503372010-05-27 20:51:26 +00001594 status=CopyImageRegion(roll_image,image,(size_t) offset.x,
1595 (size_t) offset.y,(ssize_t) image->columns-offset.x,(ssize_t) image->rows-
cristy3ed852e2009-09-05 21:47:34 +00001596 offset.y,0,0,exception);
1597 (void) SetImageProgress(image,RollImageTag,0,3);
cristyaae13f62013-08-15 14:41:32 +00001598 status&=CopyImageRegion(roll_image,image,image->columns-offset.x,
cristybb503372010-05-27 20:51:26 +00001599 (size_t) offset.y,0,(ssize_t) image->rows-offset.y,offset.x,0,
cristy3ed852e2009-09-05 21:47:34 +00001600 exception);
1601 (void) SetImageProgress(image,RollImageTag,1,3);
cristyaae13f62013-08-15 14:41:32 +00001602 status&=CopyImageRegion(roll_image,image,(size_t) offset.x,image->rows-
cristybb503372010-05-27 20:51:26 +00001603 offset.y,(ssize_t) image->columns-offset.x,0,0,offset.y,exception);
cristy3ed852e2009-09-05 21:47:34 +00001604 (void) SetImageProgress(image,RollImageTag,2,3);
cristyaae13f62013-08-15 14:41:32 +00001605 status&=CopyImageRegion(roll_image,image,image->columns-offset.x,image->rows-
cristy3ed852e2009-09-05 21:47:34 +00001606 offset.y,0,0,offset.x,offset.y,exception);
1607 (void) SetImageProgress(image,RollImageTag,3,3);
1608 roll_image->type=image->type;
1609 if (status == MagickFalse)
1610 roll_image=DestroyImage(roll_image);
1611 return(roll_image);
1612}
1613
1614/*
1615%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1616% %
1617% %
1618% %
1619% S h a v e I m a g e %
1620% %
1621% %
1622% %
1623%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1624%
1625% ShaveImage() shaves pixels from the image edges. It allocates the memory
1626% necessary for the new Image structure and returns a pointer to the new
1627% image.
1628%
1629% The format of the ShaveImage method is:
1630%
1631% Image *ShaveImage(const Image *image,const RectangleInfo *shave_info,
1632% ExceptionInfo *exception)
1633%
1634% A description of each parameter follows:
1635%
1636% o shave_image: Method ShaveImage returns a pointer to the shaved
1637% image. A null image is returned if there is a memory shortage or
1638% if the image width or height is zero.
1639%
1640% o image: the image.
1641%
1642% o shave_info: Specifies a pointer to a RectangleInfo which defines the
1643% region of the image to crop.
1644%
1645% o exception: return any errors or warnings in this structure.
1646%
1647*/
1648MagickExport Image *ShaveImage(const Image *image,
1649 const RectangleInfo *shave_info,ExceptionInfo *exception)
1650{
1651 Image
1652 *shave_image;
1653
1654 RectangleInfo
1655 geometry;
1656
1657 assert(image != (const Image *) NULL);
1658 assert(image->signature == MagickSignature);
1659 if (image->debug != MagickFalse)
1660 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1661 if (((2*shave_info->width) >= image->columns) ||
1662 ((2*shave_info->height) >= image->rows))
1663 ThrowImageException(OptionWarning,"GeometryDoesNotContainImage");
1664 SetGeometry(image,&geometry);
1665 geometry.width-=2*shave_info->width;
1666 geometry.height-=2*shave_info->height;
cristybb503372010-05-27 20:51:26 +00001667 geometry.x=(ssize_t) shave_info->width+image->page.x;
1668 geometry.y=(ssize_t) shave_info->height+image->page.y;
cristy3ed852e2009-09-05 21:47:34 +00001669 shave_image=CropImage(image,&geometry,exception);
1670 if (shave_image == (Image *) NULL)
1671 return((Image *) NULL);
1672 shave_image->page.width-=2*shave_info->width;
1673 shave_image->page.height-=2*shave_info->height;
cristyeaedf062010-05-29 22:36:02 +00001674 shave_image->page.x-=(ssize_t) shave_info->width;
1675 shave_image->page.y-=(ssize_t) shave_info->height;
cristy3ed852e2009-09-05 21:47:34 +00001676 return(shave_image);
1677}
1678
1679/*
1680%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1681% %
1682% %
1683% %
1684% S p l i c e I m a g e %
1685% %
1686% %
1687% %
1688%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1689%
1690% SpliceImage() splices a solid color into the image as defined by the
1691% geometry.
1692%
1693% The format of the SpliceImage method is:
1694%
1695% Image *SpliceImage(const Image *image,const RectangleInfo *geometry,
1696% ExceptionInfo *exception)
1697%
1698% A description of each parameter follows:
1699%
1700% o image: the image.
1701%
1702% o geometry: Define the region of the image to splice with members
1703% x, y, width, and height.
1704%
1705% o exception: return any errors or warnings in this structure.
1706%
1707*/
1708MagickExport Image *SpliceImage(const Image *image,
1709 const RectangleInfo *geometry,ExceptionInfo *exception)
1710{
1711#define SpliceImageTag "Splice/Image"
1712
cristyc4c8d132010-01-07 01:58:38 +00001713 CacheView
1714 *image_view,
1715 *splice_view;
1716
cristy3ed852e2009-09-05 21:47:34 +00001717 Image
1718 *splice_image;
1719
cristy3ed852e2009-09-05 21:47:34 +00001720 MagickBooleanType
cristy3ed852e2009-09-05 21:47:34 +00001721 status;
1722
cristybb503372010-05-27 20:51:26 +00001723 MagickOffsetType
1724 progress;
1725
cristy3ed852e2009-09-05 21:47:34 +00001726 RectangleInfo
1727 splice_geometry;
1728
cristybb503372010-05-27 20:51:26 +00001729 ssize_t
1730 y;
1731
cristy3ed852e2009-09-05 21:47:34 +00001732 /*
1733 Allocate splice image.
1734 */
1735 assert(image != (const Image *) NULL);
1736 assert(image->signature == MagickSignature);
1737 if (image->debug != MagickFalse)
1738 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1739 assert(geometry != (const RectangleInfo *) NULL);
1740 assert(exception != (ExceptionInfo *) NULL);
1741 assert(exception->signature == MagickSignature);
1742 splice_geometry=(*geometry);
1743 splice_image=CloneImage(image,image->columns+splice_geometry.width,
1744 image->rows+splice_geometry.height,MagickTrue,exception);
1745 if (splice_image == (Image *) NULL)
1746 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00001747 if (SetImageStorageClass(splice_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001748 {
cristy3ed852e2009-09-05 21:47:34 +00001749 splice_image=DestroyImage(splice_image);
1750 return((Image *) NULL);
1751 }
cristyea1a8aa2011-10-20 13:24:06 +00001752 (void) SetImageBackgroundColor(splice_image,exception);
cristy3ed852e2009-09-05 21:47:34 +00001753 /*
1754 Respect image geometry.
1755 */
1756 switch (image->gravity)
1757 {
1758 default:
1759 case UndefinedGravity:
1760 case NorthWestGravity:
1761 break;
1762 case NorthGravity:
1763 {
cristyeaedf062010-05-29 22:36:02 +00001764 splice_geometry.x+=(ssize_t) splice_geometry.width/2;
cristy3ed852e2009-09-05 21:47:34 +00001765 break;
1766 }
1767 case NorthEastGravity:
1768 {
cristyeaedf062010-05-29 22:36:02 +00001769 splice_geometry.x+=(ssize_t) splice_geometry.width;
cristy3ed852e2009-09-05 21:47:34 +00001770 break;
1771 }
1772 case WestGravity:
1773 {
cristyeaedf062010-05-29 22:36:02 +00001774 splice_geometry.y+=(ssize_t) splice_geometry.width/2;
cristy3ed852e2009-09-05 21:47:34 +00001775 break;
1776 }
cristy3ed852e2009-09-05 21:47:34 +00001777 case CenterGravity:
1778 {
cristyeaedf062010-05-29 22:36:02 +00001779 splice_geometry.x+=(ssize_t) splice_geometry.width/2;
1780 splice_geometry.y+=(ssize_t) splice_geometry.height/2;
cristy3ed852e2009-09-05 21:47:34 +00001781 break;
1782 }
1783 case EastGravity:
1784 {
cristyeaedf062010-05-29 22:36:02 +00001785 splice_geometry.x+=(ssize_t) splice_geometry.width;
1786 splice_geometry.y+=(ssize_t) splice_geometry.height/2;
cristy3ed852e2009-09-05 21:47:34 +00001787 break;
1788 }
1789 case SouthWestGravity:
1790 {
cristyeaedf062010-05-29 22:36:02 +00001791 splice_geometry.y+=(ssize_t) splice_geometry.height;
cristy3ed852e2009-09-05 21:47:34 +00001792 break;
1793 }
1794 case SouthGravity:
1795 {
cristyeaedf062010-05-29 22:36:02 +00001796 splice_geometry.x+=(ssize_t) splice_geometry.width/2;
1797 splice_geometry.y+=(ssize_t) splice_geometry.height;
cristy3ed852e2009-09-05 21:47:34 +00001798 break;
1799 }
1800 case SouthEastGravity:
1801 {
cristyeaedf062010-05-29 22:36:02 +00001802 splice_geometry.x+=(ssize_t) splice_geometry.width;
1803 splice_geometry.y+=(ssize_t) splice_geometry.height;
cristy3ed852e2009-09-05 21:47:34 +00001804 break;
1805 }
1806 }
1807 /*
1808 Splice image.
1809 */
1810 status=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00001811 progress=0;
cristy46ff2672012-12-14 15:32:26 +00001812 image_view=AcquireVirtualCacheView(image,exception);
1813 splice_view=AcquireAuthenticCacheView(splice_image,exception);
cristyb5d5f722009-11-04 03:03:49 +00001814#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +00001815 #pragma omp parallel for schedule(static,4) shared(progress,status) \
cristycb7dfcc2013-01-06 18:34:59 +00001816 magick_threads(image,splice_image,1,1)
cristy3ed852e2009-09-05 21:47:34 +00001817#endif
cristybb503372010-05-27 20:51:26 +00001818 for (y=0; y < (ssize_t) splice_geometry.y; y++)
cristy3ed852e2009-09-05 21:47:34 +00001819 {
cristy4c08aed2011-07-01 19:47:50 +00001820 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00001821 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001822
cristybb503372010-05-27 20:51:26 +00001823 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001824 x;
1825
cristy4c08aed2011-07-01 19:47:50 +00001826 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00001827 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001828
1829 if (status == MagickFalse)
1830 continue;
1831 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1832 q=QueueCacheViewAuthenticPixels(splice_view,0,y,splice_image->columns,1,
1833 exception);
cristy4c08aed2011-07-01 19:47:50 +00001834 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00001835 {
1836 status=MagickFalse;
1837 continue;
1838 }
cristy3ed852e2009-09-05 21:47:34 +00001839 for (x=0; x < splice_geometry.x; x++)
1840 {
cristy010d7d12011-08-31 01:02:48 +00001841 register ssize_t
1842 i;
1843
cristy883fde12013-04-08 00:50:13 +00001844 if (GetPixelReadMask(image,p) == 0)
cristy10a6c612012-01-29 21:41:05 +00001845 {
cristyc3a58022013-10-09 23:22:42 +00001846 SetPixelBackgoundColor(splice_image,q);
cristy10a6c612012-01-29 21:41:05 +00001847 p+=GetPixelChannels(image);
1848 q+=GetPixelChannels(splice_image);
1849 continue;
1850 }
cristy010d7d12011-08-31 01:02:48 +00001851 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1852 {
cristy5a23c552013-02-13 14:34:28 +00001853 PixelChannel channel=GetPixelChannelChannel(image,i);
1854 PixelTrait traits=GetPixelChannelTraits(image,channel);
1855 PixelTrait splice_traits=GetPixelChannelTraits(splice_image,channel);
cristy010d7d12011-08-31 01:02:48 +00001856 if ((traits == UndefinedPixelTrait) ||
1857 (splice_traits == UndefinedPixelTrait))
1858 continue;
cristy0beccfa2011-09-25 20:47:53 +00001859 SetPixelChannel(splice_image,channel,p[i],q);
cristy010d7d12011-08-31 01:02:48 +00001860 }
cristyed231572011-07-14 02:18:59 +00001861 p+=GetPixelChannels(image);
1862 q+=GetPixelChannels(splice_image);
cristy3ed852e2009-09-05 21:47:34 +00001863 }
cristybb503372010-05-27 20:51:26 +00001864 for ( ; x < (ssize_t) (splice_geometry.x+splice_geometry.width); x++)
cristyed231572011-07-14 02:18:59 +00001865 q+=GetPixelChannels(splice_image);
cristybb503372010-05-27 20:51:26 +00001866 for ( ; x < (ssize_t) splice_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001867 {
cristy010d7d12011-08-31 01:02:48 +00001868 register ssize_t
1869 i;
1870
cristy883fde12013-04-08 00:50:13 +00001871 if (GetPixelReadMask(image,p) == 0)
cristy10a6c612012-01-29 21:41:05 +00001872 {
cristyc3a58022013-10-09 23:22:42 +00001873 SetPixelBackgoundColor(splice_image,q);
cristy10a6c612012-01-29 21:41:05 +00001874 p+=GetPixelChannels(image);
1875 q+=GetPixelChannels(splice_image);
1876 continue;
1877 }
cristy010d7d12011-08-31 01:02:48 +00001878 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1879 {
cristy5a23c552013-02-13 14:34:28 +00001880 PixelChannel channel=GetPixelChannelChannel(image,i);
1881 PixelTrait traits=GetPixelChannelTraits(image,channel);
1882 PixelTrait splice_traits=GetPixelChannelTraits(splice_image,channel);
cristy010d7d12011-08-31 01:02:48 +00001883 if ((traits == UndefinedPixelTrait) ||
1884 (splice_traits == UndefinedPixelTrait))
1885 continue;
cristy0beccfa2011-09-25 20:47:53 +00001886 SetPixelChannel(splice_image,channel,p[i],q);
cristy010d7d12011-08-31 01:02:48 +00001887 }
cristyed231572011-07-14 02:18:59 +00001888 p+=GetPixelChannels(image);
1889 q+=GetPixelChannels(splice_image);
cristy3ed852e2009-09-05 21:47:34 +00001890 }
1891 if (SyncCacheViewAuthenticPixels(splice_view,exception) == MagickFalse)
1892 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00001893 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1894 {
1895 MagickBooleanType
1896 proceed;
1897
cristyb5d5f722009-11-04 03:03:49 +00001898#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristya5ab7ad2012-01-21 23:49:58 +00001899 #pragma omp critical (MagickCore_TransposeImage)
cristy3ed852e2009-09-05 21:47:34 +00001900#endif
1901 proceed=SetImageProgress(image,SpliceImageTag,progress++,
1902 splice_image->rows);
1903 if (proceed == MagickFalse)
1904 status=MagickFalse;
1905 }
1906 }
cristyb5d5f722009-11-04 03:03:49 +00001907#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +00001908 #pragma omp parallel for schedule(static,4) shared(progress,status) \
cristycb7dfcc2013-01-06 18:34:59 +00001909 magick_threads(image,splice_image,1,1)
cristy3ed852e2009-09-05 21:47:34 +00001910#endif
cristybb503372010-05-27 20:51:26 +00001911 for (y=(ssize_t) (splice_geometry.y+splice_geometry.height);
1912 y < (ssize_t) splice_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001913 {
cristy4c08aed2011-07-01 19:47:50 +00001914 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00001915 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001916
cristybb503372010-05-27 20:51:26 +00001917 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001918 x;
1919
cristy4c08aed2011-07-01 19:47:50 +00001920 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00001921 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001922
1923 if (status == MagickFalse)
1924 continue;
cristyeaedf062010-05-29 22:36:02 +00001925 p=GetCacheViewVirtualPixels(image_view,0,y-(ssize_t) splice_geometry.height,
cristy3ed852e2009-09-05 21:47:34 +00001926 image->columns,1,exception);
cristy13d07042010-11-21 20:56:18 +00001927 if ((y < 0) || (y >= (ssize_t) splice_image->rows))
cristy2224dcd2010-11-15 00:49:30 +00001928 continue;
cristy3ed852e2009-09-05 21:47:34 +00001929 q=QueueCacheViewAuthenticPixels(splice_view,0,y,splice_image->columns,1,
1930 exception);
cristy4c08aed2011-07-01 19:47:50 +00001931 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00001932 {
1933 status=MagickFalse;
1934 continue;
1935 }
cristy3ed852e2009-09-05 21:47:34 +00001936 for (x=0; x < splice_geometry.x; x++)
1937 {
cristy010d7d12011-08-31 01:02:48 +00001938 register ssize_t
1939 i;
1940
cristy883fde12013-04-08 00:50:13 +00001941 if (GetPixelReadMask(image,q) == 0)
cristy10a6c612012-01-29 21:41:05 +00001942 {
cristyc3a58022013-10-09 23:22:42 +00001943 SetPixelBackgoundColor(splice_image,q);
cristy10a6c612012-01-29 21:41:05 +00001944 p+=GetPixelChannels(image);
1945 q+=GetPixelChannels(splice_image);
1946 continue;
1947 }
cristy010d7d12011-08-31 01:02:48 +00001948 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1949 {
cristy5a23c552013-02-13 14:34:28 +00001950 PixelChannel channel=GetPixelChannelChannel(image,i);
1951 PixelTrait traits=GetPixelChannelTraits(image,channel);
1952 PixelTrait splice_traits=GetPixelChannelTraits(splice_image,channel);
cristy010d7d12011-08-31 01:02:48 +00001953 if ((traits == UndefinedPixelTrait) ||
1954 (splice_traits == UndefinedPixelTrait))
1955 continue;
cristy0beccfa2011-09-25 20:47:53 +00001956 SetPixelChannel(splice_image,channel,p[i],q);
cristy010d7d12011-08-31 01:02:48 +00001957 }
cristyed231572011-07-14 02:18:59 +00001958 p+=GetPixelChannels(image);
1959 q+=GetPixelChannels(splice_image);
cristy3ed852e2009-09-05 21:47:34 +00001960 }
cristybb503372010-05-27 20:51:26 +00001961 for ( ; x < (ssize_t) (splice_geometry.x+splice_geometry.width); x++)
cristyed231572011-07-14 02:18:59 +00001962 q+=GetPixelChannels(splice_image);
cristybb503372010-05-27 20:51:26 +00001963 for ( ; x < (ssize_t) splice_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001964 {
cristy010d7d12011-08-31 01:02:48 +00001965 register ssize_t
1966 i;
1967
cristy883fde12013-04-08 00:50:13 +00001968 if (GetPixelReadMask(image,q) == 0)
cristy10a6c612012-01-29 21:41:05 +00001969 {
cristyc3a58022013-10-09 23:22:42 +00001970 SetPixelBackgoundColor(splice_image,q);
cristy10a6c612012-01-29 21:41:05 +00001971 p+=GetPixelChannels(image);
1972 q+=GetPixelChannels(splice_image);
1973 continue;
1974 }
cristy010d7d12011-08-31 01:02:48 +00001975 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1976 {
cristy5a23c552013-02-13 14:34:28 +00001977 PixelChannel channel=GetPixelChannelChannel(image,i);
1978 PixelTrait traits=GetPixelChannelTraits(image,channel);
1979 PixelTrait splice_traits=GetPixelChannelTraits(splice_image,channel);
cristy010d7d12011-08-31 01:02:48 +00001980 if ((traits == UndefinedPixelTrait) ||
1981 (splice_traits == UndefinedPixelTrait))
1982 continue;
cristy0beccfa2011-09-25 20:47:53 +00001983 SetPixelChannel(splice_image,channel,p[i],q);
cristy010d7d12011-08-31 01:02:48 +00001984 }
cristyed231572011-07-14 02:18:59 +00001985 p+=GetPixelChannels(image);
1986 q+=GetPixelChannels(splice_image);
cristy3ed852e2009-09-05 21:47:34 +00001987 }
1988 if (SyncCacheViewAuthenticPixels(splice_view,exception) == MagickFalse)
1989 status=MagickFalse;
1990 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1991 {
1992 MagickBooleanType
1993 proceed;
1994
cristyb5d5f722009-11-04 03:03:49 +00001995#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristya5ab7ad2012-01-21 23:49:58 +00001996 #pragma omp critical (MagickCore_TransposeImage)
cristy3ed852e2009-09-05 21:47:34 +00001997#endif
1998 proceed=SetImageProgress(image,SpliceImageTag,progress++,
1999 splice_image->rows);
2000 if (proceed == MagickFalse)
2001 status=MagickFalse;
2002 }
2003 }
2004 splice_view=DestroyCacheView(splice_view);
2005 image_view=DestroyCacheView(image_view);
2006 if (status == MagickFalse)
2007 splice_image=DestroyImage(splice_image);
2008 return(splice_image);
2009}
2010
2011/*
2012%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2013% %
2014% %
2015% %
2016% T r a n s f o r m I m a g e %
2017% %
2018% %
2019% %
2020%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2021%
2022% TransformImage() is a convenience method that behaves like ResizeImage() or
2023% CropImage() but accepts scaling and/or cropping information as a region
2024% geometry specification. If the operation fails, the original image handle
anthony9f4f0342011-03-28 11:47:22 +00002025% is left as is.
2026%
2027% This should only be used for single images.
cristy3ed852e2009-09-05 21:47:34 +00002028%
cristy010d7d12011-08-31 01:02:48 +00002029% This function destroys what it assumes to be a single image list.
2030% If the input image is part of a larger list, all other images in that list
2031% will be simply 'lost', not destroyed.
2032%
2033% Also if the crop generates a list of images only the first image is resized.
2034% And finally if the crop succeeds and the resize failed, you will get a
2035% cropped image, as well as a 'false' or 'failed' report.
2036%
2037% This function and should probably be depreciated in favor of direct calls
2038% to CropImageToTiles() or ResizeImage(), as appropriate.
2039%
cristy3ed852e2009-09-05 21:47:34 +00002040% The format of the TransformImage method is:
2041%
2042% MagickBooleanType TransformImage(Image **image,const char *crop_geometry,
cristye941a752011-10-15 01:52:48 +00002043% const char *image_geometry,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002044%
2045% A description of each parameter follows:
2046%
2047% o image: the image The transformed image is returned as this parameter.
2048%
2049% o crop_geometry: A crop geometry string. This geometry defines a
2050% subregion of the image to crop.
2051%
2052% o image_geometry: An image geometry string. This geometry defines the
2053% final size of the image.
2054%
cristye941a752011-10-15 01:52:48 +00002055% o exception: return any errors or warnings in this structure.
2056%
cristy3ed852e2009-09-05 21:47:34 +00002057*/
2058MagickExport MagickBooleanType TransformImage(Image **image,
cristye941a752011-10-15 01:52:48 +00002059 const char *crop_geometry,const char *image_geometry,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002060{
2061 Image
2062 *resize_image,
2063 *transform_image;
2064
2065 MagickStatusType
2066 flags;
2067
2068 RectangleInfo
2069 geometry;
2070
2071 assert(image != (Image **) NULL);
2072 assert((*image)->signature == MagickSignature);
2073 if ((*image)->debug != MagickFalse)
2074 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
2075 transform_image=(*image);
2076 if (crop_geometry != (const char *) NULL)
2077 {
2078 Image
2079 *crop_image;
2080
cristy3ed852e2009-09-05 21:47:34 +00002081 /*
2082 Crop image to a user specified size.
2083 */
cristye941a752011-10-15 01:52:48 +00002084 crop_image=CropImageToTiles(*image,crop_geometry,exception);
cristy3ed852e2009-09-05 21:47:34 +00002085 if (crop_image == (Image *) NULL)
cristye941a752011-10-15 01:52:48 +00002086 transform_image=CloneImage(*image,0,0,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00002087 else
2088 {
2089 transform_image=DestroyImage(transform_image);
2090 transform_image=GetFirstImageInList(crop_image);
2091 }
2092 *image=transform_image;
2093 }
2094 if (image_geometry == (const char *) NULL)
2095 return(MagickTrue);
anthony9f4f0342011-03-28 11:47:22 +00002096
cristy3ed852e2009-09-05 21:47:34 +00002097 /*
2098 Scale image to a user specified size.
2099 */
cristye941a752011-10-15 01:52:48 +00002100 flags=ParseRegionGeometry(transform_image,image_geometry,&geometry,exception);
cristy288b0802011-03-28 13:05:34 +00002101 (void) flags;
cristy3ed852e2009-09-05 21:47:34 +00002102 if ((transform_image->columns == geometry.width) &&
2103 (transform_image->rows == geometry.height))
2104 return(MagickTrue);
cristy15b98cd2010-09-12 19:42:50 +00002105 resize_image=ResizeImage(transform_image,geometry.width,geometry.height,
cristyaa2c16c2012-03-25 22:21:35 +00002106 transform_image->filter,exception);
cristy3ed852e2009-09-05 21:47:34 +00002107 if (resize_image == (Image *) NULL)
2108 return(MagickFalse);
2109 transform_image=DestroyImage(transform_image);
2110 transform_image=resize_image;
2111 *image=transform_image;
2112 return(MagickTrue);
2113}
2114
2115/*
2116%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2117% %
2118% %
2119% %
2120% T r a n s f o r m I m a g e s %
2121% %
2122% %
anthonybdaa5b32010-12-10 13:06:46 +00002123% %
2124%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
cristy3ed852e2009-09-05 21:47:34 +00002125%
2126% TransformImages() calls TransformImage() on each image of a sequence.
2127%
2128% The format of the TransformImage method is:
2129%
2130% MagickBooleanType TransformImages(Image **image,
cristye941a752011-10-15 01:52:48 +00002131% const char *crop_geometry,const char *image_geometry,
2132% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002133%
2134% A description of each parameter follows:
2135%
2136% o image: the image The transformed image is returned as this parameter.
2137%
2138% o crop_geometry: A crop geometry string. This geometry defines a
2139% subregion of the image to crop.
2140%
2141% o image_geometry: An image geometry string. This geometry defines the
2142% final size of the image.
2143%
cristye941a752011-10-15 01:52:48 +00002144% o exception: return any errors or warnings in this structure.
2145%
cristy3ed852e2009-09-05 21:47:34 +00002146*/
2147MagickExport MagickBooleanType TransformImages(Image **images,
cristye941a752011-10-15 01:52:48 +00002148 const char *crop_geometry,const char *image_geometry,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002149{
2150 Image
2151 *image,
2152 **image_list,
2153 *transform_images;
2154
2155 MagickStatusType
2156 status;
2157
cristybb503372010-05-27 20:51:26 +00002158 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002159 i;
2160
2161 assert(images != (Image **) NULL);
2162 assert((*images)->signature == MagickSignature);
2163 if ((*images)->debug != MagickFalse)
2164 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
2165 (*images)->filename);
cristye941a752011-10-15 01:52:48 +00002166 image_list=ImageListToArray(*images,exception);
cristy3ed852e2009-09-05 21:47:34 +00002167 if (image_list == (Image **) NULL)
2168 return(MagickFalse);
2169 status=MagickTrue;
2170 transform_images=NewImageList();
2171 for (i=0; image_list[i] != (Image *) NULL; i++)
2172 {
2173 image=image_list[i];
cristyaae13f62013-08-15 14:41:32 +00002174 status&=TransformImage(&image,crop_geometry,image_geometry,exception);
cristy3ed852e2009-09-05 21:47:34 +00002175 AppendImageToList(&transform_images,image);
2176 }
2177 *images=transform_images;
2178 image_list=(Image **) RelinquishMagickMemory(image_list);
2179 return(status != 0 ? MagickTrue : MagickFalse);
2180}
2181
2182/*
2183%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2184% %
2185% %
2186% %
2187% T r a n s p o s e I m a g e %
2188% %
2189% %
2190% %
2191%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2192%
2193% TransposeImage() creates a horizontal mirror image by reflecting the pixels
2194% around the central y-axis while rotating them by 90 degrees.
2195%
2196% The format of the TransposeImage method is:
2197%
2198% Image *TransposeImage(const Image *image,ExceptionInfo *exception)
2199%
2200% A description of each parameter follows:
2201%
2202% o image: the image.
2203%
2204% o exception: return any errors or warnings in this structure.
2205%
2206*/
2207MagickExport Image *TransposeImage(const Image *image,ExceptionInfo *exception)
2208{
2209#define TransposeImageTag "Transpose/Image"
2210
cristyc4c8d132010-01-07 01:58:38 +00002211 CacheView
2212 *image_view,
2213 *transpose_view;
2214
cristy3ed852e2009-09-05 21:47:34 +00002215 Image
2216 *transpose_image;
2217
cristy3ed852e2009-09-05 21:47:34 +00002218 MagickBooleanType
2219 status;
2220
cristybb503372010-05-27 20:51:26 +00002221 MagickOffsetType
2222 progress;
2223
cristy3ed852e2009-09-05 21:47:34 +00002224 RectangleInfo
2225 page;
2226
cristybb503372010-05-27 20:51:26 +00002227 ssize_t
2228 y;
2229
cristy3ed852e2009-09-05 21:47:34 +00002230 assert(image != (const Image *) NULL);
2231 assert(image->signature == MagickSignature);
2232 if (image->debug != MagickFalse)
2233 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2234 assert(exception != (ExceptionInfo *) NULL);
2235 assert(exception->signature == MagickSignature);
2236 transpose_image=CloneImage(image,image->rows,image->columns,MagickTrue,
2237 exception);
2238 if (transpose_image == (Image *) NULL)
2239 return((Image *) NULL);
2240 /*
2241 Transpose image.
2242 */
2243 status=MagickTrue;
2244 progress=0;
cristy46ff2672012-12-14 15:32:26 +00002245 image_view=AcquireVirtualCacheView(image,exception);
2246 transpose_view=AcquireAuthenticCacheView(transpose_image,exception);
cristyb5d5f722009-11-04 03:03:49 +00002247#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +00002248 #pragma omp parallel for schedule(static,4) shared(progress,status) \
cristy5e6b2592012-12-19 14:08:11 +00002249 magick_threads(image,transpose_image,image->rows,1)
cristy3ed852e2009-09-05 21:47:34 +00002250#endif
cristybb503372010-05-27 20:51:26 +00002251 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002252 {
cristy4c08aed2011-07-01 19:47:50 +00002253 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00002254 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002255
cristy4c08aed2011-07-01 19:47:50 +00002256 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00002257 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002258
cristy4c08aed2011-07-01 19:47:50 +00002259 register ssize_t
2260 x;
2261
cristy3ed852e2009-09-05 21:47:34 +00002262 if (status == MagickFalse)
2263 continue;
cristybb503372010-05-27 20:51:26 +00002264 p=GetCacheViewVirtualPixels(image_view,0,(ssize_t) image->rows-y-1,
cristy3ed852e2009-09-05 21:47:34 +00002265 image->columns,1,exception);
cristy9af9b5d2010-08-15 17:04:28 +00002266 q=QueueCacheViewAuthenticPixels(transpose_view,(ssize_t) (image->rows-y-1),
2267 0,1,transpose_image->rows,exception);
cristy4c08aed2011-07-01 19:47:50 +00002268 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00002269 {
2270 status=MagickFalse;
2271 continue;
2272 }
cristy4c08aed2011-07-01 19:47:50 +00002273 for (x=0; x < (ssize_t) image->columns; x++)
2274 {
cristy010d7d12011-08-31 01:02:48 +00002275 register ssize_t
2276 i;
2277
cristy883fde12013-04-08 00:50:13 +00002278 if (GetPixelReadMask(image,q) == 0)
cristy10a6c612012-01-29 21:41:05 +00002279 {
cristyc3a58022013-10-09 23:22:42 +00002280 SetPixelBackgoundColor(transpose_image,q);
cristy10a6c612012-01-29 21:41:05 +00002281 p+=GetPixelChannels(image);
2282 q+=GetPixelChannels(transpose_image);
2283 continue;
2284 }
cristy010d7d12011-08-31 01:02:48 +00002285 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2286 {
cristy5a23c552013-02-13 14:34:28 +00002287 PixelChannel channel=GetPixelChannelChannel(image,i);
2288 PixelTrait traits=GetPixelChannelTraits(image,channel);
2289 PixelTrait transpose_traits=GetPixelChannelTraits(transpose_image,
2290 channel);
cristy010d7d12011-08-31 01:02:48 +00002291 if ((traits == UndefinedPixelTrait) ||
2292 (transpose_traits == UndefinedPixelTrait))
2293 continue;
cristy0beccfa2011-09-25 20:47:53 +00002294 SetPixelChannel(transpose_image,channel,p[i],q);
cristy010d7d12011-08-31 01:02:48 +00002295 }
cristyed231572011-07-14 02:18:59 +00002296 p+=GetPixelChannels(image);
2297 q+=GetPixelChannels(transpose_image);
cristy4c08aed2011-07-01 19:47:50 +00002298 }
cristy3ed852e2009-09-05 21:47:34 +00002299 if (SyncCacheViewAuthenticPixels(transpose_view,exception) == MagickFalse)
2300 status=MagickFalse;
2301 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2302 {
2303 MagickBooleanType
2304 proceed;
2305
cristyb5d5f722009-11-04 03:03:49 +00002306#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristya5ab7ad2012-01-21 23:49:58 +00002307 #pragma omp critical (MagickCore_TransposeImage)
cristy3ed852e2009-09-05 21:47:34 +00002308#endif
2309 proceed=SetImageProgress(image,TransposeImageTag,progress++,
2310 image->rows);
2311 if (proceed == MagickFalse)
2312 status=MagickFalse;
2313 }
2314 }
2315 transpose_view=DestroyCacheView(transpose_view);
2316 image_view=DestroyCacheView(image_view);
2317 transpose_image->type=image->type;
2318 page=transpose_image->page;
2319 Swap(page.width,page.height);
2320 Swap(page.x,page.y);
cristy3ed852e2009-09-05 21:47:34 +00002321 transpose_image->page=page;
2322 if (status == MagickFalse)
2323 transpose_image=DestroyImage(transpose_image);
2324 return(transpose_image);
2325}
2326
2327/*
2328%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2329% %
2330% %
2331% %
2332% T r a n s v e r s e I m a g e %
2333% %
2334% %
2335% %
2336%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2337%
2338% TransverseImage() creates a vertical mirror image by reflecting the pixels
2339% around the central x-axis while rotating them by 270 degrees.
2340%
2341% The format of the TransverseImage method is:
2342%
2343% Image *TransverseImage(const Image *image,ExceptionInfo *exception)
2344%
2345% A description of each parameter follows:
2346%
2347% o image: the image.
2348%
2349% o exception: return any errors or warnings in this structure.
2350%
2351*/
2352MagickExport Image *TransverseImage(const Image *image,ExceptionInfo *exception)
2353{
2354#define TransverseImageTag "Transverse/Image"
2355
cristyc4c8d132010-01-07 01:58:38 +00002356 CacheView
2357 *image_view,
2358 *transverse_view;
2359
cristy3ed852e2009-09-05 21:47:34 +00002360 Image
2361 *transverse_image;
2362
cristy3ed852e2009-09-05 21:47:34 +00002363 MagickBooleanType
2364 status;
2365
cristybb503372010-05-27 20:51:26 +00002366 MagickOffsetType
2367 progress;
2368
cristy3ed852e2009-09-05 21:47:34 +00002369 RectangleInfo
2370 page;
2371
cristybb503372010-05-27 20:51:26 +00002372 ssize_t
2373 y;
2374
cristy3ed852e2009-09-05 21:47:34 +00002375 assert(image != (const Image *) NULL);
2376 assert(image->signature == MagickSignature);
2377 if (image->debug != MagickFalse)
2378 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2379 assert(exception != (ExceptionInfo *) NULL);
2380 assert(exception->signature == MagickSignature);
2381 transverse_image=CloneImage(image,image->rows,image->columns,MagickTrue,
2382 exception);
2383 if (transverse_image == (Image *) NULL)
2384 return((Image *) NULL);
2385 /*
2386 Transverse image.
2387 */
2388 status=MagickTrue;
2389 progress=0;
cristy46ff2672012-12-14 15:32:26 +00002390 image_view=AcquireVirtualCacheView(image,exception);
2391 transverse_view=AcquireAuthenticCacheView(transverse_image,exception);
cristyb5d5f722009-11-04 03:03:49 +00002392#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +00002393 #pragma omp parallel for schedule(static,4) shared(progress,status) \
cristy5e6b2592012-12-19 14:08:11 +00002394 magick_threads(image,transverse_image,image->rows,1)
cristy3ed852e2009-09-05 21:47:34 +00002395#endif
cristybb503372010-05-27 20:51:26 +00002396 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002397 {
2398 MagickBooleanType
2399 sync;
2400
cristy4c08aed2011-07-01 19:47:50 +00002401 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +00002402 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002403
cristy4c08aed2011-07-01 19:47:50 +00002404 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00002405 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002406
cristy010d7d12011-08-31 01:02:48 +00002407 register ssize_t
2408 x;
2409
cristy3ed852e2009-09-05 21:47:34 +00002410 if (status == MagickFalse)
2411 continue;
2412 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
cristy010d7d12011-08-31 01:02:48 +00002413 q=QueueCacheViewAuthenticPixels(transverse_view,(ssize_t) (image->rows-y-1),
2414 0,1,transverse_image->rows,exception);
cristy4c08aed2011-07-01 19:47:50 +00002415 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00002416 {
2417 status=MagickFalse;
2418 continue;
2419 }
cristyed231572011-07-14 02:18:59 +00002420 q+=GetPixelChannels(transverse_image)*image->columns;
cristybb503372010-05-27 20:51:26 +00002421 for (x=0; x < (ssize_t) image->columns; x++)
cristy4c08aed2011-07-01 19:47:50 +00002422 {
cristy010d7d12011-08-31 01:02:48 +00002423 register ssize_t
2424 i;
2425
cristyed231572011-07-14 02:18:59 +00002426 q-=GetPixelChannels(transverse_image);
cristy883fde12013-04-08 00:50:13 +00002427 if (GetPixelReadMask(image,p) == 0)
cristy10a6c612012-01-29 21:41:05 +00002428 {
2429 p+=GetPixelChannels(image);
2430 continue;
2431 }
cristy010d7d12011-08-31 01:02:48 +00002432 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2433 {
cristy5a23c552013-02-13 14:34:28 +00002434 PixelChannel channel=GetPixelChannelChannel(image,i);
2435 PixelTrait traits=GetPixelChannelTraits(image,channel);
2436 PixelTrait transverse_traits=GetPixelChannelTraits(transverse_image,
2437 channel);
cristy010d7d12011-08-31 01:02:48 +00002438 if ((traits == UndefinedPixelTrait) ||
2439 (transverse_traits == UndefinedPixelTrait))
2440 continue;
cristy0beccfa2011-09-25 20:47:53 +00002441 SetPixelChannel(transverse_image,channel,p[i],q);
cristy010d7d12011-08-31 01:02:48 +00002442 }
cristyed231572011-07-14 02:18:59 +00002443 p+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00002444 }
cristy3ed852e2009-09-05 21:47:34 +00002445 sync=SyncCacheViewAuthenticPixels(transverse_view,exception);
2446 if (sync == MagickFalse)
2447 status=MagickFalse;
2448 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2449 {
2450 MagickBooleanType
2451 proceed;
2452
cristyb5d5f722009-11-04 03:03:49 +00002453#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristya5ab7ad2012-01-21 23:49:58 +00002454 #pragma omp critical (MagickCore_TransverseImage)
cristy3ed852e2009-09-05 21:47:34 +00002455#endif
2456 proceed=SetImageProgress(image,TransverseImageTag,progress++,
2457 image->rows);
2458 if (proceed == MagickFalse)
2459 status=MagickFalse;
2460 }
2461 }
2462 transverse_view=DestroyCacheView(transverse_view);
2463 image_view=DestroyCacheView(image_view);
2464 transverse_image->type=image->type;
2465 page=transverse_image->page;
2466 Swap(page.width,page.height);
2467 Swap(page.x,page.y);
anthonybdaa5b32010-12-10 13:06:46 +00002468 if (page.width != 0)
2469 page.x=(ssize_t) (page.width-transverse_image->columns-page.x);
cristy3ed852e2009-09-05 21:47:34 +00002470 if (page.height != 0)
cristybb503372010-05-27 20:51:26 +00002471 page.y=(ssize_t) (page.height-transverse_image->rows-page.y);
cristy3ed852e2009-09-05 21:47:34 +00002472 transverse_image->page=page;
2473 if (status == MagickFalse)
2474 transverse_image=DestroyImage(transverse_image);
2475 return(transverse_image);
2476}
2477
2478/*
2479%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2480% %
2481% %
2482% %
2483% T r i m I m a g e %
2484% %
2485% %
2486% %
2487%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2488%
2489% TrimImage() trims pixels from the image edges. It allocates the memory
2490% necessary for the new Image structure and returns a pointer to the new
2491% image.
2492%
2493% The format of the TrimImage method is:
2494%
2495% Image *TrimImage(const Image *image,ExceptionInfo *exception)
2496%
2497% A description of each parameter follows:
2498%
2499% o image: the image.
2500%
2501% o exception: return any errors or warnings in this structure.
2502%
2503*/
2504MagickExport Image *TrimImage(const Image *image,ExceptionInfo *exception)
2505{
2506 RectangleInfo
2507 geometry;
2508
2509 assert(image != (const Image *) NULL);
2510 assert(image->signature == MagickSignature);
2511 if (image->debug != MagickFalse)
2512 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2513 geometry=GetImageBoundingBox(image,exception);
2514 if ((geometry.width == 0) || (geometry.height == 0))
2515 {
2516 Image
2517 *crop_image;
2518
2519 crop_image=CloneImage(image,1,1,MagickTrue,exception);
2520 if (crop_image == (Image *) NULL)
2521 return((Image *) NULL);
cristy4c08aed2011-07-01 19:47:50 +00002522 crop_image->background_color.alpha=(Quantum) TransparentAlpha;
cristy6d94a302013-03-01 00:47:47 +00002523 crop_image->alpha_trait=BlendPixelTrait;
cristyea1a8aa2011-10-20 13:24:06 +00002524 (void) SetImageBackgroundColor(crop_image,exception);
cristy3ed852e2009-09-05 21:47:34 +00002525 crop_image->page=image->page;
2526 crop_image->page.x=(-1);
2527 crop_image->page.y=(-1);
2528 return(crop_image);
2529 }
2530 geometry.x+=image->page.x;
2531 geometry.y+=image->page.y;
2532 return(CropImage(image,&geometry,exception));
2533}