blob: cd82fdfce551158191b93c870ccfdb2fb5bf63cf [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 %
cristyde984cd2013-12-01 14:49:27 +000016% Cristy %
cristy3ed852e2009-09-05 21:47:34 +000017% July 1992 %
18% %
19% %
Cristyf6ff9ea2016-12-05 09:53:35 -050020% Copyright 1999-2017 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% %
Cristyf19d4142017-04-24 11:34:30 -040026% https://www.imagemagick.org/script/license.php %
cristy3ed852e2009-09-05 21:47:34 +000027% %
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);
cristye1c94d92015-06-28 12:16:33 +0000105 assert(image->signature == MagickCoreSignature);
cristyfa5f6c72013-01-01 14:37:35 +0000106 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000107 assert(exception->signature == MagickCoreSignature);
cristyfa5f6c72013-01-01 14:37:35 +0000108 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 {
cristyfb348292015-04-12 00:28:54 +0000140 orient_image=RotateImage(image,90.0,exception);
cristyfa5f6c72013-01-01 14:37:35 +0000141 break;
142 }
143 case RightBottomOrientation:
144 {
cristyfb348292015-04-12 00:28:54 +0000145 orient_image=TransverseImage(image,exception);
cristyfa5f6c72013-01-01 14:37:35 +0000146 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);
cristye1c94d92015-06-28 12:16:33 +0000215 assert(image->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +0000216 if (image->debug != MagickFalse)
217 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
218 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000219 assert(exception->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +0000220 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
dirk05d2ff72015-11-18 23:13:43 +0100259 *magick_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
dirk05d2ff72015-11-18 23:13:43 +0100265 *magick_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
dirk05d2ff72015-11-18 23:13:43 +0100323 *magick_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
dirk05d2ff72015-11-18 23:13:43 +0100329 *magick_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);
cristye1c94d92015-06-28 12:16:33 +0000432 assert(images->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +0000433 if (images->debug != MagickFalse)
434 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
435 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000436 assert(exception->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +0000437 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
cristy8969a362014-01-16 01:53:19 +0000443 assert(images != (Image *) NULL);
cristy3ed852e2009-09-05 21:47:34 +0000444 cmyk_image=CloneImage(images,images->columns,images->rows,MagickTrue,
445 exception);
446 if (cmyk_image == (Image *) NULL)
447 break;
cristy574cc262011-08-05 01:23:58 +0000448 if (SetImageStorageClass(cmyk_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000449 break;
cristy63240882011-08-05 19:05:27 +0000450 (void) SetImageColorspace(cmyk_image,CMYKColorspace,exception);
cristyd000c802011-09-20 02:03:18 +0000451 for (i=0; i < 4; i++)
cristy3ed852e2009-09-05 21:47:34 +0000452 {
cristy46ff2672012-12-14 15:32:26 +0000453 image_view=AcquireVirtualCacheView(images,exception);
454 cmyk_view=AcquireAuthenticCacheView(cmyk_image,exception);
cristyd000c802011-09-20 02:03:18 +0000455 for (y=0; y < (ssize_t) images->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000456 {
cristyd000c802011-09-20 02:03:18 +0000457 register const Quantum
dirk05d2ff72015-11-18 23:13:43 +0100458 *magick_restrict p;
cristyd000c802011-09-20 02:03:18 +0000459
460 register ssize_t
461 x;
462
463 register Quantum
dirk05d2ff72015-11-18 23:13:43 +0100464 *magick_restrict q;
cristyd000c802011-09-20 02:03:18 +0000465
466 p=GetCacheViewVirtualPixels(image_view,0,y,images->columns,1,exception);
467 q=QueueCacheViewAuthenticPixels(cmyk_view,0,y,cmyk_image->columns,1,
468 exception);
469 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
470 break;
471 for (x=0; x < (ssize_t) images->columns; x++)
472 {
473 Quantum
474 pixel;
475
476 pixel=QuantumRange-GetPixelIntensity(images,p);
477 switch (i)
478 {
479 case 0: SetPixelCyan(cmyk_image,pixel,q); break;
480 case 1: SetPixelMagenta(cmyk_image,pixel,q); break;
481 case 2: SetPixelYellow(cmyk_image,pixel,q); break;
482 case 3: SetPixelBlack(cmyk_image,pixel,q); break;
483 default: break;
484 }
485 p+=GetPixelChannels(images);
486 q+=GetPixelChannels(cmyk_image);
487 }
488 if (SyncCacheViewAuthenticPixels(cmyk_view,exception) == MagickFalse)
489 break;
cristy3ed852e2009-09-05 21:47:34 +0000490 }
cristyd000c802011-09-20 02:03:18 +0000491 cmyk_view=DestroyCacheView(cmyk_view);
492 image_view=DestroyCacheView(image_view);
493 images=GetNextImageInList(images);
494 if (images == (Image *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000495 break;
496 }
cristy3ed852e2009-09-05 21:47:34 +0000497 AppendImageToList(&cmyk_images,cmyk_image);
cristy3ed852e2009-09-05 21:47:34 +0000498 }
499 return(cmyk_images);
500}
501
502/*
503%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
504% %
505% %
506% %
507% C r o p I m a g e %
508% %
509% %
510% %
511%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
512%
513% CropImage() extracts a region of the image starting at the offset defined
anthony9f4f0342011-03-28 11:47:22 +0000514% by geometry. Region must be fully defined, and no special handling of
515% geometry flags is performed.
cristy3ed852e2009-09-05 21:47:34 +0000516%
517% The format of the CropImage method is:
518%
519% Image *CropImage(const Image *image,const RectangleInfo *geometry,
520% ExceptionInfo *exception)
521%
522% A description of each parameter follows:
523%
524% o image: the image.
525%
526% o geometry: Define the region of the image to crop with members
527% x, y, width, and height.
528%
529% o exception: return any errors or warnings in this structure.
530%
531*/
532MagickExport Image *CropImage(const Image *image,const RectangleInfo *geometry,
533 ExceptionInfo *exception)
534{
535#define CropImageTag "Crop/Image"
536
cristyc4c8d132010-01-07 01:58:38 +0000537 CacheView
538 *crop_view,
539 *image_view;
540
cristy3ed852e2009-09-05 21:47:34 +0000541 Image
542 *crop_image;
543
cristy3ed852e2009-09-05 21:47:34 +0000544 MagickBooleanType
545 status;
546
cristybb503372010-05-27 20:51:26 +0000547 MagickOffsetType
548 progress;
549
cristy010d7d12011-08-31 01:02:48 +0000550 OffsetInfo
551 offset;
552
cristy3ed852e2009-09-05 21:47:34 +0000553 RectangleInfo
554 bounding_box,
555 page;
556
cristybb503372010-05-27 20:51:26 +0000557 ssize_t
558 y;
559
cristy3ed852e2009-09-05 21:47:34 +0000560 /*
561 Check crop geometry.
562 */
563 assert(image != (const Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000564 assert(image->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +0000565 if (image->debug != MagickFalse)
566 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
567 assert(geometry != (const RectangleInfo *) NULL);
568 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000569 assert(exception->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +0000570 bounding_box=image->page;
571 if ((bounding_box.width == 0) || (bounding_box.height == 0))
572 {
573 bounding_box.width=image->columns;
574 bounding_box.height=image->rows;
575 }
576 page=(*geometry);
577 if (page.width == 0)
578 page.width=bounding_box.width;
579 if (page.height == 0)
580 page.height=bounding_box.height;
cristybb503372010-05-27 20:51:26 +0000581 if (((bounding_box.x-page.x) >= (ssize_t) page.width) ||
582 ((bounding_box.y-page.y) >= (ssize_t) page.height) ||
583 ((page.x-bounding_box.x) > (ssize_t) image->columns) ||
584 ((page.y-bounding_box.y) > (ssize_t) image->rows))
cristy3ed852e2009-09-05 21:47:34 +0000585 {
586 /*
587 Crop is not within virtual canvas, return 1 pixel transparent image.
588 */
589 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
cristyefe601c2013-01-05 17:51:12 +0000590 "GeometryDoesNotContainImage","`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000591 crop_image=CloneImage(image,1,1,MagickTrue,exception);
592 if (crop_image == (Image *) NULL)
593 return((Image *) NULL);
cristy4c08aed2011-07-01 19:47:50 +0000594 crop_image->background_color.alpha=(Quantum) TransparentAlpha;
cristy6d94a302013-03-01 00:47:47 +0000595 crop_image->alpha_trait=BlendPixelTrait;
cristyea1a8aa2011-10-20 13:24:06 +0000596 (void) SetImageBackgroundColor(crop_image,exception);
cristy3ed852e2009-09-05 21:47:34 +0000597 crop_image->page=bounding_box;
598 crop_image->page.x=(-1);
599 crop_image->page.y=(-1);
600 if (crop_image->dispose == BackgroundDispose)
601 crop_image->dispose=NoneDispose;
602 return(crop_image);
603 }
604 if ((page.x < 0) && (bounding_box.x >= 0))
605 {
606 page.width+=page.x-bounding_box.x;
607 page.x=0;
608 }
609 else
610 {
611 page.width-=bounding_box.x-page.x;
612 page.x-=bounding_box.x;
613 if (page.x < 0)
614 page.x=0;
615 }
616 if ((page.y < 0) && (bounding_box.y >= 0))
617 {
618 page.height+=page.y-bounding_box.y;
619 page.y=0;
620 }
621 else
622 {
623 page.height-=bounding_box.y-page.y;
624 page.y-=bounding_box.y;
625 if (page.y < 0)
626 page.y=0;
627 }
cristycca05852014-04-30 10:27:20 +0000628 if ((page.x+(ssize_t) page.width) > (ssize_t) image->columns)
cristy3ed852e2009-09-05 21:47:34 +0000629 page.width=image->columns-page.x;
cristy1e4aa462010-02-15 00:04:18 +0000630 if ((geometry->width != 0) && (page.width > geometry->width))
631 page.width=geometry->width;
cristycca05852014-04-30 10:27:20 +0000632 if ((page.y+(ssize_t) page.height) > (ssize_t) image->rows)
cristy3ed852e2009-09-05 21:47:34 +0000633 page.height=image->rows-page.y;
cristy1e4aa462010-02-15 00:04:18 +0000634 if ((geometry->height != 0) && (page.height > geometry->height))
635 page.height=geometry->height;
cristy3ed852e2009-09-05 21:47:34 +0000636 bounding_box.x+=page.x;
637 bounding_box.y+=page.y;
638 if ((page.width == 0) || (page.height == 0))
639 {
640 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
cristyefe601c2013-01-05 17:51:12 +0000641 "GeometryDoesNotContainImage","`%s'",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000642 return((Image *) NULL);
643 }
644 /*
645 Initialize crop image attributes.
646 */
647 crop_image=CloneImage(image,page.width,page.height,MagickTrue,exception);
648 if (crop_image == (Image *) NULL)
649 return((Image *) NULL);
650 crop_image->page.width=image->page.width;
651 crop_image->page.height=image->page.height;
cristy010d7d12011-08-31 01:02:48 +0000652 offset.x=(ssize_t) (bounding_box.x+bounding_box.width);
653 offset.y=(ssize_t) (bounding_box.y+bounding_box.height);
654 if ((offset.x > (ssize_t) image->page.width) ||
655 (offset.y > (ssize_t) image->page.height))
cristy3ed852e2009-09-05 21:47:34 +0000656 {
657 crop_image->page.width=bounding_box.width;
658 crop_image->page.height=bounding_box.height;
659 }
660 crop_image->page.x=bounding_box.x;
661 crop_image->page.y=bounding_box.y;
662 /*
663 Crop image.
664 */
665 status=MagickTrue;
666 progress=0;
cristy46ff2672012-12-14 15:32:26 +0000667 image_view=AcquireVirtualCacheView(image,exception);
668 crop_view=AcquireAuthenticCacheView(crop_image,exception);
cristy2224dcd2010-11-15 00:49:30 +0000669#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyd6432472013-01-06 16:56:13 +0000670 #pragma omp parallel for schedule(static,4) shared(status) \
671 magick_threads(image,crop_image,1,1)
cristy3ed852e2009-09-05 21:47:34 +0000672#endif
cristybb503372010-05-27 20:51:26 +0000673 for (y=0; y < (ssize_t) crop_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000674 {
cristy4c08aed2011-07-01 19:47:50 +0000675 register const Quantum
dirk05d2ff72015-11-18 23:13:43 +0100676 *magick_restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000677
cristy4c08aed2011-07-01 19:47:50 +0000678 register Quantum
dirk05d2ff72015-11-18 23:13:43 +0100679 *magick_restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000680
cristy5ce8df82011-07-07 14:52:23 +0000681 register ssize_t
cristy4c08aed2011-07-01 19:47:50 +0000682 x;
683
cristy3ed852e2009-09-05 21:47:34 +0000684 if (status == MagickFalse)
685 continue;
686 p=GetCacheViewVirtualPixels(image_view,page.x,page.y+y,crop_image->columns,
687 1,exception);
688 q=QueueCacheViewAuthenticPixels(crop_view,0,y,crop_image->columns,1,
689 exception);
cristy4c08aed2011-07-01 19:47:50 +0000690 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000691 {
692 status=MagickFalse;
693 continue;
694 }
cristy4c08aed2011-07-01 19:47:50 +0000695 for (x=0; x < (ssize_t) crop_image->columns; x++)
696 {
cristy010d7d12011-08-31 01:02:48 +0000697 register ssize_t
698 i;
699
Cristydf5e8b12016-12-02 17:26:39 -0500700 if (GetPixelWriteMask(image,p) == 0)
cristy10a6c612012-01-29 21:41:05 +0000701 {
cristyc3a58022013-10-09 23:22:42 +0000702 SetPixelBackgoundColor(crop_image,q);
cristy10a6c612012-01-29 21:41:05 +0000703 p+=GetPixelChannels(image);
704 q+=GetPixelChannels(crop_image);
705 continue;
706 }
cristy010d7d12011-08-31 01:02:48 +0000707 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
708 {
cristy5a23c552013-02-13 14:34:28 +0000709 PixelChannel channel=GetPixelChannelChannel(image,i);
710 PixelTrait traits=GetPixelChannelTraits(image,channel);
711 PixelTrait crop_traits=GetPixelChannelTraits(crop_image,channel);
cristy010d7d12011-08-31 01:02:48 +0000712 if ((traits == UndefinedPixelTrait) ||
713 (crop_traits == UndefinedPixelTrait))
714 continue;
cristy0beccfa2011-09-25 20:47:53 +0000715 SetPixelChannel(crop_image,channel,p[i],q);
cristy010d7d12011-08-31 01:02:48 +0000716 }
cristyed231572011-07-14 02:18:59 +0000717 p+=GetPixelChannels(image);
718 q+=GetPixelChannels(crop_image);
cristy4c08aed2011-07-01 19:47:50 +0000719 }
cristy3ed852e2009-09-05 21:47:34 +0000720 if (SyncCacheViewAuthenticPixels(crop_view,exception) == MagickFalse)
721 status=MagickFalse;
722 if (image->progress_monitor != (MagickProgressMonitor) NULL)
723 {
724 MagickBooleanType
725 proceed;
726
cristy2224dcd2010-11-15 00:49:30 +0000727#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristya5ab7ad2012-01-21 23:49:58 +0000728 #pragma omp critical (MagickCore_CropImage)
cristy3ed852e2009-09-05 21:47:34 +0000729#endif
730 proceed=SetImageProgress(image,CropImageTag,progress++,image->rows);
731 if (proceed == MagickFalse)
732 status=MagickFalse;
733 }
734 }
735 crop_view=DestroyCacheView(crop_view);
736 image_view=DestroyCacheView(image_view);
737 crop_image->type=image->type;
738 if (status == MagickFalse)
739 crop_image=DestroyImage(crop_image);
740 return(crop_image);
741}
742
743/*
744%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
745% %
746% %
747% %
anthony9f4f0342011-03-28 11:47:22 +0000748% C r o p I m a g e T o T i l e s %
749% %
750% %
751% %
752%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
753%
cristyfaf49552011-09-16 01:37:07 +0000754% CropImageToTiles() crops a single image, into a possible list of tiles.
anthony9f4f0342011-03-28 11:47:22 +0000755% This may include a single sub-region of the image. This basically applies
756% all the normal geometry flags for Crop.
757%
cristyfaf49552011-09-16 01:37:07 +0000758% Image *CropImageToTiles(const Image *image,
759% const RectangleInfo *crop_geometry, ExceptionInfo *exception)
anthony9f4f0342011-03-28 11:47:22 +0000760%
761% A description of each parameter follows:
762%
763% o image: the image The transformed image is returned as this parameter.
764%
765% o crop_geometry: A crop geometry string.
766%
767% o exception: return any errors or warnings in this structure.
768%
769*/
cristyfde3fa52011-07-22 17:49:35 +0000770
cristy72844f12013-04-28 23:52:36 +0000771static inline double MagickRound(double x)
anthony9f4f0342011-03-28 11:47:22 +0000772{
773 /*
774 Round the fraction to nearest integer.
775 */
cristyae0a3fc2013-04-29 08:29:35 +0000776 if ((x-floor(x)) < (ceil(x)-x))
cristy72844f12013-04-28 23:52:36 +0000777 return(floor(x));
778 return(ceil(x));
anthony9f4f0342011-03-28 11:47:22 +0000779}
780
781MagickExport Image *CropImageToTiles(const Image *image,
dirk081aaa12016-02-01 22:36:58 +0100782 const char *crop_geometry,ExceptionInfo *exception)
anthony9f4f0342011-03-28 11:47:22 +0000783{
784 Image
785 *next,
786 *crop_image;
787
788 MagickStatusType
789 flags;
790
791 RectangleInfo
792 geometry;
793
794 assert(image != (Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000795 assert(image->signature == MagickCoreSignature);
anthony9f4f0342011-03-28 11:47:22 +0000796 if (image->debug != MagickFalse)
797 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
anthony9f4f0342011-03-28 11:47:22 +0000798 crop_image=NewImageList();
799 next=NewImageList();
800 flags=ParseGravityGeometry(image,crop_geometry,&geometry,exception);
anthony9f4f0342011-03-28 11:47:22 +0000801 if ((flags & AreaValue) != 0)
802 {
anthony9f4f0342011-03-28 11:47:22 +0000803 PointInfo
804 delta,
805 offset;
806
807 RectangleInfo
808 crop;
809
cristyfde3fa52011-07-22 17:49:35 +0000810 size_t
811 height,
812 width;
813
anthony9f4f0342011-03-28 11:47:22 +0000814 /*
cristyfde3fa52011-07-22 17:49:35 +0000815 Crop into NxM tiles (@ flag).
anthony9f4f0342011-03-28 11:47:22 +0000816 */
817 width=image->columns;
818 height=image->rows;
819 if (geometry.width == 0)
820 geometry.width=1;
821 if (geometry.height == 0)
822 geometry.height=1;
823 if ((flags & AspectValue) == 0)
824 {
825 width-=(geometry.x < 0 ? -1 : 1)*geometry.x;
826 height-=(geometry.y < 0 ? -1 : 1)*geometry.y;
827 }
828 else
829 {
830 width+=(geometry.x < 0 ? -1 : 1)*geometry.x;
831 height+=(geometry.y < 0 ? -1 : 1)*geometry.y;
832 }
cristy240ae872011-12-02 12:05:20 +0000833 delta.x=(double) width/geometry.width;
834 delta.y=(double) height/geometry.height;
cristy9ec43c12012-03-03 15:11:08 +0000835 if (delta.x < 1.0)
836 delta.x=1.0;
837 if (delta.y < 1.0)
838 delta.y=1.0;
anthony9f4f0342011-03-28 11:47:22 +0000839 for (offset.y=0; offset.y < (double) height; )
840 {
841 if ((flags & AspectValue) == 0)
842 {
cristya19f1d72012-08-07 18:24:38 +0000843 crop.y=(ssize_t) MagickRound((double) (offset.y-
cristyfde3fa52011-07-22 17:49:35 +0000844 (geometry.y > 0 ? 0 : geometry.y)));
anthony9f4f0342011-03-28 11:47:22 +0000845 offset.y+=delta.y; /* increment now to find width */
cristya19f1d72012-08-07 18:24:38 +0000846 crop.height=(size_t) MagickRound((double) (offset.y+
cristyfde3fa52011-07-22 17:49:35 +0000847 (geometry.y < 0 ? 0 : geometry.y)));
anthony9f4f0342011-03-28 11:47:22 +0000848 }
849 else
850 {
cristya19f1d72012-08-07 18:24:38 +0000851 crop.y=(ssize_t) MagickRound((double) (offset.y-
cristyfde3fa52011-07-22 17:49:35 +0000852 (geometry.y > 0 ? geometry.y : 0)));
anthony9f4f0342011-03-28 11:47:22 +0000853 offset.y+=delta.y; /* increment now to find width */
cristya19f1d72012-08-07 18:24:38 +0000854 crop.height=(size_t) MagickRound((double)
cristyfde3fa52011-07-22 17:49:35 +0000855 (offset.y+(geometry.y < -1 ? geometry.y : 0)));
anthony9f4f0342011-03-28 11:47:22 +0000856 }
857 crop.height-=crop.y;
858 crop.y+=image->page.y;
859 for (offset.x=0; offset.x < (double) width; )
860 {
anthony9f4f0342011-03-28 11:47:22 +0000861 if ((flags & AspectValue) == 0)
862 {
cristya19f1d72012-08-07 18:24:38 +0000863 crop.x=(ssize_t) MagickRound((double) (offset.x-
cristyfde3fa52011-07-22 17:49:35 +0000864 (geometry.x > 0 ? 0 : geometry.x)));
865 offset.x+=delta.x; /* increment now to find height */
cristya19f1d72012-08-07 18:24:38 +0000866 crop.width=(size_t) MagickRound((double) (offset.x+
cristyfde3fa52011-07-22 17:49:35 +0000867 (geometry.x < 0 ? 0 : geometry.x)));
anthony9f4f0342011-03-28 11:47:22 +0000868 }
869 else
870 {
cristya19f1d72012-08-07 18:24:38 +0000871 crop.x=(ssize_t) MagickRound((double) (offset.x-
anthony9f4f0342011-03-28 11:47:22 +0000872 (geometry.x > 0 ? geometry.x : 0)));
cristyfde3fa52011-07-22 17:49:35 +0000873 offset.x+=delta.x; /* increment now to find height */
cristya19f1d72012-08-07 18:24:38 +0000874 crop.width=(size_t) MagickRound((double) (offset.x+
cristyfde3fa52011-07-22 17:49:35 +0000875 (geometry.x < 0 ? geometry.x : 0)));
anthony9f4f0342011-03-28 11:47:22 +0000876 }
877 crop.width-=crop.x;
878 crop.x+=image->page.x;
cristybf46f102011-08-01 18:00:16 +0000879 next=CropImage(image,&crop,exception);
cristyc1beb522014-12-15 12:42:20 +0000880 if (next != (Image *) NULL)
881 AppendImageToList(&crop_image,next);
anthony9f4f0342011-03-28 11:47:22 +0000882 }
anthony9f4f0342011-03-28 11:47:22 +0000883 }
cristy52224bf2011-08-01 18:17:07 +0000884 ClearMagickException(exception);
anthony9f4f0342011-03-28 11:47:22 +0000885 return(crop_image);
886 }
anthony9f4f0342011-03-28 11:47:22 +0000887 if (((geometry.width == 0) && (geometry.height == 0)) ||
888 ((flags & XValue) != 0) || ((flags & YValue) != 0))
889 {
890 /*
891 Crop a single region at +X+Y.
892 */
893 crop_image=CropImage(image,&geometry,exception);
894 if ((crop_image != (Image *) NULL) && ((flags & AspectValue) != 0))
895 {
896 crop_image->page.width=geometry.width;
897 crop_image->page.height=geometry.height;
898 crop_image->page.x-=geometry.x;
899 crop_image->page.y-=geometry.y;
900 }
901 return(crop_image);
cristy9ec43c12012-03-03 15:11:08 +0000902 }
cristyfde3fa52011-07-22 17:49:35 +0000903 if ((image->columns > geometry.width) || (image->rows > geometry.height))
anthony9f4f0342011-03-28 11:47:22 +0000904 {
cristyfde3fa52011-07-22 17:49:35 +0000905 RectangleInfo
906 page;
anthony9f4f0342011-03-28 11:47:22 +0000907
anthony9f4f0342011-03-28 11:47:22 +0000908 size_t
909 height,
910 width;
911
912 ssize_t
913 x,
914 y;
915
anthony9f4f0342011-03-28 11:47:22 +0000916 /*
917 Crop into tiles of fixed size WxH.
918 */
anthony9f4f0342011-03-28 11:47:22 +0000919 page=image->page;
920 if (page.width == 0)
921 page.width=image->columns;
anthony5ea220b2011-04-22 13:15:46 +0000922 if (page.height == 0)
anthony9f4f0342011-03-28 11:47:22 +0000923 page.height=image->rows;
anthony5ea220b2011-04-22 13:15:46 +0000924 width=geometry.width;
925 if (width == 0)
926 width=page.width;
927 height=geometry.height;
928 if (height == 0)
929 height=page.height;
anthony9f4f0342011-03-28 11:47:22 +0000930 next=NewImageList();
anthony9f4f0342011-03-28 11:47:22 +0000931 for (y=0; y < (ssize_t) page.height; y+=(ssize_t) height)
932 {
933 for (x=0; x < (ssize_t) page.width; x+=(ssize_t) width)
934 {
anthony9f4f0342011-03-28 11:47:22 +0000935 geometry.width=width;
936 geometry.height=height;
937 geometry.x=x;
938 geometry.y=y;
939 next=CropImage(image,&geometry,exception);
anthony9f4f0342011-03-28 11:47:22 +0000940 if (next == (Image *) NULL)
941 break;
anthony9f4f0342011-03-28 11:47:22 +0000942 AppendImageToList(&crop_image,next);
943 }
944 if (next == (Image *) NULL)
945 break;
anthony9f4f0342011-03-28 11:47:22 +0000946 }
947 return(crop_image);
948 }
anthony9f4f0342011-03-28 11:47:22 +0000949 return(CloneImage(image,0,0,MagickTrue,exception));
950}
951
952/*
953%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
954% %
955% %
956% %
cristy3ed852e2009-09-05 21:47:34 +0000957% E x c e r p t I m a g e %
958% %
959% %
960% %
961%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
962%
963% ExcerptImage() returns a excerpt of the image as defined by the geometry.
964%
965% The format of the ExcerptImage method is:
966%
967% Image *ExcerptImage(const Image *image,const RectangleInfo *geometry,
968% ExceptionInfo *exception)
969%
970% A description of each parameter follows:
971%
972% o image: the image.
973%
974% o geometry: Define the region of the image to extend with members
975% x, y, width, and height.
976%
977% o exception: return any errors or warnings in this structure.
978%
979*/
980MagickExport Image *ExcerptImage(const Image *image,
981 const RectangleInfo *geometry,ExceptionInfo *exception)
982{
983#define ExcerptImageTag "Excerpt/Image"
984
cristyc4c8d132010-01-07 01:58:38 +0000985 CacheView
986 *excerpt_view,
987 *image_view;
988
cristy3ed852e2009-09-05 21:47:34 +0000989 Image
990 *excerpt_image;
991
cristy3ed852e2009-09-05 21:47:34 +0000992 MagickBooleanType
993 status;
994
cristybb503372010-05-27 20:51:26 +0000995 MagickOffsetType
996 progress;
997
998 ssize_t
999 y;
1000
cristy3ed852e2009-09-05 21:47:34 +00001001 /*
1002 Allocate excerpt image.
1003 */
1004 assert(image != (const Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +00001005 assert(image->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00001006 if (image->debug != MagickFalse)
1007 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1008 assert(geometry != (const RectangleInfo *) NULL);
1009 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00001010 assert(exception->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00001011 excerpt_image=CloneImage(image,geometry->width,geometry->height,MagickTrue,
1012 exception);
1013 if (excerpt_image == (Image *) NULL)
1014 return((Image *) NULL);
1015 /*
1016 Excerpt each row.
1017 */
1018 status=MagickTrue;
1019 progress=0;
cristy46ff2672012-12-14 15:32:26 +00001020 image_view=AcquireVirtualCacheView(image,exception);
1021 excerpt_view=AcquireAuthenticCacheView(excerpt_image,exception);
cristyb5d5f722009-11-04 03:03:49 +00001022#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +00001023 #pragma omp parallel for schedule(static,4) shared(progress,status) \
cristy5e6b2592012-12-19 14:08:11 +00001024 magick_threads(image,excerpt_image,excerpt_image->rows,1)
cristy3ed852e2009-09-05 21:47:34 +00001025#endif
cristybb503372010-05-27 20:51:26 +00001026 for (y=0; y < (ssize_t) excerpt_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001027 {
cristy4c08aed2011-07-01 19:47:50 +00001028 register const Quantum
dirk05d2ff72015-11-18 23:13:43 +01001029 *magick_restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001030
cristy4c08aed2011-07-01 19:47:50 +00001031 register Quantum
dirk05d2ff72015-11-18 23:13:43 +01001032 *magick_restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001033
cristy4c08aed2011-07-01 19:47:50 +00001034 register ssize_t
1035 x;
1036
cristy3ed852e2009-09-05 21:47:34 +00001037 if (status == MagickFalse)
1038 continue;
1039 p=GetCacheViewVirtualPixels(image_view,geometry->x,geometry->y+y,
1040 geometry->width,1,exception);
1041 q=GetCacheViewAuthenticPixels(excerpt_view,0,y,excerpt_image->columns,1,
1042 exception);
cristy4c08aed2011-07-01 19:47:50 +00001043 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00001044 {
1045 status=MagickFalse;
1046 continue;
1047 }
cristy4c08aed2011-07-01 19:47:50 +00001048 for (x=0; x < (ssize_t) excerpt_image->columns; x++)
1049 {
cristy010d7d12011-08-31 01:02:48 +00001050 register ssize_t
1051 i;
1052
Cristydf5e8b12016-12-02 17:26:39 -05001053 if (GetPixelWriteMask(image,p) == 0)
cristy10a6c612012-01-29 21:41:05 +00001054 {
cristyc3a58022013-10-09 23:22:42 +00001055 SetPixelBackgoundColor(excerpt_image,q);
cristy10a6c612012-01-29 21:41:05 +00001056 p+=GetPixelChannels(image);
1057 q+=GetPixelChannels(excerpt_image);
1058 continue;
1059 }
cristy010d7d12011-08-31 01:02:48 +00001060 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1061 {
cristy5a23c552013-02-13 14:34:28 +00001062 PixelChannel channel=GetPixelChannelChannel(image,i);
1063 PixelTrait traits=GetPixelChannelTraits(image,channel);
1064 PixelTrait excerpt_traits=GetPixelChannelTraits(excerpt_image,channel);
cristy010d7d12011-08-31 01:02:48 +00001065 if ((traits == UndefinedPixelTrait) ||
1066 (excerpt_traits == UndefinedPixelTrait))
1067 continue;
cristy0beccfa2011-09-25 20:47:53 +00001068 SetPixelChannel(excerpt_image,channel,p[i],q);
cristy010d7d12011-08-31 01:02:48 +00001069 }
cristyed231572011-07-14 02:18:59 +00001070 p+=GetPixelChannels(image);
1071 q+=GetPixelChannels(excerpt_image);
cristy4c08aed2011-07-01 19:47:50 +00001072 }
cristy3ed852e2009-09-05 21:47:34 +00001073 if (SyncCacheViewAuthenticPixels(excerpt_view,exception) == MagickFalse)
1074 status=MagickFalse;
1075 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1076 {
1077 MagickBooleanType
1078 proceed;
1079
cristyb5d5f722009-11-04 03:03:49 +00001080#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristya5ab7ad2012-01-21 23:49:58 +00001081 #pragma omp critical (MagickCore_ExcerptImage)
cristy3ed852e2009-09-05 21:47:34 +00001082#endif
1083 proceed=SetImageProgress(image,ExcerptImageTag,progress++,image->rows);
1084 if (proceed == MagickFalse)
1085 status=MagickFalse;
1086 }
1087 }
1088 excerpt_view=DestroyCacheView(excerpt_view);
1089 image_view=DestroyCacheView(image_view);
1090 excerpt_image->type=image->type;
1091 if (status == MagickFalse)
1092 excerpt_image=DestroyImage(excerpt_image);
1093 return(excerpt_image);
1094}
1095
1096/*
1097%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1098% %
1099% %
1100% %
1101% E x t e n t I m a g e %
1102% %
1103% %
1104% %
1105%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1106%
1107% ExtentImage() extends the image as defined by the geometry, gravity, and
1108% image background color. Set the (x,y) offset of the geometry to move the
1109% original image relative to the extended image.
1110%
1111% The format of the ExtentImage method is:
1112%
1113% Image *ExtentImage(const Image *image,const RectangleInfo *geometry,
1114% ExceptionInfo *exception)
1115%
1116% A description of each parameter follows:
1117%
1118% o image: the image.
1119%
1120% o geometry: Define the region of the image to extend with members
1121% x, y, width, and height.
1122%
1123% o exception: return any errors or warnings in this structure.
1124%
1125*/
1126MagickExport Image *ExtentImage(const Image *image,
1127 const RectangleInfo *geometry,ExceptionInfo *exception)
1128{
1129 Image
1130 *extent_image;
1131
1132 /*
1133 Allocate extent image.
1134 */
1135 assert(image != (const Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +00001136 assert(image->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00001137 if (image->debug != MagickFalse)
1138 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1139 assert(geometry != (const RectangleInfo *) NULL);
1140 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00001141 assert(exception->signature == MagickCoreSignature);
cristyc6677872014-11-24 00:01:24 +00001142 if ((image->columns == geometry->width) &&
1143 (image->rows == geometry->height) &&
1144 (geometry->x == 0) && (geometry->y == 0))
1145 return(CloneImage(image,0,0,MagickTrue,exception));
cristy3ed852e2009-09-05 21:47:34 +00001146 extent_image=CloneImage(image,geometry->width,geometry->height,MagickTrue,
1147 exception);
1148 if (extent_image == (Image *) NULL)
1149 return((Image *) NULL);
cristyea1a8aa2011-10-20 13:24:06 +00001150 (void) SetImageBackgroundColor(extent_image,exception);
cristy39172402012-03-30 13:04:39 +00001151 (void) CompositeImage(extent_image,image,image->compose,MagickTrue,
cristyfeb3e962012-03-29 17:25:55 +00001152 -geometry->x,-geometry->y,exception);
cristy3ed852e2009-09-05 21:47:34 +00001153 return(extent_image);
1154}
1155
1156/*
1157%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1158% %
1159% %
1160% %
1161% F l i p I m a g e %
1162% %
1163% %
1164% %
1165%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1166%
1167% FlipImage() creates a vertical mirror image by reflecting the pixels
1168% around the central x-axis.
1169%
1170% The format of the FlipImage method is:
1171%
1172% Image *FlipImage(const Image *image,ExceptionInfo *exception)
1173%
1174% A description of each parameter follows:
1175%
1176% o image: the image.
1177%
1178% o exception: return any errors or warnings in this structure.
1179%
1180*/
1181MagickExport Image *FlipImage(const Image *image,ExceptionInfo *exception)
1182{
1183#define FlipImageTag "Flip/Image"
1184
cristyc4c8d132010-01-07 01:58:38 +00001185 CacheView
1186 *flip_view,
1187 *image_view;
1188
cristy3ed852e2009-09-05 21:47:34 +00001189 Image
1190 *flip_image;
1191
cristy3ed852e2009-09-05 21:47:34 +00001192 MagickBooleanType
1193 status;
1194
cristybb503372010-05-27 20:51:26 +00001195 MagickOffsetType
1196 progress;
1197
cristy74ea2cd2010-12-08 02:54:25 +00001198 RectangleInfo
1199 page;
1200
cristybb503372010-05-27 20:51:26 +00001201 ssize_t
1202 y;
1203
cristy3ed852e2009-09-05 21:47:34 +00001204 assert(image != (const Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +00001205 assert(image->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00001206 if (image->debug != MagickFalse)
1207 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1208 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00001209 assert(exception->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00001210 flip_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
1211 if (flip_image == (Image *) NULL)
1212 return((Image *) NULL);
1213 /*
1214 Flip image.
1215 */
1216 status=MagickTrue;
1217 progress=0;
cristy74ea2cd2010-12-08 02:54:25 +00001218 page=image->page;
cristy46ff2672012-12-14 15:32:26 +00001219 image_view=AcquireVirtualCacheView(image,exception);
1220 flip_view=AcquireAuthenticCacheView(flip_image,exception);
cristy26b64912012-12-16 18:20:09 +00001221#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyd6432472013-01-06 16:56:13 +00001222 #pragma omp parallel for schedule(static,4) shared(status) \
1223 magick_threads(image,flip_image,1,1)
cristy3ed852e2009-09-05 21:47:34 +00001224#endif
cristybb503372010-05-27 20:51:26 +00001225 for (y=0; y < (ssize_t) flip_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001226 {
cristy4c08aed2011-07-01 19:47:50 +00001227 register const Quantum
dirk05d2ff72015-11-18 23:13:43 +01001228 *magick_restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001229
cristy4c08aed2011-07-01 19:47:50 +00001230 register Quantum
dirk05d2ff72015-11-18 23:13:43 +01001231 *magick_restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001232
cristy4c08aed2011-07-01 19:47:50 +00001233 register ssize_t
1234 x;
1235
cristy3ed852e2009-09-05 21:47:34 +00001236 if (status == MagickFalse)
1237 continue;
1238 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
cristy5db1f092010-06-07 13:07:26 +00001239 q=QueueCacheViewAuthenticPixels(flip_view,0,(ssize_t) (flip_image->rows-y-
1240 1),flip_image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001241 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00001242 {
1243 status=MagickFalse;
1244 continue;
1245 }
cristy4c08aed2011-07-01 19:47:50 +00001246 for (x=0; x < (ssize_t) flip_image->columns; x++)
1247 {
cristy010d7d12011-08-31 01:02:48 +00001248 register ssize_t
1249 i;
1250
Cristydf5e8b12016-12-02 17:26:39 -05001251 if (GetPixelWriteMask(image,p) == 0)
cristy10a6c612012-01-29 21:41:05 +00001252 {
cristyc3a58022013-10-09 23:22:42 +00001253 SetPixelBackgoundColor(flip_image,q);
cristy10a6c612012-01-29 21:41:05 +00001254 p+=GetPixelChannels(image);
1255 q+=GetPixelChannels(flip_image);
1256 continue;
1257 }
cristy010d7d12011-08-31 01:02:48 +00001258 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1259 {
cristy5a23c552013-02-13 14:34:28 +00001260 PixelChannel channel=GetPixelChannelChannel(image,i);
1261 PixelTrait traits=GetPixelChannelTraits(image,channel);
1262 PixelTrait flip_traits=GetPixelChannelTraits(flip_image,channel);
cristy010d7d12011-08-31 01:02:48 +00001263 if ((traits == UndefinedPixelTrait) ||
1264 (flip_traits == UndefinedPixelTrait))
1265 continue;
cristy0beccfa2011-09-25 20:47:53 +00001266 SetPixelChannel(flip_image,channel,p[i],q);
cristy010d7d12011-08-31 01:02:48 +00001267 }
cristyed231572011-07-14 02:18:59 +00001268 p+=GetPixelChannels(image);
1269 q+=GetPixelChannels(flip_image);
cristy4c08aed2011-07-01 19:47:50 +00001270 }
cristy3ed852e2009-09-05 21:47:34 +00001271 if (SyncCacheViewAuthenticPixels(flip_view,exception) == MagickFalse)
1272 status=MagickFalse;
1273 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1274 {
1275 MagickBooleanType
1276 proceed;
1277
cristy26b64912012-12-16 18:20:09 +00001278#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristya5ab7ad2012-01-21 23:49:58 +00001279 #pragma omp critical (MagickCore_FlipImage)
cristy3ed852e2009-09-05 21:47:34 +00001280#endif
1281 proceed=SetImageProgress(image,FlipImageTag,progress++,image->rows);
1282 if (proceed == MagickFalse)
1283 status=MagickFalse;
1284 }
1285 }
1286 flip_view=DestroyCacheView(flip_view);
1287 image_view=DestroyCacheView(image_view);
1288 flip_image->type=image->type;
anthony37a1b912010-12-10 12:45:59 +00001289 if (page.height != 0)
1290 page.y=(ssize_t) (page.height-flip_image->rows-page.y);
cristy74ea2cd2010-12-08 02:54:25 +00001291 flip_image->page=page;
cristy3ed852e2009-09-05 21:47:34 +00001292 if (status == MagickFalse)
1293 flip_image=DestroyImage(flip_image);
1294 return(flip_image);
1295}
1296
1297/*
1298%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1299% %
1300% %
1301% %
1302% F l o p I m a g e %
1303% %
1304% %
1305% %
1306%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1307%
1308% FlopImage() creates a horizontal mirror image by reflecting the pixels
1309% around the central y-axis.
1310%
1311% The format of the FlopImage method is:
1312%
1313% Image *FlopImage(const Image *image,ExceptionInfo *exception)
1314%
1315% A description of each parameter follows:
1316%
1317% o image: the image.
1318%
1319% o exception: return any errors or warnings in this structure.
1320%
1321*/
1322MagickExport Image *FlopImage(const Image *image,ExceptionInfo *exception)
1323{
1324#define FlopImageTag "Flop/Image"
1325
cristyc4c8d132010-01-07 01:58:38 +00001326 CacheView
1327 *flop_view,
1328 *image_view;
1329
cristy3ed852e2009-09-05 21:47:34 +00001330 Image
1331 *flop_image;
1332
cristy3ed852e2009-09-05 21:47:34 +00001333 MagickBooleanType
1334 status;
1335
cristybb503372010-05-27 20:51:26 +00001336 MagickOffsetType
1337 progress;
1338
cristy74ea2cd2010-12-08 02:54:25 +00001339 RectangleInfo
1340 page;
1341
cristybb503372010-05-27 20:51:26 +00001342 ssize_t
1343 y;
1344
cristy3ed852e2009-09-05 21:47:34 +00001345 assert(image != (const Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +00001346 assert(image->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00001347 if (image->debug != MagickFalse)
1348 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1349 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00001350 assert(exception->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00001351 flop_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
1352 if (flop_image == (Image *) NULL)
1353 return((Image *) NULL);
1354 /*
1355 Flop each row.
1356 */
1357 status=MagickTrue;
1358 progress=0;
cristy74ea2cd2010-12-08 02:54:25 +00001359 page=image->page;
cristy46ff2672012-12-14 15:32:26 +00001360 image_view=AcquireVirtualCacheView(image,exception);
1361 flop_view=AcquireAuthenticCacheView(flop_image,exception);
cristy26b64912012-12-16 18:20:09 +00001362#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyd6432472013-01-06 16:56:13 +00001363 #pragma omp parallel for schedule(static,4) shared(status) \
1364 magick_threads(image,flop_image,1,1)
cristy3ed852e2009-09-05 21:47:34 +00001365#endif
cristybb503372010-05-27 20:51:26 +00001366 for (y=0; y < (ssize_t) flop_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001367 {
cristy4c08aed2011-07-01 19:47:50 +00001368 register const Quantum
dirk05d2ff72015-11-18 23:13:43 +01001369 *magick_restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001370
cristybb503372010-05-27 20:51:26 +00001371 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001372 x;
1373
cristy4c08aed2011-07-01 19:47:50 +00001374 register Quantum
dirk05d2ff72015-11-18 23:13:43 +01001375 *magick_restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001376
1377 if (status == MagickFalse)
1378 continue;
1379 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1380 q=QueueCacheViewAuthenticPixels(flop_view,0,y,flop_image->columns,1,
1381 exception);
cristy4c08aed2011-07-01 19:47:50 +00001382 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00001383 {
1384 status=MagickFalse;
1385 continue;
1386 }
cristyed231572011-07-14 02:18:59 +00001387 q+=GetPixelChannels(flop_image)*flop_image->columns;
cristybb503372010-05-27 20:51:26 +00001388 for (x=0; x < (ssize_t) flop_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001389 {
cristy010d7d12011-08-31 01:02:48 +00001390 register ssize_t
1391 i;
1392
cristyed231572011-07-14 02:18:59 +00001393 q-=GetPixelChannels(flop_image);
Cristydf5e8b12016-12-02 17:26:39 -05001394 if (GetPixelWriteMask(image,p) == 0)
cristy10a6c612012-01-29 21:41:05 +00001395 {
1396 p+=GetPixelChannels(image);
1397 continue;
1398 }
cristy010d7d12011-08-31 01:02:48 +00001399 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1400 {
cristy5a23c552013-02-13 14:34:28 +00001401 PixelChannel channel=GetPixelChannelChannel(image,i);
1402 PixelTrait traits=GetPixelChannelTraits(image,channel);
1403 PixelTrait flop_traits=GetPixelChannelTraits(flop_image,channel);
cristy010d7d12011-08-31 01:02:48 +00001404 if ((traits == UndefinedPixelTrait) ||
1405 (flop_traits == UndefinedPixelTrait))
1406 continue;
cristy0beccfa2011-09-25 20:47:53 +00001407 SetPixelChannel(flop_image,channel,p[i],q);
cristy010d7d12011-08-31 01:02:48 +00001408 }
cristyed231572011-07-14 02:18:59 +00001409 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001410 }
1411 if (SyncCacheViewAuthenticPixels(flop_view,exception) == MagickFalse)
1412 status=MagickFalse;
1413 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1414 {
1415 MagickBooleanType
1416 proceed;
1417
cristy26b64912012-12-16 18:20:09 +00001418#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristya5ab7ad2012-01-21 23:49:58 +00001419 #pragma omp critical (MagickCore_FlopImage)
cristy3ed852e2009-09-05 21:47:34 +00001420#endif
1421 proceed=SetImageProgress(image,FlopImageTag,progress++,image->rows);
1422 if (proceed == MagickFalse)
1423 status=MagickFalse;
1424 }
1425 }
1426 flop_view=DestroyCacheView(flop_view);
1427 image_view=DestroyCacheView(image_view);
1428 flop_image->type=image->type;
anthony37a1b912010-12-10 12:45:59 +00001429 if (page.width != 0)
1430 page.x=(ssize_t) (page.width-flop_image->columns-page.x);
cristy74ea2cd2010-12-08 02:54:25 +00001431 flop_image->page=page;
cristy3ed852e2009-09-05 21:47:34 +00001432 if (status == MagickFalse)
1433 flop_image=DestroyImage(flop_image);
1434 return(flop_image);
1435}
1436
1437/*
1438%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1439% %
1440% %
1441% %
1442% R o l l I m a g e %
1443% %
1444% %
1445% %
1446%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1447%
1448% RollImage() offsets an image as defined by x_offset and y_offset.
1449%
1450% The format of the RollImage method is:
1451%
cristybb503372010-05-27 20:51:26 +00001452% Image *RollImage(const Image *image,const ssize_t x_offset,
1453% const ssize_t y_offset,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001454%
1455% A description of each parameter follows:
1456%
1457% o image: the image.
1458%
1459% o x_offset: the number of columns to roll in the horizontal direction.
1460%
1461% o y_offset: the number of rows to roll in the vertical direction.
1462%
1463% o exception: return any errors or warnings in this structure.
1464%
1465*/
1466
cristya1f115c2015-05-26 23:25:11 +00001467static MagickBooleanType CopyImageRegion(Image *destination,const Image *source, const size_t columns,const size_t rows,const ssize_t sx,const ssize_t sy,
1468 const ssize_t dx,const ssize_t dy,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001469{
cristyc4c8d132010-01-07 01:58:38 +00001470 CacheView
1471 *source_view,
1472 *destination_view;
1473
cristy3ed852e2009-09-05 21:47:34 +00001474 MagickBooleanType
1475 status;
1476
cristy9d314ff2011-03-09 01:30:28 +00001477 ssize_t
1478 y;
1479
cristye02079c2013-10-24 13:00:14 +00001480 if (columns == 0)
1481 return(MagickTrue);
cristy3ed852e2009-09-05 21:47:34 +00001482 status=MagickTrue;
cristy46ff2672012-12-14 15:32:26 +00001483 source_view=AcquireVirtualCacheView(source,exception);
1484 destination_view=AcquireAuthenticCacheView(destination,exception);
cristyb5d5f722009-11-04 03:03:49 +00001485#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy9a5a52f2012-10-09 14:40:31 +00001486 #pragma omp parallel for schedule(static,4) shared(status) \
cristy5e6b2592012-12-19 14:08:11 +00001487 magick_threads(source,destination,rows,1)
cristy3ed852e2009-09-05 21:47:34 +00001488#endif
cristybb503372010-05-27 20:51:26 +00001489 for (y=0; y < (ssize_t) rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001490 {
1491 MagickBooleanType
1492 sync;
1493
cristy4c08aed2011-07-01 19:47:50 +00001494 register const Quantum
dirk05d2ff72015-11-18 23:13:43 +01001495 *magick_restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001496
cristy4c08aed2011-07-01 19:47:50 +00001497 register Quantum
dirk05d2ff72015-11-18 23:13:43 +01001498 *magick_restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001499
cristy4c08aed2011-07-01 19:47:50 +00001500 register ssize_t
1501 x;
1502
cristy3ed852e2009-09-05 21:47:34 +00001503 /*
1504 Transfer scanline.
1505 */
1506 if (status == MagickFalse)
1507 continue;
1508 p=GetCacheViewVirtualPixels(source_view,sx,sy+y,columns,1,exception);
1509 q=GetCacheViewAuthenticPixels(destination_view,dx,dy+y,columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001510 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00001511 {
1512 status=MagickFalse;
1513 continue;
1514 }
cristy4c08aed2011-07-01 19:47:50 +00001515 for (x=0; x < (ssize_t) columns; x++)
1516 {
cristy010d7d12011-08-31 01:02:48 +00001517 register ssize_t
1518 i;
1519
Cristydf5e8b12016-12-02 17:26:39 -05001520 if (GetPixelWriteMask(source,p) == 0)
cristy10a6c612012-01-29 21:41:05 +00001521 {
cristyc3a58022013-10-09 23:22:42 +00001522 SetPixelBackgoundColor(destination,q);
cristy10a6c612012-01-29 21:41:05 +00001523 p+=GetPixelChannels(source);
1524 q+=GetPixelChannels(destination);
1525 continue;
1526 }
cristy010d7d12011-08-31 01:02:48 +00001527 for (i=0; i < (ssize_t) GetPixelChannels(source); i++)
1528 {
cristy5a23c552013-02-13 14:34:28 +00001529 PixelChannel channel=GetPixelChannelChannel(source,i);
1530 PixelTrait source_traits=GetPixelChannelTraits(source,channel);
1531 PixelTrait destination_traits=GetPixelChannelTraits(destination,
1532 channel);
cristy010d7d12011-08-31 01:02:48 +00001533 if ((source_traits == UndefinedPixelTrait) ||
1534 (destination_traits == UndefinedPixelTrait))
1535 continue;
cristy0beccfa2011-09-25 20:47:53 +00001536 SetPixelChannel(destination,channel,p[i],q);
cristy010d7d12011-08-31 01:02:48 +00001537 }
cristyed231572011-07-14 02:18:59 +00001538 p+=GetPixelChannels(source);
1539 q+=GetPixelChannels(destination);
cristy4c08aed2011-07-01 19:47:50 +00001540 }
cristy3ed852e2009-09-05 21:47:34 +00001541 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
1542 if (sync == MagickFalse)
1543 status=MagickFalse;
1544 }
1545 destination_view=DestroyCacheView(destination_view);
1546 source_view=DestroyCacheView(source_view);
1547 return(status);
1548}
1549
cristybb503372010-05-27 20:51:26 +00001550MagickExport Image *RollImage(const Image *image,const ssize_t x_offset,
1551 const ssize_t y_offset,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001552{
1553#define RollImageTag "Roll/Image"
1554
1555 Image
1556 *roll_image;
1557
1558 MagickStatusType
1559 status;
1560
1561 RectangleInfo
1562 offset;
1563
1564 /*
1565 Initialize roll image attributes.
1566 */
1567 assert(image != (const Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +00001568 assert(image->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00001569 if (image->debug != MagickFalse)
1570 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1571 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00001572 assert(exception->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00001573 roll_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
1574 if (roll_image == (Image *) NULL)
1575 return((Image *) NULL);
1576 offset.x=x_offset;
1577 offset.y=y_offset;
1578 while (offset.x < 0)
cristyeaedf062010-05-29 22:36:02 +00001579 offset.x+=(ssize_t) image->columns;
cristybb503372010-05-27 20:51:26 +00001580 while (offset.x >= (ssize_t) image->columns)
cristyeaedf062010-05-29 22:36:02 +00001581 offset.x-=(ssize_t) image->columns;
cristy3ed852e2009-09-05 21:47:34 +00001582 while (offset.y < 0)
cristyeaedf062010-05-29 22:36:02 +00001583 offset.y+=(ssize_t) image->rows;
cristybb503372010-05-27 20:51:26 +00001584 while (offset.y >= (ssize_t) image->rows)
cristyeaedf062010-05-29 22:36:02 +00001585 offset.y-=(ssize_t) image->rows;
cristy3ed852e2009-09-05 21:47:34 +00001586 /*
1587 Roll image.
1588 */
cristybb503372010-05-27 20:51:26 +00001589 status=CopyImageRegion(roll_image,image,(size_t) offset.x,
1590 (size_t) offset.y,(ssize_t) image->columns-offset.x,(ssize_t) image->rows-
cristy3ed852e2009-09-05 21:47:34 +00001591 offset.y,0,0,exception);
1592 (void) SetImageProgress(image,RollImageTag,0,3);
cristyaae13f62013-08-15 14:41:32 +00001593 status&=CopyImageRegion(roll_image,image,image->columns-offset.x,
cristybb503372010-05-27 20:51:26 +00001594 (size_t) offset.y,0,(ssize_t) image->rows-offset.y,offset.x,0,
cristy3ed852e2009-09-05 21:47:34 +00001595 exception);
1596 (void) SetImageProgress(image,RollImageTag,1,3);
cristyaae13f62013-08-15 14:41:32 +00001597 status&=CopyImageRegion(roll_image,image,(size_t) offset.x,image->rows-
cristybb503372010-05-27 20:51:26 +00001598 offset.y,(ssize_t) image->columns-offset.x,0,0,offset.y,exception);
cristy3ed852e2009-09-05 21:47:34 +00001599 (void) SetImageProgress(image,RollImageTag,2,3);
cristyaae13f62013-08-15 14:41:32 +00001600 status&=CopyImageRegion(roll_image,image,image->columns-offset.x,image->rows-
cristy3ed852e2009-09-05 21:47:34 +00001601 offset.y,0,0,offset.x,offset.y,exception);
1602 (void) SetImageProgress(image,RollImageTag,3,3);
1603 roll_image->type=image->type;
1604 if (status == MagickFalse)
1605 roll_image=DestroyImage(roll_image);
1606 return(roll_image);
1607}
1608
1609/*
1610%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1611% %
1612% %
1613% %
1614% S h a v e I m a g e %
1615% %
1616% %
1617% %
1618%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1619%
1620% ShaveImage() shaves pixels from the image edges. It allocates the memory
1621% necessary for the new Image structure and returns a pointer to the new
1622% image.
1623%
1624% The format of the ShaveImage method is:
1625%
1626% Image *ShaveImage(const Image *image,const RectangleInfo *shave_info,
1627% ExceptionInfo *exception)
1628%
1629% A description of each parameter follows:
1630%
1631% o shave_image: Method ShaveImage returns a pointer to the shaved
1632% image. A null image is returned if there is a memory shortage or
1633% if the image width or height is zero.
1634%
1635% o image: the image.
1636%
1637% o shave_info: Specifies a pointer to a RectangleInfo which defines the
1638% region of the image to crop.
1639%
1640% o exception: return any errors or warnings in this structure.
1641%
1642*/
1643MagickExport Image *ShaveImage(const Image *image,
1644 const RectangleInfo *shave_info,ExceptionInfo *exception)
1645{
1646 Image
1647 *shave_image;
1648
1649 RectangleInfo
1650 geometry;
1651
1652 assert(image != (const Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +00001653 assert(image->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00001654 if (image->debug != MagickFalse)
1655 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1656 if (((2*shave_info->width) >= image->columns) ||
1657 ((2*shave_info->height) >= image->rows))
1658 ThrowImageException(OptionWarning,"GeometryDoesNotContainImage");
1659 SetGeometry(image,&geometry);
1660 geometry.width-=2*shave_info->width;
1661 geometry.height-=2*shave_info->height;
cristybb503372010-05-27 20:51:26 +00001662 geometry.x=(ssize_t) shave_info->width+image->page.x;
1663 geometry.y=(ssize_t) shave_info->height+image->page.y;
cristy3ed852e2009-09-05 21:47:34 +00001664 shave_image=CropImage(image,&geometry,exception);
1665 if (shave_image == (Image *) NULL)
1666 return((Image *) NULL);
1667 shave_image->page.width-=2*shave_info->width;
1668 shave_image->page.height-=2*shave_info->height;
cristyeaedf062010-05-29 22:36:02 +00001669 shave_image->page.x-=(ssize_t) shave_info->width;
1670 shave_image->page.y-=(ssize_t) shave_info->height;
cristy3ed852e2009-09-05 21:47:34 +00001671 return(shave_image);
1672}
1673
1674/*
1675%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1676% %
1677% %
1678% %
1679% S p l i c e I m a g e %
1680% %
1681% %
1682% %
1683%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1684%
1685% SpliceImage() splices a solid color into the image as defined by the
1686% geometry.
1687%
1688% The format of the SpliceImage method is:
1689%
1690% Image *SpliceImage(const Image *image,const RectangleInfo *geometry,
1691% ExceptionInfo *exception)
1692%
1693% A description of each parameter follows:
1694%
1695% o image: the image.
1696%
1697% o geometry: Define the region of the image to splice with members
1698% x, y, width, and height.
1699%
1700% o exception: return any errors or warnings in this structure.
1701%
1702*/
1703MagickExport Image *SpliceImage(const Image *image,
1704 const RectangleInfo *geometry,ExceptionInfo *exception)
1705{
1706#define SpliceImageTag "Splice/Image"
1707
cristyc4c8d132010-01-07 01:58:38 +00001708 CacheView
1709 *image_view,
1710 *splice_view;
1711
cristy3ed852e2009-09-05 21:47:34 +00001712 Image
1713 *splice_image;
1714
cristy3ed852e2009-09-05 21:47:34 +00001715 MagickBooleanType
cristy3ed852e2009-09-05 21:47:34 +00001716 status;
1717
cristybb503372010-05-27 20:51:26 +00001718 MagickOffsetType
1719 progress;
1720
cristy3ed852e2009-09-05 21:47:34 +00001721 RectangleInfo
1722 splice_geometry;
1723
cristybb503372010-05-27 20:51:26 +00001724 ssize_t
dirk7b1cf572015-10-09 10:07:51 +02001725 columns,
cristybb503372010-05-27 20:51:26 +00001726 y;
1727
cristy3ed852e2009-09-05 21:47:34 +00001728 /*
1729 Allocate splice image.
1730 */
1731 assert(image != (const Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +00001732 assert(image->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00001733 if (image->debug != MagickFalse)
1734 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1735 assert(geometry != (const RectangleInfo *) NULL);
1736 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00001737 assert(exception->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00001738 splice_geometry=(*geometry);
1739 splice_image=CloneImage(image,image->columns+splice_geometry.width,
1740 image->rows+splice_geometry.height,MagickTrue,exception);
1741 if (splice_image == (Image *) NULL)
1742 return((Image *) NULL);
cristy574cc262011-08-05 01:23:58 +00001743 if (SetImageStorageClass(splice_image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001744 {
cristy3ed852e2009-09-05 21:47:34 +00001745 splice_image=DestroyImage(splice_image);
1746 return((Image *) NULL);
1747 }
cristycb228bb2014-09-14 21:17:36 +00001748 if ((IsPixelInfoGray(&splice_image->background_color) == MagickFalse) &&
1749 (IsGrayColorspace(splice_image->colorspace) != MagickFalse))
1750 (void) SetImageColorspace(splice_image,sRGBColorspace,exception);
cristy17f11b02014-12-20 19:37:04 +00001751 if ((splice_image->background_color.alpha_trait != UndefinedPixelTrait) &&
1752 (splice_image->alpha_trait == UndefinedPixelTrait))
cristycb228bb2014-09-14 21:17:36 +00001753 (void) SetImageAlpha(splice_image,OpaqueAlpha,exception);
cristyea1a8aa2011-10-20 13:24:06 +00001754 (void) SetImageBackgroundColor(splice_image,exception);
cristy3ed852e2009-09-05 21:47:34 +00001755 /*
1756 Respect image geometry.
1757 */
1758 switch (image->gravity)
1759 {
1760 default:
1761 case UndefinedGravity:
1762 case NorthWestGravity:
1763 break;
1764 case NorthGravity:
1765 {
cristyeaedf062010-05-29 22:36:02 +00001766 splice_geometry.x+=(ssize_t) splice_geometry.width/2;
cristy3ed852e2009-09-05 21:47:34 +00001767 break;
1768 }
1769 case NorthEastGravity:
1770 {
cristyeaedf062010-05-29 22:36:02 +00001771 splice_geometry.x+=(ssize_t) splice_geometry.width;
cristy3ed852e2009-09-05 21:47:34 +00001772 break;
1773 }
1774 case WestGravity:
1775 {
cristyeaedf062010-05-29 22:36:02 +00001776 splice_geometry.y+=(ssize_t) splice_geometry.width/2;
cristy3ed852e2009-09-05 21:47:34 +00001777 break;
1778 }
cristy3ed852e2009-09-05 21:47:34 +00001779 case CenterGravity:
1780 {
cristyeaedf062010-05-29 22:36:02 +00001781 splice_geometry.x+=(ssize_t) splice_geometry.width/2;
1782 splice_geometry.y+=(ssize_t) splice_geometry.height/2;
cristy3ed852e2009-09-05 21:47:34 +00001783 break;
1784 }
1785 case EastGravity:
1786 {
cristyeaedf062010-05-29 22:36:02 +00001787 splice_geometry.x+=(ssize_t) splice_geometry.width;
1788 splice_geometry.y+=(ssize_t) splice_geometry.height/2;
cristy3ed852e2009-09-05 21:47:34 +00001789 break;
1790 }
1791 case SouthWestGravity:
1792 {
cristyeaedf062010-05-29 22:36:02 +00001793 splice_geometry.y+=(ssize_t) splice_geometry.height;
cristy3ed852e2009-09-05 21:47:34 +00001794 break;
1795 }
1796 case SouthGravity:
1797 {
cristyeaedf062010-05-29 22:36:02 +00001798 splice_geometry.x+=(ssize_t) splice_geometry.width/2;
1799 splice_geometry.y+=(ssize_t) splice_geometry.height;
cristy3ed852e2009-09-05 21:47:34 +00001800 break;
1801 }
1802 case SouthEastGravity:
1803 {
cristyeaedf062010-05-29 22:36:02 +00001804 splice_geometry.x+=(ssize_t) splice_geometry.width;
1805 splice_geometry.y+=(ssize_t) splice_geometry.height;
cristy3ed852e2009-09-05 21:47:34 +00001806 break;
1807 }
1808 }
1809 /*
1810 Splice image.
1811 */
1812 status=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00001813 progress=0;
dirk7b1cf572015-10-09 10:07:51 +02001814 columns=MagickMin(splice_geometry.x,(ssize_t) splice_image->columns);
cristy46ff2672012-12-14 15:32:26 +00001815 image_view=AcquireVirtualCacheView(image,exception);
1816 splice_view=AcquireAuthenticCacheView(splice_image,exception);
cristyb5d5f722009-11-04 03:03:49 +00001817#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +00001818 #pragma omp parallel for schedule(static,4) shared(progress,status) \
cristycb7dfcc2013-01-06 18:34:59 +00001819 magick_threads(image,splice_image,1,1)
cristy3ed852e2009-09-05 21:47:34 +00001820#endif
cristybb503372010-05-27 20:51:26 +00001821 for (y=0; y < (ssize_t) splice_geometry.y; y++)
cristy3ed852e2009-09-05 21:47:34 +00001822 {
cristy4c08aed2011-07-01 19:47:50 +00001823 register const Quantum
dirk05d2ff72015-11-18 23:13:43 +01001824 *magick_restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001825
cristybb503372010-05-27 20:51:26 +00001826 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001827 x;
1828
cristy4c08aed2011-07-01 19:47:50 +00001829 register Quantum
dirk05d2ff72015-11-18 23:13:43 +01001830 *magick_restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001831
1832 if (status == MagickFalse)
1833 continue;
dirk7b1cf572015-10-09 10:07:51 +02001834 p=GetCacheViewVirtualPixels(image_view,0,y,splice_image->columns,1,
1835 exception);
cristy3ed852e2009-09-05 21:47:34 +00001836 q=QueueCacheViewAuthenticPixels(splice_view,0,y,splice_image->columns,1,
1837 exception);
cristy4c08aed2011-07-01 19:47:50 +00001838 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00001839 {
1840 status=MagickFalse;
1841 continue;
1842 }
dirk7b1cf572015-10-09 10:07:51 +02001843 for (x=0; x < columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001844 {
cristy010d7d12011-08-31 01:02:48 +00001845 register ssize_t
1846 i;
1847
Cristydf5e8b12016-12-02 17:26:39 -05001848 if (GetPixelWriteMask(image,p) == 0)
cristy10a6c612012-01-29 21:41:05 +00001849 {
cristyc3a58022013-10-09 23:22:42 +00001850 SetPixelBackgoundColor(splice_image,q);
cristy10a6c612012-01-29 21:41:05 +00001851 p+=GetPixelChannels(image);
1852 q+=GetPixelChannels(splice_image);
1853 continue;
1854 }
cristy010d7d12011-08-31 01:02:48 +00001855 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1856 {
cristy5a23c552013-02-13 14:34:28 +00001857 PixelChannel channel=GetPixelChannelChannel(image,i);
1858 PixelTrait traits=GetPixelChannelTraits(image,channel);
1859 PixelTrait splice_traits=GetPixelChannelTraits(splice_image,channel);
cristy010d7d12011-08-31 01:02:48 +00001860 if ((traits == UndefinedPixelTrait) ||
1861 (splice_traits == UndefinedPixelTrait))
1862 continue;
cristy0beccfa2011-09-25 20:47:53 +00001863 SetPixelChannel(splice_image,channel,p[i],q);
cristy010d7d12011-08-31 01:02:48 +00001864 }
cristycb228bb2014-09-14 21:17:36 +00001865 SetPixelRed(splice_image,GetPixelRed(image,p),q);
1866 SetPixelGreen(splice_image,GetPixelGreen(image,p),q);
1867 SetPixelBlue(splice_image,GetPixelBlue(image,p),q);
1868 SetPixelAlpha(splice_image,GetPixelAlpha(image,p),q);
cristyed231572011-07-14 02:18:59 +00001869 p+=GetPixelChannels(image);
1870 q+=GetPixelChannels(splice_image);
cristy3ed852e2009-09-05 21:47:34 +00001871 }
cristybb503372010-05-27 20:51:26 +00001872 for ( ; x < (ssize_t) (splice_geometry.x+splice_geometry.width); x++)
cristyed231572011-07-14 02:18:59 +00001873 q+=GetPixelChannels(splice_image);
cristybb503372010-05-27 20:51:26 +00001874 for ( ; x < (ssize_t) splice_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001875 {
cristy010d7d12011-08-31 01:02:48 +00001876 register ssize_t
1877 i;
1878
Cristydf5e8b12016-12-02 17:26:39 -05001879 if (GetPixelWriteMask(image,p) == 0)
cristy10a6c612012-01-29 21:41:05 +00001880 {
cristyc3a58022013-10-09 23:22:42 +00001881 SetPixelBackgoundColor(splice_image,q);
cristy10a6c612012-01-29 21:41:05 +00001882 p+=GetPixelChannels(image);
1883 q+=GetPixelChannels(splice_image);
1884 continue;
1885 }
cristy010d7d12011-08-31 01:02:48 +00001886 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1887 {
cristy5a23c552013-02-13 14:34:28 +00001888 PixelChannel channel=GetPixelChannelChannel(image,i);
1889 PixelTrait traits=GetPixelChannelTraits(image,channel);
1890 PixelTrait splice_traits=GetPixelChannelTraits(splice_image,channel);
cristy010d7d12011-08-31 01:02:48 +00001891 if ((traits == UndefinedPixelTrait) ||
1892 (splice_traits == UndefinedPixelTrait))
1893 continue;
cristy0beccfa2011-09-25 20:47:53 +00001894 SetPixelChannel(splice_image,channel,p[i],q);
cristy010d7d12011-08-31 01:02:48 +00001895 }
cristycb228bb2014-09-14 21:17:36 +00001896 SetPixelRed(splice_image,GetPixelRed(image,p),q);
1897 SetPixelGreen(splice_image,GetPixelGreen(image,p),q);
1898 SetPixelBlue(splice_image,GetPixelBlue(image,p),q);
1899 SetPixelAlpha(splice_image,GetPixelAlpha(image,p),q);
cristyed231572011-07-14 02:18:59 +00001900 p+=GetPixelChannels(image);
1901 q+=GetPixelChannels(splice_image);
cristy3ed852e2009-09-05 21:47:34 +00001902 }
1903 if (SyncCacheViewAuthenticPixels(splice_view,exception) == MagickFalse)
1904 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00001905 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1906 {
1907 MagickBooleanType
1908 proceed;
1909
cristyb5d5f722009-11-04 03:03:49 +00001910#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristya5ab7ad2012-01-21 23:49:58 +00001911 #pragma omp critical (MagickCore_TransposeImage)
cristy3ed852e2009-09-05 21:47:34 +00001912#endif
1913 proceed=SetImageProgress(image,SpliceImageTag,progress++,
1914 splice_image->rows);
1915 if (proceed == MagickFalse)
1916 status=MagickFalse;
1917 }
1918 }
cristyb5d5f722009-11-04 03:03:49 +00001919#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +00001920 #pragma omp parallel for schedule(static,4) shared(progress,status) \
cristycb7dfcc2013-01-06 18:34:59 +00001921 magick_threads(image,splice_image,1,1)
cristy3ed852e2009-09-05 21:47:34 +00001922#endif
cristybb503372010-05-27 20:51:26 +00001923 for (y=(ssize_t) (splice_geometry.y+splice_geometry.height);
1924 y < (ssize_t) splice_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001925 {
cristy4c08aed2011-07-01 19:47:50 +00001926 register const Quantum
dirk05d2ff72015-11-18 23:13:43 +01001927 *magick_restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001928
cristybb503372010-05-27 20:51:26 +00001929 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001930 x;
1931
cristy4c08aed2011-07-01 19:47:50 +00001932 register Quantum
dirk05d2ff72015-11-18 23:13:43 +01001933 *magick_restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001934
1935 if (status == MagickFalse)
1936 continue;
dirk7b1cf572015-10-09 10:07:51 +02001937 if ((y < 0) || (y >= (ssize_t)splice_image->rows))
cristy2224dcd2010-11-15 00:49:30 +00001938 continue;
dirk7b1cf572015-10-09 10:07:51 +02001939 p=GetCacheViewVirtualPixels(image_view,0,y-(ssize_t) splice_geometry.height,
1940 splice_image->columns,1,exception);
cristy3ed852e2009-09-05 21:47:34 +00001941 q=QueueCacheViewAuthenticPixels(splice_view,0,y,splice_image->columns,1,
1942 exception);
cristy4c08aed2011-07-01 19:47:50 +00001943 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00001944 {
1945 status=MagickFalse;
1946 continue;
1947 }
dirk7b1cf572015-10-09 10:07:51 +02001948 for (x=0; x < columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001949 {
cristy010d7d12011-08-31 01:02:48 +00001950 register ssize_t
1951 i;
1952
Cristydf5e8b12016-12-02 17:26:39 -05001953 if (GetPixelWriteMask(image,q) == 0)
cristy10a6c612012-01-29 21:41:05 +00001954 {
cristyc3a58022013-10-09 23:22:42 +00001955 SetPixelBackgoundColor(splice_image,q);
cristy10a6c612012-01-29 21:41:05 +00001956 p+=GetPixelChannels(image);
1957 q+=GetPixelChannels(splice_image);
1958 continue;
1959 }
cristy010d7d12011-08-31 01:02:48 +00001960 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1961 {
cristy5a23c552013-02-13 14:34:28 +00001962 PixelChannel channel=GetPixelChannelChannel(image,i);
1963 PixelTrait traits=GetPixelChannelTraits(image,channel);
1964 PixelTrait splice_traits=GetPixelChannelTraits(splice_image,channel);
cristy010d7d12011-08-31 01:02:48 +00001965 if ((traits == UndefinedPixelTrait) ||
1966 (splice_traits == UndefinedPixelTrait))
1967 continue;
cristy0beccfa2011-09-25 20:47:53 +00001968 SetPixelChannel(splice_image,channel,p[i],q);
cristy010d7d12011-08-31 01:02:48 +00001969 }
cristycb228bb2014-09-14 21:17:36 +00001970 SetPixelRed(splice_image,GetPixelRed(image,p),q);
1971 SetPixelGreen(splice_image,GetPixelGreen(image,p),q);
1972 SetPixelBlue(splice_image,GetPixelBlue(image,p),q);
1973 SetPixelAlpha(splice_image,GetPixelAlpha(image,p),q);
cristyed231572011-07-14 02:18:59 +00001974 p+=GetPixelChannels(image);
1975 q+=GetPixelChannels(splice_image);
cristy3ed852e2009-09-05 21:47:34 +00001976 }
cristybb503372010-05-27 20:51:26 +00001977 for ( ; x < (ssize_t) (splice_geometry.x+splice_geometry.width); x++)
cristyed231572011-07-14 02:18:59 +00001978 q+=GetPixelChannels(splice_image);
cristybb503372010-05-27 20:51:26 +00001979 for ( ; x < (ssize_t) splice_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001980 {
cristy010d7d12011-08-31 01:02:48 +00001981 register ssize_t
1982 i;
1983
Cristydf5e8b12016-12-02 17:26:39 -05001984 if (GetPixelWriteMask(image,q) == 0)
cristy10a6c612012-01-29 21:41:05 +00001985 {
cristyc3a58022013-10-09 23:22:42 +00001986 SetPixelBackgoundColor(splice_image,q);
cristy10a6c612012-01-29 21:41:05 +00001987 p+=GetPixelChannels(image);
1988 q+=GetPixelChannels(splice_image);
1989 continue;
1990 }
cristy010d7d12011-08-31 01:02:48 +00001991 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1992 {
cristy5a23c552013-02-13 14:34:28 +00001993 PixelChannel channel=GetPixelChannelChannel(image,i);
1994 PixelTrait traits=GetPixelChannelTraits(image,channel);
1995 PixelTrait splice_traits=GetPixelChannelTraits(splice_image,channel);
cristy010d7d12011-08-31 01:02:48 +00001996 if ((traits == UndefinedPixelTrait) ||
1997 (splice_traits == UndefinedPixelTrait))
1998 continue;
cristy0beccfa2011-09-25 20:47:53 +00001999 SetPixelChannel(splice_image,channel,p[i],q);
cristy010d7d12011-08-31 01:02:48 +00002000 }
cristycb228bb2014-09-14 21:17:36 +00002001 SetPixelRed(splice_image,GetPixelRed(image,p),q);
2002 SetPixelGreen(splice_image,GetPixelGreen(image,p),q);
2003 SetPixelBlue(splice_image,GetPixelBlue(image,p),q);
2004 SetPixelAlpha(splice_image,GetPixelAlpha(image,p),q);
cristyed231572011-07-14 02:18:59 +00002005 p+=GetPixelChannels(image);
2006 q+=GetPixelChannels(splice_image);
cristy3ed852e2009-09-05 21:47:34 +00002007 }
2008 if (SyncCacheViewAuthenticPixels(splice_view,exception) == MagickFalse)
2009 status=MagickFalse;
2010 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2011 {
2012 MagickBooleanType
2013 proceed;
2014
cristyb5d5f722009-11-04 03:03:49 +00002015#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristya5ab7ad2012-01-21 23:49:58 +00002016 #pragma omp critical (MagickCore_TransposeImage)
cristy3ed852e2009-09-05 21:47:34 +00002017#endif
2018 proceed=SetImageProgress(image,SpliceImageTag,progress++,
2019 splice_image->rows);
2020 if (proceed == MagickFalse)
2021 status=MagickFalse;
2022 }
2023 }
2024 splice_view=DestroyCacheView(splice_view);
2025 image_view=DestroyCacheView(image_view);
2026 if (status == MagickFalse)
2027 splice_image=DestroyImage(splice_image);
2028 return(splice_image);
2029}
2030
2031/*
2032%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2033% %
2034% %
2035% %
2036% T r a n s f o r m I m a g e %
2037% %
2038% %
2039% %
2040%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2041%
2042% TransformImage() is a convenience method that behaves like ResizeImage() or
2043% CropImage() but accepts scaling and/or cropping information as a region
2044% geometry specification. If the operation fails, the original image handle
anthony9f4f0342011-03-28 11:47:22 +00002045% is left as is.
2046%
2047% This should only be used for single images.
cristy3ed852e2009-09-05 21:47:34 +00002048%
cristy010d7d12011-08-31 01:02:48 +00002049% This function destroys what it assumes to be a single image list.
2050% If the input image is part of a larger list, all other images in that list
2051% will be simply 'lost', not destroyed.
2052%
2053% Also if the crop generates a list of images only the first image is resized.
2054% And finally if the crop succeeds and the resize failed, you will get a
2055% cropped image, as well as a 'false' or 'failed' report.
2056%
dirk4e3ea8f2014-08-28 20:07:20 +00002057% This function and should probably be deprecated in favor of direct calls
cristy010d7d12011-08-31 01:02:48 +00002058% to CropImageToTiles() or ResizeImage(), as appropriate.
2059%
cristy3ed852e2009-09-05 21:47:34 +00002060% The format of the TransformImage method is:
2061%
2062% MagickBooleanType TransformImage(Image **image,const char *crop_geometry,
cristye941a752011-10-15 01:52:48 +00002063% const char *image_geometry,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002064%
2065% A description of each parameter follows:
2066%
2067% o image: the image The transformed image is returned as this parameter.
2068%
2069% o crop_geometry: A crop geometry string. This geometry defines a
2070% subregion of the image to crop.
2071%
2072% o image_geometry: An image geometry string. This geometry defines the
2073% final size of the image.
2074%
cristye941a752011-10-15 01:52:48 +00002075% o exception: return any errors or warnings in this structure.
2076%
cristy3ed852e2009-09-05 21:47:34 +00002077*/
dirk06f59012016-03-13 20:51:16 +01002078MagickPrivate MagickBooleanType TransformImage(Image **image,
cristye941a752011-10-15 01:52:48 +00002079 const char *crop_geometry,const char *image_geometry,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002080{
2081 Image
2082 *resize_image,
2083 *transform_image;
2084
cristy3ed852e2009-09-05 21:47:34 +00002085 RectangleInfo
2086 geometry;
2087
2088 assert(image != (Image **) NULL);
cristye1c94d92015-06-28 12:16:33 +00002089 assert((*image)->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00002090 if ((*image)->debug != MagickFalse)
2091 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
2092 transform_image=(*image);
2093 if (crop_geometry != (const char *) NULL)
2094 {
2095 Image
2096 *crop_image;
2097
cristy3ed852e2009-09-05 21:47:34 +00002098 /*
2099 Crop image to a user specified size.
2100 */
cristye941a752011-10-15 01:52:48 +00002101 crop_image=CropImageToTiles(*image,crop_geometry,exception);
cristy3ed852e2009-09-05 21:47:34 +00002102 if (crop_image == (Image *) NULL)
cristye941a752011-10-15 01:52:48 +00002103 transform_image=CloneImage(*image,0,0,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00002104 else
2105 {
2106 transform_image=DestroyImage(transform_image);
2107 transform_image=GetFirstImageInList(crop_image);
2108 }
2109 *image=transform_image;
2110 }
2111 if (image_geometry == (const char *) NULL)
2112 return(MagickTrue);
anthony9f4f0342011-03-28 11:47:22 +00002113
cristy3ed852e2009-09-05 21:47:34 +00002114 /*
2115 Scale image to a user specified size.
2116 */
dirk06f59012016-03-13 20:51:16 +01002117 (void) ParseRegionGeometry(transform_image,image_geometry,&geometry,exception);
cristy3ed852e2009-09-05 21:47:34 +00002118 if ((transform_image->columns == geometry.width) &&
2119 (transform_image->rows == geometry.height))
2120 return(MagickTrue);
cristy15b98cd2010-09-12 19:42:50 +00002121 resize_image=ResizeImage(transform_image,geometry.width,geometry.height,
cristyaa2c16c2012-03-25 22:21:35 +00002122 transform_image->filter,exception);
cristy3ed852e2009-09-05 21:47:34 +00002123 if (resize_image == (Image *) NULL)
2124 return(MagickFalse);
2125 transform_image=DestroyImage(transform_image);
2126 transform_image=resize_image;
2127 *image=transform_image;
2128 return(MagickTrue);
2129}
2130
2131/*
2132%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2133% %
2134% %
2135% %
cristy3ed852e2009-09-05 21:47:34 +00002136% T r a n s p o s e I m a g e %
2137% %
2138% %
2139% %
2140%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2141%
2142% TransposeImage() creates a horizontal mirror image by reflecting the pixels
2143% around the central y-axis while rotating them by 90 degrees.
2144%
2145% The format of the TransposeImage method is:
2146%
2147% Image *TransposeImage(const Image *image,ExceptionInfo *exception)
2148%
2149% A description of each parameter follows:
2150%
2151% o image: the image.
2152%
2153% o exception: return any errors or warnings in this structure.
2154%
2155*/
2156MagickExport Image *TransposeImage(const Image *image,ExceptionInfo *exception)
2157{
2158#define TransposeImageTag "Transpose/Image"
2159
cristyc4c8d132010-01-07 01:58:38 +00002160 CacheView
2161 *image_view,
2162 *transpose_view;
2163
cristy3ed852e2009-09-05 21:47:34 +00002164 Image
2165 *transpose_image;
2166
cristy3ed852e2009-09-05 21:47:34 +00002167 MagickBooleanType
2168 status;
2169
cristybb503372010-05-27 20:51:26 +00002170 MagickOffsetType
2171 progress;
2172
cristy3ed852e2009-09-05 21:47:34 +00002173 RectangleInfo
2174 page;
2175
cristybb503372010-05-27 20:51:26 +00002176 ssize_t
2177 y;
2178
cristy3ed852e2009-09-05 21:47:34 +00002179 assert(image != (const Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +00002180 assert(image->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00002181 if (image->debug != MagickFalse)
2182 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2183 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00002184 assert(exception->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00002185 transpose_image=CloneImage(image,image->rows,image->columns,MagickTrue,
2186 exception);
2187 if (transpose_image == (Image *) NULL)
2188 return((Image *) NULL);
2189 /*
2190 Transpose image.
2191 */
2192 status=MagickTrue;
2193 progress=0;
cristy46ff2672012-12-14 15:32:26 +00002194 image_view=AcquireVirtualCacheView(image,exception);
2195 transpose_view=AcquireAuthenticCacheView(transpose_image,exception);
cristyb5d5f722009-11-04 03:03:49 +00002196#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +00002197 #pragma omp parallel for schedule(static,4) shared(progress,status) \
cristy5e6b2592012-12-19 14:08:11 +00002198 magick_threads(image,transpose_image,image->rows,1)
cristy3ed852e2009-09-05 21:47:34 +00002199#endif
cristybb503372010-05-27 20:51:26 +00002200 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002201 {
cristy4c08aed2011-07-01 19:47:50 +00002202 register const Quantum
dirk05d2ff72015-11-18 23:13:43 +01002203 *magick_restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002204
cristy4c08aed2011-07-01 19:47:50 +00002205 register Quantum
dirk05d2ff72015-11-18 23:13:43 +01002206 *magick_restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002207
cristy4c08aed2011-07-01 19:47:50 +00002208 register ssize_t
2209 x;
2210
cristy3ed852e2009-09-05 21:47:34 +00002211 if (status == MagickFalse)
2212 continue;
cristybb503372010-05-27 20:51:26 +00002213 p=GetCacheViewVirtualPixels(image_view,0,(ssize_t) image->rows-y-1,
cristy3ed852e2009-09-05 21:47:34 +00002214 image->columns,1,exception);
cristy9af9b5d2010-08-15 17:04:28 +00002215 q=QueueCacheViewAuthenticPixels(transpose_view,(ssize_t) (image->rows-y-1),
2216 0,1,transpose_image->rows,exception);
cristy4c08aed2011-07-01 19:47:50 +00002217 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00002218 {
2219 status=MagickFalse;
2220 continue;
2221 }
cristy4c08aed2011-07-01 19:47:50 +00002222 for (x=0; x < (ssize_t) image->columns; x++)
2223 {
cristy010d7d12011-08-31 01:02:48 +00002224 register ssize_t
2225 i;
2226
Cristydf5e8b12016-12-02 17:26:39 -05002227 if (GetPixelWriteMask(image,q) == 0)
cristy10a6c612012-01-29 21:41:05 +00002228 {
cristyc3a58022013-10-09 23:22:42 +00002229 SetPixelBackgoundColor(transpose_image,q);
cristy10a6c612012-01-29 21:41:05 +00002230 p+=GetPixelChannels(image);
2231 q+=GetPixelChannels(transpose_image);
2232 continue;
2233 }
cristy010d7d12011-08-31 01:02:48 +00002234 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2235 {
cristy5a23c552013-02-13 14:34:28 +00002236 PixelChannel channel=GetPixelChannelChannel(image,i);
2237 PixelTrait traits=GetPixelChannelTraits(image,channel);
2238 PixelTrait transpose_traits=GetPixelChannelTraits(transpose_image,
2239 channel);
cristy010d7d12011-08-31 01:02:48 +00002240 if ((traits == UndefinedPixelTrait) ||
2241 (transpose_traits == UndefinedPixelTrait))
2242 continue;
cristy0beccfa2011-09-25 20:47:53 +00002243 SetPixelChannel(transpose_image,channel,p[i],q);
cristy010d7d12011-08-31 01:02:48 +00002244 }
cristyed231572011-07-14 02:18:59 +00002245 p+=GetPixelChannels(image);
2246 q+=GetPixelChannels(transpose_image);
cristy4c08aed2011-07-01 19:47:50 +00002247 }
cristy3ed852e2009-09-05 21:47:34 +00002248 if (SyncCacheViewAuthenticPixels(transpose_view,exception) == MagickFalse)
2249 status=MagickFalse;
2250 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2251 {
2252 MagickBooleanType
2253 proceed;
2254
cristyb5d5f722009-11-04 03:03:49 +00002255#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristya5ab7ad2012-01-21 23:49:58 +00002256 #pragma omp critical (MagickCore_TransposeImage)
cristy3ed852e2009-09-05 21:47:34 +00002257#endif
2258 proceed=SetImageProgress(image,TransposeImageTag,progress++,
2259 image->rows);
2260 if (proceed == MagickFalse)
2261 status=MagickFalse;
2262 }
2263 }
2264 transpose_view=DestroyCacheView(transpose_view);
2265 image_view=DestroyCacheView(image_view);
2266 transpose_image->type=image->type;
2267 page=transpose_image->page;
2268 Swap(page.width,page.height);
2269 Swap(page.x,page.y);
cristy3ed852e2009-09-05 21:47:34 +00002270 transpose_image->page=page;
2271 if (status == MagickFalse)
2272 transpose_image=DestroyImage(transpose_image);
2273 return(transpose_image);
2274}
2275
2276/*
2277%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2278% %
2279% %
2280% %
2281% T r a n s v e r s e I m a g e %
2282% %
2283% %
2284% %
2285%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2286%
2287% TransverseImage() creates a vertical mirror image by reflecting the pixels
2288% around the central x-axis while rotating them by 270 degrees.
2289%
2290% The format of the TransverseImage method is:
2291%
2292% Image *TransverseImage(const Image *image,ExceptionInfo *exception)
2293%
2294% A description of each parameter follows:
2295%
2296% o image: the image.
2297%
2298% o exception: return any errors or warnings in this structure.
2299%
2300*/
2301MagickExport Image *TransverseImage(const Image *image,ExceptionInfo *exception)
2302{
2303#define TransverseImageTag "Transverse/Image"
2304
cristyc4c8d132010-01-07 01:58:38 +00002305 CacheView
2306 *image_view,
2307 *transverse_view;
2308
cristy3ed852e2009-09-05 21:47:34 +00002309 Image
2310 *transverse_image;
2311
cristy3ed852e2009-09-05 21:47:34 +00002312 MagickBooleanType
2313 status;
2314
cristybb503372010-05-27 20:51:26 +00002315 MagickOffsetType
2316 progress;
2317
cristy3ed852e2009-09-05 21:47:34 +00002318 RectangleInfo
2319 page;
2320
cristybb503372010-05-27 20:51:26 +00002321 ssize_t
2322 y;
2323
cristy3ed852e2009-09-05 21:47:34 +00002324 assert(image != (const Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +00002325 assert(image->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00002326 if (image->debug != MagickFalse)
2327 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2328 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00002329 assert(exception->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00002330 transverse_image=CloneImage(image,image->rows,image->columns,MagickTrue,
2331 exception);
2332 if (transverse_image == (Image *) NULL)
2333 return((Image *) NULL);
2334 /*
2335 Transverse image.
2336 */
2337 status=MagickTrue;
2338 progress=0;
cristy46ff2672012-12-14 15:32:26 +00002339 image_view=AcquireVirtualCacheView(image,exception);
2340 transverse_view=AcquireAuthenticCacheView(transverse_image,exception);
cristyb5d5f722009-11-04 03:03:49 +00002341#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +00002342 #pragma omp parallel for schedule(static,4) shared(progress,status) \
cristy5e6b2592012-12-19 14:08:11 +00002343 magick_threads(image,transverse_image,image->rows,1)
cristy3ed852e2009-09-05 21:47:34 +00002344#endif
cristybb503372010-05-27 20:51:26 +00002345 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002346 {
2347 MagickBooleanType
2348 sync;
2349
cristy4c08aed2011-07-01 19:47:50 +00002350 register const Quantum
dirk05d2ff72015-11-18 23:13:43 +01002351 *magick_restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002352
cristy4c08aed2011-07-01 19:47:50 +00002353 register Quantum
dirk05d2ff72015-11-18 23:13:43 +01002354 *magick_restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002355
cristy010d7d12011-08-31 01:02:48 +00002356 register ssize_t
2357 x;
2358
cristy3ed852e2009-09-05 21:47:34 +00002359 if (status == MagickFalse)
2360 continue;
2361 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
cristy010d7d12011-08-31 01:02:48 +00002362 q=QueueCacheViewAuthenticPixels(transverse_view,(ssize_t) (image->rows-y-1),
2363 0,1,transverse_image->rows,exception);
cristy4c08aed2011-07-01 19:47:50 +00002364 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00002365 {
2366 status=MagickFalse;
2367 continue;
2368 }
cristyed231572011-07-14 02:18:59 +00002369 q+=GetPixelChannels(transverse_image)*image->columns;
cristybb503372010-05-27 20:51:26 +00002370 for (x=0; x < (ssize_t) image->columns; x++)
cristy4c08aed2011-07-01 19:47:50 +00002371 {
cristy010d7d12011-08-31 01:02:48 +00002372 register ssize_t
2373 i;
2374
cristyed231572011-07-14 02:18:59 +00002375 q-=GetPixelChannels(transverse_image);
Cristydf5e8b12016-12-02 17:26:39 -05002376 if (GetPixelWriteMask(image,p) == 0)
cristy10a6c612012-01-29 21:41:05 +00002377 {
2378 p+=GetPixelChannels(image);
2379 continue;
2380 }
cristy010d7d12011-08-31 01:02:48 +00002381 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2382 {
cristy5a23c552013-02-13 14:34:28 +00002383 PixelChannel channel=GetPixelChannelChannel(image,i);
2384 PixelTrait traits=GetPixelChannelTraits(image,channel);
2385 PixelTrait transverse_traits=GetPixelChannelTraits(transverse_image,
2386 channel);
cristy010d7d12011-08-31 01:02:48 +00002387 if ((traits == UndefinedPixelTrait) ||
2388 (transverse_traits == UndefinedPixelTrait))
2389 continue;
cristy0beccfa2011-09-25 20:47:53 +00002390 SetPixelChannel(transverse_image,channel,p[i],q);
cristy010d7d12011-08-31 01:02:48 +00002391 }
cristyed231572011-07-14 02:18:59 +00002392 p+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00002393 }
cristy3ed852e2009-09-05 21:47:34 +00002394 sync=SyncCacheViewAuthenticPixels(transverse_view,exception);
2395 if (sync == MagickFalse)
2396 status=MagickFalse;
2397 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2398 {
2399 MagickBooleanType
2400 proceed;
2401
cristyb5d5f722009-11-04 03:03:49 +00002402#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristya5ab7ad2012-01-21 23:49:58 +00002403 #pragma omp critical (MagickCore_TransverseImage)
cristy3ed852e2009-09-05 21:47:34 +00002404#endif
2405 proceed=SetImageProgress(image,TransverseImageTag,progress++,
2406 image->rows);
2407 if (proceed == MagickFalse)
2408 status=MagickFalse;
2409 }
2410 }
2411 transverse_view=DestroyCacheView(transverse_view);
2412 image_view=DestroyCacheView(image_view);
2413 transverse_image->type=image->type;
2414 page=transverse_image->page;
2415 Swap(page.width,page.height);
2416 Swap(page.x,page.y);
anthonybdaa5b32010-12-10 13:06:46 +00002417 if (page.width != 0)
2418 page.x=(ssize_t) (page.width-transverse_image->columns-page.x);
cristy3ed852e2009-09-05 21:47:34 +00002419 if (page.height != 0)
cristybb503372010-05-27 20:51:26 +00002420 page.y=(ssize_t) (page.height-transverse_image->rows-page.y);
cristy3ed852e2009-09-05 21:47:34 +00002421 transverse_image->page=page;
2422 if (status == MagickFalse)
2423 transverse_image=DestroyImage(transverse_image);
2424 return(transverse_image);
2425}
2426
2427/*
2428%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2429% %
2430% %
2431% %
2432% T r i m I m a g e %
2433% %
2434% %
2435% %
2436%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2437%
2438% TrimImage() trims pixels from the image edges. It allocates the memory
2439% necessary for the new Image structure and returns a pointer to the new
2440% image.
2441%
2442% The format of the TrimImage method is:
2443%
2444% Image *TrimImage(const Image *image,ExceptionInfo *exception)
2445%
2446% A description of each parameter follows:
2447%
2448% o image: the image.
2449%
2450% o exception: return any errors or warnings in this structure.
2451%
2452*/
2453MagickExport Image *TrimImage(const Image *image,ExceptionInfo *exception)
2454{
2455 RectangleInfo
2456 geometry;
2457
2458 assert(image != (const Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +00002459 assert(image->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00002460 if (image->debug != MagickFalse)
2461 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2462 geometry=GetImageBoundingBox(image,exception);
2463 if ((geometry.width == 0) || (geometry.height == 0))
2464 {
2465 Image
2466 *crop_image;
2467
2468 crop_image=CloneImage(image,1,1,MagickTrue,exception);
2469 if (crop_image == (Image *) NULL)
2470 return((Image *) NULL);
cristy4c08aed2011-07-01 19:47:50 +00002471 crop_image->background_color.alpha=(Quantum) TransparentAlpha;
cristy6d94a302013-03-01 00:47:47 +00002472 crop_image->alpha_trait=BlendPixelTrait;
cristyea1a8aa2011-10-20 13:24:06 +00002473 (void) SetImageBackgroundColor(crop_image,exception);
cristy3ed852e2009-09-05 21:47:34 +00002474 crop_image->page=image->page;
2475 crop_image->page.x=(-1);
2476 crop_image->page.y=(-1);
2477 return(crop_image);
2478 }
2479 geometry.x+=image->page.x;
2480 geometry.y+=image->page.y;
2481 return(CropImage(image,&geometry,exception));
2482}