blob: bd85ee4e1f587d4732044e81e418bcf888f4e98b [file] [log] [blame]
cristyda06ed12009-10-14 18:36:54 +00001/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% AAA TTTTT TTTTT RRRR IIIII BBBB U U TTTTT EEEEE %
7% A A T T R R I B B U U T E %
8% AAAAA T T RRRR I BBBB U U T EEE %
9% A A T T R R I B B U U T E %
10% A A T T R R IIIII BBBB UUU T EEEEE %
11% %
12% %
13% MagickCore Get / Set Image Attributes %
14% %
15% Software Design %
16% John Cristy %
17% October 2002 %
18% %
19% %
cristy16af1cb2009-12-11 21:38:29 +000020% Copyright 1999-2010 ImageMagick Studio LLC, a non-profit organization %
cristyda06ed12009-10-14 18:36:54 +000021% dedicated to making software imaging solutions freely available. %
22% %
23% You may not use this file except in compliance with the License. You may %
24% obtain a copy of the License at %
25% %
26% http://www.imagemagick.org/script/license.php %
27% %
28% Unless required by applicable law or agreed to in writing, software %
29% distributed under the License is distributed on an "AS IS" BASIS, %
30% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31% See the License for the specific language governing permissions and %
32% limitations under the License. %
33% %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36%
37%
38*/
39
40/*
41 Include declarations.
42*/
43#include "magick/studio.h"
44#include "magick/attribute.h"
45#include "magick/blob.h"
46#include "magick/blob-private.h"
47#include "magick/cache.h"
48#include "magick/cache-view.h"
49#include "magick/client.h"
50#include "magick/color.h"
51#include "magick/color-private.h"
52#include "magick/colormap.h"
53#include "magick/colormap-private.h"
54#include "magick/colorspace.h"
55#include "magick/composite.h"
56#include "magick/composite-private.h"
57#include "magick/constitute.h"
58#include "magick/deprecate.h"
59#include "magick/draw.h"
60#include "magick/draw-private.h"
61#include "magick/effect.h"
62#include "magick/enhance.h"
63#include "magick/exception.h"
64#include "magick/exception-private.h"
65#include "magick/geometry.h"
66#include "magick/histogram.h"
67#include "magick/identify.h"
68#include "magick/image.h"
69#include "magick/image-private.h"
70#include "magick/list.h"
71#include "magick/log.h"
72#include "magick/memory_.h"
73#include "magick/magick.h"
74#include "magick/monitor.h"
75#include "magick/monitor-private.h"
76#include "magick/paint.h"
77#include "magick/pixel.h"
78#include "magick/pixel-private.h"
79#include "magick/property.h"
80#include "magick/quantize.h"
81#include "magick/random_.h"
82#include "magick/resource_.h"
83#include "magick/semaphore.h"
84#include "magick/segment.h"
85#include "magick/splay-tree.h"
86#include "magick/string_.h"
87#include "magick/thread-private.h"
88#include "magick/threshold.h"
89#include "magick/transform.h"
90#include "magick/utility.h"
91
92/*
93%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
94% %
95% %
96% %
97+ G e t I m a g e B o u n d i n g B o x %
98% %
99% %
100% %
101%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
102%
103% GetImageBoundingBox() returns the bounding box of an image canvas.
104%
105% The format of the GetImageBoundingBox method is:
106%
107% RectangleInfo GetImageBoundingBox(const Image *image,
108% ExceptionInfo *exception)
109%
110% A description of each parameter follows:
111%
112% o bounds: Method GetImageBoundingBox returns the bounding box of an
113% image canvas.
114%
115% o image: the image.
116%
117% o exception: return any errors or warnings in this structure.
118%
119*/
120MagickExport RectangleInfo GetImageBoundingBox(const Image *image,
121 ExceptionInfo *exception)
122{
cristyc4c8d132010-01-07 01:58:38 +0000123 CacheView
124 *image_view;
125
cristybb503372010-05-27 20:51:26 +0000126 ssize_t
cristyda06ed12009-10-14 18:36:54 +0000127 y;
128
129 MagickBooleanType
130 status;
131
132 MagickPixelPacket
133 target[3],
134 zero;
135
136 RectangleInfo
137 bounds;
138
139 register const PixelPacket
140 *p;
141
cristyda06ed12009-10-14 18:36:54 +0000142 assert(image != (Image *) NULL);
143 assert(image->signature == MagickSignature);
144 if (image->debug != MagickFalse)
145 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
146 bounds.width=0;
147 bounds.height=0;
cristybb503372010-05-27 20:51:26 +0000148 bounds.x=(ssize_t) image->columns;
149 bounds.y=(ssize_t) image->rows;
cristyda06ed12009-10-14 18:36:54 +0000150 GetMagickPixelPacket(image,&target[0]);
151 image_view=AcquireCacheView(image);
152 p=GetCacheViewVirtualPixels(image_view,0,0,1,1,exception);
153 if (p == (const PixelPacket *) NULL)
154 {
155 image_view=DestroyCacheView(image_view);
156 return(bounds);
157 }
158 SetMagickPixelPacket(image,p,GetCacheViewAuthenticIndexQueue(image_view),
159 &target[0]);
160 GetMagickPixelPacket(image,&target[1]);
cristybb503372010-05-27 20:51:26 +0000161 p=GetCacheViewVirtualPixels(image_view,(ssize_t) image->columns-1,0,1,1,
cristyda06ed12009-10-14 18:36:54 +0000162 exception);
163 SetMagickPixelPacket(image,p,GetCacheViewAuthenticIndexQueue(image_view),
164 &target[1]);
165 GetMagickPixelPacket(image,&target[2]);
cristybb503372010-05-27 20:51:26 +0000166 p=GetCacheViewVirtualPixels(image_view,0,(ssize_t) image->rows-1,1,1,exception);
cristyda06ed12009-10-14 18:36:54 +0000167 SetMagickPixelPacket(image,p,GetCacheViewAuthenticIndexQueue(image_view),
168 &target[2]);
169 status=MagickTrue;
170 GetMagickPixelPacket(image,&zero);
cristyb5d5f722009-11-04 03:03:49 +0000171#if defined(MAGICKCORE_OPENMP_SUPPORT)
172 #pragma omp parallel for schedule(dynamic,4) shared(status)
cristyda06ed12009-10-14 18:36:54 +0000173#endif
cristybb503372010-05-27 20:51:26 +0000174 for (y=0; y < (ssize_t) image->rows; y++)
cristyda06ed12009-10-14 18:36:54 +0000175 {
176 MagickPixelPacket
177 pixel;
178
179 RectangleInfo
180 bounding_box;
181
182 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000183 *restrict indexes;
cristyda06ed12009-10-14 18:36:54 +0000184
185 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000186 *restrict p;
cristyda06ed12009-10-14 18:36:54 +0000187
cristybb503372010-05-27 20:51:26 +0000188 register ssize_t
cristyda06ed12009-10-14 18:36:54 +0000189 x;
190
191 if (status == MagickFalse)
192 continue;
cristyb5d5f722009-11-04 03:03:49 +0000193#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyda06ed12009-10-14 18:36:54 +0000194# pragma omp critical (MagickCore_GetImageBoundingBox)
195#endif
196 bounding_box=bounds;
197 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
198 if (p == (const PixelPacket *) NULL)
199 {
200 status=MagickFalse;
201 continue;
202 }
203 indexes=GetCacheViewVirtualIndexQueue(image_view);
204 pixel=zero;
cristybb503372010-05-27 20:51:26 +0000205 for (x=0; x < (ssize_t) image->columns; x++)
cristyda06ed12009-10-14 18:36:54 +0000206 {
207 SetMagickPixelPacket(image,p,indexes+x,&pixel);
208 if ((x < bounding_box.x) &&
209 (IsMagickColorSimilar(&pixel,&target[0]) == MagickFalse))
210 bounding_box.x=x;
cristybb503372010-05-27 20:51:26 +0000211 if ((x > (ssize_t) bounding_box.width) &&
cristyda06ed12009-10-14 18:36:54 +0000212 (IsMagickColorSimilar(&pixel,&target[1]) == MagickFalse))
cristybb503372010-05-27 20:51:26 +0000213 bounding_box.width=(size_t) x;
cristyda06ed12009-10-14 18:36:54 +0000214 if ((y < bounding_box.y) &&
215 (IsMagickColorSimilar(&pixel,&target[0]) == MagickFalse))
216 bounding_box.y=y;
cristybb503372010-05-27 20:51:26 +0000217 if ((y > (ssize_t) bounding_box.height) &&
cristyda06ed12009-10-14 18:36:54 +0000218 (IsMagickColorSimilar(&pixel,&target[2]) == MagickFalse))
cristybb503372010-05-27 20:51:26 +0000219 bounding_box.height=(size_t) y;
cristyda06ed12009-10-14 18:36:54 +0000220 p++;
221 }
cristyb5d5f722009-11-04 03:03:49 +0000222#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyda06ed12009-10-14 18:36:54 +0000223# pragma omp critical (MagickCore_GetImageBoundingBox)
224#endif
225 {
226 if (bounding_box.x < bounds.x)
227 bounds.x=bounding_box.x;
228 if (bounding_box.y < bounds.y)
229 bounds.y=bounding_box.y;
230 if (bounding_box.width > bounds.width)
231 bounds.width=bounding_box.width;
232 if (bounding_box.height > bounds.height)
233 bounds.height=bounding_box.height;
234 }
235 }
236 image_view=DestroyCacheView(image_view);
237 if ((bounds.width == 0) || (bounds.height == 0))
238 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
239 "GeometryDoesNotContainImage","`%s'",image->filename);
240 else
241 {
242 bounds.width-=(bounds.x-1);
243 bounds.height-=(bounds.y-1);
244 }
245 return(bounds);
246}
247
248/*
249%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
250% %
251% %
252% %
253% G e t I m a g e C h a n n e l D e p t h %
254% %
255% %
256% %
257%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
258%
259% GetImageChannelDepth() returns the depth of a particular image channel.
260%
261% The format of the GetImageChannelDepth method is:
262%
cristybb503372010-05-27 20:51:26 +0000263% size_t GetImageDepth(const Image *image,ExceptionInfo *exception)
264% size_t GetImageChannelDepth(const Image *image,
cristyda06ed12009-10-14 18:36:54 +0000265% const ChannelType channel,ExceptionInfo *exception)
266%
267% A description of each parameter follows:
268%
269% o image: the image.
270%
271% o channel: the channel.
272%
273% o exception: return any errors or warnings in this structure.
274%
275*/
276
cristybb503372010-05-27 20:51:26 +0000277MagickExport size_t GetImageDepth(const Image *image,
cristyda06ed12009-10-14 18:36:54 +0000278 ExceptionInfo *exception)
279{
280 return(GetImageChannelDepth(image,AllChannels,exception));
281}
282
cristybb503372010-05-27 20:51:26 +0000283MagickExport size_t GetImageChannelDepth(const Image *image,
cristyda06ed12009-10-14 18:36:54 +0000284 const ChannelType channel,ExceptionInfo *exception)
285{
cristyc4c8d132010-01-07 01:58:38 +0000286 CacheView
287 *image_view;
288
cristybb503372010-05-27 20:51:26 +0000289 ssize_t
cristyda06ed12009-10-14 18:36:54 +0000290 y;
291
292 MagickBooleanType
293 status;
294
cristybb503372010-05-27 20:51:26 +0000295 register ssize_t
cristyda06ed12009-10-14 18:36:54 +0000296 id;
297
cristybb503372010-05-27 20:51:26 +0000298 size_t
cristyda06ed12009-10-14 18:36:54 +0000299 *current_depth,
300 depth,
301 number_threads;
302
cristyda06ed12009-10-14 18:36:54 +0000303 /*
304 Compute image depth.
305 */
306 assert(image != (Image *) NULL);
307 assert(image->signature == MagickSignature);
308 if (image->debug != MagickFalse)
309 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
310 number_threads=GetOpenMPMaximumThreads();
cristybb503372010-05-27 20:51:26 +0000311 current_depth=(size_t *) AcquireQuantumMemory(number_threads,
cristyda06ed12009-10-14 18:36:54 +0000312 sizeof(*current_depth));
cristybb503372010-05-27 20:51:26 +0000313 if (current_depth == (size_t *) NULL)
cristyda06ed12009-10-14 18:36:54 +0000314 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
315 status=MagickTrue;
cristybb503372010-05-27 20:51:26 +0000316 for (id=0; id < (ssize_t) number_threads; id++)
cristyda06ed12009-10-14 18:36:54 +0000317 current_depth[id]=1;
318 if ((image->storage_class == PseudoClass) && (image->matte == MagickFalse))
319 {
320 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000321 *restrict p;
cristyda06ed12009-10-14 18:36:54 +0000322
cristybb503372010-05-27 20:51:26 +0000323 register ssize_t
cristyda06ed12009-10-14 18:36:54 +0000324 i;
325
326 p=image->colormap;
cristyb5d5f722009-11-04 03:03:49 +0000327#if defined(MAGICKCORE_OPENMP_SUPPORT)
328 #pragma omp parallel for schedule(dynamic,4) shared(status)
cristyda06ed12009-10-14 18:36:54 +0000329#endif
cristybb503372010-05-27 20:51:26 +0000330 for (i=0; i < (ssize_t) image->colors; i++)
cristyda06ed12009-10-14 18:36:54 +0000331 {
332 if (status == MagickFalse)
333 continue;
334 id=GetOpenMPThreadId();
335 while (current_depth[id] < MAGICKCORE_QUANTUM_DEPTH)
336 {
337 MagickStatusType
338 status;
339
340 QuantumAny
341 range;
342
343 status=0;
344 range=GetQuantumRange(current_depth[id]);
345 if ((channel & RedChannel) != 0)
346 status|=p->red != ScaleAnyToQuantum(ScaleQuantumToAny(p->red,
347 range),range);
348 if ((channel & GreenChannel) != 0)
349 status|=p->green != ScaleAnyToQuantum(ScaleQuantumToAny(p->green,
350 range),range);
351 if ((channel & BlueChannel) != 0)
352 status|=p->blue != ScaleAnyToQuantum(ScaleQuantumToAny(p->blue,
353 range),range);
354 if (status == 0)
355 break;
356 current_depth[id]++;
357 }
358 p++;
359 }
360 depth=current_depth[0];
cristybb503372010-05-27 20:51:26 +0000361 for (id=1; id < (ssize_t) number_threads; id++)
cristyda06ed12009-10-14 18:36:54 +0000362 if (depth < current_depth[id])
363 depth=current_depth[id];
cristybb503372010-05-27 20:51:26 +0000364 current_depth=(size_t *) RelinquishMagickMemory(current_depth);
cristyda06ed12009-10-14 18:36:54 +0000365 return(depth);
366 }
367 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +0000368#if defined(MAGICKCORE_OPENMP_SUPPORT)
369 #pragma omp parallel for schedule(dynamic,4) shared(status)
cristyda06ed12009-10-14 18:36:54 +0000370#endif
cristybb503372010-05-27 20:51:26 +0000371 for (y=0; y < (ssize_t) image->rows; y++)
cristyda06ed12009-10-14 18:36:54 +0000372 {
373 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000374 *restrict indexes;
cristyda06ed12009-10-14 18:36:54 +0000375
376 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000377 *restrict p;
cristyda06ed12009-10-14 18:36:54 +0000378
cristybb503372010-05-27 20:51:26 +0000379 register ssize_t
cristyda06ed12009-10-14 18:36:54 +0000380 id,
381 x;
382
383 if (status == MagickFalse)
384 continue;
385 id=GetOpenMPThreadId();
386 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
387 if (p == (const PixelPacket *) NULL)
388 continue;
389 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +0000390 for (x=0; x < (ssize_t) image->columns; x++)
cristyda06ed12009-10-14 18:36:54 +0000391 {
392 while (current_depth[id] < MAGICKCORE_QUANTUM_DEPTH)
393 {
394 MagickStatusType
395 status;
396
397 QuantumAny
398 range;
399
400 status=0;
401 range=GetQuantumRange(current_depth[id]);
402 if ((channel & RedChannel) != 0)
403 status|=p->red != ScaleAnyToQuantum(ScaleQuantumToAny(p->red,range),
404 range);
405 if ((channel & GreenChannel) != 0)
406 status|=p->green != ScaleAnyToQuantum(ScaleQuantumToAny(p->green,
407 range),range);
408 if ((channel & BlueChannel) != 0)
409 status|=p->blue != ScaleAnyToQuantum(ScaleQuantumToAny(p->blue,range),
410 range);
411 if (((channel & OpacityChannel) != 0) && (image->matte != MagickFalse))
412 status|=p->opacity != ScaleAnyToQuantum(ScaleQuantumToAny(p->opacity,
413 range),range);
414 if (((channel & IndexChannel) != 0) &&
415 (image->colorspace == CMYKColorspace))
416 status|=indexes[x] != ScaleAnyToQuantum(ScaleQuantumToAny(indexes[x],
417 range),range);
418 if (status == 0)
419 break;
420 current_depth[id]++;
421 }
422 p++;
423 }
424 if (current_depth[id] == MAGICKCORE_QUANTUM_DEPTH)
425 status=MagickFalse;
426 }
427 image_view=DestroyCacheView(image_view);
428 depth=current_depth[0];
cristybb503372010-05-27 20:51:26 +0000429 for (id=1; id < (ssize_t) number_threads; id++)
cristyda06ed12009-10-14 18:36:54 +0000430 if (depth < current_depth[id])
431 depth=current_depth[id];
cristybb503372010-05-27 20:51:26 +0000432 current_depth=(size_t *) RelinquishMagickMemory(current_depth);
cristyda06ed12009-10-14 18:36:54 +0000433 return(depth);
434}
435
436/*
437%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
438% %
439% %
440% %
441% G e t I m a g e Q u a n t u m D e p t h %
442% %
443% %
444% %
445%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
446%
447% GetImageQuantumDepth() returns the depth of the image rounded to a legal
448% quantum depth: 8, 16, or 32.
449%
450% The format of the GetImageQuantumDepth method is:
451%
cristybb503372010-05-27 20:51:26 +0000452% size_t GetImageQuantumDepth(const Image *image,
cristyda06ed12009-10-14 18:36:54 +0000453% const MagickBooleanType constrain)
454%
455% A description of each parameter follows:
456%
457% o image: the image.
458%
459% o constrain: A value other than MagickFalse, constrains the depth to
460% a maximum of MAGICKCORE_QUANTUM_DEPTH.
461%
462*/
463
464static inline double MagickMin(const double x,const double y)
465{
466 if (x < y)
467 return(x);
468 return(y);
469}
470
cristybb503372010-05-27 20:51:26 +0000471MagickExport size_t GetImageQuantumDepth(const Image *image,
cristyda06ed12009-10-14 18:36:54 +0000472 const MagickBooleanType constrain)
473{
cristybb503372010-05-27 20:51:26 +0000474 size_t
cristyda06ed12009-10-14 18:36:54 +0000475 depth;
476
477 depth=image->depth;
478 if (depth <= 8)
479 depth=8;
480 else
481 if (depth <= 16)
482 depth=16;
483 else
484 if (depth <= 32)
485 depth=32;
486 else
487 if (depth <= 64)
488 depth=64;
489 if (constrain != MagickFalse)
cristybb503372010-05-27 20:51:26 +0000490 depth=(size_t) MagickMin((double) depth,(double)
cristyda06ed12009-10-14 18:36:54 +0000491 MAGICKCORE_QUANTUM_DEPTH);
492 return(depth);
493}
494
495/*
496%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
497% %
498% %
499% %
500% G e t I m a g e T y p e %
501% %
502% %
503% %
504%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
505%
506% GetImageType() returns the potential type of image:
507%
508% Bilevel Grayscale GrayscaleMatte
509% Palette PaletteMatte TrueColor
510% TrueColorMatte ColorSeparation ColorSeparationMatte
511%
512% To ensure the image type matches its potential, use SetImageType():
513%
514% (void) SetImageType(image,GetImageType(image));
515%
516% The format of the GetImageType method is:
517%
518% ImageType GetImageType(const Image *image,ExceptionInfo *exception)
519%
520% A description of each parameter follows:
521%
522% o image: the image.
523%
524% o exception: return any errors or warnings in this structure.
525%
526*/
527MagickExport ImageType GetImageType(const Image *image,ExceptionInfo *exception)
528{
529 assert(image != (Image *) NULL);
530 assert(image->signature == MagickSignature);
531 if (image->debug != MagickFalse)
532 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
533 if (image->colorspace == CMYKColorspace)
534 {
535 if (image->matte == MagickFalse)
536 return(ColorSeparationType);
537 return(ColorSeparationMatteType);
538 }
539 if (IsMonochromeImage(image,exception) != MagickFalse)
540 return(BilevelType);
541 if (IsGrayImage(image,exception) != MagickFalse)
542 {
543 if (image->matte != MagickFalse)
544 return(GrayscaleMatteType);
545 return(GrayscaleType);
546 }
547 if (IsPaletteImage(image,exception) != MagickFalse)
548 {
549 if (image->matte != MagickFalse)
550 return(PaletteMatteType);
551 return(PaletteType);
552 }
553 if (image->matte != MagickFalse)
554 return(TrueColorMatteType);
555 return(TrueColorType);
556}
557
558/*
559%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
560% %
561% %
562% %
563% I s G r a y I m a g e %
564% %
565% %
566% %
567%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
568%
569% IsGrayImage() returns MagickTrue if all the pixels in the image have the
570% same red, green, and blue intensities.
571%
572% The format of the IsGrayImage method is:
573%
574% MagickBooleanType IsGrayImage(const Image *image,
575% ExceptionInfo *exception)
576%
577% A description of each parameter follows:
578%
579% o image: the image.
580%
581% o exception: return any errors or warnings in this structure.
582%
583*/
584MagickExport MagickBooleanType IsGrayImage(const Image *image,
585 ExceptionInfo *exception)
586{
cristy0910f242010-04-01 18:55:09 +0000587 CacheView
588 *image_view;
589
cristyda06ed12009-10-14 18:36:54 +0000590 ImageType
591 type;
592
cristybb503372010-05-27 20:51:26 +0000593 ssize_t
cristy0910f242010-04-01 18:55:09 +0000594 y;
595
cristyda06ed12009-10-14 18:36:54 +0000596 register const PixelPacket
597 *p;
598
cristybb503372010-05-27 20:51:26 +0000599 register ssize_t
cristy0910f242010-04-01 18:55:09 +0000600 x;
601
cristyda06ed12009-10-14 18:36:54 +0000602 assert(image != (Image *) NULL);
603 assert(image->signature == MagickSignature);
604 if (image->debug != MagickFalse)
605 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
606 if ((image->type == BilevelType) || (image->type == GrayscaleType) ||
607 (image->type == GrayscaleMatteType))
608 return(MagickTrue);
609 if (image->colorspace == CMYKColorspace)
610 return(MagickFalse);
611 type=BilevelType;
cristy0910f242010-04-01 18:55:09 +0000612 image_view=AcquireCacheView(image);
cristybb503372010-05-27 20:51:26 +0000613 for (y=0; y < (ssize_t) image->rows; y++)
cristyda06ed12009-10-14 18:36:54 +0000614 {
cristy0910f242010-04-01 18:55:09 +0000615 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
616 if (p == (const PixelPacket *) NULL)
617 break;
cristybb503372010-05-27 20:51:26 +0000618 for (x=0; x < (ssize_t) image->columns; x++)
cristyda06ed12009-10-14 18:36:54 +0000619 {
cristy0910f242010-04-01 18:55:09 +0000620 if (IsGrayPixel(p) == MagickFalse)
cristyda06ed12009-10-14 18:36:54 +0000621 {
cristy0910f242010-04-01 18:55:09 +0000622 type=UndefinedType;
cristyda06ed12009-10-14 18:36:54 +0000623 break;
cristy0910f242010-04-01 18:55:09 +0000624 }
625 if ((type == BilevelType) && (IsMonochromePixel(p) == MagickFalse))
626 type=GrayscaleType;
627 p++;
cristyda06ed12009-10-14 18:36:54 +0000628 }
cristy0910f242010-04-01 18:55:09 +0000629 if (type == UndefinedType)
cristyda06ed12009-10-14 18:36:54 +0000630 break;
cristyda06ed12009-10-14 18:36:54 +0000631 }
cristy0910f242010-04-01 18:55:09 +0000632 image_view=DestroyCacheView(image_view);
cristyda06ed12009-10-14 18:36:54 +0000633 if (type == UndefinedType)
634 return(MagickFalse);
635 ((Image *) image)->type=type;
636 if ((type == GrayscaleType) && (image->matte != MagickFalse))
637 ((Image *) image)->type=GrayscaleMatteType;
638 return(MagickTrue);
639}
640
641/*
642%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
643% %
644% %
645% %
646% I s M o n o c h r o m e I m a g e %
647% %
648% %
649% %
650%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
651%
652% IsMonochromeImage() returns MagickTrue if all the pixels in the image have
653% the same red, green, and blue intensities and the intensity is either
654% 0 or QuantumRange.
655%
656% The format of the IsMonochromeImage method is:
657%
658% MagickBooleanType IsMonochromeImage(const Image *image,
659% ExceptionInfo *exception)
660%
661% A description of each parameter follows:
662%
663% o image: the image.
664%
665% o exception: return any errors or warnings in this structure.
666%
667*/
668MagickExport MagickBooleanType IsMonochromeImage(const Image *image,
669 ExceptionInfo *exception)
670{
cristy0910f242010-04-01 18:55:09 +0000671 CacheView
672 *image_view;
673
cristyda06ed12009-10-14 18:36:54 +0000674 ImageType
675 type;
676
cristybb503372010-05-27 20:51:26 +0000677 ssize_t
cristy0910f242010-04-01 18:55:09 +0000678 y;
679
cristybb503372010-05-27 20:51:26 +0000680 register ssize_t
cristy0910f242010-04-01 18:55:09 +0000681 x;
682
cristyda06ed12009-10-14 18:36:54 +0000683 register const PixelPacket
684 *p;
685
686 assert(image != (Image *) NULL);
687 assert(image->signature == MagickSignature);
688 if (image->debug != MagickFalse)
689 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
690 if (image->type == BilevelType)
691 return(MagickTrue);
692 if (image->colorspace == CMYKColorspace)
693 return(MagickFalse);
694 type=BilevelType;
cristy0910f242010-04-01 18:55:09 +0000695 image_view=AcquireCacheView(image);
cristybb503372010-05-27 20:51:26 +0000696 for (y=0; y < (ssize_t) image->rows; y++)
cristyda06ed12009-10-14 18:36:54 +0000697 {
cristy0910f242010-04-01 18:55:09 +0000698 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
699 if (p == (const PixelPacket *) NULL)
700 break;
cristybb503372010-05-27 20:51:26 +0000701 for (x=0; x < (ssize_t) image->columns; x++)
cristyda06ed12009-10-14 18:36:54 +0000702 {
cristy0910f242010-04-01 18:55:09 +0000703 if (IsMonochromePixel(p) == MagickFalse)
cristyda06ed12009-10-14 18:36:54 +0000704 {
cristy0910f242010-04-01 18:55:09 +0000705 type=UndefinedType;
cristyda06ed12009-10-14 18:36:54 +0000706 break;
cristy0910f242010-04-01 18:55:09 +0000707 }
708 p++;
cristyda06ed12009-10-14 18:36:54 +0000709 }
cristy0910f242010-04-01 18:55:09 +0000710 if (type == UndefinedType)
cristyda06ed12009-10-14 18:36:54 +0000711 break;
cristyda06ed12009-10-14 18:36:54 +0000712 }
cristy0910f242010-04-01 18:55:09 +0000713 image_view=DestroyCacheView(image_view);
cristyda06ed12009-10-14 18:36:54 +0000714 if (type == UndefinedType)
715 return(MagickFalse);
716 ((Image *) image)->type=type;
717 return(MagickTrue);
718}
719
720/*
721%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
722% %
723% %
724% %
725% I s O p a q u e I m a g e %
726% %
727% %
728% %
729%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
730%
731% IsOpaqueImage() returns MagickTrue if none of the pixels in the image have
732% an opacity value other than opaque (0).
733%
734% The format of the IsOpaqueImage method is:
735%
736% MagickBooleanType IsOpaqueImage(const Image *image,
737% ExceptionInfo *exception)
738%
739% A description of each parameter follows:
740%
741% o image: the image.
742%
743% o exception: return any errors or warnings in this structure.
744%
745*/
746MagickExport MagickBooleanType IsOpaqueImage(const Image *image,
747 ExceptionInfo *exception)
748{
cristyc4c8d132010-01-07 01:58:38 +0000749 CacheView
750 *image_view;
751
cristybb503372010-05-27 20:51:26 +0000752 ssize_t
cristyda06ed12009-10-14 18:36:54 +0000753 y;
754
755 register const PixelPacket
756 *p;
757
cristybb503372010-05-27 20:51:26 +0000758 register ssize_t
cristyda06ed12009-10-14 18:36:54 +0000759 x;
760
cristyda06ed12009-10-14 18:36:54 +0000761 /*
762 Determine if image is opaque.
763 */
764 assert(image != (Image *) NULL);
765 assert(image->signature == MagickSignature);
766 if (image->debug != MagickFalse)
767 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
768 if (image->matte == MagickFalse)
769 return(MagickTrue);
770 image_view=AcquireCacheView(image);
cristybb503372010-05-27 20:51:26 +0000771 for (y=0; y < (ssize_t) image->rows; y++)
cristyda06ed12009-10-14 18:36:54 +0000772 {
773 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
774 if (p == (const PixelPacket *) NULL)
775 break;
cristybb503372010-05-27 20:51:26 +0000776 for (x=0; x < (ssize_t) image->columns; x++)
cristyda06ed12009-10-14 18:36:54 +0000777 {
778 if (p->opacity != OpaqueOpacity)
779 break;
780 p++;
781 }
cristybb503372010-05-27 20:51:26 +0000782 if (x < (ssize_t) image->columns)
cristyda06ed12009-10-14 18:36:54 +0000783 break;
784 }
785 image_view=DestroyCacheView(image_view);
cristybb503372010-05-27 20:51:26 +0000786 return(y < (ssize_t) image->rows ? MagickFalse : MagickTrue);
cristyda06ed12009-10-14 18:36:54 +0000787}
788
789/*
790%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
791% %
792% %
793% %
794% S e t I m a g e C h a n n e l D e p t h %
795% %
796% %
797% %
798%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
799%
800% SetImageChannelDepth() sets the depth of the image.
801%
802% The format of the SetImageChannelDepth method is:
803%
cristybb503372010-05-27 20:51:26 +0000804% MagickBooleanType SetImageDepth(Image *image,const size_t depth)
cristyda06ed12009-10-14 18:36:54 +0000805% MagickBooleanType SetImageChannelDepth(Image *image,
cristybb503372010-05-27 20:51:26 +0000806% const ChannelType channel,const size_t depth)
cristyda06ed12009-10-14 18:36:54 +0000807%
808% A description of each parameter follows:
809%
810% o image: the image.
811%
812% o channel: the channel.
813%
814% o depth: the image depth.
815%
816*/
817
818MagickExport MagickBooleanType SetImageDepth(Image *image,
cristybb503372010-05-27 20:51:26 +0000819 const size_t depth)
cristyda06ed12009-10-14 18:36:54 +0000820{
821 return(SetImageChannelDepth(image,AllChannels,depth));
822}
823
824MagickExport MagickBooleanType SetImageChannelDepth(Image *image,
cristybb503372010-05-27 20:51:26 +0000825 const ChannelType channel,const size_t depth)
cristyda06ed12009-10-14 18:36:54 +0000826{
cristyc4c8d132010-01-07 01:58:38 +0000827 CacheView
828 *image_view;
829
cristyda06ed12009-10-14 18:36:54 +0000830 ExceptionInfo
831 *exception;
832
cristybb503372010-05-27 20:51:26 +0000833 ssize_t
cristyda06ed12009-10-14 18:36:54 +0000834 y;
835
836 MagickBooleanType
837 status;
838
839 QuantumAny
840 range;
841
cristyda06ed12009-10-14 18:36:54 +0000842 assert(image != (Image *) NULL);
843 if (image->debug != MagickFalse)
844 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
845 assert(image->signature == MagickSignature);
cristybb503372010-05-27 20:51:26 +0000846 if (GetImageDepth(image,&image->exception) <= (size_t)
cristyda06ed12009-10-14 18:36:54 +0000847 MagickMin((double) depth,(double) MAGICKCORE_QUANTUM_DEPTH))
848 {
849 image->depth=depth;
850 return(MagickTrue);
851 }
852 /*
853 Scale pixels to desired depth.
854 */
855 status=MagickTrue;
856 range=GetQuantumRange(depth);
857 exception=(&image->exception);
858 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +0000859#if defined(MAGICKCORE_OPENMP_SUPPORT)
860 #pragma omp parallel for schedule(dynamic,4) shared(status)
cristyda06ed12009-10-14 18:36:54 +0000861#endif
cristybb503372010-05-27 20:51:26 +0000862 for (y=0; y < (ssize_t) image->rows; y++)
cristyda06ed12009-10-14 18:36:54 +0000863 {
864 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000865 *restrict indexes;
cristyda06ed12009-10-14 18:36:54 +0000866
cristybb503372010-05-27 20:51:26 +0000867 register ssize_t
cristyda06ed12009-10-14 18:36:54 +0000868 x;
869
870 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000871 *restrict q;
cristyda06ed12009-10-14 18:36:54 +0000872
873 if (status == MagickFalse)
874 continue;
875 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
876 exception);
877 if (q == (PixelPacket *) NULL)
878 {
879 status=MagickFalse;
880 continue;
881 }
882 indexes=GetCacheViewAuthenticIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +0000883 for (x=0; x < (ssize_t) image->columns; x++)
cristyda06ed12009-10-14 18:36:54 +0000884 {
885 if ((channel & RedChannel) != 0)
886 q->red=ScaleAnyToQuantum(ScaleQuantumToAny(q->red,range),range);
887 if ((channel & GreenChannel) != 0)
888 q->green=ScaleAnyToQuantum(ScaleQuantumToAny(q->green,range),range);
889 if ((channel & BlueChannel) != 0)
890 q->blue=ScaleAnyToQuantum(ScaleQuantumToAny(q->blue,range),range);
891 if (((channel & OpacityChannel) != 0) && (image->matte != MagickFalse))
892 q->opacity=ScaleAnyToQuantum(ScaleQuantumToAny(q->opacity,range),range);
893 if (((channel & IndexChannel) != 0) &&
894 (image->colorspace == CMYKColorspace))
895 indexes[x]=ScaleAnyToQuantum(ScaleQuantumToAny(indexes[x],range),range);
896 q++;
897 }
898 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
899 {
900 status=MagickFalse;
901 continue;
902 }
903 }
904 image_view=DestroyCacheView(image_view);
905 if (image->storage_class == PseudoClass)
906 {
907 QuantumAny
908 range;
909
cristybb503372010-05-27 20:51:26 +0000910 register ssize_t
cristyda06ed12009-10-14 18:36:54 +0000911 i;
912
913 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000914 *restrict p;
cristyda06ed12009-10-14 18:36:54 +0000915
916 p=image->colormap;
917 range=GetQuantumRange(depth);
cristyb5d5f722009-11-04 03:03:49 +0000918#if defined(MAGICKCORE_OPENMP_SUPPORT)
919 #pragma omp parallel for schedule(dynamic,4) shared(status)
cristyda06ed12009-10-14 18:36:54 +0000920#endif
cristybb503372010-05-27 20:51:26 +0000921 for (i=0; i < (ssize_t) image->colors; i++)
cristyda06ed12009-10-14 18:36:54 +0000922 {
923 if ((channel & RedChannel) != 0)
924 p->red=ScaleAnyToQuantum(ScaleQuantumToAny(p->red,range),range);
925 if ((channel & GreenChannel) != 0)
926 p->green=ScaleAnyToQuantum(ScaleQuantumToAny(p->green,range),range);
927 if ((channel & BlueChannel) != 0)
928 p->blue=ScaleAnyToQuantum(ScaleQuantumToAny(p->blue,range),range);
929 if ((channel & OpacityChannel) != 0)
930 p->opacity=ScaleAnyToQuantum(ScaleQuantumToAny(p->opacity,range),
931 range);
932 p++;
933 }
934 }
935 image->depth=depth;
936 return(status);
937}