blob: 86ec542760342c16546df80a2a7d5e3c64fe4fdf [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% %
cristy16af1cb2009-12-11 21:38:29 +000020% Copyright 1999-2010 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"
66#include "magick/transform.h"
67
68/*
69%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
70% %
71% %
72% %
73% C h o p I m a g e %
74% %
75% %
76% %
77%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
78%
cristy00f95372010-02-13 16:39:29 +000079% ChopImage() removes a region of an image and collapses the image to occupy
80% the removed portion.
cristy3ed852e2009-09-05 21:47:34 +000081%
82% The format of the ChopImage method is:
83%
84% Image *ChopImage(const Image *image,const RectangleInfo *chop_info)
85% ExceptionInfo *exception)
86%
87% A description of each parameter follows:
88%
89% o image: the image.
90%
91% o chop_info: Define the region of the image to chop.
92%
93% o exception: return any errors or warnings in this structure.
94%
95*/
96MagickExport Image *ChopImage(const Image *image,const RectangleInfo *chop_info,
97 ExceptionInfo *exception)
98{
99#define ChopImageTag "Chop/Image"
100
cristyc4c8d132010-01-07 01:58:38 +0000101 CacheView
102 *chop_view,
103 *image_view;
104
cristy3ed852e2009-09-05 21:47:34 +0000105 Image
106 *chop_image;
107
cristybb503372010-05-27 20:51:26 +0000108 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000109 j,
110 y;
111
112 MagickBooleanType
cristy00f95372010-02-13 16:39:29 +0000113 proceed,
114 status;
cristy3ed852e2009-09-05 21:47:34 +0000115
116 RectangleInfo
117 extent;
118
cristybb503372010-05-27 20:51:26 +0000119 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000120 i;
121
cristy3ed852e2009-09-05 21:47:34 +0000122 /*
123 Check chop geometry.
124 */
125 assert(image != (const Image *) NULL);
126 assert(image->signature == MagickSignature);
127 if (image->debug != MagickFalse)
128 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
129 assert(exception != (ExceptionInfo *) NULL);
130 assert(exception->signature == MagickSignature);
131 assert(chop_info != (RectangleInfo *) NULL);
cristybb503372010-05-27 20:51:26 +0000132 if (((chop_info->x+(ssize_t) chop_info->width) < 0) ||
133 ((chop_info->y+(ssize_t) chop_info->height) < 0) ||
134 (chop_info->x > (ssize_t) image->columns) ||
135 (chop_info->y > (ssize_t) image->rows))
cristy3ed852e2009-09-05 21:47:34 +0000136 ThrowImageException(OptionWarning,"GeometryDoesNotContainImage");
137 extent=(*chop_info);
cristybb503372010-05-27 20:51:26 +0000138 if ((extent.x+(ssize_t) extent.width) > (ssize_t) image->columns)
139 extent.width=(size_t) ((ssize_t) image->columns-extent.x);
140 if ((extent.y+(ssize_t) extent.height) > (ssize_t) image->rows)
141 extent.height=(size_t) ((ssize_t) image->rows-extent.y);
cristy3ed852e2009-09-05 21:47:34 +0000142 if (extent.x < 0)
143 {
cristybb503372010-05-27 20:51:26 +0000144 extent.width-=(size_t) (-extent.x);
cristy3ed852e2009-09-05 21:47:34 +0000145 extent.x=0;
146 }
147 if (extent.y < 0)
148 {
cristybb503372010-05-27 20:51:26 +0000149 extent.height-=(size_t) (-extent.y);
cristy3ed852e2009-09-05 21:47:34 +0000150 extent.y=0;
151 }
152 chop_image=CloneImage(image,image->columns-extent.width,image->rows-
153 extent.height,MagickTrue,exception);
154 if (chop_image == (Image *) NULL)
155 return((Image *) NULL);
156 /*
157 Extract chop image.
158 */
cristy00f95372010-02-13 16:39:29 +0000159 status=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +0000160 i=0;
161 j=0;
162 image_view=AcquireCacheView(image);
163 chop_view=AcquireCacheView(chop_image);
cristybb503372010-05-27 20:51:26 +0000164 for (y=0; y < (ssize_t) extent.y; y++)
cristy3ed852e2009-09-05 21:47:34 +0000165 {
166 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000167 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000168
169 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000170 *restrict chop_indexes,
171 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +0000172
cristybb503372010-05-27 20:51:26 +0000173 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000174 x;
175
176 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000177 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000178
cristy00f95372010-02-13 16:39:29 +0000179 if (status == MagickFalse)
180 continue;
cristy3ed852e2009-09-05 21:47:34 +0000181 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,exception);
182 q=QueueCacheViewAuthenticPixels(chop_view,0,j++,chop_image->columns,1,
183 exception);
184 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
cristy00f95372010-02-13 16:39:29 +0000185 {
186 status=MagickFalse;
187 continue;
188 }
cristy3ed852e2009-09-05 21:47:34 +0000189 indexes=GetCacheViewAuthenticIndexQueue(image_view);
190 chop_indexes=GetCacheViewAuthenticIndexQueue(chop_view);
cristybb503372010-05-27 20:51:26 +0000191 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000192 {
cristybb503372010-05-27 20:51:26 +0000193 if ((x < extent.x) || (x >= (ssize_t) (extent.x+extent.width)))
cristy3ed852e2009-09-05 21:47:34 +0000194 {
195 *q=(*p);
196 if (indexes != (IndexPacket *) NULL)
197 {
198 if (chop_indexes != (IndexPacket *) NULL)
199 *chop_indexes++=indexes[x];
200 }
201 q++;
202 }
203 p++;
204 }
205 if (SyncCacheViewAuthenticPixels(chop_view,exception) == MagickFalse)
cristy00f95372010-02-13 16:39:29 +0000206 status=MagickFalse;
cristybb503372010-05-27 20:51:26 +0000207 proceed=SetImageProgress(image,ChopImageTag,(MagickOffsetType) y,
208 chop_image->rows);
cristy3ed852e2009-09-05 21:47:34 +0000209 if (proceed == MagickFalse)
cristy00f95372010-02-13 16:39:29 +0000210 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +0000211 }
212 /*
213 Extract chop image.
214 */
215 i+=extent.height;
cristybb503372010-05-27 20:51:26 +0000216 for (y=0; y < (ssize_t) (image->rows-(extent.y+extent.height)); y++)
cristy3ed852e2009-09-05 21:47:34 +0000217 {
218 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000219 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000220
221 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000222 *restrict chop_indexes,
223 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +0000224
cristybb503372010-05-27 20:51:26 +0000225 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000226 x;
227
228 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000229 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000230
cristy00f95372010-02-13 16:39:29 +0000231 if (status == MagickFalse)
232 continue;
cristy3ed852e2009-09-05 21:47:34 +0000233 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,exception);
234 q=QueueCacheViewAuthenticPixels(chop_view,0,j++,chop_image->columns,1,
235 exception);
236 if ((p == (PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
cristy00f95372010-02-13 16:39:29 +0000237 {
238 status=MagickFalse;
239 continue;
240 }
cristy3ed852e2009-09-05 21:47:34 +0000241 indexes=GetCacheViewAuthenticIndexQueue(image_view);
242 chop_indexes=GetCacheViewAuthenticIndexQueue(chop_view);
cristybb503372010-05-27 20:51:26 +0000243 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000244 {
cristybb503372010-05-27 20:51:26 +0000245 if ((x < extent.x) || (x >= (ssize_t) (extent.x+extent.width)))
cristy3ed852e2009-09-05 21:47:34 +0000246 {
247 *q=(*p);
248 if (indexes != (IndexPacket *) NULL)
249 {
250 if (chop_indexes != (IndexPacket *) NULL)
251 *chop_indexes++=indexes[x];
252 }
253 q++;
254 }
255 p++;
256 }
257 if (SyncCacheViewAuthenticPixels(chop_view,exception) == MagickFalse)
cristy00f95372010-02-13 16:39:29 +0000258 status=MagickFalse;
cristybb503372010-05-27 20:51:26 +0000259 proceed=SetImageProgress(image,ChopImageTag,(MagickOffsetType) y,
260 chop_image->rows);
cristy3ed852e2009-09-05 21:47:34 +0000261 if (proceed == MagickFalse)
cristy00f95372010-02-13 16:39:29 +0000262 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +0000263 }
264 chop_view=DestroyCacheView(chop_view);
265 image_view=DestroyCacheView(image_view);
266 chop_image->type=image->type;
267 return(chop_image);
268}
269
270/*
271%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
272% %
273% %
274% %
275+ C o n s o l i d a t e C M Y K I m a g e %
276% %
277% %
278% %
279%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
280%
281% ConsolidateCMYKImage() consolidates separate C, M, Y, and K planes into a
282% single image.
283%
284% The format of the ConsolidateCMYKImage method is:
285%
286% Image *ConsolidateCMYKImage(const Image *image,ExceptionInfo *exception)
287%
288% A description of each parameter follows:
289%
290% o image: the image sequence.
291%
292% o exception: return any errors or warnings in this structure.
293%
294*/
295MagickExport Image *ConsolidateCMYKImages(const Image *images,
296 ExceptionInfo *exception)
297{
298 Image
299 *cmyk_image,
300 *cmyk_images;
301
cristybb503372010-05-27 20:51:26 +0000302 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000303 y;
304
cristybb503372010-05-27 20:51:26 +0000305 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000306 i;
307
308 /*
309 Consolidate separate C, M, Y, and K planes into a single image.
310 */
311 assert(images != (Image *) NULL);
312 assert(images->signature == MagickSignature);
313 if (images->debug != MagickFalse)
314 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
315 assert(exception != (ExceptionInfo *) NULL);
316 assert(exception->signature == MagickSignature);
317 cmyk_images=NewImageList();
cristybb503372010-05-27 20:51:26 +0000318 for (i=0; i < (ssize_t) GetImageListLength(images); i+=4)
cristy3ed852e2009-09-05 21:47:34 +0000319 {
320 cmyk_image=CloneImage(images,images->columns,images->rows,MagickTrue,
321 exception);
322 if (cmyk_image == (Image *) NULL)
323 break;
324 if (SetImageStorageClass(cmyk_image,DirectClass) == MagickFalse)
325 break;
326 (void) SetImageColorspace(cmyk_image,CMYKColorspace);
cristybb503372010-05-27 20:51:26 +0000327 for (y=0; y < (ssize_t) images->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000328 {
329 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000330 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000331
cristybb503372010-05-27 20:51:26 +0000332 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000333 x;
334
335 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000336 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000337
338 p=GetVirtualPixels(images,0,y,images->columns,1,exception);
339 q=QueueAuthenticPixels(cmyk_image,0,y,cmyk_image->columns,1,exception);
340 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
341 break;
cristybb503372010-05-27 20:51:26 +0000342 for (x=0; x < (ssize_t) images->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000343 {
344 q->red=(Quantum) (QuantumRange-PixelIntensityToQuantum(p));
345 p++;
346 q++;
347 }
348 if (SyncAuthenticPixels(cmyk_image,exception) == MagickFalse)
349 break;
350 }
351 images=GetNextImageInList(images);
352 if (images == (Image *) NULL)
353 break;
cristybb503372010-05-27 20:51:26 +0000354 for (y=0; y < (ssize_t) images->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000355 {
356 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000357 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000358
cristybb503372010-05-27 20:51:26 +0000359 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000360 x;
361
362 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000363 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000364
365 p=GetVirtualPixels(images,0,y,images->columns,1,exception);
366 q=GetAuthenticPixels(cmyk_image,0,y,cmyk_image->columns,1,exception);
367 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->green=(Quantum) (QuantumRange-PixelIntensityToQuantum(p));
372 p++;
373 q++;
374 }
375 if (SyncAuthenticPixels(cmyk_image,exception) == MagickFalse)
376 break;
377 }
378 images=GetNextImageInList(images);
379 if (images == (Image *) NULL)
380 break;
cristybb503372010-05-27 20:51:26 +0000381 for (y=0; y < (ssize_t) images->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000382 {
383 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000384 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000385
cristybb503372010-05-27 20:51:26 +0000386 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000387 x;
388
389 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000390 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000391
392 p=GetVirtualPixels(images,0,y,images->columns,1,exception);
393 q=GetAuthenticPixels(cmyk_image,0,y,cmyk_image->columns,1,exception);
394 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
395 break;
cristybb503372010-05-27 20:51:26 +0000396 for (x=0; x < (ssize_t) images->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000397 {
398 q->blue=(Quantum) (QuantumRange-PixelIntensityToQuantum(p));
399 p++;
400 q++;
401 }
402 if (SyncAuthenticPixels(cmyk_image,exception) == MagickFalse)
403 break;
404 }
405 images=GetNextImageInList(images);
406 if (images == (Image *) NULL)
407 break;
cristybb503372010-05-27 20:51:26 +0000408 for (y=0; y < (ssize_t) images->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000409 {
410 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000411 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000412
413 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000414 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +0000415
cristybb503372010-05-27 20:51:26 +0000416 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000417 x;
418
419 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000420 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000421
422 p=GetVirtualPixels(images,0,y,images->columns,1,exception);
423 q=GetAuthenticPixels(cmyk_image,0,y,cmyk_image->columns,1,exception);
424 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
425 break;
426 indexes=GetAuthenticIndexQueue(cmyk_image);
cristybb503372010-05-27 20:51:26 +0000427 for (x=0; x < (ssize_t) images->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000428 {
429 indexes[x]=(IndexPacket) (QuantumRange-PixelIntensityToQuantum(p));
430 p++;
431 }
432 if (SyncAuthenticPixels(cmyk_image,exception) == MagickFalse)
433 break;
434 }
435 AppendImageToList(&cmyk_images,cmyk_image);
436 images=GetNextImageInList(images);
437 if (images == (Image *) NULL)
438 break;
439 }
440 return(cmyk_images);
441}
442
443/*
444%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
445% %
446% %
447% %
448% C r o p I m a g e %
449% %
450% %
451% %
452%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
453%
454% CropImage() extracts a region of the image starting at the offset defined
455% by geometry.
456%
457% The format of the CropImage method is:
458%
459% Image *CropImage(const Image *image,const RectangleInfo *geometry,
460% ExceptionInfo *exception)
461%
462% A description of each parameter follows:
463%
464% o image: the image.
465%
466% o geometry: Define the region of the image to crop with members
467% x, y, width, and height.
468%
469% o exception: return any errors or warnings in this structure.
470%
471*/
472MagickExport Image *CropImage(const Image *image,const RectangleInfo *geometry,
473 ExceptionInfo *exception)
474{
475#define CropImageTag "Crop/Image"
476
cristyc4c8d132010-01-07 01:58:38 +0000477 CacheView
478 *crop_view,
479 *image_view;
480
cristy3ed852e2009-09-05 21:47:34 +0000481 Image
482 *crop_image;
483
cristy3ed852e2009-09-05 21:47:34 +0000484 MagickBooleanType
485 status;
486
cristybb503372010-05-27 20:51:26 +0000487 MagickOffsetType
488 progress;
489
cristy3ed852e2009-09-05 21:47:34 +0000490 RectangleInfo
491 bounding_box,
492 page;
493
cristybb503372010-05-27 20:51:26 +0000494 ssize_t
495 y;
496
cristy3ed852e2009-09-05 21:47:34 +0000497 /*
498 Check crop geometry.
499 */
500 assert(image != (const Image *) NULL);
501 assert(image->signature == MagickSignature);
502 if (image->debug != MagickFalse)
503 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
504 assert(geometry != (const RectangleInfo *) NULL);
505 assert(exception != (ExceptionInfo *) NULL);
506 assert(exception->signature == MagickSignature);
507 bounding_box=image->page;
508 if ((bounding_box.width == 0) || (bounding_box.height == 0))
509 {
510 bounding_box.width=image->columns;
511 bounding_box.height=image->rows;
512 }
513 page=(*geometry);
514 if (page.width == 0)
515 page.width=bounding_box.width;
516 if (page.height == 0)
517 page.height=bounding_box.height;
cristybb503372010-05-27 20:51:26 +0000518 if (((bounding_box.x-page.x) >= (ssize_t) page.width) ||
519 ((bounding_box.y-page.y) >= (ssize_t) page.height) ||
520 ((page.x-bounding_box.x) > (ssize_t) image->columns) ||
521 ((page.y-bounding_box.y) > (ssize_t) image->rows))
cristy3ed852e2009-09-05 21:47:34 +0000522 {
523 /*
524 Crop is not within virtual canvas, return 1 pixel transparent image.
525 */
526 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
527 "GeometryDoesNotContainImage","`%s'",image->filename);
528 crop_image=CloneImage(image,1,1,MagickTrue,exception);
529 if (crop_image == (Image *) NULL)
530 return((Image *) NULL);
531 crop_image->background_color.opacity=(Quantum) TransparentOpacity;
532 (void) SetImageBackgroundColor(crop_image);
533 crop_image->page=bounding_box;
534 crop_image->page.x=(-1);
535 crop_image->page.y=(-1);
536 if (crop_image->dispose == BackgroundDispose)
537 crop_image->dispose=NoneDispose;
538 return(crop_image);
539 }
540 if ((page.x < 0) && (bounding_box.x >= 0))
541 {
542 page.width+=page.x-bounding_box.x;
543 page.x=0;
544 }
545 else
546 {
547 page.width-=bounding_box.x-page.x;
548 page.x-=bounding_box.x;
549 if (page.x < 0)
550 page.x=0;
551 }
552 if ((page.y < 0) && (bounding_box.y >= 0))
553 {
554 page.height+=page.y-bounding_box.y;
555 page.y=0;
556 }
557 else
558 {
559 page.height-=bounding_box.y-page.y;
560 page.y-=bounding_box.y;
561 if (page.y < 0)
562 page.y=0;
563 }
cristybb503372010-05-27 20:51:26 +0000564 if ((size_t) (page.x+page.width) > image->columns)
cristy3ed852e2009-09-05 21:47:34 +0000565 page.width=image->columns-page.x;
cristy1e4aa462010-02-15 00:04:18 +0000566 if ((geometry->width != 0) && (page.width > geometry->width))
567 page.width=geometry->width;
cristybb503372010-05-27 20:51:26 +0000568 if ((size_t) (page.y+page.height) > image->rows)
cristy3ed852e2009-09-05 21:47:34 +0000569 page.height=image->rows-page.y;
cristy1e4aa462010-02-15 00:04:18 +0000570 if ((geometry->height != 0) && (page.height > geometry->height))
571 page.height=geometry->height;
cristy3ed852e2009-09-05 21:47:34 +0000572 bounding_box.x+=page.x;
573 bounding_box.y+=page.y;
574 if ((page.width == 0) || (page.height == 0))
575 {
576 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
577 "GeometryDoesNotContainImage","`%s'",image->filename);
578 return((Image *) NULL);
579 }
580 /*
581 Initialize crop image attributes.
582 */
583 crop_image=CloneImage(image,page.width,page.height,MagickTrue,exception);
584 if (crop_image == (Image *) NULL)
585 return((Image *) NULL);
586 crop_image->page.width=image->page.width;
587 crop_image->page.height=image->page.height;
cristybb503372010-05-27 20:51:26 +0000588 if (((ssize_t) (bounding_box.x+bounding_box.width) > (ssize_t) image->page.width) ||
589 ((ssize_t) (bounding_box.y+bounding_box.height) > (ssize_t) image->page.height))
cristy3ed852e2009-09-05 21:47:34 +0000590 {
591 crop_image->page.width=bounding_box.width;
592 crop_image->page.height=bounding_box.height;
593 }
594 crop_image->page.x=bounding_box.x;
595 crop_image->page.y=bounding_box.y;
596 /*
597 Crop image.
598 */
599 status=MagickTrue;
600 progress=0;
601 image_view=AcquireCacheView(image);
602 crop_view=AcquireCacheView(crop_image);
cristyd3dd0502010-04-26 00:57:20 +0000603#if defined(MAGICKCORE_OPENMP_SUPPORT) && defined(MAGICKCORE_FUTURE)
cristy8a7ea362010-03-10 20:31:43 +0000604 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000605#endif
cristybb503372010-05-27 20:51:26 +0000606 for (y=0; y < (ssize_t) crop_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000607 {
608 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000609 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +0000610
611 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000612 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000613
614 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000615 *restrict crop_indexes;
cristy3ed852e2009-09-05 21:47:34 +0000616
617 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000618 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000619
620 if (status == MagickFalse)
621 continue;
622 p=GetCacheViewVirtualPixels(image_view,page.x,page.y+y,crop_image->columns,
623 1,exception);
624 q=QueueCacheViewAuthenticPixels(crop_view,0,y,crop_image->columns,1,
625 exception);
626 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
627 {
628 status=MagickFalse;
629 continue;
630 }
cristy3ed852e2009-09-05 21:47:34 +0000631 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristy6c207342010-02-19 17:49:50 +0000632 crop_indexes=GetCacheViewAuthenticIndexQueue(crop_view);
633 (void) CopyMagickMemory(q,p,(size_t) crop_image->columns*sizeof(*q));
634 if ((indexes != (IndexPacket *) NULL) &&
635 (crop_indexes != (IndexPacket *) NULL))
636 (void) CopyMagickMemory(crop_indexes,indexes,(size_t) crop_image->columns*
637 sizeof(*crop_indexes));
cristy3ed852e2009-09-05 21:47:34 +0000638 if (SyncCacheViewAuthenticPixels(crop_view,exception) == MagickFalse)
639 status=MagickFalse;
640 if (image->progress_monitor != (MagickProgressMonitor) NULL)
641 {
642 MagickBooleanType
643 proceed;
644
cristyd3dd0502010-04-26 00:57:20 +0000645#if defined(MAGICKCORE_OPENMP_SUPPORT) && defined(MAGICKCORE_FUTURE)
cristy3ed852e2009-09-05 21:47:34 +0000646 #pragma omp critical (MagickCore_CropImage)
647#endif
648 proceed=SetImageProgress(image,CropImageTag,progress++,image->rows);
649 if (proceed == MagickFalse)
650 status=MagickFalse;
651 }
652 }
653 crop_view=DestroyCacheView(crop_view);
654 image_view=DestroyCacheView(image_view);
655 crop_image->type=image->type;
656 if (status == MagickFalse)
657 crop_image=DestroyImage(crop_image);
658 return(crop_image);
659}
660
661/*
662%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
663% %
664% %
665% %
666% E x c e r p t I m a g e %
667% %
668% %
669% %
670%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
671%
672% ExcerptImage() returns a excerpt of the image as defined by the geometry.
673%
674% The format of the ExcerptImage method is:
675%
676% Image *ExcerptImage(const Image *image,const RectangleInfo *geometry,
677% ExceptionInfo *exception)
678%
679% A description of each parameter follows:
680%
681% o image: the image.
682%
683% o geometry: Define the region of the image to extend with members
684% x, y, width, and height.
685%
686% o exception: return any errors or warnings in this structure.
687%
688*/
689MagickExport Image *ExcerptImage(const Image *image,
690 const RectangleInfo *geometry,ExceptionInfo *exception)
691{
692#define ExcerptImageTag "Excerpt/Image"
693
cristyc4c8d132010-01-07 01:58:38 +0000694 CacheView
695 *excerpt_view,
696 *image_view;
697
cristy3ed852e2009-09-05 21:47:34 +0000698 Image
699 *excerpt_image;
700
cristy3ed852e2009-09-05 21:47:34 +0000701 MagickBooleanType
702 status;
703
cristybb503372010-05-27 20:51:26 +0000704 MagickOffsetType
705 progress;
706
707 ssize_t
708 y;
709
cristy3ed852e2009-09-05 21:47:34 +0000710 /*
711 Allocate excerpt image.
712 */
713 assert(image != (const Image *) NULL);
714 assert(image->signature == MagickSignature);
715 if (image->debug != MagickFalse)
716 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
717 assert(geometry != (const RectangleInfo *) NULL);
718 assert(exception != (ExceptionInfo *) NULL);
719 assert(exception->signature == MagickSignature);
720 excerpt_image=CloneImage(image,geometry->width,geometry->height,MagickTrue,
721 exception);
722 if (excerpt_image == (Image *) NULL)
723 return((Image *) NULL);
724 /*
725 Excerpt each row.
726 */
727 status=MagickTrue;
728 progress=0;
729 image_view=AcquireCacheView(image);
730 excerpt_view=AcquireCacheView(excerpt_image);
cristyb5d5f722009-11-04 03:03:49 +0000731#if defined(MAGICKCORE_OPENMP_SUPPORT)
732 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000733#endif
cristybb503372010-05-27 20:51:26 +0000734 for (y=0; y < (ssize_t) excerpt_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000735 {
736 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000737 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000738
739 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000740 *restrict excerpt_indexes,
741 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +0000742
743 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000744 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000745
746 if (status == MagickFalse)
747 continue;
748 p=GetCacheViewVirtualPixels(image_view,geometry->x,geometry->y+y,
749 geometry->width,1,exception);
750 q=GetCacheViewAuthenticPixels(excerpt_view,0,y,excerpt_image->columns,1,
751 exception);
752 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
753 {
754 status=MagickFalse;
755 continue;
756 }
757 (void) CopyMagickMemory(q,p,(size_t) excerpt_image->columns*sizeof(*q));
758 indexes=GetCacheViewAuthenticIndexQueue(image_view);
759 if (indexes != (IndexPacket *) NULL)
760 {
761 excerpt_indexes=GetCacheViewAuthenticIndexQueue(excerpt_view);
762 if (excerpt_indexes != (IndexPacket *) NULL)
763 (void) CopyMagickMemory(excerpt_indexes,indexes,(size_t)
764 excerpt_image->columns*sizeof(*excerpt_indexes));
765 }
766 if (SyncCacheViewAuthenticPixels(excerpt_view,exception) == MagickFalse)
767 status=MagickFalse;
768 if (image->progress_monitor != (MagickProgressMonitor) NULL)
769 {
770 MagickBooleanType
771 proceed;
772
cristyb5d5f722009-11-04 03:03:49 +0000773#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +0000774 #pragma omp critical (MagickCore_ExcerptImage)
775#endif
776 proceed=SetImageProgress(image,ExcerptImageTag,progress++,image->rows);
777 if (proceed == MagickFalse)
778 status=MagickFalse;
779 }
780 }
781 excerpt_view=DestroyCacheView(excerpt_view);
782 image_view=DestroyCacheView(image_view);
783 excerpt_image->type=image->type;
784 if (status == MagickFalse)
785 excerpt_image=DestroyImage(excerpt_image);
786 return(excerpt_image);
787}
788
789/*
790%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
791% %
792% %
793% %
794% E x t e n t I m a g e %
795% %
796% %
797% %
798%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
799%
800% ExtentImage() extends the image as defined by the geometry, gravity, and
801% image background color. Set the (x,y) offset of the geometry to move the
802% original image relative to the extended image.
803%
804% The format of the ExtentImage method is:
805%
806% Image *ExtentImage(const Image *image,const RectangleInfo *geometry,
807% ExceptionInfo *exception)
808%
809% A description of each parameter follows:
810%
811% o image: the image.
812%
813% o geometry: Define the region of the image to extend with members
814% x, y, width, and height.
815%
816% o exception: return any errors or warnings in this structure.
817%
818*/
819MagickExport Image *ExtentImage(const Image *image,
820 const RectangleInfo *geometry,ExceptionInfo *exception)
821{
822 Image
823 *extent_image;
824
825 /*
826 Allocate extent image.
827 */
828 assert(image != (const Image *) NULL);
829 assert(image->signature == MagickSignature);
830 if (image->debug != MagickFalse)
831 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
832 assert(geometry != (const RectangleInfo *) NULL);
833 assert(exception != (ExceptionInfo *) NULL);
834 assert(exception->signature == MagickSignature);
835 extent_image=CloneImage(image,geometry->width,geometry->height,MagickTrue,
836 exception);
837 if (extent_image == (Image *) NULL)
838 return((Image *) NULL);
839 if (SetImageStorageClass(extent_image,DirectClass) == MagickFalse)
840 {
841 InheritException(exception,&extent_image->exception);
842 extent_image=DestroyImage(extent_image);
843 return((Image *) NULL);
844 }
845 if (extent_image->background_color.opacity != OpaqueOpacity)
846 extent_image->matte=MagickTrue;
847 (void) SetImageBackgroundColor(extent_image);
848 (void) CompositeImage(extent_image,image->compose,image,geometry->x,
849 geometry->y);
850 return(extent_image);
851}
852
853/*
854%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
855% %
856% %
857% %
858% F l i p I m a g e %
859% %
860% %
861% %
862%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
863%
864% FlipImage() creates a vertical mirror image by reflecting the pixels
865% around the central x-axis.
866%
867% The format of the FlipImage method is:
868%
869% Image *FlipImage(const Image *image,ExceptionInfo *exception)
870%
871% A description of each parameter follows:
872%
873% o image: the image.
874%
875% o exception: return any errors or warnings in this structure.
876%
877*/
878MagickExport Image *FlipImage(const Image *image,ExceptionInfo *exception)
879{
880#define FlipImageTag "Flip/Image"
881
cristyc4c8d132010-01-07 01:58:38 +0000882 CacheView
883 *flip_view,
884 *image_view;
885
cristy3ed852e2009-09-05 21:47:34 +0000886 Image
887 *flip_image;
888
cristy3ed852e2009-09-05 21:47:34 +0000889 MagickBooleanType
890 status;
891
cristybb503372010-05-27 20:51:26 +0000892 MagickOffsetType
893 progress;
894
895 ssize_t
896 y;
897
cristy3ed852e2009-09-05 21:47:34 +0000898 assert(image != (const Image *) NULL);
899 assert(image->signature == MagickSignature);
900 if (image->debug != MagickFalse)
901 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
902 assert(exception != (ExceptionInfo *) NULL);
903 assert(exception->signature == MagickSignature);
904 flip_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
905 if (flip_image == (Image *) NULL)
906 return((Image *) NULL);
907 /*
908 Flip image.
909 */
910 status=MagickTrue;
911 progress=0;
912 image_view=AcquireCacheView(image);
913 flip_view=AcquireCacheView(flip_image);
cristyd3dd0502010-04-26 00:57:20 +0000914#if defined(MAGICKCORE_OPENMP_SUPPORT) && defined(MAGICKCORE_FUTURE)
cristy8a7ea362010-03-10 20:31:43 +0000915 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000916#endif
cristybb503372010-05-27 20:51:26 +0000917 for (y=0; y < (ssize_t) flip_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000918 {
919 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000920 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +0000921
922 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000923 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000924
925 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000926 *restrict flip_indexes;
cristy3ed852e2009-09-05 21:47:34 +0000927
928 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000929 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000930
931 if (status == MagickFalse)
932 continue;
933 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
cristybb503372010-05-27 20:51:26 +0000934 q=QueueCacheViewAuthenticPixels(flip_view,0,(ssize_t) (flip_image->rows-y-1),
cristy3ed852e2009-09-05 21:47:34 +0000935 flip_image->columns,1,exception);
936 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
937 {
938 status=MagickFalse;
939 continue;
940 }
941 (void) CopyMagickMemory(q,p,(size_t) image->columns*sizeof(*q));
942 indexes=GetCacheViewVirtualIndexQueue(image_view);
943 if (indexes != (const IndexPacket *) NULL)
944 {
945 flip_indexes=GetCacheViewAuthenticIndexQueue(flip_view);
946 if (flip_indexes != (IndexPacket *) NULL)
947 (void) CopyMagickMemory(flip_indexes,indexes,(size_t) image->columns*
948 sizeof(*flip_indexes));
949 }
950 if (SyncCacheViewAuthenticPixels(flip_view,exception) == MagickFalse)
951 status=MagickFalse;
952 if (image->progress_monitor != (MagickProgressMonitor) NULL)
953 {
954 MagickBooleanType
955 proceed;
956
cristyd3dd0502010-04-26 00:57:20 +0000957#if defined(MAGICKCORE_OPENMP_SUPPORT) && defined(MAGICKCORE_FUTURE)
cristy3ed852e2009-09-05 21:47:34 +0000958 #pragma omp critical (MagickCore_FlipImage)
959#endif
960 proceed=SetImageProgress(image,FlipImageTag,progress++,image->rows);
961 if (proceed == MagickFalse)
962 status=MagickFalse;
963 }
964 }
965 flip_view=DestroyCacheView(flip_view);
966 image_view=DestroyCacheView(image_view);
967 flip_image->type=image->type;
968 if (status == MagickFalse)
969 flip_image=DestroyImage(flip_image);
970 return(flip_image);
971}
972
973/*
974%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
975% %
976% %
977% %
978% F l o p I m a g e %
979% %
980% %
981% %
982%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
983%
984% FlopImage() creates a horizontal mirror image by reflecting the pixels
985% around the central y-axis.
986%
987% The format of the FlopImage method is:
988%
989% Image *FlopImage(const Image *image,ExceptionInfo *exception)
990%
991% A description of each parameter follows:
992%
993% o image: the image.
994%
995% o exception: return any errors or warnings in this structure.
996%
997*/
998MagickExport Image *FlopImage(const Image *image,ExceptionInfo *exception)
999{
1000#define FlopImageTag "Flop/Image"
1001
cristyc4c8d132010-01-07 01:58:38 +00001002 CacheView
1003 *flop_view,
1004 *image_view;
1005
cristy3ed852e2009-09-05 21:47:34 +00001006 Image
1007 *flop_image;
1008
cristy3ed852e2009-09-05 21:47:34 +00001009 MagickBooleanType
1010 status;
1011
cristybb503372010-05-27 20:51:26 +00001012 MagickOffsetType
1013 progress;
1014
1015 ssize_t
1016 y;
1017
cristy3ed852e2009-09-05 21:47:34 +00001018 assert(image != (const Image *) NULL);
1019 assert(image->signature == MagickSignature);
1020 if (image->debug != MagickFalse)
1021 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1022 assert(exception != (ExceptionInfo *) NULL);
1023 assert(exception->signature == MagickSignature);
1024 flop_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
1025 if (flop_image == (Image *) NULL)
1026 return((Image *) NULL);
1027 /*
1028 Flop each row.
1029 */
1030 status=MagickTrue;
1031 progress=0;
1032 image_view=AcquireCacheView(image);
1033 flop_view=AcquireCacheView(flop_image);
cristyd3dd0502010-04-26 00:57:20 +00001034#if defined(MAGICKCORE_OPENMP_SUPPORT) && defined(MAGICKCORE_FUTURE)
cristy8a7ea362010-03-10 20:31:43 +00001035 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00001036#endif
cristybb503372010-05-27 20:51:26 +00001037 for (y=0; y < (ssize_t) flop_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001038 {
1039 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001040 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00001041
1042 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001043 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001044
1045 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001046 *restrict flop_indexes;
cristy3ed852e2009-09-05 21:47:34 +00001047
cristybb503372010-05-27 20:51:26 +00001048 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001049 x;
1050
1051 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001052 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001053
1054 if (status == MagickFalse)
1055 continue;
1056 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1057 q=QueueCacheViewAuthenticPixels(flop_view,0,y,flop_image->columns,1,
1058 exception);
1059 if ((p == (PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
1060 {
1061 status=MagickFalse;
1062 continue;
1063 }
1064 q+=flop_image->columns;
1065 indexes=GetCacheViewVirtualIndexQueue(image_view);
1066 flop_indexes=GetCacheViewAuthenticIndexQueue(flop_view);
cristybb503372010-05-27 20:51:26 +00001067 for (x=0; x < (ssize_t) flop_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001068 {
1069 (*--q)=(*p++);
1070 if ((indexes != (const IndexPacket *) NULL) &&
1071 (flop_indexes != (IndexPacket *) NULL))
1072 flop_indexes[flop_image->columns-x-1]=indexes[x];
1073 }
1074 if (SyncCacheViewAuthenticPixels(flop_view,exception) == MagickFalse)
1075 status=MagickFalse;
1076 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1077 {
1078 MagickBooleanType
1079 proceed;
1080
cristyb5d5f722009-11-04 03:03:49 +00001081#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00001082 #pragma omp critical (MagickCore_FlopImage)
1083#endif
1084 proceed=SetImageProgress(image,FlopImageTag,progress++,image->rows);
1085 if (proceed == MagickFalse)
1086 status=MagickFalse;
1087 }
1088 }
1089 flop_view=DestroyCacheView(flop_view);
1090 image_view=DestroyCacheView(image_view);
1091 flop_image->type=image->type;
1092 if (status == MagickFalse)
1093 flop_image=DestroyImage(flop_image);
1094 return(flop_image);
1095}
1096
1097/*
1098%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1099% %
1100% %
1101% %
1102% R o l l I m a g e %
1103% %
1104% %
1105% %
1106%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1107%
1108% RollImage() offsets an image as defined by x_offset and y_offset.
1109%
1110% The format of the RollImage method is:
1111%
cristybb503372010-05-27 20:51:26 +00001112% Image *RollImage(const Image *image,const ssize_t x_offset,
1113% const ssize_t y_offset,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001114%
1115% A description of each parameter follows:
1116%
1117% o image: the image.
1118%
1119% o x_offset: the number of columns to roll in the horizontal direction.
1120%
1121% o y_offset: the number of rows to roll in the vertical direction.
1122%
1123% o exception: return any errors or warnings in this structure.
1124%
1125*/
1126
1127static inline MagickBooleanType CopyImageRegion(Image *destination,
cristybb503372010-05-27 20:51:26 +00001128 const Image *source,const size_t columns,const size_t rows,
1129 const ssize_t sx,const ssize_t sy,const ssize_t dx,const ssize_t dy,
cristy3ed852e2009-09-05 21:47:34 +00001130 ExceptionInfo *exception)
1131{
cristyc4c8d132010-01-07 01:58:38 +00001132 CacheView
1133 *source_view,
1134 *destination_view;
1135
cristybb503372010-05-27 20:51:26 +00001136 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001137 y;
1138
1139 MagickBooleanType
1140 status;
1141
cristy3ed852e2009-09-05 21:47:34 +00001142 status=MagickTrue;
1143 source_view=AcquireCacheView(source);
1144 destination_view=AcquireCacheView(destination);
cristyb5d5f722009-11-04 03:03:49 +00001145#if defined(MAGICKCORE_OPENMP_SUPPORT)
1146 #pragma omp parallel for schedule(dynamic,4) shared(status)
cristy3ed852e2009-09-05 21:47:34 +00001147#endif
cristybb503372010-05-27 20:51:26 +00001148 for (y=0; y < (ssize_t) rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001149 {
1150 MagickBooleanType
1151 sync;
1152
cristy61a3d792010-03-12 14:04:54 +00001153 register const IndexPacket
1154 *restrict indexes;
1155
cristy3ed852e2009-09-05 21:47:34 +00001156 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001157 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001158
1159 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001160 *restrict destination_indexes;
cristy3ed852e2009-09-05 21:47:34 +00001161
cristy3ed852e2009-09-05 21:47:34 +00001162 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001163 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001164
1165 /*
1166 Transfer scanline.
1167 */
1168 if (status == MagickFalse)
1169 continue;
1170 p=GetCacheViewVirtualPixels(source_view,sx,sy+y,columns,1,exception);
1171 q=GetCacheViewAuthenticPixels(destination_view,dx,dy+y,columns,1,exception);
1172 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
1173 {
1174 status=MagickFalse;
1175 continue;
1176 }
cristy11b66ce2010-03-11 13:34:19 +00001177 indexes=GetCacheViewVirtualIndexQueue(source_view);
cristya3e7c382010-03-12 17:54:07 +00001178 (void) CopyMagickMemory(q,p,(size_t) columns*sizeof(*p));
cristy3ed852e2009-09-05 21:47:34 +00001179 if (indexes != (IndexPacket *) NULL)
1180 {
1181 destination_indexes=GetCacheViewAuthenticIndexQueue(destination_view);
cristya3e7c382010-03-12 17:54:07 +00001182 if (destination_indexes != (IndexPacket *) NULL)
1183 (void) CopyMagickMemory(destination_indexes,indexes,(size_t)
1184 columns*sizeof(*indexes));
cristy3ed852e2009-09-05 21:47:34 +00001185 }
1186 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
1187 if (sync == MagickFalse)
1188 status=MagickFalse;
1189 }
1190 destination_view=DestroyCacheView(destination_view);
1191 source_view=DestroyCacheView(source_view);
1192 return(status);
1193}
1194
cristybb503372010-05-27 20:51:26 +00001195MagickExport Image *RollImage(const Image *image,const ssize_t x_offset,
1196 const ssize_t y_offset,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001197{
1198#define RollImageTag "Roll/Image"
1199
1200 Image
1201 *roll_image;
1202
1203 MagickStatusType
1204 status;
1205
1206 RectangleInfo
1207 offset;
1208
1209 /*
1210 Initialize roll image attributes.
1211 */
1212 assert(image != (const Image *) NULL);
1213 assert(image->signature == MagickSignature);
1214 if (image->debug != MagickFalse)
1215 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1216 assert(exception != (ExceptionInfo *) NULL);
1217 assert(exception->signature == MagickSignature);
1218 roll_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
1219 if (roll_image == (Image *) NULL)
1220 return((Image *) NULL);
1221 offset.x=x_offset;
1222 offset.y=y_offset;
1223 while (offset.x < 0)
1224 offset.x+=image->columns;
cristybb503372010-05-27 20:51:26 +00001225 while (offset.x >= (ssize_t) image->columns)
cristy3ed852e2009-09-05 21:47:34 +00001226 offset.x-=image->columns;
1227 while (offset.y < 0)
1228 offset.y+=image->rows;
cristybb503372010-05-27 20:51:26 +00001229 while (offset.y >= (ssize_t) image->rows)
cristy3ed852e2009-09-05 21:47:34 +00001230 offset.y-=image->rows;
1231 /*
1232 Roll image.
1233 */
cristybb503372010-05-27 20:51:26 +00001234 status=CopyImageRegion(roll_image,image,(size_t) offset.x,
1235 (size_t) offset.y,(ssize_t) image->columns-offset.x,(ssize_t) image->rows-
cristy3ed852e2009-09-05 21:47:34 +00001236 offset.y,0,0,exception);
1237 (void) SetImageProgress(image,RollImageTag,0,3);
1238 status|=CopyImageRegion(roll_image,image,image->columns-offset.x,
cristybb503372010-05-27 20:51:26 +00001239 (size_t) offset.y,0,(ssize_t) image->rows-offset.y,offset.x,0,
cristy3ed852e2009-09-05 21:47:34 +00001240 exception);
1241 (void) SetImageProgress(image,RollImageTag,1,3);
cristybb503372010-05-27 20:51:26 +00001242 status|=CopyImageRegion(roll_image,image,(size_t) offset.x,image->rows-
1243 offset.y,(ssize_t) image->columns-offset.x,0,0,offset.y,exception);
cristy3ed852e2009-09-05 21:47:34 +00001244 (void) SetImageProgress(image,RollImageTag,2,3);
1245 status|=CopyImageRegion(roll_image,image,image->columns-offset.x,image->rows-
1246 offset.y,0,0,offset.x,offset.y,exception);
1247 (void) SetImageProgress(image,RollImageTag,3,3);
1248 roll_image->type=image->type;
1249 if (status == MagickFalse)
1250 roll_image=DestroyImage(roll_image);
1251 return(roll_image);
1252}
1253
1254/*
1255%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1256% %
1257% %
1258% %
1259% S h a v e I m a g e %
1260% %
1261% %
1262% %
1263%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1264%
1265% ShaveImage() shaves pixels from the image edges. It allocates the memory
1266% necessary for the new Image structure and returns a pointer to the new
1267% image.
1268%
1269% The format of the ShaveImage method is:
1270%
1271% Image *ShaveImage(const Image *image,const RectangleInfo *shave_info,
1272% ExceptionInfo *exception)
1273%
1274% A description of each parameter follows:
1275%
1276% o shave_image: Method ShaveImage returns a pointer to the shaved
1277% image. A null image is returned if there is a memory shortage or
1278% if the image width or height is zero.
1279%
1280% o image: the image.
1281%
1282% o shave_info: Specifies a pointer to a RectangleInfo which defines the
1283% region of the image to crop.
1284%
1285% o exception: return any errors or warnings in this structure.
1286%
1287*/
1288MagickExport Image *ShaveImage(const Image *image,
1289 const RectangleInfo *shave_info,ExceptionInfo *exception)
1290{
1291 Image
1292 *shave_image;
1293
1294 RectangleInfo
1295 geometry;
1296
1297 assert(image != (const Image *) NULL);
1298 assert(image->signature == MagickSignature);
1299 if (image->debug != MagickFalse)
1300 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1301 if (((2*shave_info->width) >= image->columns) ||
1302 ((2*shave_info->height) >= image->rows))
1303 ThrowImageException(OptionWarning,"GeometryDoesNotContainImage");
1304 SetGeometry(image,&geometry);
1305 geometry.width-=2*shave_info->width;
1306 geometry.height-=2*shave_info->height;
cristybb503372010-05-27 20:51:26 +00001307 geometry.x=(ssize_t) shave_info->width+image->page.x;
1308 geometry.y=(ssize_t) shave_info->height+image->page.y;
cristy3ed852e2009-09-05 21:47:34 +00001309 shave_image=CropImage(image,&geometry,exception);
1310 if (shave_image == (Image *) NULL)
1311 return((Image *) NULL);
1312 shave_image->page.width-=2*shave_info->width;
1313 shave_image->page.height-=2*shave_info->height;
1314 shave_image->page.x-=shave_info->width;
1315 shave_image->page.y-=shave_info->height;
1316 return(shave_image);
1317}
1318
1319/*
1320%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1321% %
1322% %
1323% %
1324% S p l i c e I m a g e %
1325% %
1326% %
1327% %
1328%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1329%
1330% SpliceImage() splices a solid color into the image as defined by the
1331% geometry.
1332%
1333% The format of the SpliceImage method is:
1334%
1335% Image *SpliceImage(const Image *image,const RectangleInfo *geometry,
1336% ExceptionInfo *exception)
1337%
1338% A description of each parameter follows:
1339%
1340% o image: the image.
1341%
1342% o geometry: Define the region of the image to splice with members
1343% x, y, width, and height.
1344%
1345% o exception: return any errors or warnings in this structure.
1346%
1347*/
1348MagickExport Image *SpliceImage(const Image *image,
1349 const RectangleInfo *geometry,ExceptionInfo *exception)
1350{
1351#define SpliceImageTag "Splice/Image"
1352
cristyc4c8d132010-01-07 01:58:38 +00001353 CacheView
1354 *image_view,
1355 *splice_view;
1356
cristy3ed852e2009-09-05 21:47:34 +00001357 Image
1358 *splice_image;
1359
cristy3ed852e2009-09-05 21:47:34 +00001360 MagickBooleanType
1361 proceed,
1362 status;
1363
cristybb503372010-05-27 20:51:26 +00001364 MagickOffsetType
1365 progress;
1366
cristy3ed852e2009-09-05 21:47:34 +00001367 RectangleInfo
1368 splice_geometry;
1369
cristybb503372010-05-27 20:51:26 +00001370 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001371 i;
1372
cristybb503372010-05-27 20:51:26 +00001373 ssize_t
1374 y;
1375
cristy3ed852e2009-09-05 21:47:34 +00001376 /*
1377 Allocate splice image.
1378 */
1379 assert(image != (const Image *) NULL);
1380 assert(image->signature == MagickSignature);
1381 if (image->debug != MagickFalse)
1382 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1383 assert(geometry != (const RectangleInfo *) NULL);
1384 assert(exception != (ExceptionInfo *) NULL);
1385 assert(exception->signature == MagickSignature);
1386 splice_geometry=(*geometry);
1387 splice_image=CloneImage(image,image->columns+splice_geometry.width,
1388 image->rows+splice_geometry.height,MagickTrue,exception);
1389 if (splice_image == (Image *) NULL)
1390 return((Image *) NULL);
1391 if (SetImageStorageClass(splice_image,DirectClass) == MagickFalse)
1392 {
1393 InheritException(exception,&splice_image->exception);
1394 splice_image=DestroyImage(splice_image);
1395 return((Image *) NULL);
1396 }
1397 (void) SetImageBackgroundColor(splice_image);
1398 /*
1399 Respect image geometry.
1400 */
1401 switch (image->gravity)
1402 {
1403 default:
1404 case UndefinedGravity:
1405 case NorthWestGravity:
1406 break;
1407 case NorthGravity:
1408 {
1409 splice_geometry.x+=splice_geometry.width/2;
1410 break;
1411 }
1412 case NorthEastGravity:
1413 {
1414 splice_geometry.x+=splice_geometry.width;
1415 break;
1416 }
1417 case WestGravity:
1418 {
1419 splice_geometry.y+=splice_geometry.width/2;
1420 break;
1421 }
1422 case StaticGravity:
1423 case CenterGravity:
1424 {
1425 splice_geometry.x+=splice_geometry.width/2;
1426 splice_geometry.y+=splice_geometry.height/2;
1427 break;
1428 }
1429 case EastGravity:
1430 {
1431 splice_geometry.x+=splice_geometry.width;
1432 splice_geometry.y+=splice_geometry.height/2;
1433 break;
1434 }
1435 case SouthWestGravity:
1436 {
1437 splice_geometry.y+=splice_geometry.height;
1438 break;
1439 }
1440 case SouthGravity:
1441 {
1442 splice_geometry.x+=splice_geometry.width/2;
1443 splice_geometry.y+=splice_geometry.height;
1444 break;
1445 }
1446 case SouthEastGravity:
1447 {
1448 splice_geometry.x+=splice_geometry.width;
1449 splice_geometry.y+=splice_geometry.height;
1450 break;
1451 }
1452 }
1453 /*
1454 Splice image.
1455 */
1456 status=MagickTrue;
1457 i=0;
1458 progress=0;
1459 image_view=AcquireCacheView(image);
1460 splice_view=AcquireCacheView(splice_image);
cristyb5d5f722009-11-04 03:03:49 +00001461#if defined(MAGICKCORE_OPENMP_SUPPORT)
1462 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00001463#endif
cristybb503372010-05-27 20:51:26 +00001464 for (y=0; y < (ssize_t) splice_geometry.y; y++)
cristy3ed852e2009-09-05 21:47:34 +00001465 {
1466 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001467 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001468
1469 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001470 *restrict indexes,
1471 *restrict splice_indexes;
cristy3ed852e2009-09-05 21:47:34 +00001472
cristybb503372010-05-27 20:51:26 +00001473 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001474 x;
1475
1476 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001477 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001478
1479 if (status == MagickFalse)
1480 continue;
1481 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1482 q=QueueCacheViewAuthenticPixels(splice_view,0,y,splice_image->columns,1,
1483 exception);
1484 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
1485 {
1486 status=MagickFalse;
1487 continue;
1488 }
1489 indexes=GetCacheViewAuthenticIndexQueue(image_view);
1490 splice_indexes=GetCacheViewAuthenticIndexQueue(splice_view);
1491 for (x=0; x < splice_geometry.x; x++)
1492 {
cristyce70c172010-01-07 17:15:30 +00001493 SetRedPixelComponent(q,GetRedPixelComponent(p));
1494 SetGreenPixelComponent(q,GetGreenPixelComponent(p));
1495 SetBluePixelComponent(q,GetBluePixelComponent(p));
1496 SetOpacityPixelComponent(q,OpaqueOpacity);
cristy3ed852e2009-09-05 21:47:34 +00001497 if (image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00001498 SetOpacityPixelComponent(q,GetOpacityPixelComponent(p));
cristy3ed852e2009-09-05 21:47:34 +00001499 if (image->colorspace == CMYKColorspace)
1500 splice_indexes[x]=(*indexes++);
1501 p++;
1502 q++;
1503 }
cristybb503372010-05-27 20:51:26 +00001504 for ( ; x < (ssize_t) (splice_geometry.x+splice_geometry.width); x++)
cristy3ed852e2009-09-05 21:47:34 +00001505 q++;
cristybb503372010-05-27 20:51:26 +00001506 for ( ; x < (ssize_t) splice_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001507 {
cristyce70c172010-01-07 17:15:30 +00001508 SetRedPixelComponent(q,GetRedPixelComponent(p));
1509 SetGreenPixelComponent(q,GetGreenPixelComponent(p));
1510 SetBluePixelComponent(q,GetBluePixelComponent(p));
1511 SetOpacityPixelComponent(q,OpaqueOpacity);
cristy3ed852e2009-09-05 21:47:34 +00001512 if (image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00001513 SetOpacityPixelComponent(q,GetOpacityPixelComponent(p));
cristy3ed852e2009-09-05 21:47:34 +00001514 if (image->colorspace == CMYKColorspace)
1515 splice_indexes[x]=(*indexes++);
1516 p++;
1517 q++;
1518 }
1519 if (SyncCacheViewAuthenticPixels(splice_view,exception) == MagickFalse)
1520 status=MagickFalse;
cristybb503372010-05-27 20:51:26 +00001521 proceed=SetImageProgress(image,SpliceImageTag,(MagickOffsetType) y,
1522 splice_image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001523 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1524 {
1525 MagickBooleanType
1526 proceed;
1527
cristyb5d5f722009-11-04 03:03:49 +00001528#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00001529 #pragma omp critical (MagickCore_TransposeImage)
1530#endif
1531 proceed=SetImageProgress(image,SpliceImageTag,progress++,
1532 splice_image->rows);
1533 if (proceed == MagickFalse)
1534 status=MagickFalse;
1535 }
1536 }
cristyb5d5f722009-11-04 03:03:49 +00001537#if defined(MAGICKCORE_OPENMP_SUPPORT)
1538 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00001539#endif
cristybb503372010-05-27 20:51:26 +00001540 for (y=(ssize_t) (splice_geometry.y+splice_geometry.height);
1541 y < (ssize_t) splice_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001542 {
1543 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001544 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001545
1546 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001547 *restrict indexes,
1548 *restrict splice_indexes;
cristy3ed852e2009-09-05 21:47:34 +00001549
cristybb503372010-05-27 20:51:26 +00001550 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001551 x;
1552
1553 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001554 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001555
1556 if (status == MagickFalse)
1557 continue;
1558 p=GetCacheViewVirtualPixels(image_view,0,y-splice_geometry.height,
1559 image->columns,1,exception);
1560 q=QueueCacheViewAuthenticPixels(splice_view,0,y,splice_image->columns,1,
1561 exception);
1562 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
1563 {
1564 status=MagickFalse;
1565 continue;
1566 }
1567 indexes=GetCacheViewAuthenticIndexQueue(image_view);
1568 splice_indexes=GetCacheViewAuthenticIndexQueue(splice_view);
1569 for (x=0; x < splice_geometry.x; x++)
1570 {
cristyce70c172010-01-07 17:15:30 +00001571 SetRedPixelComponent(q,GetRedPixelComponent(p));
1572 SetGreenPixelComponent(q,GetGreenPixelComponent(p));
1573 SetBluePixelComponent(q,GetBluePixelComponent(p));
1574 SetOpacityPixelComponent(q,OpaqueOpacity);
cristy3ed852e2009-09-05 21:47:34 +00001575 if (image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00001576 SetOpacityPixelComponent(q,GetOpacityPixelComponent(p));
cristy3ed852e2009-09-05 21:47:34 +00001577 if (image->colorspace == CMYKColorspace)
1578 splice_indexes[x]=(*indexes++);
1579 p++;
1580 q++;
1581 }
cristybb503372010-05-27 20:51:26 +00001582 for ( ; x < (ssize_t) (splice_geometry.x+splice_geometry.width); x++)
cristy3ed852e2009-09-05 21:47:34 +00001583 q++;
cristybb503372010-05-27 20:51:26 +00001584 for ( ; x < (ssize_t) splice_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001585 {
cristyce70c172010-01-07 17:15:30 +00001586 SetRedPixelComponent(q,GetRedPixelComponent(p));
1587 SetGreenPixelComponent(q,GetGreenPixelComponent(p));
1588 SetBluePixelComponent(q,GetBluePixelComponent(p));
1589 SetOpacityPixelComponent(q,OpaqueOpacity);
cristy3ed852e2009-09-05 21:47:34 +00001590 if (image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00001591 SetOpacityPixelComponent(q,GetOpacityPixelComponent(p));
cristy3ed852e2009-09-05 21:47:34 +00001592 if (image->colorspace == CMYKColorspace)
1593 splice_indexes[x]=(*indexes++);
1594 p++;
1595 q++;
1596 }
1597 if (SyncCacheViewAuthenticPixels(splice_view,exception) == MagickFalse)
1598 status=MagickFalse;
1599 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1600 {
1601 MagickBooleanType
1602 proceed;
1603
cristyb5d5f722009-11-04 03:03:49 +00001604#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00001605 #pragma omp critical (MagickCore_TransposeImage)
1606#endif
1607 proceed=SetImageProgress(image,SpliceImageTag,progress++,
1608 splice_image->rows);
1609 if (proceed == MagickFalse)
1610 status=MagickFalse;
1611 }
1612 }
1613 splice_view=DestroyCacheView(splice_view);
1614 image_view=DestroyCacheView(image_view);
1615 if (status == MagickFalse)
1616 splice_image=DestroyImage(splice_image);
1617 return(splice_image);
1618}
1619
1620/*
1621%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1622% %
1623% %
1624% %
1625% T r a n s f o r m I m a g e %
1626% %
1627% %
1628% %
1629%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1630%
1631% TransformImage() is a convenience method that behaves like ResizeImage() or
1632% CropImage() but accepts scaling and/or cropping information as a region
1633% geometry specification. If the operation fails, the original image handle
1634% is returned.
1635%
1636% The format of the TransformImage method is:
1637%
1638% MagickBooleanType TransformImage(Image **image,const char *crop_geometry,
1639% const char *image_geometry)
1640%
1641% A description of each parameter follows:
1642%
1643% o image: the image The transformed image is returned as this parameter.
1644%
1645% o crop_geometry: A crop geometry string. This geometry defines a
1646% subregion of the image to crop.
1647%
1648% o image_geometry: An image geometry string. This geometry defines the
1649% final size of the image.
1650%
1651*/
cristybb503372010-05-27 20:51:26 +00001652static inline ssize_t MagickRound(MagickRealType x)
anthonyc0429ed2009-12-27 08:22:42 +00001653{
cristy508d9312010-02-10 21:10:30 +00001654 /*
1655 Round the fraction to nearest integer.
1656 */
anthonyc0429ed2009-12-27 08:22:42 +00001657 if (x >= 0.0)
cristybb503372010-05-27 20:51:26 +00001658 return((ssize_t) (x+0.5));
1659 return((ssize_t) (x-0.5));
anthonyc0429ed2009-12-27 08:22:42 +00001660}
1661
cristy3ed852e2009-09-05 21:47:34 +00001662MagickExport MagickBooleanType TransformImage(Image **image,
1663 const char *crop_geometry,const char *image_geometry)
1664{
1665 Image
cristy508d9312010-02-10 21:10:30 +00001666 *next,
cristy3ed852e2009-09-05 21:47:34 +00001667 *resize_image,
1668 *transform_image;
1669
cristybb503372010-05-27 20:51:26 +00001670 ssize_t
cristy508d9312010-02-10 21:10:30 +00001671 x,
1672 y;
1673
cristy3ed852e2009-09-05 21:47:34 +00001674 MagickStatusType
1675 flags;
1676
1677 RectangleInfo
1678 geometry;
1679
cristybb503372010-05-27 20:51:26 +00001680 size_t
cristy508d9312010-02-10 21:10:30 +00001681 height,
1682 width;
1683
cristy3ed852e2009-09-05 21:47:34 +00001684 assert(image != (Image **) NULL);
1685 assert((*image)->signature == MagickSignature);
1686 if ((*image)->debug != MagickFalse)
1687 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
1688 transform_image=(*image);
1689 if (crop_geometry != (const char *) NULL)
1690 {
1691 Image
1692 *crop_image;
1693
1694 RectangleInfo
1695 geometry;
1696
1697 /*
1698 Crop image to a user specified size.
1699 */
1700 crop_image=NewImageList();
1701 flags=ParseGravityGeometry(transform_image,crop_geometry,&geometry,
1702 &(*image)->exception);
cristy508d9312010-02-10 21:10:30 +00001703 if ((flags & AreaValue) != 0)
anthonyc0429ed2009-12-27 08:22:42 +00001704 {
cristy508d9312010-02-10 21:10:30 +00001705 PointInfo
cristy4eced732010-02-10 22:26:28 +00001706 delta,
1707 offset;
anthonyc0429ed2009-12-27 08:22:42 +00001708
1709 RectangleInfo
1710 crop;
1711
cristy508d9312010-02-10 21:10:30 +00001712 /*
1713 Crop into NxM tiles (@ flag) - AT.
1714 */
1715 if (geometry.width == 0)
1716 geometry.width=1;
1717 if (geometry.height == 0)
1718 geometry.height=1;
1719 width=transform_image->columns;
1720 height=transform_image->rows;
anthonyc0429ed2009-12-27 08:22:42 +00001721 if ((flags & AspectValue) == 0)
1722 {
cristy508d9312010-02-10 21:10:30 +00001723 width-=(geometry.x < 0 ? -1 : 1)*geometry.x;
1724 height-=(geometry.y < 0 ? -1 : 1)*geometry.y;
anthonyc0429ed2009-12-27 08:22:42 +00001725 }
1726 else
1727 {
cristy508d9312010-02-10 21:10:30 +00001728 width+=(geometry.x < 0 ? -1 : 1)*geometry.x;
1729 height+=(geometry.y < 0 ? -1 : 1)*geometry.y;
anthonyc0429ed2009-12-27 08:22:42 +00001730 }
cristy508d9312010-02-10 21:10:30 +00001731 delta.x=(double) width/geometry.width;
1732 delta.y=(double) height/geometry.height;
anthonyc0429ed2009-12-27 08:22:42 +00001733 next=NewImageList();
cristy4eced732010-02-10 22:26:28 +00001734 for (offset.y=0; offset.y < (double) height; )
anthonyc0429ed2009-12-27 08:22:42 +00001735 {
1736 if ((flags & AspectValue) == 0)
1737 {
cristybb503372010-05-27 20:51:26 +00001738 crop.y=(ssize_t) MagickRound((MagickRealType) (offset.y-
cristy508d9312010-02-10 21:10:30 +00001739 (geometry.y > 0 ? 0 : geometry.y)));
cristy4eced732010-02-10 22:26:28 +00001740 offset.y+=delta.y;
cristybb503372010-05-27 20:51:26 +00001741 crop.height=(size_t) MagickRound((MagickRealType)
cristy4eced732010-02-10 22:26:28 +00001742 (offset.y+(geometry.y < 0 ? 0 : geometry.y)));
anthonyc0429ed2009-12-27 08:22:42 +00001743 }
1744 else
1745 {
cristybb503372010-05-27 20:51:26 +00001746 crop.y=(ssize_t) MagickRound((MagickRealType) (offset.y-
cristy508d9312010-02-10 21:10:30 +00001747 (geometry.y > 0 ? geometry.y : 0)));
cristy4eced732010-02-10 22:26:28 +00001748 offset.y+=delta.y;
cristybb503372010-05-27 20:51:26 +00001749 crop.height=(size_t) MagickRound((MagickRealType)
cristy4eced732010-02-10 22:26:28 +00001750 (offset.y+(geometry.y < 0 ? geometry.y : 0)));
anthonyc0429ed2009-12-27 08:22:42 +00001751 }
cristy508d9312010-02-10 21:10:30 +00001752 crop.height-=crop.y;
cristy3d818612010-04-01 18:56:37 +00001753 crop.y+=transform_image->page.y;
cristy4eced732010-02-10 22:26:28 +00001754 for (offset.x=0; offset.x < (double) width; )
anthonyc0429ed2009-12-27 08:22:42 +00001755 {
1756 if ((flags & AspectValue) == 0)
1757 {
cristybb503372010-05-27 20:51:26 +00001758 crop.x=(ssize_t) MagickRound((MagickRealType) (offset.x-
cristy508d9312010-02-10 21:10:30 +00001759 (geometry.x > 0 ? 0 : geometry.x)));
cristy4eced732010-02-10 22:26:28 +00001760 offset.x+=+delta.x;
cristybb503372010-05-27 20:51:26 +00001761 crop.width=(size_t) MagickRound((MagickRealType)
cristy4eced732010-02-10 22:26:28 +00001762 (offset.x+(geometry.x < 0 ? 0 : geometry.x)));
anthonyc0429ed2009-12-27 08:22:42 +00001763 }
1764 else
1765 {
cristybb503372010-05-27 20:51:26 +00001766 crop.x=(ssize_t) MagickRound((MagickRealType) (offset.x-
cristy508d9312010-02-10 21:10:30 +00001767 (geometry.x > 0 ? geometry.x : 0)));
cristy4eced732010-02-10 22:26:28 +00001768 offset.x+=+delta.x;
cristybb503372010-05-27 20:51:26 +00001769 crop.width=(size_t) MagickRound((MagickRealType)
cristy4eced732010-02-10 22:26:28 +00001770 (offset.x+(geometry.x < 0 ? geometry.x : 0)));
anthonyc0429ed2009-12-27 08:22:42 +00001771 }
cristy508d9312010-02-10 21:10:30 +00001772 crop.width-=crop.x;
cristy3d818612010-04-01 18:56:37 +00001773 crop.x+=transform_image->page.x;
anthonyc0429ed2009-12-27 08:22:42 +00001774 next=CropImage(transform_image,&crop,&(*image)->exception);
1775 if (next == (Image *) NULL)
1776 break;
1777 AppendImageToList(&crop_image,next);
1778 }
1779 if (next == (Image *) NULL)
1780 break;
1781 }
1782 }
cristy508d9312010-02-10 21:10:30 +00001783 else
1784 if (((geometry.width == 0) && (geometry.height == 0)) ||
anthonyc0429ed2009-12-27 08:22:42 +00001785 ((flags & XValue) != 0) || ((flags & YValue) != 0))
cristy3ed852e2009-09-05 21:47:34 +00001786 {
cristy508d9312010-02-10 21:10:30 +00001787 /*
1788 Crop a single region at +X+Y.
1789 */
1790 crop_image=CropImage(transform_image,&geometry,
1791 &(*image)->exception);
1792 if ((crop_image != (Image *) NULL) && ((flags & AspectValue) != 0))
1793 {
1794 crop_image->page.width=geometry.width;
1795 crop_image->page.height=geometry.height;
1796 crop_image->page.x-=geometry.x;
1797 crop_image->page.y-=geometry.y;
1798 }
1799 }
1800 else
1801 if ((transform_image->columns > geometry.width) ||
1802 (transform_image->rows > geometry.height))
1803 {
1804 MagickBooleanType
1805 proceed;
1806
1807 MagickProgressMonitor
1808 progress_monitor;
1809
1810 MagickOffsetType
1811 i;
1812
1813 MagickSizeType
1814 number_images;
1815
1816 /*
1817 Crop into tiles of fixed size WxH.
1818 */
1819 if (transform_image->page.width == 0)
1820 transform_image->page.width=transform_image->columns;
1821 if (transform_image->page.height == 0)
1822 transform_image->page.height=transform_image->rows;
1823 width=geometry.width;
1824 if (width == 0)
1825 width=transform_image->page.width;
1826 height=geometry.height;
1827 if (height == 0)
1828 height=transform_image->page.height;
1829 next=NewImageList();
1830 proceed=MagickTrue;
1831 i=0;
cristy0e9f9c12010-02-11 03:00:47 +00001832 number_images=0;
cristybb503372010-05-27 20:51:26 +00001833 for (y=0; y < (ssize_t) transform_image->page.height; y+=height)
1834 for (x=0; x < (ssize_t) transform_image->page.width; x+=width)
cristy0e9f9c12010-02-11 03:00:47 +00001835 number_images++;
cristybb503372010-05-27 20:51:26 +00001836 for (y=0; y < (ssize_t) transform_image->page.height; y+=height)
cristy508d9312010-02-10 21:10:30 +00001837 {
cristybb503372010-05-27 20:51:26 +00001838 for (x=0; x < (ssize_t) transform_image->page.width; x+=width)
cristy508d9312010-02-10 21:10:30 +00001839 {
1840 progress_monitor=SetImageProgressMonitor(transform_image,
1841 (MagickProgressMonitor) NULL,transform_image->client_data);
1842 geometry.width=width;
1843 geometry.height=height;
1844 geometry.x=x;
1845 geometry.y=y;
1846 next=CropImage(transform_image,&geometry,&(*image)->exception);
1847 (void) SetImageProgressMonitor(transform_image,
1848 progress_monitor,transform_image->client_data);
1849 proceed=SetImageProgress(transform_image,CropImageTag,i++,
1850 number_images);
1851 if (proceed == MagickFalse)
1852 break;
1853 if (next == (Image *) NULL)
1854 break;
cristy36507e62010-02-11 00:26:48 +00001855 (void) SetImageProgressMonitor(next,progress_monitor,
1856 next->client_data);
cristy2a9e7cb2010-03-05 02:15:05 +00001857 if (crop_image == (Image *) NULL)
1858 crop_image=next;
1859 else
1860 {
1861 next->previous=crop_image;
1862 crop_image->next=next;
1863 crop_image=crop_image->next;
1864 }
cristy508d9312010-02-10 21:10:30 +00001865 }
1866 if (next == (Image *) NULL)
1867 break;
1868 if (proceed == MagickFalse)
1869 break;
1870 }
1871 }
cristy3ed852e2009-09-05 21:47:34 +00001872 if (crop_image == (Image *) NULL)
1873 transform_image=CloneImage(*image,0,0,MagickTrue,&(*image)->exception);
1874 else
1875 {
1876 transform_image=DestroyImage(transform_image);
1877 transform_image=GetFirstImageInList(crop_image);
1878 }
1879 *image=transform_image;
1880 }
1881 if (image_geometry == (const char *) NULL)
1882 return(MagickTrue);
1883 /*
1884 Scale image to a user specified size.
1885 */
1886 flags=ParseRegionGeometry(transform_image,image_geometry,&geometry,
1887 &(*image)->exception);
1888 if ((transform_image->columns == geometry.width) &&
1889 (transform_image->rows == geometry.height))
1890 return(MagickTrue);
1891 resize_image=ZoomImage(transform_image,geometry.width,geometry.height,
1892 &(*image)->exception);
1893 if (resize_image == (Image *) NULL)
1894 return(MagickFalse);
1895 transform_image=DestroyImage(transform_image);
1896 transform_image=resize_image;
1897 *image=transform_image;
1898 return(MagickTrue);
1899}
1900
1901/*
1902%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1903% %
1904% %
1905% %
1906% T r a n s f o r m I m a g e s %
1907% %
1908% %
1909% % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1910%
1911% TransformImages() calls TransformImage() on each image of a sequence.
1912%
1913% The format of the TransformImage method is:
1914%
1915% MagickBooleanType TransformImages(Image **image,
1916% const char *crop_geometry,const char *image_geometry)
1917%
1918% A description of each parameter follows:
1919%
1920% o image: the image The transformed image is returned as this parameter.
1921%
1922% o crop_geometry: A crop geometry string. This geometry defines a
1923% subregion of the image to crop.
1924%
1925% o image_geometry: An image geometry string. This geometry defines the
1926% final size of the image.
1927%
1928*/
1929MagickExport MagickBooleanType TransformImages(Image **images,
1930 const char *crop_geometry,const char *image_geometry)
1931{
1932 Image
1933 *image,
1934 **image_list,
1935 *transform_images;
1936
1937 MagickStatusType
1938 status;
1939
cristybb503372010-05-27 20:51:26 +00001940 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001941 i;
1942
1943 assert(images != (Image **) NULL);
1944 assert((*images)->signature == MagickSignature);
1945 if ((*images)->debug != MagickFalse)
1946 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
1947 (*images)->filename);
1948 image_list=ImageListToArray(*images,&(*images)->exception);
1949 if (image_list == (Image **) NULL)
1950 return(MagickFalse);
1951 status=MagickTrue;
1952 transform_images=NewImageList();
1953 for (i=0; image_list[i] != (Image *) NULL; i++)
1954 {
1955 image=image_list[i];
1956 status|=TransformImage(&image,crop_geometry,image_geometry);
1957 AppendImageToList(&transform_images,image);
1958 }
1959 *images=transform_images;
1960 image_list=(Image **) RelinquishMagickMemory(image_list);
1961 return(status != 0 ? MagickTrue : MagickFalse);
1962}
1963
1964/*
1965%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1966% %
1967% %
1968% %
1969% T r a n s p o s e I m a g e %
1970% %
1971% %
1972% %
1973%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1974%
1975% TransposeImage() creates a horizontal mirror image by reflecting the pixels
1976% around the central y-axis while rotating them by 90 degrees.
1977%
1978% The format of the TransposeImage method is:
1979%
1980% Image *TransposeImage(const Image *image,ExceptionInfo *exception)
1981%
1982% A description of each parameter follows:
1983%
1984% o image: the image.
1985%
1986% o exception: return any errors or warnings in this structure.
1987%
1988*/
1989MagickExport Image *TransposeImage(const Image *image,ExceptionInfo *exception)
1990{
1991#define TransposeImageTag "Transpose/Image"
1992
cristyc4c8d132010-01-07 01:58:38 +00001993 CacheView
1994 *image_view,
1995 *transpose_view;
1996
cristy3ed852e2009-09-05 21:47:34 +00001997 Image
1998 *transpose_image;
1999
cristy3ed852e2009-09-05 21:47:34 +00002000 MagickBooleanType
2001 status;
2002
cristybb503372010-05-27 20:51:26 +00002003 MagickOffsetType
2004 progress;
2005
cristy3ed852e2009-09-05 21:47:34 +00002006 RectangleInfo
2007 page;
2008
cristybb503372010-05-27 20:51:26 +00002009 ssize_t
2010 y;
2011
cristy3ed852e2009-09-05 21:47:34 +00002012 assert(image != (const Image *) NULL);
2013 assert(image->signature == MagickSignature);
2014 if (image->debug != MagickFalse)
2015 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2016 assert(exception != (ExceptionInfo *) NULL);
2017 assert(exception->signature == MagickSignature);
2018 transpose_image=CloneImage(image,image->rows,image->columns,MagickTrue,
2019 exception);
2020 if (transpose_image == (Image *) NULL)
2021 return((Image *) NULL);
2022 /*
2023 Transpose image.
2024 */
2025 status=MagickTrue;
2026 progress=0;
2027 image_view=AcquireCacheView(image);
2028 transpose_view=AcquireCacheView(transpose_image);
cristyb5d5f722009-11-04 03:03:49 +00002029#if defined(MAGICKCORE_OPENMP_SUPPORT)
2030 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00002031#endif
cristybb503372010-05-27 20:51:26 +00002032 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002033 {
2034 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002035 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002036
2037 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002038 *restrict transpose_indexes,
2039 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002040
2041 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002042 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002043
2044 if (status == MagickFalse)
2045 continue;
cristybb503372010-05-27 20:51:26 +00002046 p=GetCacheViewVirtualPixels(image_view,0,(ssize_t) image->rows-y-1,
cristy3ed852e2009-09-05 21:47:34 +00002047 image->columns,1,exception);
cristybb503372010-05-27 20:51:26 +00002048 q=QueueCacheViewAuthenticPixels(transpose_view,(ssize_t) (image->rows-y-1),0,
cristy3ed852e2009-09-05 21:47:34 +00002049 1,transpose_image->rows,exception);
2050 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2051 {
2052 status=MagickFalse;
2053 continue;
2054 }
2055 (void) CopyMagickMemory(q,p,(size_t) image->columns*sizeof(*q));
2056 indexes=GetCacheViewAuthenticIndexQueue(image_view);
2057 if (indexes != (IndexPacket *) NULL)
2058 {
2059 transpose_indexes=GetCacheViewAuthenticIndexQueue(transpose_view);
2060 if (transpose_indexes != (IndexPacket *) NULL)
2061 (void) CopyMagickMemory(transpose_indexes,indexes,(size_t)
2062 image->columns*sizeof(*transpose_indexes));
2063 }
2064 if (SyncCacheViewAuthenticPixels(transpose_view,exception) == MagickFalse)
2065 status=MagickFalse;
2066 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2067 {
2068 MagickBooleanType
2069 proceed;
2070
cristyb5d5f722009-11-04 03:03:49 +00002071#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002072 #pragma omp critical (MagickCore_TransposeImage)
2073#endif
2074 proceed=SetImageProgress(image,TransposeImageTag,progress++,
2075 image->rows);
2076 if (proceed == MagickFalse)
2077 status=MagickFalse;
2078 }
2079 }
2080 transpose_view=DestroyCacheView(transpose_view);
2081 image_view=DestroyCacheView(image_view);
2082 transpose_image->type=image->type;
2083 page=transpose_image->page;
2084 Swap(page.width,page.height);
2085 Swap(page.x,page.y);
2086 if (page.width != 0)
cristybb503372010-05-27 20:51:26 +00002087 page.x=(ssize_t) (page.width-transpose_image->columns-page.x);
cristy3ed852e2009-09-05 21:47:34 +00002088 transpose_image->page=page;
2089 if (status == MagickFalse)
2090 transpose_image=DestroyImage(transpose_image);
2091 return(transpose_image);
2092}
2093
2094/*
2095%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2096% %
2097% %
2098% %
2099% T r a n s v e r s e I m a g e %
2100% %
2101% %
2102% %
2103%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2104%
2105% TransverseImage() creates a vertical mirror image by reflecting the pixels
2106% around the central x-axis while rotating them by 270 degrees.
2107%
2108% The format of the TransverseImage method is:
2109%
2110% Image *TransverseImage(const Image *image,ExceptionInfo *exception)
2111%
2112% A description of each parameter follows:
2113%
2114% o image: the image.
2115%
2116% o exception: return any errors or warnings in this structure.
2117%
2118*/
2119MagickExport Image *TransverseImage(const Image *image,ExceptionInfo *exception)
2120{
2121#define TransverseImageTag "Transverse/Image"
2122
cristyc4c8d132010-01-07 01:58:38 +00002123 CacheView
2124 *image_view,
2125 *transverse_view;
2126
cristy3ed852e2009-09-05 21:47:34 +00002127 Image
2128 *transverse_image;
2129
cristy3ed852e2009-09-05 21:47:34 +00002130 MagickBooleanType
2131 status;
2132
cristybb503372010-05-27 20:51:26 +00002133 MagickOffsetType
2134 progress;
2135
cristy3ed852e2009-09-05 21:47:34 +00002136 RectangleInfo
2137 page;
2138
cristybb503372010-05-27 20:51:26 +00002139 ssize_t
2140 y;
2141
cristy3ed852e2009-09-05 21:47:34 +00002142 assert(image != (const Image *) NULL);
2143 assert(image->signature == MagickSignature);
2144 if (image->debug != MagickFalse)
2145 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2146 assert(exception != (ExceptionInfo *) NULL);
2147 assert(exception->signature == MagickSignature);
2148 transverse_image=CloneImage(image,image->rows,image->columns,MagickTrue,
2149 exception);
2150 if (transverse_image == (Image *) NULL)
2151 return((Image *) NULL);
2152 /*
2153 Transverse image.
2154 */
2155 status=MagickTrue;
2156 progress=0;
2157 image_view=AcquireCacheView(image);
2158 transverse_view=AcquireCacheView(transverse_image);
cristyb5d5f722009-11-04 03:03:49 +00002159#if defined(MAGICKCORE_OPENMP_SUPPORT)
2160 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00002161#endif
cristybb503372010-05-27 20:51:26 +00002162 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002163 {
2164 MagickBooleanType
2165 sync;
2166
2167 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002168 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002169
2170 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002171 *restrict transverse_indexes,
2172 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002173
cristybb503372010-05-27 20:51:26 +00002174 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002175 x;
2176
2177 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002178 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002179
2180 if (status == MagickFalse)
2181 continue;
2182 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
cristybb503372010-05-27 20:51:26 +00002183 q=QueueCacheViewAuthenticPixels(transverse_view,(ssize_t) (image->rows-y-
cristy3ed852e2009-09-05 21:47:34 +00002184 1),0,1,transverse_image->rows,exception);
2185 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2186 {
2187 status=MagickFalse;
2188 continue;
2189 }
2190 q+=image->columns;
cristybb503372010-05-27 20:51:26 +00002191 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002192 *--q=(*p++);
2193 indexes=GetCacheViewAuthenticIndexQueue(image_view);
2194 if (indexes != (IndexPacket *) NULL)
2195 {
2196 transverse_indexes=GetCacheViewAuthenticIndexQueue(transverse_view);
2197 if (transverse_indexes != (IndexPacket *) NULL)
cristybb503372010-05-27 20:51:26 +00002198 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002199 transverse_indexes[image->columns-x-1]=indexes[x];
2200 }
2201 sync=SyncCacheViewAuthenticPixels(transverse_view,exception);
2202 if (sync == MagickFalse)
2203 status=MagickFalse;
2204 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2205 {
2206 MagickBooleanType
2207 proceed;
2208
cristyb5d5f722009-11-04 03:03:49 +00002209#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002210 #pragma omp critical (MagickCore_TransverseImage)
2211#endif
2212 proceed=SetImageProgress(image,TransverseImageTag,progress++,
2213 image->rows);
2214 if (proceed == MagickFalse)
2215 status=MagickFalse;
2216 }
2217 }
2218 transverse_view=DestroyCacheView(transverse_view);
2219 image_view=DestroyCacheView(image_view);
2220 transverse_image->type=image->type;
2221 page=transverse_image->page;
2222 Swap(page.width,page.height);
2223 Swap(page.x,page.y);
2224 if (page.height != 0)
cristybb503372010-05-27 20:51:26 +00002225 page.y=(ssize_t) (page.height-transverse_image->rows-page.y);
cristy3ed852e2009-09-05 21:47:34 +00002226 transverse_image->page=page;
2227 if (status == MagickFalse)
2228 transverse_image=DestroyImage(transverse_image);
2229 return(transverse_image);
2230}
2231
2232/*
2233%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2234% %
2235% %
2236% %
2237% T r i m I m a g e %
2238% %
2239% %
2240% %
2241%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2242%
2243% TrimImage() trims pixels from the image edges. It allocates the memory
2244% necessary for the new Image structure and returns a pointer to the new
2245% image.
2246%
2247% The format of the TrimImage method is:
2248%
2249% Image *TrimImage(const Image *image,ExceptionInfo *exception)
2250%
2251% A description of each parameter follows:
2252%
2253% o image: the image.
2254%
2255% o exception: return any errors or warnings in this structure.
2256%
2257*/
2258MagickExport Image *TrimImage(const Image *image,ExceptionInfo *exception)
2259{
2260 RectangleInfo
2261 geometry;
2262
2263 assert(image != (const Image *) NULL);
2264 assert(image->signature == MagickSignature);
2265 if (image->debug != MagickFalse)
2266 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2267 geometry=GetImageBoundingBox(image,exception);
2268 if ((geometry.width == 0) || (geometry.height == 0))
2269 {
2270 Image
2271 *crop_image;
2272
2273 crop_image=CloneImage(image,1,1,MagickTrue,exception);
2274 if (crop_image == (Image *) NULL)
2275 return((Image *) NULL);
2276 crop_image->background_color.opacity=(Quantum) TransparentOpacity;
2277 (void) SetImageBackgroundColor(crop_image);
2278 crop_image->page=image->page;
2279 crop_image->page.x=(-1);
2280 crop_image->page.y=(-1);
2281 return(crop_image);
2282 }
2283 geometry.x+=image->page.x;
2284 geometry.y+=image->page.y;
2285 return(CropImage(image,&geometry,exception));
2286}