/src/graphicsmagick/magick/analyze.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | % Copyright (C) 2003-2025 GraphicsMagick Group |
3 | | % Copyright (C) 2003 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 | | % GraphicsMagick Image Analysis Methods |
11 | | % |
12 | | */ |
13 | | |
14 | | /* |
15 | | Include declarations. |
16 | | */ |
17 | | #include "magick/studio.h" |
18 | | #include "magick/analyze.h" |
19 | | #include "magick/color.h" |
20 | | #include "magick/log.h" |
21 | | #include "magick/monitor.h" |
22 | | #include "magick/pixel_cache.h" |
23 | | #include "magick/pixel_iterator.h" |
24 | | #include "magick/utility.h" |
25 | | |
26 | | /* |
27 | | Constant declaration. |
28 | | */ |
29 | | |
30 | | /* |
31 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
32 | | % % |
33 | | % % |
34 | | % % |
35 | | + G e t I m a g e B o u n d i n g B o x % |
36 | | % % |
37 | | % % |
38 | | % % |
39 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
40 | | % |
41 | | % Method GetImageBoundingBox returns the bounding box of an image canvas. |
42 | | % If the image has an opacity channel then return a bounding box based |
43 | | % only on the opacity channel, otherwise return the bounding box of the |
44 | | % image based on the current image fuzz setting. |
45 | | % |
46 | | % The format of the GetImageBoundingBox method is: |
47 | | % |
48 | | % RectangleInfo GetImageBoundingBox(const Image *image, |
49 | | % ExceptionInfo *exception) |
50 | | % |
51 | | % A description of each parameter follows: |
52 | | % |
53 | | % o bounds: Method GetImageBoundingBox returns the bounding box of an |
54 | | % image canvas. |
55 | | % |
56 | | % o image: The image. |
57 | | % |
58 | | % o exception: Return any errors or warnings in this structure. |
59 | | % |
60 | | % |
61 | | */ |
62 | 0 | #define GetImageBoundingBoxText "[%s] Get bounding box..." |
63 | | MagickExport RectangleInfo GetImageBoundingBox(const Image *image, |
64 | | ExceptionInfo *exception) |
65 | 0 | { |
66 | 0 | MagickPassFail |
67 | 0 | status=MagickPass; |
68 | |
|
69 | 0 | long |
70 | 0 | y; |
71 | |
|
72 | 0 | unsigned long |
73 | 0 | row_count=0; |
74 | |
|
75 | 0 | MagickBool |
76 | 0 | monitor_active; |
77 | |
|
78 | 0 | PixelPacket |
79 | 0 | corners[3]; |
80 | |
|
81 | 0 | RectangleInfo |
82 | 0 | bounds; |
83 | |
|
84 | 0 | assert(image != (Image *) NULL); |
85 | 0 | assert(image->signature == MagickSignature); |
86 | | |
87 | 0 | monitor_active=MagickMonitorActive(); |
88 | 0 | bounds.width=0; |
89 | 0 | bounds.height=0; |
90 | 0 | bounds.x=(long) image->columns; |
91 | 0 | bounds.y=(long) image->rows; |
92 | |
|
93 | 0 | if ((image->columns == 0) || (image->rows == 0)) |
94 | 0 | return(bounds); |
95 | | |
96 | 0 | (void) AcquireOnePixelByReference(image,&corners[0],0,0,exception); |
97 | 0 | (void) AcquireOnePixelByReference(image,&corners[1],(long) image->columns-1,0,exception); |
98 | 0 | (void) AcquireOnePixelByReference(image,&corners[2],0,(long) image->rows-1,exception); |
99 | | #if defined(HAVE_OPENMP) |
100 | | # pragma omp parallel for schedule(static,4) shared(bounds, row_count, status) |
101 | | #endif |
102 | 0 | for (y=0; y < (long) image->rows; y++) |
103 | 0 | { |
104 | 0 | register const PixelPacket |
105 | 0 | * restrict p; |
106 | |
|
107 | 0 | register long |
108 | 0 | x; |
109 | |
|
110 | 0 | RectangleInfo |
111 | 0 | thread_bounds; |
112 | |
|
113 | 0 | MagickPassFail |
114 | 0 | thread_status; |
115 | |
|
116 | | #if defined(HAVE_OPENMP) |
117 | | # pragma omp critical (GM_GetImageBoundingBox) |
118 | | #endif |
119 | 0 | { |
120 | 0 | thread_status=status; |
121 | 0 | thread_bounds=bounds; |
122 | 0 | } |
123 | 0 | if (thread_status == MagickFail) |
124 | 0 | continue; |
125 | | |
126 | 0 | p=AcquireImagePixels(image,0,y,image->columns,1,exception); |
127 | 0 | if (p == (const PixelPacket *) NULL) |
128 | 0 | thread_status=MagickFail; |
129 | 0 | if (thread_status != MagickFail) |
130 | 0 | { |
131 | 0 | if ((image->matte) && |
132 | 0 | (corners[0].opacity != OpaqueOpacity) && |
133 | 0 | (corners[0].opacity == corners[1].opacity) && |
134 | 0 | (corners[1].opacity == corners[2].opacity)) |
135 | | /* |
136 | | Consider only the opacity channel. Not currently fuzzy |
137 | | so only applied for simple transparency. |
138 | | */ |
139 | 0 | for (x=0; x < (long) image->columns; x++) |
140 | 0 | { |
141 | 0 | if (p->opacity != corners[0].opacity) |
142 | 0 | if (x < thread_bounds.x) |
143 | 0 | thread_bounds.x=x; |
144 | 0 | if (p->opacity != corners[1].opacity) |
145 | 0 | if (x > (long) thread_bounds.width) |
146 | 0 | thread_bounds.width=x; |
147 | 0 | if (p->opacity != corners[0].opacity) |
148 | 0 | if (y < thread_bounds.y) |
149 | 0 | thread_bounds.y=y; |
150 | 0 | if (p->opacity != corners[2].opacity) |
151 | 0 | if (y > (long) thread_bounds.height) |
152 | 0 | thread_bounds.height=y; |
153 | 0 | p++; |
154 | 0 | } |
155 | 0 | else if (image->fuzz <= MagickEpsilon) |
156 | 0 | { |
157 | | /* |
158 | | Consider only the RGB channels using absolute comparison |
159 | | */ |
160 | 0 | for (x=0; x < (long) image->columns; x++) |
161 | 0 | { |
162 | 0 | if (!ColorMatch(p,&corners[0])) |
163 | 0 | if (x < thread_bounds.x) |
164 | 0 | thread_bounds.x=x; |
165 | 0 | if (!ColorMatch(p,&corners[1])) |
166 | 0 | if (x > (long) thread_bounds.width) |
167 | 0 | thread_bounds.width=x; |
168 | 0 | if (!ColorMatch(p,&corners[0])) |
169 | 0 | if (y < thread_bounds.y) |
170 | 0 | thread_bounds.y=y; |
171 | 0 | if (!ColorMatch(p,&corners[2])) |
172 | 0 | if (y > (long) thread_bounds.height) |
173 | 0 | thread_bounds.height=y; |
174 | 0 | p++; |
175 | 0 | } |
176 | 0 | } |
177 | 0 | else |
178 | | /* |
179 | | Consider only the RGB channels using fuzzy comparison |
180 | | */ |
181 | 0 | for (x=0; x < (long) image->columns; x++) |
182 | 0 | { |
183 | 0 | if (!FuzzyColorMatch(p,&corners[0],image->fuzz)) |
184 | 0 | if (x < thread_bounds.x) |
185 | 0 | thread_bounds.x=x; |
186 | 0 | if (!FuzzyColorMatch(p,&corners[1],image->fuzz)) |
187 | 0 | if (x > (long) thread_bounds.width) |
188 | 0 | thread_bounds.width=x; |
189 | 0 | if (!FuzzyColorMatch(p,&corners[0],image->fuzz)) |
190 | 0 | if (y < thread_bounds.y) |
191 | 0 | thread_bounds.y=y; |
192 | 0 | if (!FuzzyColorMatch(p,&corners[2],image->fuzz)) |
193 | 0 | if (y > (long) thread_bounds.height) |
194 | 0 | thread_bounds.height=y; |
195 | 0 | p++; |
196 | 0 | } |
197 | 0 | } |
198 | |
|
199 | 0 | if (monitor_active) |
200 | 0 | { |
201 | 0 | unsigned long |
202 | 0 | thread_row_count; |
203 | |
|
204 | | #if defined(HAVE_OPENMP) |
205 | | # pragma omp atomic |
206 | | #endif |
207 | 0 | row_count++; |
208 | | #if defined(HAVE_OPENMP) |
209 | | # pragma omp flush (row_count) |
210 | | #endif |
211 | 0 | thread_row_count=row_count; |
212 | 0 | if (QuantumTick(thread_row_count,image->rows)) |
213 | 0 | if (!MagickMonitorFormatted(thread_row_count,image->rows,exception, |
214 | 0 | GetImageBoundingBoxText,image->filename)) |
215 | 0 | thread_status=MagickFail; |
216 | 0 | } |
217 | |
|
218 | | #if defined(HAVE_OPENMP) |
219 | | # pragma omp critical (GM_GetImageBoundingBox) |
220 | | #endif |
221 | 0 | { |
222 | 0 | if (thread_bounds.x < bounds.x) |
223 | 0 | bounds.x=thread_bounds.x; |
224 | 0 | if (thread_bounds.y < bounds.y) |
225 | 0 | bounds.y=thread_bounds.y; |
226 | 0 | if (thread_bounds.width > bounds.width) |
227 | 0 | bounds.width=thread_bounds.width; |
228 | 0 | if (thread_bounds.height > bounds.height) |
229 | 0 | bounds.height=thread_bounds.height; |
230 | 0 | } |
231 | |
|
232 | 0 | if (thread_status == MagickFail) |
233 | 0 | { |
234 | 0 | status=MagickFail; |
235 | | #if defined(HAVE_OPENMP) |
236 | | # pragma omp flush (status) |
237 | | #endif |
238 | 0 | } |
239 | 0 | } |
240 | 0 | if (bounds.width != 0) |
241 | 0 | bounds.width-=(bounds.x-1); |
242 | 0 | if (bounds.height != 0) |
243 | 0 | bounds.height-=(bounds.y-1); |
244 | 0 | if (bounds.x < 0) |
245 | 0 | bounds.x=0; |
246 | 0 | if (bounds.y < 0) |
247 | 0 | bounds.y=0; |
248 | | /* |
249 | | If we fail to find smaller bounds, then return original image |
250 | | dimensions. |
251 | | */ |
252 | 0 | if ((bounds.width == 0) || (bounds.height == 0)) |
253 | 0 | { |
254 | 0 | bounds.width=image->columns; |
255 | 0 | bounds.height=image->rows; |
256 | 0 | bounds.x=0; |
257 | 0 | bounds.y=0; |
258 | 0 | } |
259 | 0 | if (IsEventLogged(TransformEvent)) |
260 | 0 | (void) LogMagickEvent(TransformEvent,GetMagickModule(), |
261 | 0 | "Bounding Box: %lux%lu%+ld%+ld", |
262 | 0 | bounds.width, bounds.height, bounds.x, bounds.y); |
263 | |
|
264 | 0 | return(bounds); |
265 | 0 | } |
266 | | |
267 | | /* |
268 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
269 | | % % |
270 | | % % |
271 | | % % |
272 | | % G e t I m a g e D e p t h % |
273 | | % % |
274 | | % % |
275 | | % % |
276 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
277 | | % |
278 | | % GetImageDepth() returns the minimum bit depth of the image required to |
279 | | % ensure that data is not lost in the red, green, blue, and opacity, channels. |
280 | | % Pixel components are stored in a Quantum, which is 8, 16, or 32 bits |
281 | | % depending on the QuantumDepth value set when the software is compiled. |
282 | | % GetImageDepth() returns the smallest modulus storage size which supports |
283 | | % the scale of the pixel within the range (i.e. no information is lost). |
284 | | % As an example, the value one is returned for a black and white image |
285 | | % since only one bit of resolution is required to represent a black and white |
286 | | % image. |
287 | | % |
288 | | % The format of the GetImageDepth method is: |
289 | | % |
290 | | % unsigned long GetImageDepth(const Image *image,ExceptionInfo *exception) |
291 | | % |
292 | | % A description of each parameter follows: |
293 | | % |
294 | | % o image: The image. |
295 | | % |
296 | | % o exception: Return any errors or warnings in this structure. |
297 | | % |
298 | | % |
299 | | */ |
300 | | #if MaxMap == MaxRGB |
301 | | static inline unsigned char MinimumDepthForValue(const Quantum quantum) |
302 | 0 | { |
303 | 0 | register unsigned int |
304 | 0 | depth, |
305 | 0 | scale; |
306 | |
|
307 | 0 | for (depth=1 ; depth <= (unsigned int) QuantumDepth; depth++) |
308 | 0 | { |
309 | 0 | scale=MaxRGB / (MaxRGB >> (QuantumDepth-depth)); |
310 | 0 | if (quantum == scale*(quantum/scale)) |
311 | 0 | break; |
312 | 0 | } |
313 | |
|
314 | 0 | return (unsigned char) depth; |
315 | 0 | } |
316 | | static magick_uint8_t* AllocateDepthMap(void) |
317 | 0 | { |
318 | 0 | magick_uint8_t |
319 | 0 | *map; |
320 | |
|
321 | 0 | map = MagickAllocateResourceLimitedArray(unsigned char *, MaxMap+1, sizeof(magick_uint8_t)); |
322 | 0 | if (map != (unsigned char *) NULL) |
323 | 0 | { |
324 | 0 | unsigned int |
325 | 0 | i; |
326 | |
|
327 | 0 | for (i=0; i <= MaxMap; i++) |
328 | 0 | map[i] = (magick_uint8_t) MinimumDepthForValue(i); |
329 | 0 | } |
330 | 0 | return map; |
331 | 0 | } |
332 | | #endif /* MaxMap == MaxRGB */ |
333 | 0 | #define GetImageDepthText "[%s] Get depth..." |
334 | | |
335 | | static MagickPassFail |
336 | | GetImageDepthCallBack(void *mutable_data, /* User provided mutable data */ |
337 | | const void *immutable_data, /* User provided immutable data */ |
338 | | const Image * restrict image, /* Input image */ |
339 | | const PixelPacket * restrict pixels, /* Pixel row */ |
340 | | const IndexPacket * restrict indexes, /* Pixel indexes */ |
341 | | const long npixels, /* Number of pixels in row */ |
342 | | ExceptionInfo * restrict exception /* Exception report */ |
343 | | ) |
344 | 0 | { |
345 | 0 | unsigned int |
346 | 0 | *current_depth=(unsigned int *) mutable_data; |
347 | |
|
348 | 0 | magick_uint8_t |
349 | 0 | *map = (magick_uint8_t *) immutable_data; |
350 | |
|
351 | 0 | register unsigned int |
352 | 0 | depth; |
353 | |
|
354 | 0 | register long |
355 | 0 | i; |
356 | |
|
357 | 0 | ARG_NOT_USED(indexes); |
358 | 0 | ARG_NOT_USED(exception); |
359 | |
|
360 | | #if defined(HAVE_OPENMP) |
361 | | # pragma omp critical (GM_GetImageDepthCallBack) |
362 | | #endif |
363 | 0 | { |
364 | 0 | depth=*current_depth; |
365 | 0 | } |
366 | |
|
367 | 0 | #if MaxMap == MaxRGB |
368 | 0 | if (map) |
369 | 0 | { |
370 | | /* |
371 | | Use fast table lookups if we can |
372 | | */ |
373 | 0 | for (i=0; i < npixels; i++) |
374 | 0 | { |
375 | 0 | depth=Max(depth,map[pixels[i].red]); |
376 | 0 | depth=Max(depth,map[pixels[i].green]); |
377 | 0 | depth=Max(depth,map[pixels[i].blue]); |
378 | 0 | if (image->matte) |
379 | 0 | depth=Max(depth,map[pixels[i].opacity]); |
380 | 0 | if (depth == QuantumDepth) |
381 | 0 | break; |
382 | 0 | } |
383 | 0 | } |
384 | | #else |
385 | | { |
386 | | /* |
387 | | Use the slow, sure, way (Q32 only) |
388 | | */ |
389 | | register unsigned int |
390 | | scale; |
391 | | |
392 | | ARG_NOT_USED(map); |
393 | | scale=MaxRGB / (MaxRGB >> (QuantumDepth-depth)); |
394 | | i=0; |
395 | | while (i < npixels) |
396 | | { |
397 | | if ((pixels[i].red != scale*(pixels[i].red/scale)) || |
398 | | (pixels[i].green != scale*(pixels[i].green/scale)) || |
399 | | (pixels[i].blue != scale*(pixels[i].blue/scale)) || |
400 | | (image->matte && |
401 | | (pixels[i].opacity != scale*((pixels[i].opacity/scale))))) |
402 | | { |
403 | | depth++; |
404 | | if (depth == QuantumDepth) |
405 | | break; |
406 | | scale=MaxRGB / (MaxRGB >> (QuantumDepth-depth)); |
407 | | continue; |
408 | | } |
409 | | i++; |
410 | | } |
411 | | } |
412 | | #endif |
413 | |
|
414 | | #if defined(HAVE_OPENMP) |
415 | | # pragma omp critical (GM_GetImageDepthCallBack) |
416 | | #endif |
417 | 0 | { |
418 | 0 | if (depth > *current_depth) |
419 | 0 | *current_depth=depth; |
420 | 0 | } |
421 | |
|
422 | 0 | return (depth >= QuantumDepth ? MagickFail : MagickPass); |
423 | 0 | } |
424 | | |
425 | | MagickExport unsigned long GetImageDepth(const Image *image, |
426 | | ExceptionInfo *exception) |
427 | 3.41k | { |
428 | 3.41k | magick_uint8_t |
429 | 3.41k | *map = (magick_uint8_t *) NULL; |
430 | | |
431 | 3.41k | unsigned int |
432 | 3.41k | depth=1; |
433 | | |
434 | 3.41k | assert(image != (Image *) NULL); |
435 | 3.41k | assert(image->signature == MagickSignature); |
436 | | |
437 | 3.41k | if (image->is_monochrome) |
438 | 3.41k | return depth; |
439 | | |
440 | 0 | #if MaxMap == MaxRGB |
441 | | /* |
442 | | Use fast table lookups if we can |
443 | | */ |
444 | 0 | map = AllocateDepthMap(); |
445 | 0 | #endif |
446 | 0 | if ((image->storage_class == PseudoClass) && !(image->matte)) |
447 | 0 | { |
448 | | /* |
449 | | PseudoClass |
450 | | */ |
451 | 0 | (void) GetImageDepthCallBack(&depth,map,image, |
452 | 0 | image->colormap, |
453 | 0 | (IndexPacket *) NULL, |
454 | 0 | image->colors, |
455 | 0 | exception); |
456 | 0 | } |
457 | 0 | else |
458 | 0 | { |
459 | | /* |
460 | | DirectClass. |
461 | | |
462 | | Notice that all pixels in the image must be inspected if the |
463 | | image depth is less than QuantumDepth. |
464 | | */ |
465 | |
|
466 | 0 | (void) PixelIterateMonoRead(GetImageDepthCallBack, |
467 | 0 | NULL, |
468 | 0 | GetImageDepthText, |
469 | 0 | &depth,map,0,0,image->columns, |
470 | 0 | image->rows,image,exception); |
471 | 0 | } |
472 | |
|
473 | 0 | MagickFreeResourceLimitedMemory(map); |
474 | |
|
475 | 0 | return depth; |
476 | 3.41k | } |
477 | | |
478 | | /* |
479 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
480 | | % % |
481 | | % % |
482 | | % % |
483 | | % G e t I m a g e C h a r a c t e r i s t i c s % |
484 | | % % |
485 | | % % |
486 | | % % |
487 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
488 | | % |
489 | | % GetImageCharacteristics obtains the basic characteristics of the image |
490 | | % and stores the characterisistics in the user provided |
491 | | % ImageCharacteristics structure. If optimize is set to MagickTrue, then |
492 | | % exhaustive testing of the image pixels is performed (as required). |
493 | | % MagickPass is returned if this method executes without error. |
494 | | % |
495 | | % The format of the GetImageCharacteristics method is: |
496 | | % |
497 | | % MagickPassFail GetImageCharacteristics(const Image *image, |
498 | | % ImageCharacteristics *characteristics, |
499 | | % MagickBool optimize, |
500 | | % ExceptionInfo *exception) |
501 | | % |
502 | | % A description of each parameter follows: |
503 | | % |
504 | | % o image: The image. |
505 | | % |
506 | | % o characteristics: An ImageCharacteristics structure to update. |
507 | | % |
508 | | % o optimize: Inspect image pixels (if required) |
509 | | % |
510 | | % o exception: Any errors are reported here. |
511 | | % |
512 | | */ |
513 | 530 | #define AnalyzeImageText "[%s] Analyze... " |
514 | | MagickExport MagickPassFail GetImageCharacteristics(const Image *image, |
515 | | ImageCharacteristics *characteristics, |
516 | | const MagickBool optimize, |
517 | | ExceptionInfo *exception) |
518 | 84.4k | { |
519 | 84.4k | unsigned long |
520 | 84.4k | y; |
521 | | |
522 | 84.4k | register const PixelPacket |
523 | 84.4k | *p; |
524 | | |
525 | 84.4k | register unsigned long |
526 | 84.4k | x; |
527 | | |
528 | 84.4k | MagickBool |
529 | 84.4k | broke_loop = MagickFalse; |
530 | | |
531 | 84.4k | MagickPassFail |
532 | 84.4k | status = MagickPass; |
533 | | |
534 | 84.4k | assert(image != (Image *) NULL); |
535 | 84.4k | assert(image->signature == MagickSignature); |
536 | 84.4k | assert(characteristics != (ImageCharacteristics *) NULL); |
537 | 84.4k | assert(exception != (ExceptionInfo *) NULL); |
538 | | |
539 | 84.4k | characteristics->cmyk = (image->colorspace == CMYKColorspace ? MagickTrue : MagickFalse); |
540 | 84.4k | characteristics->grayscale = (image->is_grayscale ? MagickTrue : MagickFalse); |
541 | 84.4k | characteristics->monochrome = (image->is_monochrome ? MagickTrue : MagickFalse); |
542 | 84.4k | characteristics->opaque = (image->matte ? MagickFalse : MagickTrue); |
543 | 84.4k | characteristics->palette = (image->storage_class == PseudoClass ? MagickTrue : MagickFalse); |
544 | | |
545 | 84.4k | if ((optimize) && (GetPixelCachePresent(image))) |
546 | 530 | { |
547 | 530 | MagickBool |
548 | 530 | grayscale, |
549 | 530 | monochrome, |
550 | 530 | opaque; |
551 | | |
552 | | /* Predicate to test */ |
553 | 530 | grayscale=(image->is_grayscale ? MagickFalse : MagickTrue); |
554 | 530 | monochrome=(image->is_monochrome ? MagickFalse : MagickTrue); |
555 | 530 | opaque=(image->matte ? MagickTrue : MagickFalse); |
556 | 530 | switch (image->storage_class) |
557 | 530 | { |
558 | 0 | case DirectClass: |
559 | 0 | case UndefinedClass: |
560 | 0 | { |
561 | 0 | for (y=0; y < image->rows; y++) |
562 | 0 | { |
563 | 0 | p=AcquireImagePixels(image,0,y,image->columns,1,exception); |
564 | 0 | if (p == (const PixelPacket *) NULL) |
565 | 0 | { |
566 | 0 | status = MagickFail; |
567 | 0 | break; |
568 | 0 | } |
569 | 0 | for (x=image->columns; x != 0; x--) |
570 | 0 | { |
571 | 0 | grayscale = ((grayscale) && |
572 | 0 | (p->red == p->green) && (p->red == p->blue)); |
573 | 0 | monochrome = ((monochrome) && (grayscale) && |
574 | 0 | ((0 == p->red) || (MaxRGB == p->red))); |
575 | 0 | opaque = ((opaque) && |
576 | 0 | (p->opacity == OpaqueOpacity)); |
577 | 0 | if (!grayscale && |
578 | 0 | !monochrome && |
579 | 0 | !opaque) |
580 | 0 | { |
581 | 0 | broke_loop=MagickTrue; |
582 | 0 | break; |
583 | 0 | } |
584 | 0 | p++; |
585 | 0 | } |
586 | 0 | if (!grayscale && |
587 | 0 | !monochrome && |
588 | 0 | !opaque) |
589 | 0 | break; |
590 | 0 | if (QuantumTick(y,image->rows)) |
591 | 0 | if (!MagickMonitorFormatted(y,image->rows,exception, |
592 | 0 | AnalyzeImageText,image->filename)) |
593 | 0 | break; |
594 | 0 | } |
595 | 0 | break; |
596 | 0 | } |
597 | 530 | case PseudoClass: |
598 | 530 | { |
599 | 530 | p=image->colormap; |
600 | 530 | for (x=image->colors; x != 0; x--) |
601 | 530 | { |
602 | 530 | grayscale = ((grayscale) && |
603 | 530 | (p->red == p->green) && (p->red == p->blue)); |
604 | 530 | monochrome = ((monochrome) && (grayscale) && |
605 | 530 | ((0 == p->red) || (MaxRGB == p->red))); |
606 | 530 | if (!grayscale && |
607 | 530 | !monochrome) |
608 | 530 | { |
609 | 530 | broke_loop=MagickTrue; |
610 | 530 | break; |
611 | 530 | } |
612 | 0 | p++; |
613 | 0 | } |
614 | 530 | if (opaque) |
615 | 0 | { |
616 | 0 | for (y=0; y < image->rows; y++) |
617 | 0 | { |
618 | 0 | p=AcquireImagePixels(image,0,y,image->columns,1,exception); |
619 | 0 | if (p == (const PixelPacket *) NULL) |
620 | 0 | { |
621 | 0 | status = MagickFail; |
622 | 0 | break; |
623 | 0 | } |
624 | 0 | for (x=image->columns; x != 0; x--) |
625 | 0 | { |
626 | 0 | opaque = ((opaque) && |
627 | 0 | (p->opacity == OpaqueOpacity)); |
628 | 0 | if (!opaque) |
629 | 0 | { |
630 | 0 | broke_loop=MagickTrue; |
631 | 0 | break; |
632 | 0 | } |
633 | 0 | p++; |
634 | 0 | } |
635 | 0 | if (!opaque) |
636 | 0 | break; |
637 | 0 | if (QuantumTick(y,image->rows)) |
638 | 0 | if (!MagickMonitorFormatted(y,image->rows,exception, |
639 | 0 | AnalyzeImageText,image->filename)) |
640 | 0 | break; |
641 | 0 | } |
642 | 0 | } |
643 | 530 | break; |
644 | 0 | } |
645 | 530 | } |
646 | 530 | if (!characteristics->grayscale) |
647 | 0 | { |
648 | 0 | characteristics->grayscale=grayscale; |
649 | 0 | ((Image *)image)->is_grayscale=grayscale; /* Intentionally ignore const */ |
650 | 0 | } |
651 | 530 | if (!characteristics->monochrome) |
652 | 413 | { |
653 | 413 | characteristics->monochrome=monochrome; |
654 | 413 | ((Image *)image)->is_monochrome=monochrome; /* Intentionally ignore const */ |
655 | 413 | } |
656 | 530 | if (!characteristics->opaque) |
657 | 0 | characteristics->opaque=opaque; |
658 | 530 | } |
659 | | |
660 | | /* |
661 | | Force progress indication to 100% |
662 | | */ |
663 | 84.4k | if (broke_loop) |
664 | 530 | (void) MagickMonitorFormatted(image->rows-1,image->rows,exception, |
665 | 530 | AnalyzeImageText,image->filename); |
666 | | /* printf("status=%s, cmyk=%u, grayscale=%u, monochrome=%u, opaque=%u, palette=%u\n", */ |
667 | | /* (status == MagickFail ? "Fail" : "Pass"),characteristics->cmyk,characteristics->grayscale, */ |
668 | | /* characteristics->monochrome,characteristics->opaque,characteristics->palette); */ |
669 | | |
670 | 84.4k | return status; |
671 | 84.4k | } |
672 | | |
673 | | /* |
674 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
675 | | % % |
676 | | % % |
677 | | % % |
678 | | % G e t I m a g e T y p e % |
679 | | % % |
680 | | % % |
681 | | % % |
682 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
683 | | % |
684 | | % GetImageType() returns the type of image: |
685 | | % |
686 | | % Bilevel Grayscale GrayscaleMatte |
687 | | % Palette PaletteMatte TrueColor |
688 | | % TrueColorMatte ColorSeparation ColorSeparationMatte |
689 | | % |
690 | | % |
691 | | % The format of the GetImageType method is: |
692 | | % |
693 | | % ImageType GetImageType(const Image *image,ExceptionInfo *exception) |
694 | | % |
695 | | % A description of each parameter follows: |
696 | | % |
697 | | % o image: The image. |
698 | | % |
699 | | % o exception: Return any errors or warnings in this structure. |
700 | | % |
701 | | % |
702 | | */ |
703 | | MagickExport ImageType |
704 | | GetImageType(const Image *image,ExceptionInfo *exception) |
705 | 530 | { |
706 | 530 | ImageCharacteristics |
707 | 530 | characteristics; |
708 | | |
709 | 530 | ImageType |
710 | 530 | image_type; |
711 | | |
712 | 530 | assert(image != (Image *) NULL); |
713 | 530 | assert(image->signature == MagickSignature); |
714 | | |
715 | 530 | image_type=UndefinedType; |
716 | 530 | if (GetImageCharacteristics(image,&characteristics,MagickTrue,exception)) |
717 | 530 | { |
718 | 530 | if (characteristics.cmyk) |
719 | 0 | image_type=(characteristics.opaque ? ColorSeparationType : ColorSeparationMatteType); |
720 | 530 | else if (characteristics.monochrome) |
721 | 117 | image_type=BilevelType; |
722 | 413 | else if (characteristics.grayscale) |
723 | 413 | image_type=(characteristics.opaque ? GrayscaleType : GrayscaleMatteType); |
724 | 0 | else if (characteristics.palette) |
725 | 0 | image_type=(characteristics.opaque ? PaletteType : PaletteMatteType); |
726 | 0 | else |
727 | 0 | image_type=(characteristics.opaque ? TrueColorType : TrueColorMatteType); |
728 | 530 | } |
729 | 530 | return image_type; |
730 | 530 | } |
731 | | |
732 | | /* |
733 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
734 | | % % |
735 | | % % |
736 | | % % |
737 | | % I s G r a y I m a g e % |
738 | | % % |
739 | | % % |
740 | | % % |
741 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
742 | | % |
743 | | % IsGrayImage() returns MagickTrue if all the pixels in the image have the same |
744 | | % red, green, and blue intensities. |
745 | | % |
746 | | % The format of the IsGrayImage method is: |
747 | | % |
748 | | % MagickBool IsGrayImage(const Image *image,ExceptionInfo *exception) |
749 | | % |
750 | | % A description of each parameter follows: |
751 | | % |
752 | | % o status: Method IsGrayImage returns MagickTrue if the image is grayscale |
753 | | % otherwise MagickFalse is returned. |
754 | | % |
755 | | % o image: The image. |
756 | | % |
757 | | % o exception: Return any errors or warnings in this structure. |
758 | | % |
759 | | % |
760 | | */ |
761 | 84.3k | #define AnalyzeGrayImageText "[%s] Analyze for gray..." |
762 | | MagickExport MagickBool IsGrayImage(const Image *image, |
763 | | ExceptionInfo *exception) |
764 | 122k | { |
765 | 122k | unsigned long |
766 | 122k | y; |
767 | | |
768 | 122k | register const PixelPacket |
769 | 122k | *p; |
770 | | |
771 | 122k | register unsigned long |
772 | 122k | x; |
773 | | |
774 | 122k | MagickBool |
775 | 122k | is_grayscale; |
776 | | |
777 | 122k | assert(image != (Image *) NULL); |
778 | 122k | assert(image->signature == MagickSignature); |
779 | 122k | assert(exception != (ExceptionInfo *) NULL); |
780 | 122k | if (image->colorspace == CMYKColorspace) |
781 | 17 | return(MagickFalse); |
782 | 122k | if (image->is_grayscale) |
783 | 36.2k | return(MagickTrue); |
784 | 86.1k | is_grayscale=MagickTrue; |
785 | 86.1k | switch (image->storage_class) |
786 | 86.1k | { |
787 | 26.2k | case DirectClass: |
788 | 26.2k | case UndefinedClass: |
789 | 26.2k | { |
790 | 26.2k | (void) LogMagickEvent(TransformEvent,GetMagickModule(), |
791 | 26.2k | "IsGrayImage(): Exhaustive pixel test!"); |
792 | 102k | for (y=0; y < image->rows; y++) |
793 | 90.5k | { |
794 | 90.5k | p=AcquireImagePixels(image,0,y,image->columns,1,exception); |
795 | 90.5k | if (p == (const PixelPacket *) NULL) |
796 | 1 | return(MagickFalse); |
797 | 7.05M | for (x=image->columns; x != 0; x--) |
798 | 6.97M | { |
799 | 6.97M | if ((p->red != p->green) || (p->green != p->blue)) |
800 | 14.0k | { |
801 | 14.0k | is_grayscale=MagickFalse; |
802 | 14.0k | break; |
803 | 14.0k | } |
804 | 6.96M | p++; |
805 | 6.96M | } |
806 | 90.5k | if (!is_grayscale) |
807 | 14.0k | break; |
808 | 76.5k | if (QuantumTick(y,image->rows)) |
809 | 35.1k | if (!MagickMonitorFormatted(y,image->rows, |
810 | 35.1k | exception,AnalyzeGrayImageText, |
811 | 35.1k | image->filename)) |
812 | 0 | break; |
813 | 76.5k | } |
814 | 26.2k | break; |
815 | 26.2k | } |
816 | 59.8k | case PseudoClass: |
817 | 59.8k | { |
818 | 59.8k | p=image->colormap; |
819 | 64.1M | for (x=image->colors; x != 0; x--) |
820 | 64.1M | { |
821 | 64.1M | if ((p->red != p->green) || (p->green != p->blue)) |
822 | 35.1k | { |
823 | 35.1k | is_grayscale=MagickFalse; |
824 | 35.1k | break; |
825 | 35.1k | } |
826 | 64.0M | p++; |
827 | 64.0M | } |
828 | 59.8k | break; |
829 | 26.2k | } |
830 | 86.1k | } |
831 | | |
832 | | /* |
833 | | Force progress indication to 100% |
834 | | */ |
835 | 86.1k | if (!is_grayscale) |
836 | 49.2k | (void) MagickMonitorFormatted(image->rows-1,image->rows,exception, |
837 | 49.2k | AnalyzeGrayImageText,image->filename); |
838 | | |
839 | 86.1k | ((Image *)image)->is_grayscale=is_grayscale; |
840 | 86.1k | return(is_grayscale); |
841 | 86.1k | } |
842 | | |
843 | | /* |
844 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
845 | | % % |
846 | | % % |
847 | | % % |
848 | | % I s M o n o c h r o m e I m a g e % |
849 | | % % |
850 | | % % |
851 | | % % |
852 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
853 | | % |
854 | | % IsMonochromeImage() returns MagickTrue if all the pixels in the image have |
855 | | % the same red, green, and blue intensities and the intensity is either |
856 | | % 0 or MaxRGB. |
857 | | % |
858 | | % The format of the IsMonochromeImage method is: |
859 | | % |
860 | | % MagickBool IsMonochromeImage(const Image *image, |
861 | | % ExceptionInfo *exception) |
862 | | % |
863 | | % A description of each parameter follows: |
864 | | % |
865 | | % o image: The image. |
866 | | % |
867 | | % o exception: Return any errors or warnings in this structure. |
868 | | % |
869 | | % |
870 | | */ |
871 | 54.8k | #define AnalyzeBilevelImageText "[%s] Analyze for bilevel..." |
872 | | MagickExport MagickBool IsMonochromeImage(const Image *image, |
873 | | ExceptionInfo *exception) |
874 | 93.7k | { |
875 | 93.7k | unsigned long |
876 | 93.7k | y; |
877 | | |
878 | 93.7k | register const PixelPacket |
879 | 93.7k | *p; |
880 | | |
881 | 93.7k | register unsigned long |
882 | 93.7k | x; |
883 | | |
884 | 93.7k | MagickBool |
885 | 93.7k | is_monochrome; |
886 | | |
887 | 93.7k | assert(image != (Image *) NULL); |
888 | 93.7k | assert(image->signature == MagickSignature); |
889 | 93.7k | assert(exception != (ExceptionInfo *) NULL); |
890 | 93.7k | if (image->colorspace == CMYKColorspace) |
891 | 349 | return(MagickFalse); |
892 | 93.3k | if (image->is_monochrome) |
893 | 17.5k | return(MagickTrue); |
894 | 75.8k | is_monochrome=MagickTrue; |
895 | 75.8k | switch (image->storage_class) |
896 | 75.8k | { |
897 | 0 | case DirectClass: |
898 | 0 | case UndefinedClass: |
899 | 0 | { |
900 | 0 | (void) LogMagickEvent(TransformEvent,GetMagickModule(), |
901 | 0 | "IsMonochromeImage(): Exhaustive pixel test!"); |
902 | 0 | for (y=0; y < image->rows; y++) |
903 | 0 | { |
904 | 0 | p=AcquireImagePixels(image,0,y,image->columns,1,exception); |
905 | 0 | if (p == (const PixelPacket *) NULL) |
906 | 0 | return(MagickFalse); |
907 | 0 | for (x=image->columns; x != 0; x--) |
908 | 0 | { |
909 | 0 | if ((p->red != p->green) || (p->green != p->blue) || |
910 | 0 | ((p->red != 0) && (p->red != MaxRGB))) |
911 | 0 | { |
912 | 0 | is_monochrome=MagickFalse; |
913 | 0 | break; |
914 | 0 | } |
915 | 0 | p++; |
916 | 0 | } |
917 | 0 | if (!is_monochrome) |
918 | 0 | break; |
919 | 0 | if (QuantumTick(y,image->rows)) |
920 | 0 | if (!MagickMonitorFormatted(y,image->rows,exception, |
921 | 0 | AnalyzeBilevelImageText,image->filename)) |
922 | 0 | break; |
923 | 0 | } |
924 | 0 | break; |
925 | 0 | } |
926 | 75.8k | case PseudoClass: |
927 | 75.8k | { |
928 | 75.8k | p=image->colormap; |
929 | 482k | for (x=image->colors; x != 0; x--) |
930 | 461k | { |
931 | 461k | if ((p->red != p->green) || (p->green != p->blue) || |
932 | 461k | ((p->red != 0) && (p->red != MaxRGB))) |
933 | 54.8k | { |
934 | 54.8k | is_monochrome=MagickFalse; |
935 | 54.8k | break; |
936 | 54.8k | } |
937 | 407k | p++; |
938 | 407k | } |
939 | 75.8k | break; |
940 | 0 | } |
941 | 75.8k | } |
942 | | |
943 | | /* |
944 | | Force progress indication to 100% |
945 | | */ |
946 | 75.8k | if (!is_monochrome) |
947 | 54.8k | (void) MagickMonitorFormatted(image->rows-1,image->rows,exception, |
948 | 54.8k | AnalyzeBilevelImageText,image->filename); |
949 | | |
950 | 75.8k | ((Image *)image)->is_monochrome=is_monochrome; |
951 | 75.8k | return(is_monochrome); |
952 | 75.8k | } |
953 | | |
954 | | /* |
955 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
956 | | % % |
957 | | % % |
958 | | % % |
959 | | % I s O p a q u e I m a g e % |
960 | | % % |
961 | | % % |
962 | | % % |
963 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
964 | | % |
965 | | % IsOpaqueImage() returns MagickTrue if none of the pixels in the image have an |
966 | | % opacity value other than opaque (0). |
967 | | % |
968 | | % The format of the IsOpaqueImage method is: |
969 | | % |
970 | | % MagickBool IsOpaqueImage(const Image *image,ExceptionInfo *exception) |
971 | | % |
972 | | % A description of each parameter follows: |
973 | | % |
974 | | % o status: Method IsOpaqueImage returns MagickFalse if the image has one or more |
975 | | % pixels that are transparent otherwise MagickTrue is returned. |
976 | | % |
977 | | % o image: The image. |
978 | | % |
979 | | % o exception: Return any errors or warnings in this structure. |
980 | | % |
981 | | % |
982 | | */ |
983 | 0 | #define AnalyzeOpaqueImageText "[%s] Analyze for opacity..." |
984 | | MagickExport MagickBool IsOpaqueImage(const Image *image, |
985 | | ExceptionInfo *exception) |
986 | 0 | { |
987 | 0 | unsigned long |
988 | 0 | y; |
989 | |
|
990 | 0 | register const PixelPacket |
991 | 0 | *p; |
992 | |
|
993 | 0 | register unsigned long |
994 | 0 | x; |
995 | |
|
996 | 0 | MagickBool |
997 | 0 | is_opaque; |
998 | | |
999 | | /* |
1000 | | Determine if image is opaque. |
1001 | | */ |
1002 | 0 | assert(image != (Image *) NULL); |
1003 | 0 | assert(image->signature == MagickSignature); |
1004 | 0 | if (!image->matte) |
1005 | 0 | return(MagickTrue); |
1006 | 0 | is_opaque=MagickTrue; |
1007 | 0 | (void) LogMagickEvent(TransformEvent,GetMagickModule(), |
1008 | 0 | "IsOpaqueImage(): Exhaustive pixel test!"); |
1009 | 0 | for (y=0; y < image->rows; y++) |
1010 | 0 | { |
1011 | 0 | p=AcquireImagePixels(image,0,y,image->columns,1,exception); |
1012 | 0 | if (p == (const PixelPacket *) NULL) |
1013 | 0 | return(MagickFalse); |
1014 | 0 | for (x=image->columns; x > 0; x--) |
1015 | 0 | { |
1016 | 0 | if (p->opacity != OpaqueOpacity) |
1017 | 0 | { |
1018 | 0 | is_opaque=MagickFalse; |
1019 | 0 | break; |
1020 | 0 | } |
1021 | 0 | p++; |
1022 | 0 | } |
1023 | 0 | if (!is_opaque) |
1024 | 0 | break; |
1025 | 0 | if (QuantumTick(y,image->rows)) |
1026 | 0 | if (!MagickMonitorFormatted(y,image->rows,exception, |
1027 | 0 | AnalyzeOpaqueImageText,image->filename)) |
1028 | 0 | break; |
1029 | 0 | } |
1030 | | |
1031 | | /* |
1032 | | Force progress indication to 100% |
1033 | | */ |
1034 | 0 | if (!is_opaque) |
1035 | 0 | (void) MagickMonitorFormatted(image->rows-1,image->rows,exception, |
1036 | 0 | AnalyzeOpaqueImageText,image->filename); |
1037 | |
|
1038 | 0 | return(is_opaque); |
1039 | 0 | } |