blob: 6f1e5d1f7d934fc8df52390982ad7bdcdc2e85f5 [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"
50#include "magick/effect.h"
51#include "magick/exception.h"
52#include "magick/exception-private.h"
53#include "magick/geometry.h"
54#include "magick/image.h"
55#include "magick/memory_.h"
56#include "magick/layer.h"
57#include "magick/list.h"
58#include "magick/monitor.h"
59#include "magick/monitor-private.h"
60#include "magick/pixel-private.h"
61#include "magick/resource_.h"
62#include "magick/resize.h"
63#include "magick/statistic.h"
64#include "magick/string_.h"
65#include "magick/transform.h"
66
67/*
68%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
69% %
70% %
71% %
72% C h o p I m a g e %
73% %
74% %
75% %
76%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
77%
78% Chop() removes a region of an image and collapses the image to occupy the
79% removed portion.
80%
81% The format of the ChopImage method is:
82%
83% Image *ChopImage(const Image *image,const RectangleInfo *chop_info)
84% ExceptionInfo *exception)
85%
86% A description of each parameter follows:
87%
88% o image: the image.
89%
90% o chop_info: Define the region of the image to chop.
91%
92% o exception: return any errors or warnings in this structure.
93%
94*/
95MagickExport Image *ChopImage(const Image *image,const RectangleInfo *chop_info,
96 ExceptionInfo *exception)
97{
98#define ChopImageTag "Chop/Image"
99
100 Image
101 *chop_image;
102
103 long
104 j,
105 y;
106
107 MagickBooleanType
108 proceed;
109
110 RectangleInfo
111 extent;
112
113 register long
114 i;
115
116 CacheView
117 *chop_view,
118 *image_view;
119
120 /*
121 Check chop geometry.
122 */
123 assert(image != (const Image *) NULL);
124 assert(image->signature == MagickSignature);
125 if (image->debug != MagickFalse)
126 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
127 assert(exception != (ExceptionInfo *) NULL);
128 assert(exception->signature == MagickSignature);
129 assert(chop_info != (RectangleInfo *) NULL);
130 if (((chop_info->x+(long) chop_info->width) < 0) ||
131 ((chop_info->y+(long) chop_info->height) < 0) ||
132 (chop_info->x > (long) image->columns) ||
133 (chop_info->y > (long) image->rows))
134 ThrowImageException(OptionWarning,"GeometryDoesNotContainImage");
135 extent=(*chop_info);
136 if ((extent.x+(long) extent.width) > (long) image->columns)
137 extent.width=(unsigned long) ((long) image->columns-extent.x);
138 if ((extent.y+(long) extent.height) > (long) image->rows)
139 extent.height=(unsigned long) ((long) image->rows-extent.y);
140 if (extent.x < 0)
141 {
142 extent.width-=(unsigned long) (-extent.x);
143 extent.x=0;
144 }
145 if (extent.y < 0)
146 {
147 extent.height-=(unsigned long) (-extent.y);
148 extent.y=0;
149 }
150 chop_image=CloneImage(image,image->columns-extent.width,image->rows-
151 extent.height,MagickTrue,exception);
152 if (chop_image == (Image *) NULL)
153 return((Image *) NULL);
154 /*
155 Extract chop image.
156 */
157 i=0;
158 j=0;
159 image_view=AcquireCacheView(image);
160 chop_view=AcquireCacheView(chop_image);
161 for (y=0; y < (long) extent.y; y++)
162 {
163 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000164 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000165
166 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000167 *restrict chop_indexes,
168 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +0000169
170 register long
171 x;
172
173 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000174 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000175
176 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,exception);
177 q=QueueCacheViewAuthenticPixels(chop_view,0,j++,chop_image->columns,1,
178 exception);
179 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
180 break;
181 indexes=GetCacheViewAuthenticIndexQueue(image_view);
182 chop_indexes=GetCacheViewAuthenticIndexQueue(chop_view);
183 for (x=0; x < (long) image->columns; x++)
184 {
185 if ((x < extent.x) || (x >= (long) (extent.x+extent.width)))
186 {
187 *q=(*p);
188 if (indexes != (IndexPacket *) NULL)
189 {
190 if (chop_indexes != (IndexPacket *) NULL)
191 *chop_indexes++=indexes[x];
192 }
193 q++;
194 }
195 p++;
196 }
197 if (SyncCacheViewAuthenticPixels(chop_view,exception) == MagickFalse)
198 break;
199 proceed=SetImageProgress(image,ChopImageTag,y,chop_image->rows);
200 if (proceed == MagickFalse)
201 break;
202 }
203 /*
204 Extract chop image.
205 */
206 i+=extent.height;
207 for (y=0; y < (long) (image->rows-(extent.y+extent.height)); y++)
208 {
209 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000210 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000211
212 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000213 *restrict chop_indexes,
214 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +0000215
216 register long
217 x;
218
219 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000220 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000221
222 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,exception);
223 q=QueueCacheViewAuthenticPixels(chop_view,0,j++,chop_image->columns,1,
224 exception);
225 if ((p == (PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
226 break;
227 indexes=GetCacheViewAuthenticIndexQueue(image_view);
228 chop_indexes=GetCacheViewAuthenticIndexQueue(chop_view);
229 for (x=0; x < (long) image->columns; x++)
230 {
231 if ((x < extent.x) || (x >= (long) (extent.x+extent.width)))
232 {
233 *q=(*p);
234 if (indexes != (IndexPacket *) NULL)
235 {
236 if (chop_indexes != (IndexPacket *) NULL)
237 *chop_indexes++=indexes[x];
238 }
239 q++;
240 }
241 p++;
242 }
243 if (SyncCacheViewAuthenticPixels(chop_view,exception) == MagickFalse)
244 break;
245 proceed=SetImageProgress(image,ChopImageTag,y,chop_image->rows);
246 if (proceed == MagickFalse)
247 break;
248 }
249 chop_view=DestroyCacheView(chop_view);
250 image_view=DestroyCacheView(image_view);
251 chop_image->type=image->type;
252 return(chop_image);
253}
254
255/*
256%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
257% %
258% %
259% %
260+ C o n s o l i d a t e C M Y K I m a g e %
261% %
262% %
263% %
264%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
265%
266% ConsolidateCMYKImage() consolidates separate C, M, Y, and K planes into a
267% single image.
268%
269% The format of the ConsolidateCMYKImage method is:
270%
271% Image *ConsolidateCMYKImage(const Image *image,ExceptionInfo *exception)
272%
273% A description of each parameter follows:
274%
275% o image: the image sequence.
276%
277% o exception: return any errors or warnings in this structure.
278%
279*/
280MagickExport Image *ConsolidateCMYKImages(const Image *images,
281 ExceptionInfo *exception)
282{
283 Image
284 *cmyk_image,
285 *cmyk_images;
286
287 long
288 y;
289
290 register long
291 i;
292
293 /*
294 Consolidate separate C, M, Y, and K planes into a single image.
295 */
296 assert(images != (Image *) NULL);
297 assert(images->signature == MagickSignature);
298 if (images->debug != MagickFalse)
299 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
300 assert(exception != (ExceptionInfo *) NULL);
301 assert(exception->signature == MagickSignature);
302 cmyk_images=NewImageList();
303 for (i=0; i < (long) GetImageListLength(images); i+=4)
304 {
305 cmyk_image=CloneImage(images,images->columns,images->rows,MagickTrue,
306 exception);
307 if (cmyk_image == (Image *) NULL)
308 break;
309 if (SetImageStorageClass(cmyk_image,DirectClass) == MagickFalse)
310 break;
311 (void) SetImageColorspace(cmyk_image,CMYKColorspace);
312 for (y=0; y < (long) images->rows; y++)
313 {
314 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000315 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000316
317 register long
318 x;
319
320 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000321 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000322
323 p=GetVirtualPixels(images,0,y,images->columns,1,exception);
324 q=QueueAuthenticPixels(cmyk_image,0,y,cmyk_image->columns,1,exception);
325 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
326 break;
327 for (x=0; x < (long) images->columns; x++)
328 {
329 q->red=(Quantum) (QuantumRange-PixelIntensityToQuantum(p));
330 p++;
331 q++;
332 }
333 if (SyncAuthenticPixels(cmyk_image,exception) == MagickFalse)
334 break;
335 }
336 images=GetNextImageInList(images);
337 if (images == (Image *) NULL)
338 break;
339 for (y=0; y < (long) images->rows; y++)
340 {
341 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000342 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000343
344 register long
345 x;
346
347 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000348 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000349
350 p=GetVirtualPixels(images,0,y,images->columns,1,exception);
351 q=GetAuthenticPixels(cmyk_image,0,y,cmyk_image->columns,1,exception);
352 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
353 break;
354 for (x=0; x < (long) images->columns; x++)
355 {
356 q->green=(Quantum) (QuantumRange-PixelIntensityToQuantum(p));
357 p++;
358 q++;
359 }
360 if (SyncAuthenticPixels(cmyk_image,exception) == MagickFalse)
361 break;
362 }
363 images=GetNextImageInList(images);
364 if (images == (Image *) NULL)
365 break;
366 for (y=0; y < (long) images->rows; y++)
367 {
368 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000369 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000370
371 register long
372 x;
373
374 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000375 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000376
377 p=GetVirtualPixels(images,0,y,images->columns,1,exception);
378 q=GetAuthenticPixels(cmyk_image,0,y,cmyk_image->columns,1,exception);
379 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
380 break;
381 for (x=0; x < (long) images->columns; x++)
382 {
383 q->blue=(Quantum) (QuantumRange-PixelIntensityToQuantum(p));
384 p++;
385 q++;
386 }
387 if (SyncAuthenticPixels(cmyk_image,exception) == MagickFalse)
388 break;
389 }
390 images=GetNextImageInList(images);
391 if (images == (Image *) NULL)
392 break;
393 for (y=0; y < (long) images->rows; y++)
394 {
395 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000396 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000397
398 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000399 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +0000400
401 register long
402 x;
403
404 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000405 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000406
407 p=GetVirtualPixels(images,0,y,images->columns,1,exception);
408 q=GetAuthenticPixels(cmyk_image,0,y,cmyk_image->columns,1,exception);
409 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
410 break;
411 indexes=GetAuthenticIndexQueue(cmyk_image);
412 for (x=0; x < (long) images->columns; x++)
413 {
414 indexes[x]=(IndexPacket) (QuantumRange-PixelIntensityToQuantum(p));
415 p++;
416 }
417 if (SyncAuthenticPixels(cmyk_image,exception) == MagickFalse)
418 break;
419 }
420 AppendImageToList(&cmyk_images,cmyk_image);
421 images=GetNextImageInList(images);
422 if (images == (Image *) NULL)
423 break;
424 }
425 return(cmyk_images);
426}
427
428/*
429%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
430% %
431% %
432% %
433% C r o p I m a g e %
434% %
435% %
436% %
437%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
438%
439% CropImage() extracts a region of the image starting at the offset defined
440% by geometry.
441%
442% The format of the CropImage method is:
443%
444% Image *CropImage(const Image *image,const RectangleInfo *geometry,
445% ExceptionInfo *exception)
446%
447% A description of each parameter follows:
448%
449% o image: the image.
450%
451% o geometry: Define the region of the image to crop with members
452% x, y, width, and height.
453%
454% o exception: return any errors or warnings in this structure.
455%
456*/
457MagickExport Image *CropImage(const Image *image,const RectangleInfo *geometry,
458 ExceptionInfo *exception)
459{
460#define CropImageTag "Crop/Image"
461
462 Image
463 *crop_image;
464
465 long
466 progress,
467 y;
468
469 MagickBooleanType
470 status;
471
472 RectangleInfo
473 bounding_box,
474 page;
475
476 CacheView
477 *crop_view,
478 *image_view;
479
480 /*
481 Check crop geometry.
482 */
483 assert(image != (const Image *) NULL);
484 assert(image->signature == MagickSignature);
485 if (image->debug != MagickFalse)
486 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
487 assert(geometry != (const RectangleInfo *) NULL);
488 assert(exception != (ExceptionInfo *) NULL);
489 assert(exception->signature == MagickSignature);
490 bounding_box=image->page;
491 if ((bounding_box.width == 0) || (bounding_box.height == 0))
492 {
493 bounding_box.width=image->columns;
494 bounding_box.height=image->rows;
495 }
496 page=(*geometry);
497 if (page.width == 0)
498 page.width=bounding_box.width;
499 if (page.height == 0)
500 page.height=bounding_box.height;
501 if (((bounding_box.x-page.x) >= (long) page.width) ||
502 ((bounding_box.y-page.y) >= (long) page.height) ||
503 ((page.x-bounding_box.x) > (long) image->columns) ||
504 ((page.y-bounding_box.y) > (long) image->rows))
505 {
506 /*
507 Crop is not within virtual canvas, return 1 pixel transparent image.
508 */
509 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
510 "GeometryDoesNotContainImage","`%s'",image->filename);
511 crop_image=CloneImage(image,1,1,MagickTrue,exception);
512 if (crop_image == (Image *) NULL)
513 return((Image *) NULL);
514 crop_image->background_color.opacity=(Quantum) TransparentOpacity;
515 (void) SetImageBackgroundColor(crop_image);
516 crop_image->page=bounding_box;
517 crop_image->page.x=(-1);
518 crop_image->page.y=(-1);
519 if (crop_image->dispose == BackgroundDispose)
520 crop_image->dispose=NoneDispose;
521 return(crop_image);
522 }
523 if ((page.x < 0) && (bounding_box.x >= 0))
524 {
525 page.width+=page.x-bounding_box.x;
526 page.x=0;
527 }
528 else
529 {
530 page.width-=bounding_box.x-page.x;
531 page.x-=bounding_box.x;
532 if (page.x < 0)
533 page.x=0;
534 }
535 if ((page.y < 0) && (bounding_box.y >= 0))
536 {
537 page.height+=page.y-bounding_box.y;
538 page.y=0;
539 }
540 else
541 {
542 page.height-=bounding_box.y-page.y;
543 page.y-=bounding_box.y;
544 if (page.y < 0)
545 page.y=0;
546 }
547 if ((unsigned long) (page.x+page.width) > image->columns)
548 page.width=image->columns-page.x;
549 if (geometry->width != 0)
550 if (page.width > geometry->width)
551 page.width=geometry->width;
552 if ((unsigned long) (page.y+page.height) > image->rows)
553 page.height=image->rows-page.y;
554 if (geometry->height != 0)
555 if (page.height > geometry->height)
556 page.height=geometry->height;
557 bounding_box.x+=page.x;
558 bounding_box.y+=page.y;
559 if ((page.width == 0) || (page.height == 0))
560 {
561 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
562 "GeometryDoesNotContainImage","`%s'",image->filename);
563 return((Image *) NULL);
564 }
565 /*
566 Initialize crop image attributes.
567 */
568 crop_image=CloneImage(image,page.width,page.height,MagickTrue,exception);
569 if (crop_image == (Image *) NULL)
570 return((Image *) NULL);
571 crop_image->page.width=image->page.width;
572 crop_image->page.height=image->page.height;
573 if (((long) (bounding_box.x+bounding_box.width) > (long) image->page.width) ||
574 ((long) (bounding_box.y+bounding_box.height) > (long) image->page.height))
575 {
576 crop_image->page.width=bounding_box.width;
577 crop_image->page.height=bounding_box.height;
578 }
579 crop_image->page.x=bounding_box.x;
580 crop_image->page.y=bounding_box.y;
581 /*
582 Crop image.
583 */
584 status=MagickTrue;
585 progress=0;
586 image_view=AcquireCacheView(image);
587 crop_view=AcquireCacheView(crop_image);
cristyb5d5f722009-11-04 03:03:49 +0000588#if defined(MAGICKCORE_OPENMP_SUPPORT)
589 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000590#endif
591 for (y=0; y < (long) crop_image->rows; y++)
592 {
593 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000594 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +0000595
596 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000597 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000598
599 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000600 *restrict crop_indexes;
cristy3ed852e2009-09-05 21:47:34 +0000601
602 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000603 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000604
605 if (status == MagickFalse)
606 continue;
607 p=GetCacheViewVirtualPixels(image_view,page.x,page.y+y,crop_image->columns,
608 1,exception);
609 q=QueueCacheViewAuthenticPixels(crop_view,0,y,crop_image->columns,1,
610 exception);
611 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
612 {
613 status=MagickFalse;
614 continue;
615 }
616 (void) CopyMagickMemory(q,p,(size_t) crop_image->columns*sizeof(*q));
617 indexes=GetCacheViewVirtualIndexQueue(image_view);
618 if (indexes != (IndexPacket *) NULL)
619 {
620 crop_indexes=GetCacheViewAuthenticIndexQueue(crop_view);
621 if (crop_indexes != (IndexPacket *) NULL)
622 (void) CopyMagickMemory(crop_indexes,indexes,(size_t)
623 crop_image->columns*sizeof(*crop_indexes));
624 }
625 if (SyncCacheViewAuthenticPixels(crop_view,exception) == MagickFalse)
626 status=MagickFalse;
627 if (image->progress_monitor != (MagickProgressMonitor) NULL)
628 {
629 MagickBooleanType
630 proceed;
631
cristyb5d5f722009-11-04 03:03:49 +0000632#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +0000633 #pragma omp critical (MagickCore_CropImage)
634#endif
635 proceed=SetImageProgress(image,CropImageTag,progress++,image->rows);
636 if (proceed == MagickFalse)
637 status=MagickFalse;
638 }
639 }
640 crop_view=DestroyCacheView(crop_view);
641 image_view=DestroyCacheView(image_view);
642 crop_image->type=image->type;
643 if (status == MagickFalse)
644 crop_image=DestroyImage(crop_image);
645 return(crop_image);
646}
647
648/*
649%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
650% %
651% %
652% %
653% E x c e r p t I m a g e %
654% %
655% %
656% %
657%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
658%
659% ExcerptImage() returns a excerpt of the image as defined by the geometry.
660%
661% The format of the ExcerptImage method is:
662%
663% Image *ExcerptImage(const Image *image,const RectangleInfo *geometry,
664% ExceptionInfo *exception)
665%
666% A description of each parameter follows:
667%
668% o image: the image.
669%
670% o geometry: Define the region of the image to extend with members
671% x, y, width, and height.
672%
673% o exception: return any errors or warnings in this structure.
674%
675*/
676MagickExport Image *ExcerptImage(const Image *image,
677 const RectangleInfo *geometry,ExceptionInfo *exception)
678{
679#define ExcerptImageTag "Excerpt/Image"
680
681 Image
682 *excerpt_image;
683
684 long
685 progress,
686 y;
687
688 MagickBooleanType
689 status;
690
691 CacheView
692 *excerpt_view,
693 *image_view;
694
695 /*
696 Allocate excerpt image.
697 */
698 assert(image != (const Image *) NULL);
699 assert(image->signature == MagickSignature);
700 if (image->debug != MagickFalse)
701 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
702 assert(geometry != (const RectangleInfo *) NULL);
703 assert(exception != (ExceptionInfo *) NULL);
704 assert(exception->signature == MagickSignature);
705 excerpt_image=CloneImage(image,geometry->width,geometry->height,MagickTrue,
706 exception);
707 if (excerpt_image == (Image *) NULL)
708 return((Image *) NULL);
709 /*
710 Excerpt each row.
711 */
712 status=MagickTrue;
713 progress=0;
714 image_view=AcquireCacheView(image);
715 excerpt_view=AcquireCacheView(excerpt_image);
cristyb5d5f722009-11-04 03:03:49 +0000716#if defined(MAGICKCORE_OPENMP_SUPPORT)
717 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000718#endif
719 for (y=0; y < (long) excerpt_image->rows; y++)
720 {
721 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000722 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000723
724 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000725 *restrict excerpt_indexes,
726 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +0000727
728 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000729 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000730
731 if (status == MagickFalse)
732 continue;
733 p=GetCacheViewVirtualPixels(image_view,geometry->x,geometry->y+y,
734 geometry->width,1,exception);
735 q=GetCacheViewAuthenticPixels(excerpt_view,0,y,excerpt_image->columns,1,
736 exception);
737 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
738 {
739 status=MagickFalse;
740 continue;
741 }
742 (void) CopyMagickMemory(q,p,(size_t) excerpt_image->columns*sizeof(*q));
743 indexes=GetCacheViewAuthenticIndexQueue(image_view);
744 if (indexes != (IndexPacket *) NULL)
745 {
746 excerpt_indexes=GetCacheViewAuthenticIndexQueue(excerpt_view);
747 if (excerpt_indexes != (IndexPacket *) NULL)
748 (void) CopyMagickMemory(excerpt_indexes,indexes,(size_t)
749 excerpt_image->columns*sizeof(*excerpt_indexes));
750 }
751 if (SyncCacheViewAuthenticPixels(excerpt_view,exception) == MagickFalse)
752 status=MagickFalse;
753 if (image->progress_monitor != (MagickProgressMonitor) NULL)
754 {
755 MagickBooleanType
756 proceed;
757
cristyb5d5f722009-11-04 03:03:49 +0000758#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +0000759 #pragma omp critical (MagickCore_ExcerptImage)
760#endif
761 proceed=SetImageProgress(image,ExcerptImageTag,progress++,image->rows);
762 if (proceed == MagickFalse)
763 status=MagickFalse;
764 }
765 }
766 excerpt_view=DestroyCacheView(excerpt_view);
767 image_view=DestroyCacheView(image_view);
768 excerpt_image->type=image->type;
769 if (status == MagickFalse)
770 excerpt_image=DestroyImage(excerpt_image);
771 return(excerpt_image);
772}
773
774/*
775%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
776% %
777% %
778% %
779% E x t e n t I m a g e %
780% %
781% %
782% %
783%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
784%
785% ExtentImage() extends the image as defined by the geometry, gravity, and
786% image background color. Set the (x,y) offset of the geometry to move the
787% original image relative to the extended image.
788%
789% The format of the ExtentImage method is:
790%
791% Image *ExtentImage(const Image *image,const RectangleInfo *geometry,
792% ExceptionInfo *exception)
793%
794% A description of each parameter follows:
795%
796% o image: the image.
797%
798% o geometry: Define the region of the image to extend with members
799% x, y, width, and height.
800%
801% o exception: return any errors or warnings in this structure.
802%
803*/
804MagickExport Image *ExtentImage(const Image *image,
805 const RectangleInfo *geometry,ExceptionInfo *exception)
806{
807 Image
808 *extent_image;
809
810 /*
811 Allocate extent image.
812 */
813 assert(image != (const Image *) NULL);
814 assert(image->signature == MagickSignature);
815 if (image->debug != MagickFalse)
816 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
817 assert(geometry != (const RectangleInfo *) NULL);
818 assert(exception != (ExceptionInfo *) NULL);
819 assert(exception->signature == MagickSignature);
820 extent_image=CloneImage(image,geometry->width,geometry->height,MagickTrue,
821 exception);
822 if (extent_image == (Image *) NULL)
823 return((Image *) NULL);
824 if (SetImageStorageClass(extent_image,DirectClass) == MagickFalse)
825 {
826 InheritException(exception,&extent_image->exception);
827 extent_image=DestroyImage(extent_image);
828 return((Image *) NULL);
829 }
830 if (extent_image->background_color.opacity != OpaqueOpacity)
831 extent_image->matte=MagickTrue;
832 (void) SetImageBackgroundColor(extent_image);
833 (void) CompositeImage(extent_image,image->compose,image,geometry->x,
834 geometry->y);
835 return(extent_image);
836}
837
838/*
839%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
840% %
841% %
842% %
843% F l i p I m a g e %
844% %
845% %
846% %
847%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
848%
849% FlipImage() creates a vertical mirror image by reflecting the pixels
850% around the central x-axis.
851%
852% The format of the FlipImage method is:
853%
854% Image *FlipImage(const Image *image,ExceptionInfo *exception)
855%
856% A description of each parameter follows:
857%
858% o image: the image.
859%
860% o exception: return any errors or warnings in this structure.
861%
862*/
863MagickExport Image *FlipImage(const Image *image,ExceptionInfo *exception)
864{
865#define FlipImageTag "Flip/Image"
866
867 Image
868 *flip_image;
869
870 long
871 progress,
872 y;
873
874 MagickBooleanType
875 status;
876
877 CacheView
878 *flip_view,
879 *image_view;
880
881 assert(image != (const Image *) NULL);
882 assert(image->signature == MagickSignature);
883 if (image->debug != MagickFalse)
884 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
885 assert(exception != (ExceptionInfo *) NULL);
886 assert(exception->signature == MagickSignature);
887 flip_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
888 if (flip_image == (Image *) NULL)
889 return((Image *) NULL);
890 /*
891 Flip image.
892 */
893 status=MagickTrue;
894 progress=0;
895 image_view=AcquireCacheView(image);
896 flip_view=AcquireCacheView(flip_image);
cristyb5d5f722009-11-04 03:03:49 +0000897#if defined(MAGICKCORE_OPENMP_SUPPORT)
898 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000899#endif
900 for (y=0; y < (long) flip_image->rows; y++)
901 {
902 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000903 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +0000904
905 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000906 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000907
908 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000909 *restrict flip_indexes;
cristy3ed852e2009-09-05 21:47:34 +0000910
911 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000912 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000913
914 if (status == MagickFalse)
915 continue;
916 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
917 q=QueueCacheViewAuthenticPixels(flip_view,0,(long) (flip_image->rows-y-1),
918 flip_image->columns,1,exception);
919 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
920 {
921 status=MagickFalse;
922 continue;
923 }
924 (void) CopyMagickMemory(q,p,(size_t) image->columns*sizeof(*q));
925 indexes=GetCacheViewVirtualIndexQueue(image_view);
926 if (indexes != (const IndexPacket *) NULL)
927 {
928 flip_indexes=GetCacheViewAuthenticIndexQueue(flip_view);
929 if (flip_indexes != (IndexPacket *) NULL)
930 (void) CopyMagickMemory(flip_indexes,indexes,(size_t) image->columns*
931 sizeof(*flip_indexes));
932 }
933 if (SyncCacheViewAuthenticPixels(flip_view,exception) == MagickFalse)
934 status=MagickFalse;
935 if (image->progress_monitor != (MagickProgressMonitor) NULL)
936 {
937 MagickBooleanType
938 proceed;
939
cristyb5d5f722009-11-04 03:03:49 +0000940#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +0000941 #pragma omp critical (MagickCore_FlipImage)
942#endif
943 proceed=SetImageProgress(image,FlipImageTag,progress++,image->rows);
944 if (proceed == MagickFalse)
945 status=MagickFalse;
946 }
947 }
948 flip_view=DestroyCacheView(flip_view);
949 image_view=DestroyCacheView(image_view);
950 flip_image->type=image->type;
951 if (status == MagickFalse)
952 flip_image=DestroyImage(flip_image);
953 return(flip_image);
954}
955
956/*
957%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
958% %
959% %
960% %
961% F l o p I m a g e %
962% %
963% %
964% %
965%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
966%
967% FlopImage() creates a horizontal mirror image by reflecting the pixels
968% around the central y-axis.
969%
970% The format of the FlopImage method is:
971%
972% Image *FlopImage(const Image *image,ExceptionInfo *exception)
973%
974% A description of each parameter follows:
975%
976% o image: the image.
977%
978% o exception: return any errors or warnings in this structure.
979%
980*/
981MagickExport Image *FlopImage(const Image *image,ExceptionInfo *exception)
982{
983#define FlopImageTag "Flop/Image"
984
985 Image
986 *flop_image;
987
988 long
989 progress,
990 y;
991
992 MagickBooleanType
993 status;
994
995 CacheView
996 *flop_view,
997 *image_view;
998
999 assert(image != (const Image *) NULL);
1000 assert(image->signature == MagickSignature);
1001 if (image->debug != MagickFalse)
1002 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1003 assert(exception != (ExceptionInfo *) NULL);
1004 assert(exception->signature == MagickSignature);
1005 flop_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
1006 if (flop_image == (Image *) NULL)
1007 return((Image *) NULL);
1008 /*
1009 Flop each row.
1010 */
1011 status=MagickTrue;
1012 progress=0;
1013 image_view=AcquireCacheView(image);
1014 flop_view=AcquireCacheView(flop_image);
cristyb5d5f722009-11-04 03:03:49 +00001015#if defined(MAGICKCORE_OPENMP_SUPPORT)
1016 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00001017#endif
1018 for (y=0; y < (long) flop_image->rows; y++)
1019 {
1020 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001021 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00001022
1023 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001024 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001025
1026 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001027 *restrict flop_indexes;
cristy3ed852e2009-09-05 21:47:34 +00001028
1029 register long
1030 x;
1031
1032 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001033 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001034
1035 if (status == MagickFalse)
1036 continue;
1037 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1038 q=QueueCacheViewAuthenticPixels(flop_view,0,y,flop_image->columns,1,
1039 exception);
1040 if ((p == (PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
1041 {
1042 status=MagickFalse;
1043 continue;
1044 }
1045 q+=flop_image->columns;
1046 indexes=GetCacheViewVirtualIndexQueue(image_view);
1047 flop_indexes=GetCacheViewAuthenticIndexQueue(flop_view);
1048 for (x=0; x < (long) flop_image->columns; x++)
1049 {
1050 (*--q)=(*p++);
1051 if ((indexes != (const IndexPacket *) NULL) &&
1052 (flop_indexes != (IndexPacket *) NULL))
1053 flop_indexes[flop_image->columns-x-1]=indexes[x];
1054 }
1055 if (SyncCacheViewAuthenticPixels(flop_view,exception) == MagickFalse)
1056 status=MagickFalse;
1057 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1058 {
1059 MagickBooleanType
1060 proceed;
1061
cristyb5d5f722009-11-04 03:03:49 +00001062#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00001063 #pragma omp critical (MagickCore_FlopImage)
1064#endif
1065 proceed=SetImageProgress(image,FlopImageTag,progress++,image->rows);
1066 if (proceed == MagickFalse)
1067 status=MagickFalse;
1068 }
1069 }
1070 flop_view=DestroyCacheView(flop_view);
1071 image_view=DestroyCacheView(image_view);
1072 flop_image->type=image->type;
1073 if (status == MagickFalse)
1074 flop_image=DestroyImage(flop_image);
1075 return(flop_image);
1076}
1077
1078/*
1079%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1080% %
1081% %
1082% %
1083% R o l l I m a g e %
1084% %
1085% %
1086% %
1087%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1088%
1089% RollImage() offsets an image as defined by x_offset and y_offset.
1090%
1091% The format of the RollImage method is:
1092%
1093% Image *RollImage(const Image *image,const long x_offset,
1094% const long y_offset,ExceptionInfo *exception)
1095%
1096% A description of each parameter follows:
1097%
1098% o image: the image.
1099%
1100% o x_offset: the number of columns to roll in the horizontal direction.
1101%
1102% o y_offset: the number of rows to roll in the vertical direction.
1103%
1104% o exception: return any errors or warnings in this structure.
1105%
1106*/
1107
1108static inline MagickBooleanType CopyImageRegion(Image *destination,
1109 const Image *source,const unsigned long columns,const unsigned long rows,
1110 const long sx,const long sy,const long dx,const long dy,
1111 ExceptionInfo *exception)
1112{
1113 long
1114 y;
1115
1116 MagickBooleanType
1117 status;
1118
1119 CacheView
1120 *source_view,
1121 *destination_view;
1122
1123 status=MagickTrue;
1124 source_view=AcquireCacheView(source);
1125 destination_view=AcquireCacheView(destination);
cristyb5d5f722009-11-04 03:03:49 +00001126#if defined(MAGICKCORE_OPENMP_SUPPORT)
1127 #pragma omp parallel for schedule(dynamic,4) shared(status)
cristy3ed852e2009-09-05 21:47:34 +00001128#endif
1129 for (y=0; y < (long) rows; y++)
1130 {
1131 MagickBooleanType
1132 sync;
1133
1134 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001135 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001136
1137 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001138 *restrict indexes,
1139 *restrict destination_indexes;
cristy3ed852e2009-09-05 21:47:34 +00001140
1141 register long
1142 x;
1143
1144 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001145 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001146
1147 /*
1148 Transfer scanline.
1149 */
1150 if (status == MagickFalse)
1151 continue;
1152 p=GetCacheViewVirtualPixels(source_view,sx,sy+y,columns,1,exception);
1153 q=GetCacheViewAuthenticPixels(destination_view,dx,dy+y,columns,1,exception);
1154 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
1155 {
1156 status=MagickFalse;
1157 continue;
1158 }
1159 indexes=GetCacheViewAuthenticIndexQueue(source_view);
1160 for (x=0; x < (long) columns; x++)
1161 *q++=(*p++);
1162 if (indexes != (IndexPacket *) NULL)
1163 {
1164 destination_indexes=GetCacheViewAuthenticIndexQueue(destination_view);
1165 for (x=0; x < (long) columns; x++)
1166 destination_indexes[x]=indexes[x];
1167 }
1168 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
1169 if (sync == MagickFalse)
1170 status=MagickFalse;
1171 }
1172 destination_view=DestroyCacheView(destination_view);
1173 source_view=DestroyCacheView(source_view);
1174 return(status);
1175}
1176
1177MagickExport Image *RollImage(const Image *image,const long x_offset,
1178 const long y_offset,ExceptionInfo *exception)
1179{
1180#define RollImageTag "Roll/Image"
1181
1182 Image
1183 *roll_image;
1184
1185 MagickStatusType
1186 status;
1187
1188 RectangleInfo
1189 offset;
1190
1191 /*
1192 Initialize roll image attributes.
1193 */
1194 assert(image != (const Image *) NULL);
1195 assert(image->signature == MagickSignature);
1196 if (image->debug != MagickFalse)
1197 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1198 assert(exception != (ExceptionInfo *) NULL);
1199 assert(exception->signature == MagickSignature);
1200 roll_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
1201 if (roll_image == (Image *) NULL)
1202 return((Image *) NULL);
1203 offset.x=x_offset;
1204 offset.y=y_offset;
1205 while (offset.x < 0)
1206 offset.x+=image->columns;
1207 while (offset.x >= (long) image->columns)
1208 offset.x-=image->columns;
1209 while (offset.y < 0)
1210 offset.y+=image->rows;
1211 while (offset.y >= (long) image->rows)
1212 offset.y-=image->rows;
1213 /*
1214 Roll image.
1215 */
1216 status=CopyImageRegion(roll_image,image,(unsigned long) offset.x,
1217 (unsigned long) offset.y,(long) image->columns-offset.x,(long) image->rows-
1218 offset.y,0,0,exception);
1219 (void) SetImageProgress(image,RollImageTag,0,3);
1220 status|=CopyImageRegion(roll_image,image,image->columns-offset.x,
1221 (unsigned long) offset.y,0,(long) image->rows-offset.y,offset.x,0,
1222 exception);
1223 (void) SetImageProgress(image,RollImageTag,1,3);
1224 status|=CopyImageRegion(roll_image,image,(unsigned long) offset.x,image->rows-
1225 offset.y,(long) image->columns-offset.x,0,0,offset.y,exception);
1226 (void) SetImageProgress(image,RollImageTag,2,3);
1227 status|=CopyImageRegion(roll_image,image,image->columns-offset.x,image->rows-
1228 offset.y,0,0,offset.x,offset.y,exception);
1229 (void) SetImageProgress(image,RollImageTag,3,3);
1230 roll_image->type=image->type;
1231 if (status == MagickFalse)
1232 roll_image=DestroyImage(roll_image);
1233 return(roll_image);
1234}
1235
1236/*
1237%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1238% %
1239% %
1240% %
1241% S h a v e I m a g e %
1242% %
1243% %
1244% %
1245%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1246%
1247% ShaveImage() shaves pixels from the image edges. It allocates the memory
1248% necessary for the new Image structure and returns a pointer to the new
1249% image.
1250%
1251% The format of the ShaveImage method is:
1252%
1253% Image *ShaveImage(const Image *image,const RectangleInfo *shave_info,
1254% ExceptionInfo *exception)
1255%
1256% A description of each parameter follows:
1257%
1258% o shave_image: Method ShaveImage returns a pointer to the shaved
1259% image. A null image is returned if there is a memory shortage or
1260% if the image width or height is zero.
1261%
1262% o image: the image.
1263%
1264% o shave_info: Specifies a pointer to a RectangleInfo which defines the
1265% region of the image to crop.
1266%
1267% o exception: return any errors or warnings in this structure.
1268%
1269*/
1270MagickExport Image *ShaveImage(const Image *image,
1271 const RectangleInfo *shave_info,ExceptionInfo *exception)
1272{
1273 Image
1274 *shave_image;
1275
1276 RectangleInfo
1277 geometry;
1278
1279 assert(image != (const Image *) NULL);
1280 assert(image->signature == MagickSignature);
1281 if (image->debug != MagickFalse)
1282 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1283 if (((2*shave_info->width) >= image->columns) ||
1284 ((2*shave_info->height) >= image->rows))
1285 ThrowImageException(OptionWarning,"GeometryDoesNotContainImage");
1286 SetGeometry(image,&geometry);
1287 geometry.width-=2*shave_info->width;
1288 geometry.height-=2*shave_info->height;
1289 geometry.x=(long) shave_info->width+image->page.x;
1290 geometry.y=(long) shave_info->height+image->page.y;
1291 shave_image=CropImage(image,&geometry,exception);
1292 if (shave_image == (Image *) NULL)
1293 return((Image *) NULL);
1294 shave_image->page.width-=2*shave_info->width;
1295 shave_image->page.height-=2*shave_info->height;
1296 shave_image->page.x-=shave_info->width;
1297 shave_image->page.y-=shave_info->height;
1298 return(shave_image);
1299}
1300
1301/*
1302%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1303% %
1304% %
1305% %
1306% S p l i c e I m a g e %
1307% %
1308% %
1309% %
1310%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1311%
1312% SpliceImage() splices a solid color into the image as defined by the
1313% geometry.
1314%
1315% The format of the SpliceImage method is:
1316%
1317% Image *SpliceImage(const Image *image,const RectangleInfo *geometry,
1318% ExceptionInfo *exception)
1319%
1320% A description of each parameter follows:
1321%
1322% o image: the image.
1323%
1324% o geometry: Define the region of the image to splice with members
1325% x, y, width, and height.
1326%
1327% o exception: return any errors or warnings in this structure.
1328%
1329*/
1330MagickExport Image *SpliceImage(const Image *image,
1331 const RectangleInfo *geometry,ExceptionInfo *exception)
1332{
1333#define SpliceImageTag "Splice/Image"
1334
1335 Image
1336 *splice_image;
1337
1338 long
1339 progress,
1340 y;
1341
1342 MagickBooleanType
1343 proceed,
1344 status;
1345
1346 RectangleInfo
1347 splice_geometry;
1348
1349 register long
1350 i;
1351
1352 CacheView
1353 *image_view,
1354 *splice_view;
1355
1356 /*
1357 Allocate splice image.
1358 */
1359 assert(image != (const Image *) NULL);
1360 assert(image->signature == MagickSignature);
1361 if (image->debug != MagickFalse)
1362 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1363 assert(geometry != (const RectangleInfo *) NULL);
1364 assert(exception != (ExceptionInfo *) NULL);
1365 assert(exception->signature == MagickSignature);
1366 splice_geometry=(*geometry);
1367 splice_image=CloneImage(image,image->columns+splice_geometry.width,
1368 image->rows+splice_geometry.height,MagickTrue,exception);
1369 if (splice_image == (Image *) NULL)
1370 return((Image *) NULL);
1371 if (SetImageStorageClass(splice_image,DirectClass) == MagickFalse)
1372 {
1373 InheritException(exception,&splice_image->exception);
1374 splice_image=DestroyImage(splice_image);
1375 return((Image *) NULL);
1376 }
1377 (void) SetImageBackgroundColor(splice_image);
1378 /*
1379 Respect image geometry.
1380 */
1381 switch (image->gravity)
1382 {
1383 default:
1384 case UndefinedGravity:
1385 case NorthWestGravity:
1386 break;
1387 case NorthGravity:
1388 {
1389 splice_geometry.x+=splice_geometry.width/2;
1390 break;
1391 }
1392 case NorthEastGravity:
1393 {
1394 splice_geometry.x+=splice_geometry.width;
1395 break;
1396 }
1397 case WestGravity:
1398 {
1399 splice_geometry.y+=splice_geometry.width/2;
1400 break;
1401 }
1402 case StaticGravity:
1403 case CenterGravity:
1404 {
1405 splice_geometry.x+=splice_geometry.width/2;
1406 splice_geometry.y+=splice_geometry.height/2;
1407 break;
1408 }
1409 case EastGravity:
1410 {
1411 splice_geometry.x+=splice_geometry.width;
1412 splice_geometry.y+=splice_geometry.height/2;
1413 break;
1414 }
1415 case SouthWestGravity:
1416 {
1417 splice_geometry.y+=splice_geometry.height;
1418 break;
1419 }
1420 case SouthGravity:
1421 {
1422 splice_geometry.x+=splice_geometry.width/2;
1423 splice_geometry.y+=splice_geometry.height;
1424 break;
1425 }
1426 case SouthEastGravity:
1427 {
1428 splice_geometry.x+=splice_geometry.width;
1429 splice_geometry.y+=splice_geometry.height;
1430 break;
1431 }
1432 }
1433 /*
1434 Splice image.
1435 */
1436 status=MagickTrue;
1437 i=0;
1438 progress=0;
1439 image_view=AcquireCacheView(image);
1440 splice_view=AcquireCacheView(splice_image);
cristyb5d5f722009-11-04 03:03:49 +00001441#if defined(MAGICKCORE_OPENMP_SUPPORT)
1442 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00001443#endif
1444 for (y=0; y < (long) splice_geometry.y; y++)
1445 {
1446 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001447 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001448
1449 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001450 *restrict indexes,
1451 *restrict splice_indexes;
cristy3ed852e2009-09-05 21:47:34 +00001452
1453 register long
1454 x;
1455
1456 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001457 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001458
1459 if (status == MagickFalse)
1460 continue;
1461 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1462 q=QueueCacheViewAuthenticPixels(splice_view,0,y,splice_image->columns,1,
1463 exception);
1464 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
1465 {
1466 status=MagickFalse;
1467 continue;
1468 }
1469 indexes=GetCacheViewAuthenticIndexQueue(image_view);
1470 splice_indexes=GetCacheViewAuthenticIndexQueue(splice_view);
1471 for (x=0; x < splice_geometry.x; x++)
1472 {
1473 q->red=p->red;
1474 q->green=p->green;
1475 q->blue=p->blue;
1476 q->opacity=OpaqueOpacity;
1477 if (image->matte != MagickFalse)
1478 q->opacity=p->opacity;
1479 if (image->colorspace == CMYKColorspace)
1480 splice_indexes[x]=(*indexes++);
1481 p++;
1482 q++;
1483 }
1484 for ( ; x < (long) (splice_geometry.x+splice_geometry.width); x++)
1485 q++;
1486 for ( ; x < (long) splice_image->columns; x++)
1487 {
1488 q->red=p->red;
1489 q->green=p->green;
1490 q->blue=p->blue;
1491 q->opacity=OpaqueOpacity;
1492 if (image->matte != MagickFalse)
1493 q->opacity=p->opacity;
1494 if (image->colorspace == CMYKColorspace)
1495 splice_indexes[x]=(*indexes++);
1496 p++;
1497 q++;
1498 }
1499 if (SyncCacheViewAuthenticPixels(splice_view,exception) == MagickFalse)
1500 status=MagickFalse;
1501 proceed=SetImageProgress(image,SpliceImageTag,y,splice_image->rows);
1502 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1503 {
1504 MagickBooleanType
1505 proceed;
1506
cristyb5d5f722009-11-04 03:03:49 +00001507#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00001508 #pragma omp critical (MagickCore_TransposeImage)
1509#endif
1510 proceed=SetImageProgress(image,SpliceImageTag,progress++,
1511 splice_image->rows);
1512 if (proceed == MagickFalse)
1513 status=MagickFalse;
1514 }
1515 }
cristyb5d5f722009-11-04 03:03:49 +00001516#if defined(MAGICKCORE_OPENMP_SUPPORT)
1517 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00001518#endif
1519 for (y=(long) (splice_geometry.y+splice_geometry.height);
1520 y < (long) splice_image->rows; y++)
1521 {
1522 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001523 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001524
1525 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001526 *restrict indexes,
1527 *restrict splice_indexes;
cristy3ed852e2009-09-05 21:47:34 +00001528
1529 register long
1530 x;
1531
1532 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001533 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001534
1535 if (status == MagickFalse)
1536 continue;
1537 p=GetCacheViewVirtualPixels(image_view,0,y-splice_geometry.height,
1538 image->columns,1,exception);
1539 q=QueueCacheViewAuthenticPixels(splice_view,0,y,splice_image->columns,1,
1540 exception);
1541 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
1542 {
1543 status=MagickFalse;
1544 continue;
1545 }
1546 indexes=GetCacheViewAuthenticIndexQueue(image_view);
1547 splice_indexes=GetCacheViewAuthenticIndexQueue(splice_view);
1548 for (x=0; x < splice_geometry.x; x++)
1549 {
1550 q->red=p->red;
1551 q->green=p->green;
1552 q->blue=p->blue;
1553 q->opacity=OpaqueOpacity;
1554 if (image->matte != MagickFalse)
1555 q->opacity=p->opacity;
1556 if (image->colorspace == CMYKColorspace)
1557 splice_indexes[x]=(*indexes++);
1558 p++;
1559 q++;
1560 }
1561 for ( ; x < (long) (splice_geometry.x+splice_geometry.width); x++)
1562 q++;
1563 for ( ; x < (long) splice_image->columns; x++)
1564 {
1565 q->red=p->red;
1566 q->green=p->green;
1567 q->blue=p->blue;
1568 q->opacity=OpaqueOpacity;
1569 if (image->matte != MagickFalse)
1570 q->opacity=p->opacity;
1571 if (image->colorspace == CMYKColorspace)
1572 splice_indexes[x]=(*indexes++);
1573 p++;
1574 q++;
1575 }
1576 if (SyncCacheViewAuthenticPixels(splice_view,exception) == MagickFalse)
1577 status=MagickFalse;
1578 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1579 {
1580 MagickBooleanType
1581 proceed;
1582
cristyb5d5f722009-11-04 03:03:49 +00001583#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00001584 #pragma omp critical (MagickCore_TransposeImage)
1585#endif
1586 proceed=SetImageProgress(image,SpliceImageTag,progress++,
1587 splice_image->rows);
1588 if (proceed == MagickFalse)
1589 status=MagickFalse;
1590 }
1591 }
1592 splice_view=DestroyCacheView(splice_view);
1593 image_view=DestroyCacheView(image_view);
1594 if (status == MagickFalse)
1595 splice_image=DestroyImage(splice_image);
1596 return(splice_image);
1597}
1598
1599/*
1600%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1601% %
1602% %
1603% %
1604% T r a n s f o r m I m a g e %
1605% %
1606% %
1607% %
1608%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1609%
1610% TransformImage() is a convenience method that behaves like ResizeImage() or
1611% CropImage() but accepts scaling and/or cropping information as a region
1612% geometry specification. If the operation fails, the original image handle
1613% is returned.
1614%
1615% The format of the TransformImage method is:
1616%
1617% MagickBooleanType TransformImage(Image **image,const char *crop_geometry,
1618% const char *image_geometry)
1619%
1620% A description of each parameter follows:
1621%
1622% o image: the image The transformed image is returned as this parameter.
1623%
1624% o crop_geometry: A crop geometry string. This geometry defines a
1625% subregion of the image to crop.
1626%
1627% o image_geometry: An image geometry string. This geometry defines the
1628% final size of the image.
1629%
1630*/
anthonyc0429ed2009-12-27 08:22:42 +00001631static inline double MagickRound(double x)
1632{
1633 /* round the fraction to nearest integer */
1634 if (x >= 0.0)
1635 return((double) ((long) (x+0.5)));
1636 return((double) ((long) (x-0.5)));
1637}
1638
cristy3ed852e2009-09-05 21:47:34 +00001639MagickExport MagickBooleanType TransformImage(Image **image,
1640 const char *crop_geometry,const char *image_geometry)
1641{
1642 Image
1643 *resize_image,
1644 *transform_image;
1645
1646 MagickStatusType
1647 flags;
1648
1649 RectangleInfo
1650 geometry;
1651
1652 assert(image != (Image **) NULL);
1653 assert((*image)->signature == MagickSignature);
1654 if ((*image)->debug != MagickFalse)
1655 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
1656 transform_image=(*image);
1657 if (crop_geometry != (const char *) NULL)
1658 {
1659 Image
1660 *crop_image;
1661
1662 RectangleInfo
1663 geometry;
1664
1665 /*
1666 Crop image to a user specified size.
1667 */
1668 crop_image=NewImageList();
1669 flags=ParseGravityGeometry(transform_image,crop_geometry,&geometry,
1670 &(*image)->exception);
anthonyc0429ed2009-12-27 08:22:42 +00001671 /* crop into NxM tiles (@ flag) - AT */
1672 if ((flags & AreaValue) != 0 )
1673 {
1674 Image
1675 *next;
1676
1677 MagickRealType
1678 width, height, w_step, h_step, x, y;
1679
1680 RectangleInfo
1681 crop;
1682
1683 if ( geometry.width == 0 ) geometry.width = 1;
1684 if ( geometry.height == 0 ) geometry.height = 1;
1685
1686 width=(MagickRealType)transform_image->columns;
1687 height=(MagickRealType)transform_image->rows;
1688 if ((flags & AspectValue) == 0)
1689 {
1690 width -= (MagickRealType)labs(geometry.x);
1691 height -= (MagickRealType)labs(geometry.y);
1692 }
1693 else
1694 {
1695 width += (MagickRealType)labs(geometry.x);
1696 height += (MagickRealType)labs(geometry.y);
1697 }
1698 w_step=width/geometry.width;
1699 h_step=height/geometry.height;
1700
1701 next=NewImageList();
1702 for (y=0.0; y < height; )
1703 {
1704 if ((flags & AspectValue) == 0)
1705 {
1706 crop.y=MagickRound(y-(geometry.y>0?0:geometry.y));
1707 y+=h_step;
1708 crop.height=MagickRound(y+(geometry.y<0?0:geometry.y));
1709 }
1710 else
1711 {
1712 crop.y=MagickRound(y-(geometry.y>0?geometry.y:0) );
1713 y+=h_step;
1714 crop.height=MagickRound(y+(geometry.y<0?geometry.y:0) );
1715 }
1716 crop.height -= crop.y;
1717 for (x=0.0; x < width; )
1718 {
1719 if ((flags & AspectValue) == 0)
1720 {
1721 crop.x=MagickRound(x-(geometry.x>0?0:geometry.x));
1722 x+=w_step;
1723 crop.width=MagickRound(x+(geometry.x<0?0:geometry.x));
1724 }
1725 else
1726 {
1727 crop.x=MagickRound(x-(geometry.x>0?geometry.x:0) );
1728 x+=w_step;
1729 crop.width=MagickRound(x+(geometry.x<0?geometry.x:0) );
1730 }
1731 crop.width -= crop.x;
1732printf("crop %ldx%ld+%ld+%ld\n", crop.width, crop.height, crop.x, crop.y);
1733 next=CropImage(transform_image,&crop,&(*image)->exception);
1734 if (next == (Image *) NULL)
1735 break;
1736 AppendImageToList(&crop_image,next);
1737 }
1738 if (next == (Image *) NULL)
1739 break;
1740 }
1741 }
1742 /* crop a single region at +X+Y */
1743 else if (((geometry.width == 0) && (geometry.height == 0)) ||
1744 ((flags & XValue) != 0) || ((flags & YValue) != 0))
cristy3ed852e2009-09-05 21:47:34 +00001745 {
1746 crop_image=CropImage(transform_image,&geometry,&(*image)->exception);
1747 if ((crop_image != (Image *) NULL) && ((flags & AspectValue) != 0))
1748 {
1749 crop_image->page.width=geometry.width;
1750 crop_image->page.height=geometry.height;
1751 crop_image->page.x-=geometry.x;
1752 crop_image->page.y-=geometry.y;
1753 }
1754 }
anthonyc0429ed2009-12-27 08:22:42 +00001755 /* crop into tiles of fixed size WxH */
1756 else if ((transform_image->columns > geometry.width) ||
cristy3ed852e2009-09-05 21:47:34 +00001757 (transform_image->rows > geometry.height))
anthonyc0429ed2009-12-27 08:22:42 +00001758 {
1759 Image
1760 *next;
1761
1762 long
1763 y;
1764
1765 register long
1766 x;
1767
1768 unsigned long
1769 height,
1770 width;
1771
1772 /*
1773 Crop repeatedly to create uniform scenes.
1774 */
1775 if (transform_image->page.width == 0)
1776 transform_image->page.width=transform_image->columns;
1777 if (transform_image->page.height == 0)
1778 transform_image->page.height=transform_image->rows;
1779 width=geometry.width;
1780 if (width == 0)
1781 width=transform_image->page.width;
1782 height=geometry.height;
1783 if (height == 0)
1784 height=transform_image->page.height;
1785 next=NewImageList();
1786 for (y=0; y < (long) transform_image->page.height; y+=height)
cristy3ed852e2009-09-05 21:47:34 +00001787 {
anthonyc0429ed2009-12-27 08:22:42 +00001788 for (x=0; x < (long) transform_image->page.width; x+=width)
cristy3ed852e2009-09-05 21:47:34 +00001789 {
anthonyc0429ed2009-12-27 08:22:42 +00001790 geometry.width=width;
1791 geometry.height=height;
1792 geometry.x=x;
1793 geometry.y=y;
1794 next=CropImage(transform_image,&geometry,&(*image)->exception);
cristy3ed852e2009-09-05 21:47:34 +00001795 if (next == (Image *) NULL)
1796 break;
anthonyc0429ed2009-12-27 08:22:42 +00001797 AppendImageToList(&crop_image,next);
cristy3ed852e2009-09-05 21:47:34 +00001798 }
anthonyc0429ed2009-12-27 08:22:42 +00001799 if (next == (Image *) NULL)
1800 break;
cristy3ed852e2009-09-05 21:47:34 +00001801 }
anthonyc0429ed2009-12-27 08:22:42 +00001802 }
cristy3ed852e2009-09-05 21:47:34 +00001803 if (crop_image == (Image *) NULL)
1804 transform_image=CloneImage(*image,0,0,MagickTrue,&(*image)->exception);
1805 else
1806 {
1807 transform_image=DestroyImage(transform_image);
1808 transform_image=GetFirstImageInList(crop_image);
1809 }
1810 *image=transform_image;
1811 }
1812 if (image_geometry == (const char *) NULL)
1813 return(MagickTrue);
1814 /*
1815 Scale image to a user specified size.
1816 */
1817 flags=ParseRegionGeometry(transform_image,image_geometry,&geometry,
1818 &(*image)->exception);
1819 if ((transform_image->columns == geometry.width) &&
1820 (transform_image->rows == geometry.height))
1821 return(MagickTrue);
1822 resize_image=ZoomImage(transform_image,geometry.width,geometry.height,
1823 &(*image)->exception);
1824 if (resize_image == (Image *) NULL)
1825 return(MagickFalse);
1826 transform_image=DestroyImage(transform_image);
1827 transform_image=resize_image;
1828 *image=transform_image;
1829 return(MagickTrue);
1830}
1831
1832/*
1833%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1834% %
1835% %
1836% %
1837% T r a n s f o r m I m a g e s %
1838% %
1839% %
1840% % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1841%
1842% TransformImages() calls TransformImage() on each image of a sequence.
1843%
1844% The format of the TransformImage method is:
1845%
1846% MagickBooleanType TransformImages(Image **image,
1847% const char *crop_geometry,const char *image_geometry)
1848%
1849% A description of each parameter follows:
1850%
1851% o image: the image The transformed image is returned as this parameter.
1852%
1853% o crop_geometry: A crop geometry string. This geometry defines a
1854% subregion of the image to crop.
1855%
1856% o image_geometry: An image geometry string. This geometry defines the
1857% final size of the image.
1858%
1859*/
1860MagickExport MagickBooleanType TransformImages(Image **images,
1861 const char *crop_geometry,const char *image_geometry)
1862{
1863 Image
1864 *image,
1865 **image_list,
1866 *transform_images;
1867
1868 MagickStatusType
1869 status;
1870
1871 register long
1872 i;
1873
1874 assert(images != (Image **) NULL);
1875 assert((*images)->signature == MagickSignature);
1876 if ((*images)->debug != MagickFalse)
1877 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
1878 (*images)->filename);
1879 image_list=ImageListToArray(*images,&(*images)->exception);
1880 if (image_list == (Image **) NULL)
1881 return(MagickFalse);
1882 status=MagickTrue;
1883 transform_images=NewImageList();
1884 for (i=0; image_list[i] != (Image *) NULL; i++)
1885 {
1886 image=image_list[i];
1887 status|=TransformImage(&image,crop_geometry,image_geometry);
1888 AppendImageToList(&transform_images,image);
1889 }
1890 *images=transform_images;
1891 image_list=(Image **) RelinquishMagickMemory(image_list);
1892 return(status != 0 ? MagickTrue : MagickFalse);
1893}
1894
1895/*
1896%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1897% %
1898% %
1899% %
1900% T r a n s p o s e I m a g e %
1901% %
1902% %
1903% %
1904%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1905%
1906% TransposeImage() creates a horizontal mirror image by reflecting the pixels
1907% around the central y-axis while rotating them by 90 degrees.
1908%
1909% The format of the TransposeImage method is:
1910%
1911% Image *TransposeImage(const Image *image,ExceptionInfo *exception)
1912%
1913% A description of each parameter follows:
1914%
1915% o image: the image.
1916%
1917% o exception: return any errors or warnings in this structure.
1918%
1919*/
1920MagickExport Image *TransposeImage(const Image *image,ExceptionInfo *exception)
1921{
1922#define TransposeImageTag "Transpose/Image"
1923
1924 Image
1925 *transpose_image;
1926
1927 long
1928 progress,
1929 y;
1930
1931 MagickBooleanType
1932 status;
1933
1934 RectangleInfo
1935 page;
1936
1937 CacheView
1938 *image_view,
1939 *transpose_view;
1940
1941 assert(image != (const Image *) NULL);
1942 assert(image->signature == MagickSignature);
1943 if (image->debug != MagickFalse)
1944 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1945 assert(exception != (ExceptionInfo *) NULL);
1946 assert(exception->signature == MagickSignature);
1947 transpose_image=CloneImage(image,image->rows,image->columns,MagickTrue,
1948 exception);
1949 if (transpose_image == (Image *) NULL)
1950 return((Image *) NULL);
1951 /*
1952 Transpose image.
1953 */
1954 status=MagickTrue;
1955 progress=0;
1956 image_view=AcquireCacheView(image);
1957 transpose_view=AcquireCacheView(transpose_image);
cristyb5d5f722009-11-04 03:03:49 +00001958#if defined(MAGICKCORE_OPENMP_SUPPORT)
1959 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00001960#endif
1961 for (y=0; y < (long) image->rows; y++)
1962 {
1963 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001964 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001965
1966 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001967 *restrict transpose_indexes,
1968 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00001969
1970 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001971 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001972
1973 if (status == MagickFalse)
1974 continue;
1975 p=GetCacheViewVirtualPixels(image_view,0,(long) image->rows-y-1,
1976 image->columns,1,exception);
1977 q=QueueCacheViewAuthenticPixels(transpose_view,(long) (image->rows-y-1),0,
1978 1,transpose_image->rows,exception);
1979 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
1980 {
1981 status=MagickFalse;
1982 continue;
1983 }
1984 (void) CopyMagickMemory(q,p,(size_t) image->columns*sizeof(*q));
1985 indexes=GetCacheViewAuthenticIndexQueue(image_view);
1986 if (indexes != (IndexPacket *) NULL)
1987 {
1988 transpose_indexes=GetCacheViewAuthenticIndexQueue(transpose_view);
1989 if (transpose_indexes != (IndexPacket *) NULL)
1990 (void) CopyMagickMemory(transpose_indexes,indexes,(size_t)
1991 image->columns*sizeof(*transpose_indexes));
1992 }
1993 if (SyncCacheViewAuthenticPixels(transpose_view,exception) == MagickFalse)
1994 status=MagickFalse;
1995 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1996 {
1997 MagickBooleanType
1998 proceed;
1999
cristyb5d5f722009-11-04 03:03:49 +00002000#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002001 #pragma omp critical (MagickCore_TransposeImage)
2002#endif
2003 proceed=SetImageProgress(image,TransposeImageTag,progress++,
2004 image->rows);
2005 if (proceed == MagickFalse)
2006 status=MagickFalse;
2007 }
2008 }
2009 transpose_view=DestroyCacheView(transpose_view);
2010 image_view=DestroyCacheView(image_view);
2011 transpose_image->type=image->type;
2012 page=transpose_image->page;
2013 Swap(page.width,page.height);
2014 Swap(page.x,page.y);
2015 if (page.width != 0)
2016 page.x=(long) (page.width-transpose_image->columns-page.x);
2017 transpose_image->page=page;
2018 if (status == MagickFalse)
2019 transpose_image=DestroyImage(transpose_image);
2020 return(transpose_image);
2021}
2022
2023/*
2024%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2025% %
2026% %
2027% %
2028% T r a n s v e r s e I m a g e %
2029% %
2030% %
2031% %
2032%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2033%
2034% TransverseImage() creates a vertical mirror image by reflecting the pixels
2035% around the central x-axis while rotating them by 270 degrees.
2036%
2037% The format of the TransverseImage method is:
2038%
2039% Image *TransverseImage(const Image *image,ExceptionInfo *exception)
2040%
2041% A description of each parameter follows:
2042%
2043% o image: the image.
2044%
2045% o exception: return any errors or warnings in this structure.
2046%
2047*/
2048MagickExport Image *TransverseImage(const Image *image,ExceptionInfo *exception)
2049{
2050#define TransverseImageTag "Transverse/Image"
2051
2052 Image
2053 *transverse_image;
2054
2055 long
2056 progress,
2057 y;
2058
2059 MagickBooleanType
2060 status;
2061
2062 RectangleInfo
2063 page;
2064
2065 CacheView
2066 *image_view,
2067 *transverse_view;
2068
2069 assert(image != (const Image *) NULL);
2070 assert(image->signature == MagickSignature);
2071 if (image->debug != MagickFalse)
2072 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2073 assert(exception != (ExceptionInfo *) NULL);
2074 assert(exception->signature == MagickSignature);
2075 transverse_image=CloneImage(image,image->rows,image->columns,MagickTrue,
2076 exception);
2077 if (transverse_image == (Image *) NULL)
2078 return((Image *) NULL);
2079 /*
2080 Transverse image.
2081 */
2082 status=MagickTrue;
2083 progress=0;
2084 image_view=AcquireCacheView(image);
2085 transverse_view=AcquireCacheView(transverse_image);
cristyb5d5f722009-11-04 03:03:49 +00002086#if defined(MAGICKCORE_OPENMP_SUPPORT)
2087 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00002088#endif
2089 for (y=0; y < (long) image->rows; y++)
2090 {
2091 MagickBooleanType
2092 sync;
2093
2094 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002095 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002096
2097 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002098 *restrict transverse_indexes,
2099 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002100
2101 register long
2102 x;
2103
2104 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002105 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002106
2107 if (status == MagickFalse)
2108 continue;
2109 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
2110 q=QueueCacheViewAuthenticPixels(transverse_view,(long) (image->rows-y-
2111 1),0,1,transverse_image->rows,exception);
2112 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2113 {
2114 status=MagickFalse;
2115 continue;
2116 }
2117 q+=image->columns;
2118 for (x=0; x < (long) image->columns; x++)
2119 *--q=(*p++);
2120 indexes=GetCacheViewAuthenticIndexQueue(image_view);
2121 if (indexes != (IndexPacket *) NULL)
2122 {
2123 transverse_indexes=GetCacheViewAuthenticIndexQueue(transverse_view);
2124 if (transverse_indexes != (IndexPacket *) NULL)
2125 for (x=0; x < (long) image->columns; x++)
2126 transverse_indexes[image->columns-x-1]=indexes[x];
2127 }
2128 sync=SyncCacheViewAuthenticPixels(transverse_view,exception);
2129 if (sync == MagickFalse)
2130 status=MagickFalse;
2131 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2132 {
2133 MagickBooleanType
2134 proceed;
2135
cristyb5d5f722009-11-04 03:03:49 +00002136#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002137 #pragma omp critical (MagickCore_TransverseImage)
2138#endif
2139 proceed=SetImageProgress(image,TransverseImageTag,progress++,
2140 image->rows);
2141 if (proceed == MagickFalse)
2142 status=MagickFalse;
2143 }
2144 }
2145 transverse_view=DestroyCacheView(transverse_view);
2146 image_view=DestroyCacheView(image_view);
2147 transverse_image->type=image->type;
2148 page=transverse_image->page;
2149 Swap(page.width,page.height);
2150 Swap(page.x,page.y);
2151 if (page.height != 0)
2152 page.y=(long) (page.height-transverse_image->rows-page.y);
2153 transverse_image->page=page;
2154 if (status == MagickFalse)
2155 transverse_image=DestroyImage(transverse_image);
2156 return(transverse_image);
2157}
2158
2159/*
2160%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2161% %
2162% %
2163% %
2164% T r i m I m a g e %
2165% %
2166% %
2167% %
2168%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2169%
2170% TrimImage() trims pixels from the image edges. It allocates the memory
2171% necessary for the new Image structure and returns a pointer to the new
2172% image.
2173%
2174% The format of the TrimImage method is:
2175%
2176% Image *TrimImage(const Image *image,ExceptionInfo *exception)
2177%
2178% A description of each parameter follows:
2179%
2180% o image: the image.
2181%
2182% o exception: return any errors or warnings in this structure.
2183%
2184*/
2185MagickExport Image *TrimImage(const Image *image,ExceptionInfo *exception)
2186{
2187 RectangleInfo
2188 geometry;
2189
2190 assert(image != (const Image *) NULL);
2191 assert(image->signature == MagickSignature);
2192 if (image->debug != MagickFalse)
2193 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2194 geometry=GetImageBoundingBox(image,exception);
2195 if ((geometry.width == 0) || (geometry.height == 0))
2196 {
2197 Image
2198 *crop_image;
2199
2200 crop_image=CloneImage(image,1,1,MagickTrue,exception);
2201 if (crop_image == (Image *) NULL)
2202 return((Image *) NULL);
2203 crop_image->background_color.opacity=(Quantum) TransparentOpacity;
2204 (void) SetImageBackgroundColor(crop_image);
2205 crop_image->page=image->page;
2206 crop_image->page.x=(-1);
2207 crop_image->page.y=(-1);
2208 return(crop_image);
2209 }
2210 geometry.x+=image->page.x;
2211 geometry.y+=image->page.y;
2212 return(CropImage(image,&geometry,exception));
2213}