/src/graphicsmagick/magick/transform.c
Line | Count | Source |
1 | | /* |
2 | | % Copyright (C) 2003 - 2022 GraphicsMagick Group |
3 | | % Copyright (C) 2002 ImageMagick Studio |
4 | | % Copyright 1991-1999 E. I. du Pont de Nemours and Company |
5 | | % |
6 | | % This program is covered by multiple licenses, which are described in |
7 | | % Copyright.txt. You should have received a copy of Copyright.txt with this |
8 | | % package; otherwise see http://www.graphicsmagick.org/www/Copyright.html. |
9 | | % |
10 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
11 | | % % |
12 | | % % |
13 | | % % |
14 | | % TTTTT RRRR AAA N N SSSSS FFFFF OOO RRRR M M % |
15 | | % T R R A A NN N SS F O O R R MM MM % |
16 | | % T RRRR AAAAA N N N SSS FFF O O RRRR M M M % |
17 | | % T R R A A N NN SS F O O R R M M % |
18 | | % T R R A A N N SSSSS F OOO R R M M % |
19 | | % % |
20 | | % % |
21 | | % GraphicsMagick Image Transform Methods % |
22 | | % % |
23 | | % % |
24 | | % Software Design % |
25 | | % John Cristy % |
26 | | % July 1992 % |
27 | | % % |
28 | | % % |
29 | | % % |
30 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
31 | | % |
32 | | % |
33 | | % |
34 | | */ |
35 | | |
36 | | /* |
37 | | Include declarations. |
38 | | */ |
39 | | #include "magick/studio.h" |
40 | | #include "magick/analyze.h" |
41 | | #include "magick/color.h" |
42 | | #include "magick/composite.h" |
43 | | #include "magick/log.h" |
44 | | #include "magick/monitor.h" |
45 | | #include "magick/pixel_cache.h" |
46 | | #include "magick/resize.h" |
47 | | #include "magick/texture.h" |
48 | | #include "magick/transform.h" |
49 | | #include "magick/utility.h" |
50 | | #include "magick/log.h" |
51 | | |
52 | | /* |
53 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
54 | | % % |
55 | | % % |
56 | | % % |
57 | | % C h o p I m a g e % |
58 | | % % |
59 | | % % |
60 | | % % |
61 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
62 | | % |
63 | | % Chop() removes a region of an image and collapses the image to occupy the |
64 | | % removed portion. |
65 | | % |
66 | | % The format of the ChopImage method is: |
67 | | % |
68 | | % Image *ChopImage(const Image *image,const RectangleInfo *chop_info |
69 | | % ExceptionInfo *exception) |
70 | | % |
71 | | % A description of each parameter follows: |
72 | | % |
73 | | % o image: The image. |
74 | | % |
75 | | % o chop_info: Define the region of the image to chop. |
76 | | % |
77 | | % o exception: Return any errors or warnings in this structure. |
78 | | % |
79 | | % |
80 | | */ |
81 | | MagickExport Image *ChopImage(const Image *image,const RectangleInfo *chop_info, |
82 | | ExceptionInfo *exception) |
83 | 0 | { |
84 | 0 | #define ChopImageText "[%s] Chop..." |
85 | |
|
86 | 0 | Image |
87 | 0 | *chop_image; |
88 | |
|
89 | 0 | unsigned long |
90 | 0 | row_count=0; |
91 | |
|
92 | 0 | MagickBool |
93 | 0 | monitor_active; |
94 | |
|
95 | 0 | long |
96 | 0 | y; |
97 | |
|
98 | 0 | RectangleInfo |
99 | 0 | clone_info; |
100 | |
|
101 | 0 | MagickPassFail |
102 | 0 | status=MagickPass; |
103 | | |
104 | | /* |
105 | | Check chop geometry. |
106 | | */ |
107 | 0 | assert(image != (const Image *) NULL); |
108 | 0 | assert(image->signature == MagickSignature); |
109 | 0 | assert(exception != (ExceptionInfo *) NULL); |
110 | 0 | assert(exception->signature == MagickSignature); |
111 | 0 | assert(chop_info != (RectangleInfo *) NULL); |
112 | 0 | if (((chop_info->x+(long) chop_info->width) < 0) || |
113 | 0 | ((chop_info->y+(long) chop_info->height) < 0) || |
114 | 0 | (chop_info->x > (long) image->columns) || |
115 | 0 | (chop_info->y > (long) image->rows)) |
116 | 0 | ThrowImageException3(OptionError,GeometryDoesNotContainImage, |
117 | 0 | UnableToChopImage); |
118 | 0 | clone_info=(*chop_info); |
119 | 0 | if ((clone_info.x+(long) clone_info.width) > (long) image->columns) |
120 | 0 | clone_info.width=(unsigned long) ((long) image->columns-clone_info.x); |
121 | 0 | if ((clone_info.y+(long) clone_info.height) > (long) image->rows) |
122 | 0 | clone_info.height=(unsigned long) ((long) image->rows-clone_info.y); |
123 | 0 | if (clone_info.x < 0) |
124 | 0 | { |
125 | 0 | clone_info.width-=(unsigned long) (-clone_info.x); |
126 | 0 | clone_info.x=0; |
127 | 0 | } |
128 | 0 | if (clone_info.y < 0) |
129 | 0 | { |
130 | 0 | clone_info.height-=(unsigned long) (-clone_info.y); |
131 | 0 | clone_info.y=0; |
132 | 0 | } |
133 | 0 | if ((clone_info.width >= image->columns) || |
134 | 0 | (clone_info.height >= image->rows)) |
135 | 0 | ThrowImageException3(OptionError,GeometryDoesNotContainImage, |
136 | 0 | UnableToChopImage); |
137 | | /* |
138 | | Initialize chop image attributes. |
139 | | */ |
140 | 0 | chop_image=CloneImage(image,image->columns-clone_info.width, |
141 | 0 | image->rows-clone_info.height,False,exception); |
142 | 0 | if (chop_image == (Image *) NULL) |
143 | 0 | return((Image *) NULL); |
144 | | |
145 | | /* |
146 | | Extract chop image. |
147 | | */ |
148 | 0 | monitor_active=MagickMonitorActive(); |
149 | |
|
150 | | #if defined(HAVE_OPENMP) && !defined(DisableSlowOpenMP) |
151 | | # if defined(TUNE_OPENMP) |
152 | | # pragma omp parallel for schedule(runtime) shared(row_count, status) |
153 | | # else |
154 | | # pragma omp parallel for schedule(static,4) shared(row_count, status) |
155 | | # endif |
156 | | #endif |
157 | 0 | for (y=0; y < (long) clone_info.y; y++) |
158 | 0 | { |
159 | 0 | register const PixelPacket |
160 | 0 | *p; |
161 | |
|
162 | 0 | register const IndexPacket |
163 | 0 | *indexes; |
164 | |
|
165 | 0 | register IndexPacket |
166 | 0 | *chop_indexes; |
167 | |
|
168 | 0 | register long |
169 | 0 | x; |
170 | |
|
171 | 0 | register PixelPacket |
172 | 0 | *q; |
173 | |
|
174 | 0 | MagickBool |
175 | 0 | thread_status; |
176 | |
|
177 | 0 | thread_status=status; |
178 | 0 | if (thread_status == MagickFail) |
179 | 0 | continue; |
180 | | |
181 | 0 | p=AcquireImagePixels(image,0,y,image->columns,1,exception); |
182 | 0 | q=SetImagePixelsEx(chop_image,0,y,chop_image->columns,1,exception); |
183 | 0 | if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL)) |
184 | 0 | thread_status=MagickFail; |
185 | |
|
186 | 0 | if (thread_status != MagickFail) |
187 | 0 | { |
188 | 0 | indexes=AccessImmutableIndexes(image); |
189 | 0 | chop_indexes=AccessMutableIndexes(chop_image); |
190 | 0 | for (x=0; x < (long) image->columns; x++) |
191 | 0 | { |
192 | 0 | if ((x < clone_info.x) || (x >= (long) (clone_info.x+clone_info.width))) |
193 | 0 | { |
194 | 0 | if ((indexes != (const IndexPacket *) NULL) && |
195 | 0 | (chop_indexes != (IndexPacket *) NULL)) |
196 | 0 | *chop_indexes++=indexes[x]; |
197 | 0 | *q=(*p); |
198 | 0 | q++; |
199 | 0 | } |
200 | 0 | p++; |
201 | 0 | } |
202 | 0 | if (!SyncImagePixelsEx(chop_image,exception)) |
203 | 0 | thread_status=MagickFail; |
204 | 0 | } |
205 | |
|
206 | | #if defined(HAVE_OPENMP) && !defined(DisableSlowOpenMP) |
207 | | # pragma omp atomic |
208 | | #endif |
209 | 0 | row_count++; |
210 | |
|
211 | 0 | if (monitor_active) |
212 | 0 | { |
213 | 0 | unsigned long |
214 | 0 | thread_row_count; |
215 | |
|
216 | | #if defined(HAVE_OPENMP) |
217 | | # pragma omp flush (row_count) |
218 | | #endif |
219 | 0 | thread_row_count=row_count; |
220 | 0 | if (QuantumTick(thread_row_count,chop_image->rows)) |
221 | 0 | if (!MagickMonitorFormatted(thread_row_count,chop_image->rows,exception, |
222 | 0 | ChopImageText,image->filename)) |
223 | 0 | thread_status=MagickFail; |
224 | 0 | } |
225 | |
|
226 | 0 | if (thread_status == MagickFail) |
227 | 0 | { |
228 | 0 | status=MagickFail; |
229 | | #if defined(HAVE_OPENMP) && !defined(DisableSlowOpenMP) |
230 | | # pragma omp flush (status) |
231 | | #endif |
232 | 0 | } |
233 | 0 | } |
234 | | /* |
235 | | Extract chop image. |
236 | | */ |
237 | | #if defined(HAVE_OPENMP) && !defined(DisableSlowOpenMP) |
238 | | # if defined(TUNE_OPENMP) |
239 | | # pragma omp parallel for schedule(runtime) shared(row_count, status) |
240 | | # else |
241 | | # pragma omp parallel for schedule(static,4) shared(row_count, status) |
242 | | # endif |
243 | | #endif |
244 | 0 | for (y=0; y < (long) (image->rows-(clone_info.y+clone_info.height)); y++) |
245 | 0 | { |
246 | 0 | register const PixelPacket |
247 | 0 | *p; |
248 | |
|
249 | 0 | register const IndexPacket |
250 | 0 | *indexes; |
251 | |
|
252 | 0 | register IndexPacket |
253 | 0 | *chop_indexes; |
254 | |
|
255 | 0 | register long |
256 | 0 | x; |
257 | |
|
258 | 0 | register PixelPacket |
259 | 0 | *q; |
260 | |
|
261 | 0 | MagickBool |
262 | 0 | thread_status; |
263 | |
|
264 | 0 | thread_status=status; |
265 | 0 | if (thread_status == MagickFail) |
266 | 0 | continue; |
267 | | |
268 | 0 | p=AcquireImagePixels(image,0,clone_info.y+clone_info.height+y,image->columns,1,exception); |
269 | 0 | q=SetImagePixelsEx(chop_image,0,clone_info.y+y,chop_image->columns,1,exception); |
270 | 0 | if ((p == (PixelPacket *) NULL) || (q == (PixelPacket *) NULL)) |
271 | 0 | thread_status=MagickFail; |
272 | |
|
273 | 0 | if (thread_status != MagickFail) |
274 | 0 | { |
275 | 0 | indexes=AccessImmutableIndexes(image); |
276 | 0 | chop_indexes=AccessMutableIndexes(chop_image); |
277 | 0 | for (x=0; x < (long) image->columns; x++) |
278 | 0 | { |
279 | 0 | if ((x < clone_info.x) || (x >= (long) (clone_info.x+clone_info.width))) |
280 | 0 | { |
281 | 0 | if ((indexes != (const IndexPacket *) NULL) && |
282 | 0 | (chop_indexes != (IndexPacket *) NULL)) |
283 | 0 | *chop_indexes++=indexes[x]; |
284 | 0 | *q=(*p); |
285 | 0 | q++; |
286 | 0 | } |
287 | 0 | p++; |
288 | 0 | } |
289 | 0 | if (!SyncImagePixelsEx(chop_image,exception)) |
290 | 0 | thread_status=MagickFail; |
291 | 0 | } |
292 | |
|
293 | | #if defined(HAVE_OPENMP) && !defined(DisableSlowOpenMP) |
294 | | # pragma omp atomic |
295 | | #endif |
296 | 0 | row_count++; |
297 | |
|
298 | 0 | if (monitor_active) |
299 | 0 | { |
300 | 0 | unsigned long |
301 | 0 | thread_row_count; |
302 | |
|
303 | | #if defined(HAVE_OPENMP) |
304 | | # pragma omp flush (row_count) |
305 | | #endif |
306 | 0 | thread_row_count=row_count; |
307 | 0 | if (QuantumTick(thread_row_count,chop_image->rows)) |
308 | 0 | if (!MagickMonitorFormatted(thread_row_count,chop_image->rows,exception, |
309 | 0 | ChopImageText,image->filename)) |
310 | 0 | thread_status=MagickFail; |
311 | 0 | } |
312 | |
|
313 | 0 | if (thread_status == MagickFail) |
314 | 0 | { |
315 | 0 | status=MagickFail; |
316 | | #if defined(HAVE_OPENMP) && !defined(DisableSlowOpenMP) |
317 | | # pragma omp flush (status) |
318 | | #endif |
319 | 0 | } |
320 | 0 | } |
321 | 0 | if (row_count < chop_image->rows) |
322 | 0 | { |
323 | 0 | DestroyImage(chop_image); |
324 | 0 | return((Image *) NULL); |
325 | 0 | } |
326 | 0 | chop_image->is_grayscale=image->is_grayscale; |
327 | 0 | return(chop_image); |
328 | 0 | } |
329 | | |
330 | | /* |
331 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
332 | | % % |
333 | | % % |
334 | | % % |
335 | | % C o a l e s c e I m a g e s % |
336 | | % % |
337 | | % % |
338 | | % % |
339 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
340 | | % |
341 | | % CoalesceImages() composites a set of images while respecting any page |
342 | | % offsets and disposal methods. GIF, MIFF, and MNG animation sequences |
343 | | % typically start with an image background and each subsequent image |
344 | | % varies in size and offset. CoalesceImages() returns a new sequence |
345 | | % where each image in the sequence is the same size as the first and |
346 | | % composited with the next image in the sequence. |
347 | | % |
348 | | % The format of the CoalesceImages method is: |
349 | | % |
350 | | % Image *CoalesceImages(const Image *image,ExceptionInfo *exception) |
351 | | % |
352 | | % A description of each parameter follows: |
353 | | % |
354 | | % o image: The image sequence. |
355 | | % |
356 | | % o exception: Return any errors or warnings in this structure. |
357 | | % |
358 | | */ |
359 | | MagickExport Image *CoalesceImages(const Image *image,ExceptionInfo *exception) |
360 | 726 | { |
361 | 726 | Image |
362 | 726 | *coalesce_image, |
363 | 726 | *previous_image; |
364 | | |
365 | 726 | register const Image |
366 | 726 | *next; |
367 | | |
368 | 726 | register long |
369 | 726 | i; |
370 | | |
371 | 726 | MagickBool |
372 | 726 | found_transparency=False; |
373 | | |
374 | | /* |
375 | | Coalesce the image sequence. |
376 | | */ |
377 | 726 | assert(image != (Image *) NULL); |
378 | 726 | assert(image->signature == MagickSignature); |
379 | 726 | assert(exception != (ExceptionInfo *) NULL); |
380 | 726 | assert(exception->signature == MagickSignature); |
381 | 726 | if (image->next == (Image *) NULL) |
382 | 0 | ThrowImageException3(ImageError,ImageSequenceIsRequired, |
383 | 726 | UnableToCoalesceImage); |
384 | | /* |
385 | | Clone first image in sequence. |
386 | | */ |
387 | 726 | coalesce_image=CloneImage(image,0,0,True,exception); |
388 | 726 | if (coalesce_image == (Image *) NULL) |
389 | 0 | return((Image *) NULL); |
390 | 726 | (void) memset(&coalesce_image->page,0,sizeof(RectangleInfo)); |
391 | 726 | previous_image=coalesce_image; |
392 | | /* |
393 | | Coalesce image. |
394 | | */ |
395 | 28.5k | for (next=image->next; next != (Image *) NULL; next=next->next) |
396 | 27.8k | { |
397 | 27.8k | switch (next->dispose) |
398 | 27.8k | { |
399 | 27.8k | case UndefinedDispose: |
400 | 27.8k | case NoneDispose: |
401 | 27.8k | { |
402 | 27.8k | coalesce_image->next=CloneImage(coalesce_image,0,0,True,exception); |
403 | 27.8k | if (coalesce_image->next != (Image *) NULL) |
404 | 27.8k | previous_image=coalesce_image->next; |
405 | 27.8k | break; |
406 | 27.8k | } |
407 | 0 | case BackgroundDispose: |
408 | 0 | { |
409 | | /* |
410 | | Fill image with transparent color, if one exists. |
411 | | */ |
412 | 0 | coalesce_image->next=CloneImage(coalesce_image,0,0,True,exception); |
413 | 0 | if (coalesce_image->next != (Image *) NULL) { |
414 | 0 | for (i = 0; i < (long) coalesce_image->colors; i++) { |
415 | 0 | if (coalesce_image->colormap[i].opacity == TransparentOpacity) { |
416 | 0 | found_transparency = True; |
417 | 0 | (void) SetImageColor(coalesce_image->next,&coalesce_image->colormap[i]); |
418 | 0 | break; |
419 | 0 | } |
420 | 0 | } |
421 | 0 | if (!found_transparency) |
422 | 0 | (void) SetImage(coalesce_image->next,OpaqueOpacity); |
423 | 0 | } |
424 | 0 | break; |
425 | 27.8k | } |
426 | 0 | case PreviousDispose: |
427 | 0 | default: |
428 | 0 | { |
429 | 0 | coalesce_image->next=CloneImage(previous_image,0,0,True,exception); |
430 | 0 | break; |
431 | 0 | } |
432 | 27.8k | } |
433 | 27.8k | if (coalesce_image->next == (Image *) NULL) |
434 | 0 | { |
435 | 0 | DestroyImageList(coalesce_image); |
436 | 0 | return((Image *) NULL); |
437 | 0 | } |
438 | 27.8k | coalesce_image->next->previous=coalesce_image; |
439 | 27.8k | coalesce_image=coalesce_image->next; |
440 | 27.8k | coalesce_image->delay=next->delay; |
441 | 27.8k | coalesce_image->start_loop=next->start_loop; |
442 | 27.8k | (void) CompositeImage(coalesce_image,next->matte ? OverCompositeOp : |
443 | 27.8k | CopyCompositeOp,next,next->page.x,next->page.y); |
444 | 27.8k | } |
445 | | |
446 | 28.5k | while (coalesce_image->previous != (Image *) NULL) |
447 | 27.8k | coalesce_image=coalesce_image->previous; |
448 | 726 | return(coalesce_image); |
449 | 726 | } |
450 | | |
451 | | /* |
452 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
453 | | % % |
454 | | % % |
455 | | % % |
456 | | % C r o p I m a g e % |
457 | | % % |
458 | | % % |
459 | | % % |
460 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
461 | | % |
462 | | % Use CropImage() to extract a region of the image starting at the offset |
463 | | % defined by geometry. As a special feature, if the geometry "0x0" is |
464 | | % is passed, GetImageBoundingBox() is used to locate the edges of the |
465 | | % image and the image is cropped ("trimmed") to that boundary. |
466 | | % |
467 | | % The format of the CropImage method is: |
468 | | % |
469 | | % Image *CropImage(const Image *image,const RectangleInfo *geometry, |
470 | | % ExceptionInfo *exception) |
471 | | % |
472 | | % A description of each parameter follows: |
473 | | % |
474 | | % o image: The image. |
475 | | % |
476 | | % o geometry: Define the region of the image to crop with members |
477 | | % x, y, width, and height. |
478 | | % |
479 | | % o exception: Return any errors or warnings in this structure. |
480 | | % |
481 | | % |
482 | | */ |
483 | | MagickExport Image *CropImage(const Image *image,const RectangleInfo *geometry, |
484 | | ExceptionInfo *exception) |
485 | 1.17k | { |
486 | 1.17k | Image |
487 | 1.17k | *crop_image; |
488 | | |
489 | 1.17k | unsigned long |
490 | 1.17k | row_count=0; |
491 | | |
492 | 1.17k | MagickBool |
493 | 1.17k | monitor_active; |
494 | | |
495 | 1.17k | long |
496 | 1.17k | y; |
497 | | |
498 | 1.17k | RectangleInfo |
499 | 1.17k | page; |
500 | | |
501 | 1.17k | MagickPassFail |
502 | 1.17k | status=MagickPass; |
503 | | |
504 | 1.17k | MagickBool |
505 | 1.17k | transform_logging; |
506 | | |
507 | | /* |
508 | | Check crop geometry. |
509 | | */ |
510 | 1.17k | assert(image != (const Image *) NULL); |
511 | 1.17k | assert(image->signature == MagickSignature); |
512 | 1.17k | assert(geometry != (const RectangleInfo *) NULL); |
513 | 1.17k | assert(exception != (ExceptionInfo *) NULL); |
514 | 1.17k | assert(exception->signature == MagickSignature); |
515 | 1.17k | transform_logging=IsEventLogged(TransformEvent); |
516 | 1.17k | if (transform_logging) |
517 | 0 | (void) LogMagickEvent(TransformEvent,GetMagickModule(), |
518 | 0 | "Crop Geometry: %lux%lu%+ld%+ld", |
519 | 0 | geometry->width, geometry->height, |
520 | 0 | geometry->x, geometry->y); |
521 | 1.17k | if ((geometry->width != 0) || (geometry->height != 0)) |
522 | 1.17k | { |
523 | 1.17k | if (((geometry->x+(long) geometry->width) < 0) || |
524 | 1.17k | ((geometry->y+(long) geometry->height) < 0) || |
525 | 1.17k | (geometry->x >= (long) image->columns) || |
526 | 1.17k | (geometry->y >= (long) image->rows)) |
527 | 0 | ThrowImageException(OptionError,GeometryDoesNotContainImage, |
528 | 1.17k | MagickMsg(ResourceLimitError,UnableToCropImage)); |
529 | 1.17k | } |
530 | 1.17k | page=(*geometry); |
531 | 1.17k | if ((page.width != 0) || (page.height != 0)) |
532 | 1.17k | { |
533 | 1.17k | if ((page.x+(long) page.width) > (long) image->columns) |
534 | 0 | page.width=image->columns-page.x; |
535 | 1.17k | if ((page.y+(long) page.height) > (long) image->rows) |
536 | 0 | page.height=image->rows-page.y; |
537 | 1.17k | if (page.x < 0) |
538 | 0 | { |
539 | 0 | page.width+=page.x; |
540 | 0 | page.x=0; |
541 | 0 | } |
542 | 1.17k | if (page.y < 0) |
543 | 0 | { |
544 | 0 | page.height+=page.y; |
545 | 0 | page.y=0; |
546 | 0 | } |
547 | 1.17k | } |
548 | 0 | else |
549 | 0 | { |
550 | | /* |
551 | | Set bounding box to the image dimensions. |
552 | | */ |
553 | 0 | page=GetImageBoundingBox(image,exception); |
554 | 0 | page.width+=geometry->x*2; |
555 | 0 | page.height+=geometry->y*2; |
556 | 0 | page.x-=geometry->x; |
557 | 0 | if (page.x < 0) |
558 | 0 | page.x=0; |
559 | 0 | page.y-=geometry->y; |
560 | 0 | if (page.y < 0) |
561 | 0 | page.y=0; |
562 | 0 | if (transform_logging) |
563 | 0 | (void) LogMagickEvent(TransformEvent,GetMagickModule(), |
564 | 0 | "Bounding Page: %lux%lu%+ld%+ld", |
565 | 0 | page.width, page.height, page.x, page.y); |
566 | 0 | if ((((long) page.width+page.x) > (long) image->columns) || |
567 | 0 | (((long) page.height+page.y) > (long) image->rows)) |
568 | 0 | ThrowImageException(OptionError,GeometryDoesNotContainImage, |
569 | 0 | MagickMsg(ResourceLimitError,UnableToCropImage)); |
570 | 0 | } |
571 | 1.17k | if ((page.width == 0) || (page.height == 0)) |
572 | 0 | ThrowImageException(OptionError,GeometryDimensionsAreZero, |
573 | 1.17k | MagickMsg(ResourceLimitError,UnableToCropImage)); |
574 | 1.17k | if ((page.width == image->columns) && (page.height == image->rows) && |
575 | 0 | (page.x == 0) && (page.y == 0)) |
576 | 0 | return(CloneImage(image,0,0,True,exception)); |
577 | | /* |
578 | | Initialize crop image attributes. |
579 | | */ |
580 | 1.17k | crop_image=CloneImage(image,page.width,page.height,True,exception); |
581 | 1.17k | if (crop_image == (Image *) NULL) |
582 | 0 | return((Image *) NULL); |
583 | | /* |
584 | | Extract crop image. |
585 | | */ |
586 | 1.17k | crop_image->page=page; |
587 | 1.17k | if ((geometry->width == 0) || (geometry->height == 0)) |
588 | 0 | (void) memset(&crop_image->page,0,sizeof(RectangleInfo)); |
589 | | |
590 | 1.17k | monitor_active=MagickMonitorActive(); |
591 | | |
592 | | #if defined(HAVE_OPENMP) && !defined(DisableSlowOpenMP) |
593 | | # if defined(TUNE_OPENMP) |
594 | | # pragma omp parallel for schedule(runtime) shared(row_count, status) |
595 | | # else |
596 | | # pragma omp parallel for schedule(static,4) shared(row_count, status) |
597 | | # endif |
598 | | #endif |
599 | 356k | for (y=0; y < (long) crop_image->rows; y++) |
600 | 355k | { |
601 | 355k | const PixelPacket |
602 | 355k | *p; |
603 | | |
604 | 355k | const IndexPacket |
605 | 355k | *indexes; |
606 | | |
607 | 355k | IndexPacket |
608 | 355k | *crop_indexes; |
609 | | |
610 | 355k | PixelPacket |
611 | 355k | *q; |
612 | | |
613 | 355k | MagickBool |
614 | 355k | thread_status; |
615 | | |
616 | 355k | thread_status=status; |
617 | 355k | if (thread_status == MagickFail) |
618 | 0 | continue; |
619 | | |
620 | 355k | p=AcquireImagePixels(image,page.x,page.y+y,crop_image->columns,1,exception); |
621 | 355k | q=SetImagePixelsEx(crop_image,0,y,crop_image->columns,1,exception); |
622 | 355k | if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL)) |
623 | 0 | thread_status=MagickFail; |
624 | | |
625 | 355k | if (thread_status != MagickFail) |
626 | 355k | { |
627 | 355k | (void) memcpy(q,p,crop_image->columns*sizeof(PixelPacket)); |
628 | 355k | indexes=AccessImmutableIndexes(image); |
629 | 355k | crop_indexes=AccessMutableIndexes(crop_image); |
630 | 355k | if ((indexes != (const IndexPacket *) NULL) && |
631 | 553 | (crop_indexes != (IndexPacket *) NULL)) |
632 | 553 | (void) memcpy(crop_indexes,indexes,crop_image->columns* |
633 | 553 | sizeof(IndexPacket)); |
634 | 355k | if (!SyncImagePixelsEx(crop_image,exception)) |
635 | 0 | thread_status=MagickFail; |
636 | 355k | } |
637 | | |
638 | | #if defined(HAVE_OPENMP) && !defined(DisableSlowOpenMP) |
639 | | # pragma omp atomic |
640 | | #endif |
641 | 355k | row_count++; |
642 | | |
643 | 355k | if (monitor_active) |
644 | 0 | { |
645 | 0 | unsigned long |
646 | 0 | thread_row_count; |
647 | |
|
648 | | #if defined(HAVE_OPENMP) |
649 | | # pragma omp flush (row_count) |
650 | | #endif |
651 | 0 | thread_row_count=row_count; |
652 | 0 | if (QuantumTick(thread_row_count,crop_image->rows)) |
653 | 0 | if (!MagickMonitorFormatted(thread_row_count,crop_image->rows,exception, |
654 | 0 | "[%s] Crop: %lux%lu+%ld+%ld...", |
655 | 0 | crop_image->filename, |
656 | 0 | crop_image->columns,crop_image->rows, |
657 | 0 | page.x,page.y)) |
658 | 0 | thread_status=MagickFail; |
659 | 0 | } |
660 | | |
661 | 355k | if (thread_status == MagickFail) |
662 | 0 | { |
663 | 0 | status=MagickFail; |
664 | | #if defined(HAVE_OPENMP) && !defined(DisableSlowOpenMP) |
665 | | # pragma omp flush (status) |
666 | | #endif |
667 | 0 | } |
668 | 355k | } |
669 | 1.17k | if (row_count < crop_image->rows) |
670 | 0 | { |
671 | 0 | DestroyImage(crop_image); |
672 | 0 | return((Image *) NULL); |
673 | 0 | } |
674 | 1.17k | crop_image->is_grayscale=image->is_grayscale; |
675 | 1.17k | return(crop_image); |
676 | 1.17k | } |
677 | | |
678 | | /* |
679 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
680 | | % % |
681 | | % % |
682 | | % % |
683 | | % D e c o n s t r u c t I m a g e s % |
684 | | % % |
685 | | % % |
686 | | % % |
687 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
688 | | % |
689 | | % DeconstructImages() compares each image with the next in a sequence and |
690 | | % returns the maximum bounding region of any pixel differences it discovers. |
691 | | % |
692 | | % The format of the DeconstructImages method is: |
693 | | % |
694 | | % Image *DeconstructImages(const Image *image,ExceptionInfo *exception) |
695 | | % |
696 | | % A description of each parameter follows: |
697 | | % |
698 | | % o image: The image. |
699 | | % |
700 | | % o exception: Return any errors or warnings in this structure. |
701 | | % |
702 | | % |
703 | | */ |
704 | | MagickExport Image *DeconstructImages(const Image *image, |
705 | | ExceptionInfo *exception) |
706 | 0 | { |
707 | 0 | Image |
708 | 0 | *crop_image, |
709 | 0 | *crop_next, |
710 | 0 | *deconstruct_image; |
711 | |
|
712 | 0 | long |
713 | 0 | y; |
714 | |
|
715 | 0 | RectangleInfo |
716 | 0 | *bounds; |
717 | |
|
718 | 0 | register const Image |
719 | 0 | *next; |
720 | |
|
721 | 0 | register const PixelPacket |
722 | 0 | *p; |
723 | |
|
724 | 0 | register long |
725 | 0 | i, |
726 | 0 | x; |
727 | |
|
728 | 0 | register PixelPacket |
729 | 0 | *q; |
730 | |
|
731 | 0 | assert(image != (const Image *) NULL); |
732 | 0 | assert(image->signature == MagickSignature); |
733 | 0 | assert(exception != (ExceptionInfo *) NULL); |
734 | 0 | assert(exception->signature == MagickSignature); |
735 | 0 | if (image->next == (Image *) NULL) |
736 | 0 | ThrowImageException3(ImageError,ImageSequenceIsRequired, |
737 | 0 | UnableToDeconstructImageSequence); |
738 | | /* |
739 | | Ensure the image are the same size. |
740 | | */ |
741 | 0 | for (next=image; next != (Image *) NULL; next=next->next) |
742 | 0 | { |
743 | 0 | if ((next->columns != image->columns) || (next->rows != image->rows)) |
744 | 0 | ThrowImageException(OptionError,ImagesAreNotTheSameSize, |
745 | 0 | MagickMsg(ImageError,UnableToDeconstructImageSequence)); |
746 | 0 | } |
747 | | /* |
748 | | Allocate memory. |
749 | | */ |
750 | 0 | bounds=MagickAllocateMemory(RectangleInfo *, |
751 | 0 | GetImageListLength(image)*sizeof(RectangleInfo)); |
752 | 0 | if (bounds == (RectangleInfo *) NULL) |
753 | 0 | ThrowImageException(ResourceLimitError,MemoryAllocationFailed, |
754 | 0 | MagickMsg(ImageError,UnableToDeconstructImageSequence)); |
755 | | /* |
756 | | Compute the bounding box for each next in the sequence. |
757 | | */ |
758 | 0 | i=0; |
759 | 0 | for (next=image->next; next != (const Image *) NULL; next=next->next) |
760 | 0 | { |
761 | | /* |
762 | | Set bounding box to the next dimensions. |
763 | | */ |
764 | 0 | for (x=0; x < (long) next->columns; x++) |
765 | 0 | { |
766 | 0 | p=AcquireImagePixels(next,x,0,1,next->rows,exception); |
767 | 0 | q=GetImagePixels(next->previous,x,0,1,next->previous->rows); |
768 | 0 | if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL)) |
769 | 0 | break; |
770 | 0 | for (y=0; y < (long) next->rows; y++) |
771 | 0 | { |
772 | 0 | if (!FuzzyColorMatch(p,q,next->fuzz)) |
773 | 0 | break; |
774 | 0 | p++; |
775 | 0 | q++; |
776 | 0 | } |
777 | 0 | if (y < (long) next->rows) |
778 | 0 | break; |
779 | 0 | } |
780 | 0 | bounds[i].x=x; |
781 | 0 | for (y=0; y < (long) next->rows; y++) |
782 | 0 | { |
783 | 0 | p=AcquireImagePixels(next,0,y,next->columns,1,exception); |
784 | 0 | q=GetImagePixels(next->previous,0,y,next->previous->columns,1); |
785 | 0 | if ((p == (PixelPacket *) NULL) || (q == (PixelPacket *) NULL)) |
786 | 0 | break; |
787 | 0 | for (x=0; x < (long) next->columns; x++) |
788 | 0 | { |
789 | 0 | if (!FuzzyColorMatch(p,q,next->fuzz)) |
790 | 0 | break; |
791 | 0 | p++; |
792 | 0 | q++; |
793 | 0 | } |
794 | 0 | if (x < (long) next->columns) |
795 | 0 | break; |
796 | 0 | } |
797 | 0 | bounds[i].y=y; |
798 | 0 | for (x=(long) next->columns-1; x >= 0; x--) |
799 | 0 | { |
800 | 0 | p=AcquireImagePixels(next,x,0,1,next->rows,exception); |
801 | 0 | q=GetImagePixels(next->previous,x,0,1,next->previous->rows); |
802 | 0 | if ((p == (PixelPacket *) NULL) || (q == (PixelPacket *) NULL)) |
803 | 0 | break; |
804 | 0 | for (y=0; y < (long) next->rows; y++) |
805 | 0 | { |
806 | 0 | if (!FuzzyColorMatch(p,q,next->fuzz)) |
807 | 0 | break; |
808 | 0 | p++; |
809 | 0 | q++; |
810 | 0 | } |
811 | 0 | if (y < (long) next->rows) |
812 | 0 | break; |
813 | 0 | } |
814 | 0 | bounds[i].width=x-bounds[i].x+1; |
815 | 0 | for (y=(long) next->rows-1; y >= 0; y--) |
816 | 0 | { |
817 | 0 | p=AcquireImagePixels(next,0,y,next->columns,1,exception); |
818 | 0 | q=GetImagePixels(next->previous,0,y,next->previous->columns,1); |
819 | 0 | if ((p == (PixelPacket *) NULL) || (q == (PixelPacket *) NULL)) |
820 | 0 | break; |
821 | 0 | for (x=0; x < (long) next->columns; x++) |
822 | 0 | { |
823 | 0 | if (!FuzzyColorMatch(p,q,next->fuzz)) |
824 | 0 | break; |
825 | 0 | p++; |
826 | 0 | q++; |
827 | 0 | } |
828 | 0 | if (x < (long) next->columns) |
829 | 0 | break; |
830 | 0 | } |
831 | 0 | bounds[i].height=y-bounds[i].y+1; |
832 | 0 | i++; |
833 | 0 | } |
834 | | /* |
835 | | Clone first image in sequence. |
836 | | */ |
837 | 0 | deconstruct_image=CloneImage(image,0,0,True,exception); |
838 | 0 | if (deconstruct_image == (Image *) NULL) |
839 | 0 | { |
840 | 0 | MagickFreeMemory(bounds); |
841 | 0 | return((Image *) NULL); |
842 | 0 | } |
843 | | /* |
844 | | Deconstruct the image sequence. |
845 | | */ |
846 | 0 | i=0; |
847 | 0 | for (next=image->next; next != (Image *) NULL; next=next->next) |
848 | 0 | { |
849 | 0 | crop_image=CloneImage(next,0,0,True,exception); |
850 | 0 | if (crop_image == (Image *) NULL) |
851 | 0 | break; |
852 | 0 | crop_next=CropImage(crop_image,&bounds[i++],exception); |
853 | 0 | DestroyImage(crop_image); |
854 | 0 | if (crop_next == (Image *) NULL) |
855 | 0 | break; |
856 | 0 | deconstruct_image->next=crop_next; |
857 | 0 | crop_next->previous=deconstruct_image; |
858 | 0 | deconstruct_image=deconstruct_image->next; |
859 | 0 | } |
860 | 0 | MagickFreeMemory(bounds); |
861 | 0 | while (deconstruct_image->previous != (Image *) NULL) |
862 | 0 | deconstruct_image=deconstruct_image->previous; |
863 | 0 | if (next != (Image *) NULL) |
864 | 0 | { |
865 | 0 | DestroyImageList(deconstruct_image); |
866 | 0 | return((Image *) NULL); |
867 | 0 | } |
868 | 0 | return(deconstruct_image); |
869 | 0 | } |
870 | | |
871 | | /* |
872 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
873 | | % % |
874 | | % % |
875 | | % % |
876 | | % E x t e n t I m a g e % |
877 | | % % |
878 | | % % |
879 | | % % |
880 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
881 | | % |
882 | | % Use ExtentImage() to change the image dimensions as specified by geometry |
883 | | % width and height. The existing image content is composited at the position |
884 | | % specified by geometry x and y using the image compose method. Existing |
885 | | % image content which falls outside the bounds of the new image dimensions |
886 | | % is discarded. |
887 | | % |
888 | | % The format of the ExtentImage method is: |
889 | | % |
890 | | % Image *ExtentImage(const Image *image,const RectangleInfo *geometry, |
891 | | % ExceptionInfo *exception) |
892 | | % |
893 | | % A description of each parameter follows: |
894 | | % |
895 | | % o image: The image. |
896 | | % |
897 | | % o geometry: Define the new image dimension with width and height, and |
898 | | % the top left coordinate to place the existing image content with |
899 | | % x and y. |
900 | | % |
901 | | % o exception: Return any errors or warnings in this structure. |
902 | | % |
903 | | % |
904 | | */ |
905 | | MagickExport Image *ExtentImage(const Image *image,const RectangleInfo *geometry, |
906 | | ExceptionInfo *exception) |
907 | 0 | { |
908 | 0 | Image |
909 | 0 | *extent_image; |
910 | |
|
911 | 0 | assert(image != (const Image *) NULL); |
912 | 0 | assert(image->signature == MagickSignature); |
913 | 0 | assert(geometry != (const RectangleInfo *) NULL); |
914 | 0 | assert(exception != (ExceptionInfo *) NULL); |
915 | 0 | assert(exception->signature == MagickSignature); |
916 | | |
917 | | /* |
918 | | Allocate canvas image |
919 | | */ |
920 | 0 | if ((extent_image=CloneImage(image,geometry->width,geometry->height, |
921 | 0 | MagickTrue,exception)) == (Image *) NULL) |
922 | 0 | return((Image *) NULL); |
923 | | |
924 | | /* |
925 | | Set canvas image color to background color |
926 | | */ |
927 | 0 | if ((SetImage(extent_image,image->background_color.opacity)) == MagickFail) |
928 | 0 | { |
929 | 0 | CopyException(exception,&extent_image->exception); |
930 | 0 | DestroyImage(extent_image); |
931 | 0 | return((Image *) NULL); |
932 | 0 | } |
933 | | |
934 | | /* |
935 | | Composite existing image at position using requested composition |
936 | | operator. |
937 | | */ |
938 | 0 | if ((CompositeImage(extent_image,image->compose,image,geometry->x, |
939 | 0 | geometry->y)) == MagickFail) |
940 | 0 | { |
941 | 0 | CopyException(exception,&extent_image->exception); |
942 | 0 | DestroyImage(extent_image); |
943 | 0 | return((Image *) NULL); |
944 | 0 | } |
945 | | |
946 | 0 | return(extent_image); |
947 | 0 | } |
948 | | |
949 | | /* |
950 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
951 | | % % |
952 | | % % |
953 | | % % |
954 | | % F l a t t e n I m a g e % |
955 | | % % |
956 | | % % |
957 | | % % |
958 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
959 | | % |
960 | | % Method FlattenImage merges a sequence of images. This is useful for |
961 | | % combining Photoshop layers into a single image. |
962 | | % |
963 | | % The format of the FlattenImage method is: |
964 | | % |
965 | | % Image *FlattenImage(const Image *image,ExceptionInfo *exception) |
966 | | % |
967 | | % A description of each parameter follows: |
968 | | % |
969 | | % o image: The image sequence. |
970 | | % |
971 | | % o exception: Return any errors or warnings in this structure. |
972 | | % |
973 | | */ |
974 | | MagickExport Image *FlattenImages(const Image *image,ExceptionInfo *exception) |
975 | 0 | { |
976 | 0 | Image |
977 | 0 | *flatten_image; |
978 | |
|
979 | 0 | register const Image |
980 | 0 | *next; |
981 | | |
982 | | /* |
983 | | Flatten the image sequence. |
984 | | */ |
985 | 0 | assert(image != (Image *) NULL); |
986 | 0 | assert(image->signature == MagickSignature); |
987 | 0 | assert(exception != (ExceptionInfo *) NULL); |
988 | 0 | assert(exception->signature == MagickSignature); |
989 | | |
990 | | /* |
991 | | Clone first image in sequence to serve as canvas image |
992 | | */ |
993 | 0 | flatten_image=CloneImage(image,0,0,True,exception); |
994 | | |
995 | | /* |
996 | | Apply background color under image if it has a matte channel. |
997 | | */ |
998 | 0 | if ((flatten_image != (Image *) NULL) && (flatten_image->matte)) |
999 | 0 | (void) MagickCompositeImageUnderColor(flatten_image, |
1000 | 0 | &flatten_image->background_color, |
1001 | 0 | exception); |
1002 | |
|
1003 | 0 | if ((flatten_image != (Image *) NULL) && |
1004 | 0 | (image->next != (Image *) NULL)) |
1005 | 0 | { |
1006 | | /* |
1007 | | Flatten remaining images onto canvas |
1008 | | */ |
1009 | 0 | for (next=image->next; next != (Image *) NULL; next=next->next) |
1010 | 0 | (void) CompositeImage(flatten_image,next->compose,next,next->page.x, |
1011 | 0 | next->page.y); |
1012 | 0 | } |
1013 | 0 | return(flatten_image); |
1014 | 0 | } |
1015 | | |
1016 | | /* |
1017 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1018 | | % % |
1019 | | % % |
1020 | | % % |
1021 | | % F l i p I m a g e % |
1022 | | % % |
1023 | | % % |
1024 | | % % |
1025 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1026 | | % |
1027 | | % FlipImage() creates a vertical mirror image by reflecting the pixels |
1028 | | % around the central x-axis. |
1029 | | % |
1030 | | % The format of the FlipImage method is: |
1031 | | % |
1032 | | % Image *FlipImage(const Image *image,ExceptionInfo *exception) |
1033 | | % |
1034 | | % A description of each parameter follows: |
1035 | | % |
1036 | | % o image: The image. |
1037 | | % |
1038 | | % o exception: Return any errors or warnings in this structure. |
1039 | | % |
1040 | | % |
1041 | | */ |
1042 | | MagickExport Image *FlipImage(const Image *image,ExceptionInfo *exception) |
1043 | 13.9k | { |
1044 | 13.9k | #define FlipImageText "[%s] Flip..." |
1045 | | |
1046 | 13.9k | Image |
1047 | 13.9k | *flip_image; |
1048 | | |
1049 | 13.9k | unsigned long |
1050 | 13.9k | row_count=0; |
1051 | | |
1052 | 13.9k | MagickBool |
1053 | 13.9k | monitor_active; |
1054 | | |
1055 | 13.9k | long |
1056 | 13.9k | y; |
1057 | | |
1058 | 13.9k | MagickPassFail |
1059 | 13.9k | status=MagickPass; |
1060 | | |
1061 | | /* |
1062 | | Initialize flip image attributes. |
1063 | | */ |
1064 | 13.9k | assert(image != (const Image *) NULL); |
1065 | 13.9k | assert(image->signature == MagickSignature); |
1066 | 13.9k | assert(exception != (ExceptionInfo *) NULL); |
1067 | 13.9k | assert(exception->signature == MagickSignature); |
1068 | | |
1069 | 13.9k | if ((image->columns == 0UL) || (image->rows == 0UL)) |
1070 | 0 | ThrowImageException(ImageError,UnableToResizeImage, |
1071 | 13.9k | MagickMsg(OptionError,NonzeroWidthAndHeightRequired)); |
1072 | | |
1073 | 13.9k | if (((((size_t) Max(sizeof(PixelPacket),sizeof(IndexPacket)))*image->columns)/ |
1074 | 13.9k | image->columns) != Max(sizeof(PixelPacket),sizeof(IndexPacket))) |
1075 | 13.9k | ThrowImageException(ImageError,WidthOrHeightExceedsLimit,image->filename); |
1076 | | |
1077 | 13.9k | flip_image=CloneImage(image,image->columns,image->rows,True,exception); |
1078 | 13.9k | if (flip_image == (Image *) NULL) |
1079 | 0 | return((Image *) NULL); |
1080 | | /* |
1081 | | Flip each row. |
1082 | | */ |
1083 | | |
1084 | 13.9k | monitor_active=MagickMonitorActive(); |
1085 | | |
1086 | | #if defined(HAVE_OPENMP) && !defined(DisableSlowOpenMP) |
1087 | | # if defined(TUNE_OPENMP) |
1088 | | # pragma omp parallel for schedule(runtime) shared(row_count, status) |
1089 | | # else |
1090 | | # pragma omp parallel for schedule(static,4) shared(row_count, status) |
1091 | | # endif |
1092 | | #endif |
1093 | 2.45M | for (y=0; y < (long) flip_image->rows; y++) |
1094 | 2.44M | { |
1095 | 2.44M | const PixelPacket |
1096 | 2.44M | *p; |
1097 | | |
1098 | 2.44M | const IndexPacket |
1099 | 2.44M | *indexes; |
1100 | | |
1101 | 2.44M | IndexPacket |
1102 | 2.44M | *flip_indexes; |
1103 | | |
1104 | 2.44M | PixelPacket |
1105 | 2.44M | *q; |
1106 | | |
1107 | 2.44M | MagickBool |
1108 | 2.44M | thread_status; |
1109 | | |
1110 | 2.44M | thread_status=status; |
1111 | 2.44M | if (thread_status == MagickFail) |
1112 | 0 | continue; |
1113 | | |
1114 | 2.44M | p=AcquireImagePixels(image,0,y,image->columns,1,exception); |
1115 | 2.44M | q=SetImagePixelsEx(flip_image,0,(long) (flip_image->rows-y-1), |
1116 | 2.44M | flip_image->columns,1,exception); |
1117 | 2.44M | if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL)) |
1118 | 3.66k | thread_status=MagickFail; |
1119 | | |
1120 | 2.44M | if (thread_status != MagickFail) |
1121 | 2.43M | { |
1122 | 2.43M | (void) memcpy(q,p,flip_image->columns*sizeof(PixelPacket)); |
1123 | 2.43M | indexes=AccessImmutableIndexes(image); |
1124 | 2.43M | flip_indexes=AccessMutableIndexes(flip_image); |
1125 | 2.43M | if ((indexes != (IndexPacket *) NULL) && |
1126 | 2.35M | (flip_indexes != (IndexPacket *) NULL)) |
1127 | 2.35M | (void) memcpy(flip_indexes,indexes,image->columns*sizeof(IndexPacket)); |
1128 | 2.43M | if (!SyncImagePixelsEx(flip_image,exception)) |
1129 | 0 | thread_status=MagickFail; |
1130 | 2.43M | } |
1131 | | #if defined(HAVE_OPENMP) && !defined(DisableSlowOpenMP) |
1132 | | # pragma omp atomic |
1133 | | #endif |
1134 | 2.44M | row_count++; |
1135 | | |
1136 | 2.44M | if (monitor_active) |
1137 | 0 | { |
1138 | 0 | unsigned long |
1139 | 0 | thread_row_count; |
1140 | |
|
1141 | | #if defined(HAVE_OPENMP) |
1142 | | # pragma omp flush (row_count) |
1143 | | #endif |
1144 | 0 | thread_row_count=row_count; |
1145 | 0 | if (QuantumTick(thread_row_count,flip_image->rows)) |
1146 | 0 | if (!MagickMonitorFormatted(thread_row_count,flip_image->rows,exception, |
1147 | 0 | FlipImageText,image->filename)) |
1148 | 0 | thread_status=MagickFail; |
1149 | 0 | } |
1150 | | |
1151 | 2.44M | if (thread_status == MagickFail) |
1152 | 3.66k | { |
1153 | 3.66k | status=MagickFail; |
1154 | | #if defined(HAVE_OPENMP) && !defined(DisableSlowOpenMP) |
1155 | | # pragma omp flush (status) |
1156 | | #endif |
1157 | 3.66k | } |
1158 | 2.44M | } |
1159 | 13.9k | if (row_count < flip_image->rows) |
1160 | 0 | { |
1161 | 0 | DestroyImage(flip_image); |
1162 | 0 | return((Image *) NULL); |
1163 | 0 | } |
1164 | 13.9k | flip_image->is_grayscale=image->is_grayscale; |
1165 | 13.9k | return(flip_image); |
1166 | 13.9k | } |
1167 | | |
1168 | | /* |
1169 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1170 | | % % |
1171 | | % % |
1172 | | % % |
1173 | | % F l o p I m a g e % |
1174 | | % % |
1175 | | % % |
1176 | | % % |
1177 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1178 | | % |
1179 | | % FlopImage() creates a horizontal mirror image by reflecting the pixels |
1180 | | % around the central y-axis. |
1181 | | % |
1182 | | % The format of the FlopImage method is: |
1183 | | % |
1184 | | % Image *FlopImage(const Image *image,ExceptionInfo *exception) |
1185 | | % |
1186 | | % A description of each parameter follows: |
1187 | | % |
1188 | | % o image: The image. |
1189 | | % |
1190 | | % o exception: Return any errors or warnings in this structure. |
1191 | | % |
1192 | | % |
1193 | | */ |
1194 | | MagickExport Image *FlopImage(const Image *image,ExceptionInfo *exception) |
1195 | 121 | { |
1196 | 121 | #define FlopImageText "[%s] Flop..." |
1197 | | |
1198 | 121 | Image |
1199 | 121 | *flop_image; |
1200 | | |
1201 | 121 | unsigned long |
1202 | 121 | row_count=0; |
1203 | | |
1204 | 121 | MagickBool |
1205 | 121 | monitor_active; |
1206 | | |
1207 | 121 | long |
1208 | 121 | y; |
1209 | | |
1210 | 121 | MagickPassFail |
1211 | 121 | status=MagickPass; |
1212 | | |
1213 | | /* |
1214 | | Initialize flop image attributes. |
1215 | | */ |
1216 | 121 | assert(image != (const Image *) NULL); |
1217 | 121 | assert(image->signature == MagickSignature); |
1218 | 121 | assert(exception != (ExceptionInfo *) NULL); |
1219 | 121 | assert(exception->signature == MagickSignature); |
1220 | 121 | flop_image=CloneImage(image,image->columns,image->rows,True,exception); |
1221 | 121 | if (flop_image == (Image *) NULL) |
1222 | 0 | return((Image *) NULL); |
1223 | | /* |
1224 | | Flop each row. |
1225 | | */ |
1226 | | |
1227 | 121 | monitor_active=MagickMonitorActive(); |
1228 | | |
1229 | | #if defined(HAVE_OPENMP) && !defined(DisableSlowOpenMP) |
1230 | | # if defined(TUNE_OPENMP) |
1231 | | # pragma omp parallel for schedule(runtime) shared(row_count, status) |
1232 | | # else |
1233 | | # pragma omp parallel for schedule(static,4) shared(row_count, status) |
1234 | | # endif |
1235 | | #endif |
1236 | 3.06k | for (y=0; y < (long) flop_image->rows; y++) |
1237 | 2.94k | { |
1238 | 2.94k | register const IndexPacket |
1239 | 2.94k | *indexes; |
1240 | | |
1241 | 2.94k | register IndexPacket |
1242 | 2.94k | *flop_indexes; |
1243 | | |
1244 | 2.94k | register const PixelPacket |
1245 | 2.94k | *p; |
1246 | | |
1247 | 2.94k | register long |
1248 | 2.94k | x; |
1249 | | |
1250 | 2.94k | register PixelPacket |
1251 | 2.94k | *q; |
1252 | | |
1253 | 2.94k | MagickBool |
1254 | 2.94k | thread_status; |
1255 | | |
1256 | 2.94k | thread_status=status; |
1257 | 2.94k | if (thread_status == MagickFail) |
1258 | 0 | continue; |
1259 | | |
1260 | 2.94k | p=AcquireImagePixels(image,0,y,image->columns,1,exception); |
1261 | 2.94k | q=SetImagePixelsEx(flop_image,0,y,flop_image->columns,1,exception); |
1262 | 2.94k | if ((p == (PixelPacket *) NULL) || (q == (PixelPacket *) NULL)) |
1263 | 0 | thread_status=MagickFail; |
1264 | | |
1265 | 2.94k | if (thread_status != MagickFail) |
1266 | 2.94k | { |
1267 | 2.94k | indexes=AccessImmutableIndexes(image); |
1268 | 2.94k | flop_indexes=AccessMutableIndexes(flop_image); |
1269 | 2.94k | q+=flop_image->columns; |
1270 | 412k | for (x=0; x < (long) flop_image->columns; x++) |
1271 | 409k | { |
1272 | 409k | if ((indexes != (const IndexPacket *) NULL) && |
1273 | 104k | (flop_indexes != (IndexPacket *) NULL)) |
1274 | 104k | flop_indexes[flop_image->columns-x-1]=indexes[x]; |
1275 | 409k | q--; |
1276 | 409k | *q=(*p); |
1277 | 409k | p++; |
1278 | 409k | } |
1279 | 2.94k | if (!SyncImagePixelsEx(flop_image,exception)) |
1280 | 0 | thread_status=MagickFail; |
1281 | 2.94k | } |
1282 | | #if defined(HAVE_OPENMP) && !defined(DisableSlowOpenMP) |
1283 | | # pragma omp atomic |
1284 | | #endif |
1285 | 2.94k | row_count++; |
1286 | | |
1287 | 2.94k | if (monitor_active) |
1288 | 0 | { |
1289 | 0 | unsigned long |
1290 | 0 | thread_row_count; |
1291 | |
|
1292 | | #if defined(HAVE_OPENMP) |
1293 | | # pragma omp flush (row_count) |
1294 | | #endif |
1295 | 0 | thread_row_count=row_count; |
1296 | 0 | if (QuantumTick(thread_row_count,flop_image->rows)) |
1297 | 0 | if (!MagickMonitorFormatted(thread_row_count,flop_image->rows,exception, |
1298 | 0 | FlopImageText,image->filename)) |
1299 | 0 | thread_status=MagickFail; |
1300 | 0 | } |
1301 | | |
1302 | 2.94k | if (thread_status == MagickFail) |
1303 | 0 | { |
1304 | 0 | status=MagickFail; |
1305 | | #if defined(HAVE_OPENMP) && !defined(DisableSlowOpenMP) |
1306 | | # pragma omp flush (status) |
1307 | | #endif |
1308 | 0 | } |
1309 | 2.94k | } |
1310 | 121 | if (row_count < flop_image->rows) |
1311 | 0 | { |
1312 | 0 | DestroyImage(flop_image); |
1313 | 0 | return((Image *) NULL); |
1314 | 0 | } |
1315 | 121 | flop_image->is_grayscale=image->is_grayscale; |
1316 | 121 | return(flop_image); |
1317 | 121 | } |
1318 | | |
1319 | | /* |
1320 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1321 | | % % |
1322 | | % % |
1323 | | % % |
1324 | | + G e t I m a g e M o s a i c D i m e n s i o n s % |
1325 | | % % |
1326 | | % % |
1327 | | % % |
1328 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1329 | | % |
1330 | | % Method GetImageMosaicDimensions returns the bounding region of the canvas |
1331 | | % which supports the images in the list as would be returned by |
1332 | | % MosaicImages(). The bounding region is computed based on the image size |
1333 | | % and page offsets of each image in the image list. |
1334 | | % |
1335 | | % The format of the GetImageMosaicDimensions method is: |
1336 | | % |
1337 | | % RectangleInfo GetImageMosaicDimensions(const Image *image) |
1338 | | % |
1339 | | % A description of each parameter follows: |
1340 | | % |
1341 | | % o bounds: Method GetImageMosaicDimensions returns the bounding box of |
1342 | | % the image canvas. |
1343 | | % |
1344 | | % o image: The image. |
1345 | | % |
1346 | | % o exception: Return any errors or warnings in this structure. |
1347 | | % |
1348 | | % |
1349 | | */ |
1350 | | |
1351 | | static RectangleInfo GetImageMosaicDimensions(const Image *image) MAGICK_FUNC_PURE; |
1352 | | |
1353 | | static RectangleInfo GetImageMosaicDimensions(const Image *image) |
1354 | 0 | { |
1355 | 0 | RectangleInfo |
1356 | 0 | page; |
1357 | |
|
1358 | 0 | register const Image |
1359 | 0 | *next; |
1360 | |
|
1361 | 0 | page.width=image->columns; |
1362 | 0 | page.height=image->rows; |
1363 | 0 | page.x=0; |
1364 | 0 | page.y=0; |
1365 | 0 | for (next=image; next != (Image *) NULL; next=next->next) |
1366 | 0 | { |
1367 | 0 | page.x=next->page.x; |
1368 | 0 | page.y=next->page.y; |
1369 | | /* |
1370 | | Without casts, unsigned underflow can occur here if page offset |
1371 | | is negative and has greater magnitude than image size. |
1372 | | */ |
1373 | 0 | if (((long) next->columns+page.x) > (long) page.width) |
1374 | 0 | page.width=(long) next->columns+page.x; |
1375 | 0 | if (next->page.width > page.width) |
1376 | 0 | page.width=next->page.width; |
1377 | 0 | if (((long) next->rows+page.y) > (long) page.height) |
1378 | 0 | page.height=(long) next->rows+page.y; |
1379 | 0 | if (next->page.height > page.height) |
1380 | 0 | page.height=next->page.height; |
1381 | 0 | } |
1382 | |
|
1383 | 0 | return page; |
1384 | 0 | } |
1385 | | |
1386 | | /* |
1387 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1388 | | % % |
1389 | | % % |
1390 | | % % |
1391 | | % M o s a i c I m a g e s % |
1392 | | % % |
1393 | | % % |
1394 | | % % |
1395 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1396 | | % |
1397 | | % MosaicImages() inlays an image sequence to form a single coherent picture. |
1398 | | % It returns a single image with each image in the sequence composited at |
1399 | | % the location defined by the page member of the image structure. |
1400 | | % |
1401 | | % The format of the MosaicImage method is: |
1402 | | % |
1403 | | % Image *MosaicImages(const Image *image,ExceptionInfo *exception) |
1404 | | % |
1405 | | % A description of each parameter follows: |
1406 | | % |
1407 | | % o image: The image. |
1408 | | % |
1409 | | % o exception: Return any errors or warnings in this structure. |
1410 | | % |
1411 | | % |
1412 | | */ |
1413 | | MagickExport Image *MosaicImages(const Image *image,ExceptionInfo *exception) |
1414 | 0 | { |
1415 | 0 | #define MosaicImageText "[%s] Create mosaic..." |
1416 | |
|
1417 | 0 | Image |
1418 | 0 | *mosaic_image; |
1419 | |
|
1420 | 0 | RectangleInfo |
1421 | 0 | page; |
1422 | |
|
1423 | 0 | register const Image |
1424 | 0 | *next; |
1425 | |
|
1426 | 0 | unsigned int |
1427 | 0 | scene; |
1428 | |
|
1429 | 0 | MagickBool |
1430 | 0 | matte; |
1431 | |
|
1432 | 0 | MagickPassFail |
1433 | 0 | status; |
1434 | |
|
1435 | 0 | size_t |
1436 | 0 | image_list_length; |
1437 | |
|
1438 | 0 | assert(image != (Image *) NULL); |
1439 | 0 | assert(image->signature == MagickSignature); |
1440 | 0 | assert(exception != (ExceptionInfo *) NULL); |
1441 | 0 | assert(exception->signature == MagickSignature); |
1442 | |
|
1443 | 0 | image_list_length=GetImageListLength(image); |
1444 | | |
1445 | | /* |
1446 | | Determine mosaic bounding box. |
1447 | | */ |
1448 | 0 | page=GetImageMosaicDimensions(image); |
1449 | | |
1450 | | /* |
1451 | | Allocate canvas image. |
1452 | | */ |
1453 | 0 | mosaic_image=AllocateImage((ImageInfo *) NULL); |
1454 | 0 | if (mosaic_image == (Image *) NULL) |
1455 | 0 | return((Image *) NULL); |
1456 | 0 | mosaic_image->columns=page.width; |
1457 | 0 | mosaic_image->rows=page.height; |
1458 | | |
1459 | | /* |
1460 | | Canvas image supports transparency if any subordinate image uses |
1461 | | transparency. |
1462 | | */ |
1463 | 0 | matte=MagickTrue; |
1464 | 0 | for (next=image; next != (Image *) NULL; next=next->next) |
1465 | 0 | matte &= next->matte; |
1466 | 0 | mosaic_image->matte=matte; |
1467 | | |
1468 | | /* |
1469 | | Canvas color is copied from background color of first image in |
1470 | | list. Default canvas color is 'white' but opaque 'black' or |
1471 | | 'transparent' is often best for composition. |
1472 | | */ |
1473 | 0 | mosaic_image->background_color=image->background_color; |
1474 | 0 | (void) SetImage(mosaic_image,OpaqueOpacity); |
1475 | | |
1476 | | /* |
1477 | | Composite mosaic. |
1478 | | */ |
1479 | 0 | scene=0; |
1480 | 0 | for (next=image; next != (Image *) NULL; next=next->next) |
1481 | 0 | { |
1482 | 0 | (void) CompositeImage(mosaic_image,next->compose,next,next->page.x, |
1483 | 0 | next->page.y); |
1484 | 0 | status=MagickMonitorFormatted(scene++,image_list_length, |
1485 | 0 | exception,MosaicImageText,image->filename); |
1486 | 0 | if (status == MagickFail) |
1487 | 0 | break; |
1488 | 0 | } |
1489 | 0 | return(mosaic_image); |
1490 | 0 | } |
1491 | | |
1492 | | /* |
1493 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1494 | | % % |
1495 | | % % |
1496 | | % % |
1497 | | % R o l l I m a g e % |
1498 | | % % |
1499 | | % % |
1500 | | % % |
1501 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1502 | | % |
1503 | | % RollImage() offsets an image as defined by x_offset and y_offset. |
1504 | | % |
1505 | | % The format of the RollImage method is: |
1506 | | % |
1507 | | % Image *RollImage(const Image *image,const long x_offset, |
1508 | | % const long y_offset,ExceptionInfo *exception) |
1509 | | % |
1510 | | % A description of each parameter follows: |
1511 | | % |
1512 | | % o image: The image. |
1513 | | % |
1514 | | % o x_offset: The number of columns to roll in the horizontal direction. |
1515 | | % |
1516 | | % o y_offset: The number of rows to roll in the vertical direction. |
1517 | | % |
1518 | | % o exception: Return any errors or warnings in this structure. |
1519 | | % |
1520 | | % |
1521 | | */ |
1522 | | MagickExport Image *RollImage(const Image *image,const long x_offset, |
1523 | | const long y_offset,ExceptionInfo *exception) |
1524 | 0 | { |
1525 | 0 | Image |
1526 | 0 | *roll_image; |
1527 | |
|
1528 | 0 | RectangleInfo |
1529 | 0 | offset; |
1530 | | |
1531 | | /* |
1532 | | Initialize roll image attributes. |
1533 | | */ |
1534 | 0 | assert(image != (const Image *) NULL); |
1535 | 0 | assert(image->signature == MagickSignature); |
1536 | 0 | assert(image->columns != 0); |
1537 | 0 | assert(image->rows != 0); |
1538 | 0 | assert(exception != (ExceptionInfo *) NULL); |
1539 | 0 | assert(exception->signature == MagickSignature); |
1540 | 0 | roll_image=CloneImage(image,image->columns,image->rows,True,exception); |
1541 | 0 | if (roll_image == (Image *) NULL) |
1542 | 0 | return((Image *) NULL); |
1543 | | /* |
1544 | | Roll image. |
1545 | | */ |
1546 | 0 | offset.x=x_offset; |
1547 | 0 | offset.y=y_offset; |
1548 | 0 | while (offset.x < 0) |
1549 | 0 | offset.x+=image->columns; |
1550 | 0 | while (offset.x >= (long) image->columns) |
1551 | 0 | offset.x-=image->columns; |
1552 | 0 | while (offset.y < 0) |
1553 | 0 | offset.y+=image->rows; |
1554 | 0 | while (offset.y >= (long) image->rows) |
1555 | 0 | offset.y-=image->rows; |
1556 | | |
1557 | | /* Top left quadrant */ |
1558 | 0 | (void) CompositeImageRegion(CopyCompositeOp,0,offset.x,offset.y,image, |
1559 | 0 | image->columns-offset.x,image->rows-offset.y, |
1560 | 0 | roll_image,0,0,exception); |
1561 | | |
1562 | | /* Top right quadrant */ |
1563 | 0 | (void) CompositeImageRegion(CopyCompositeOp,0,image->columns-offset.x,offset.y,image, |
1564 | 0 | 0,image->rows-offset.y, |
1565 | 0 | roll_image,offset.x,0,exception); |
1566 | | |
1567 | | /* Bottom left quadrant */ |
1568 | 0 | (void) CompositeImageRegion(CopyCompositeOp,0,offset.x,image->rows-offset.y,image, |
1569 | 0 | image->columns-offset.x,0, |
1570 | 0 | roll_image,0,offset.y,exception); |
1571 | | |
1572 | | /* Bottom right quadrant */ |
1573 | 0 | (void) CompositeImageRegion(CopyCompositeOp,0,image->columns-offset.x,image->rows-offset.y,image, |
1574 | 0 | 0,0, |
1575 | 0 | roll_image,offset.x,offset.y,exception); |
1576 | |
|
1577 | 0 | roll_image->is_grayscale=image->is_grayscale; |
1578 | 0 | return(roll_image); |
1579 | 0 | } |
1580 | | |
1581 | | /* |
1582 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1583 | | % % |
1584 | | % % |
1585 | | % % |
1586 | | % S h a v e I m a g e % |
1587 | | % % |
1588 | | % % |
1589 | | % % |
1590 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1591 | | % |
1592 | | % Method ShaveImage shaves pixels from the image edges. It allocates the |
1593 | | % memory necessary for the new Image structure and returns a pointer to the |
1594 | | % new image. |
1595 | | % |
1596 | | % The format of the ShaveImage method is: |
1597 | | % |
1598 | | % Image *ShaveImage(const Image *image,const RectangleInfo *shave_info, |
1599 | | % ExceptionInfo *exception) |
1600 | | % |
1601 | | % A description of each parameter follows: |
1602 | | % |
1603 | | % o shave_image: Method ShaveImage returns a pointer to the shaved |
1604 | | % image. A null image is returned if there is a memory shortage or |
1605 | | % if the image width or height is zero. |
1606 | | % |
1607 | | % o image: The image. |
1608 | | % |
1609 | | % o shave_info: Specifies a pointer to a RectangleInfo which defines the |
1610 | | % region of the image to shave. |
1611 | | % |
1612 | | % o exception: Return any errors or warnings in this structure. |
1613 | | % |
1614 | | % |
1615 | | */ |
1616 | | MagickExport Image *ShaveImage(const Image *image, |
1617 | | const RectangleInfo *shave_info,ExceptionInfo *exception) |
1618 | 0 | { |
1619 | 0 | RectangleInfo |
1620 | 0 | geometry; |
1621 | |
|
1622 | 0 | if (((2*shave_info->width) >= image->columns) || |
1623 | 0 | ((2*shave_info->height) >= image->rows)) |
1624 | 0 | ThrowImageException(OptionError,GeometryDoesNotContainImage, |
1625 | 0 | MagickMsg(ResourceLimitError,UnableToShaveImage)); |
1626 | 0 | SetGeometry(image,&geometry); |
1627 | 0 | geometry.width-=2*shave_info->width; |
1628 | 0 | geometry.height-=2*shave_info->height; |
1629 | 0 | geometry.x=(long) shave_info->width; |
1630 | 0 | geometry.y=(long) shave_info->height; |
1631 | 0 | return(CropImage(image,&geometry,exception)); |
1632 | 0 | } |
1633 | | |
1634 | | /* |
1635 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1636 | | % % |
1637 | | % % |
1638 | | % % |
1639 | | % T r a n s f o r m I m a g e % |
1640 | | % % |
1641 | | % % |
1642 | | % % |
1643 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1644 | | % |
1645 | | % TransformImage() is a convenience method that behaves like ResizeImage() or |
1646 | | % CropImage() but accepts scaling and/or cropping information as a region |
1647 | | % geometry specification. If the operation fails, the original image handle |
1648 | | % is returned. |
1649 | | % |
1650 | | % The format of the TransformImage method is: |
1651 | | % |
1652 | | % MagickPassFail TransformImage(Image **image,const char *crop_geometry, |
1653 | | % const char *image_geometry) |
1654 | | % |
1655 | | % A description of each parameter follows: |
1656 | | % |
1657 | | % o image: The image to transform. The resulting transformed image is |
1658 | | % returned via this same parameter. |
1659 | | % |
1660 | | % o crop_geometry: An optional crop geometry string in the form |
1661 | | % <width>x<height>{+-}<x>{+-}<y>{%}. This geometry defines a subregion |
1662 | | % of the image to optionally crop. If crop_geometry is non-NULL, then if |
1663 | | % the x and y offsets are present, a single image is generated, consisting |
1664 | | % of the pixels from the cropping region, otherwise a list of image tiles |
1665 | | % of the specified geometry, covering the entire input image is generated. |
1666 | | % In this case, the rightmost tiles and the bottom tiles are smaller if |
1667 | | % the specified geometry extends beyond the dimensions of the input image. |
1668 | | % |
1669 | | % o image_geometry: An optional image geometry string in the form |
1670 | | % <width>x<height>. This geometry defines the final size of the image, |
1671 | | % or image tiles. If non-NULL, the resulting image (or image tiles) |
1672 | | % are resized similar to ZoomImage(). |
1673 | | % |
1674 | | % |
1675 | | */ |
1676 | | MagickExport MagickPassFail TransformImage(Image **image,const char *crop_geometry, |
1677 | | const char *image_geometry) |
1678 | 31.1k | { |
1679 | 31.1k | Image |
1680 | 31.1k | *previous, |
1681 | 31.1k | *resize_image, |
1682 | 31.1k | *transform_image; |
1683 | | |
1684 | 31.1k | RectangleInfo |
1685 | 31.1k | geometry; |
1686 | | |
1687 | 31.1k | int |
1688 | 31.1k | flags; |
1689 | | |
1690 | 31.1k | MagickPassFail |
1691 | 31.1k | status = MagickPass; |
1692 | | |
1693 | 31.1k | MagickBool |
1694 | 31.1k | transform_logging; |
1695 | | |
1696 | 31.1k | assert(image != (Image **) NULL); |
1697 | 31.1k | assert((*image)->signature == MagickSignature); |
1698 | | |
1699 | 31.1k | transform_logging=IsEventLogged(TransformEvent); |
1700 | 31.1k | transform_image=(*image); |
1701 | 31.1k | if (crop_geometry != (const char *) NULL) |
1702 | 0 | { |
1703 | 0 | Image |
1704 | 0 | *crop_image; |
1705 | | |
1706 | | /* |
1707 | | Crop image to a user specified size. |
1708 | | */ |
1709 | 0 | crop_image=(Image *) NULL; |
1710 | 0 | flags=GetImageGeometry(transform_image,crop_geometry,False,&geometry); |
1711 | 0 | if (transform_logging) |
1712 | 0 | (void) LogMagickEvent(TransformEvent,GetMagickModule(), |
1713 | 0 | "Crop Geometry: %lux%lu%+ld%+ld", |
1714 | 0 | geometry.width, geometry.height, |
1715 | 0 | geometry.x, geometry.y); |
1716 | 0 | if ((geometry.width == 0) || (geometry.height == 0) || |
1717 | 0 | ((flags & XValue) != 0) || ((flags & YValue) != 0)) |
1718 | 0 | { |
1719 | 0 | crop_image=CropImage(transform_image,&geometry,&(*image)->exception); |
1720 | 0 | if (crop_image == (Image *) NULL) |
1721 | 0 | status = MagickFail; |
1722 | 0 | } |
1723 | 0 | else |
1724 | 0 | if ((transform_image->columns > geometry.width) || |
1725 | 0 | (transform_image->rows > geometry.height)) |
1726 | 0 | { |
1727 | 0 | Image |
1728 | 0 | *next; |
1729 | |
|
1730 | 0 | long |
1731 | 0 | x, |
1732 | 0 | y; |
1733 | |
|
1734 | 0 | unsigned long |
1735 | 0 | height, |
1736 | 0 | width; |
1737 | | |
1738 | | /* |
1739 | | Crop repeatedly to create uniform subimages. |
1740 | | */ |
1741 | 0 | width=geometry.width; |
1742 | 0 | height=geometry.height; |
1743 | 0 | next=(Image *) NULL; |
1744 | 0 | for (y=0; y < (long) transform_image->rows; y+=height) |
1745 | 0 | { |
1746 | 0 | for (x=0; x < (long) transform_image->columns; x+=width) |
1747 | 0 | { |
1748 | 0 | geometry.width=width; |
1749 | 0 | geometry.height=height; |
1750 | 0 | geometry.x=x; |
1751 | 0 | geometry.y=y; |
1752 | 0 | next=CropImage(transform_image,&geometry,&(*image)->exception); |
1753 | 0 | if (next == (Image *) NULL) |
1754 | 0 | break; |
1755 | 0 | if (crop_image == (Image *) NULL) |
1756 | 0 | crop_image=next; |
1757 | 0 | else |
1758 | 0 | { |
1759 | 0 | next->previous=crop_image; |
1760 | 0 | crop_image->next=next; |
1761 | 0 | crop_image=crop_image->next; |
1762 | 0 | } |
1763 | 0 | } |
1764 | 0 | if (next == (Image *) NULL) |
1765 | 0 | { |
1766 | 0 | status=MagickFail; |
1767 | 0 | break; |
1768 | 0 | } |
1769 | 0 | } |
1770 | 0 | } |
1771 | 0 | if (crop_image != (Image *) NULL) |
1772 | 0 | { |
1773 | 0 | previous=transform_image->previous; |
1774 | 0 | crop_image->next=transform_image->next; |
1775 | 0 | DestroyImage(transform_image); |
1776 | 0 | transform_image=(Image *) NULL; |
1777 | 0 | while (crop_image->previous != (Image *) NULL) |
1778 | 0 | crop_image=crop_image->previous; |
1779 | 0 | crop_image->previous=previous; |
1780 | 0 | transform_image=crop_image; |
1781 | 0 | } |
1782 | 0 | *image=transform_image; |
1783 | 0 | } |
1784 | 31.1k | if (image_geometry == (const char *) NULL) |
1785 | 0 | return status; |
1786 | | |
1787 | | /* |
1788 | | Scale image to a user specified size. |
1789 | | */ |
1790 | 31.1k | SetGeometry(transform_image,&geometry); |
1791 | 31.1k | flags=GetMagickGeometry(image_geometry,&geometry.x,&geometry.y, |
1792 | 31.1k | &geometry.width,&geometry.height); |
1793 | 31.1k | if (transform_logging) |
1794 | 0 | (void) LogMagickEvent(TransformEvent,GetMagickModule(), |
1795 | 0 | "Transform Geometry: %lux%lu%+ld%+ld", |
1796 | 0 | geometry.width, geometry.height, |
1797 | 0 | geometry.x, geometry.y); |
1798 | 31.1k | if ((transform_image->columns == geometry.width) && |
1799 | 5.03k | (transform_image->rows == geometry.height)) |
1800 | 4.94k | return status; |
1801 | | |
1802 | | /* |
1803 | | Resize image. |
1804 | | */ |
1805 | 26.2k | resize_image=ZoomImage(transform_image,geometry.width,geometry.height, |
1806 | 26.2k | &(*image)->exception); |
1807 | 26.2k | if (resize_image == (Image *) NULL) |
1808 | 628 | { |
1809 | 628 | status=MagickFail; |
1810 | 628 | return status; |
1811 | 628 | } |
1812 | | |
1813 | 25.5k | ReplaceImageInList(&transform_image,resize_image); |
1814 | 25.5k | *image=transform_image; |
1815 | 25.5k | return status; |
1816 | 26.2k | } |