blob: 3b0ae53448733b92203eaac18116c8521f287c73 [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%
79% Chop() removes a region of an image and collapses the image to occupy the
80% removed portion.
81%
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
113 proceed;
114
115 RectangleInfo
116 extent;
117
118 register long
119 i;
120
cristy3ed852e2009-09-05 21:47:34 +0000121 /*
122 Check chop geometry.
123 */
124 assert(image != (const Image *) NULL);
125 assert(image->signature == MagickSignature);
126 if (image->debug != MagickFalse)
127 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
128 assert(exception != (ExceptionInfo *) NULL);
129 assert(exception->signature == MagickSignature);
130 assert(chop_info != (RectangleInfo *) NULL);
131 if (((chop_info->x+(long) chop_info->width) < 0) ||
132 ((chop_info->y+(long) chop_info->height) < 0) ||
133 (chop_info->x > (long) image->columns) ||
134 (chop_info->y > (long) image->rows))
135 ThrowImageException(OptionWarning,"GeometryDoesNotContainImage");
136 extent=(*chop_info);
137 if ((extent.x+(long) extent.width) > (long) image->columns)
138 extent.width=(unsigned long) ((long) image->columns-extent.x);
139 if ((extent.y+(long) extent.height) > (long) image->rows)
140 extent.height=(unsigned long) ((long) image->rows-extent.y);
141 if (extent.x < 0)
142 {
143 extent.width-=(unsigned long) (-extent.x);
144 extent.x=0;
145 }
146 if (extent.y < 0)
147 {
148 extent.height-=(unsigned long) (-extent.y);
149 extent.y=0;
150 }
151 chop_image=CloneImage(image,image->columns-extent.width,image->rows-
152 extent.height,MagickTrue,exception);
153 if (chop_image == (Image *) NULL)
154 return((Image *) NULL);
155 /*
156 Extract chop image.
157 */
158 i=0;
159 j=0;
160 image_view=AcquireCacheView(image);
161 chop_view=AcquireCacheView(chop_image);
162 for (y=0; y < (long) extent.y; y++)
163 {
164 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000165 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000166
167 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000168 *restrict chop_indexes,
169 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +0000170
171 register long
172 x;
173
174 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000175 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000176
177 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,exception);
178 q=QueueCacheViewAuthenticPixels(chop_view,0,j++,chop_image->columns,1,
179 exception);
180 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
181 break;
182 indexes=GetCacheViewAuthenticIndexQueue(image_view);
183 chop_indexes=GetCacheViewAuthenticIndexQueue(chop_view);
184 for (x=0; x < (long) image->columns; x++)
185 {
186 if ((x < extent.x) || (x >= (long) (extent.x+extent.width)))
187 {
188 *q=(*p);
189 if (indexes != (IndexPacket *) NULL)
190 {
191 if (chop_indexes != (IndexPacket *) NULL)
192 *chop_indexes++=indexes[x];
193 }
194 q++;
195 }
196 p++;
197 }
198 if (SyncCacheViewAuthenticPixels(chop_view,exception) == MagickFalse)
199 break;
200 proceed=SetImageProgress(image,ChopImageTag,y,chop_image->rows);
201 if (proceed == MagickFalse)
202 break;
203 }
204 /*
205 Extract chop image.
206 */
207 i+=extent.height;
208 for (y=0; y < (long) (image->rows-(extent.y+extent.height)); y++)
209 {
210 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000211 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000212
213 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000214 *restrict chop_indexes,
215 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +0000216
217 register long
218 x;
219
220 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000221 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000222
223 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,exception);
224 q=QueueCacheViewAuthenticPixels(chop_view,0,j++,chop_image->columns,1,
225 exception);
226 if ((p == (PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
227 break;
228 indexes=GetCacheViewAuthenticIndexQueue(image_view);
229 chop_indexes=GetCacheViewAuthenticIndexQueue(chop_view);
230 for (x=0; x < (long) image->columns; x++)
231 {
232 if ((x < extent.x) || (x >= (long) (extent.x+extent.width)))
233 {
234 *q=(*p);
235 if (indexes != (IndexPacket *) NULL)
236 {
237 if (chop_indexes != (IndexPacket *) NULL)
238 *chop_indexes++=indexes[x];
239 }
240 q++;
241 }
242 p++;
243 }
244 if (SyncCacheViewAuthenticPixels(chop_view,exception) == MagickFalse)
245 break;
246 proceed=SetImageProgress(image,ChopImageTag,y,chop_image->rows);
247 if (proceed == MagickFalse)
248 break;
249 }
250 chop_view=DestroyCacheView(chop_view);
251 image_view=DestroyCacheView(image_view);
252 chop_image->type=image->type;
253 return(chop_image);
254}
255
256/*
257%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
258% %
259% %
260% %
261+ C o n s o l i d a t e C M Y K I m a g e %
262% %
263% %
264% %
265%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
266%
267% ConsolidateCMYKImage() consolidates separate C, M, Y, and K planes into a
268% single image.
269%
270% The format of the ConsolidateCMYKImage method is:
271%
272% Image *ConsolidateCMYKImage(const Image *image,ExceptionInfo *exception)
273%
274% A description of each parameter follows:
275%
276% o image: the image sequence.
277%
278% o exception: return any errors or warnings in this structure.
279%
280*/
281MagickExport Image *ConsolidateCMYKImages(const Image *images,
282 ExceptionInfo *exception)
283{
284 Image
285 *cmyk_image,
286 *cmyk_images;
287
288 long
289 y;
290
291 register long
292 i;
293
294 /*
295 Consolidate separate C, M, Y, and K planes into a single image.
296 */
297 assert(images != (Image *) NULL);
298 assert(images->signature == MagickSignature);
299 if (images->debug != MagickFalse)
300 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
301 assert(exception != (ExceptionInfo *) NULL);
302 assert(exception->signature == MagickSignature);
303 cmyk_images=NewImageList();
304 for (i=0; i < (long) GetImageListLength(images); i+=4)
305 {
306 cmyk_image=CloneImage(images,images->columns,images->rows,MagickTrue,
307 exception);
308 if (cmyk_image == (Image *) NULL)
309 break;
310 if (SetImageStorageClass(cmyk_image,DirectClass) == MagickFalse)
311 break;
312 (void) SetImageColorspace(cmyk_image,CMYKColorspace);
313 for (y=0; y < (long) images->rows; y++)
314 {
315 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000316 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000317
318 register long
319 x;
320
321 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000322 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000323
324 p=GetVirtualPixels(images,0,y,images->columns,1,exception);
325 q=QueueAuthenticPixels(cmyk_image,0,y,cmyk_image->columns,1,exception);
326 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
327 break;
328 for (x=0; x < (long) images->columns; x++)
329 {
330 q->red=(Quantum) (QuantumRange-PixelIntensityToQuantum(p));
331 p++;
332 q++;
333 }
334 if (SyncAuthenticPixels(cmyk_image,exception) == MagickFalse)
335 break;
336 }
337 images=GetNextImageInList(images);
338 if (images == (Image *) NULL)
339 break;
340 for (y=0; y < (long) images->rows; y++)
341 {
342 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000343 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000344
345 register long
346 x;
347
348 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000349 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000350
351 p=GetVirtualPixels(images,0,y,images->columns,1,exception);
352 q=GetAuthenticPixels(cmyk_image,0,y,cmyk_image->columns,1,exception);
353 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
354 break;
355 for (x=0; x < (long) images->columns; x++)
356 {
357 q->green=(Quantum) (QuantumRange-PixelIntensityToQuantum(p));
358 p++;
359 q++;
360 }
361 if (SyncAuthenticPixels(cmyk_image,exception) == MagickFalse)
362 break;
363 }
364 images=GetNextImageInList(images);
365 if (images == (Image *) NULL)
366 break;
367 for (y=0; y < (long) images->rows; y++)
368 {
369 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000370 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000371
372 register long
373 x;
374
375 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000376 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000377
378 p=GetVirtualPixels(images,0,y,images->columns,1,exception);
379 q=GetAuthenticPixels(cmyk_image,0,y,cmyk_image->columns,1,exception);
380 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
381 break;
382 for (x=0; x < (long) images->columns; x++)
383 {
384 q->blue=(Quantum) (QuantumRange-PixelIntensityToQuantum(p));
385 p++;
386 q++;
387 }
388 if (SyncAuthenticPixels(cmyk_image,exception) == MagickFalse)
389 break;
390 }
391 images=GetNextImageInList(images);
392 if (images == (Image *) NULL)
393 break;
394 for (y=0; y < (long) images->rows; y++)
395 {
396 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000397 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000398
399 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000400 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +0000401
402 register long
403 x;
404
405 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000406 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000407
408 p=GetVirtualPixels(images,0,y,images->columns,1,exception);
409 q=GetAuthenticPixels(cmyk_image,0,y,cmyk_image->columns,1,exception);
410 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
411 break;
412 indexes=GetAuthenticIndexQueue(cmyk_image);
413 for (x=0; x < (long) images->columns; x++)
414 {
415 indexes[x]=(IndexPacket) (QuantumRange-PixelIntensityToQuantum(p));
416 p++;
417 }
418 if (SyncAuthenticPixels(cmyk_image,exception) == MagickFalse)
419 break;
420 }
421 AppendImageToList(&cmyk_images,cmyk_image);
422 images=GetNextImageInList(images);
423 if (images == (Image *) NULL)
424 break;
425 }
426 return(cmyk_images);
427}
428
429/*
430%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
431% %
432% %
433% %
434% C r o p I m a g e %
435% %
436% %
437% %
438%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
439%
440% CropImage() extracts a region of the image starting at the offset defined
441% by geometry.
442%
443% The format of the CropImage method is:
444%
445% Image *CropImage(const Image *image,const RectangleInfo *geometry,
446% ExceptionInfo *exception)
447%
448% A description of each parameter follows:
449%
450% o image: the image.
451%
452% o geometry: Define the region of the image to crop with members
453% x, y, width, and height.
454%
455% o exception: return any errors or warnings in this structure.
456%
457*/
458MagickExport Image *CropImage(const Image *image,const RectangleInfo *geometry,
459 ExceptionInfo *exception)
460{
461#define CropImageTag "Crop/Image"
462
cristyc4c8d132010-01-07 01:58:38 +0000463 CacheView
464 *crop_view,
465 *image_view;
466
cristy3ed852e2009-09-05 21:47:34 +0000467 Image
468 *crop_image;
469
470 long
471 progress,
472 y;
473
474 MagickBooleanType
475 status;
476
477 RectangleInfo
478 bounding_box,
479 page;
480
cristy3ed852e2009-09-05 21:47:34 +0000481 /*
482 Check crop geometry.
483 */
484 assert(image != (const Image *) NULL);
485 assert(image->signature == MagickSignature);
486 if (image->debug != MagickFalse)
487 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
488 assert(geometry != (const RectangleInfo *) NULL);
489 assert(exception != (ExceptionInfo *) NULL);
490 assert(exception->signature == MagickSignature);
491 bounding_box=image->page;
492 if ((bounding_box.width == 0) || (bounding_box.height == 0))
493 {
494 bounding_box.width=image->columns;
495 bounding_box.height=image->rows;
496 }
497 page=(*geometry);
498 if (page.width == 0)
499 page.width=bounding_box.width;
500 if (page.height == 0)
501 page.height=bounding_box.height;
502 if (((bounding_box.x-page.x) >= (long) page.width) ||
503 ((bounding_box.y-page.y) >= (long) page.height) ||
504 ((page.x-bounding_box.x) > (long) image->columns) ||
505 ((page.y-bounding_box.y) > (long) image->rows))
506 {
507 /*
508 Crop is not within virtual canvas, return 1 pixel transparent image.
509 */
510 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
511 "GeometryDoesNotContainImage","`%s'",image->filename);
512 crop_image=CloneImage(image,1,1,MagickTrue,exception);
513 if (crop_image == (Image *) NULL)
514 return((Image *) NULL);
515 crop_image->background_color.opacity=(Quantum) TransparentOpacity;
516 (void) SetImageBackgroundColor(crop_image);
517 crop_image->page=bounding_box;
518 crop_image->page.x=(-1);
519 crop_image->page.y=(-1);
520 if (crop_image->dispose == BackgroundDispose)
521 crop_image->dispose=NoneDispose;
522 return(crop_image);
523 }
524 if ((page.x < 0) && (bounding_box.x >= 0))
525 {
526 page.width+=page.x-bounding_box.x;
527 page.x=0;
528 }
529 else
530 {
531 page.width-=bounding_box.x-page.x;
532 page.x-=bounding_box.x;
533 if (page.x < 0)
534 page.x=0;
535 }
536 if ((page.y < 0) && (bounding_box.y >= 0))
537 {
538 page.height+=page.y-bounding_box.y;
539 page.y=0;
540 }
541 else
542 {
543 page.height-=bounding_box.y-page.y;
544 page.y-=bounding_box.y;
545 if (page.y < 0)
546 page.y=0;
547 }
548 if ((unsigned long) (page.x+page.width) > image->columns)
549 page.width=image->columns-page.x;
550 if (geometry->width != 0)
551 if (page.width > geometry->width)
552 page.width=geometry->width;
553 if ((unsigned long) (page.y+page.height) > image->rows)
554 page.height=image->rows-page.y;
555 if (geometry->height != 0)
556 if (page.height > geometry->height)
557 page.height=geometry->height;
558 bounding_box.x+=page.x;
559 bounding_box.y+=page.y;
560 if ((page.width == 0) || (page.height == 0))
561 {
562 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
563 "GeometryDoesNotContainImage","`%s'",image->filename);
564 return((Image *) NULL);
565 }
566 /*
567 Initialize crop image attributes.
568 */
569 crop_image=CloneImage(image,page.width,page.height,MagickTrue,exception);
570 if (crop_image == (Image *) NULL)
571 return((Image *) NULL);
572 crop_image->page.width=image->page.width;
573 crop_image->page.height=image->page.height;
574 if (((long) (bounding_box.x+bounding_box.width) > (long) image->page.width) ||
575 ((long) (bounding_box.y+bounding_box.height) > (long) image->page.height))
576 {
577 crop_image->page.width=bounding_box.width;
578 crop_image->page.height=bounding_box.height;
579 }
580 crop_image->page.x=bounding_box.x;
581 crop_image->page.y=bounding_box.y;
582 /*
583 Crop image.
584 */
585 status=MagickTrue;
586 progress=0;
587 image_view=AcquireCacheView(image);
588 crop_view=AcquireCacheView(crop_image);
cristyb5d5f722009-11-04 03:03:49 +0000589#if defined(MAGICKCORE_OPENMP_SUPPORT)
590 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000591#endif
592 for (y=0; y < (long) crop_image->rows; y++)
593 {
594 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000595 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +0000596
597 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000598 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000599
600 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000601 *restrict crop_indexes;
cristy3ed852e2009-09-05 21:47:34 +0000602
603 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000604 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000605
606 if (status == MagickFalse)
607 continue;
608 p=GetCacheViewVirtualPixels(image_view,page.x,page.y+y,crop_image->columns,
609 1,exception);
610 q=QueueCacheViewAuthenticPixels(crop_view,0,y,crop_image->columns,1,
611 exception);
612 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
613 {
614 status=MagickFalse;
615 continue;
616 }
617 (void) CopyMagickMemory(q,p,(size_t) crop_image->columns*sizeof(*q));
618 indexes=GetCacheViewVirtualIndexQueue(image_view);
619 if (indexes != (IndexPacket *) NULL)
620 {
621 crop_indexes=GetCacheViewAuthenticIndexQueue(crop_view);
622 if (crop_indexes != (IndexPacket *) NULL)
623 (void) CopyMagickMemory(crop_indexes,indexes,(size_t)
624 crop_image->columns*sizeof(*crop_indexes));
625 }
626 if (SyncCacheViewAuthenticPixels(crop_view,exception) == MagickFalse)
627 status=MagickFalse;
628 if (image->progress_monitor != (MagickProgressMonitor) NULL)
629 {
630 MagickBooleanType
631 proceed;
632
cristyb5d5f722009-11-04 03:03:49 +0000633#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +0000634 #pragma omp critical (MagickCore_CropImage)
635#endif
636 proceed=SetImageProgress(image,CropImageTag,progress++,image->rows);
637 if (proceed == MagickFalse)
638 status=MagickFalse;
639 }
640 }
641 crop_view=DestroyCacheView(crop_view);
642 image_view=DestroyCacheView(image_view);
643 crop_image->type=image->type;
644 if (status == MagickFalse)
645 crop_image=DestroyImage(crop_image);
646 return(crop_image);
647}
648
649/*
650%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
651% %
652% %
653% %
654% E x c e r p t I m a g e %
655% %
656% %
657% %
658%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
659%
660% ExcerptImage() returns a excerpt of the image as defined by the geometry.
661%
662% The format of the ExcerptImage method is:
663%
664% Image *ExcerptImage(const Image *image,const RectangleInfo *geometry,
665% ExceptionInfo *exception)
666%
667% A description of each parameter follows:
668%
669% o image: the image.
670%
671% o geometry: Define the region of the image to extend with members
672% x, y, width, and height.
673%
674% o exception: return any errors or warnings in this structure.
675%
676*/
677MagickExport Image *ExcerptImage(const Image *image,
678 const RectangleInfo *geometry,ExceptionInfo *exception)
679{
680#define ExcerptImageTag "Excerpt/Image"
681
cristyc4c8d132010-01-07 01:58:38 +0000682 CacheView
683 *excerpt_view,
684 *image_view;
685
cristy3ed852e2009-09-05 21:47:34 +0000686 Image
687 *excerpt_image;
688
689 long
690 progress,
691 y;
692
693 MagickBooleanType
694 status;
695
cristy3ed852e2009-09-05 21:47:34 +0000696 /*
697 Allocate excerpt image.
698 */
699 assert(image != (const Image *) NULL);
700 assert(image->signature == MagickSignature);
701 if (image->debug != MagickFalse)
702 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
703 assert(geometry != (const RectangleInfo *) NULL);
704 assert(exception != (ExceptionInfo *) NULL);
705 assert(exception->signature == MagickSignature);
706 excerpt_image=CloneImage(image,geometry->width,geometry->height,MagickTrue,
707 exception);
708 if (excerpt_image == (Image *) NULL)
709 return((Image *) NULL);
710 /*
711 Excerpt each row.
712 */
713 status=MagickTrue;
714 progress=0;
715 image_view=AcquireCacheView(image);
716 excerpt_view=AcquireCacheView(excerpt_image);
cristyb5d5f722009-11-04 03:03:49 +0000717#if defined(MAGICKCORE_OPENMP_SUPPORT)
718 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000719#endif
720 for (y=0; y < (long) excerpt_image->rows; y++)
721 {
722 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000723 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000724
725 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000726 *restrict excerpt_indexes,
727 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +0000728
729 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000730 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000731
732 if (status == MagickFalse)
733 continue;
734 p=GetCacheViewVirtualPixels(image_view,geometry->x,geometry->y+y,
735 geometry->width,1,exception);
736 q=GetCacheViewAuthenticPixels(excerpt_view,0,y,excerpt_image->columns,1,
737 exception);
738 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
739 {
740 status=MagickFalse;
741 continue;
742 }
743 (void) CopyMagickMemory(q,p,(size_t) excerpt_image->columns*sizeof(*q));
744 indexes=GetCacheViewAuthenticIndexQueue(image_view);
745 if (indexes != (IndexPacket *) NULL)
746 {
747 excerpt_indexes=GetCacheViewAuthenticIndexQueue(excerpt_view);
748 if (excerpt_indexes != (IndexPacket *) NULL)
749 (void) CopyMagickMemory(excerpt_indexes,indexes,(size_t)
750 excerpt_image->columns*sizeof(*excerpt_indexes));
751 }
752 if (SyncCacheViewAuthenticPixels(excerpt_view,exception) == MagickFalse)
753 status=MagickFalse;
754 if (image->progress_monitor != (MagickProgressMonitor) NULL)
755 {
756 MagickBooleanType
757 proceed;
758
cristyb5d5f722009-11-04 03:03:49 +0000759#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +0000760 #pragma omp critical (MagickCore_ExcerptImage)
761#endif
762 proceed=SetImageProgress(image,ExcerptImageTag,progress++,image->rows);
763 if (proceed == MagickFalse)
764 status=MagickFalse;
765 }
766 }
767 excerpt_view=DestroyCacheView(excerpt_view);
768 image_view=DestroyCacheView(image_view);
769 excerpt_image->type=image->type;
770 if (status == MagickFalse)
771 excerpt_image=DestroyImage(excerpt_image);
772 return(excerpt_image);
773}
774
775/*
776%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
777% %
778% %
779% %
780% E x t e n t I m a g e %
781% %
782% %
783% %
784%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
785%
786% ExtentImage() extends the image as defined by the geometry, gravity, and
787% image background color. Set the (x,y) offset of the geometry to move the
788% original image relative to the extended image.
789%
790% The format of the ExtentImage method is:
791%
792% Image *ExtentImage(const Image *image,const RectangleInfo *geometry,
793% ExceptionInfo *exception)
794%
795% A description of each parameter follows:
796%
797% o image: the image.
798%
799% o geometry: Define the region of the image to extend with members
800% x, y, width, and height.
801%
802% o exception: return any errors or warnings in this structure.
803%
804*/
805MagickExport Image *ExtentImage(const Image *image,
806 const RectangleInfo *geometry,ExceptionInfo *exception)
807{
808 Image
809 *extent_image;
810
811 /*
812 Allocate extent image.
813 */
814 assert(image != (const Image *) NULL);
815 assert(image->signature == MagickSignature);
816 if (image->debug != MagickFalse)
817 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
818 assert(geometry != (const RectangleInfo *) NULL);
819 assert(exception != (ExceptionInfo *) NULL);
820 assert(exception->signature == MagickSignature);
821 extent_image=CloneImage(image,geometry->width,geometry->height,MagickTrue,
822 exception);
823 if (extent_image == (Image *) NULL)
824 return((Image *) NULL);
825 if (SetImageStorageClass(extent_image,DirectClass) == MagickFalse)
826 {
827 InheritException(exception,&extent_image->exception);
828 extent_image=DestroyImage(extent_image);
829 return((Image *) NULL);
830 }
831 if (extent_image->background_color.opacity != OpaqueOpacity)
832 extent_image->matte=MagickTrue;
833 (void) SetImageBackgroundColor(extent_image);
834 (void) CompositeImage(extent_image,image->compose,image,geometry->x,
835 geometry->y);
836 return(extent_image);
837}
838
839/*
840%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
841% %
842% %
843% %
844% F l i p I m a g e %
845% %
846% %
847% %
848%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
849%
850% FlipImage() creates a vertical mirror image by reflecting the pixels
851% around the central x-axis.
852%
853% The format of the FlipImage method is:
854%
855% Image *FlipImage(const Image *image,ExceptionInfo *exception)
856%
857% A description of each parameter follows:
858%
859% o image: the image.
860%
861% o exception: return any errors or warnings in this structure.
862%
863*/
864MagickExport Image *FlipImage(const Image *image,ExceptionInfo *exception)
865{
866#define FlipImageTag "Flip/Image"
867
cristyc4c8d132010-01-07 01:58:38 +0000868 CacheView
869 *flip_view,
870 *image_view;
871
cristy3ed852e2009-09-05 21:47:34 +0000872 Image
873 *flip_image;
874
875 long
876 progress,
877 y;
878
879 MagickBooleanType
880 status;
881
cristy3ed852e2009-09-05 21:47:34 +0000882 assert(image != (const Image *) NULL);
883 assert(image->signature == MagickSignature);
884 if (image->debug != MagickFalse)
885 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
886 assert(exception != (ExceptionInfo *) NULL);
887 assert(exception->signature == MagickSignature);
888 flip_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
889 if (flip_image == (Image *) NULL)
890 return((Image *) NULL);
891 /*
892 Flip image.
893 */
894 status=MagickTrue;
895 progress=0;
896 image_view=AcquireCacheView(image);
897 flip_view=AcquireCacheView(flip_image);
cristyb5d5f722009-11-04 03:03:49 +0000898#if defined(MAGICKCORE_OPENMP_SUPPORT)
899 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000900#endif
901 for (y=0; y < (long) flip_image->rows; y++)
902 {
903 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000904 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +0000905
906 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000907 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000908
909 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000910 *restrict flip_indexes;
cristy3ed852e2009-09-05 21:47:34 +0000911
912 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000913 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000914
915 if (status == MagickFalse)
916 continue;
917 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
918 q=QueueCacheViewAuthenticPixels(flip_view,0,(long) (flip_image->rows-y-1),
919 flip_image->columns,1,exception);
920 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
921 {
922 status=MagickFalse;
923 continue;
924 }
925 (void) CopyMagickMemory(q,p,(size_t) image->columns*sizeof(*q));
926 indexes=GetCacheViewVirtualIndexQueue(image_view);
927 if (indexes != (const IndexPacket *) NULL)
928 {
929 flip_indexes=GetCacheViewAuthenticIndexQueue(flip_view);
930 if (flip_indexes != (IndexPacket *) NULL)
931 (void) CopyMagickMemory(flip_indexes,indexes,(size_t) image->columns*
932 sizeof(*flip_indexes));
933 }
934 if (SyncCacheViewAuthenticPixels(flip_view,exception) == MagickFalse)
935 status=MagickFalse;
936 if (image->progress_monitor != (MagickProgressMonitor) NULL)
937 {
938 MagickBooleanType
939 proceed;
940
cristyb5d5f722009-11-04 03:03:49 +0000941#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +0000942 #pragma omp critical (MagickCore_FlipImage)
943#endif
944 proceed=SetImageProgress(image,FlipImageTag,progress++,image->rows);
945 if (proceed == MagickFalse)
946 status=MagickFalse;
947 }
948 }
949 flip_view=DestroyCacheView(flip_view);
950 image_view=DestroyCacheView(image_view);
951 flip_image->type=image->type;
952 if (status == MagickFalse)
953 flip_image=DestroyImage(flip_image);
954 return(flip_image);
955}
956
957/*
958%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
959% %
960% %
961% %
962% F l o p I m a g e %
963% %
964% %
965% %
966%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
967%
968% FlopImage() creates a horizontal mirror image by reflecting the pixels
969% around the central y-axis.
970%
971% The format of the FlopImage method is:
972%
973% Image *FlopImage(const Image *image,ExceptionInfo *exception)
974%
975% A description of each parameter follows:
976%
977% o image: the image.
978%
979% o exception: return any errors or warnings in this structure.
980%
981*/
982MagickExport Image *FlopImage(const Image *image,ExceptionInfo *exception)
983{
984#define FlopImageTag "Flop/Image"
985
cristyc4c8d132010-01-07 01:58:38 +0000986 CacheView
987 *flop_view,
988 *image_view;
989
cristy3ed852e2009-09-05 21:47:34 +0000990 Image
991 *flop_image;
992
993 long
994 progress,
995 y;
996
997 MagickBooleanType
998 status;
999
cristy3ed852e2009-09-05 21:47:34 +00001000 assert(image != (const Image *) NULL);
1001 assert(image->signature == MagickSignature);
1002 if (image->debug != MagickFalse)
1003 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1004 assert(exception != (ExceptionInfo *) NULL);
1005 assert(exception->signature == MagickSignature);
1006 flop_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
1007 if (flop_image == (Image *) NULL)
1008 return((Image *) NULL);
1009 /*
1010 Flop each row.
1011 */
1012 status=MagickTrue;
1013 progress=0;
1014 image_view=AcquireCacheView(image);
1015 flop_view=AcquireCacheView(flop_image);
cristyb5d5f722009-11-04 03:03:49 +00001016#if defined(MAGICKCORE_OPENMP_SUPPORT)
1017 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00001018#endif
1019 for (y=0; y < (long) flop_image->rows; y++)
1020 {
1021 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001022 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00001023
1024 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001025 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001026
1027 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001028 *restrict flop_indexes;
cristy3ed852e2009-09-05 21:47:34 +00001029
1030 register long
1031 x;
1032
1033 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001034 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001035
1036 if (status == MagickFalse)
1037 continue;
1038 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1039 q=QueueCacheViewAuthenticPixels(flop_view,0,y,flop_image->columns,1,
1040 exception);
1041 if ((p == (PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
1042 {
1043 status=MagickFalse;
1044 continue;
1045 }
1046 q+=flop_image->columns;
1047 indexes=GetCacheViewVirtualIndexQueue(image_view);
1048 flop_indexes=GetCacheViewAuthenticIndexQueue(flop_view);
1049 for (x=0; x < (long) flop_image->columns; x++)
1050 {
1051 (*--q)=(*p++);
1052 if ((indexes != (const IndexPacket *) NULL) &&
1053 (flop_indexes != (IndexPacket *) NULL))
1054 flop_indexes[flop_image->columns-x-1]=indexes[x];
1055 }
1056 if (SyncCacheViewAuthenticPixels(flop_view,exception) == MagickFalse)
1057 status=MagickFalse;
1058 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1059 {
1060 MagickBooleanType
1061 proceed;
1062
cristyb5d5f722009-11-04 03:03:49 +00001063#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00001064 #pragma omp critical (MagickCore_FlopImage)
1065#endif
1066 proceed=SetImageProgress(image,FlopImageTag,progress++,image->rows);
1067 if (proceed == MagickFalse)
1068 status=MagickFalse;
1069 }
1070 }
1071 flop_view=DestroyCacheView(flop_view);
1072 image_view=DestroyCacheView(image_view);
1073 flop_image->type=image->type;
1074 if (status == MagickFalse)
1075 flop_image=DestroyImage(flop_image);
1076 return(flop_image);
1077}
1078
1079/*
1080%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1081% %
1082% %
1083% %
1084% R o l l I m a g e %
1085% %
1086% %
1087% %
1088%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1089%
1090% RollImage() offsets an image as defined by x_offset and y_offset.
1091%
1092% The format of the RollImage method is:
1093%
1094% Image *RollImage(const Image *image,const long x_offset,
1095% const long y_offset,ExceptionInfo *exception)
1096%
1097% A description of each parameter follows:
1098%
1099% o image: the image.
1100%
1101% o x_offset: the number of columns to roll in the horizontal direction.
1102%
1103% o y_offset: the number of rows to roll in the vertical direction.
1104%
1105% o exception: return any errors or warnings in this structure.
1106%
1107*/
1108
1109static inline MagickBooleanType CopyImageRegion(Image *destination,
1110 const Image *source,const unsigned long columns,const unsigned long rows,
1111 const long sx,const long sy,const long dx,const long dy,
1112 ExceptionInfo *exception)
1113{
cristyc4c8d132010-01-07 01:58:38 +00001114 CacheView
1115 *source_view,
1116 *destination_view;
1117
cristy3ed852e2009-09-05 21:47:34 +00001118 long
1119 y;
1120
1121 MagickBooleanType
1122 status;
1123
cristy3ed852e2009-09-05 21:47:34 +00001124 status=MagickTrue;
1125 source_view=AcquireCacheView(source);
1126 destination_view=AcquireCacheView(destination);
cristyb5d5f722009-11-04 03:03:49 +00001127#if defined(MAGICKCORE_OPENMP_SUPPORT)
1128 #pragma omp parallel for schedule(dynamic,4) shared(status)
cristy3ed852e2009-09-05 21:47:34 +00001129#endif
1130 for (y=0; y < (long) rows; y++)
1131 {
1132 MagickBooleanType
1133 sync;
1134
1135 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001136 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001137
1138 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001139 *restrict indexes,
1140 *restrict destination_indexes;
cristy3ed852e2009-09-05 21:47:34 +00001141
1142 register long
1143 x;
1144
1145 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001146 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001147
1148 /*
1149 Transfer scanline.
1150 */
1151 if (status == MagickFalse)
1152 continue;
1153 p=GetCacheViewVirtualPixels(source_view,sx,sy+y,columns,1,exception);
1154 q=GetCacheViewAuthenticPixels(destination_view,dx,dy+y,columns,1,exception);
1155 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
1156 {
1157 status=MagickFalse;
1158 continue;
1159 }
1160 indexes=GetCacheViewAuthenticIndexQueue(source_view);
1161 for (x=0; x < (long) columns; x++)
1162 *q++=(*p++);
1163 if (indexes != (IndexPacket *) NULL)
1164 {
1165 destination_indexes=GetCacheViewAuthenticIndexQueue(destination_view);
1166 for (x=0; x < (long) columns; x++)
1167 destination_indexes[x]=indexes[x];
1168 }
1169 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
1170 if (sync == MagickFalse)
1171 status=MagickFalse;
1172 }
1173 destination_view=DestroyCacheView(destination_view);
1174 source_view=DestroyCacheView(source_view);
1175 return(status);
1176}
1177
1178MagickExport Image *RollImage(const Image *image,const long x_offset,
1179 const long y_offset,ExceptionInfo *exception)
1180{
1181#define RollImageTag "Roll/Image"
1182
1183 Image
1184 *roll_image;
1185
1186 MagickStatusType
1187 status;
1188
1189 RectangleInfo
1190 offset;
1191
1192 /*
1193 Initialize roll image attributes.
1194 */
1195 assert(image != (const Image *) NULL);
1196 assert(image->signature == MagickSignature);
1197 if (image->debug != MagickFalse)
1198 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1199 assert(exception != (ExceptionInfo *) NULL);
1200 assert(exception->signature == MagickSignature);
1201 roll_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
1202 if (roll_image == (Image *) NULL)
1203 return((Image *) NULL);
1204 offset.x=x_offset;
1205 offset.y=y_offset;
1206 while (offset.x < 0)
1207 offset.x+=image->columns;
1208 while (offset.x >= (long) image->columns)
1209 offset.x-=image->columns;
1210 while (offset.y < 0)
1211 offset.y+=image->rows;
1212 while (offset.y >= (long) image->rows)
1213 offset.y-=image->rows;
1214 /*
1215 Roll image.
1216 */
1217 status=CopyImageRegion(roll_image,image,(unsigned long) offset.x,
1218 (unsigned long) offset.y,(long) image->columns-offset.x,(long) image->rows-
1219 offset.y,0,0,exception);
1220 (void) SetImageProgress(image,RollImageTag,0,3);
1221 status|=CopyImageRegion(roll_image,image,image->columns-offset.x,
1222 (unsigned long) offset.y,0,(long) image->rows-offset.y,offset.x,0,
1223 exception);
1224 (void) SetImageProgress(image,RollImageTag,1,3);
1225 status|=CopyImageRegion(roll_image,image,(unsigned long) offset.x,image->rows-
1226 offset.y,(long) image->columns-offset.x,0,0,offset.y,exception);
1227 (void) SetImageProgress(image,RollImageTag,2,3);
1228 status|=CopyImageRegion(roll_image,image,image->columns-offset.x,image->rows-
1229 offset.y,0,0,offset.x,offset.y,exception);
1230 (void) SetImageProgress(image,RollImageTag,3,3);
1231 roll_image->type=image->type;
1232 if (status == MagickFalse)
1233 roll_image=DestroyImage(roll_image);
1234 return(roll_image);
1235}
1236
1237/*
1238%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1239% %
1240% %
1241% %
1242% S h a v e I m a g e %
1243% %
1244% %
1245% %
1246%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1247%
1248% ShaveImage() shaves pixels from the image edges. It allocates the memory
1249% necessary for the new Image structure and returns a pointer to the new
1250% image.
1251%
1252% The format of the ShaveImage method is:
1253%
1254% Image *ShaveImage(const Image *image,const RectangleInfo *shave_info,
1255% ExceptionInfo *exception)
1256%
1257% A description of each parameter follows:
1258%
1259% o shave_image: Method ShaveImage returns a pointer to the shaved
1260% image. A null image is returned if there is a memory shortage or
1261% if the image width or height is zero.
1262%
1263% o image: the image.
1264%
1265% o shave_info: Specifies a pointer to a RectangleInfo which defines the
1266% region of the image to crop.
1267%
1268% o exception: return any errors or warnings in this structure.
1269%
1270*/
1271MagickExport Image *ShaveImage(const Image *image,
1272 const RectangleInfo *shave_info,ExceptionInfo *exception)
1273{
1274 Image
1275 *shave_image;
1276
1277 RectangleInfo
1278 geometry;
1279
1280 assert(image != (const Image *) NULL);
1281 assert(image->signature == MagickSignature);
1282 if (image->debug != MagickFalse)
1283 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1284 if (((2*shave_info->width) >= image->columns) ||
1285 ((2*shave_info->height) >= image->rows))
1286 ThrowImageException(OptionWarning,"GeometryDoesNotContainImage");
1287 SetGeometry(image,&geometry);
1288 geometry.width-=2*shave_info->width;
1289 geometry.height-=2*shave_info->height;
1290 geometry.x=(long) shave_info->width+image->page.x;
1291 geometry.y=(long) shave_info->height+image->page.y;
1292 shave_image=CropImage(image,&geometry,exception);
1293 if (shave_image == (Image *) NULL)
1294 return((Image *) NULL);
1295 shave_image->page.width-=2*shave_info->width;
1296 shave_image->page.height-=2*shave_info->height;
1297 shave_image->page.x-=shave_info->width;
1298 shave_image->page.y-=shave_info->height;
1299 return(shave_image);
1300}
1301
1302/*
1303%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1304% %
1305% %
1306% %
1307% S p l i c e I m a g e %
1308% %
1309% %
1310% %
1311%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1312%
1313% SpliceImage() splices a solid color into the image as defined by the
1314% geometry.
1315%
1316% The format of the SpliceImage method is:
1317%
1318% Image *SpliceImage(const Image *image,const RectangleInfo *geometry,
1319% ExceptionInfo *exception)
1320%
1321% A description of each parameter follows:
1322%
1323% o image: the image.
1324%
1325% o geometry: Define the region of the image to splice with members
1326% x, y, width, and height.
1327%
1328% o exception: return any errors or warnings in this structure.
1329%
1330*/
1331MagickExport Image *SpliceImage(const Image *image,
1332 const RectangleInfo *geometry,ExceptionInfo *exception)
1333{
1334#define SpliceImageTag "Splice/Image"
1335
cristyc4c8d132010-01-07 01:58:38 +00001336 CacheView
1337 *image_view,
1338 *splice_view;
1339
cristy3ed852e2009-09-05 21:47:34 +00001340 Image
1341 *splice_image;
1342
1343 long
1344 progress,
1345 y;
1346
1347 MagickBooleanType
1348 proceed,
1349 status;
1350
1351 RectangleInfo
1352 splice_geometry;
1353
1354 register long
1355 i;
1356
cristy3ed852e2009-09-05 21:47:34 +00001357 /*
1358 Allocate splice image.
1359 */
1360 assert(image != (const Image *) NULL);
1361 assert(image->signature == MagickSignature);
1362 if (image->debug != MagickFalse)
1363 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1364 assert(geometry != (const RectangleInfo *) NULL);
1365 assert(exception != (ExceptionInfo *) NULL);
1366 assert(exception->signature == MagickSignature);
1367 splice_geometry=(*geometry);
1368 splice_image=CloneImage(image,image->columns+splice_geometry.width,
1369 image->rows+splice_geometry.height,MagickTrue,exception);
1370 if (splice_image == (Image *) NULL)
1371 return((Image *) NULL);
1372 if (SetImageStorageClass(splice_image,DirectClass) == MagickFalse)
1373 {
1374 InheritException(exception,&splice_image->exception);
1375 splice_image=DestroyImage(splice_image);
1376 return((Image *) NULL);
1377 }
1378 (void) SetImageBackgroundColor(splice_image);
1379 /*
1380 Respect image geometry.
1381 */
1382 switch (image->gravity)
1383 {
1384 default:
1385 case UndefinedGravity:
1386 case NorthWestGravity:
1387 break;
1388 case NorthGravity:
1389 {
1390 splice_geometry.x+=splice_geometry.width/2;
1391 break;
1392 }
1393 case NorthEastGravity:
1394 {
1395 splice_geometry.x+=splice_geometry.width;
1396 break;
1397 }
1398 case WestGravity:
1399 {
1400 splice_geometry.y+=splice_geometry.width/2;
1401 break;
1402 }
1403 case StaticGravity:
1404 case CenterGravity:
1405 {
1406 splice_geometry.x+=splice_geometry.width/2;
1407 splice_geometry.y+=splice_geometry.height/2;
1408 break;
1409 }
1410 case EastGravity:
1411 {
1412 splice_geometry.x+=splice_geometry.width;
1413 splice_geometry.y+=splice_geometry.height/2;
1414 break;
1415 }
1416 case SouthWestGravity:
1417 {
1418 splice_geometry.y+=splice_geometry.height;
1419 break;
1420 }
1421 case SouthGravity:
1422 {
1423 splice_geometry.x+=splice_geometry.width/2;
1424 splice_geometry.y+=splice_geometry.height;
1425 break;
1426 }
1427 case SouthEastGravity:
1428 {
1429 splice_geometry.x+=splice_geometry.width;
1430 splice_geometry.y+=splice_geometry.height;
1431 break;
1432 }
1433 }
1434 /*
1435 Splice image.
1436 */
1437 status=MagickTrue;
1438 i=0;
1439 progress=0;
1440 image_view=AcquireCacheView(image);
1441 splice_view=AcquireCacheView(splice_image);
cristyb5d5f722009-11-04 03:03:49 +00001442#if defined(MAGICKCORE_OPENMP_SUPPORT)
1443 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00001444#endif
1445 for (y=0; y < (long) splice_geometry.y; y++)
1446 {
1447 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001448 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001449
1450 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001451 *restrict indexes,
1452 *restrict splice_indexes;
cristy3ed852e2009-09-05 21:47:34 +00001453
1454 register long
1455 x;
1456
1457 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001458 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001459
1460 if (status == MagickFalse)
1461 continue;
1462 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1463 q=QueueCacheViewAuthenticPixels(splice_view,0,y,splice_image->columns,1,
1464 exception);
1465 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
1466 {
1467 status=MagickFalse;
1468 continue;
1469 }
1470 indexes=GetCacheViewAuthenticIndexQueue(image_view);
1471 splice_indexes=GetCacheViewAuthenticIndexQueue(splice_view);
1472 for (x=0; x < splice_geometry.x; x++)
1473 {
cristyce70c172010-01-07 17:15:30 +00001474 SetRedPixelComponent(q,GetRedPixelComponent(p));
1475 SetGreenPixelComponent(q,GetGreenPixelComponent(p));
1476 SetBluePixelComponent(q,GetBluePixelComponent(p));
1477 SetOpacityPixelComponent(q,OpaqueOpacity);
cristy3ed852e2009-09-05 21:47:34 +00001478 if (image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00001479 SetOpacityPixelComponent(q,GetOpacityPixelComponent(p));
cristy3ed852e2009-09-05 21:47:34 +00001480 if (image->colorspace == CMYKColorspace)
1481 splice_indexes[x]=(*indexes++);
1482 p++;
1483 q++;
1484 }
1485 for ( ; x < (long) (splice_geometry.x+splice_geometry.width); x++)
1486 q++;
1487 for ( ; x < (long) splice_image->columns; x++)
1488 {
cristyce70c172010-01-07 17:15:30 +00001489 SetRedPixelComponent(q,GetRedPixelComponent(p));
1490 SetGreenPixelComponent(q,GetGreenPixelComponent(p));
1491 SetBluePixelComponent(q,GetBluePixelComponent(p));
1492 SetOpacityPixelComponent(q,OpaqueOpacity);
cristy3ed852e2009-09-05 21:47:34 +00001493 if (image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00001494 SetOpacityPixelComponent(q,GetOpacityPixelComponent(p));
cristy3ed852e2009-09-05 21:47:34 +00001495 if (image->colorspace == CMYKColorspace)
1496 splice_indexes[x]=(*indexes++);
1497 p++;
1498 q++;
1499 }
1500 if (SyncCacheViewAuthenticPixels(splice_view,exception) == MagickFalse)
1501 status=MagickFalse;
1502 proceed=SetImageProgress(image,SpliceImageTag,y,splice_image->rows);
1503 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1504 {
1505 MagickBooleanType
1506 proceed;
1507
cristyb5d5f722009-11-04 03:03:49 +00001508#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00001509 #pragma omp critical (MagickCore_TransposeImage)
1510#endif
1511 proceed=SetImageProgress(image,SpliceImageTag,progress++,
1512 splice_image->rows);
1513 if (proceed == MagickFalse)
1514 status=MagickFalse;
1515 }
1516 }
cristyb5d5f722009-11-04 03:03:49 +00001517#if defined(MAGICKCORE_OPENMP_SUPPORT)
1518 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00001519#endif
1520 for (y=(long) (splice_geometry.y+splice_geometry.height);
1521 y < (long) splice_image->rows; y++)
1522 {
1523 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001524 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001525
1526 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001527 *restrict indexes,
1528 *restrict splice_indexes;
cristy3ed852e2009-09-05 21:47:34 +00001529
1530 register long
1531 x;
1532
1533 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001534 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001535
1536 if (status == MagickFalse)
1537 continue;
1538 p=GetCacheViewVirtualPixels(image_view,0,y-splice_geometry.height,
1539 image->columns,1,exception);
1540 q=QueueCacheViewAuthenticPixels(splice_view,0,y,splice_image->columns,1,
1541 exception);
1542 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
1543 {
1544 status=MagickFalse;
1545 continue;
1546 }
1547 indexes=GetCacheViewAuthenticIndexQueue(image_view);
1548 splice_indexes=GetCacheViewAuthenticIndexQueue(splice_view);
1549 for (x=0; x < splice_geometry.x; x++)
1550 {
cristyce70c172010-01-07 17:15:30 +00001551 SetRedPixelComponent(q,GetRedPixelComponent(p));
1552 SetGreenPixelComponent(q,GetGreenPixelComponent(p));
1553 SetBluePixelComponent(q,GetBluePixelComponent(p));
1554 SetOpacityPixelComponent(q,OpaqueOpacity);
cristy3ed852e2009-09-05 21:47:34 +00001555 if (image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00001556 SetOpacityPixelComponent(q,GetOpacityPixelComponent(p));
cristy3ed852e2009-09-05 21:47:34 +00001557 if (image->colorspace == CMYKColorspace)
1558 splice_indexes[x]=(*indexes++);
1559 p++;
1560 q++;
1561 }
1562 for ( ; x < (long) (splice_geometry.x+splice_geometry.width); x++)
1563 q++;
1564 for ( ; x < (long) splice_image->columns; x++)
1565 {
cristyce70c172010-01-07 17:15:30 +00001566 SetRedPixelComponent(q,GetRedPixelComponent(p));
1567 SetGreenPixelComponent(q,GetGreenPixelComponent(p));
1568 SetBluePixelComponent(q,GetBluePixelComponent(p));
1569 SetOpacityPixelComponent(q,OpaqueOpacity);
cristy3ed852e2009-09-05 21:47:34 +00001570 if (image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00001571 SetOpacityPixelComponent(q,GetOpacityPixelComponent(p));
cristy3ed852e2009-09-05 21:47:34 +00001572 if (image->colorspace == CMYKColorspace)
1573 splice_indexes[x]=(*indexes++);
1574 p++;
1575 q++;
1576 }
1577 if (SyncCacheViewAuthenticPixels(splice_view,exception) == MagickFalse)
1578 status=MagickFalse;
1579 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1580 {
1581 MagickBooleanType
1582 proceed;
1583
cristyb5d5f722009-11-04 03:03:49 +00001584#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00001585 #pragma omp critical (MagickCore_TransposeImage)
1586#endif
1587 proceed=SetImageProgress(image,SpliceImageTag,progress++,
1588 splice_image->rows);
1589 if (proceed == MagickFalse)
1590 status=MagickFalse;
1591 }
1592 }
1593 splice_view=DestroyCacheView(splice_view);
1594 image_view=DestroyCacheView(image_view);
1595 if (status == MagickFalse)
1596 splice_image=DestroyImage(splice_image);
1597 return(splice_image);
1598}
1599
1600/*
1601%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1602% %
1603% %
1604% %
1605% T r a n s f o r m I m a g e %
1606% %
1607% %
1608% %
1609%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1610%
1611% TransformImage() is a convenience method that behaves like ResizeImage() or
1612% CropImage() but accepts scaling and/or cropping information as a region
1613% geometry specification. If the operation fails, the original image handle
1614% is returned.
1615%
1616% The format of the TransformImage method is:
1617%
1618% MagickBooleanType TransformImage(Image **image,const char *crop_geometry,
1619% const char *image_geometry)
1620%
1621% A description of each parameter follows:
1622%
1623% o image: the image The transformed image is returned as this parameter.
1624%
1625% o crop_geometry: A crop geometry string. This geometry defines a
1626% subregion of the image to crop.
1627%
1628% o image_geometry: An image geometry string. This geometry defines the
1629% final size of the image.
1630%
1631*/
cristy508d9312010-02-10 21:10:30 +00001632static inline long MagickRound(MagickRealType x)
anthonyc0429ed2009-12-27 08:22:42 +00001633{
cristy508d9312010-02-10 21:10:30 +00001634 /*
1635 Round the fraction to nearest integer.
1636 */
anthonyc0429ed2009-12-27 08:22:42 +00001637 if (x >= 0.0)
cristy508d9312010-02-10 21:10:30 +00001638 return((long) (x+0.5));
1639 return((long) (x-0.5));
anthonyc0429ed2009-12-27 08:22:42 +00001640}
1641
cristy3ed852e2009-09-05 21:47:34 +00001642MagickExport MagickBooleanType TransformImage(Image **image,
1643 const char *crop_geometry,const char *image_geometry)
1644{
1645 Image
cristy508d9312010-02-10 21:10:30 +00001646 *next,
cristy3ed852e2009-09-05 21:47:34 +00001647 *resize_image,
1648 *transform_image;
1649
cristy508d9312010-02-10 21:10:30 +00001650 long
1651 x,
1652 y;
1653
cristy3ed852e2009-09-05 21:47:34 +00001654 MagickStatusType
1655 flags;
1656
1657 RectangleInfo
1658 geometry;
1659
cristy508d9312010-02-10 21:10:30 +00001660 unsigned long
1661 height,
1662 width;
1663
cristy3ed852e2009-09-05 21:47:34 +00001664 assert(image != (Image **) NULL);
1665 assert((*image)->signature == MagickSignature);
1666 if ((*image)->debug != MagickFalse)
1667 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
1668 transform_image=(*image);
1669 if (crop_geometry != (const char *) NULL)
1670 {
1671 Image
1672 *crop_image;
1673
1674 RectangleInfo
1675 geometry;
1676
1677 /*
1678 Crop image to a user specified size.
1679 */
1680 crop_image=NewImageList();
1681 flags=ParseGravityGeometry(transform_image,crop_geometry,&geometry,
1682 &(*image)->exception);
cristy508d9312010-02-10 21:10:30 +00001683 if ((flags & AreaValue) != 0)
anthonyc0429ed2009-12-27 08:22:42 +00001684 {
cristy508d9312010-02-10 21:10:30 +00001685 PointInfo
cristy4eced732010-02-10 22:26:28 +00001686 delta,
1687 offset;
anthonyc0429ed2009-12-27 08:22:42 +00001688
1689 RectangleInfo
1690 crop;
1691
cristy508d9312010-02-10 21:10:30 +00001692 /*
1693 Crop into NxM tiles (@ flag) - AT.
1694 */
1695 if (geometry.width == 0)
1696 geometry.width=1;
1697 if (geometry.height == 0)
1698 geometry.height=1;
1699 width=transform_image->columns;
1700 height=transform_image->rows;
anthonyc0429ed2009-12-27 08:22:42 +00001701 if ((flags & AspectValue) == 0)
1702 {
cristy508d9312010-02-10 21:10:30 +00001703 width-=(geometry.x < 0 ? -1 : 1)*geometry.x;
1704 height-=(geometry.y < 0 ? -1 : 1)*geometry.y;
anthonyc0429ed2009-12-27 08:22:42 +00001705 }
1706 else
1707 {
cristy508d9312010-02-10 21:10:30 +00001708 width+=(geometry.x < 0 ? -1 : 1)*geometry.x;
1709 height+=(geometry.y < 0 ? -1 : 1)*geometry.y;
anthonyc0429ed2009-12-27 08:22:42 +00001710 }
cristy508d9312010-02-10 21:10:30 +00001711 delta.x=(double) width/geometry.width;
1712 delta.y=(double) height/geometry.height;
anthonyc0429ed2009-12-27 08:22:42 +00001713 next=NewImageList();
cristy4eced732010-02-10 22:26:28 +00001714 for (offset.y=0; offset.y < (double) height; )
anthonyc0429ed2009-12-27 08:22:42 +00001715 {
1716 if ((flags & AspectValue) == 0)
1717 {
cristy4eced732010-02-10 22:26:28 +00001718 crop.y=(long) MagickRound((MagickRealType) (offset.y-
cristy508d9312010-02-10 21:10:30 +00001719 (geometry.y > 0 ? 0 : geometry.y)));
cristy4eced732010-02-10 22:26:28 +00001720 offset.y+=delta.y;
cristy508d9312010-02-10 21:10:30 +00001721 crop.height=(unsigned long) MagickRound((MagickRealType)
cristy4eced732010-02-10 22:26:28 +00001722 (offset.y+(geometry.y < 0 ? 0 : geometry.y)));
anthonyc0429ed2009-12-27 08:22:42 +00001723 }
1724 else
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 ? geometry.y : 0)));
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 ? geometry.y : 0)));
anthonyc0429ed2009-12-27 08:22:42 +00001731 }
cristy508d9312010-02-10 21:10:30 +00001732 crop.height-=crop.y;
cristy4eced732010-02-10 22:26:28 +00001733 for (offset.x=0; offset.x < (double) width; )
anthonyc0429ed2009-12-27 08:22:42 +00001734 {
1735 if ((flags & AspectValue) == 0)
1736 {
cristy4eced732010-02-10 22:26:28 +00001737 crop.x=(long) MagickRound((MagickRealType) (offset.x-
cristy508d9312010-02-10 21:10:30 +00001738 (geometry.x > 0 ? 0 : geometry.x)));
cristy4eced732010-02-10 22:26:28 +00001739 offset.x+=+delta.x;
cristy508d9312010-02-10 21:10:30 +00001740 crop.width=(unsigned long) MagickRound((MagickRealType)
cristy4eced732010-02-10 22:26:28 +00001741 (offset.x+(geometry.x < 0 ? 0 : geometry.x)));
anthonyc0429ed2009-12-27 08:22:42 +00001742 }
1743 else
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 ? geometry.x : 0)));
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 ? geometry.x : 0)));
anthonyc0429ed2009-12-27 08:22:42 +00001750 }
cristy508d9312010-02-10 21:10:30 +00001751 crop.width-=crop.x;
anthonyc0429ed2009-12-27 08:22:42 +00001752 next=CropImage(transform_image,&crop,&(*image)->exception);
1753 if (next == (Image *) NULL)
1754 break;
1755 AppendImageToList(&crop_image,next);
1756 }
1757 if (next == (Image *) NULL)
1758 break;
1759 }
1760 }
cristy508d9312010-02-10 21:10:30 +00001761 else
1762 if (((geometry.width == 0) && (geometry.height == 0)) ||
anthonyc0429ed2009-12-27 08:22:42 +00001763 ((flags & XValue) != 0) || ((flags & YValue) != 0))
cristy3ed852e2009-09-05 21:47:34 +00001764 {
cristy508d9312010-02-10 21:10:30 +00001765 /*
1766 Crop a single region at +X+Y.
1767 */
1768 crop_image=CropImage(transform_image,&geometry,
1769 &(*image)->exception);
1770 if ((crop_image != (Image *) NULL) && ((flags & AspectValue) != 0))
1771 {
1772 crop_image->page.width=geometry.width;
1773 crop_image->page.height=geometry.height;
1774 crop_image->page.x-=geometry.x;
1775 crop_image->page.y-=geometry.y;
1776 }
1777 }
1778 else
1779 if ((transform_image->columns > geometry.width) ||
1780 (transform_image->rows > geometry.height))
1781 {
1782 MagickBooleanType
1783 proceed;
1784
1785 MagickProgressMonitor
1786 progress_monitor;
1787
1788 MagickOffsetType
1789 i;
1790
1791 MagickSizeType
1792 number_images;
1793
1794 /*
1795 Crop into tiles of fixed size WxH.
1796 */
1797 if (transform_image->page.width == 0)
1798 transform_image->page.width=transform_image->columns;
1799 if (transform_image->page.height == 0)
1800 transform_image->page.height=transform_image->rows;
1801 width=geometry.width;
1802 if (width == 0)
1803 width=transform_image->page.width;
1804 height=geometry.height;
1805 if (height == 0)
1806 height=transform_image->page.height;
1807 next=NewImageList();
1808 proceed=MagickTrue;
1809 i=0;
cristy0e9f9c12010-02-11 03:00:47 +00001810 number_images=0;
1811 for (y=0; y < (long) transform_image->page.height; y+=height)
1812 for (x=0; x < (long) transform_image->page.width; x+=width)
1813 number_images++;
cristy508d9312010-02-10 21:10:30 +00001814 for (y=0; y < (long) transform_image->page.height; y+=height)
1815 {
1816 for (x=0; x < (long) transform_image->page.width; x+=width)
1817 {
1818 progress_monitor=SetImageProgressMonitor(transform_image,
1819 (MagickProgressMonitor) NULL,transform_image->client_data);
1820 geometry.width=width;
1821 geometry.height=height;
1822 geometry.x=x;
1823 geometry.y=y;
1824 next=CropImage(transform_image,&geometry,&(*image)->exception);
1825 (void) SetImageProgressMonitor(transform_image,
1826 progress_monitor,transform_image->client_data);
1827 proceed=SetImageProgress(transform_image,CropImageTag,i++,
1828 number_images);
1829 if (proceed == MagickFalse)
1830 break;
1831 if (next == (Image *) NULL)
1832 break;
cristy36507e62010-02-11 00:26:48 +00001833 (void) SetImageProgressMonitor(next,progress_monitor,
1834 next->client_data);
cristy508d9312010-02-10 21:10:30 +00001835 AppendImageToList(&crop_image,next);
1836 }
1837 if (next == (Image *) NULL)
1838 break;
1839 if (proceed == MagickFalse)
1840 break;
1841 }
1842 }
cristy3ed852e2009-09-05 21:47:34 +00001843 if (crop_image == (Image *) NULL)
1844 transform_image=CloneImage(*image,0,0,MagickTrue,&(*image)->exception);
1845 else
1846 {
1847 transform_image=DestroyImage(transform_image);
1848 transform_image=GetFirstImageInList(crop_image);
1849 }
1850 *image=transform_image;
1851 }
1852 if (image_geometry == (const char *) NULL)
1853 return(MagickTrue);
1854 /*
1855 Scale image to a user specified size.
1856 */
1857 flags=ParseRegionGeometry(transform_image,image_geometry,&geometry,
1858 &(*image)->exception);
1859 if ((transform_image->columns == geometry.width) &&
1860 (transform_image->rows == geometry.height))
1861 return(MagickTrue);
1862 resize_image=ZoomImage(transform_image,geometry.width,geometry.height,
1863 &(*image)->exception);
1864 if (resize_image == (Image *) NULL)
1865 return(MagickFalse);
1866 transform_image=DestroyImage(transform_image);
1867 transform_image=resize_image;
1868 *image=transform_image;
1869 return(MagickTrue);
1870}
1871
1872/*
1873%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1874% %
1875% %
1876% %
1877% T r a n s f o r m I m a g e s %
1878% %
1879% %
1880% % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1881%
1882% TransformImages() calls TransformImage() on each image of a sequence.
1883%
1884% The format of the TransformImage method is:
1885%
1886% MagickBooleanType TransformImages(Image **image,
1887% const char *crop_geometry,const char *image_geometry)
1888%
1889% A description of each parameter follows:
1890%
1891% o image: the image The transformed image is returned as this parameter.
1892%
1893% o crop_geometry: A crop geometry string. This geometry defines a
1894% subregion of the image to crop.
1895%
1896% o image_geometry: An image geometry string. This geometry defines the
1897% final size of the image.
1898%
1899*/
1900MagickExport MagickBooleanType TransformImages(Image **images,
1901 const char *crop_geometry,const char *image_geometry)
1902{
1903 Image
1904 *image,
1905 **image_list,
1906 *transform_images;
1907
1908 MagickStatusType
1909 status;
1910
1911 register long
1912 i;
1913
1914 assert(images != (Image **) NULL);
1915 assert((*images)->signature == MagickSignature);
1916 if ((*images)->debug != MagickFalse)
1917 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
1918 (*images)->filename);
1919 image_list=ImageListToArray(*images,&(*images)->exception);
1920 if (image_list == (Image **) NULL)
1921 return(MagickFalse);
1922 status=MagickTrue;
1923 transform_images=NewImageList();
1924 for (i=0; image_list[i] != (Image *) NULL; i++)
1925 {
1926 image=image_list[i];
1927 status|=TransformImage(&image,crop_geometry,image_geometry);
1928 AppendImageToList(&transform_images,image);
1929 }
1930 *images=transform_images;
1931 image_list=(Image **) RelinquishMagickMemory(image_list);
1932 return(status != 0 ? MagickTrue : MagickFalse);
1933}
1934
1935/*
1936%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1937% %
1938% %
1939% %
1940% T r a n s p o s e I m a g e %
1941% %
1942% %
1943% %
1944%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1945%
1946% TransposeImage() creates a horizontal mirror image by reflecting the pixels
1947% around the central y-axis while rotating them by 90 degrees.
1948%
1949% The format of the TransposeImage method is:
1950%
1951% Image *TransposeImage(const Image *image,ExceptionInfo *exception)
1952%
1953% A description of each parameter follows:
1954%
1955% o image: the image.
1956%
1957% o exception: return any errors or warnings in this structure.
1958%
1959*/
1960MagickExport Image *TransposeImage(const Image *image,ExceptionInfo *exception)
1961{
1962#define TransposeImageTag "Transpose/Image"
1963
cristyc4c8d132010-01-07 01:58:38 +00001964 CacheView
1965 *image_view,
1966 *transpose_view;
1967
cristy3ed852e2009-09-05 21:47:34 +00001968 Image
1969 *transpose_image;
1970
1971 long
1972 progress,
1973 y;
1974
1975 MagickBooleanType
1976 status;
1977
1978 RectangleInfo
1979 page;
1980
cristy3ed852e2009-09-05 21:47:34 +00001981 assert(image != (const Image *) NULL);
1982 assert(image->signature == MagickSignature);
1983 if (image->debug != MagickFalse)
1984 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1985 assert(exception != (ExceptionInfo *) NULL);
1986 assert(exception->signature == MagickSignature);
1987 transpose_image=CloneImage(image,image->rows,image->columns,MagickTrue,
1988 exception);
1989 if (transpose_image == (Image *) NULL)
1990 return((Image *) NULL);
1991 /*
1992 Transpose image.
1993 */
1994 status=MagickTrue;
1995 progress=0;
1996 image_view=AcquireCacheView(image);
1997 transpose_view=AcquireCacheView(transpose_image);
cristyb5d5f722009-11-04 03:03:49 +00001998#if defined(MAGICKCORE_OPENMP_SUPPORT)
1999 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00002000#endif
2001 for (y=0; y < (long) image->rows; y++)
2002 {
2003 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002004 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002005
2006 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002007 *restrict transpose_indexes,
2008 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002009
2010 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002011 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002012
2013 if (status == MagickFalse)
2014 continue;
2015 p=GetCacheViewVirtualPixels(image_view,0,(long) image->rows-y-1,
2016 image->columns,1,exception);
2017 q=QueueCacheViewAuthenticPixels(transpose_view,(long) (image->rows-y-1),0,
2018 1,transpose_image->rows,exception);
2019 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2020 {
2021 status=MagickFalse;
2022 continue;
2023 }
2024 (void) CopyMagickMemory(q,p,(size_t) image->columns*sizeof(*q));
2025 indexes=GetCacheViewAuthenticIndexQueue(image_view);
2026 if (indexes != (IndexPacket *) NULL)
2027 {
2028 transpose_indexes=GetCacheViewAuthenticIndexQueue(transpose_view);
2029 if (transpose_indexes != (IndexPacket *) NULL)
2030 (void) CopyMagickMemory(transpose_indexes,indexes,(size_t)
2031 image->columns*sizeof(*transpose_indexes));
2032 }
2033 if (SyncCacheViewAuthenticPixels(transpose_view,exception) == MagickFalse)
2034 status=MagickFalse;
2035 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2036 {
2037 MagickBooleanType
2038 proceed;
2039
cristyb5d5f722009-11-04 03:03:49 +00002040#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002041 #pragma omp critical (MagickCore_TransposeImage)
2042#endif
2043 proceed=SetImageProgress(image,TransposeImageTag,progress++,
2044 image->rows);
2045 if (proceed == MagickFalse)
2046 status=MagickFalse;
2047 }
2048 }
2049 transpose_view=DestroyCacheView(transpose_view);
2050 image_view=DestroyCacheView(image_view);
2051 transpose_image->type=image->type;
2052 page=transpose_image->page;
2053 Swap(page.width,page.height);
2054 Swap(page.x,page.y);
2055 if (page.width != 0)
2056 page.x=(long) (page.width-transpose_image->columns-page.x);
2057 transpose_image->page=page;
2058 if (status == MagickFalse)
2059 transpose_image=DestroyImage(transpose_image);
2060 return(transpose_image);
2061}
2062
2063/*
2064%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2065% %
2066% %
2067% %
2068% T r a n s v e r s e I m a g e %
2069% %
2070% %
2071% %
2072%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2073%
2074% TransverseImage() creates a vertical mirror image by reflecting the pixels
2075% around the central x-axis while rotating them by 270 degrees.
2076%
2077% The format of the TransverseImage method is:
2078%
2079% Image *TransverseImage(const Image *image,ExceptionInfo *exception)
2080%
2081% A description of each parameter follows:
2082%
2083% o image: the image.
2084%
2085% o exception: return any errors or warnings in this structure.
2086%
2087*/
2088MagickExport Image *TransverseImage(const Image *image,ExceptionInfo *exception)
2089{
2090#define TransverseImageTag "Transverse/Image"
2091
cristyc4c8d132010-01-07 01:58:38 +00002092 CacheView
2093 *image_view,
2094 *transverse_view;
2095
cristy3ed852e2009-09-05 21:47:34 +00002096 Image
2097 *transverse_image;
2098
2099 long
2100 progress,
2101 y;
2102
2103 MagickBooleanType
2104 status;
2105
2106 RectangleInfo
2107 page;
2108
cristy3ed852e2009-09-05 21:47:34 +00002109 assert(image != (const Image *) NULL);
2110 assert(image->signature == MagickSignature);
2111 if (image->debug != MagickFalse)
2112 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2113 assert(exception != (ExceptionInfo *) NULL);
2114 assert(exception->signature == MagickSignature);
2115 transverse_image=CloneImage(image,image->rows,image->columns,MagickTrue,
2116 exception);
2117 if (transverse_image == (Image *) NULL)
2118 return((Image *) NULL);
2119 /*
2120 Transverse image.
2121 */
2122 status=MagickTrue;
2123 progress=0;
2124 image_view=AcquireCacheView(image);
2125 transverse_view=AcquireCacheView(transverse_image);
cristyb5d5f722009-11-04 03:03:49 +00002126#if defined(MAGICKCORE_OPENMP_SUPPORT)
2127 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00002128#endif
2129 for (y=0; y < (long) image->rows; y++)
2130 {
2131 MagickBooleanType
2132 sync;
2133
2134 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002135 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002136
2137 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002138 *restrict transverse_indexes,
2139 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002140
2141 register long
2142 x;
2143
2144 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002145 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002146
2147 if (status == MagickFalse)
2148 continue;
2149 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
2150 q=QueueCacheViewAuthenticPixels(transverse_view,(long) (image->rows-y-
2151 1),0,1,transverse_image->rows,exception);
2152 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2153 {
2154 status=MagickFalse;
2155 continue;
2156 }
2157 q+=image->columns;
2158 for (x=0; x < (long) image->columns; x++)
2159 *--q=(*p++);
2160 indexes=GetCacheViewAuthenticIndexQueue(image_view);
2161 if (indexes != (IndexPacket *) NULL)
2162 {
2163 transverse_indexes=GetCacheViewAuthenticIndexQueue(transverse_view);
2164 if (transverse_indexes != (IndexPacket *) NULL)
2165 for (x=0; x < (long) image->columns; x++)
2166 transverse_indexes[image->columns-x-1]=indexes[x];
2167 }
2168 sync=SyncCacheViewAuthenticPixels(transverse_view,exception);
2169 if (sync == MagickFalse)
2170 status=MagickFalse;
2171 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2172 {
2173 MagickBooleanType
2174 proceed;
2175
cristyb5d5f722009-11-04 03:03:49 +00002176#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002177 #pragma omp critical (MagickCore_TransverseImage)
2178#endif
2179 proceed=SetImageProgress(image,TransverseImageTag,progress++,
2180 image->rows);
2181 if (proceed == MagickFalse)
2182 status=MagickFalse;
2183 }
2184 }
2185 transverse_view=DestroyCacheView(transverse_view);
2186 image_view=DestroyCacheView(image_view);
2187 transverse_image->type=image->type;
2188 page=transverse_image->page;
2189 Swap(page.width,page.height);
2190 Swap(page.x,page.y);
2191 if (page.height != 0)
2192 page.y=(long) (page.height-transverse_image->rows-page.y);
2193 transverse_image->page=page;
2194 if (status == MagickFalse)
2195 transverse_image=DestroyImage(transverse_image);
2196 return(transverse_image);
2197}
2198
2199/*
2200%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2201% %
2202% %
2203% %
2204% T r i m I m a g e %
2205% %
2206% %
2207% %
2208%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2209%
2210% TrimImage() trims pixels from the image edges. It allocates the memory
2211% necessary for the new Image structure and returns a pointer to the new
2212% image.
2213%
2214% The format of the TrimImage method is:
2215%
2216% Image *TrimImage(const Image *image,ExceptionInfo *exception)
2217%
2218% A description of each parameter follows:
2219%
2220% o image: the image.
2221%
2222% o exception: return any errors or warnings in this structure.
2223%
2224*/
2225MagickExport Image *TrimImage(const Image *image,ExceptionInfo *exception)
2226{
2227 RectangleInfo
2228 geometry;
2229
2230 assert(image != (const Image *) NULL);
2231 assert(image->signature == MagickSignature);
2232 if (image->debug != MagickFalse)
2233 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2234 geometry=GetImageBoundingBox(image,exception);
2235 if ((geometry.width == 0) || (geometry.height == 0))
2236 {
2237 Image
2238 *crop_image;
2239
2240 crop_image=CloneImage(image,1,1,MagickTrue,exception);
2241 if (crop_image == (Image *) NULL)
2242 return((Image *) NULL);
2243 crop_image->background_color.opacity=(Quantum) TransparentOpacity;
2244 (void) SetImageBackgroundColor(crop_image);
2245 crop_image->page=image->page;
2246 crop_image->page.x=(-1);
2247 crop_image->page.y=(-1);
2248 return(crop_image);
2249 }
2250 geometry.x+=image->page.x;
2251 geometry.y+=image->page.y;
2252 return(CropImage(image,&geometry,exception));
2253}