blob: a89a81dfc019696331a98bc65a43af722d96d7ee [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{
123 long
124 y;
125
126 MagickBooleanType
127 status;
128
129 MagickPixelPacket
130 target[3],
131 zero;
132
133 RectangleInfo
134 bounds;
135
136 register const PixelPacket
137 *p;
138
139 CacheView
140 *image_view;
141
142 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;
148 bounds.x=(long) image->columns;
149 bounds.y=(long) image->rows;
150 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]);
161 p=GetCacheViewVirtualPixels(image_view,(long) image->columns-1,0,1,1,
162 exception);
163 SetMagickPixelPacket(image,p,GetCacheViewAuthenticIndexQueue(image_view),
164 &target[1]);
165 GetMagickPixelPacket(image,&target[2]);
166 p=GetCacheViewVirtualPixels(image_view,0,(long) image->rows-1,1,1,exception);
167 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
174 for (y=0; y < (long) image->rows; y++)
175 {
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
188 register long
189 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;
205 for (x=0; x < (long) image->columns; x++)
206 {
207 SetMagickPixelPacket(image,p,indexes+x,&pixel);
208 if ((x < bounding_box.x) &&
209 (IsMagickColorSimilar(&pixel,&target[0]) == MagickFalse))
210 bounding_box.x=x;
211 if ((x > (long) bounding_box.width) &&
212 (IsMagickColorSimilar(&pixel,&target[1]) == MagickFalse))
213 bounding_box.width=(unsigned long) x;
214 if ((y < bounding_box.y) &&
215 (IsMagickColorSimilar(&pixel,&target[0]) == MagickFalse))
216 bounding_box.y=y;
217 if ((y > (long) bounding_box.height) &&
218 (IsMagickColorSimilar(&pixel,&target[2]) == MagickFalse))
219 bounding_box.height=(unsigned long) y;
220 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%
263% unsigned long GetImageDepth(const Image *image,ExceptionInfo *exception)
264% unsigned long GetImageChannelDepth(const Image *image,
265% 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
277MagickExport unsigned long GetImageDepth(const Image *image,
278 ExceptionInfo *exception)
279{
280 return(GetImageChannelDepth(image,AllChannels,exception));
281}
282
283MagickExport unsigned long GetImageChannelDepth(const Image *image,
284 const ChannelType channel,ExceptionInfo *exception)
285{
286 long
287 y;
288
289 MagickBooleanType
290 status;
291
292 register long
293 id;
294
295 unsigned long
296 *current_depth,
297 depth,
298 number_threads;
299
300 CacheView
301 *image_view;
302
303 /*
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();
311 current_depth=(unsigned long *) AcquireQuantumMemory(number_threads,
312 sizeof(*current_depth));
313 if (current_depth == (unsigned long *) NULL)
314 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
315 status=MagickTrue;
316 for (id=0; id < (long) number_threads; id++)
317 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
323 register long
324 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
330 for (i=0; i < (long) image->colors; i++)
331 {
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];
361 for (id=1; id < (long) number_threads; id++)
362 if (depth < current_depth[id])
363 depth=current_depth[id];
364 current_depth=(unsigned long *) RelinquishMagickMemory(current_depth);
365 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
371 for (y=0; y < (long) image->rows; y++)
372 {
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
379 register long
380 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);
390 for (x=0; x < (long) image->columns; x++)
391 {
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];
429 for (id=1; id < (long) number_threads; id++)
430 if (depth < current_depth[id])
431 depth=current_depth[id];
432 current_depth=(unsigned long *) RelinquishMagickMemory(current_depth);
433 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%
452% unsigned long GetImageQuantumDepth(const Image *image,
453% 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
471MagickExport unsigned long GetImageQuantumDepth(const Image *image,
472 const MagickBooleanType constrain)
473{
474 unsigned long
475 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)
490 depth=(unsigned long) MagickMin((double) depth,(double)
491 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{
587 ImageType
588 type;
589
590 register const PixelPacket
591 *p;
592
593 assert(image != (Image *) NULL);
594 assert(image->signature == MagickSignature);
595 if (image->debug != MagickFalse)
596 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
597 if ((image->type == BilevelType) || (image->type == GrayscaleType) ||
598 (image->type == GrayscaleMatteType))
599 return(MagickTrue);
600 if (image->colorspace == CMYKColorspace)
601 return(MagickFalse);
602 type=BilevelType;
603 switch (image->storage_class)
604 {
605 case DirectClass:
606 case UndefinedClass:
607 {
608 long
609 y;
610
611 register long
612 x;
613
614 CacheView
615 *image_view;
616
617 image_view=AcquireCacheView(image);
618 for (y=0; y < (long) image->rows; y++)
619 {
620 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
621 if (p == (const PixelPacket *) NULL)
622 break;
623 for (x=0; x < (long) image->columns; x++)
624 {
625 if (IsGrayPixel(p) == MagickFalse)
626 {
627 type=UndefinedType;
628 break;
629 }
630 if ((type == BilevelType) && (IsMonochromePixel(p) == MagickFalse))
631 type=GrayscaleType;
632 p++;
633 }
634 if (type == UndefinedType)
635 break;
636 }
637 image_view=DestroyCacheView(image_view);
638 break;
639 }
640 case PseudoClass:
641 {
642 register long
643 i;
644
645 p=image->colormap;
646 for (i=0; i < (long) image->colors; i++)
647 {
648 if (IsGrayPixel(p) == MagickFalse)
649 {
650 type=UndefinedType;
651 break;
652 }
653 if ((type == BilevelType) && (IsMonochromePixel(p) == MagickFalse))
654 type=GrayscaleType;
655 p++;
656 }
657 break;
658 }
659 }
660 if (type == UndefinedType)
661 return(MagickFalse);
662 ((Image *) image)->type=type;
663 if ((type == GrayscaleType) && (image->matte != MagickFalse))
664 ((Image *) image)->type=GrayscaleMatteType;
665 return(MagickTrue);
666}
667
668/*
669%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
670% %
671% %
672% %
673% I s M o n o c h r o m e I m a g e %
674% %
675% %
676% %
677%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
678%
679% IsMonochromeImage() returns MagickTrue if all the pixels in the image have
680% the same red, green, and blue intensities and the intensity is either
681% 0 or QuantumRange.
682%
683% The format of the IsMonochromeImage method is:
684%
685% MagickBooleanType IsMonochromeImage(const Image *image,
686% ExceptionInfo *exception)
687%
688% A description of each parameter follows:
689%
690% o image: the image.
691%
692% o exception: return any errors or warnings in this structure.
693%
694*/
695MagickExport MagickBooleanType IsMonochromeImage(const Image *image,
696 ExceptionInfo *exception)
697{
698 ImageType
699 type;
700
701 register const PixelPacket
702 *p;
703
704 assert(image != (Image *) NULL);
705 assert(image->signature == MagickSignature);
706 if (image->debug != MagickFalse)
707 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
708 if (image->type == BilevelType)
709 return(MagickTrue);
710 if (image->colorspace == CMYKColorspace)
711 return(MagickFalse);
712 type=BilevelType;
713 switch (image->storage_class)
714 {
715 case DirectClass:
716 case UndefinedClass:
717 {
718 long
719 y;
720
721 register long
722 x;
723
724 CacheView
725 *image_view;
726
727 image_view=AcquireCacheView(image);
728 for (y=0; y < (long) image->rows; y++)
729 {
730 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
731 if (p == (const PixelPacket *) NULL)
732 break;
733 for (x=0; x < (long) image->columns; x++)
734 {
735 if (IsMonochromePixel(p) == MagickFalse)
736 {
737 type=UndefinedType;
738 break;
739 }
740 p++;
741 }
742 if (type == UndefinedType)
743 break;
744 }
745 image_view=DestroyCacheView(image_view);
746 if (y == (long) image->rows)
747 ((Image *) image)->type=BilevelType;
748 break;
749 }
750 case PseudoClass:
751 {
752 register long
753 i;
754
755 p=image->colormap;
756 for (i=0; i < (long) image->colors; i++)
757 {
758 if (IsMonochromePixel(p) == MagickFalse)
759 {
760 type=UndefinedType;
761 break;
762 }
763 p++;
764 }
765 break;
766 }
767 }
768 if (type == UndefinedType)
769 return(MagickFalse);
770 ((Image *) image)->type=type;
771 return(MagickTrue);
772}
773
774/*
775%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
776% %
777% %
778% %
779% I s O p a q u e I m a g e %
780% %
781% %
782% %
783%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
784%
785% IsOpaqueImage() returns MagickTrue if none of the pixels in the image have
786% an opacity value other than opaque (0).
787%
788% The format of the IsOpaqueImage method is:
789%
790% MagickBooleanType IsOpaqueImage(const Image *image,
791% ExceptionInfo *exception)
792%
793% A description of each parameter follows:
794%
795% o image: the image.
796%
797% o exception: return any errors or warnings in this structure.
798%
799*/
800MagickExport MagickBooleanType IsOpaqueImage(const Image *image,
801 ExceptionInfo *exception)
802{
803 long
804 y;
805
806 register const PixelPacket
807 *p;
808
809 register long
810 x;
811
812 CacheView
813 *image_view;
814
815 /*
816 Determine if image is opaque.
817 */
818 assert(image != (Image *) NULL);
819 assert(image->signature == MagickSignature);
820 if (image->debug != MagickFalse)
821 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
822 if (image->matte == MagickFalse)
823 return(MagickTrue);
824 image_view=AcquireCacheView(image);
825 for (y=0; y < (long) image->rows; y++)
826 {
827 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
828 if (p == (const PixelPacket *) NULL)
829 break;
830 for (x=0; x < (long) image->columns; x++)
831 {
832 if (p->opacity != OpaqueOpacity)
833 break;
834 p++;
835 }
836 if (x < (long) image->columns)
837 break;
838 }
839 image_view=DestroyCacheView(image_view);
840 return(y < (long) image->rows ? MagickFalse : MagickTrue);
841}
842
843/*
844%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
845% %
846% %
847% %
848% S e t I m a g e C h a n n e l D e p t h %
849% %
850% %
851% %
852%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
853%
854% SetImageChannelDepth() sets the depth of the image.
855%
856% The format of the SetImageChannelDepth method is:
857%
858% MagickBooleanType SetImageDepth(Image *image,const unsigned long depth)
859% MagickBooleanType SetImageChannelDepth(Image *image,
860% const ChannelType channel,const unsigned long depth)
861%
862% A description of each parameter follows:
863%
864% o image: the image.
865%
866% o channel: the channel.
867%
868% o depth: the image depth.
869%
870*/
871
872MagickExport MagickBooleanType SetImageDepth(Image *image,
873 const unsigned long depth)
874{
875 return(SetImageChannelDepth(image,AllChannels,depth));
876}
877
878MagickExport MagickBooleanType SetImageChannelDepth(Image *image,
879 const ChannelType channel,const unsigned long depth)
880{
881 ExceptionInfo
882 *exception;
883
884 long
885 y;
886
887 MagickBooleanType
888 status;
889
890 QuantumAny
891 range;
892
893 CacheView
894 *image_view;
895
896 assert(image != (Image *) NULL);
897 if (image->debug != MagickFalse)
898 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
899 assert(image->signature == MagickSignature);
900 if (GetImageDepth(image,&image->exception) <= (unsigned long)
901 MagickMin((double) depth,(double) MAGICKCORE_QUANTUM_DEPTH))
902 {
903 image->depth=depth;
904 return(MagickTrue);
905 }
906 /*
907 Scale pixels to desired depth.
908 */
909 status=MagickTrue;
910 range=GetQuantumRange(depth);
911 exception=(&image->exception);
912 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +0000913#if defined(MAGICKCORE_OPENMP_SUPPORT)
914 #pragma omp parallel for schedule(dynamic,4) shared(status)
cristyda06ed12009-10-14 18:36:54 +0000915#endif
916 for (y=0; y < (long) image->rows; y++)
917 {
918 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000919 *restrict indexes;
cristyda06ed12009-10-14 18:36:54 +0000920
921 register long
922 x;
923
924 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000925 *restrict q;
cristyda06ed12009-10-14 18:36:54 +0000926
927 if (status == MagickFalse)
928 continue;
929 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
930 exception);
931 if (q == (PixelPacket *) NULL)
932 {
933 status=MagickFalse;
934 continue;
935 }
936 indexes=GetCacheViewAuthenticIndexQueue(image_view);
937 for (x=0; x < (long) image->columns; x++)
938 {
939 if ((channel & RedChannel) != 0)
940 q->red=ScaleAnyToQuantum(ScaleQuantumToAny(q->red,range),range);
941 if ((channel & GreenChannel) != 0)
942 q->green=ScaleAnyToQuantum(ScaleQuantumToAny(q->green,range),range);
943 if ((channel & BlueChannel) != 0)
944 q->blue=ScaleAnyToQuantum(ScaleQuantumToAny(q->blue,range),range);
945 if (((channel & OpacityChannel) != 0) && (image->matte != MagickFalse))
946 q->opacity=ScaleAnyToQuantum(ScaleQuantumToAny(q->opacity,range),range);
947 if (((channel & IndexChannel) != 0) &&
948 (image->colorspace == CMYKColorspace))
949 indexes[x]=ScaleAnyToQuantum(ScaleQuantumToAny(indexes[x],range),range);
950 q++;
951 }
952 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
953 {
954 status=MagickFalse;
955 continue;
956 }
957 }
958 image_view=DestroyCacheView(image_view);
959 if (image->storage_class == PseudoClass)
960 {
961 QuantumAny
962 range;
963
964 register long
965 i;
966
967 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000968 *restrict p;
cristyda06ed12009-10-14 18:36:54 +0000969
970 p=image->colormap;
971 range=GetQuantumRange(depth);
cristyb5d5f722009-11-04 03:03:49 +0000972#if defined(MAGICKCORE_OPENMP_SUPPORT)
973 #pragma omp parallel for schedule(dynamic,4) shared(status)
cristyda06ed12009-10-14 18:36:54 +0000974#endif
975 for (i=0; i < (long) image->colors; i++)
976 {
977 if ((channel & RedChannel) != 0)
978 p->red=ScaleAnyToQuantum(ScaleQuantumToAny(p->red,range),range);
979 if ((channel & GreenChannel) != 0)
980 p->green=ScaleAnyToQuantum(ScaleQuantumToAny(p->green,range),range);
981 if ((channel & BlueChannel) != 0)
982 p->blue=ScaleAnyToQuantum(ScaleQuantumToAny(p->blue,range),range);
983 if ((channel & OpacityChannel) != 0)
984 p->opacity=ScaleAnyToQuantum(ScaleQuantumToAny(p->opacity,range),
985 range);
986 p++;
987 }
988 }
989 image->depth=depth;
990 return(status);
991}