blob: 8378d460c365704551cb3826c065595c8ea2198d [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% %
cristy7e41fe82010-12-04 23:12:08 +000020% Copyright 1999-2011 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
cristyda06ed12009-10-14 18:36:54 +0000126 MagickBooleanType
127 status;
128
129 MagickPixelPacket
130 target[3],
131 zero;
132
133 RectangleInfo
134 bounds;
135
136 register const PixelPacket
137 *p;
138
cristy9d314ff2011-03-09 01:30:28 +0000139 ssize_t
140 y;
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]);
cristy4cb39ab2010-06-07 13:59:16 +0000166 p=GetCacheViewVirtualPixels(image_view,0,(ssize_t) image->rows-1,1,1,
167 exception);
cristyda06ed12009-10-14 18:36:54 +0000168 SetMagickPixelPacket(image,p,GetCacheViewAuthenticIndexQueue(image_view),
169 &target[2]);
170 status=MagickTrue;
171 GetMagickPixelPacket(image,&zero);
cristyb5d5f722009-11-04 03:03:49 +0000172#if defined(MAGICKCORE_OPENMP_SUPPORT)
173 #pragma omp parallel for schedule(dynamic,4) shared(status)
cristyda06ed12009-10-14 18:36:54 +0000174#endif
cristybb503372010-05-27 20:51:26 +0000175 for (y=0; y < (ssize_t) image->rows; y++)
cristyda06ed12009-10-14 18:36:54 +0000176 {
177 MagickPixelPacket
178 pixel;
179
180 RectangleInfo
181 bounding_box;
182
183 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000184 *restrict indexes;
cristyda06ed12009-10-14 18:36:54 +0000185
186 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000187 *restrict p;
cristyda06ed12009-10-14 18:36:54 +0000188
cristybb503372010-05-27 20:51:26 +0000189 register ssize_t
cristyda06ed12009-10-14 18:36:54 +0000190 x;
191
192 if (status == MagickFalse)
193 continue;
cristyb5d5f722009-11-04 03:03:49 +0000194#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyda06ed12009-10-14 18:36:54 +0000195# pragma omp critical (MagickCore_GetImageBoundingBox)
196#endif
197 bounding_box=bounds;
198 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
199 if (p == (const PixelPacket *) NULL)
200 {
201 status=MagickFalse;
202 continue;
203 }
204 indexes=GetCacheViewVirtualIndexQueue(image_view);
205 pixel=zero;
cristybb503372010-05-27 20:51:26 +0000206 for (x=0; x < (ssize_t) image->columns; x++)
cristyda06ed12009-10-14 18:36:54 +0000207 {
208 SetMagickPixelPacket(image,p,indexes+x,&pixel);
209 if ((x < bounding_box.x) &&
210 (IsMagickColorSimilar(&pixel,&target[0]) == MagickFalse))
211 bounding_box.x=x;
cristybb503372010-05-27 20:51:26 +0000212 if ((x > (ssize_t) bounding_box.width) &&
cristyda06ed12009-10-14 18:36:54 +0000213 (IsMagickColorSimilar(&pixel,&target[1]) == MagickFalse))
cristybb503372010-05-27 20:51:26 +0000214 bounding_box.width=(size_t) x;
cristyda06ed12009-10-14 18:36:54 +0000215 if ((y < bounding_box.y) &&
216 (IsMagickColorSimilar(&pixel,&target[0]) == MagickFalse))
217 bounding_box.y=y;
cristybb503372010-05-27 20:51:26 +0000218 if ((y > (ssize_t) bounding_box.height) &&
cristyda06ed12009-10-14 18:36:54 +0000219 (IsMagickColorSimilar(&pixel,&target[2]) == MagickFalse))
cristybb503372010-05-27 20:51:26 +0000220 bounding_box.height=(size_t) y;
cristyda06ed12009-10-14 18:36:54 +0000221 p++;
222 }
cristyb5d5f722009-11-04 03:03:49 +0000223#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyda06ed12009-10-14 18:36:54 +0000224# pragma omp critical (MagickCore_GetImageBoundingBox)
225#endif
226 {
227 if (bounding_box.x < bounds.x)
228 bounds.x=bounding_box.x;
229 if (bounding_box.y < bounds.y)
230 bounds.y=bounding_box.y;
231 if (bounding_box.width > bounds.width)
232 bounds.width=bounding_box.width;
233 if (bounding_box.height > bounds.height)
234 bounds.height=bounding_box.height;
235 }
236 }
237 image_view=DestroyCacheView(image_view);
238 if ((bounds.width == 0) || (bounds.height == 0))
239 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
240 "GeometryDoesNotContainImage","`%s'",image->filename);
241 else
242 {
243 bounds.width-=(bounds.x-1);
244 bounds.height-=(bounds.y-1);
245 }
246 return(bounds);
247}
248
249/*
250%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
251% %
252% %
253% %
254% G e t I m a g e C h a n n e l D e p t h %
255% %
256% %
257% %
258%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
259%
260% GetImageChannelDepth() returns the depth of a particular image channel.
261%
262% The format of the GetImageChannelDepth method is:
263%
cristybb503372010-05-27 20:51:26 +0000264% size_t GetImageDepth(const Image *image,ExceptionInfo *exception)
265% size_t GetImageChannelDepth(const Image *image,
cristyda06ed12009-10-14 18:36:54 +0000266% const ChannelType channel,ExceptionInfo *exception)
267%
268% A description of each parameter follows:
269%
270% o image: the image.
271%
272% o channel: the channel.
273%
274% o exception: return any errors or warnings in this structure.
275%
276*/
cristye8c25f92010-06-03 00:53:06 +0000277MagickExport size_t GetImageDepth(const Image *image,ExceptionInfo *exception)
cristyda06ed12009-10-14 18:36:54 +0000278{
cristy9a9230e2011-04-26 14:56:14 +0000279 return(GetImageChannelDepth(image,CompositeChannels,exception));
cristyda06ed12009-10-14 18:36:54 +0000280}
281
cristybb503372010-05-27 20:51:26 +0000282MagickExport size_t GetImageChannelDepth(const Image *image,
cristyda06ed12009-10-14 18:36:54 +0000283 const ChannelType channel,ExceptionInfo *exception)
284{
cristyc4c8d132010-01-07 01:58:38 +0000285 CacheView
286 *image_view;
287
cristyda06ed12009-10-14 18:36:54 +0000288 MagickBooleanType
289 status;
290
cristybb503372010-05-27 20:51:26 +0000291 register ssize_t
cristyda06ed12009-10-14 18:36:54 +0000292 id;
293
cristybb503372010-05-27 20:51:26 +0000294 size_t
cristyda06ed12009-10-14 18:36:54 +0000295 *current_depth,
296 depth,
297 number_threads;
298
cristy9d314ff2011-03-09 01:30:28 +0000299 ssize_t
300 y;
301
cristyda06ed12009-10-14 18:36:54 +0000302 /*
303 Compute image depth.
304 */
305 assert(image != (Image *) NULL);
306 assert(image->signature == MagickSignature);
307 if (image->debug != MagickFalse)
308 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
309 number_threads=GetOpenMPMaximumThreads();
cristybb503372010-05-27 20:51:26 +0000310 current_depth=(size_t *) AcquireQuantumMemory(number_threads,
cristyda06ed12009-10-14 18:36:54 +0000311 sizeof(*current_depth));
cristybb503372010-05-27 20:51:26 +0000312 if (current_depth == (size_t *) NULL)
cristyda06ed12009-10-14 18:36:54 +0000313 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
314 status=MagickTrue;
cristybb503372010-05-27 20:51:26 +0000315 for (id=0; id < (ssize_t) number_threads; id++)
cristyda06ed12009-10-14 18:36:54 +0000316 current_depth[id]=1;
317 if ((image->storage_class == PseudoClass) && (image->matte == MagickFalse))
318 {
319 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000320 *restrict p;
cristyda06ed12009-10-14 18:36:54 +0000321
cristybb503372010-05-27 20:51:26 +0000322 register ssize_t
cristyda06ed12009-10-14 18:36:54 +0000323 i;
324
325 p=image->colormap;
cristyb5d5f722009-11-04 03:03:49 +0000326#if defined(MAGICKCORE_OPENMP_SUPPORT)
327 #pragma omp parallel for schedule(dynamic,4) shared(status)
cristyda06ed12009-10-14 18:36:54 +0000328#endif
cristybb503372010-05-27 20:51:26 +0000329 for (i=0; i < (ssize_t) image->colors; i++)
cristyda06ed12009-10-14 18:36:54 +0000330 {
cristy5c9e6f22010-09-17 17:31:01 +0000331 const int
332 id = GetOpenMPThreadId();
333
cristyda06ed12009-10-14 18:36:54 +0000334 if (status == MagickFalse)
335 continue;
cristyda06ed12009-10-14 18:36:54 +0000336 while (current_depth[id] < MAGICKCORE_QUANTUM_DEPTH)
337 {
338 MagickStatusType
339 status;
340
341 QuantumAny
cristyb6d6f7c2010-06-09 13:17:57 +0000342 range;
cristyda06ed12009-10-14 18:36:54 +0000343
344 status=0;
cristyb6d6f7c2010-06-09 13:17:57 +0000345 range=GetQuantumRange(current_depth[id]);
cristyda06ed12009-10-14 18:36:54 +0000346 if ((channel & RedChannel) != 0)
cristy89bbeaf2011-04-22 20:25:27 +0000347 status|=GetRedPixelComponent(p) != ScaleAnyToQuantum(ScaleQuantumToAny(GetRedPixelComponent(p),
cristyb6d6f7c2010-06-09 13:17:57 +0000348 range),range);
cristyda06ed12009-10-14 18:36:54 +0000349 if ((channel & GreenChannel) != 0)
cristy89bbeaf2011-04-22 20:25:27 +0000350 status|=GetGreenPixelComponent(p) != ScaleAnyToQuantum(ScaleQuantumToAny(GetGreenPixelComponent(p),
cristyb6d6f7c2010-06-09 13:17:57 +0000351 range),range);
cristyda06ed12009-10-14 18:36:54 +0000352 if ((channel & BlueChannel) != 0)
cristy89bbeaf2011-04-22 20:25:27 +0000353 status|=GetBluePixelComponent(p) != ScaleAnyToQuantum(ScaleQuantumToAny(GetBluePixelComponent(p),
cristyb6d6f7c2010-06-09 13:17:57 +0000354 range),range);
cristyda06ed12009-10-14 18:36:54 +0000355 if (status == 0)
356 break;
357 current_depth[id]++;
358 }
359 p++;
360 }
361 depth=current_depth[0];
cristybb503372010-05-27 20:51:26 +0000362 for (id=1; id < (ssize_t) number_threads; id++)
cristyda06ed12009-10-14 18:36:54 +0000363 if (depth < current_depth[id])
364 depth=current_depth[id];
cristybb503372010-05-27 20:51:26 +0000365 current_depth=(size_t *) RelinquishMagickMemory(current_depth);
cristyda06ed12009-10-14 18:36:54 +0000366 return(depth);
367 }
368 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +0000369#if defined(MAGICKCORE_OPENMP_SUPPORT)
370 #pragma omp parallel for schedule(dynamic,4) shared(status)
cristyda06ed12009-10-14 18:36:54 +0000371#endif
cristybb503372010-05-27 20:51:26 +0000372 for (y=0; y < (ssize_t) image->rows; y++)
cristyda06ed12009-10-14 18:36:54 +0000373 {
cristy5c9e6f22010-09-17 17:31:01 +0000374 const int
375 id = GetOpenMPThreadId();
cristy6ebe97c2010-07-03 01:17:28 +0000376
cristyda06ed12009-10-14 18:36:54 +0000377 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000378 *restrict indexes;
cristyda06ed12009-10-14 18:36:54 +0000379
380 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000381 *restrict p;
cristyda06ed12009-10-14 18:36:54 +0000382
cristybb503372010-05-27 20:51:26 +0000383 register ssize_t
cristyda06ed12009-10-14 18:36:54 +0000384 x;
385
386 if (status == MagickFalse)
387 continue;
cristyda06ed12009-10-14 18:36:54 +0000388 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
389 if (p == (const PixelPacket *) NULL)
390 continue;
391 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +0000392 for (x=0; x < (ssize_t) image->columns; x++)
cristyda06ed12009-10-14 18:36:54 +0000393 {
394 while (current_depth[id] < MAGICKCORE_QUANTUM_DEPTH)
395 {
396 MagickStatusType
397 status;
398
399 QuantumAny
cristyb6d6f7c2010-06-09 13:17:57 +0000400 range;
cristyda06ed12009-10-14 18:36:54 +0000401
402 status=0;
cristyb6d6f7c2010-06-09 13:17:57 +0000403 range=GetQuantumRange(current_depth[id]);
cristyda06ed12009-10-14 18:36:54 +0000404 if ((channel & RedChannel) != 0)
cristy89bbeaf2011-04-22 20:25:27 +0000405 status|=GetRedPixelComponent(p) != ScaleAnyToQuantum(
406 ScaleQuantumToAny(GetRedPixelComponent(p),range),range);
cristyda06ed12009-10-14 18:36:54 +0000407 if ((channel & GreenChannel) != 0)
cristy89bbeaf2011-04-22 20:25:27 +0000408 status|=GetGreenPixelComponent(p) != ScaleAnyToQuantum(
409 ScaleQuantumToAny(GetGreenPixelComponent(p),range),range);
cristyda06ed12009-10-14 18:36:54 +0000410 if ((channel & BlueChannel) != 0)
cristy89bbeaf2011-04-22 20:25:27 +0000411 status|=GetBluePixelComponent(p) != ScaleAnyToQuantum(
412 ScaleQuantumToAny(GetBluePixelComponent(p),range),range);
cristyda06ed12009-10-14 18:36:54 +0000413 if (((channel & OpacityChannel) != 0) && (image->matte != MagickFalse))
cristy89bbeaf2011-04-22 20:25:27 +0000414 status|=GetOpacityPixelComponent(p) != ScaleAnyToQuantum(
415 ScaleQuantumToAny(GetOpacityPixelComponent(p),range),range);
cristyda06ed12009-10-14 18:36:54 +0000416 if (((channel & IndexChannel) != 0) &&
417 (image->colorspace == CMYKColorspace))
cristybaacd172011-04-30 23:11:03 +0000418 status|=GetIndexPixelComponent(indexes+x) !=
419 ScaleAnyToQuantum(ScaleQuantumToAny(GetIndexPixelComponent(indexes+
420 x),range),range);
cristyda06ed12009-10-14 18:36:54 +0000421 if (status == 0)
422 break;
423 current_depth[id]++;
424 }
425 p++;
426 }
427 if (current_depth[id] == MAGICKCORE_QUANTUM_DEPTH)
428 status=MagickFalse;
429 }
430 image_view=DestroyCacheView(image_view);
431 depth=current_depth[0];
cristybb503372010-05-27 20:51:26 +0000432 for (id=1; id < (ssize_t) number_threads; id++)
cristyda06ed12009-10-14 18:36:54 +0000433 if (depth < current_depth[id])
434 depth=current_depth[id];
cristybb503372010-05-27 20:51:26 +0000435 current_depth=(size_t *) RelinquishMagickMemory(current_depth);
cristyda06ed12009-10-14 18:36:54 +0000436 return(depth);
437}
438
439/*
440%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
441% %
442% %
443% %
444% G e t I m a g e Q u a n t u m D e p t h %
445% %
446% %
447% %
448%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
449%
450% GetImageQuantumDepth() returns the depth of the image rounded to a legal
451% quantum depth: 8, 16, or 32.
452%
453% The format of the GetImageQuantumDepth method is:
454%
cristybb503372010-05-27 20:51:26 +0000455% size_t GetImageQuantumDepth(const Image *image,
cristyda06ed12009-10-14 18:36:54 +0000456% const MagickBooleanType constrain)
457%
458% A description of each parameter follows:
459%
460% o image: the image.
461%
462% o constrain: A value other than MagickFalse, constrains the depth to
463% a maximum of MAGICKCORE_QUANTUM_DEPTH.
464%
465*/
466
467static inline double MagickMin(const double x,const double y)
468{
469 if (x < y)
470 return(x);
471 return(y);
472}
473
cristybb503372010-05-27 20:51:26 +0000474MagickExport size_t GetImageQuantumDepth(const Image *image,
cristyda06ed12009-10-14 18:36:54 +0000475 const MagickBooleanType constrain)
476{
cristybb503372010-05-27 20:51:26 +0000477 size_t
cristyda06ed12009-10-14 18:36:54 +0000478 depth;
479
480 depth=image->depth;
481 if (depth <= 8)
482 depth=8;
483 else
484 if (depth <= 16)
485 depth=16;
486 else
487 if (depth <= 32)
488 depth=32;
489 else
490 if (depth <= 64)
491 depth=64;
492 if (constrain != MagickFalse)
cristybb503372010-05-27 20:51:26 +0000493 depth=(size_t) MagickMin((double) depth,(double)
cristyda06ed12009-10-14 18:36:54 +0000494 MAGICKCORE_QUANTUM_DEPTH);
495 return(depth);
496}
497
498/*
499%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
500% %
501% %
502% %
503% G e t I m a g e T y p e %
504% %
505% %
506% %
507%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
508%
509% GetImageType() returns the potential type of image:
510%
511% Bilevel Grayscale GrayscaleMatte
512% Palette PaletteMatte TrueColor
513% TrueColorMatte ColorSeparation ColorSeparationMatte
514%
515% To ensure the image type matches its potential, use SetImageType():
516%
517% (void) SetImageType(image,GetImageType(image));
518%
519% The format of the GetImageType method is:
520%
521% ImageType GetImageType(const Image *image,ExceptionInfo *exception)
522%
523% A description of each parameter follows:
524%
525% o image: the image.
526%
527% o exception: return any errors or warnings in this structure.
528%
529*/
530MagickExport ImageType GetImageType(const Image *image,ExceptionInfo *exception)
531{
532 assert(image != (Image *) NULL);
533 assert(image->signature == MagickSignature);
534 if (image->debug != MagickFalse)
535 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
536 if (image->colorspace == CMYKColorspace)
537 {
538 if (image->matte == MagickFalse)
539 return(ColorSeparationType);
540 return(ColorSeparationMatteType);
541 }
542 if (IsMonochromeImage(image,exception) != MagickFalse)
543 return(BilevelType);
544 if (IsGrayImage(image,exception) != MagickFalse)
545 {
546 if (image->matte != MagickFalse)
547 return(GrayscaleMatteType);
548 return(GrayscaleType);
549 }
550 if (IsPaletteImage(image,exception) != MagickFalse)
551 {
552 if (image->matte != MagickFalse)
553 return(PaletteMatteType);
554 return(PaletteType);
555 }
556 if (image->matte != MagickFalse)
557 return(TrueColorMatteType);
558 return(TrueColorType);
559}
560
561/*
562%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
563% %
564% %
565% %
566% I s G r a y I m a g e %
567% %
568% %
569% %
570%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
571%
572% IsGrayImage() returns MagickTrue if all the pixels in the image have the
573% same red, green, and blue intensities.
574%
575% The format of the IsGrayImage method is:
576%
577% MagickBooleanType IsGrayImage(const Image *image,
578% ExceptionInfo *exception)
579%
580% A description of each parameter follows:
581%
582% o image: the image.
583%
584% o exception: return any errors or warnings in this structure.
585%
586*/
587MagickExport MagickBooleanType IsGrayImage(const Image *image,
588 ExceptionInfo *exception)
589{
cristy0910f242010-04-01 18:55:09 +0000590 CacheView
591 *image_view;
592
cristyda06ed12009-10-14 18:36:54 +0000593 ImageType
594 type;
595
596 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
cristy95802a72010-09-05 19:07:17 +0000602 ssize_t
603 y;
604
cristyda06ed12009-10-14 18:36:54 +0000605 assert(image != (Image *) NULL);
606 assert(image->signature == MagickSignature);
607 if (image->debug != MagickFalse)
608 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
609 if ((image->type == BilevelType) || (image->type == GrayscaleType) ||
610 (image->type == GrayscaleMatteType))
611 return(MagickTrue);
612 if (image->colorspace == CMYKColorspace)
613 return(MagickFalse);
614 type=BilevelType;
cristy0910f242010-04-01 18:55:09 +0000615 image_view=AcquireCacheView(image);
cristybb503372010-05-27 20:51:26 +0000616 for (y=0; y < (ssize_t) image->rows; y++)
cristyda06ed12009-10-14 18:36:54 +0000617 {
cristy0910f242010-04-01 18:55:09 +0000618 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
619 if (p == (const PixelPacket *) NULL)
620 break;
cristybb503372010-05-27 20:51:26 +0000621 for (x=0; x < (ssize_t) image->columns; x++)
cristyda06ed12009-10-14 18:36:54 +0000622 {
cristy0910f242010-04-01 18:55:09 +0000623 if (IsGrayPixel(p) == MagickFalse)
cristyda06ed12009-10-14 18:36:54 +0000624 {
cristy5f1c1ff2010-12-23 21:38:06 +0000625 type=UndefinedType;
cristyda06ed12009-10-14 18:36:54 +0000626 break;
cristy0910f242010-04-01 18:55:09 +0000627 }
628 if ((type == BilevelType) && (IsMonochromePixel(p) == MagickFalse))
629 type=GrayscaleType;
630 p++;
cristyda06ed12009-10-14 18:36:54 +0000631 }
cristy5f1c1ff2010-12-23 21:38:06 +0000632 if (type == UndefinedType)
cristyda06ed12009-10-14 18:36:54 +0000633 break;
cristyda06ed12009-10-14 18:36:54 +0000634 }
cristy0910f242010-04-01 18:55:09 +0000635 image_view=DestroyCacheView(image_view);
cristy5f1c1ff2010-12-23 21:38:06 +0000636 if (type == UndefinedType)
cristyda06ed12009-10-14 18:36:54 +0000637 return(MagickFalse);
638 ((Image *) image)->type=type;
639 if ((type == GrayscaleType) && (image->matte != MagickFalse))
640 ((Image *) image)->type=GrayscaleMatteType;
641 return(MagickTrue);
642}
643
644/*
645%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
646% %
647% %
648% %
649% I s M o n o c h r o m e I m a g e %
650% %
651% %
652% %
653%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
654%
655% IsMonochromeImage() returns MagickTrue if all the pixels in the image have
656% the same red, green, and blue intensities and the intensity is either
657% 0 or QuantumRange.
658%
659% The format of the IsMonochromeImage method is:
660%
661% MagickBooleanType IsMonochromeImage(const Image *image,
662% ExceptionInfo *exception)
663%
664% A description of each parameter follows:
665%
666% o image: the image.
667%
668% o exception: return any errors or warnings in this structure.
669%
670*/
671MagickExport MagickBooleanType IsMonochromeImage(const Image *image,
672 ExceptionInfo *exception)
673{
cristy0910f242010-04-01 18:55:09 +0000674 CacheView
675 *image_view;
676
cristyda06ed12009-10-14 18:36:54 +0000677 ImageType
678 type;
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
cristy9d314ff2011-03-09 01:30:28 +0000686 ssize_t
687 y;
688
cristyda06ed12009-10-14 18:36:54 +0000689 assert(image != (Image *) NULL);
690 assert(image->signature == MagickSignature);
691 if (image->debug != MagickFalse)
692 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
693 if (image->type == BilevelType)
694 return(MagickTrue);
695 if (image->colorspace == CMYKColorspace)
696 return(MagickFalse);
697 type=BilevelType;
cristy0910f242010-04-01 18:55:09 +0000698 image_view=AcquireCacheView(image);
cristybb503372010-05-27 20:51:26 +0000699 for (y=0; y < (ssize_t) image->rows; y++)
cristyda06ed12009-10-14 18:36:54 +0000700 {
cristy0910f242010-04-01 18:55:09 +0000701 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
702 if (p == (const PixelPacket *) NULL)
703 break;
cristybb503372010-05-27 20:51:26 +0000704 for (x=0; x < (ssize_t) image->columns; x++)
cristyda06ed12009-10-14 18:36:54 +0000705 {
cristy0910f242010-04-01 18:55:09 +0000706 if (IsMonochromePixel(p) == MagickFalse)
cristyda06ed12009-10-14 18:36:54 +0000707 {
cristy5f1c1ff2010-12-23 21:38:06 +0000708 type=UndefinedType;
cristyda06ed12009-10-14 18:36:54 +0000709 break;
cristy0910f242010-04-01 18:55:09 +0000710 }
711 p++;
cristyda06ed12009-10-14 18:36:54 +0000712 }
cristy5f1c1ff2010-12-23 21:38:06 +0000713 if (type == UndefinedType)
cristyda06ed12009-10-14 18:36:54 +0000714 break;
cristyda06ed12009-10-14 18:36:54 +0000715 }
cristy0910f242010-04-01 18:55:09 +0000716 image_view=DestroyCacheView(image_view);
cristy5f1c1ff2010-12-23 21:38:06 +0000717 if (type == UndefinedType)
cristyda06ed12009-10-14 18:36:54 +0000718 return(MagickFalse);
719 ((Image *) image)->type=type;
720 return(MagickTrue);
721}
722
723/*
724%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
725% %
726% %
727% %
728% I s O p a q u e I m a g e %
729% %
730% %
731% %
732%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
733%
734% IsOpaqueImage() returns MagickTrue if none of the pixels in the image have
735% an opacity value other than opaque (0).
736%
737% The format of the IsOpaqueImage method is:
738%
739% MagickBooleanType IsOpaqueImage(const Image *image,
740% ExceptionInfo *exception)
741%
742% A description of each parameter follows:
743%
744% o image: the image.
745%
746% o exception: return any errors or warnings in this structure.
747%
748*/
749MagickExport MagickBooleanType IsOpaqueImage(const Image *image,
750 ExceptionInfo *exception)
751{
cristyc4c8d132010-01-07 01:58:38 +0000752 CacheView
753 *image_view;
754
cristyda06ed12009-10-14 18:36:54 +0000755 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
cristy9d314ff2011-03-09 01:30:28 +0000761 ssize_t
762 y;
763
cristyda06ed12009-10-14 18:36:54 +0000764 /*
765 Determine if image is opaque.
766 */
767 assert(image != (Image *) NULL);
768 assert(image->signature == MagickSignature);
769 if (image->debug != MagickFalse)
770 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
771 if (image->matte == MagickFalse)
772 return(MagickTrue);
773 image_view=AcquireCacheView(image);
cristybb503372010-05-27 20:51:26 +0000774 for (y=0; y < (ssize_t) image->rows; y++)
cristyda06ed12009-10-14 18:36:54 +0000775 {
776 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
777 if (p == (const PixelPacket *) NULL)
778 break;
cristybb503372010-05-27 20:51:26 +0000779 for (x=0; x < (ssize_t) image->columns; x++)
cristyda06ed12009-10-14 18:36:54 +0000780 {
cristy89bbeaf2011-04-22 20:25:27 +0000781 if (GetOpacityPixelComponent(p) != OpaqueOpacity)
cristyda06ed12009-10-14 18:36:54 +0000782 break;
783 p++;
784 }
cristybb503372010-05-27 20:51:26 +0000785 if (x < (ssize_t) image->columns)
cristyda06ed12009-10-14 18:36:54 +0000786 break;
787 }
788 image_view=DestroyCacheView(image_view);
cristybb503372010-05-27 20:51:26 +0000789 return(y < (ssize_t) image->rows ? MagickFalse : MagickTrue);
cristyda06ed12009-10-14 18:36:54 +0000790}
791
792/*
793%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
794% %
795% %
796% %
797% S e t I m a g e C h a n n e l D e p t h %
798% %
799% %
800% %
801%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
802%
803% SetImageChannelDepth() sets the depth of the image.
804%
805% The format of the SetImageChannelDepth method is:
806%
cristybb503372010-05-27 20:51:26 +0000807% MagickBooleanType SetImageDepth(Image *image,const size_t depth)
cristyda06ed12009-10-14 18:36:54 +0000808% MagickBooleanType SetImageChannelDepth(Image *image,
cristybb503372010-05-27 20:51:26 +0000809% const ChannelType channel,const size_t depth)
cristyda06ed12009-10-14 18:36:54 +0000810%
811% A description of each parameter follows:
812%
813% o image: the image.
814%
815% o channel: the channel.
816%
817% o depth: the image depth.
818%
819*/
cristyda06ed12009-10-14 18:36:54 +0000820MagickExport MagickBooleanType SetImageDepth(Image *image,
cristybb503372010-05-27 20:51:26 +0000821 const size_t depth)
cristyda06ed12009-10-14 18:36:54 +0000822{
cristy9a9230e2011-04-26 14:56:14 +0000823 return(SetImageChannelDepth(image,CompositeChannels,depth));
cristyda06ed12009-10-14 18:36:54 +0000824}
825
826MagickExport MagickBooleanType SetImageChannelDepth(Image *image,
cristybb503372010-05-27 20:51:26 +0000827 const ChannelType channel,const size_t depth)
cristyda06ed12009-10-14 18:36:54 +0000828{
cristyc4c8d132010-01-07 01:58:38 +0000829 CacheView
830 *image_view;
831
cristyda06ed12009-10-14 18:36:54 +0000832 ExceptionInfo
833 *exception;
834
cristyda06ed12009-10-14 18:36:54 +0000835 MagickBooleanType
836 status;
837
838 QuantumAny
cristyb6d6f7c2010-06-09 13:17:57 +0000839 range;
cristyda06ed12009-10-14 18:36:54 +0000840
cristy9d314ff2011-03-09 01:30:28 +0000841 ssize_t
842 y;
843
cristyda06ed12009-10-14 18:36:54 +0000844 assert(image != (Image *) NULL);
845 if (image->debug != MagickFalse)
846 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
847 assert(image->signature == MagickSignature);
cristybb503372010-05-27 20:51:26 +0000848 if (GetImageDepth(image,&image->exception) <= (size_t)
cristyda06ed12009-10-14 18:36:54 +0000849 MagickMin((double) depth,(double) MAGICKCORE_QUANTUM_DEPTH))
850 {
851 image->depth=depth;
852 return(MagickTrue);
853 }
854 /*
855 Scale pixels to desired depth.
856 */
857 status=MagickTrue;
cristyb6d6f7c2010-06-09 13:17:57 +0000858 range=GetQuantumRange(depth);
cristyda06ed12009-10-14 18:36:54 +0000859 exception=(&image->exception);
860 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +0000861#if defined(MAGICKCORE_OPENMP_SUPPORT)
862 #pragma omp parallel for schedule(dynamic,4) shared(status)
cristyda06ed12009-10-14 18:36:54 +0000863#endif
cristybb503372010-05-27 20:51:26 +0000864 for (y=0; y < (ssize_t) image->rows; y++)
cristyda06ed12009-10-14 18:36:54 +0000865 {
866 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000867 *restrict indexes;
cristyda06ed12009-10-14 18:36:54 +0000868
cristybb503372010-05-27 20:51:26 +0000869 register ssize_t
cristyda06ed12009-10-14 18:36:54 +0000870 x;
871
872 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000873 *restrict q;
cristyda06ed12009-10-14 18:36:54 +0000874
875 if (status == MagickFalse)
876 continue;
877 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
878 exception);
879 if (q == (PixelPacket *) NULL)
880 {
881 status=MagickFalse;
882 continue;
883 }
884 indexes=GetCacheViewAuthenticIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +0000885 for (x=0; x < (ssize_t) image->columns; x++)
cristyda06ed12009-10-14 18:36:54 +0000886 {
887 if ((channel & RedChannel) != 0)
cristyb40bd892011-04-23 00:52:38 +0000888 SetRedPixelComponent(q,ScaleAnyToQuantum(ScaleQuantumToAny(
889 GetRedPixelComponent(q),range),range));
cristyda06ed12009-10-14 18:36:54 +0000890 if ((channel & GreenChannel) != 0)
cristyb40bd892011-04-23 00:52:38 +0000891 SetGreenPixelComponent(q,ScaleAnyToQuantum(ScaleQuantumToAny(
892 GetGreenPixelComponent(q),range),range));
cristyda06ed12009-10-14 18:36:54 +0000893 if ((channel & BlueChannel) != 0)
cristyb40bd892011-04-23 00:52:38 +0000894 SetBluePixelComponent(q,ScaleAnyToQuantum(ScaleQuantumToAny(
895 GetBluePixelComponent(q),range),range));
cristyda06ed12009-10-14 18:36:54 +0000896 if (((channel & OpacityChannel) != 0) && (image->matte != MagickFalse))
cristyb40bd892011-04-23 00:52:38 +0000897 SetOpacityPixelComponent(q,ScaleAnyToQuantum(ScaleQuantumToAny(
898 GetOpacityPixelComponent(q),range),range));
cristyda06ed12009-10-14 18:36:54 +0000899 if (((channel & IndexChannel) != 0) &&
900 (image->colorspace == CMYKColorspace))
cristyb40bd892011-04-23 00:52:38 +0000901 SetIndexPixelComponent(indexes+x,ScaleAnyToQuantum(ScaleQuantumToAny(
902 GetIndexPixelComponent(indexes+x),range),range));
cristyda06ed12009-10-14 18:36:54 +0000903 q++;
904 }
905 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
906 {
907 status=MagickFalse;
908 continue;
909 }
910 }
911 image_view=DestroyCacheView(image_view);
912 if (image->storage_class == PseudoClass)
913 {
cristybb503372010-05-27 20:51:26 +0000914 register ssize_t
cristyda06ed12009-10-14 18:36:54 +0000915 i;
916
917 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000918 *restrict p;
cristyda06ed12009-10-14 18:36:54 +0000919
920 p=image->colormap;
cristyb5d5f722009-11-04 03:03:49 +0000921#if defined(MAGICKCORE_OPENMP_SUPPORT)
922 #pragma omp parallel for schedule(dynamic,4) shared(status)
cristyda06ed12009-10-14 18:36:54 +0000923#endif
cristybb503372010-05-27 20:51:26 +0000924 for (i=0; i < (ssize_t) image->colors; i++)
cristyda06ed12009-10-14 18:36:54 +0000925 {
926 if ((channel & RedChannel) != 0)
cristyd05ecd12011-04-22 20:44:42 +0000927 GetRedPixelComponent(p)=ScaleAnyToQuantum(ScaleQuantumToAny(GetRedPixelComponent(p),range),range);
cristyda06ed12009-10-14 18:36:54 +0000928 if ((channel & GreenChannel) != 0)
cristyd05ecd12011-04-22 20:44:42 +0000929 GetGreenPixelComponent(p)=ScaleAnyToQuantum(ScaleQuantumToAny(GetGreenPixelComponent(p),range),range);
cristyda06ed12009-10-14 18:36:54 +0000930 if ((channel & BlueChannel) != 0)
cristyd05ecd12011-04-22 20:44:42 +0000931 GetBluePixelComponent(p)=ScaleAnyToQuantum(ScaleQuantumToAny(GetBluePixelComponent(p),range),range);
cristyda06ed12009-10-14 18:36:54 +0000932 if ((channel & OpacityChannel) != 0)
cristyd05ecd12011-04-22 20:44:42 +0000933 GetOpacityPixelComponent(p)=ScaleAnyToQuantum(ScaleQuantumToAny(GetOpacityPixelComponent(p),range),
cristyb6d6f7c2010-06-09 13:17:57 +0000934 range);
cristyda06ed12009-10-14 18:36:54 +0000935 p++;
936 }
937 }
938 image->depth=depth;
939 return(status);
940}