blob: 3d16bdb52122aa8c0b898536f7ec84b120851629 [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
108 long
109 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
119 register long
120 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);
132 if (((chop_info->x+(long) chop_info->width) < 0) ||
133 ((chop_info->y+(long) chop_info->height) < 0) ||
134 (chop_info->x > (long) image->columns) ||
135 (chop_info->y > (long) image->rows))
136 ThrowImageException(OptionWarning,"GeometryDoesNotContainImage");
137 extent=(*chop_info);
138 if ((extent.x+(long) extent.width) > (long) image->columns)
139 extent.width=(unsigned long) ((long) image->columns-extent.x);
140 if ((extent.y+(long) extent.height) > (long) image->rows)
141 extent.height=(unsigned long) ((long) image->rows-extent.y);
142 if (extent.x < 0)
143 {
144 extent.width-=(unsigned long) (-extent.x);
145 extent.x=0;
146 }
147 if (extent.y < 0)
148 {
149 extent.height-=(unsigned long) (-extent.y);
150 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);
164 for (y=0; y < (long) extent.y; y++)
165 {
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
173 register long
174 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);
191 for (x=0; x < (long) image->columns; x++)
192 {
193 if ((x < extent.x) || (x >= (long) (extent.x+extent.width)))
194 {
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;
cristy3ed852e2009-09-05 21:47:34 +0000207 proceed=SetImageProgress(image,ChopImageTag,y,chop_image->rows);
208 if (proceed == MagickFalse)
cristy00f95372010-02-13 16:39:29 +0000209 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +0000210 }
211 /*
212 Extract chop image.
213 */
214 i+=extent.height;
215 for (y=0; y < (long) (image->rows-(extent.y+extent.height)); y++)
216 {
217 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000218 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000219
220 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000221 *restrict chop_indexes,
222 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +0000223
224 register long
225 x;
226
227 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000228 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000229
cristy00f95372010-02-13 16:39:29 +0000230 if (status == MagickFalse)
231 continue;
cristy3ed852e2009-09-05 21:47:34 +0000232 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,exception);
233 q=QueueCacheViewAuthenticPixels(chop_view,0,j++,chop_image->columns,1,
234 exception);
235 if ((p == (PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
cristy00f95372010-02-13 16:39:29 +0000236 {
237 status=MagickFalse;
238 continue;
239 }
cristy3ed852e2009-09-05 21:47:34 +0000240 indexes=GetCacheViewAuthenticIndexQueue(image_view);
241 chop_indexes=GetCacheViewAuthenticIndexQueue(chop_view);
242 for (x=0; x < (long) image->columns; x++)
243 {
244 if ((x < extent.x) || (x >= (long) (extent.x+extent.width)))
245 {
246 *q=(*p);
247 if (indexes != (IndexPacket *) NULL)
248 {
249 if (chop_indexes != (IndexPacket *) NULL)
250 *chop_indexes++=indexes[x];
251 }
252 q++;
253 }
254 p++;
255 }
256 if (SyncCacheViewAuthenticPixels(chop_view,exception) == MagickFalse)
cristy00f95372010-02-13 16:39:29 +0000257 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +0000258 proceed=SetImageProgress(image,ChopImageTag,y,chop_image->rows);
259 if (proceed == MagickFalse)
cristy00f95372010-02-13 16:39:29 +0000260 status=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +0000261 }
262 chop_view=DestroyCacheView(chop_view);
263 image_view=DestroyCacheView(image_view);
264 chop_image->type=image->type;
265 return(chop_image);
266}
267
268/*
269%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
270% %
271% %
272% %
273+ C o n s o l i d a t e C M Y K I m a g e %
274% %
275% %
276% %
277%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
278%
279% ConsolidateCMYKImage() consolidates separate C, M, Y, and K planes into a
280% single image.
281%
282% The format of the ConsolidateCMYKImage method is:
283%
284% Image *ConsolidateCMYKImage(const Image *image,ExceptionInfo *exception)
285%
286% A description of each parameter follows:
287%
288% o image: the image sequence.
289%
290% o exception: return any errors or warnings in this structure.
291%
292*/
293MagickExport Image *ConsolidateCMYKImages(const Image *images,
294 ExceptionInfo *exception)
295{
296 Image
297 *cmyk_image,
298 *cmyk_images;
299
300 long
301 y;
302
303 register long
304 i;
305
306 /*
307 Consolidate separate C, M, Y, and K planes into a single image.
308 */
309 assert(images != (Image *) NULL);
310 assert(images->signature == MagickSignature);
311 if (images->debug != MagickFalse)
312 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
313 assert(exception != (ExceptionInfo *) NULL);
314 assert(exception->signature == MagickSignature);
315 cmyk_images=NewImageList();
316 for (i=0; i < (long) GetImageListLength(images); i+=4)
317 {
318 cmyk_image=CloneImage(images,images->columns,images->rows,MagickTrue,
319 exception);
320 if (cmyk_image == (Image *) NULL)
321 break;
322 if (SetImageStorageClass(cmyk_image,DirectClass) == MagickFalse)
323 break;
324 (void) SetImageColorspace(cmyk_image,CMYKColorspace);
325 for (y=0; y < (long) images->rows; y++)
326 {
327 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000328 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000329
330 register long
331 x;
332
333 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000334 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000335
336 p=GetVirtualPixels(images,0,y,images->columns,1,exception);
337 q=QueueAuthenticPixels(cmyk_image,0,y,cmyk_image->columns,1,exception);
338 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
339 break;
340 for (x=0; x < (long) images->columns; x++)
341 {
342 q->red=(Quantum) (QuantumRange-PixelIntensityToQuantum(p));
343 p++;
344 q++;
345 }
346 if (SyncAuthenticPixels(cmyk_image,exception) == MagickFalse)
347 break;
348 }
349 images=GetNextImageInList(images);
350 if (images == (Image *) NULL)
351 break;
352 for (y=0; y < (long) images->rows; y++)
353 {
354 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000355 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000356
357 register long
358 x;
359
360 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000361 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000362
363 p=GetVirtualPixels(images,0,y,images->columns,1,exception);
364 q=GetAuthenticPixels(cmyk_image,0,y,cmyk_image->columns,1,exception);
365 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
366 break;
367 for (x=0; x < (long) images->columns; x++)
368 {
369 q->green=(Quantum) (QuantumRange-PixelIntensityToQuantum(p));
370 p++;
371 q++;
372 }
373 if (SyncAuthenticPixels(cmyk_image,exception) == MagickFalse)
374 break;
375 }
376 images=GetNextImageInList(images);
377 if (images == (Image *) NULL)
378 break;
379 for (y=0; y < (long) images->rows; y++)
380 {
381 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000382 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000383
384 register long
385 x;
386
387 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000388 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000389
390 p=GetVirtualPixels(images,0,y,images->columns,1,exception);
391 q=GetAuthenticPixels(cmyk_image,0,y,cmyk_image->columns,1,exception);
392 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
393 break;
394 for (x=0; x < (long) images->columns; x++)
395 {
396 q->blue=(Quantum) (QuantumRange-PixelIntensityToQuantum(p));
397 p++;
398 q++;
399 }
400 if (SyncAuthenticPixels(cmyk_image,exception) == MagickFalse)
401 break;
402 }
403 images=GetNextImageInList(images);
404 if (images == (Image *) NULL)
405 break;
406 for (y=0; y < (long) images->rows; y++)
407 {
408 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000409 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000410
411 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000412 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +0000413
414 register long
415 x;
416
417 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000418 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000419
420 p=GetVirtualPixels(images,0,y,images->columns,1,exception);
421 q=GetAuthenticPixels(cmyk_image,0,y,cmyk_image->columns,1,exception);
422 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
423 break;
424 indexes=GetAuthenticIndexQueue(cmyk_image);
425 for (x=0; x < (long) images->columns; x++)
426 {
427 indexes[x]=(IndexPacket) (QuantumRange-PixelIntensityToQuantum(p));
428 p++;
429 }
430 if (SyncAuthenticPixels(cmyk_image,exception) == MagickFalse)
431 break;
432 }
433 AppendImageToList(&cmyk_images,cmyk_image);
434 images=GetNextImageInList(images);
435 if (images == (Image *) NULL)
436 break;
437 }
438 return(cmyk_images);
439}
440
441/*
442%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
443% %
444% %
445% %
446% C r o p I m a g e %
447% %
448% %
449% %
450%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
451%
452% CropImage() extracts a region of the image starting at the offset defined
453% by geometry.
454%
455% The format of the CropImage method is:
456%
457% Image *CropImage(const Image *image,const RectangleInfo *geometry,
458% ExceptionInfo *exception)
459%
460% A description of each parameter follows:
461%
462% o image: the image.
463%
464% o geometry: Define the region of the image to crop with members
465% x, y, width, and height.
466%
467% o exception: return any errors or warnings in this structure.
468%
469*/
470MagickExport Image *CropImage(const Image *image,const RectangleInfo *geometry,
471 ExceptionInfo *exception)
472{
473#define CropImageTag "Crop/Image"
474
cristyc4c8d132010-01-07 01:58:38 +0000475 CacheView
476 *crop_view,
477 *image_view;
478
cristy3ed852e2009-09-05 21:47:34 +0000479 Image
480 *crop_image;
481
482 long
483 progress,
484 y;
485
486 MagickBooleanType
487 status;
488
489 RectangleInfo
490 bounding_box,
491 page;
492
cristy3ed852e2009-09-05 21:47:34 +0000493 /*
494 Check crop geometry.
495 */
496 assert(image != (const Image *) NULL);
497 assert(image->signature == MagickSignature);
498 if (image->debug != MagickFalse)
499 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
500 assert(geometry != (const RectangleInfo *) NULL);
501 assert(exception != (ExceptionInfo *) NULL);
502 assert(exception->signature == MagickSignature);
503 bounding_box=image->page;
504 if ((bounding_box.width == 0) || (bounding_box.height == 0))
505 {
506 bounding_box.width=image->columns;
507 bounding_box.height=image->rows;
508 }
509 page=(*geometry);
510 if (page.width == 0)
511 page.width=bounding_box.width;
512 if (page.height == 0)
513 page.height=bounding_box.height;
514 if (((bounding_box.x-page.x) >= (long) page.width) ||
515 ((bounding_box.y-page.y) >= (long) page.height) ||
516 ((page.x-bounding_box.x) > (long) image->columns) ||
517 ((page.y-bounding_box.y) > (long) image->rows))
518 {
519 /*
520 Crop is not within virtual canvas, return 1 pixel transparent image.
521 */
522 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
523 "GeometryDoesNotContainImage","`%s'",image->filename);
524 crop_image=CloneImage(image,1,1,MagickTrue,exception);
525 if (crop_image == (Image *) NULL)
526 return((Image *) NULL);
527 crop_image->background_color.opacity=(Quantum) TransparentOpacity;
528 (void) SetImageBackgroundColor(crop_image);
529 crop_image->page=bounding_box;
530 crop_image->page.x=(-1);
531 crop_image->page.y=(-1);
532 if (crop_image->dispose == BackgroundDispose)
533 crop_image->dispose=NoneDispose;
534 return(crop_image);
535 }
536 if ((page.x < 0) && (bounding_box.x >= 0))
537 {
538 page.width+=page.x-bounding_box.x;
539 page.x=0;
540 }
541 else
542 {
543 page.width-=bounding_box.x-page.x;
544 page.x-=bounding_box.x;
545 if (page.x < 0)
546 page.x=0;
547 }
548 if ((page.y < 0) && (bounding_box.y >= 0))
549 {
550 page.height+=page.y-bounding_box.y;
551 page.y=0;
552 }
553 else
554 {
555 page.height-=bounding_box.y-page.y;
556 page.y-=bounding_box.y;
557 if (page.y < 0)
558 page.y=0;
559 }
560 if ((unsigned long) (page.x+page.width) > image->columns)
561 page.width=image->columns-page.x;
cristy1e4aa462010-02-15 00:04:18 +0000562 if ((geometry->width != 0) && (page.width > geometry->width))
563 page.width=geometry->width;
cristy3ed852e2009-09-05 21:47:34 +0000564 if ((unsigned long) (page.y+page.height) > image->rows)
565 page.height=image->rows-page.y;
cristy1e4aa462010-02-15 00:04:18 +0000566 if ((geometry->height != 0) && (page.height > geometry->height))
567 page.height=geometry->height;
cristy3ed852e2009-09-05 21:47:34 +0000568 bounding_box.x+=page.x;
569 bounding_box.y+=page.y;
570 if ((page.width == 0) || (page.height == 0))
571 {
572 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
573 "GeometryDoesNotContainImage","`%s'",image->filename);
574 return((Image *) NULL);
575 }
576 /*
577 Initialize crop image attributes.
578 */
579 crop_image=CloneImage(image,page.width,page.height,MagickTrue,exception);
580 if (crop_image == (Image *) NULL)
581 return((Image *) NULL);
582 crop_image->page.width=image->page.width;
583 crop_image->page.height=image->page.height;
584 if (((long) (bounding_box.x+bounding_box.width) > (long) image->page.width) ||
585 ((long) (bounding_box.y+bounding_box.height) > (long) image->page.height))
586 {
587 crop_image->page.width=bounding_box.width;
588 crop_image->page.height=bounding_box.height;
589 }
590 crop_image->page.x=bounding_box.x;
591 crop_image->page.y=bounding_box.y;
592 /*
593 Crop image.
594 */
595 status=MagickTrue;
596 progress=0;
597 image_view=AcquireCacheView(image);
598 crop_view=AcquireCacheView(crop_image);
cristyb5d5f722009-11-04 03:03:49 +0000599#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy00f95372010-02-13 16:39:29 +0000600 #pragma omp parallel for schedule(static) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000601#endif
602 for (y=0; y < (long) crop_image->rows; y++)
603 {
604 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000605 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +0000606
607 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000608 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000609
610 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000611 *restrict crop_indexes;
cristy3ed852e2009-09-05 21:47:34 +0000612
613 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000614 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000615
616 if (status == MagickFalse)
617 continue;
618 p=GetCacheViewVirtualPixels(image_view,page.x,page.y+y,crop_image->columns,
619 1,exception);
620 q=QueueCacheViewAuthenticPixels(crop_view,0,y,crop_image->columns,1,
621 exception);
622 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
623 {
624 status=MagickFalse;
625 continue;
626 }
cristy3ed852e2009-09-05 21:47:34 +0000627 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristy6c207342010-02-19 17:49:50 +0000628 crop_indexes=GetCacheViewAuthenticIndexQueue(crop_view);
629 (void) CopyMagickMemory(q,p,(size_t) crop_image->columns*sizeof(*q));
630 if ((indexes != (IndexPacket *) NULL) &&
631 (crop_indexes != (IndexPacket *) NULL))
632 (void) CopyMagickMemory(crop_indexes,indexes,(size_t) crop_image->columns*
633 sizeof(*crop_indexes));
cristy3ed852e2009-09-05 21:47:34 +0000634 if (SyncCacheViewAuthenticPixels(crop_view,exception) == MagickFalse)
635 status=MagickFalse;
636 if (image->progress_monitor != (MagickProgressMonitor) NULL)
637 {
638 MagickBooleanType
639 proceed;
640
cristyb5d5f722009-11-04 03:03:49 +0000641#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +0000642 #pragma omp critical (MagickCore_CropImage)
643#endif
644 proceed=SetImageProgress(image,CropImageTag,progress++,image->rows);
645 if (proceed == MagickFalse)
646 status=MagickFalse;
647 }
648 }
649 crop_view=DestroyCacheView(crop_view);
650 image_view=DestroyCacheView(image_view);
651 crop_image->type=image->type;
652 if (status == MagickFalse)
653 crop_image=DestroyImage(crop_image);
654 return(crop_image);
655}
656
657/*
658%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
659% %
660% %
661% %
662% E x c e r p t I m a g e %
663% %
664% %
665% %
666%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
667%
668% ExcerptImage() returns a excerpt of the image as defined by the geometry.
669%
670% The format of the ExcerptImage method is:
671%
672% Image *ExcerptImage(const Image *image,const RectangleInfo *geometry,
673% ExceptionInfo *exception)
674%
675% A description of each parameter follows:
676%
677% o image: the image.
678%
679% o geometry: Define the region of the image to extend with members
680% x, y, width, and height.
681%
682% o exception: return any errors or warnings in this structure.
683%
684*/
685MagickExport Image *ExcerptImage(const Image *image,
686 const RectangleInfo *geometry,ExceptionInfo *exception)
687{
688#define ExcerptImageTag "Excerpt/Image"
689
cristyc4c8d132010-01-07 01:58:38 +0000690 CacheView
691 *excerpt_view,
692 *image_view;
693
cristy3ed852e2009-09-05 21:47:34 +0000694 Image
695 *excerpt_image;
696
697 long
698 progress,
699 y;
700
701 MagickBooleanType
702 status;
703
cristy3ed852e2009-09-05 21:47:34 +0000704 /*
705 Allocate excerpt image.
706 */
707 assert(image != (const Image *) NULL);
708 assert(image->signature == MagickSignature);
709 if (image->debug != MagickFalse)
710 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
711 assert(geometry != (const RectangleInfo *) NULL);
712 assert(exception != (ExceptionInfo *) NULL);
713 assert(exception->signature == MagickSignature);
714 excerpt_image=CloneImage(image,geometry->width,geometry->height,MagickTrue,
715 exception);
716 if (excerpt_image == (Image *) NULL)
717 return((Image *) NULL);
718 /*
719 Excerpt each row.
720 */
721 status=MagickTrue;
722 progress=0;
723 image_view=AcquireCacheView(image);
724 excerpt_view=AcquireCacheView(excerpt_image);
cristyb5d5f722009-11-04 03:03:49 +0000725#if defined(MAGICKCORE_OPENMP_SUPPORT)
726 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000727#endif
728 for (y=0; y < (long) excerpt_image->rows; y++)
729 {
730 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000731 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000732
733 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000734 *restrict excerpt_indexes,
735 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +0000736
737 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000738 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000739
740 if (status == MagickFalse)
741 continue;
742 p=GetCacheViewVirtualPixels(image_view,geometry->x,geometry->y+y,
743 geometry->width,1,exception);
744 q=GetCacheViewAuthenticPixels(excerpt_view,0,y,excerpt_image->columns,1,
745 exception);
746 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
747 {
748 status=MagickFalse;
749 continue;
750 }
751 (void) CopyMagickMemory(q,p,(size_t) excerpt_image->columns*sizeof(*q));
752 indexes=GetCacheViewAuthenticIndexQueue(image_view);
753 if (indexes != (IndexPacket *) NULL)
754 {
755 excerpt_indexes=GetCacheViewAuthenticIndexQueue(excerpt_view);
756 if (excerpt_indexes != (IndexPacket *) NULL)
757 (void) CopyMagickMemory(excerpt_indexes,indexes,(size_t)
758 excerpt_image->columns*sizeof(*excerpt_indexes));
759 }
760 if (SyncCacheViewAuthenticPixels(excerpt_view,exception) == MagickFalse)
761 status=MagickFalse;
762 if (image->progress_monitor != (MagickProgressMonitor) NULL)
763 {
764 MagickBooleanType
765 proceed;
766
cristyb5d5f722009-11-04 03:03:49 +0000767#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +0000768 #pragma omp critical (MagickCore_ExcerptImage)
769#endif
770 proceed=SetImageProgress(image,ExcerptImageTag,progress++,image->rows);
771 if (proceed == MagickFalse)
772 status=MagickFalse;
773 }
774 }
775 excerpt_view=DestroyCacheView(excerpt_view);
776 image_view=DestroyCacheView(image_view);
777 excerpt_image->type=image->type;
778 if (status == MagickFalse)
779 excerpt_image=DestroyImage(excerpt_image);
780 return(excerpt_image);
781}
782
783/*
784%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
785% %
786% %
787% %
788% E x t e n t I m a g e %
789% %
790% %
791% %
792%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
793%
794% ExtentImage() extends the image as defined by the geometry, gravity, and
795% image background color. Set the (x,y) offset of the geometry to move the
796% original image relative to the extended image.
797%
798% The format of the ExtentImage method is:
799%
800% Image *ExtentImage(const Image *image,const RectangleInfo *geometry,
801% ExceptionInfo *exception)
802%
803% A description of each parameter follows:
804%
805% o image: the image.
806%
807% o geometry: Define the region of the image to extend with members
808% x, y, width, and height.
809%
810% o exception: return any errors or warnings in this structure.
811%
812*/
813MagickExport Image *ExtentImage(const Image *image,
814 const RectangleInfo *geometry,ExceptionInfo *exception)
815{
816 Image
817 *extent_image;
818
819 /*
820 Allocate extent image.
821 */
822 assert(image != (const Image *) NULL);
823 assert(image->signature == MagickSignature);
824 if (image->debug != MagickFalse)
825 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
826 assert(geometry != (const RectangleInfo *) NULL);
827 assert(exception != (ExceptionInfo *) NULL);
828 assert(exception->signature == MagickSignature);
829 extent_image=CloneImage(image,geometry->width,geometry->height,MagickTrue,
830 exception);
831 if (extent_image == (Image *) NULL)
832 return((Image *) NULL);
833 if (SetImageStorageClass(extent_image,DirectClass) == MagickFalse)
834 {
835 InheritException(exception,&extent_image->exception);
836 extent_image=DestroyImage(extent_image);
837 return((Image *) NULL);
838 }
839 if (extent_image->background_color.opacity != OpaqueOpacity)
840 extent_image->matte=MagickTrue;
841 (void) SetImageBackgroundColor(extent_image);
842 (void) CompositeImage(extent_image,image->compose,image,geometry->x,
843 geometry->y);
844 return(extent_image);
845}
846
847/*
848%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
849% %
850% %
851% %
852% F l i p I m a g e %
853% %
854% %
855% %
856%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
857%
858% FlipImage() creates a vertical mirror image by reflecting the pixels
859% around the central x-axis.
860%
861% The format of the FlipImage method is:
862%
863% Image *FlipImage(const Image *image,ExceptionInfo *exception)
864%
865% A description of each parameter follows:
866%
867% o image: the image.
868%
869% o exception: return any errors or warnings in this structure.
870%
871*/
872MagickExport Image *FlipImage(const Image *image,ExceptionInfo *exception)
873{
874#define FlipImageTag "Flip/Image"
875
cristyc4c8d132010-01-07 01:58:38 +0000876 CacheView
877 *flip_view,
878 *image_view;
879
cristy3ed852e2009-09-05 21:47:34 +0000880 Image
881 *flip_image;
882
883 long
884 progress,
885 y;
886
887 MagickBooleanType
888 status;
889
cristy3ed852e2009-09-05 21:47:34 +0000890 assert(image != (const Image *) NULL);
891 assert(image->signature == MagickSignature);
892 if (image->debug != MagickFalse)
893 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
894 assert(exception != (ExceptionInfo *) NULL);
895 assert(exception->signature == MagickSignature);
896 flip_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
897 if (flip_image == (Image *) NULL)
898 return((Image *) NULL);
899 /*
900 Flip image.
901 */
902 status=MagickTrue;
903 progress=0;
904 image_view=AcquireCacheView(image);
905 flip_view=AcquireCacheView(flip_image);
cristyb5d5f722009-11-04 03:03:49 +0000906#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy00f95372010-02-13 16:39:29 +0000907 #pragma omp parallel for schedule(static) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000908#endif
909 for (y=0; y < (long) flip_image->rows; y++)
910 {
911 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000912 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +0000913
914 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000915 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000916
917 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000918 *restrict flip_indexes;
cristy3ed852e2009-09-05 21:47:34 +0000919
920 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000921 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000922
923 if (status == MagickFalse)
924 continue;
925 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
926 q=QueueCacheViewAuthenticPixels(flip_view,0,(long) (flip_image->rows-y-1),
927 flip_image->columns,1,exception);
928 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
929 {
930 status=MagickFalse;
931 continue;
932 }
933 (void) CopyMagickMemory(q,p,(size_t) image->columns*sizeof(*q));
934 indexes=GetCacheViewVirtualIndexQueue(image_view);
935 if (indexes != (const IndexPacket *) NULL)
936 {
937 flip_indexes=GetCacheViewAuthenticIndexQueue(flip_view);
938 if (flip_indexes != (IndexPacket *) NULL)
939 (void) CopyMagickMemory(flip_indexes,indexes,(size_t) image->columns*
940 sizeof(*flip_indexes));
941 }
942 if (SyncCacheViewAuthenticPixels(flip_view,exception) == MagickFalse)
943 status=MagickFalse;
944 if (image->progress_monitor != (MagickProgressMonitor) NULL)
945 {
946 MagickBooleanType
947 proceed;
948
cristyb5d5f722009-11-04 03:03:49 +0000949#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +0000950 #pragma omp critical (MagickCore_FlipImage)
951#endif
952 proceed=SetImageProgress(image,FlipImageTag,progress++,image->rows);
953 if (proceed == MagickFalse)
954 status=MagickFalse;
955 }
956 }
957 flip_view=DestroyCacheView(flip_view);
958 image_view=DestroyCacheView(image_view);
959 flip_image->type=image->type;
960 if (status == MagickFalse)
961 flip_image=DestroyImage(flip_image);
962 return(flip_image);
963}
964
965/*
966%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
967% %
968% %
969% %
970% F l o p I m a g e %
971% %
972% %
973% %
974%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
975%
976% FlopImage() creates a horizontal mirror image by reflecting the pixels
977% around the central y-axis.
978%
979% The format of the FlopImage method is:
980%
981% Image *FlopImage(const Image *image,ExceptionInfo *exception)
982%
983% A description of each parameter follows:
984%
985% o image: the image.
986%
987% o exception: return any errors or warnings in this structure.
988%
989*/
990MagickExport Image *FlopImage(const Image *image,ExceptionInfo *exception)
991{
992#define FlopImageTag "Flop/Image"
993
cristyc4c8d132010-01-07 01:58:38 +0000994 CacheView
995 *flop_view,
996 *image_view;
997
cristy3ed852e2009-09-05 21:47:34 +0000998 Image
999 *flop_image;
1000
1001 long
1002 progress,
1003 y;
1004
1005 MagickBooleanType
1006 status;
1007
cristy3ed852e2009-09-05 21:47:34 +00001008 assert(image != (const Image *) NULL);
1009 assert(image->signature == MagickSignature);
1010 if (image->debug != MagickFalse)
1011 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1012 assert(exception != (ExceptionInfo *) NULL);
1013 assert(exception->signature == MagickSignature);
1014 flop_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
1015 if (flop_image == (Image *) NULL)
1016 return((Image *) NULL);
1017 /*
1018 Flop each row.
1019 */
1020 status=MagickTrue;
1021 progress=0;
1022 image_view=AcquireCacheView(image);
1023 flop_view=AcquireCacheView(flop_image);
cristyb5d5f722009-11-04 03:03:49 +00001024#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy00f95372010-02-13 16:39:29 +00001025 #pragma omp parallel for schedule(static) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00001026#endif
1027 for (y=0; y < (long) flop_image->rows; y++)
1028 {
1029 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001030 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00001031
1032 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001033 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001034
1035 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001036 *restrict flop_indexes;
cristy3ed852e2009-09-05 21:47:34 +00001037
1038 register long
1039 x;
1040
1041 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001042 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001043
1044 if (status == MagickFalse)
1045 continue;
1046 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1047 q=QueueCacheViewAuthenticPixels(flop_view,0,y,flop_image->columns,1,
1048 exception);
1049 if ((p == (PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
1050 {
1051 status=MagickFalse;
1052 continue;
1053 }
1054 q+=flop_image->columns;
1055 indexes=GetCacheViewVirtualIndexQueue(image_view);
1056 flop_indexes=GetCacheViewAuthenticIndexQueue(flop_view);
1057 for (x=0; x < (long) flop_image->columns; x++)
1058 {
1059 (*--q)=(*p++);
1060 if ((indexes != (const IndexPacket *) NULL) &&
1061 (flop_indexes != (IndexPacket *) NULL))
1062 flop_indexes[flop_image->columns-x-1]=indexes[x];
1063 }
1064 if (SyncCacheViewAuthenticPixels(flop_view,exception) == MagickFalse)
1065 status=MagickFalse;
1066 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1067 {
1068 MagickBooleanType
1069 proceed;
1070
cristyb5d5f722009-11-04 03:03:49 +00001071#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00001072 #pragma omp critical (MagickCore_FlopImage)
1073#endif
1074 proceed=SetImageProgress(image,FlopImageTag,progress++,image->rows);
1075 if (proceed == MagickFalse)
1076 status=MagickFalse;
1077 }
1078 }
1079 flop_view=DestroyCacheView(flop_view);
1080 image_view=DestroyCacheView(image_view);
1081 flop_image->type=image->type;
1082 if (status == MagickFalse)
1083 flop_image=DestroyImage(flop_image);
1084 return(flop_image);
1085}
1086
1087/*
1088%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1089% %
1090% %
1091% %
1092% R o l l I m a g e %
1093% %
1094% %
1095% %
1096%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1097%
1098% RollImage() offsets an image as defined by x_offset and y_offset.
1099%
1100% The format of the RollImage method is:
1101%
1102% Image *RollImage(const Image *image,const long x_offset,
1103% const long y_offset,ExceptionInfo *exception)
1104%
1105% A description of each parameter follows:
1106%
1107% o image: the image.
1108%
1109% o x_offset: the number of columns to roll in the horizontal direction.
1110%
1111% o y_offset: the number of rows to roll in the vertical direction.
1112%
1113% o exception: return any errors or warnings in this structure.
1114%
1115*/
1116
1117static inline MagickBooleanType CopyImageRegion(Image *destination,
1118 const Image *source,const unsigned long columns,const unsigned long rows,
1119 const long sx,const long sy,const long dx,const long dy,
1120 ExceptionInfo *exception)
1121{
cristyc4c8d132010-01-07 01:58:38 +00001122 CacheView
1123 *source_view,
1124 *destination_view;
1125
cristy3ed852e2009-09-05 21:47:34 +00001126 long
1127 y;
1128
1129 MagickBooleanType
1130 status;
1131
cristy3ed852e2009-09-05 21:47:34 +00001132 status=MagickTrue;
1133 source_view=AcquireCacheView(source);
1134 destination_view=AcquireCacheView(destination);
cristyb5d5f722009-11-04 03:03:49 +00001135#if defined(MAGICKCORE_OPENMP_SUPPORT)
1136 #pragma omp parallel for schedule(dynamic,4) shared(status)
cristy3ed852e2009-09-05 21:47:34 +00001137#endif
1138 for (y=0; y < (long) rows; y++)
1139 {
1140 MagickBooleanType
1141 sync;
1142
1143 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001144 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001145
1146 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001147 *restrict indexes,
1148 *restrict destination_indexes;
cristy3ed852e2009-09-05 21:47:34 +00001149
1150 register long
1151 x;
1152
1153 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001154 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001155
1156 /*
1157 Transfer scanline.
1158 */
1159 if (status == MagickFalse)
1160 continue;
1161 p=GetCacheViewVirtualPixels(source_view,sx,sy+y,columns,1,exception);
1162 q=GetCacheViewAuthenticPixels(destination_view,dx,dy+y,columns,1,exception);
1163 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
1164 {
1165 status=MagickFalse;
1166 continue;
1167 }
1168 indexes=GetCacheViewAuthenticIndexQueue(source_view);
1169 for (x=0; x < (long) columns; x++)
1170 *q++=(*p++);
1171 if (indexes != (IndexPacket *) NULL)
1172 {
1173 destination_indexes=GetCacheViewAuthenticIndexQueue(destination_view);
1174 for (x=0; x < (long) columns; x++)
1175 destination_indexes[x]=indexes[x];
1176 }
1177 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
1178 if (sync == MagickFalse)
1179 status=MagickFalse;
1180 }
1181 destination_view=DestroyCacheView(destination_view);
1182 source_view=DestroyCacheView(source_view);
1183 return(status);
1184}
1185
1186MagickExport Image *RollImage(const Image *image,const long x_offset,
1187 const long y_offset,ExceptionInfo *exception)
1188{
1189#define RollImageTag "Roll/Image"
1190
1191 Image
1192 *roll_image;
1193
1194 MagickStatusType
1195 status;
1196
1197 RectangleInfo
1198 offset;
1199
1200 /*
1201 Initialize roll image attributes.
1202 */
1203 assert(image != (const Image *) NULL);
1204 assert(image->signature == MagickSignature);
1205 if (image->debug != MagickFalse)
1206 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1207 assert(exception != (ExceptionInfo *) NULL);
1208 assert(exception->signature == MagickSignature);
1209 roll_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
1210 if (roll_image == (Image *) NULL)
1211 return((Image *) NULL);
1212 offset.x=x_offset;
1213 offset.y=y_offset;
1214 while (offset.x < 0)
1215 offset.x+=image->columns;
1216 while (offset.x >= (long) image->columns)
1217 offset.x-=image->columns;
1218 while (offset.y < 0)
1219 offset.y+=image->rows;
1220 while (offset.y >= (long) image->rows)
1221 offset.y-=image->rows;
1222 /*
1223 Roll image.
1224 */
1225 status=CopyImageRegion(roll_image,image,(unsigned long) offset.x,
1226 (unsigned long) offset.y,(long) image->columns-offset.x,(long) image->rows-
1227 offset.y,0,0,exception);
1228 (void) SetImageProgress(image,RollImageTag,0,3);
1229 status|=CopyImageRegion(roll_image,image,image->columns-offset.x,
1230 (unsigned long) offset.y,0,(long) image->rows-offset.y,offset.x,0,
1231 exception);
1232 (void) SetImageProgress(image,RollImageTag,1,3);
1233 status|=CopyImageRegion(roll_image,image,(unsigned long) offset.x,image->rows-
1234 offset.y,(long) image->columns-offset.x,0,0,offset.y,exception);
1235 (void) SetImageProgress(image,RollImageTag,2,3);
1236 status|=CopyImageRegion(roll_image,image,image->columns-offset.x,image->rows-
1237 offset.y,0,0,offset.x,offset.y,exception);
1238 (void) SetImageProgress(image,RollImageTag,3,3);
1239 roll_image->type=image->type;
1240 if (status == MagickFalse)
1241 roll_image=DestroyImage(roll_image);
1242 return(roll_image);
1243}
1244
1245/*
1246%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1247% %
1248% %
1249% %
1250% S h a v e I m a g e %
1251% %
1252% %
1253% %
1254%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1255%
1256% ShaveImage() shaves pixels from the image edges. It allocates the memory
1257% necessary for the new Image structure and returns a pointer to the new
1258% image.
1259%
1260% The format of the ShaveImage method is:
1261%
1262% Image *ShaveImage(const Image *image,const RectangleInfo *shave_info,
1263% ExceptionInfo *exception)
1264%
1265% A description of each parameter follows:
1266%
1267% o shave_image: Method ShaveImage returns a pointer to the shaved
1268% image. A null image is returned if there is a memory shortage or
1269% if the image width or height is zero.
1270%
1271% o image: the image.
1272%
1273% o shave_info: Specifies a pointer to a RectangleInfo which defines the
1274% region of the image to crop.
1275%
1276% o exception: return any errors or warnings in this structure.
1277%
1278*/
1279MagickExport Image *ShaveImage(const Image *image,
1280 const RectangleInfo *shave_info,ExceptionInfo *exception)
1281{
1282 Image
1283 *shave_image;
1284
1285 RectangleInfo
1286 geometry;
1287
1288 assert(image != (const Image *) NULL);
1289 assert(image->signature == MagickSignature);
1290 if (image->debug != MagickFalse)
1291 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1292 if (((2*shave_info->width) >= image->columns) ||
1293 ((2*shave_info->height) >= image->rows))
1294 ThrowImageException(OptionWarning,"GeometryDoesNotContainImage");
1295 SetGeometry(image,&geometry);
1296 geometry.width-=2*shave_info->width;
1297 geometry.height-=2*shave_info->height;
1298 geometry.x=(long) shave_info->width+image->page.x;
1299 geometry.y=(long) shave_info->height+image->page.y;
1300 shave_image=CropImage(image,&geometry,exception);
1301 if (shave_image == (Image *) NULL)
1302 return((Image *) NULL);
1303 shave_image->page.width-=2*shave_info->width;
1304 shave_image->page.height-=2*shave_info->height;
1305 shave_image->page.x-=shave_info->width;
1306 shave_image->page.y-=shave_info->height;
1307 return(shave_image);
1308}
1309
1310/*
1311%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1312% %
1313% %
1314% %
1315% S p l i c e I m a g e %
1316% %
1317% %
1318% %
1319%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1320%
1321% SpliceImage() splices a solid color into the image as defined by the
1322% geometry.
1323%
1324% The format of the SpliceImage method is:
1325%
1326% Image *SpliceImage(const Image *image,const RectangleInfo *geometry,
1327% ExceptionInfo *exception)
1328%
1329% A description of each parameter follows:
1330%
1331% o image: the image.
1332%
1333% o geometry: Define the region of the image to splice with members
1334% x, y, width, and height.
1335%
1336% o exception: return any errors or warnings in this structure.
1337%
1338*/
1339MagickExport Image *SpliceImage(const Image *image,
1340 const RectangleInfo *geometry,ExceptionInfo *exception)
1341{
1342#define SpliceImageTag "Splice/Image"
1343
cristyc4c8d132010-01-07 01:58:38 +00001344 CacheView
1345 *image_view,
1346 *splice_view;
1347
cristy3ed852e2009-09-05 21:47:34 +00001348 Image
1349 *splice_image;
1350
1351 long
1352 progress,
1353 y;
1354
1355 MagickBooleanType
1356 proceed,
1357 status;
1358
1359 RectangleInfo
1360 splice_geometry;
1361
1362 register long
1363 i;
1364
cristy3ed852e2009-09-05 21:47:34 +00001365 /*
1366 Allocate splice image.
1367 */
1368 assert(image != (const Image *) NULL);
1369 assert(image->signature == MagickSignature);
1370 if (image->debug != MagickFalse)
1371 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1372 assert(geometry != (const RectangleInfo *) NULL);
1373 assert(exception != (ExceptionInfo *) NULL);
1374 assert(exception->signature == MagickSignature);
1375 splice_geometry=(*geometry);
1376 splice_image=CloneImage(image,image->columns+splice_geometry.width,
1377 image->rows+splice_geometry.height,MagickTrue,exception);
1378 if (splice_image == (Image *) NULL)
1379 return((Image *) NULL);
1380 if (SetImageStorageClass(splice_image,DirectClass) == MagickFalse)
1381 {
1382 InheritException(exception,&splice_image->exception);
1383 splice_image=DestroyImage(splice_image);
1384 return((Image *) NULL);
1385 }
1386 (void) SetImageBackgroundColor(splice_image);
1387 /*
1388 Respect image geometry.
1389 */
1390 switch (image->gravity)
1391 {
1392 default:
1393 case UndefinedGravity:
1394 case NorthWestGravity:
1395 break;
1396 case NorthGravity:
1397 {
1398 splice_geometry.x+=splice_geometry.width/2;
1399 break;
1400 }
1401 case NorthEastGravity:
1402 {
1403 splice_geometry.x+=splice_geometry.width;
1404 break;
1405 }
1406 case WestGravity:
1407 {
1408 splice_geometry.y+=splice_geometry.width/2;
1409 break;
1410 }
1411 case StaticGravity:
1412 case CenterGravity:
1413 {
1414 splice_geometry.x+=splice_geometry.width/2;
1415 splice_geometry.y+=splice_geometry.height/2;
1416 break;
1417 }
1418 case EastGravity:
1419 {
1420 splice_geometry.x+=splice_geometry.width;
1421 splice_geometry.y+=splice_geometry.height/2;
1422 break;
1423 }
1424 case SouthWestGravity:
1425 {
1426 splice_geometry.y+=splice_geometry.height;
1427 break;
1428 }
1429 case SouthGravity:
1430 {
1431 splice_geometry.x+=splice_geometry.width/2;
1432 splice_geometry.y+=splice_geometry.height;
1433 break;
1434 }
1435 case SouthEastGravity:
1436 {
1437 splice_geometry.x+=splice_geometry.width;
1438 splice_geometry.y+=splice_geometry.height;
1439 break;
1440 }
1441 }
1442 /*
1443 Splice image.
1444 */
1445 status=MagickTrue;
1446 i=0;
1447 progress=0;
1448 image_view=AcquireCacheView(image);
1449 splice_view=AcquireCacheView(splice_image);
cristyb5d5f722009-11-04 03:03:49 +00001450#if defined(MAGICKCORE_OPENMP_SUPPORT)
1451 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00001452#endif
1453 for (y=0; y < (long) splice_geometry.y; y++)
1454 {
1455 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001456 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001457
1458 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001459 *restrict indexes,
1460 *restrict splice_indexes;
cristy3ed852e2009-09-05 21:47:34 +00001461
1462 register long
1463 x;
1464
1465 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001466 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001467
1468 if (status == MagickFalse)
1469 continue;
1470 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1471 q=QueueCacheViewAuthenticPixels(splice_view,0,y,splice_image->columns,1,
1472 exception);
1473 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
1474 {
1475 status=MagickFalse;
1476 continue;
1477 }
1478 indexes=GetCacheViewAuthenticIndexQueue(image_view);
1479 splice_indexes=GetCacheViewAuthenticIndexQueue(splice_view);
1480 for (x=0; x < splice_geometry.x; x++)
1481 {
cristyce70c172010-01-07 17:15:30 +00001482 SetRedPixelComponent(q,GetRedPixelComponent(p));
1483 SetGreenPixelComponent(q,GetGreenPixelComponent(p));
1484 SetBluePixelComponent(q,GetBluePixelComponent(p));
1485 SetOpacityPixelComponent(q,OpaqueOpacity);
cristy3ed852e2009-09-05 21:47:34 +00001486 if (image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00001487 SetOpacityPixelComponent(q,GetOpacityPixelComponent(p));
cristy3ed852e2009-09-05 21:47:34 +00001488 if (image->colorspace == CMYKColorspace)
1489 splice_indexes[x]=(*indexes++);
1490 p++;
1491 q++;
1492 }
1493 for ( ; x < (long) (splice_geometry.x+splice_geometry.width); x++)
1494 q++;
1495 for ( ; x < (long) splice_image->columns; x++)
1496 {
cristyce70c172010-01-07 17:15:30 +00001497 SetRedPixelComponent(q,GetRedPixelComponent(p));
1498 SetGreenPixelComponent(q,GetGreenPixelComponent(p));
1499 SetBluePixelComponent(q,GetBluePixelComponent(p));
1500 SetOpacityPixelComponent(q,OpaqueOpacity);
cristy3ed852e2009-09-05 21:47:34 +00001501 if (image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00001502 SetOpacityPixelComponent(q,GetOpacityPixelComponent(p));
cristy3ed852e2009-09-05 21:47:34 +00001503 if (image->colorspace == CMYKColorspace)
1504 splice_indexes[x]=(*indexes++);
1505 p++;
1506 q++;
1507 }
1508 if (SyncCacheViewAuthenticPixels(splice_view,exception) == MagickFalse)
1509 status=MagickFalse;
1510 proceed=SetImageProgress(image,SpliceImageTag,y,splice_image->rows);
1511 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1512 {
1513 MagickBooleanType
1514 proceed;
1515
cristyb5d5f722009-11-04 03:03:49 +00001516#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00001517 #pragma omp critical (MagickCore_TransposeImage)
1518#endif
1519 proceed=SetImageProgress(image,SpliceImageTag,progress++,
1520 splice_image->rows);
1521 if (proceed == MagickFalse)
1522 status=MagickFalse;
1523 }
1524 }
cristyb5d5f722009-11-04 03:03:49 +00001525#if defined(MAGICKCORE_OPENMP_SUPPORT)
1526 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00001527#endif
1528 for (y=(long) (splice_geometry.y+splice_geometry.height);
1529 y < (long) splice_image->rows; y++)
1530 {
1531 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001532 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001533
1534 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001535 *restrict indexes,
1536 *restrict splice_indexes;
cristy3ed852e2009-09-05 21:47:34 +00001537
1538 register long
1539 x;
1540
1541 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001542 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001543
1544 if (status == MagickFalse)
1545 continue;
1546 p=GetCacheViewVirtualPixels(image_view,0,y-splice_geometry.height,
1547 image->columns,1,exception);
1548 q=QueueCacheViewAuthenticPixels(splice_view,0,y,splice_image->columns,1,
1549 exception);
1550 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
1551 {
1552 status=MagickFalse;
1553 continue;
1554 }
1555 indexes=GetCacheViewAuthenticIndexQueue(image_view);
1556 splice_indexes=GetCacheViewAuthenticIndexQueue(splice_view);
1557 for (x=0; x < splice_geometry.x; x++)
1558 {
cristyce70c172010-01-07 17:15:30 +00001559 SetRedPixelComponent(q,GetRedPixelComponent(p));
1560 SetGreenPixelComponent(q,GetGreenPixelComponent(p));
1561 SetBluePixelComponent(q,GetBluePixelComponent(p));
1562 SetOpacityPixelComponent(q,OpaqueOpacity);
cristy3ed852e2009-09-05 21:47:34 +00001563 if (image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00001564 SetOpacityPixelComponent(q,GetOpacityPixelComponent(p));
cristy3ed852e2009-09-05 21:47:34 +00001565 if (image->colorspace == CMYKColorspace)
1566 splice_indexes[x]=(*indexes++);
1567 p++;
1568 q++;
1569 }
1570 for ( ; x < (long) (splice_geometry.x+splice_geometry.width); x++)
1571 q++;
1572 for ( ; x < (long) splice_image->columns; x++)
1573 {
cristyce70c172010-01-07 17:15:30 +00001574 SetRedPixelComponent(q,GetRedPixelComponent(p));
1575 SetGreenPixelComponent(q,GetGreenPixelComponent(p));
1576 SetBluePixelComponent(q,GetBluePixelComponent(p));
1577 SetOpacityPixelComponent(q,OpaqueOpacity);
cristy3ed852e2009-09-05 21:47:34 +00001578 if (image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00001579 SetOpacityPixelComponent(q,GetOpacityPixelComponent(p));
cristy3ed852e2009-09-05 21:47:34 +00001580 if (image->colorspace == CMYKColorspace)
1581 splice_indexes[x]=(*indexes++);
1582 p++;
1583 q++;
1584 }
1585 if (SyncCacheViewAuthenticPixels(splice_view,exception) == MagickFalse)
1586 status=MagickFalse;
1587 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1588 {
1589 MagickBooleanType
1590 proceed;
1591
cristyb5d5f722009-11-04 03:03:49 +00001592#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00001593 #pragma omp critical (MagickCore_TransposeImage)
1594#endif
1595 proceed=SetImageProgress(image,SpliceImageTag,progress++,
1596 splice_image->rows);
1597 if (proceed == MagickFalse)
1598 status=MagickFalse;
1599 }
1600 }
1601 splice_view=DestroyCacheView(splice_view);
1602 image_view=DestroyCacheView(image_view);
1603 if (status == MagickFalse)
1604 splice_image=DestroyImage(splice_image);
1605 return(splice_image);
1606}
1607
1608/*
1609%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1610% %
1611% %
1612% %
1613% T r a n s f o r m I m a g e %
1614% %
1615% %
1616% %
1617%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1618%
1619% TransformImage() is a convenience method that behaves like ResizeImage() or
1620% CropImage() but accepts scaling and/or cropping information as a region
1621% geometry specification. If the operation fails, the original image handle
1622% is returned.
1623%
1624% The format of the TransformImage method is:
1625%
1626% MagickBooleanType TransformImage(Image **image,const char *crop_geometry,
1627% const char *image_geometry)
1628%
1629% A description of each parameter follows:
1630%
1631% o image: the image The transformed image is returned as this parameter.
1632%
1633% o crop_geometry: A crop geometry string. This geometry defines a
1634% subregion of the image to crop.
1635%
1636% o image_geometry: An image geometry string. This geometry defines the
1637% final size of the image.
1638%
1639*/
cristy508d9312010-02-10 21:10:30 +00001640static inline long MagickRound(MagickRealType x)
anthonyc0429ed2009-12-27 08:22:42 +00001641{
cristy508d9312010-02-10 21:10:30 +00001642 /*
1643 Round the fraction to nearest integer.
1644 */
anthonyc0429ed2009-12-27 08:22:42 +00001645 if (x >= 0.0)
cristy508d9312010-02-10 21:10:30 +00001646 return((long) (x+0.5));
1647 return((long) (x-0.5));
anthonyc0429ed2009-12-27 08:22:42 +00001648}
1649
cristy3ed852e2009-09-05 21:47:34 +00001650MagickExport MagickBooleanType TransformImage(Image **image,
1651 const char *crop_geometry,const char *image_geometry)
1652{
1653 Image
cristy508d9312010-02-10 21:10:30 +00001654 *next,
cristy3ed852e2009-09-05 21:47:34 +00001655 *resize_image,
1656 *transform_image;
1657
cristy508d9312010-02-10 21:10:30 +00001658 long
1659 x,
1660 y;
1661
cristy3ed852e2009-09-05 21:47:34 +00001662 MagickStatusType
1663 flags;
1664
1665 RectangleInfo
1666 geometry;
1667
cristy508d9312010-02-10 21:10:30 +00001668 unsigned long
1669 height,
1670 width;
1671
cristy3ed852e2009-09-05 21:47:34 +00001672 assert(image != (Image **) NULL);
1673 assert((*image)->signature == MagickSignature);
1674 if ((*image)->debug != MagickFalse)
1675 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
1676 transform_image=(*image);
1677 if (crop_geometry != (const char *) NULL)
1678 {
1679 Image
1680 *crop_image;
1681
1682 RectangleInfo
1683 geometry;
1684
1685 /*
1686 Crop image to a user specified size.
1687 */
1688 crop_image=NewImageList();
1689 flags=ParseGravityGeometry(transform_image,crop_geometry,&geometry,
1690 &(*image)->exception);
cristy508d9312010-02-10 21:10:30 +00001691 if ((flags & AreaValue) != 0)
anthonyc0429ed2009-12-27 08:22:42 +00001692 {
cristy508d9312010-02-10 21:10:30 +00001693 PointInfo
cristy4eced732010-02-10 22:26:28 +00001694 delta,
1695 offset;
anthonyc0429ed2009-12-27 08:22:42 +00001696
1697 RectangleInfo
1698 crop;
1699
cristy508d9312010-02-10 21:10:30 +00001700 /*
1701 Crop into NxM tiles (@ flag) - AT.
1702 */
1703 if (geometry.width == 0)
1704 geometry.width=1;
1705 if (geometry.height == 0)
1706 geometry.height=1;
1707 width=transform_image->columns;
1708 height=transform_image->rows;
anthonyc0429ed2009-12-27 08:22:42 +00001709 if ((flags & AspectValue) == 0)
1710 {
cristy508d9312010-02-10 21:10:30 +00001711 width-=(geometry.x < 0 ? -1 : 1)*geometry.x;
1712 height-=(geometry.y < 0 ? -1 : 1)*geometry.y;
anthonyc0429ed2009-12-27 08:22:42 +00001713 }
1714 else
1715 {
cristy508d9312010-02-10 21:10:30 +00001716 width+=(geometry.x < 0 ? -1 : 1)*geometry.x;
1717 height+=(geometry.y < 0 ? -1 : 1)*geometry.y;
anthonyc0429ed2009-12-27 08:22:42 +00001718 }
cristy508d9312010-02-10 21:10:30 +00001719 delta.x=(double) width/geometry.width;
1720 delta.y=(double) height/geometry.height;
anthonyc0429ed2009-12-27 08:22:42 +00001721 next=NewImageList();
cristy4eced732010-02-10 22:26:28 +00001722 for (offset.y=0; offset.y < (double) height; )
anthonyc0429ed2009-12-27 08:22:42 +00001723 {
1724 if ((flags & AspectValue) == 0)
1725 {
cristy4eced732010-02-10 22:26:28 +00001726 crop.y=(long) MagickRound((MagickRealType) (offset.y-
cristy508d9312010-02-10 21:10:30 +00001727 (geometry.y > 0 ? 0 : geometry.y)));
cristy4eced732010-02-10 22:26:28 +00001728 offset.y+=delta.y;
cristy508d9312010-02-10 21:10:30 +00001729 crop.height=(unsigned long) MagickRound((MagickRealType)
cristy4eced732010-02-10 22:26:28 +00001730 (offset.y+(geometry.y < 0 ? 0 : geometry.y)));
anthonyc0429ed2009-12-27 08:22:42 +00001731 }
1732 else
1733 {
cristy4eced732010-02-10 22:26:28 +00001734 crop.y=(long) MagickRound((MagickRealType) (offset.y-
cristy508d9312010-02-10 21:10:30 +00001735 (geometry.y > 0 ? geometry.y : 0)));
cristy4eced732010-02-10 22:26:28 +00001736 offset.y+=delta.y;
cristy508d9312010-02-10 21:10:30 +00001737 crop.height=(unsigned long) MagickRound((MagickRealType)
cristy4eced732010-02-10 22:26:28 +00001738 (offset.y+(geometry.y < 0 ? geometry.y : 0)));
anthonyc0429ed2009-12-27 08:22:42 +00001739 }
cristy508d9312010-02-10 21:10:30 +00001740 crop.height-=crop.y;
cristy4eced732010-02-10 22:26:28 +00001741 for (offset.x=0; offset.x < (double) width; )
anthonyc0429ed2009-12-27 08:22:42 +00001742 {
1743 if ((flags & AspectValue) == 0)
1744 {
cristy4eced732010-02-10 22:26:28 +00001745 crop.x=(long) MagickRound((MagickRealType) (offset.x-
cristy508d9312010-02-10 21:10:30 +00001746 (geometry.x > 0 ? 0 : geometry.x)));
cristy4eced732010-02-10 22:26:28 +00001747 offset.x+=+delta.x;
cristy508d9312010-02-10 21:10:30 +00001748 crop.width=(unsigned long) MagickRound((MagickRealType)
cristy4eced732010-02-10 22:26:28 +00001749 (offset.x+(geometry.x < 0 ? 0 : geometry.x)));
anthonyc0429ed2009-12-27 08:22:42 +00001750 }
1751 else
1752 {
cristy4eced732010-02-10 22:26:28 +00001753 crop.x=(long) MagickRound((MagickRealType) (offset.x-
cristy508d9312010-02-10 21:10:30 +00001754 (geometry.x > 0 ? geometry.x : 0)));
cristy4eced732010-02-10 22:26:28 +00001755 offset.x+=+delta.x;
cristy508d9312010-02-10 21:10:30 +00001756 crop.width=(unsigned long) MagickRound((MagickRealType)
cristy4eced732010-02-10 22:26:28 +00001757 (offset.x+(geometry.x < 0 ? geometry.x : 0)));
anthonyc0429ed2009-12-27 08:22:42 +00001758 }
cristy508d9312010-02-10 21:10:30 +00001759 crop.width-=crop.x;
anthonyc0429ed2009-12-27 08:22:42 +00001760 next=CropImage(transform_image,&crop,&(*image)->exception);
1761 if (next == (Image *) NULL)
1762 break;
1763 AppendImageToList(&crop_image,next);
1764 }
1765 if (next == (Image *) NULL)
1766 break;
1767 }
1768 }
cristy508d9312010-02-10 21:10:30 +00001769 else
1770 if (((geometry.width == 0) && (geometry.height == 0)) ||
anthonyc0429ed2009-12-27 08:22:42 +00001771 ((flags & XValue) != 0) || ((flags & YValue) != 0))
cristy3ed852e2009-09-05 21:47:34 +00001772 {
cristy508d9312010-02-10 21:10:30 +00001773 /*
1774 Crop a single region at +X+Y.
1775 */
1776 crop_image=CropImage(transform_image,&geometry,
1777 &(*image)->exception);
1778 if ((crop_image != (Image *) NULL) && ((flags & AspectValue) != 0))
1779 {
1780 crop_image->page.width=geometry.width;
1781 crop_image->page.height=geometry.height;
1782 crop_image->page.x-=geometry.x;
1783 crop_image->page.y-=geometry.y;
1784 }
1785 }
1786 else
1787 if ((transform_image->columns > geometry.width) ||
1788 (transform_image->rows > geometry.height))
1789 {
1790 MagickBooleanType
1791 proceed;
1792
1793 MagickProgressMonitor
1794 progress_monitor;
1795
1796 MagickOffsetType
1797 i;
1798
1799 MagickSizeType
1800 number_images;
1801
1802 /*
1803 Crop into tiles of fixed size WxH.
1804 */
1805 if (transform_image->page.width == 0)
1806 transform_image->page.width=transform_image->columns;
1807 if (transform_image->page.height == 0)
1808 transform_image->page.height=transform_image->rows;
1809 width=geometry.width;
1810 if (width == 0)
1811 width=transform_image->page.width;
1812 height=geometry.height;
1813 if (height == 0)
1814 height=transform_image->page.height;
1815 next=NewImageList();
1816 proceed=MagickTrue;
1817 i=0;
cristy0e9f9c12010-02-11 03:00:47 +00001818 number_images=0;
1819 for (y=0; y < (long) transform_image->page.height; y+=height)
1820 for (x=0; x < (long) transform_image->page.width; x+=width)
1821 number_images++;
cristy508d9312010-02-10 21:10:30 +00001822 for (y=0; y < (long) transform_image->page.height; y+=height)
1823 {
1824 for (x=0; x < (long) transform_image->page.width; x+=width)
1825 {
1826 progress_monitor=SetImageProgressMonitor(transform_image,
1827 (MagickProgressMonitor) NULL,transform_image->client_data);
1828 geometry.width=width;
1829 geometry.height=height;
1830 geometry.x=x;
1831 geometry.y=y;
1832 next=CropImage(transform_image,&geometry,&(*image)->exception);
1833 (void) SetImageProgressMonitor(transform_image,
1834 progress_monitor,transform_image->client_data);
1835 proceed=SetImageProgress(transform_image,CropImageTag,i++,
1836 number_images);
1837 if (proceed == MagickFalse)
1838 break;
1839 if (next == (Image *) NULL)
1840 break;
cristy36507e62010-02-11 00:26:48 +00001841 (void) SetImageProgressMonitor(next,progress_monitor,
1842 next->client_data);
cristy982af902010-02-15 19:13:14 +00001843 AppendImageToList(&crop_image,next);
cristy508d9312010-02-10 21:10:30 +00001844 }
1845 if (next == (Image *) NULL)
1846 break;
1847 if (proceed == MagickFalse)
1848 break;
1849 }
1850 }
cristy3ed852e2009-09-05 21:47:34 +00001851 if (crop_image == (Image *) NULL)
1852 transform_image=CloneImage(*image,0,0,MagickTrue,&(*image)->exception);
1853 else
1854 {
1855 transform_image=DestroyImage(transform_image);
1856 transform_image=GetFirstImageInList(crop_image);
1857 }
1858 *image=transform_image;
1859 }
1860 if (image_geometry == (const char *) NULL)
1861 return(MagickTrue);
1862 /*
1863 Scale image to a user specified size.
1864 */
1865 flags=ParseRegionGeometry(transform_image,image_geometry,&geometry,
1866 &(*image)->exception);
1867 if ((transform_image->columns == geometry.width) &&
1868 (transform_image->rows == geometry.height))
1869 return(MagickTrue);
1870 resize_image=ZoomImage(transform_image,geometry.width,geometry.height,
1871 &(*image)->exception);
1872 if (resize_image == (Image *) NULL)
1873 return(MagickFalse);
1874 transform_image=DestroyImage(transform_image);
1875 transform_image=resize_image;
1876 *image=transform_image;
1877 return(MagickTrue);
1878}
1879
1880/*
1881%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1882% %
1883% %
1884% %
1885% T r a n s f o r m I m a g e s %
1886% %
1887% %
1888% % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1889%
1890% TransformImages() calls TransformImage() on each image of a sequence.
1891%
1892% The format of the TransformImage method is:
1893%
1894% MagickBooleanType TransformImages(Image **image,
1895% const char *crop_geometry,const char *image_geometry)
1896%
1897% A description of each parameter follows:
1898%
1899% o image: the image The transformed image is returned as this parameter.
1900%
1901% o crop_geometry: A crop geometry string. This geometry defines a
1902% subregion of the image to crop.
1903%
1904% o image_geometry: An image geometry string. This geometry defines the
1905% final size of the image.
1906%
1907*/
1908MagickExport MagickBooleanType TransformImages(Image **images,
1909 const char *crop_geometry,const char *image_geometry)
1910{
1911 Image
1912 *image,
1913 **image_list,
1914 *transform_images;
1915
1916 MagickStatusType
1917 status;
1918
1919 register long
1920 i;
1921
1922 assert(images != (Image **) NULL);
1923 assert((*images)->signature == MagickSignature);
1924 if ((*images)->debug != MagickFalse)
1925 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
1926 (*images)->filename);
1927 image_list=ImageListToArray(*images,&(*images)->exception);
1928 if (image_list == (Image **) NULL)
1929 return(MagickFalse);
1930 status=MagickTrue;
1931 transform_images=NewImageList();
1932 for (i=0; image_list[i] != (Image *) NULL; i++)
1933 {
1934 image=image_list[i];
1935 status|=TransformImage(&image,crop_geometry,image_geometry);
1936 AppendImageToList(&transform_images,image);
1937 }
1938 *images=transform_images;
1939 image_list=(Image **) RelinquishMagickMemory(image_list);
1940 return(status != 0 ? MagickTrue : MagickFalse);
1941}
1942
1943/*
1944%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1945% %
1946% %
1947% %
1948% T r a n s p o s e I m a g e %
1949% %
1950% %
1951% %
1952%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1953%
1954% TransposeImage() creates a horizontal mirror image by reflecting the pixels
1955% around the central y-axis while rotating them by 90 degrees.
1956%
1957% The format of the TransposeImage method is:
1958%
1959% Image *TransposeImage(const Image *image,ExceptionInfo *exception)
1960%
1961% A description of each parameter follows:
1962%
1963% o image: the image.
1964%
1965% o exception: return any errors or warnings in this structure.
1966%
1967*/
1968MagickExport Image *TransposeImage(const Image *image,ExceptionInfo *exception)
1969{
1970#define TransposeImageTag "Transpose/Image"
1971
cristyc4c8d132010-01-07 01:58:38 +00001972 CacheView
1973 *image_view,
1974 *transpose_view;
1975
cristy3ed852e2009-09-05 21:47:34 +00001976 Image
1977 *transpose_image;
1978
1979 long
1980 progress,
1981 y;
1982
1983 MagickBooleanType
1984 status;
1985
1986 RectangleInfo
1987 page;
1988
cristy3ed852e2009-09-05 21:47:34 +00001989 assert(image != (const Image *) NULL);
1990 assert(image->signature == MagickSignature);
1991 if (image->debug != MagickFalse)
1992 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1993 assert(exception != (ExceptionInfo *) NULL);
1994 assert(exception->signature == MagickSignature);
1995 transpose_image=CloneImage(image,image->rows,image->columns,MagickTrue,
1996 exception);
1997 if (transpose_image == (Image *) NULL)
1998 return((Image *) NULL);
1999 /*
2000 Transpose image.
2001 */
2002 status=MagickTrue;
2003 progress=0;
2004 image_view=AcquireCacheView(image);
2005 transpose_view=AcquireCacheView(transpose_image);
cristyb5d5f722009-11-04 03:03:49 +00002006#if defined(MAGICKCORE_OPENMP_SUPPORT)
2007 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00002008#endif
2009 for (y=0; y < (long) image->rows; y++)
2010 {
2011 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002012 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002013
2014 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002015 *restrict transpose_indexes,
2016 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002017
2018 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002019 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002020
2021 if (status == MagickFalse)
2022 continue;
2023 p=GetCacheViewVirtualPixels(image_view,0,(long) image->rows-y-1,
2024 image->columns,1,exception);
2025 q=QueueCacheViewAuthenticPixels(transpose_view,(long) (image->rows-y-1),0,
2026 1,transpose_image->rows,exception);
2027 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2028 {
2029 status=MagickFalse;
2030 continue;
2031 }
2032 (void) CopyMagickMemory(q,p,(size_t) image->columns*sizeof(*q));
2033 indexes=GetCacheViewAuthenticIndexQueue(image_view);
2034 if (indexes != (IndexPacket *) NULL)
2035 {
2036 transpose_indexes=GetCacheViewAuthenticIndexQueue(transpose_view);
2037 if (transpose_indexes != (IndexPacket *) NULL)
2038 (void) CopyMagickMemory(transpose_indexes,indexes,(size_t)
2039 image->columns*sizeof(*transpose_indexes));
2040 }
2041 if (SyncCacheViewAuthenticPixels(transpose_view,exception) == MagickFalse)
2042 status=MagickFalse;
2043 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2044 {
2045 MagickBooleanType
2046 proceed;
2047
cristyb5d5f722009-11-04 03:03:49 +00002048#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002049 #pragma omp critical (MagickCore_TransposeImage)
2050#endif
2051 proceed=SetImageProgress(image,TransposeImageTag,progress++,
2052 image->rows);
2053 if (proceed == MagickFalse)
2054 status=MagickFalse;
2055 }
2056 }
2057 transpose_view=DestroyCacheView(transpose_view);
2058 image_view=DestroyCacheView(image_view);
2059 transpose_image->type=image->type;
2060 page=transpose_image->page;
2061 Swap(page.width,page.height);
2062 Swap(page.x,page.y);
2063 if (page.width != 0)
2064 page.x=(long) (page.width-transpose_image->columns-page.x);
2065 transpose_image->page=page;
2066 if (status == MagickFalse)
2067 transpose_image=DestroyImage(transpose_image);
2068 return(transpose_image);
2069}
2070
2071/*
2072%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2073% %
2074% %
2075% %
2076% T r a n s v e r s e I m a g e %
2077% %
2078% %
2079% %
2080%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2081%
2082% TransverseImage() creates a vertical mirror image by reflecting the pixels
2083% around the central x-axis while rotating them by 270 degrees.
2084%
2085% The format of the TransverseImage method is:
2086%
2087% Image *TransverseImage(const Image *image,ExceptionInfo *exception)
2088%
2089% A description of each parameter follows:
2090%
2091% o image: the image.
2092%
2093% o exception: return any errors or warnings in this structure.
2094%
2095*/
2096MagickExport Image *TransverseImage(const Image *image,ExceptionInfo *exception)
2097{
2098#define TransverseImageTag "Transverse/Image"
2099
cristyc4c8d132010-01-07 01:58:38 +00002100 CacheView
2101 *image_view,
2102 *transverse_view;
2103
cristy3ed852e2009-09-05 21:47:34 +00002104 Image
2105 *transverse_image;
2106
2107 long
2108 progress,
2109 y;
2110
2111 MagickBooleanType
2112 status;
2113
2114 RectangleInfo
2115 page;
2116
cristy3ed852e2009-09-05 21:47:34 +00002117 assert(image != (const Image *) NULL);
2118 assert(image->signature == MagickSignature);
2119 if (image->debug != MagickFalse)
2120 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2121 assert(exception != (ExceptionInfo *) NULL);
2122 assert(exception->signature == MagickSignature);
2123 transverse_image=CloneImage(image,image->rows,image->columns,MagickTrue,
2124 exception);
2125 if (transverse_image == (Image *) NULL)
2126 return((Image *) NULL);
2127 /*
2128 Transverse image.
2129 */
2130 status=MagickTrue;
2131 progress=0;
2132 image_view=AcquireCacheView(image);
2133 transverse_view=AcquireCacheView(transverse_image);
cristyb5d5f722009-11-04 03:03:49 +00002134#if defined(MAGICKCORE_OPENMP_SUPPORT)
2135 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00002136#endif
2137 for (y=0; y < (long) image->rows; y++)
2138 {
2139 MagickBooleanType
2140 sync;
2141
2142 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002143 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002144
2145 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002146 *restrict transverse_indexes,
2147 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002148
2149 register long
2150 x;
2151
2152 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002153 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002154
2155 if (status == MagickFalse)
2156 continue;
2157 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
2158 q=QueueCacheViewAuthenticPixels(transverse_view,(long) (image->rows-y-
2159 1),0,1,transverse_image->rows,exception);
2160 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2161 {
2162 status=MagickFalse;
2163 continue;
2164 }
2165 q+=image->columns;
2166 for (x=0; x < (long) image->columns; x++)
2167 *--q=(*p++);
2168 indexes=GetCacheViewAuthenticIndexQueue(image_view);
2169 if (indexes != (IndexPacket *) NULL)
2170 {
2171 transverse_indexes=GetCacheViewAuthenticIndexQueue(transverse_view);
2172 if (transverse_indexes != (IndexPacket *) NULL)
2173 for (x=0; x < (long) image->columns; x++)
2174 transverse_indexes[image->columns-x-1]=indexes[x];
2175 }
2176 sync=SyncCacheViewAuthenticPixels(transverse_view,exception);
2177 if (sync == MagickFalse)
2178 status=MagickFalse;
2179 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2180 {
2181 MagickBooleanType
2182 proceed;
2183
cristyb5d5f722009-11-04 03:03:49 +00002184#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002185 #pragma omp critical (MagickCore_TransverseImage)
2186#endif
2187 proceed=SetImageProgress(image,TransverseImageTag,progress++,
2188 image->rows);
2189 if (proceed == MagickFalse)
2190 status=MagickFalse;
2191 }
2192 }
2193 transverse_view=DestroyCacheView(transverse_view);
2194 image_view=DestroyCacheView(image_view);
2195 transverse_image->type=image->type;
2196 page=transverse_image->page;
2197 Swap(page.width,page.height);
2198 Swap(page.x,page.y);
2199 if (page.height != 0)
2200 page.y=(long) (page.height-transverse_image->rows-page.y);
2201 transverse_image->page=page;
2202 if (status == MagickFalse)
2203 transverse_image=DestroyImage(transverse_image);
2204 return(transverse_image);
2205}
2206
2207/*
2208%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2209% %
2210% %
2211% %
2212% T r i m I m a g e %
2213% %
2214% %
2215% %
2216%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2217%
2218% TrimImage() trims pixels from the image edges. It allocates the memory
2219% necessary for the new Image structure and returns a pointer to the new
2220% image.
2221%
2222% The format of the TrimImage method is:
2223%
2224% Image *TrimImage(const Image *image,ExceptionInfo *exception)
2225%
2226% A description of each parameter follows:
2227%
2228% o image: the image.
2229%
2230% o exception: return any errors or warnings in this structure.
2231%
2232*/
2233MagickExport Image *TrimImage(const Image *image,ExceptionInfo *exception)
2234{
2235 RectangleInfo
2236 geometry;
2237
2238 assert(image != (const Image *) NULL);
2239 assert(image->signature == MagickSignature);
2240 if (image->debug != MagickFalse)
2241 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2242 geometry=GetImageBoundingBox(image,exception);
2243 if ((geometry.width == 0) || (geometry.height == 0))
2244 {
2245 Image
2246 *crop_image;
2247
2248 crop_image=CloneImage(image,1,1,MagickTrue,exception);
2249 if (crop_image == (Image *) NULL)
2250 return((Image *) NULL);
2251 crop_image->background_color.opacity=(Quantum) TransparentOpacity;
2252 (void) SetImageBackgroundColor(crop_image);
2253 crop_image->page=image->page;
2254 crop_image->page.x=(-1);
2255 crop_image->page.y=(-1);
2256 return(crop_image);
2257 }
2258 geometry.x+=image->page.x;
2259 geometry.y+=image->page.y;
2260 return(CropImage(image,&geometry,exception));
2261}