blob: e2278b06d08f1e95c929248c4d600ddd16880e87 [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
cristye8c25f92010-06-03 00:53:06 +0000277static inline QuantumAny GetPixelDepth(const Quantum pixel,
278 const QuantumAny scale)
279{
280#if !defined(MAGICKCORE_HDRI_SUPPORT)
281 return((QuantumAny) (scale*(pixel/scale)));
282#else
283 return((QuantumAny) (scale*(pixel/scale)+0.5));
284#endif
285}
286
287MagickExport size_t GetImageDepth(const Image *image,ExceptionInfo *exception)
cristyda06ed12009-10-14 18:36:54 +0000288{
289 return(GetImageChannelDepth(image,AllChannels,exception));
290}
291
cristybb503372010-05-27 20:51:26 +0000292MagickExport size_t GetImageChannelDepth(const Image *image,
cristyda06ed12009-10-14 18:36:54 +0000293 const ChannelType channel,ExceptionInfo *exception)
294{
cristyc4c8d132010-01-07 01:58:38 +0000295 CacheView
296 *image_view;
297
cristybb503372010-05-27 20:51:26 +0000298 ssize_t
cristyda06ed12009-10-14 18:36:54 +0000299 y;
300
301 MagickBooleanType
302 status;
303
cristybb503372010-05-27 20:51:26 +0000304 register ssize_t
cristyda06ed12009-10-14 18:36:54 +0000305 id;
306
cristybb503372010-05-27 20:51:26 +0000307 size_t
cristyda06ed12009-10-14 18:36:54 +0000308 *current_depth,
309 depth,
310 number_threads;
311
cristyda06ed12009-10-14 18:36:54 +0000312 /*
313 Compute image depth.
314 */
315 assert(image != (Image *) NULL);
316 assert(image->signature == MagickSignature);
317 if (image->debug != MagickFalse)
318 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
319 number_threads=GetOpenMPMaximumThreads();
cristybb503372010-05-27 20:51:26 +0000320 current_depth=(size_t *) AcquireQuantumMemory(number_threads,
cristyda06ed12009-10-14 18:36:54 +0000321 sizeof(*current_depth));
cristybb503372010-05-27 20:51:26 +0000322 if (current_depth == (size_t *) NULL)
cristyda06ed12009-10-14 18:36:54 +0000323 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
324 status=MagickTrue;
cristybb503372010-05-27 20:51:26 +0000325 for (id=0; id < (ssize_t) number_threads; id++)
cristyda06ed12009-10-14 18:36:54 +0000326 current_depth[id]=1;
327 if ((image->storage_class == PseudoClass) && (image->matte == MagickFalse))
328 {
329 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000330 *restrict p;
cristyda06ed12009-10-14 18:36:54 +0000331
cristybb503372010-05-27 20:51:26 +0000332 register ssize_t
cristyda06ed12009-10-14 18:36:54 +0000333 i;
334
335 p=image->colormap;
cristyb5d5f722009-11-04 03:03:49 +0000336#if defined(MAGICKCORE_OPENMP_SUPPORT)
337 #pragma omp parallel for schedule(dynamic,4) shared(status)
cristyda06ed12009-10-14 18:36:54 +0000338#endif
cristybb503372010-05-27 20:51:26 +0000339 for (i=0; i < (ssize_t) image->colors; i++)
cristyda06ed12009-10-14 18:36:54 +0000340 {
341 if (status == MagickFalse)
342 continue;
343 id=GetOpenMPThreadId();
344 while (current_depth[id] < MAGICKCORE_QUANTUM_DEPTH)
345 {
346 MagickStatusType
347 status;
348
349 QuantumAny
cristye8c25f92010-06-03 00:53:06 +0000350 scale;
cristyda06ed12009-10-14 18:36:54 +0000351
352 status=0;
cristye8c25f92010-06-03 00:53:06 +0000353 scale=1;
354 if (depth < QuantumDepth)
355 scale=QuantumRange/((QuantumAny) QuantumRange >> (QuantumDepth-
356 current_depth[id]));
cristyda06ed12009-10-14 18:36:54 +0000357 if ((channel & RedChannel) != 0)
cristye8c25f92010-06-03 00:53:06 +0000358 status|=(QuantumAny) p->red != GetPixelDepth(p->red,scale);
cristyda06ed12009-10-14 18:36:54 +0000359 if ((channel & GreenChannel) != 0)
cristye8c25f92010-06-03 00:53:06 +0000360 status|=(QuantumAny) p->green != GetPixelDepth(p->green,scale);
cristyda06ed12009-10-14 18:36:54 +0000361 if ((channel & BlueChannel) != 0)
cristye8c25f92010-06-03 00:53:06 +0000362 status|=(QuantumAny) p->blue != GetPixelDepth(p->blue,scale);
cristyda06ed12009-10-14 18:36:54 +0000363 if (status == 0)
364 break;
365 current_depth[id]++;
366 }
367 p++;
368 }
369 depth=current_depth[0];
cristybb503372010-05-27 20:51:26 +0000370 for (id=1; id < (ssize_t) number_threads; id++)
cristyda06ed12009-10-14 18:36:54 +0000371 if (depth < current_depth[id])
372 depth=current_depth[id];
cristybb503372010-05-27 20:51:26 +0000373 current_depth=(size_t *) RelinquishMagickMemory(current_depth);
cristyda06ed12009-10-14 18:36:54 +0000374 return(depth);
375 }
376 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +0000377#if defined(MAGICKCORE_OPENMP_SUPPORT)
378 #pragma omp parallel for schedule(dynamic,4) shared(status)
cristyda06ed12009-10-14 18:36:54 +0000379#endif
cristybb503372010-05-27 20:51:26 +0000380 for (y=0; y < (ssize_t) image->rows; y++)
cristyda06ed12009-10-14 18:36:54 +0000381 {
382 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000383 *restrict indexes;
cristyda06ed12009-10-14 18:36:54 +0000384
385 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000386 *restrict p;
cristyda06ed12009-10-14 18:36:54 +0000387
cristybb503372010-05-27 20:51:26 +0000388 register ssize_t
cristyda06ed12009-10-14 18:36:54 +0000389 id,
390 x;
391
392 if (status == MagickFalse)
393 continue;
394 id=GetOpenMPThreadId();
395 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
396 if (p == (const PixelPacket *) NULL)
397 continue;
398 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +0000399 for (x=0; x < (ssize_t) image->columns; x++)
cristyda06ed12009-10-14 18:36:54 +0000400 {
401 while (current_depth[id] < MAGICKCORE_QUANTUM_DEPTH)
402 {
403 MagickStatusType
404 status;
405
406 QuantumAny
cristye8c25f92010-06-03 00:53:06 +0000407 scale;
cristyda06ed12009-10-14 18:36:54 +0000408
409 status=0;
cristye8c25f92010-06-03 00:53:06 +0000410 scale=1;
411 if (depth < QuantumDepth)
412 scale=QuantumRange/((QuantumAny) QuantumRange >> (QuantumDepth-
413 current_depth[id]));
cristyda06ed12009-10-14 18:36:54 +0000414 if ((channel & RedChannel) != 0)
cristye8c25f92010-06-03 00:53:06 +0000415 status|=(QuantumAny) p->red != GetPixelDepth(p->red,scale);
cristyda06ed12009-10-14 18:36:54 +0000416 if ((channel & GreenChannel) != 0)
cristye8c25f92010-06-03 00:53:06 +0000417 status|=(QuantumAny) p->green != GetPixelDepth(p->green,scale);
cristyda06ed12009-10-14 18:36:54 +0000418 if ((channel & BlueChannel) != 0)
cristye8c25f92010-06-03 00:53:06 +0000419 status|=(QuantumAny) p->blue != GetPixelDepth(p->blue,scale);
cristyda06ed12009-10-14 18:36:54 +0000420 if (((channel & OpacityChannel) != 0) && (image->matte != MagickFalse))
cristye8c25f92010-06-03 00:53:06 +0000421 status|=(QuantumAny) p->opacity != GetPixelDepth(p->opacity,scale);
cristyda06ed12009-10-14 18:36:54 +0000422 if (((channel & IndexChannel) != 0) &&
423 (image->colorspace == CMYKColorspace))
cristye8c25f92010-06-03 00:53:06 +0000424 status|=(QuantumAny) indexes[x] != GetPixelDepth(indexes[x],scale);
cristyda06ed12009-10-14 18:36:54 +0000425 if (status == 0)
426 break;
427 current_depth[id]++;
428 }
429 p++;
430 }
431 if (current_depth[id] == MAGICKCORE_QUANTUM_DEPTH)
432 status=MagickFalse;
433 }
434 image_view=DestroyCacheView(image_view);
435 depth=current_depth[0];
cristybb503372010-05-27 20:51:26 +0000436 for (id=1; id < (ssize_t) number_threads; id++)
cristyda06ed12009-10-14 18:36:54 +0000437 if (depth < current_depth[id])
438 depth=current_depth[id];
cristybb503372010-05-27 20:51:26 +0000439 current_depth=(size_t *) RelinquishMagickMemory(current_depth);
cristyda06ed12009-10-14 18:36:54 +0000440 return(depth);
441}
442
443/*
444%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
445% %
446% %
447% %
448% G e t I m a g e Q u a n t u m D e p t h %
449% %
450% %
451% %
452%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
453%
454% GetImageQuantumDepth() returns the depth of the image rounded to a legal
455% quantum depth: 8, 16, or 32.
456%
457% The format of the GetImageQuantumDepth method is:
458%
cristybb503372010-05-27 20:51:26 +0000459% size_t GetImageQuantumDepth(const Image *image,
cristyda06ed12009-10-14 18:36:54 +0000460% const MagickBooleanType constrain)
461%
462% A description of each parameter follows:
463%
464% o image: the image.
465%
466% o constrain: A value other than MagickFalse, constrains the depth to
467% a maximum of MAGICKCORE_QUANTUM_DEPTH.
468%
469*/
470
471static inline double MagickMin(const double x,const double y)
472{
473 if (x < y)
474 return(x);
475 return(y);
476}
477
cristybb503372010-05-27 20:51:26 +0000478MagickExport size_t GetImageQuantumDepth(const Image *image,
cristyda06ed12009-10-14 18:36:54 +0000479 const MagickBooleanType constrain)
480{
cristybb503372010-05-27 20:51:26 +0000481 size_t
cristyda06ed12009-10-14 18:36:54 +0000482 depth;
483
484 depth=image->depth;
485 if (depth <= 8)
486 depth=8;
487 else
488 if (depth <= 16)
489 depth=16;
490 else
491 if (depth <= 32)
492 depth=32;
493 else
494 if (depth <= 64)
495 depth=64;
496 if (constrain != MagickFalse)
cristybb503372010-05-27 20:51:26 +0000497 depth=(size_t) MagickMin((double) depth,(double)
cristyda06ed12009-10-14 18:36:54 +0000498 MAGICKCORE_QUANTUM_DEPTH);
499 return(depth);
500}
501
502/*
503%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
504% %
505% %
506% %
507% G e t I m a g e T y p e %
508% %
509% %
510% %
511%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
512%
513% GetImageType() returns the potential type of image:
514%
515% Bilevel Grayscale GrayscaleMatte
516% Palette PaletteMatte TrueColor
517% TrueColorMatte ColorSeparation ColorSeparationMatte
518%
519% To ensure the image type matches its potential, use SetImageType():
520%
521% (void) SetImageType(image,GetImageType(image));
522%
523% The format of the GetImageType method is:
524%
525% ImageType GetImageType(const Image *image,ExceptionInfo *exception)
526%
527% A description of each parameter follows:
528%
529% o image: the image.
530%
531% o exception: return any errors or warnings in this structure.
532%
533*/
534MagickExport ImageType GetImageType(const Image *image,ExceptionInfo *exception)
535{
536 assert(image != (Image *) NULL);
537 assert(image->signature == MagickSignature);
538 if (image->debug != MagickFalse)
539 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
540 if (image->colorspace == CMYKColorspace)
541 {
542 if (image->matte == MagickFalse)
543 return(ColorSeparationType);
544 return(ColorSeparationMatteType);
545 }
546 if (IsMonochromeImage(image,exception) != MagickFalse)
547 return(BilevelType);
548 if (IsGrayImage(image,exception) != MagickFalse)
549 {
550 if (image->matte != MagickFalse)
551 return(GrayscaleMatteType);
552 return(GrayscaleType);
553 }
554 if (IsPaletteImage(image,exception) != MagickFalse)
555 {
556 if (image->matte != MagickFalse)
557 return(PaletteMatteType);
558 return(PaletteType);
559 }
560 if (image->matte != MagickFalse)
561 return(TrueColorMatteType);
562 return(TrueColorType);
563}
564
565/*
566%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
567% %
568% %
569% %
570% I s G r a y I m a g e %
571% %
572% %
573% %
574%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
575%
576% IsGrayImage() returns MagickTrue if all the pixels in the image have the
577% same red, green, and blue intensities.
578%
579% The format of the IsGrayImage method is:
580%
581% MagickBooleanType IsGrayImage(const Image *image,
582% ExceptionInfo *exception)
583%
584% A description of each parameter follows:
585%
586% o image: the image.
587%
588% o exception: return any errors or warnings in this structure.
589%
590*/
591MagickExport MagickBooleanType IsGrayImage(const Image *image,
592 ExceptionInfo *exception)
593{
cristy0910f242010-04-01 18:55:09 +0000594 CacheView
595 *image_view;
596
cristyda06ed12009-10-14 18:36:54 +0000597 ImageType
598 type;
599
cristybb503372010-05-27 20:51:26 +0000600 ssize_t
cristy0910f242010-04-01 18:55:09 +0000601 y;
602
cristyda06ed12009-10-14 18:36:54 +0000603 register const PixelPacket
604 *p;
605
cristybb503372010-05-27 20:51:26 +0000606 register ssize_t
cristy0910f242010-04-01 18:55:09 +0000607 x;
608
cristyda06ed12009-10-14 18:36:54 +0000609 assert(image != (Image *) NULL);
610 assert(image->signature == MagickSignature);
611 if (image->debug != MagickFalse)
612 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
613 if ((image->type == BilevelType) || (image->type == GrayscaleType) ||
614 (image->type == GrayscaleMatteType))
615 return(MagickTrue);
616 if (image->colorspace == CMYKColorspace)
617 return(MagickFalse);
618 type=BilevelType;
cristy0910f242010-04-01 18:55:09 +0000619 image_view=AcquireCacheView(image);
cristybb503372010-05-27 20:51:26 +0000620 for (y=0; y < (ssize_t) image->rows; y++)
cristyda06ed12009-10-14 18:36:54 +0000621 {
cristy0910f242010-04-01 18:55:09 +0000622 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
623 if (p == (const PixelPacket *) NULL)
624 break;
cristybb503372010-05-27 20:51:26 +0000625 for (x=0; x < (ssize_t) image->columns; x++)
cristyda06ed12009-10-14 18:36:54 +0000626 {
cristy0910f242010-04-01 18:55:09 +0000627 if (IsGrayPixel(p) == MagickFalse)
cristyda06ed12009-10-14 18:36:54 +0000628 {
cristy0910f242010-04-01 18:55:09 +0000629 type=UndefinedType;
cristyda06ed12009-10-14 18:36:54 +0000630 break;
cristy0910f242010-04-01 18:55:09 +0000631 }
632 if ((type == BilevelType) && (IsMonochromePixel(p) == MagickFalse))
633 type=GrayscaleType;
634 p++;
cristyda06ed12009-10-14 18:36:54 +0000635 }
cristy0910f242010-04-01 18:55:09 +0000636 if (type == UndefinedType)
cristyda06ed12009-10-14 18:36:54 +0000637 break;
cristyda06ed12009-10-14 18:36:54 +0000638 }
cristy0910f242010-04-01 18:55:09 +0000639 image_view=DestroyCacheView(image_view);
cristyda06ed12009-10-14 18:36:54 +0000640 if (type == UndefinedType)
641 return(MagickFalse);
642 ((Image *) image)->type=type;
643 if ((type == GrayscaleType) && (image->matte != MagickFalse))
644 ((Image *) image)->type=GrayscaleMatteType;
645 return(MagickTrue);
646}
647
648/*
649%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
650% %
651% %
652% %
653% I s M o n o c h r o m e I m a g e %
654% %
655% %
656% %
657%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
658%
659% IsMonochromeImage() returns MagickTrue if all the pixels in the image have
660% the same red, green, and blue intensities and the intensity is either
661% 0 or QuantumRange.
662%
663% The format of the IsMonochromeImage method is:
664%
665% MagickBooleanType IsMonochromeImage(const Image *image,
666% ExceptionInfo *exception)
667%
668% A description of each parameter follows:
669%
670% o image: the image.
671%
672% o exception: return any errors or warnings in this structure.
673%
674*/
675MagickExport MagickBooleanType IsMonochromeImage(const Image *image,
676 ExceptionInfo *exception)
677{
cristy0910f242010-04-01 18:55:09 +0000678 CacheView
679 *image_view;
680
cristyda06ed12009-10-14 18:36:54 +0000681 ImageType
682 type;
683
cristybb503372010-05-27 20:51:26 +0000684 ssize_t
cristy0910f242010-04-01 18:55:09 +0000685 y;
686
cristybb503372010-05-27 20:51:26 +0000687 register ssize_t
cristy0910f242010-04-01 18:55:09 +0000688 x;
689
cristyda06ed12009-10-14 18:36:54 +0000690 register const PixelPacket
691 *p;
692
693 assert(image != (Image *) NULL);
694 assert(image->signature == MagickSignature);
695 if (image->debug != MagickFalse)
696 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
697 if (image->type == BilevelType)
698 return(MagickTrue);
699 if (image->colorspace == CMYKColorspace)
700 return(MagickFalse);
701 type=BilevelType;
cristy0910f242010-04-01 18:55:09 +0000702 image_view=AcquireCacheView(image);
cristybb503372010-05-27 20:51:26 +0000703 for (y=0; y < (ssize_t) image->rows; y++)
cristyda06ed12009-10-14 18:36:54 +0000704 {
cristy0910f242010-04-01 18:55:09 +0000705 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
706 if (p == (const PixelPacket *) NULL)
707 break;
cristybb503372010-05-27 20:51:26 +0000708 for (x=0; x < (ssize_t) image->columns; x++)
cristyda06ed12009-10-14 18:36:54 +0000709 {
cristy0910f242010-04-01 18:55:09 +0000710 if (IsMonochromePixel(p) == MagickFalse)
cristyda06ed12009-10-14 18:36:54 +0000711 {
cristy0910f242010-04-01 18:55:09 +0000712 type=UndefinedType;
cristyda06ed12009-10-14 18:36:54 +0000713 break;
cristy0910f242010-04-01 18:55:09 +0000714 }
715 p++;
cristyda06ed12009-10-14 18:36:54 +0000716 }
cristy0910f242010-04-01 18:55:09 +0000717 if (type == UndefinedType)
cristyda06ed12009-10-14 18:36:54 +0000718 break;
cristyda06ed12009-10-14 18:36:54 +0000719 }
cristy0910f242010-04-01 18:55:09 +0000720 image_view=DestroyCacheView(image_view);
cristyda06ed12009-10-14 18:36:54 +0000721 if (type == UndefinedType)
722 return(MagickFalse);
723 ((Image *) image)->type=type;
724 return(MagickTrue);
725}
726
727/*
728%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
729% %
730% %
731% %
732% I s O p a q u e I m a g e %
733% %
734% %
735% %
736%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
737%
738% IsOpaqueImage() returns MagickTrue if none of the pixels in the image have
739% an opacity value other than opaque (0).
740%
741% The format of the IsOpaqueImage method is:
742%
743% MagickBooleanType IsOpaqueImage(const Image *image,
744% ExceptionInfo *exception)
745%
746% A description of each parameter follows:
747%
748% o image: the image.
749%
750% o exception: return any errors or warnings in this structure.
751%
752*/
753MagickExport MagickBooleanType IsOpaqueImage(const Image *image,
754 ExceptionInfo *exception)
755{
cristyc4c8d132010-01-07 01:58:38 +0000756 CacheView
757 *image_view;
758
cristybb503372010-05-27 20:51:26 +0000759 ssize_t
cristyda06ed12009-10-14 18:36:54 +0000760 y;
761
762 register const PixelPacket
763 *p;
764
cristybb503372010-05-27 20:51:26 +0000765 register ssize_t
cristyda06ed12009-10-14 18:36:54 +0000766 x;
767
cristyda06ed12009-10-14 18:36:54 +0000768 /*
769 Determine if image is opaque.
770 */
771 assert(image != (Image *) NULL);
772 assert(image->signature == MagickSignature);
773 if (image->debug != MagickFalse)
774 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
775 if (image->matte == MagickFalse)
776 return(MagickTrue);
777 image_view=AcquireCacheView(image);
cristybb503372010-05-27 20:51:26 +0000778 for (y=0; y < (ssize_t) image->rows; y++)
cristyda06ed12009-10-14 18:36:54 +0000779 {
780 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
781 if (p == (const PixelPacket *) NULL)
782 break;
cristybb503372010-05-27 20:51:26 +0000783 for (x=0; x < (ssize_t) image->columns; x++)
cristyda06ed12009-10-14 18:36:54 +0000784 {
785 if (p->opacity != OpaqueOpacity)
786 break;
787 p++;
788 }
cristybb503372010-05-27 20:51:26 +0000789 if (x < (ssize_t) image->columns)
cristyda06ed12009-10-14 18:36:54 +0000790 break;
791 }
792 image_view=DestroyCacheView(image_view);
cristybb503372010-05-27 20:51:26 +0000793 return(y < (ssize_t) image->rows ? MagickFalse : MagickTrue);
cristyda06ed12009-10-14 18:36:54 +0000794}
795
796/*
797%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
798% %
799% %
800% %
801% S e t I m a g e C h a n n e l D e p t h %
802% %
803% %
804% %
805%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
806%
807% SetImageChannelDepth() sets the depth of the image.
808%
809% The format of the SetImageChannelDepth method is:
810%
cristybb503372010-05-27 20:51:26 +0000811% MagickBooleanType SetImageDepth(Image *image,const size_t depth)
cristyda06ed12009-10-14 18:36:54 +0000812% MagickBooleanType SetImageChannelDepth(Image *image,
cristybb503372010-05-27 20:51:26 +0000813% const ChannelType channel,const size_t depth)
cristyda06ed12009-10-14 18:36:54 +0000814%
815% A description of each parameter follows:
816%
817% o image: the image.
818%
819% o channel: the channel.
820%
821% o depth: the image depth.
822%
823*/
824
cristye8c25f92010-06-03 00:53:06 +0000825static inline Quantum SetPixelDepth(const Quantum pixel,
826 const QuantumAny scale)
827{
828 return((Quantum) (scale*(pixel/scale)));
829}
830
cristyda06ed12009-10-14 18:36:54 +0000831MagickExport MagickBooleanType SetImageDepth(Image *image,
cristybb503372010-05-27 20:51:26 +0000832 const size_t depth)
cristyda06ed12009-10-14 18:36:54 +0000833{
834 return(SetImageChannelDepth(image,AllChannels,depth));
835}
836
837MagickExport MagickBooleanType SetImageChannelDepth(Image *image,
cristybb503372010-05-27 20:51:26 +0000838 const ChannelType channel,const size_t depth)
cristyda06ed12009-10-14 18:36:54 +0000839{
cristyc4c8d132010-01-07 01:58:38 +0000840 CacheView
841 *image_view;
842
cristyda06ed12009-10-14 18:36:54 +0000843 ExceptionInfo
844 *exception;
845
cristybb503372010-05-27 20:51:26 +0000846 ssize_t
cristyda06ed12009-10-14 18:36:54 +0000847 y;
848
849 MagickBooleanType
850 status;
851
852 QuantumAny
cristye8c25f92010-06-03 00:53:06 +0000853 scale;
cristyda06ed12009-10-14 18:36:54 +0000854
cristyda06ed12009-10-14 18:36:54 +0000855 assert(image != (Image *) NULL);
856 if (image->debug != MagickFalse)
857 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
858 assert(image->signature == MagickSignature);
cristybb503372010-05-27 20:51:26 +0000859 if (GetImageDepth(image,&image->exception) <= (size_t)
cristyda06ed12009-10-14 18:36:54 +0000860 MagickMin((double) depth,(double) MAGICKCORE_QUANTUM_DEPTH))
861 {
862 image->depth=depth;
863 return(MagickTrue);
864 }
865 /*
866 Scale pixels to desired depth.
867 */
868 status=MagickTrue;
cristye8c25f92010-06-03 00:53:06 +0000869 scale=1;
870 if (depth < QuantumDepth)
871 scale=QuantumRange/((QuantumAny) QuantumRange >> (QuantumDepth-depth));
cristyda06ed12009-10-14 18:36:54 +0000872 exception=(&image->exception);
873 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +0000874#if defined(MAGICKCORE_OPENMP_SUPPORT)
875 #pragma omp parallel for schedule(dynamic,4) shared(status)
cristyda06ed12009-10-14 18:36:54 +0000876#endif
cristybb503372010-05-27 20:51:26 +0000877 for (y=0; y < (ssize_t) image->rows; y++)
cristyda06ed12009-10-14 18:36:54 +0000878 {
879 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000880 *restrict indexes;
cristyda06ed12009-10-14 18:36:54 +0000881
cristybb503372010-05-27 20:51:26 +0000882 register ssize_t
cristyda06ed12009-10-14 18:36:54 +0000883 x;
884
885 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000886 *restrict q;
cristyda06ed12009-10-14 18:36:54 +0000887
888 if (status == MagickFalse)
889 continue;
890 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
891 exception);
892 if (q == (PixelPacket *) NULL)
893 {
894 status=MagickFalse;
895 continue;
896 }
897 indexes=GetCacheViewAuthenticIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +0000898 for (x=0; x < (ssize_t) image->columns; x++)
cristyda06ed12009-10-14 18:36:54 +0000899 {
900 if ((channel & RedChannel) != 0)
cristye8c25f92010-06-03 00:53:06 +0000901 q->red=SetPixelDepth(q->red,scale);
cristyda06ed12009-10-14 18:36:54 +0000902 if ((channel & GreenChannel) != 0)
cristye8c25f92010-06-03 00:53:06 +0000903 q->green=SetPixelDepth(q->green,scale);
cristyda06ed12009-10-14 18:36:54 +0000904 if ((channel & BlueChannel) != 0)
cristye8c25f92010-06-03 00:53:06 +0000905 q->green=SetPixelDepth(q->blue,scale);
cristyda06ed12009-10-14 18:36:54 +0000906 if (((channel & OpacityChannel) != 0) && (image->matte != MagickFalse))
cristye8c25f92010-06-03 00:53:06 +0000907 q->opacity=SetPixelDepth(q->opacity,scale);
cristyda06ed12009-10-14 18:36:54 +0000908 if (((channel & IndexChannel) != 0) &&
909 (image->colorspace == CMYKColorspace))
cristye8c25f92010-06-03 00:53:06 +0000910 indexes[x]=SetPixelDepth(indexes[x],scale);
cristyda06ed12009-10-14 18:36:54 +0000911 q++;
912 }
913 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
914 {
915 status=MagickFalse;
916 continue;
917 }
918 }
919 image_view=DestroyCacheView(image_view);
920 if (image->storage_class == PseudoClass)
921 {
922 QuantumAny
923 range;
924
cristybb503372010-05-27 20:51:26 +0000925 register ssize_t
cristyda06ed12009-10-14 18:36:54 +0000926 i;
927
928 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000929 *restrict p;
cristyda06ed12009-10-14 18:36:54 +0000930
931 p=image->colormap;
932 range=GetQuantumRange(depth);
cristyb5d5f722009-11-04 03:03:49 +0000933#if defined(MAGICKCORE_OPENMP_SUPPORT)
934 #pragma omp parallel for schedule(dynamic,4) shared(status)
cristyda06ed12009-10-14 18:36:54 +0000935#endif
cristybb503372010-05-27 20:51:26 +0000936 for (i=0; i < (ssize_t) image->colors; i++)
cristyda06ed12009-10-14 18:36:54 +0000937 {
938 if ((channel & RedChannel) != 0)
cristye8c25f92010-06-03 00:53:06 +0000939 p->red=SetPixelDepth(p->red,scale);
cristyda06ed12009-10-14 18:36:54 +0000940 if ((channel & GreenChannel) != 0)
cristye8c25f92010-06-03 00:53:06 +0000941 p->green=SetPixelDepth(p->green,scale);
cristyda06ed12009-10-14 18:36:54 +0000942 if ((channel & BlueChannel) != 0)
cristye8c25f92010-06-03 00:53:06 +0000943 p->blue=SetPixelDepth(p->blue,scale);
cristyda06ed12009-10-14 18:36:54 +0000944 if ((channel & OpacityChannel) != 0)
cristye8c25f92010-06-03 00:53:06 +0000945 p->opacity=SetPixelDepth(p->opacity,scale);
cristyda06ed12009-10-14 18:36:54 +0000946 p++;
947 }
948 }
949 image->depth=depth;
950 return(status);
951}