blob: 62f9e889af4a3828c82150ae7cda3723cdbefca8 [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% TTTTT RRRR AAA N N SSSSS FFFFF OOO RRRR M M %
7% T R R A A NN N SS F O O R R MM MM %
8% T RRRR AAAAA N N N SSS FFF O O RRRR M M M %
9% T R R A A N NN SS F O O R R M M %
10% T R R A A N N SSSSS F OOO R R M M %
11% %
12% %
13% MagickCore Image Transform Methods %
14% %
15% Software Design %
16% John Cristy %
17% July 1992 %
18% %
19% %
cristy7e41fe82010-12-04 23:12:08 +000020% Copyright 1999-2011 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000021% dedicated to making software imaging solutions freely available. %
22% %
23% You may not use this file except in compliance with the License. You may %
24% obtain a copy of the License at %
25% %
26% http://www.imagemagick.org/script/license.php %
27% %
28% Unless required by applicable law or agreed to in writing, software %
29% distributed under the License is distributed on an "AS IS" BASIS, %
30% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31% See the License for the specific language governing permissions and %
32% limitations under the License. %
33% %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36%
37*/
38
39/*
40 Include declarations.
41*/
42#include "magick/studio.h"
cristy5a2ca482009-10-14 18:24:56 +000043#include "magick/attribute.h"
cristy3ed852e2009-09-05 21:47:34 +000044#include "magick/cache.h"
45#include "magick/cache-view.h"
46#include "magick/color.h"
47#include "magick/color-private.h"
48#include "magick/colorspace-private.h"
49#include "magick/composite.h"
cristy508d9312010-02-10 21:10:30 +000050#include "magick/draw.h"
cristy3ed852e2009-09-05 21:47:34 +000051#include "magick/effect.h"
52#include "magick/exception.h"
53#include "magick/exception-private.h"
54#include "magick/geometry.h"
55#include "magick/image.h"
56#include "magick/memory_.h"
57#include "magick/layer.h"
58#include "magick/list.h"
59#include "magick/monitor.h"
60#include "magick/monitor-private.h"
61#include "magick/pixel-private.h"
62#include "magick/resource_.h"
63#include "magick/resize.h"
64#include "magick/statistic.h"
65#include "magick/string_.h"
cristy09d81172010-10-21 16:15:05 +000066#include "magick/thread-private.h"
cristy3ed852e2009-09-05 21:47:34 +000067#include "magick/transform.h"
68
69/*
70%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
71% %
72% %
73% %
74% C h o p I m a g e %
75% %
76% %
77% %
78%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
79%
cristy00f95372010-02-13 16:39:29 +000080% ChopImage() removes a region of an image and collapses the image to occupy
81% the removed portion.
cristy3ed852e2009-09-05 21:47:34 +000082%
83% The format of the ChopImage method is:
84%
85% Image *ChopImage(const Image *image,const RectangleInfo *chop_info)
86% ExceptionInfo *exception)
87%
88% A description of each parameter follows:
89%
90% o image: the image.
91%
92% o chop_info: Define the region of the image to chop.
93%
94% o exception: return any errors or warnings in this structure.
95%
96*/
97MagickExport Image *ChopImage(const Image *image,const RectangleInfo *chop_info,
98 ExceptionInfo *exception)
99{
100#define ChopImageTag "Chop/Image"
101
cristyc4c8d132010-01-07 01:58:38 +0000102 CacheView
103 *chop_view,
104 *image_view;
105
cristy3ed852e2009-09-05 21:47:34 +0000106 Image
107 *chop_image;
108
cristy3ed852e2009-09-05 21:47:34 +0000109 MagickBooleanType
cristy00f95372010-02-13 16:39:29 +0000110 status;
cristy3ed852e2009-09-05 21:47:34 +0000111
cristyc2b1fb82010-10-25 13:01:28 +0000112 MagickOffsetType
113 progress;
114
cristy3ed852e2009-09-05 21:47:34 +0000115 RectangleInfo
116 extent;
117
cristy9d314ff2011-03-09 01:30:28 +0000118 ssize_t
119 y;
120
cristy3ed852e2009-09-05 21:47:34 +0000121 /*
122 Check chop geometry.
123 */
124 assert(image != (const Image *) NULL);
125 assert(image->signature == MagickSignature);
126 if (image->debug != MagickFalse)
127 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
128 assert(exception != (ExceptionInfo *) NULL);
129 assert(exception->signature == MagickSignature);
130 assert(chop_info != (RectangleInfo *) NULL);
cristybb503372010-05-27 20:51:26 +0000131 if (((chop_info->x+(ssize_t) chop_info->width) < 0) ||
132 ((chop_info->y+(ssize_t) chop_info->height) < 0) ||
133 (chop_info->x > (ssize_t) image->columns) ||
134 (chop_info->y > (ssize_t) image->rows))
cristy3ed852e2009-09-05 21:47:34 +0000135 ThrowImageException(OptionWarning,"GeometryDoesNotContainImage");
136 extent=(*chop_info);
cristybb503372010-05-27 20:51:26 +0000137 if ((extent.x+(ssize_t) extent.width) > (ssize_t) image->columns)
138 extent.width=(size_t) ((ssize_t) image->columns-extent.x);
139 if ((extent.y+(ssize_t) extent.height) > (ssize_t) image->rows)
140 extent.height=(size_t) ((ssize_t) image->rows-extent.y);
cristy3ed852e2009-09-05 21:47:34 +0000141 if (extent.x < 0)
142 {
cristybb503372010-05-27 20:51:26 +0000143 extent.width-=(size_t) (-extent.x);
cristy3ed852e2009-09-05 21:47:34 +0000144 extent.x=0;
145 }
146 if (extent.y < 0)
147 {
cristybb503372010-05-27 20:51:26 +0000148 extent.height-=(size_t) (-extent.y);
cristy3ed852e2009-09-05 21:47:34 +0000149 extent.y=0;
150 }
151 chop_image=CloneImage(image,image->columns-extent.width,image->rows-
152 extent.height,MagickTrue,exception);
153 if (chop_image == (Image *) NULL)
154 return((Image *) NULL);
155 /*
156 Extract chop image.
157 */
cristy00f95372010-02-13 16:39:29 +0000158 status=MagickTrue;
cristyc2b1fb82010-10-25 13:01:28 +0000159 progress=0;
cristy3ed852e2009-09-05 21:47:34 +0000160 image_view=AcquireCacheView(image);
161 chop_view=AcquireCacheView(chop_image);
cristy2224dcd2010-11-15 00:49:30 +0000162#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyc2b1fb82010-10-25 13:01:28 +0000163 #pragma omp parallel for schedule(dynamic,4) shared(progress,status) omp_throttle(1)
cristy09d81172010-10-21 16:15:05 +0000164#endif
cristybb503372010-05-27 20:51:26 +0000165 for (y=0; y < (ssize_t) extent.y; y++)
cristy3ed852e2009-09-05 21:47:34 +0000166 {
167 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000168 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000169
170 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000171 *restrict chop_indexes,
172 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +0000173
cristybb503372010-05-27 20:51:26 +0000174 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000175 x;
176
177 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000178 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000179
cristy00f95372010-02-13 16:39:29 +0000180 if (status == MagickFalse)
181 continue;
cristyc2b1fb82010-10-25 13:01:28 +0000182 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
183 q=QueueCacheViewAuthenticPixels(chop_view,0,y,chop_image->columns,1,
cristy3ed852e2009-09-05 21:47:34 +0000184 exception);
185 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
cristy00f95372010-02-13 16:39:29 +0000186 {
187 status=MagickFalse;
188 continue;
189 }
cristy3ed852e2009-09-05 21:47:34 +0000190 indexes=GetCacheViewAuthenticIndexQueue(image_view);
191 chop_indexes=GetCacheViewAuthenticIndexQueue(chop_view);
cristybb503372010-05-27 20:51:26 +0000192 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000193 {
cristybb503372010-05-27 20:51:26 +0000194 if ((x < extent.x) || (x >= (ssize_t) (extent.x+extent.width)))
cristy3ed852e2009-09-05 21:47:34 +0000195 {
196 *q=(*p);
197 if (indexes != (IndexPacket *) NULL)
198 {
199 if (chop_indexes != (IndexPacket *) NULL)
200 *chop_indexes++=indexes[x];
201 }
202 q++;
203 }
204 p++;
205 }
206 if (SyncCacheViewAuthenticPixels(chop_view,exception) == MagickFalse)
cristy00f95372010-02-13 16:39:29 +0000207 status=MagickFalse;
cristyc2b1fb82010-10-25 13:01:28 +0000208 if (image->progress_monitor != (MagickProgressMonitor) NULL)
209 {
210 MagickBooleanType
211 proceed;
212
cristy2224dcd2010-11-15 00:49:30 +0000213#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyc2b1fb82010-10-25 13:01:28 +0000214 #pragma omp critical (MagickCore_ChopImage)
215#endif
216 proceed=SetImageProgress(image,ChopImageTag,progress++,image->rows);
217 if (proceed == MagickFalse)
218 status=MagickFalse;
219 }
cristy3ed852e2009-09-05 21:47:34 +0000220 }
221 /*
222 Extract chop image.
223 */
cristy2224dcd2010-11-15 00:49:30 +0000224#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyc2b1fb82010-10-25 13:01:28 +0000225 #pragma omp parallel for schedule(dynamic,4) shared(progress,status) omp_throttle(1)
cristy09d81172010-10-21 16:15:05 +0000226#endif
cristybb503372010-05-27 20:51:26 +0000227 for (y=0; y < (ssize_t) (image->rows-(extent.y+extent.height)); y++)
cristy3ed852e2009-09-05 21:47:34 +0000228 {
229 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000230 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000231
232 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000233 *restrict chop_indexes,
234 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +0000235
cristybb503372010-05-27 20:51:26 +0000236 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000237 x;
238
239 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000240 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000241
cristy00f95372010-02-13 16:39:29 +0000242 if (status == MagickFalse)
243 continue;
cristyc2b1fb82010-10-25 13:01:28 +0000244 p=GetCacheViewVirtualPixels(image_view,0,extent.y+extent.height+y,
245 image->columns,1,exception);
246 q=QueueCacheViewAuthenticPixels(chop_view,0,extent.y+y,chop_image->columns,
247 1,exception);
cristy3ed852e2009-09-05 21:47:34 +0000248 if ((p == (PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
cristy00f95372010-02-13 16:39:29 +0000249 {
250 status=MagickFalse;
251 continue;
252 }
cristy3ed852e2009-09-05 21:47:34 +0000253 indexes=GetCacheViewAuthenticIndexQueue(image_view);
254 chop_indexes=GetCacheViewAuthenticIndexQueue(chop_view);
cristybb503372010-05-27 20:51:26 +0000255 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000256 {
cristybb503372010-05-27 20:51:26 +0000257 if ((x < extent.x) || (x >= (ssize_t) (extent.x+extent.width)))
cristy3ed852e2009-09-05 21:47:34 +0000258 {
259 *q=(*p);
260 if (indexes != (IndexPacket *) NULL)
261 {
262 if (chop_indexes != (IndexPacket *) NULL)
263 *chop_indexes++=indexes[x];
264 }
265 q++;
266 }
267 p++;
268 }
269 if (SyncCacheViewAuthenticPixels(chop_view,exception) == MagickFalse)
cristy00f95372010-02-13 16:39:29 +0000270 status=MagickFalse;
cristyc2b1fb82010-10-25 13:01:28 +0000271 if (image->progress_monitor != (MagickProgressMonitor) NULL)
272 {
273 MagickBooleanType
274 proceed;
275
cristy2224dcd2010-11-15 00:49:30 +0000276#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyc2b1fb82010-10-25 13:01:28 +0000277 #pragma omp critical (MagickCore_ChopImage)
278#endif
279 proceed=SetImageProgress(image,ChopImageTag,progress++,image->rows);
280 if (proceed == MagickFalse)
281 status=MagickFalse;
282 }
cristy3ed852e2009-09-05 21:47:34 +0000283 }
284 chop_view=DestroyCacheView(chop_view);
285 image_view=DestroyCacheView(image_view);
286 chop_image->type=image->type;
287 return(chop_image);
288}
289
290/*
291%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
292% %
293% %
294% %
295+ C o n s o l i d a t e C M Y K I m a g e %
296% %
297% %
298% %
299%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
300%
301% ConsolidateCMYKImage() consolidates separate C, M, Y, and K planes into a
302% single image.
303%
304% The format of the ConsolidateCMYKImage method is:
305%
306% Image *ConsolidateCMYKImage(const Image *image,ExceptionInfo *exception)
307%
308% A description of each parameter follows:
309%
310% o image: the image sequence.
311%
312% o exception: return any errors or warnings in this structure.
313%
314*/
315MagickExport Image *ConsolidateCMYKImages(const Image *images,
316 ExceptionInfo *exception)
317{
cristyc5c6f662010-09-22 14:23:02 +0000318 CacheView
319 *cmyk_view,
320 *image_view;
cristy2224dcd2010-11-15 00:49:30 +0000321
cristy3ed852e2009-09-05 21:47:34 +0000322 Image
323 *cmyk_image,
324 *cmyk_images;
325
cristybb503372010-05-27 20:51:26 +0000326 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000327 i;
328
cristy2224dcd2010-11-15 00:49:30 +0000329 ssize_t
330 y;
331
cristy3ed852e2009-09-05 21:47:34 +0000332 /*
333 Consolidate separate C, M, Y, and K planes into a single image.
334 */
335 assert(images != (Image *) NULL);
336 assert(images->signature == MagickSignature);
337 if (images->debug != MagickFalse)
338 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
339 assert(exception != (ExceptionInfo *) NULL);
340 assert(exception->signature == MagickSignature);
341 cmyk_images=NewImageList();
cristybb503372010-05-27 20:51:26 +0000342 for (i=0; i < (ssize_t) GetImageListLength(images); i+=4)
cristy3ed852e2009-09-05 21:47:34 +0000343 {
344 cmyk_image=CloneImage(images,images->columns,images->rows,MagickTrue,
345 exception);
346 if (cmyk_image == (Image *) NULL)
347 break;
348 if (SetImageStorageClass(cmyk_image,DirectClass) == MagickFalse)
349 break;
350 (void) SetImageColorspace(cmyk_image,CMYKColorspace);
cristyc5c6f662010-09-22 14:23:02 +0000351 image_view=AcquireCacheView(images);
352 cmyk_view=AcquireCacheView(cmyk_image);
cristybb503372010-05-27 20:51:26 +0000353 for (y=0; y < (ssize_t) images->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000354 {
355 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000356 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000357
cristybb503372010-05-27 20:51:26 +0000358 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000359 x;
360
361 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000362 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000363
cristyc5c6f662010-09-22 14:23:02 +0000364 p=GetCacheViewVirtualPixels(image_view,0,y,images->columns,1,exception);
365 q=QueueCacheViewAuthenticPixels(cmyk_view,0,y,cmyk_image->columns,1,
366 exception);
cristy3ed852e2009-09-05 21:47:34 +0000367 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
368 break;
cristybb503372010-05-27 20:51:26 +0000369 for (x=0; x < (ssize_t) images->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000370 {
371 q->red=(Quantum) (QuantumRange-PixelIntensityToQuantum(p));
372 p++;
373 q++;
374 }
cristyc5c6f662010-09-22 14:23:02 +0000375 if (SyncCacheViewAuthenticPixels(cmyk_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000376 break;
377 }
cristyc5c6f662010-09-22 14:23:02 +0000378 cmyk_view=DestroyCacheView(cmyk_view);
379 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +0000380 images=GetNextImageInList(images);
381 if (images == (Image *) NULL)
382 break;
cristyc5c6f662010-09-22 14:23:02 +0000383 image_view=AcquireCacheView(images);
384 cmyk_view=AcquireCacheView(cmyk_image);
cristybb503372010-05-27 20:51:26 +0000385 for (y=0; y < (ssize_t) images->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000386 {
387 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000388 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000389
cristybb503372010-05-27 20:51:26 +0000390 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000391 x;
392
393 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000394 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000395
cristyc5c6f662010-09-22 14:23:02 +0000396 p=GetCacheViewVirtualPixels(image_view,0,y,images->columns,1,exception);
397 q=GetCacheViewAuthenticPixels(cmyk_view,0,y,cmyk_image->columns,1,
398 exception);
cristy3ed852e2009-09-05 21:47:34 +0000399 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
400 break;
cristybb503372010-05-27 20:51:26 +0000401 for (x=0; x < (ssize_t) images->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000402 {
403 q->green=(Quantum) (QuantumRange-PixelIntensityToQuantum(p));
404 p++;
405 q++;
406 }
cristyc5c6f662010-09-22 14:23:02 +0000407 if (SyncCacheViewAuthenticPixels(cmyk_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000408 break;
409 }
cristyc5c6f662010-09-22 14:23:02 +0000410 cmyk_view=DestroyCacheView(cmyk_view);
411 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +0000412 images=GetNextImageInList(images);
413 if (images == (Image *) NULL)
414 break;
cristyc5c6f662010-09-22 14:23:02 +0000415 image_view=AcquireCacheView(images);
416 cmyk_view=AcquireCacheView(cmyk_image);
cristybb503372010-05-27 20:51:26 +0000417 for (y=0; y < (ssize_t) images->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000418 {
419 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000420 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000421
cristybb503372010-05-27 20:51:26 +0000422 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000423 x;
424
425 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000426 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000427
cristyc5c6f662010-09-22 14:23:02 +0000428 p=GetCacheViewVirtualPixels(image_view,0,y,images->columns,1,exception);
429 q=GetCacheViewAuthenticPixels(cmyk_view,0,y,cmyk_image->columns,1,
430 exception);
cristy3ed852e2009-09-05 21:47:34 +0000431 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
432 break;
cristybb503372010-05-27 20:51:26 +0000433 for (x=0; x < (ssize_t) images->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000434 {
435 q->blue=(Quantum) (QuantumRange-PixelIntensityToQuantum(p));
436 p++;
437 q++;
438 }
cristyc5c6f662010-09-22 14:23:02 +0000439 if (SyncCacheViewAuthenticPixels(cmyk_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000440 break;
441 }
cristyc5c6f662010-09-22 14:23:02 +0000442 cmyk_view=DestroyCacheView(cmyk_view);
443 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +0000444 images=GetNextImageInList(images);
445 if (images == (Image *) NULL)
446 break;
cristyc5c6f662010-09-22 14:23:02 +0000447 image_view=AcquireCacheView(images);
448 cmyk_view=AcquireCacheView(cmyk_image);
cristybb503372010-05-27 20:51:26 +0000449 for (y=0; y < (ssize_t) images->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000450 {
451 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000452 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000453
454 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000455 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +0000456
cristybb503372010-05-27 20:51:26 +0000457 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000458 x;
459
460 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000461 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000462
cristyc5c6f662010-09-22 14:23:02 +0000463 p=GetCacheViewVirtualPixels(image_view,0,y,images->columns,1,exception);
464 q=GetCacheViewAuthenticPixels(cmyk_view,0,y,cmyk_image->columns,1,
465 exception);
cristy3ed852e2009-09-05 21:47:34 +0000466 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
467 break;
cristyc5c6f662010-09-22 14:23:02 +0000468 indexes=GetCacheViewAuthenticIndexQueue(cmyk_view);
cristybb503372010-05-27 20:51:26 +0000469 for (x=0; x < (ssize_t) images->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000470 {
471 indexes[x]=(IndexPacket) (QuantumRange-PixelIntensityToQuantum(p));
472 p++;
473 }
cristyc5c6f662010-09-22 14:23:02 +0000474 if (SyncCacheViewAuthenticPixels(cmyk_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000475 break;
476 }
cristyc5c6f662010-09-22 14:23:02 +0000477 cmyk_view=DestroyCacheView(cmyk_view);
478 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +0000479 AppendImageToList(&cmyk_images,cmyk_image);
480 images=GetNextImageInList(images);
481 if (images == (Image *) NULL)
482 break;
483 }
484 return(cmyk_images);
485}
486
487/*
488%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
489% %
490% %
491% %
492% C r o p I m a g e %
493% %
494% %
495% %
496%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
497%
498% CropImage() extracts a region of the image starting at the offset defined
499% by geometry.
500%
501% The format of the CropImage method is:
502%
503% Image *CropImage(const Image *image,const RectangleInfo *geometry,
504% ExceptionInfo *exception)
505%
506% A description of each parameter follows:
507%
508% o image: the image.
509%
510% o geometry: Define the region of the image to crop with members
511% x, y, width, and height.
512%
513% o exception: return any errors or warnings in this structure.
514%
515*/
516MagickExport Image *CropImage(const Image *image,const RectangleInfo *geometry,
517 ExceptionInfo *exception)
518{
519#define CropImageTag "Crop/Image"
520
cristyc4c8d132010-01-07 01:58:38 +0000521 CacheView
522 *crop_view,
523 *image_view;
524
cristy3ed852e2009-09-05 21:47:34 +0000525 Image
526 *crop_image;
527
cristy3ed852e2009-09-05 21:47:34 +0000528 MagickBooleanType
529 status;
530
cristybb503372010-05-27 20:51:26 +0000531 MagickOffsetType
532 progress;
533
cristy3ed852e2009-09-05 21:47:34 +0000534 RectangleInfo
535 bounding_box,
536 page;
537
cristybb503372010-05-27 20:51:26 +0000538 ssize_t
539 y;
540
cristy3ed852e2009-09-05 21:47:34 +0000541 /*
542 Check crop geometry.
543 */
544 assert(image != (const Image *) NULL);
545 assert(image->signature == MagickSignature);
546 if (image->debug != MagickFalse)
547 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
548 assert(geometry != (const RectangleInfo *) NULL);
549 assert(exception != (ExceptionInfo *) NULL);
550 assert(exception->signature == MagickSignature);
551 bounding_box=image->page;
552 if ((bounding_box.width == 0) || (bounding_box.height == 0))
553 {
554 bounding_box.width=image->columns;
555 bounding_box.height=image->rows;
556 }
557 page=(*geometry);
558 if (page.width == 0)
559 page.width=bounding_box.width;
560 if (page.height == 0)
561 page.height=bounding_box.height;
cristybb503372010-05-27 20:51:26 +0000562 if (((bounding_box.x-page.x) >= (ssize_t) page.width) ||
563 ((bounding_box.y-page.y) >= (ssize_t) page.height) ||
564 ((page.x-bounding_box.x) > (ssize_t) image->columns) ||
565 ((page.y-bounding_box.y) > (ssize_t) image->rows))
cristy3ed852e2009-09-05 21:47:34 +0000566 {
567 /*
568 Crop is not within virtual canvas, return 1 pixel transparent image.
569 */
570 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
571 "GeometryDoesNotContainImage","`%s'",image->filename);
572 crop_image=CloneImage(image,1,1,MagickTrue,exception);
573 if (crop_image == (Image *) NULL)
574 return((Image *) NULL);
575 crop_image->background_color.opacity=(Quantum) TransparentOpacity;
576 (void) SetImageBackgroundColor(crop_image);
577 crop_image->page=bounding_box;
578 crop_image->page.x=(-1);
579 crop_image->page.y=(-1);
580 if (crop_image->dispose == BackgroundDispose)
581 crop_image->dispose=NoneDispose;
582 return(crop_image);
583 }
584 if ((page.x < 0) && (bounding_box.x >= 0))
585 {
586 page.width+=page.x-bounding_box.x;
587 page.x=0;
588 }
589 else
590 {
591 page.width-=bounding_box.x-page.x;
592 page.x-=bounding_box.x;
593 if (page.x < 0)
594 page.x=0;
595 }
596 if ((page.y < 0) && (bounding_box.y >= 0))
597 {
598 page.height+=page.y-bounding_box.y;
599 page.y=0;
600 }
601 else
602 {
603 page.height-=bounding_box.y-page.y;
604 page.y-=bounding_box.y;
605 if (page.y < 0)
606 page.y=0;
607 }
cristybb503372010-05-27 20:51:26 +0000608 if ((size_t) (page.x+page.width) > image->columns)
cristy3ed852e2009-09-05 21:47:34 +0000609 page.width=image->columns-page.x;
cristy1e4aa462010-02-15 00:04:18 +0000610 if ((geometry->width != 0) && (page.width > geometry->width))
611 page.width=geometry->width;
cristybb503372010-05-27 20:51:26 +0000612 if ((size_t) (page.y+page.height) > image->rows)
cristy3ed852e2009-09-05 21:47:34 +0000613 page.height=image->rows-page.y;
cristy1e4aa462010-02-15 00:04:18 +0000614 if ((geometry->height != 0) && (page.height > geometry->height))
615 page.height=geometry->height;
cristy3ed852e2009-09-05 21:47:34 +0000616 bounding_box.x+=page.x;
617 bounding_box.y+=page.y;
618 if ((page.width == 0) || (page.height == 0))
619 {
620 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
621 "GeometryDoesNotContainImage","`%s'",image->filename);
622 return((Image *) NULL);
623 }
624 /*
625 Initialize crop image attributes.
626 */
627 crop_image=CloneImage(image,page.width,page.height,MagickTrue,exception);
628 if (crop_image == (Image *) NULL)
629 return((Image *) NULL);
630 crop_image->page.width=image->page.width;
631 crop_image->page.height=image->page.height;
cristybb503372010-05-27 20:51:26 +0000632 if (((ssize_t) (bounding_box.x+bounding_box.width) > (ssize_t) image->page.width) ||
633 ((ssize_t) (bounding_box.y+bounding_box.height) > (ssize_t) image->page.height))
cristy3ed852e2009-09-05 21:47:34 +0000634 {
635 crop_image->page.width=bounding_box.width;
636 crop_image->page.height=bounding_box.height;
637 }
638 crop_image->page.x=bounding_box.x;
639 crop_image->page.y=bounding_box.y;
640 /*
641 Crop image.
642 */
643 status=MagickTrue;
644 progress=0;
645 image_view=AcquireCacheView(image);
646 crop_view=AcquireCacheView(crop_image);
cristy2224dcd2010-11-15 00:49:30 +0000647#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy09d81172010-10-21 16:15:05 +0000648 #pragma omp parallel for schedule(dynamic,4) shared(progress,status) omp_throttle(1)
cristy3ed852e2009-09-05 21:47:34 +0000649#endif
cristybb503372010-05-27 20:51:26 +0000650 for (y=0; y < (ssize_t) crop_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000651 {
652 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000653 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +0000654
655 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000656 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000657
658 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000659 *restrict crop_indexes;
cristy3ed852e2009-09-05 21:47:34 +0000660
661 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000662 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000663
664 if (status == MagickFalse)
665 continue;
666 p=GetCacheViewVirtualPixels(image_view,page.x,page.y+y,crop_image->columns,
667 1,exception);
668 q=QueueCacheViewAuthenticPixels(crop_view,0,y,crop_image->columns,1,
669 exception);
670 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
671 {
672 status=MagickFalse;
673 continue;
674 }
cristy3ed852e2009-09-05 21:47:34 +0000675 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristy6c207342010-02-19 17:49:50 +0000676 crop_indexes=GetCacheViewAuthenticIndexQueue(crop_view);
cristy4cb39ab2010-06-07 13:59:16 +0000677 (void) CopyMagickMemory(q,p,(size_t) crop_image->columns*sizeof(*p));
cristy6c207342010-02-19 17:49:50 +0000678 if ((indexes != (IndexPacket *) NULL) &&
679 (crop_indexes != (IndexPacket *) NULL))
680 (void) CopyMagickMemory(crop_indexes,indexes,(size_t) crop_image->columns*
681 sizeof(*crop_indexes));
cristy3ed852e2009-09-05 21:47:34 +0000682 if (SyncCacheViewAuthenticPixels(crop_view,exception) == MagickFalse)
683 status=MagickFalse;
684 if (image->progress_monitor != (MagickProgressMonitor) NULL)
685 {
686 MagickBooleanType
687 proceed;
688
cristy2224dcd2010-11-15 00:49:30 +0000689#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +0000690 #pragma omp critical (MagickCore_CropImage)
691#endif
692 proceed=SetImageProgress(image,CropImageTag,progress++,image->rows);
693 if (proceed == MagickFalse)
694 status=MagickFalse;
695 }
696 }
697 crop_view=DestroyCacheView(crop_view);
698 image_view=DestroyCacheView(image_view);
699 crop_image->type=image->type;
700 if (status == MagickFalse)
701 crop_image=DestroyImage(crop_image);
702 return(crop_image);
703}
704
705/*
706%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
707% %
708% %
709% %
710% E x c e r p t I m a g e %
711% %
712% %
713% %
714%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
715%
716% ExcerptImage() returns a excerpt of the image as defined by the geometry.
717%
718% The format of the ExcerptImage method is:
719%
720% Image *ExcerptImage(const Image *image,const RectangleInfo *geometry,
721% ExceptionInfo *exception)
722%
723% A description of each parameter follows:
724%
725% o image: the image.
726%
727% o geometry: Define the region of the image to extend with members
728% x, y, width, and height.
729%
730% o exception: return any errors or warnings in this structure.
731%
732*/
733MagickExport Image *ExcerptImage(const Image *image,
734 const RectangleInfo *geometry,ExceptionInfo *exception)
735{
736#define ExcerptImageTag "Excerpt/Image"
737
cristyc4c8d132010-01-07 01:58:38 +0000738 CacheView
739 *excerpt_view,
740 *image_view;
741
cristy3ed852e2009-09-05 21:47:34 +0000742 Image
743 *excerpt_image;
744
cristy3ed852e2009-09-05 21:47:34 +0000745 MagickBooleanType
746 status;
747
cristybb503372010-05-27 20:51:26 +0000748 MagickOffsetType
749 progress;
750
751 ssize_t
752 y;
753
cristy3ed852e2009-09-05 21:47:34 +0000754 /*
755 Allocate excerpt image.
756 */
757 assert(image != (const Image *) NULL);
758 assert(image->signature == MagickSignature);
759 if (image->debug != MagickFalse)
760 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
761 assert(geometry != (const RectangleInfo *) NULL);
762 assert(exception != (ExceptionInfo *) NULL);
763 assert(exception->signature == MagickSignature);
764 excerpt_image=CloneImage(image,geometry->width,geometry->height,MagickTrue,
765 exception);
766 if (excerpt_image == (Image *) NULL)
767 return((Image *) NULL);
768 /*
769 Excerpt each row.
770 */
771 status=MagickTrue;
772 progress=0;
773 image_view=AcquireCacheView(image);
774 excerpt_view=AcquireCacheView(excerpt_image);
cristyb5d5f722009-11-04 03:03:49 +0000775#if defined(MAGICKCORE_OPENMP_SUPPORT)
776 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000777#endif
cristybb503372010-05-27 20:51:26 +0000778 for (y=0; y < (ssize_t) excerpt_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000779 {
780 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000781 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000782
783 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000784 *restrict excerpt_indexes,
785 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +0000786
787 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000788 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000789
790 if (status == MagickFalse)
791 continue;
792 p=GetCacheViewVirtualPixels(image_view,geometry->x,geometry->y+y,
793 geometry->width,1,exception);
794 q=GetCacheViewAuthenticPixels(excerpt_view,0,y,excerpt_image->columns,1,
795 exception);
796 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
797 {
798 status=MagickFalse;
799 continue;
800 }
801 (void) CopyMagickMemory(q,p,(size_t) excerpt_image->columns*sizeof(*q));
802 indexes=GetCacheViewAuthenticIndexQueue(image_view);
803 if (indexes != (IndexPacket *) NULL)
804 {
805 excerpt_indexes=GetCacheViewAuthenticIndexQueue(excerpt_view);
806 if (excerpt_indexes != (IndexPacket *) NULL)
807 (void) CopyMagickMemory(excerpt_indexes,indexes,(size_t)
808 excerpt_image->columns*sizeof(*excerpt_indexes));
809 }
810 if (SyncCacheViewAuthenticPixels(excerpt_view,exception) == MagickFalse)
811 status=MagickFalse;
812 if (image->progress_monitor != (MagickProgressMonitor) NULL)
813 {
814 MagickBooleanType
815 proceed;
816
cristyb5d5f722009-11-04 03:03:49 +0000817#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +0000818 #pragma omp critical (MagickCore_ExcerptImage)
819#endif
820 proceed=SetImageProgress(image,ExcerptImageTag,progress++,image->rows);
821 if (proceed == MagickFalse)
822 status=MagickFalse;
823 }
824 }
825 excerpt_view=DestroyCacheView(excerpt_view);
826 image_view=DestroyCacheView(image_view);
827 excerpt_image->type=image->type;
828 if (status == MagickFalse)
829 excerpt_image=DestroyImage(excerpt_image);
830 return(excerpt_image);
831}
832
833/*
834%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
835% %
836% %
837% %
838% E x t e n t I m a g e %
839% %
840% %
841% %
842%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
843%
844% ExtentImage() extends the image as defined by the geometry, gravity, and
845% image background color. Set the (x,y) offset of the geometry to move the
846% original image relative to the extended image.
847%
848% The format of the ExtentImage method is:
849%
850% Image *ExtentImage(const Image *image,const RectangleInfo *geometry,
851% ExceptionInfo *exception)
852%
853% A description of each parameter follows:
854%
855% o image: the image.
856%
857% o geometry: Define the region of the image to extend with members
858% x, y, width, and height.
859%
860% o exception: return any errors or warnings in this structure.
861%
862*/
863MagickExport Image *ExtentImage(const Image *image,
864 const RectangleInfo *geometry,ExceptionInfo *exception)
865{
866 Image
867 *extent_image;
868
869 /*
870 Allocate extent image.
871 */
872 assert(image != (const Image *) NULL);
873 assert(image->signature == MagickSignature);
874 if (image->debug != MagickFalse)
875 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
876 assert(geometry != (const RectangleInfo *) NULL);
877 assert(exception != (ExceptionInfo *) NULL);
878 assert(exception->signature == MagickSignature);
879 extent_image=CloneImage(image,geometry->width,geometry->height,MagickTrue,
880 exception);
881 if (extent_image == (Image *) NULL)
882 return((Image *) NULL);
883 if (SetImageStorageClass(extent_image,DirectClass) == MagickFalse)
884 {
885 InheritException(exception,&extent_image->exception);
886 extent_image=DestroyImage(extent_image);
887 return((Image *) NULL);
888 }
889 if (extent_image->background_color.opacity != OpaqueOpacity)
890 extent_image->matte=MagickTrue;
891 (void) SetImageBackgroundColor(extent_image);
cristy45b62612010-09-13 20:01:36 +0000892 (void) CompositeImage(extent_image,image->compose,image,-geometry->x,
893 -geometry->y);
cristy3ed852e2009-09-05 21:47:34 +0000894 return(extent_image);
895}
896
897/*
898%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
899% %
900% %
901% %
902% F l i p I m a g e %
903% %
904% %
905% %
906%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
907%
908% FlipImage() creates a vertical mirror image by reflecting the pixels
909% around the central x-axis.
910%
911% The format of the FlipImage method is:
912%
913% Image *FlipImage(const Image *image,ExceptionInfo *exception)
914%
915% A description of each parameter follows:
916%
917% o image: the image.
918%
919% o exception: return any errors or warnings in this structure.
920%
921*/
922MagickExport Image *FlipImage(const Image *image,ExceptionInfo *exception)
923{
924#define FlipImageTag "Flip/Image"
925
cristyc4c8d132010-01-07 01:58:38 +0000926 CacheView
927 *flip_view,
928 *image_view;
929
cristy3ed852e2009-09-05 21:47:34 +0000930 Image
931 *flip_image;
932
cristy3ed852e2009-09-05 21:47:34 +0000933 MagickBooleanType
934 status;
935
cristybb503372010-05-27 20:51:26 +0000936 MagickOffsetType
937 progress;
938
cristy74ea2cd2010-12-08 02:54:25 +0000939 RectangleInfo
940 page;
941
cristybb503372010-05-27 20:51:26 +0000942 ssize_t
943 y;
944
cristy3ed852e2009-09-05 21:47:34 +0000945 assert(image != (const Image *) NULL);
946 assert(image->signature == MagickSignature);
947 if (image->debug != MagickFalse)
948 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
949 assert(exception != (ExceptionInfo *) NULL);
950 assert(exception->signature == MagickSignature);
951 flip_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
952 if (flip_image == (Image *) NULL)
953 return((Image *) NULL);
954 /*
955 Flip image.
956 */
957 status=MagickTrue;
958 progress=0;
cristy74ea2cd2010-12-08 02:54:25 +0000959 page=image->page;
cristy3ed852e2009-09-05 21:47:34 +0000960 image_view=AcquireCacheView(image);
961 flip_view=AcquireCacheView(flip_image);
cristy5db1f092010-06-07 13:07:26 +0000962#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy09d81172010-10-21 16:15:05 +0000963 #pragma omp parallel for schedule(dynamic,4) shared(progress,status) omp_throttle(1)
cristy3ed852e2009-09-05 21:47:34 +0000964#endif
cristybb503372010-05-27 20:51:26 +0000965 for (y=0; y < (ssize_t) flip_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000966 {
967 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000968 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +0000969
970 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000971 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000972
973 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000974 *restrict flip_indexes;
cristy3ed852e2009-09-05 21:47:34 +0000975
976 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000977 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000978
979 if (status == MagickFalse)
980 continue;
981 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
cristy5db1f092010-06-07 13:07:26 +0000982 q=QueueCacheViewAuthenticPixels(flip_view,0,(ssize_t) (flip_image->rows-y-
983 1),flip_image->columns,1,exception);
cristy3ed852e2009-09-05 21:47:34 +0000984 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
985 {
986 status=MagickFalse;
987 continue;
988 }
989 (void) CopyMagickMemory(q,p,(size_t) image->columns*sizeof(*q));
990 indexes=GetCacheViewVirtualIndexQueue(image_view);
991 if (indexes != (const IndexPacket *) NULL)
992 {
993 flip_indexes=GetCacheViewAuthenticIndexQueue(flip_view);
994 if (flip_indexes != (IndexPacket *) NULL)
995 (void) CopyMagickMemory(flip_indexes,indexes,(size_t) image->columns*
996 sizeof(*flip_indexes));
997 }
998 if (SyncCacheViewAuthenticPixels(flip_view,exception) == MagickFalse)
999 status=MagickFalse;
1000 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1001 {
1002 MagickBooleanType
1003 proceed;
1004
cristy2224dcd2010-11-15 00:49:30 +00001005#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00001006 #pragma omp critical (MagickCore_FlipImage)
1007#endif
1008 proceed=SetImageProgress(image,FlipImageTag,progress++,image->rows);
1009 if (proceed == MagickFalse)
1010 status=MagickFalse;
1011 }
1012 }
1013 flip_view=DestroyCacheView(flip_view);
1014 image_view=DestroyCacheView(image_view);
1015 flip_image->type=image->type;
anthony37a1b912010-12-10 12:45:59 +00001016 if (page.height != 0)
1017 page.y=(ssize_t) (page.height-flip_image->rows-page.y);
cristy74ea2cd2010-12-08 02:54:25 +00001018 flip_image->page=page;
cristy3ed852e2009-09-05 21:47:34 +00001019 if (status == MagickFalse)
1020 flip_image=DestroyImage(flip_image);
1021 return(flip_image);
1022}
1023
1024/*
1025%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1026% %
1027% %
1028% %
1029% F l o p I m a g e %
1030% %
1031% %
1032% %
1033%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1034%
1035% FlopImage() creates a horizontal mirror image by reflecting the pixels
1036% around the central y-axis.
1037%
1038% The format of the FlopImage method is:
1039%
1040% Image *FlopImage(const Image *image,ExceptionInfo *exception)
1041%
1042% A description of each parameter follows:
1043%
1044% o image: the image.
1045%
1046% o exception: return any errors or warnings in this structure.
1047%
1048*/
1049MagickExport Image *FlopImage(const Image *image,ExceptionInfo *exception)
1050{
1051#define FlopImageTag "Flop/Image"
1052
cristyc4c8d132010-01-07 01:58:38 +00001053 CacheView
1054 *flop_view,
1055 *image_view;
1056
cristy3ed852e2009-09-05 21:47:34 +00001057 Image
1058 *flop_image;
1059
cristy3ed852e2009-09-05 21:47:34 +00001060 MagickBooleanType
1061 status;
1062
cristybb503372010-05-27 20:51:26 +00001063 MagickOffsetType
1064 progress;
1065
cristy74ea2cd2010-12-08 02:54:25 +00001066 RectangleInfo
1067 page;
1068
cristybb503372010-05-27 20:51:26 +00001069 ssize_t
1070 y;
1071
cristy3ed852e2009-09-05 21:47:34 +00001072 assert(image != (const Image *) NULL);
1073 assert(image->signature == MagickSignature);
1074 if (image->debug != MagickFalse)
1075 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1076 assert(exception != (ExceptionInfo *) NULL);
1077 assert(exception->signature == MagickSignature);
1078 flop_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
1079 if (flop_image == (Image *) NULL)
1080 return((Image *) NULL);
1081 /*
1082 Flop each row.
1083 */
1084 status=MagickTrue;
1085 progress=0;
cristy74ea2cd2010-12-08 02:54:25 +00001086 page=image->page;
cristy3ed852e2009-09-05 21:47:34 +00001087 image_view=AcquireCacheView(image);
1088 flop_view=AcquireCacheView(flop_image);
cristy5db1f092010-06-07 13:07:26 +00001089#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy09d81172010-10-21 16:15:05 +00001090 #pragma omp parallel for schedule(dynamic,4) shared(progress,status) omp_throttle(1)
cristy3ed852e2009-09-05 21:47:34 +00001091#endif
cristybb503372010-05-27 20:51:26 +00001092 for (y=0; y < (ssize_t) flop_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001093 {
1094 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001095 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00001096
1097 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001098 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001099
1100 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001101 *restrict flop_indexes;
cristy3ed852e2009-09-05 21:47:34 +00001102
cristybb503372010-05-27 20:51:26 +00001103 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001104 x;
1105
1106 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001107 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001108
1109 if (status == MagickFalse)
1110 continue;
1111 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1112 q=QueueCacheViewAuthenticPixels(flop_view,0,y,flop_image->columns,1,
1113 exception);
1114 if ((p == (PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
1115 {
1116 status=MagickFalse;
1117 continue;
1118 }
1119 q+=flop_image->columns;
1120 indexes=GetCacheViewVirtualIndexQueue(image_view);
1121 flop_indexes=GetCacheViewAuthenticIndexQueue(flop_view);
cristybb503372010-05-27 20:51:26 +00001122 for (x=0; x < (ssize_t) flop_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001123 {
1124 (*--q)=(*p++);
1125 if ((indexes != (const IndexPacket *) NULL) &&
1126 (flop_indexes != (IndexPacket *) NULL))
1127 flop_indexes[flop_image->columns-x-1]=indexes[x];
1128 }
1129 if (SyncCacheViewAuthenticPixels(flop_view,exception) == MagickFalse)
1130 status=MagickFalse;
1131 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1132 {
1133 MagickBooleanType
1134 proceed;
1135
cristyb5d5f722009-11-04 03:03:49 +00001136#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00001137 #pragma omp critical (MagickCore_FlopImage)
1138#endif
1139 proceed=SetImageProgress(image,FlopImageTag,progress++,image->rows);
1140 if (proceed == MagickFalse)
1141 status=MagickFalse;
1142 }
1143 }
1144 flop_view=DestroyCacheView(flop_view);
1145 image_view=DestroyCacheView(image_view);
1146 flop_image->type=image->type;
anthony37a1b912010-12-10 12:45:59 +00001147 if (page.width != 0)
1148 page.x=(ssize_t) (page.width-flop_image->columns-page.x);
cristy74ea2cd2010-12-08 02:54:25 +00001149 flop_image->page=page;
cristy3ed852e2009-09-05 21:47:34 +00001150 if (status == MagickFalse)
1151 flop_image=DestroyImage(flop_image);
1152 return(flop_image);
1153}
1154
1155/*
1156%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1157% %
1158% %
1159% %
1160% R o l l I m a g e %
1161% %
1162% %
1163% %
1164%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1165%
1166% RollImage() offsets an image as defined by x_offset and y_offset.
1167%
1168% The format of the RollImage method is:
1169%
cristybb503372010-05-27 20:51:26 +00001170% Image *RollImage(const Image *image,const ssize_t x_offset,
1171% const ssize_t y_offset,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001172%
1173% A description of each parameter follows:
1174%
1175% o image: the image.
1176%
1177% o x_offset: the number of columns to roll in the horizontal direction.
1178%
1179% o y_offset: the number of rows to roll in the vertical direction.
1180%
1181% o exception: return any errors or warnings in this structure.
1182%
1183*/
1184
1185static inline MagickBooleanType CopyImageRegion(Image *destination,
cristybb503372010-05-27 20:51:26 +00001186 const Image *source,const size_t columns,const size_t rows,
1187 const ssize_t sx,const ssize_t sy,const ssize_t dx,const ssize_t dy,
cristy3ed852e2009-09-05 21:47:34 +00001188 ExceptionInfo *exception)
1189{
cristyc4c8d132010-01-07 01:58:38 +00001190 CacheView
1191 *source_view,
1192 *destination_view;
1193
cristy3ed852e2009-09-05 21:47:34 +00001194 MagickBooleanType
1195 status;
1196
cristy9d314ff2011-03-09 01:30:28 +00001197 ssize_t
1198 y;
1199
cristy3ed852e2009-09-05 21:47:34 +00001200 status=MagickTrue;
1201 source_view=AcquireCacheView(source);
1202 destination_view=AcquireCacheView(destination);
cristyb5d5f722009-11-04 03:03:49 +00001203#if defined(MAGICKCORE_OPENMP_SUPPORT)
1204 #pragma omp parallel for schedule(dynamic,4) shared(status)
cristy3ed852e2009-09-05 21:47:34 +00001205#endif
cristybb503372010-05-27 20:51:26 +00001206 for (y=0; y < (ssize_t) rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001207 {
1208 MagickBooleanType
1209 sync;
1210
cristy61a3d792010-03-12 14:04:54 +00001211 register const IndexPacket
1212 *restrict indexes;
1213
cristy3ed852e2009-09-05 21:47:34 +00001214 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001215 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001216
1217 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001218 *restrict destination_indexes;
cristy3ed852e2009-09-05 21:47:34 +00001219
cristy3ed852e2009-09-05 21:47:34 +00001220 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001221 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001222
1223 /*
1224 Transfer scanline.
1225 */
1226 if (status == MagickFalse)
1227 continue;
1228 p=GetCacheViewVirtualPixels(source_view,sx,sy+y,columns,1,exception);
1229 q=GetCacheViewAuthenticPixels(destination_view,dx,dy+y,columns,1,exception);
1230 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
1231 {
1232 status=MagickFalse;
1233 continue;
1234 }
cristy11b66ce2010-03-11 13:34:19 +00001235 indexes=GetCacheViewVirtualIndexQueue(source_view);
cristya3e7c382010-03-12 17:54:07 +00001236 (void) CopyMagickMemory(q,p,(size_t) columns*sizeof(*p));
cristy3ed852e2009-09-05 21:47:34 +00001237 if (indexes != (IndexPacket *) NULL)
1238 {
1239 destination_indexes=GetCacheViewAuthenticIndexQueue(destination_view);
cristya3e7c382010-03-12 17:54:07 +00001240 if (destination_indexes != (IndexPacket *) NULL)
1241 (void) CopyMagickMemory(destination_indexes,indexes,(size_t)
1242 columns*sizeof(*indexes));
cristy3ed852e2009-09-05 21:47:34 +00001243 }
1244 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
1245 if (sync == MagickFalse)
1246 status=MagickFalse;
1247 }
1248 destination_view=DestroyCacheView(destination_view);
1249 source_view=DestroyCacheView(source_view);
1250 return(status);
1251}
1252
cristybb503372010-05-27 20:51:26 +00001253MagickExport Image *RollImage(const Image *image,const ssize_t x_offset,
1254 const ssize_t y_offset,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001255{
1256#define RollImageTag "Roll/Image"
1257
1258 Image
1259 *roll_image;
1260
1261 MagickStatusType
1262 status;
1263
1264 RectangleInfo
1265 offset;
1266
1267 /*
1268 Initialize roll image attributes.
1269 */
1270 assert(image != (const Image *) NULL);
1271 assert(image->signature == MagickSignature);
1272 if (image->debug != MagickFalse)
1273 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1274 assert(exception != (ExceptionInfo *) NULL);
1275 assert(exception->signature == MagickSignature);
1276 roll_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
1277 if (roll_image == (Image *) NULL)
1278 return((Image *) NULL);
1279 offset.x=x_offset;
1280 offset.y=y_offset;
1281 while (offset.x < 0)
cristyeaedf062010-05-29 22:36:02 +00001282 offset.x+=(ssize_t) image->columns;
cristybb503372010-05-27 20:51:26 +00001283 while (offset.x >= (ssize_t) image->columns)
cristyeaedf062010-05-29 22:36:02 +00001284 offset.x-=(ssize_t) image->columns;
cristy3ed852e2009-09-05 21:47:34 +00001285 while (offset.y < 0)
cristyeaedf062010-05-29 22:36:02 +00001286 offset.y+=(ssize_t) image->rows;
cristybb503372010-05-27 20:51:26 +00001287 while (offset.y >= (ssize_t) image->rows)
cristyeaedf062010-05-29 22:36:02 +00001288 offset.y-=(ssize_t) image->rows;
cristy3ed852e2009-09-05 21:47:34 +00001289 /*
1290 Roll image.
1291 */
cristybb503372010-05-27 20:51:26 +00001292 status=CopyImageRegion(roll_image,image,(size_t) offset.x,
1293 (size_t) offset.y,(ssize_t) image->columns-offset.x,(ssize_t) image->rows-
cristy3ed852e2009-09-05 21:47:34 +00001294 offset.y,0,0,exception);
1295 (void) SetImageProgress(image,RollImageTag,0,3);
1296 status|=CopyImageRegion(roll_image,image,image->columns-offset.x,
cristybb503372010-05-27 20:51:26 +00001297 (size_t) offset.y,0,(ssize_t) image->rows-offset.y,offset.x,0,
cristy3ed852e2009-09-05 21:47:34 +00001298 exception);
1299 (void) SetImageProgress(image,RollImageTag,1,3);
cristybb503372010-05-27 20:51:26 +00001300 status|=CopyImageRegion(roll_image,image,(size_t) offset.x,image->rows-
1301 offset.y,(ssize_t) image->columns-offset.x,0,0,offset.y,exception);
cristy3ed852e2009-09-05 21:47:34 +00001302 (void) SetImageProgress(image,RollImageTag,2,3);
1303 status|=CopyImageRegion(roll_image,image,image->columns-offset.x,image->rows-
1304 offset.y,0,0,offset.x,offset.y,exception);
1305 (void) SetImageProgress(image,RollImageTag,3,3);
1306 roll_image->type=image->type;
1307 if (status == MagickFalse)
1308 roll_image=DestroyImage(roll_image);
1309 return(roll_image);
1310}
1311
1312/*
1313%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1314% %
1315% %
1316% %
1317% S h a v e I m a g e %
1318% %
1319% %
1320% %
1321%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1322%
1323% ShaveImage() shaves pixels from the image edges. It allocates the memory
1324% necessary for the new Image structure and returns a pointer to the new
1325% image.
1326%
1327% The format of the ShaveImage method is:
1328%
1329% Image *ShaveImage(const Image *image,const RectangleInfo *shave_info,
1330% ExceptionInfo *exception)
1331%
1332% A description of each parameter follows:
1333%
1334% o shave_image: Method ShaveImage returns a pointer to the shaved
1335% image. A null image is returned if there is a memory shortage or
1336% if the image width or height is zero.
1337%
1338% o image: the image.
1339%
1340% o shave_info: Specifies a pointer to a RectangleInfo which defines the
1341% region of the image to crop.
1342%
1343% o exception: return any errors or warnings in this structure.
1344%
1345*/
1346MagickExport Image *ShaveImage(const Image *image,
1347 const RectangleInfo *shave_info,ExceptionInfo *exception)
1348{
1349 Image
1350 *shave_image;
1351
1352 RectangleInfo
1353 geometry;
1354
1355 assert(image != (const Image *) NULL);
1356 assert(image->signature == MagickSignature);
1357 if (image->debug != MagickFalse)
1358 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1359 if (((2*shave_info->width) >= image->columns) ||
1360 ((2*shave_info->height) >= image->rows))
1361 ThrowImageException(OptionWarning,"GeometryDoesNotContainImage");
1362 SetGeometry(image,&geometry);
1363 geometry.width-=2*shave_info->width;
1364 geometry.height-=2*shave_info->height;
cristybb503372010-05-27 20:51:26 +00001365 geometry.x=(ssize_t) shave_info->width+image->page.x;
1366 geometry.y=(ssize_t) shave_info->height+image->page.y;
cristy3ed852e2009-09-05 21:47:34 +00001367 shave_image=CropImage(image,&geometry,exception);
1368 if (shave_image == (Image *) NULL)
1369 return((Image *) NULL);
1370 shave_image->page.width-=2*shave_info->width;
1371 shave_image->page.height-=2*shave_info->height;
cristyeaedf062010-05-29 22:36:02 +00001372 shave_image->page.x-=(ssize_t) shave_info->width;
1373 shave_image->page.y-=(ssize_t) shave_info->height;
cristy3ed852e2009-09-05 21:47:34 +00001374 return(shave_image);
1375}
1376
1377/*
1378%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1379% %
1380% %
1381% %
1382% S p l i c e I m a g e %
1383% %
1384% %
1385% %
1386%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1387%
1388% SpliceImage() splices a solid color into the image as defined by the
1389% geometry.
1390%
1391% The format of the SpliceImage method is:
1392%
1393% Image *SpliceImage(const Image *image,const RectangleInfo *geometry,
1394% ExceptionInfo *exception)
1395%
1396% A description of each parameter follows:
1397%
1398% o image: the image.
1399%
1400% o geometry: Define the region of the image to splice with members
1401% x, y, width, and height.
1402%
1403% o exception: return any errors or warnings in this structure.
1404%
1405*/
1406MagickExport Image *SpliceImage(const Image *image,
1407 const RectangleInfo *geometry,ExceptionInfo *exception)
1408{
1409#define SpliceImageTag "Splice/Image"
1410
cristyc4c8d132010-01-07 01:58:38 +00001411 CacheView
1412 *image_view,
1413 *splice_view;
1414
cristy3ed852e2009-09-05 21:47:34 +00001415 Image
1416 *splice_image;
1417
cristy3ed852e2009-09-05 21:47:34 +00001418 MagickBooleanType
cristy3ed852e2009-09-05 21:47:34 +00001419 status;
1420
cristybb503372010-05-27 20:51:26 +00001421 MagickOffsetType
1422 progress;
1423
cristy3ed852e2009-09-05 21:47:34 +00001424 RectangleInfo
1425 splice_geometry;
1426
cristybb503372010-05-27 20:51:26 +00001427 ssize_t
1428 y;
1429
cristy3ed852e2009-09-05 21:47:34 +00001430 /*
1431 Allocate splice image.
1432 */
1433 assert(image != (const Image *) NULL);
1434 assert(image->signature == MagickSignature);
1435 if (image->debug != MagickFalse)
1436 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1437 assert(geometry != (const RectangleInfo *) NULL);
1438 assert(exception != (ExceptionInfo *) NULL);
1439 assert(exception->signature == MagickSignature);
1440 splice_geometry=(*geometry);
1441 splice_image=CloneImage(image,image->columns+splice_geometry.width,
1442 image->rows+splice_geometry.height,MagickTrue,exception);
1443 if (splice_image == (Image *) NULL)
1444 return((Image *) NULL);
1445 if (SetImageStorageClass(splice_image,DirectClass) == MagickFalse)
1446 {
1447 InheritException(exception,&splice_image->exception);
1448 splice_image=DestroyImage(splice_image);
1449 return((Image *) NULL);
1450 }
1451 (void) SetImageBackgroundColor(splice_image);
1452 /*
1453 Respect image geometry.
1454 */
1455 switch (image->gravity)
1456 {
1457 default:
1458 case UndefinedGravity:
1459 case NorthWestGravity:
1460 break;
1461 case NorthGravity:
1462 {
cristyeaedf062010-05-29 22:36:02 +00001463 splice_geometry.x+=(ssize_t) splice_geometry.width/2;
cristy3ed852e2009-09-05 21:47:34 +00001464 break;
1465 }
1466 case NorthEastGravity:
1467 {
cristyeaedf062010-05-29 22:36:02 +00001468 splice_geometry.x+=(ssize_t) splice_geometry.width;
cristy3ed852e2009-09-05 21:47:34 +00001469 break;
1470 }
1471 case WestGravity:
1472 {
cristyeaedf062010-05-29 22:36:02 +00001473 splice_geometry.y+=(ssize_t) splice_geometry.width/2;
cristy3ed852e2009-09-05 21:47:34 +00001474 break;
1475 }
1476 case StaticGravity:
1477 case CenterGravity:
1478 {
cristyeaedf062010-05-29 22:36:02 +00001479 splice_geometry.x+=(ssize_t) splice_geometry.width/2;
1480 splice_geometry.y+=(ssize_t) splice_geometry.height/2;
cristy3ed852e2009-09-05 21:47:34 +00001481 break;
1482 }
1483 case EastGravity:
1484 {
cristyeaedf062010-05-29 22:36:02 +00001485 splice_geometry.x+=(ssize_t) splice_geometry.width;
1486 splice_geometry.y+=(ssize_t) splice_geometry.height/2;
cristy3ed852e2009-09-05 21:47:34 +00001487 break;
1488 }
1489 case SouthWestGravity:
1490 {
cristyeaedf062010-05-29 22:36:02 +00001491 splice_geometry.y+=(ssize_t) splice_geometry.height;
cristy3ed852e2009-09-05 21:47:34 +00001492 break;
1493 }
1494 case SouthGravity:
1495 {
cristyeaedf062010-05-29 22:36:02 +00001496 splice_geometry.x+=(ssize_t) splice_geometry.width/2;
1497 splice_geometry.y+=(ssize_t) splice_geometry.height;
cristy3ed852e2009-09-05 21:47:34 +00001498 break;
1499 }
1500 case SouthEastGravity:
1501 {
cristyeaedf062010-05-29 22:36:02 +00001502 splice_geometry.x+=(ssize_t) splice_geometry.width;
1503 splice_geometry.y+=(ssize_t) splice_geometry.height;
cristy3ed852e2009-09-05 21:47:34 +00001504 break;
1505 }
1506 }
1507 /*
1508 Splice image.
1509 */
1510 status=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00001511 progress=0;
1512 image_view=AcquireCacheView(image);
1513 splice_view=AcquireCacheView(splice_image);
cristyb5d5f722009-11-04 03:03:49 +00001514#if defined(MAGICKCORE_OPENMP_SUPPORT)
1515 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00001516#endif
cristybb503372010-05-27 20:51:26 +00001517 for (y=0; y < (ssize_t) splice_geometry.y; y++)
cristy3ed852e2009-09-05 21:47:34 +00001518 {
1519 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001520 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001521
1522 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001523 *restrict indexes,
1524 *restrict splice_indexes;
cristy3ed852e2009-09-05 21:47:34 +00001525
cristybb503372010-05-27 20:51:26 +00001526 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001527 x;
1528
1529 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001530 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001531
1532 if (status == MagickFalse)
1533 continue;
1534 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1535 q=QueueCacheViewAuthenticPixels(splice_view,0,y,splice_image->columns,1,
1536 exception);
1537 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
1538 {
1539 status=MagickFalse;
1540 continue;
1541 }
1542 indexes=GetCacheViewAuthenticIndexQueue(image_view);
1543 splice_indexes=GetCacheViewAuthenticIndexQueue(splice_view);
1544 for (x=0; x < splice_geometry.x; x++)
1545 {
cristyce70c172010-01-07 17:15:30 +00001546 SetRedPixelComponent(q,GetRedPixelComponent(p));
1547 SetGreenPixelComponent(q,GetGreenPixelComponent(p));
1548 SetBluePixelComponent(q,GetBluePixelComponent(p));
1549 SetOpacityPixelComponent(q,OpaqueOpacity);
cristy3ed852e2009-09-05 21:47:34 +00001550 if (image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00001551 SetOpacityPixelComponent(q,GetOpacityPixelComponent(p));
cristy3ed852e2009-09-05 21:47:34 +00001552 if (image->colorspace == CMYKColorspace)
1553 splice_indexes[x]=(*indexes++);
1554 p++;
1555 q++;
1556 }
cristybb503372010-05-27 20:51:26 +00001557 for ( ; x < (ssize_t) (splice_geometry.x+splice_geometry.width); x++)
cristy3ed852e2009-09-05 21:47:34 +00001558 q++;
cristybb503372010-05-27 20:51:26 +00001559 for ( ; x < (ssize_t) splice_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001560 {
cristyce70c172010-01-07 17:15:30 +00001561 SetRedPixelComponent(q,GetRedPixelComponent(p));
1562 SetGreenPixelComponent(q,GetGreenPixelComponent(p));
1563 SetBluePixelComponent(q,GetBluePixelComponent(p));
1564 SetOpacityPixelComponent(q,OpaqueOpacity);
cristy3ed852e2009-09-05 21:47:34 +00001565 if (image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00001566 SetOpacityPixelComponent(q,GetOpacityPixelComponent(p));
cristy3ed852e2009-09-05 21:47:34 +00001567 if (image->colorspace == CMYKColorspace)
1568 splice_indexes[x]=(*indexes++);
1569 p++;
1570 q++;
1571 }
1572 if (SyncCacheViewAuthenticPixels(splice_view,exception) == MagickFalse)
1573 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00001574 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1575 {
1576 MagickBooleanType
1577 proceed;
1578
cristyb5d5f722009-11-04 03:03:49 +00001579#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00001580 #pragma omp critical (MagickCore_TransposeImage)
1581#endif
1582 proceed=SetImageProgress(image,SpliceImageTag,progress++,
1583 splice_image->rows);
1584 if (proceed == MagickFalse)
1585 status=MagickFalse;
1586 }
1587 }
cristyb5d5f722009-11-04 03:03:49 +00001588#if defined(MAGICKCORE_OPENMP_SUPPORT)
1589 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00001590#endif
cristybb503372010-05-27 20:51:26 +00001591 for (y=(ssize_t) (splice_geometry.y+splice_geometry.height);
1592 y < (ssize_t) splice_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001593 {
1594 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001595 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001596
1597 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001598 *restrict indexes,
1599 *restrict splice_indexes;
cristy3ed852e2009-09-05 21:47:34 +00001600
cristybb503372010-05-27 20:51:26 +00001601 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001602 x;
1603
1604 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001605 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001606
1607 if (status == MagickFalse)
1608 continue;
cristyeaedf062010-05-29 22:36:02 +00001609 p=GetCacheViewVirtualPixels(image_view,0,y-(ssize_t) splice_geometry.height,
cristy3ed852e2009-09-05 21:47:34 +00001610 image->columns,1,exception);
cristy13d07042010-11-21 20:56:18 +00001611 if ((y < 0) || (y >= (ssize_t) splice_image->rows))
cristy2224dcd2010-11-15 00:49:30 +00001612 continue;
cristy3ed852e2009-09-05 21:47:34 +00001613 q=QueueCacheViewAuthenticPixels(splice_view,0,y,splice_image->columns,1,
1614 exception);
1615 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
1616 {
1617 status=MagickFalse;
1618 continue;
1619 }
1620 indexes=GetCacheViewAuthenticIndexQueue(image_view);
1621 splice_indexes=GetCacheViewAuthenticIndexQueue(splice_view);
1622 for (x=0; x < splice_geometry.x; x++)
1623 {
cristyce70c172010-01-07 17:15:30 +00001624 SetRedPixelComponent(q,GetRedPixelComponent(p));
1625 SetGreenPixelComponent(q,GetGreenPixelComponent(p));
1626 SetBluePixelComponent(q,GetBluePixelComponent(p));
1627 SetOpacityPixelComponent(q,OpaqueOpacity);
cristy3ed852e2009-09-05 21:47:34 +00001628 if (image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00001629 SetOpacityPixelComponent(q,GetOpacityPixelComponent(p));
cristy3ed852e2009-09-05 21:47:34 +00001630 if (image->colorspace == CMYKColorspace)
1631 splice_indexes[x]=(*indexes++);
1632 p++;
1633 q++;
1634 }
cristybb503372010-05-27 20:51:26 +00001635 for ( ; x < (ssize_t) (splice_geometry.x+splice_geometry.width); x++)
cristy3ed852e2009-09-05 21:47:34 +00001636 q++;
cristybb503372010-05-27 20:51:26 +00001637 for ( ; x < (ssize_t) splice_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001638 {
cristyce70c172010-01-07 17:15:30 +00001639 SetRedPixelComponent(q,GetRedPixelComponent(p));
1640 SetGreenPixelComponent(q,GetGreenPixelComponent(p));
1641 SetBluePixelComponent(q,GetBluePixelComponent(p));
1642 SetOpacityPixelComponent(q,OpaqueOpacity);
cristy3ed852e2009-09-05 21:47:34 +00001643 if (image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00001644 SetOpacityPixelComponent(q,GetOpacityPixelComponent(p));
cristy3ed852e2009-09-05 21:47:34 +00001645 if (image->colorspace == CMYKColorspace)
1646 splice_indexes[x]=(*indexes++);
1647 p++;
1648 q++;
1649 }
1650 if (SyncCacheViewAuthenticPixels(splice_view,exception) == MagickFalse)
1651 status=MagickFalse;
1652 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1653 {
1654 MagickBooleanType
1655 proceed;
1656
cristyb5d5f722009-11-04 03:03:49 +00001657#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00001658 #pragma omp critical (MagickCore_TransposeImage)
1659#endif
1660 proceed=SetImageProgress(image,SpliceImageTag,progress++,
1661 splice_image->rows);
1662 if (proceed == MagickFalse)
1663 status=MagickFalse;
1664 }
1665 }
1666 splice_view=DestroyCacheView(splice_view);
1667 image_view=DestroyCacheView(image_view);
1668 if (status == MagickFalse)
1669 splice_image=DestroyImage(splice_image);
1670 return(splice_image);
1671}
1672
1673/*
1674%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1675% %
1676% %
1677% %
1678% T r a n s f o r m I m a g e %
1679% %
1680% %
1681% %
1682%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1683%
1684% TransformImage() is a convenience method that behaves like ResizeImage() or
1685% CropImage() but accepts scaling and/or cropping information as a region
1686% geometry specification. If the operation fails, the original image handle
1687% is returned.
1688%
1689% The format of the TransformImage method is:
1690%
1691% MagickBooleanType TransformImage(Image **image,const char *crop_geometry,
1692% const char *image_geometry)
1693%
1694% A description of each parameter follows:
1695%
1696% o image: the image The transformed image is returned as this parameter.
1697%
1698% o crop_geometry: A crop geometry string. This geometry defines a
1699% subregion of the image to crop.
1700%
1701% o image_geometry: An image geometry string. This geometry defines the
1702% final size of the image.
1703%
1704*/
cristy4d727152011-02-10 19:57:21 +00001705
cristybb503372010-05-27 20:51:26 +00001706static inline ssize_t MagickRound(MagickRealType x)
anthonyc0429ed2009-12-27 08:22:42 +00001707{
cristy508d9312010-02-10 21:10:30 +00001708 /*
1709 Round the fraction to nearest integer.
1710 */
anthonyc0429ed2009-12-27 08:22:42 +00001711 if (x >= 0.0)
cristybb503372010-05-27 20:51:26 +00001712 return((ssize_t) (x+0.5));
1713 return((ssize_t) (x-0.5));
anthonyc0429ed2009-12-27 08:22:42 +00001714}
1715
cristy3ed852e2009-09-05 21:47:34 +00001716MagickExport MagickBooleanType TransformImage(Image **image,
1717 const char *crop_geometry,const char *image_geometry)
1718{
1719 Image
cristy508d9312010-02-10 21:10:30 +00001720 *next,
cristy3ed852e2009-09-05 21:47:34 +00001721 *resize_image,
1722 *transform_image;
1723
1724 MagickStatusType
1725 flags;
1726
1727 RectangleInfo
1728 geometry;
1729
cristybb503372010-05-27 20:51:26 +00001730 size_t
cristy508d9312010-02-10 21:10:30 +00001731 height,
1732 width;
1733
cristy9d314ff2011-03-09 01:30:28 +00001734 ssize_t
1735 x,
1736 y;
1737
cristy3ed852e2009-09-05 21:47:34 +00001738 assert(image != (Image **) NULL);
1739 assert((*image)->signature == MagickSignature);
1740 if ((*image)->debug != MagickFalse)
1741 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
1742 transform_image=(*image);
1743 if (crop_geometry != (const char *) NULL)
1744 {
1745 Image
1746 *crop_image;
1747
1748 RectangleInfo
1749 geometry;
1750
1751 /*
1752 Crop image to a user specified size.
1753 */
1754 crop_image=NewImageList();
1755 flags=ParseGravityGeometry(transform_image,crop_geometry,&geometry,
1756 &(*image)->exception);
cristy508d9312010-02-10 21:10:30 +00001757 if ((flags & AreaValue) != 0)
anthonyc0429ed2009-12-27 08:22:42 +00001758 {
cristy508d9312010-02-10 21:10:30 +00001759 PointInfo
cristy4eced732010-02-10 22:26:28 +00001760 delta,
1761 offset;
anthonyc0429ed2009-12-27 08:22:42 +00001762
1763 RectangleInfo
1764 crop;
1765
cristy508d9312010-02-10 21:10:30 +00001766 /*
1767 Crop into NxM tiles (@ flag) - AT.
1768 */
1769 if (geometry.width == 0)
1770 geometry.width=1;
1771 if (geometry.height == 0)
1772 geometry.height=1;
1773 width=transform_image->columns;
1774 height=transform_image->rows;
anthonyc0429ed2009-12-27 08:22:42 +00001775 if ((flags & AspectValue) == 0)
1776 {
cristy508d9312010-02-10 21:10:30 +00001777 width-=(geometry.x < 0 ? -1 : 1)*geometry.x;
1778 height-=(geometry.y < 0 ? -1 : 1)*geometry.y;
anthonyc0429ed2009-12-27 08:22:42 +00001779 }
1780 else
1781 {
cristy508d9312010-02-10 21:10:30 +00001782 width+=(geometry.x < 0 ? -1 : 1)*geometry.x;
1783 height+=(geometry.y < 0 ? -1 : 1)*geometry.y;
anthonyc0429ed2009-12-27 08:22:42 +00001784 }
cristy508d9312010-02-10 21:10:30 +00001785 delta.x=(double) width/geometry.width;
1786 delta.y=(double) height/geometry.height;
anthonyc0429ed2009-12-27 08:22:42 +00001787 next=NewImageList();
cristy4eced732010-02-10 22:26:28 +00001788 for (offset.y=0; offset.y < (double) height; )
anthonyc0429ed2009-12-27 08:22:42 +00001789 {
1790 if ((flags & AspectValue) == 0)
1791 {
cristybb503372010-05-27 20:51:26 +00001792 crop.y=(ssize_t) MagickRound((MagickRealType) (offset.y-
cristy508d9312010-02-10 21:10:30 +00001793 (geometry.y > 0 ? 0 : geometry.y)));
cristy4eced732010-02-10 22:26:28 +00001794 offset.y+=delta.y;
cristybb503372010-05-27 20:51:26 +00001795 crop.height=(size_t) MagickRound((MagickRealType)
cristy4eced732010-02-10 22:26:28 +00001796 (offset.y+(geometry.y < 0 ? 0 : geometry.y)));
anthonyc0429ed2009-12-27 08:22:42 +00001797 }
1798 else
1799 {
cristybb503372010-05-27 20:51:26 +00001800 crop.y=(ssize_t) MagickRound((MagickRealType) (offset.y-
cristy508d9312010-02-10 21:10:30 +00001801 (geometry.y > 0 ? geometry.y : 0)));
cristy4eced732010-02-10 22:26:28 +00001802 offset.y+=delta.y;
cristybb503372010-05-27 20:51:26 +00001803 crop.height=(size_t) MagickRound((MagickRealType)
cristy4eced732010-02-10 22:26:28 +00001804 (offset.y+(geometry.y < 0 ? geometry.y : 0)));
anthonyc0429ed2009-12-27 08:22:42 +00001805 }
cristy508d9312010-02-10 21:10:30 +00001806 crop.height-=crop.y;
cristy3d818612010-04-01 18:56:37 +00001807 crop.y+=transform_image->page.y;
cristy4eced732010-02-10 22:26:28 +00001808 for (offset.x=0; offset.x < (double) width; )
anthonyc0429ed2009-12-27 08:22:42 +00001809 {
1810 if ((flags & AspectValue) == 0)
1811 {
cristybb503372010-05-27 20:51:26 +00001812 crop.x=(ssize_t) MagickRound((MagickRealType) (offset.x-
cristy508d9312010-02-10 21:10:30 +00001813 (geometry.x > 0 ? 0 : geometry.x)));
cristy4eced732010-02-10 22:26:28 +00001814 offset.x+=+delta.x;
cristybb503372010-05-27 20:51:26 +00001815 crop.width=(size_t) MagickRound((MagickRealType)
cristy4eced732010-02-10 22:26:28 +00001816 (offset.x+(geometry.x < 0 ? 0 : geometry.x)));
anthonyc0429ed2009-12-27 08:22:42 +00001817 }
1818 else
1819 {
cristybb503372010-05-27 20:51:26 +00001820 crop.x=(ssize_t) MagickRound((MagickRealType) (offset.x-
cristy508d9312010-02-10 21:10:30 +00001821 (geometry.x > 0 ? geometry.x : 0)));
cristy4eced732010-02-10 22:26:28 +00001822 offset.x+=+delta.x;
cristybb503372010-05-27 20:51:26 +00001823 crop.width=(size_t) MagickRound((MagickRealType)
cristy4eced732010-02-10 22:26:28 +00001824 (offset.x+(geometry.x < 0 ? geometry.x : 0)));
anthonyc0429ed2009-12-27 08:22:42 +00001825 }
cristy508d9312010-02-10 21:10:30 +00001826 crop.width-=crop.x;
cristy3d818612010-04-01 18:56:37 +00001827 crop.x+=transform_image->page.x;
anthonyc0429ed2009-12-27 08:22:42 +00001828 next=CropImage(transform_image,&crop,&(*image)->exception);
1829 if (next == (Image *) NULL)
1830 break;
1831 AppendImageToList(&crop_image,next);
1832 }
1833 if (next == (Image *) NULL)
1834 break;
1835 }
1836 }
cristy508d9312010-02-10 21:10:30 +00001837 else
1838 if (((geometry.width == 0) && (geometry.height == 0)) ||
anthonyc0429ed2009-12-27 08:22:42 +00001839 ((flags & XValue) != 0) || ((flags & YValue) != 0))
cristy3ed852e2009-09-05 21:47:34 +00001840 {
cristy508d9312010-02-10 21:10:30 +00001841 /*
1842 Crop a single region at +X+Y.
1843 */
1844 crop_image=CropImage(transform_image,&geometry,
1845 &(*image)->exception);
1846 if ((crop_image != (Image *) NULL) && ((flags & AspectValue) != 0))
1847 {
1848 crop_image->page.width=geometry.width;
1849 crop_image->page.height=geometry.height;
1850 crop_image->page.x-=geometry.x;
1851 crop_image->page.y-=geometry.y;
1852 }
1853 }
1854 else
1855 if ((transform_image->columns > geometry.width) ||
1856 (transform_image->rows > geometry.height))
1857 {
1858 MagickBooleanType
1859 proceed;
1860
1861 MagickProgressMonitor
1862 progress_monitor;
1863
1864 MagickOffsetType
1865 i;
1866
1867 MagickSizeType
1868 number_images;
1869
cristyc2b1fb82010-10-25 13:01:28 +00001870 RectangleInfo
1871 page;
1872
cristy508d9312010-02-10 21:10:30 +00001873 /*
1874 Crop into tiles of fixed size WxH.
1875 */
1876 if (transform_image->page.width == 0)
1877 transform_image->page.width=transform_image->columns;
1878 if (transform_image->page.height == 0)
1879 transform_image->page.height=transform_image->rows;
1880 width=geometry.width;
1881 if (width == 0)
1882 width=transform_image->page.width;
1883 height=geometry.height;
1884 if (height == 0)
1885 height=transform_image->page.height;
1886 next=NewImageList();
1887 proceed=MagickTrue;
1888 i=0;
cristy0e9f9c12010-02-11 03:00:47 +00001889 number_images=0;
cristyc2b1fb82010-10-25 13:01:28 +00001890 page=transform_image->page;
1891 for (y=0; y < (ssize_t) page.height; y+=(ssize_t) height)
1892 for (x=0; x < (ssize_t) page.width; x+=(ssize_t) width)
cristy0e9f9c12010-02-11 03:00:47 +00001893 number_images++;
cristyc2b1fb82010-10-25 13:01:28 +00001894 for (y=0; y < (ssize_t) page.height; y+=(ssize_t) height)
cristy508d9312010-02-10 21:10:30 +00001895 {
cristyc2b1fb82010-10-25 13:01:28 +00001896 for (x=0; x < (ssize_t) page.width; x+=(ssize_t) width)
cristy508d9312010-02-10 21:10:30 +00001897 {
1898 progress_monitor=SetImageProgressMonitor(transform_image,
1899 (MagickProgressMonitor) NULL,transform_image->client_data);
1900 geometry.width=width;
1901 geometry.height=height;
1902 geometry.x=x;
1903 geometry.y=y;
1904 next=CropImage(transform_image,&geometry,&(*image)->exception);
1905 (void) SetImageProgressMonitor(transform_image,
1906 progress_monitor,transform_image->client_data);
1907 proceed=SetImageProgress(transform_image,CropImageTag,i++,
1908 number_images);
1909 if (proceed == MagickFalse)
1910 break;
1911 if (next == (Image *) NULL)
1912 break;
cristy36507e62010-02-11 00:26:48 +00001913 (void) SetImageProgressMonitor(next,progress_monitor,
1914 next->client_data);
cristy2a9e7cb2010-03-05 02:15:05 +00001915 if (crop_image == (Image *) NULL)
1916 crop_image=next;
1917 else
1918 {
1919 next->previous=crop_image;
1920 crop_image->next=next;
1921 crop_image=crop_image->next;
1922 }
cristy508d9312010-02-10 21:10:30 +00001923 }
1924 if (next == (Image *) NULL)
1925 break;
1926 if (proceed == MagickFalse)
1927 break;
1928 }
1929 }
cristy3ed852e2009-09-05 21:47:34 +00001930 if (crop_image == (Image *) NULL)
1931 transform_image=CloneImage(*image,0,0,MagickTrue,&(*image)->exception);
1932 else
1933 {
1934 transform_image=DestroyImage(transform_image);
1935 transform_image=GetFirstImageInList(crop_image);
1936 }
1937 *image=transform_image;
1938 }
1939 if (image_geometry == (const char *) NULL)
1940 return(MagickTrue);
1941 /*
1942 Scale image to a user specified size.
1943 */
1944 flags=ParseRegionGeometry(transform_image,image_geometry,&geometry,
1945 &(*image)->exception);
1946 if ((transform_image->columns == geometry.width) &&
1947 (transform_image->rows == geometry.height))
1948 return(MagickTrue);
cristy15b98cd2010-09-12 19:42:50 +00001949 resize_image=ResizeImage(transform_image,geometry.width,geometry.height,
1950 transform_image->filter,transform_image->blur,&(*image)->exception);
cristy3ed852e2009-09-05 21:47:34 +00001951 if (resize_image == (Image *) NULL)
1952 return(MagickFalse);
1953 transform_image=DestroyImage(transform_image);
1954 transform_image=resize_image;
1955 *image=transform_image;
1956 return(MagickTrue);
1957}
1958
1959/*
1960%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1961% %
1962% %
1963% %
1964% T r a n s f o r m I m a g e s %
1965% %
1966% %
anthonybdaa5b32010-12-10 13:06:46 +00001967% %
1968%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
cristy3ed852e2009-09-05 21:47:34 +00001969%
1970% TransformImages() calls TransformImage() on each image of a sequence.
1971%
1972% The format of the TransformImage method is:
1973%
1974% MagickBooleanType TransformImages(Image **image,
1975% const char *crop_geometry,const char *image_geometry)
1976%
1977% A description of each parameter follows:
1978%
1979% o image: the image The transformed image is returned as this parameter.
1980%
1981% o crop_geometry: A crop geometry string. This geometry defines a
1982% subregion of the image to crop.
1983%
1984% o image_geometry: An image geometry string. This geometry defines the
1985% final size of the image.
1986%
1987*/
1988MagickExport MagickBooleanType TransformImages(Image **images,
1989 const char *crop_geometry,const char *image_geometry)
1990{
1991 Image
1992 *image,
1993 **image_list,
1994 *transform_images;
1995
1996 MagickStatusType
1997 status;
1998
cristybb503372010-05-27 20:51:26 +00001999 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002000 i;
2001
2002 assert(images != (Image **) NULL);
2003 assert((*images)->signature == MagickSignature);
2004 if ((*images)->debug != MagickFalse)
2005 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
2006 (*images)->filename);
2007 image_list=ImageListToArray(*images,&(*images)->exception);
2008 if (image_list == (Image **) NULL)
2009 return(MagickFalse);
2010 status=MagickTrue;
2011 transform_images=NewImageList();
2012 for (i=0; image_list[i] != (Image *) NULL; i++)
2013 {
2014 image=image_list[i];
2015 status|=TransformImage(&image,crop_geometry,image_geometry);
2016 AppendImageToList(&transform_images,image);
2017 }
2018 *images=transform_images;
2019 image_list=(Image **) RelinquishMagickMemory(image_list);
2020 return(status != 0 ? MagickTrue : MagickFalse);
2021}
2022
2023/*
2024%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2025% %
2026% %
2027% %
2028% T r a n s p o s e I m a g e %
2029% %
2030% %
2031% %
2032%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2033%
2034% TransposeImage() creates a horizontal mirror image by reflecting the pixels
2035% around the central y-axis while rotating them by 90 degrees.
2036%
2037% The format of the TransposeImage method is:
2038%
2039% Image *TransposeImage(const Image *image,ExceptionInfo *exception)
2040%
2041% A description of each parameter follows:
2042%
2043% o image: the image.
2044%
2045% o exception: return any errors or warnings in this structure.
2046%
2047*/
2048MagickExport Image *TransposeImage(const Image *image,ExceptionInfo *exception)
2049{
2050#define TransposeImageTag "Transpose/Image"
2051
cristyc4c8d132010-01-07 01:58:38 +00002052 CacheView
2053 *image_view,
2054 *transpose_view;
2055
cristy3ed852e2009-09-05 21:47:34 +00002056 Image
2057 *transpose_image;
2058
cristy3ed852e2009-09-05 21:47:34 +00002059 MagickBooleanType
2060 status;
2061
cristybb503372010-05-27 20:51:26 +00002062 MagickOffsetType
2063 progress;
2064
cristy3ed852e2009-09-05 21:47:34 +00002065 RectangleInfo
2066 page;
2067
cristybb503372010-05-27 20:51:26 +00002068 ssize_t
2069 y;
2070
cristy3ed852e2009-09-05 21:47:34 +00002071 assert(image != (const Image *) NULL);
2072 assert(image->signature == MagickSignature);
2073 if (image->debug != MagickFalse)
2074 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2075 assert(exception != (ExceptionInfo *) NULL);
2076 assert(exception->signature == MagickSignature);
2077 transpose_image=CloneImage(image,image->rows,image->columns,MagickTrue,
2078 exception);
2079 if (transpose_image == (Image *) NULL)
2080 return((Image *) NULL);
2081 /*
2082 Transpose image.
2083 */
2084 status=MagickTrue;
2085 progress=0;
2086 image_view=AcquireCacheView(image);
2087 transpose_view=AcquireCacheView(transpose_image);
cristyb5d5f722009-11-04 03:03:49 +00002088#if defined(MAGICKCORE_OPENMP_SUPPORT)
2089 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00002090#endif
cristybb503372010-05-27 20:51:26 +00002091 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002092 {
2093 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002094 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002095
2096 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002097 *restrict transpose_indexes,
2098 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002099
2100 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002101 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002102
2103 if (status == MagickFalse)
2104 continue;
cristybb503372010-05-27 20:51:26 +00002105 p=GetCacheViewVirtualPixels(image_view,0,(ssize_t) image->rows-y-1,
cristy3ed852e2009-09-05 21:47:34 +00002106 image->columns,1,exception);
cristy9af9b5d2010-08-15 17:04:28 +00002107 q=QueueCacheViewAuthenticPixels(transpose_view,(ssize_t) (image->rows-y-1),
2108 0,1,transpose_image->rows,exception);
cristy3ed852e2009-09-05 21:47:34 +00002109 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2110 {
2111 status=MagickFalse;
2112 continue;
2113 }
2114 (void) CopyMagickMemory(q,p,(size_t) image->columns*sizeof(*q));
2115 indexes=GetCacheViewAuthenticIndexQueue(image_view);
2116 if (indexes != (IndexPacket *) NULL)
2117 {
2118 transpose_indexes=GetCacheViewAuthenticIndexQueue(transpose_view);
2119 if (transpose_indexes != (IndexPacket *) NULL)
2120 (void) CopyMagickMemory(transpose_indexes,indexes,(size_t)
2121 image->columns*sizeof(*transpose_indexes));
2122 }
2123 if (SyncCacheViewAuthenticPixels(transpose_view,exception) == MagickFalse)
2124 status=MagickFalse;
2125 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2126 {
2127 MagickBooleanType
2128 proceed;
2129
cristyb5d5f722009-11-04 03:03:49 +00002130#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002131 #pragma omp critical (MagickCore_TransposeImage)
2132#endif
2133 proceed=SetImageProgress(image,TransposeImageTag,progress++,
2134 image->rows);
2135 if (proceed == MagickFalse)
2136 status=MagickFalse;
2137 }
2138 }
2139 transpose_view=DestroyCacheView(transpose_view);
2140 image_view=DestroyCacheView(image_view);
2141 transpose_image->type=image->type;
2142 page=transpose_image->page;
2143 Swap(page.width,page.height);
2144 Swap(page.x,page.y);
cristy3ed852e2009-09-05 21:47:34 +00002145 transpose_image->page=page;
2146 if (status == MagickFalse)
2147 transpose_image=DestroyImage(transpose_image);
2148 return(transpose_image);
2149}
2150
2151/*
2152%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2153% %
2154% %
2155% %
2156% T r a n s v e r s e I m a g e %
2157% %
2158% %
2159% %
2160%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2161%
2162% TransverseImage() creates a vertical mirror image by reflecting the pixels
2163% around the central x-axis while rotating them by 270 degrees.
2164%
2165% The format of the TransverseImage method is:
2166%
2167% Image *TransverseImage(const Image *image,ExceptionInfo *exception)
2168%
2169% A description of each parameter follows:
2170%
2171% o image: the image.
2172%
2173% o exception: return any errors or warnings in this structure.
2174%
2175*/
2176MagickExport Image *TransverseImage(const Image *image,ExceptionInfo *exception)
2177{
2178#define TransverseImageTag "Transverse/Image"
2179
cristyc4c8d132010-01-07 01:58:38 +00002180 CacheView
2181 *image_view,
2182 *transverse_view;
2183
cristy3ed852e2009-09-05 21:47:34 +00002184 Image
2185 *transverse_image;
2186
cristy3ed852e2009-09-05 21:47:34 +00002187 MagickBooleanType
2188 status;
2189
cristybb503372010-05-27 20:51:26 +00002190 MagickOffsetType
2191 progress;
2192
cristy3ed852e2009-09-05 21:47:34 +00002193 RectangleInfo
2194 page;
2195
cristybb503372010-05-27 20:51:26 +00002196 ssize_t
2197 y;
2198
cristy3ed852e2009-09-05 21:47:34 +00002199 assert(image != (const Image *) NULL);
2200 assert(image->signature == MagickSignature);
2201 if (image->debug != MagickFalse)
2202 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2203 assert(exception != (ExceptionInfo *) NULL);
2204 assert(exception->signature == MagickSignature);
2205 transverse_image=CloneImage(image,image->rows,image->columns,MagickTrue,
2206 exception);
2207 if (transverse_image == (Image *) NULL)
2208 return((Image *) NULL);
2209 /*
2210 Transverse image.
2211 */
2212 status=MagickTrue;
2213 progress=0;
2214 image_view=AcquireCacheView(image);
2215 transverse_view=AcquireCacheView(transverse_image);
cristyb5d5f722009-11-04 03:03:49 +00002216#if defined(MAGICKCORE_OPENMP_SUPPORT)
2217 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00002218#endif
cristybb503372010-05-27 20:51:26 +00002219 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002220 {
2221 MagickBooleanType
2222 sync;
2223
2224 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002225 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002226
2227 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002228 *restrict transverse_indexes,
2229 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002230
cristybb503372010-05-27 20:51:26 +00002231 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002232 x;
2233
2234 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002235 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002236
2237 if (status == MagickFalse)
2238 continue;
2239 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
cristybb503372010-05-27 20:51:26 +00002240 q=QueueCacheViewAuthenticPixels(transverse_view,(ssize_t) (image->rows-y-
cristy3ed852e2009-09-05 21:47:34 +00002241 1),0,1,transverse_image->rows,exception);
2242 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2243 {
2244 status=MagickFalse;
2245 continue;
2246 }
2247 q+=image->columns;
cristybb503372010-05-27 20:51:26 +00002248 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002249 *--q=(*p++);
2250 indexes=GetCacheViewAuthenticIndexQueue(image_view);
2251 if (indexes != (IndexPacket *) NULL)
2252 {
2253 transverse_indexes=GetCacheViewAuthenticIndexQueue(transverse_view);
2254 if (transverse_indexes != (IndexPacket *) NULL)
cristybb503372010-05-27 20:51:26 +00002255 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002256 transverse_indexes[image->columns-x-1]=indexes[x];
2257 }
2258 sync=SyncCacheViewAuthenticPixels(transverse_view,exception);
2259 if (sync == MagickFalse)
2260 status=MagickFalse;
2261 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2262 {
2263 MagickBooleanType
2264 proceed;
2265
cristyb5d5f722009-11-04 03:03:49 +00002266#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002267 #pragma omp critical (MagickCore_TransverseImage)
2268#endif
2269 proceed=SetImageProgress(image,TransverseImageTag,progress++,
2270 image->rows);
2271 if (proceed == MagickFalse)
2272 status=MagickFalse;
2273 }
2274 }
2275 transverse_view=DestroyCacheView(transverse_view);
2276 image_view=DestroyCacheView(image_view);
2277 transverse_image->type=image->type;
2278 page=transverse_image->page;
2279 Swap(page.width,page.height);
2280 Swap(page.x,page.y);
anthonybdaa5b32010-12-10 13:06:46 +00002281 if (page.width != 0)
2282 page.x=(ssize_t) (page.width-transverse_image->columns-page.x);
cristy3ed852e2009-09-05 21:47:34 +00002283 if (page.height != 0)
cristybb503372010-05-27 20:51:26 +00002284 page.y=(ssize_t) (page.height-transverse_image->rows-page.y);
cristy3ed852e2009-09-05 21:47:34 +00002285 transverse_image->page=page;
2286 if (status == MagickFalse)
2287 transverse_image=DestroyImage(transverse_image);
2288 return(transverse_image);
2289}
2290
2291/*
2292%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2293% %
2294% %
2295% %
2296% T r i m I m a g e %
2297% %
2298% %
2299% %
2300%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2301%
2302% TrimImage() trims pixels from the image edges. It allocates the memory
2303% necessary for the new Image structure and returns a pointer to the new
2304% image.
2305%
2306% The format of the TrimImage method is:
2307%
2308% Image *TrimImage(const Image *image,ExceptionInfo *exception)
2309%
2310% A description of each parameter follows:
2311%
2312% o image: the image.
2313%
2314% o exception: return any errors or warnings in this structure.
2315%
2316*/
2317MagickExport Image *TrimImage(const Image *image,ExceptionInfo *exception)
2318{
2319 RectangleInfo
2320 geometry;
2321
2322 assert(image != (const Image *) NULL);
2323 assert(image->signature == MagickSignature);
2324 if (image->debug != MagickFalse)
2325 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2326 geometry=GetImageBoundingBox(image,exception);
2327 if ((geometry.width == 0) || (geometry.height == 0))
2328 {
2329 Image
2330 *crop_image;
2331
2332 crop_image=CloneImage(image,1,1,MagickTrue,exception);
2333 if (crop_image == (Image *) NULL)
2334 return((Image *) NULL);
2335 crop_image->background_color.opacity=(Quantum) TransparentOpacity;
2336 (void) SetImageBackgroundColor(crop_image);
2337 crop_image->page=image->page;
2338 crop_image->page.x=(-1);
2339 crop_image->page.y=(-1);
2340 return(crop_image);
2341 }
2342 geometry.x+=image->page.x;
2343 geometry.y+=image->page.y;
2344 return(CropImage(image,&geometry,exception));
2345}