]> granicus.if.org Git - imagemagick/blob - magick/attribute.c
(no commit message)
[imagemagick] / magick / attribute.c
1 /*
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 %                                                                             %
20 %  Copyright 1999-2010 ImageMagick Studio LLC, a non-profit organization      %
21 %  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 \f
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 \f
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 */
120 MagickExport RectangleInfo GetImageBoundingBox(const Image *image,
121   ExceptionInfo *exception)
122 {
123   CacheView
124     *image_view;
125
126   ssize_t
127     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
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=(ssize_t) image->columns;
149   bounds.y=(ssize_t) 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,(ssize_t) 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,(ssize_t) image->rows-1,1,1,exception);
167   SetMagickPixelPacket(image,p,GetCacheViewAuthenticIndexQueue(image_view),
168     &target[2]);
169   status=MagickTrue;
170   GetMagickPixelPacket(image,&zero);
171 #if defined(MAGICKCORE_OPENMP_SUPPORT)
172   #pragma omp parallel for schedule(dynamic,4) shared(status)
173 #endif
174   for (y=0; y < (ssize_t) image->rows; y++)
175   {
176     MagickPixelPacket
177       pixel;
178
179     RectangleInfo
180       bounding_box;
181
182     register const IndexPacket
183       *restrict indexes;
184
185     register const PixelPacket
186       *restrict p;
187
188     register ssize_t
189       x;
190
191     if (status == MagickFalse)
192       continue;
193 #if defined(MAGICKCORE_OPENMP_SUPPORT)
194 #  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 < (ssize_t) 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 > (ssize_t) bounding_box.width) &&
212           (IsMagickColorSimilar(&pixel,&target[1]) == MagickFalse))
213         bounding_box.width=(size_t) x;
214       if ((y < bounding_box.y) &&
215           (IsMagickColorSimilar(&pixel,&target[0]) == MagickFalse))
216         bounding_box.y=y;
217       if ((y > (ssize_t) bounding_box.height) &&
218           (IsMagickColorSimilar(&pixel,&target[2]) == MagickFalse))
219         bounding_box.height=(size_t) y;
220       p++;
221     }
222 #if defined(MAGICKCORE_OPENMP_SUPPORT)
223 #  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 \f
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 %      size_t GetImageDepth(const Image *image,ExceptionInfo *exception)
264 %      size_t 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
277 static 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
287 MagickExport size_t GetImageDepth(const Image *image,ExceptionInfo *exception)
288 {
289   return(GetImageChannelDepth(image,AllChannels,exception));
290 }
291
292 MagickExport size_t GetImageChannelDepth(const Image *image,
293   const ChannelType channel,ExceptionInfo *exception)
294 {
295   CacheView
296     *image_view;
297
298   ssize_t
299     y;
300
301   MagickBooleanType
302     status;
303
304   register ssize_t
305     id;
306
307   size_t
308     *current_depth,
309     depth,
310     number_threads;
311
312   /*
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();
320   current_depth=(size_t *) AcquireQuantumMemory(number_threads,
321     sizeof(*current_depth));
322   if (current_depth == (size_t *) NULL)
323     ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
324   status=MagickTrue;
325   for (id=0; id < (ssize_t) number_threads; id++)
326     current_depth[id]=1;
327   if ((image->storage_class == PseudoClass) && (image->matte == MagickFalse))
328     {
329       register const PixelPacket
330         *restrict p;
331
332       register ssize_t
333         i;
334
335       p=image->colormap;
336 #if defined(MAGICKCORE_OPENMP_SUPPORT)
337   #pragma omp parallel for schedule(dynamic,4) shared(status)
338 #endif
339       for (i=0; i < (ssize_t) image->colors; i++)
340       {
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
350             scale;
351
352           status=0;
353           scale=1;
354           if (current_depth[id] < QuantumDepth)
355             scale=QuantumRange/((QuantumAny) QuantumRange >> (QuantumDepth-
356               current_depth[id]));
357           if ((channel & RedChannel) != 0)
358             status|=(QuantumAny) p->red != GetPixelDepth(p->red,scale);
359           if ((channel & GreenChannel) != 0)
360             status|=(QuantumAny) p->green != GetPixelDepth(p->green,scale);
361           if ((channel & BlueChannel) != 0)
362             status|=(QuantumAny) p->blue != GetPixelDepth(p->blue,scale);
363           if (status == 0)
364             break;
365           current_depth[id]++;
366         }
367         p++;
368       }
369       depth=current_depth[0];
370       for (id=1; id < (ssize_t) number_threads; id++)
371         if (depth < current_depth[id])
372           depth=current_depth[id];
373       current_depth=(size_t *) RelinquishMagickMemory(current_depth);
374       return(depth);
375     }
376   image_view=AcquireCacheView(image);
377 #if defined(MAGICKCORE_OPENMP_SUPPORT)
378   #pragma omp parallel for schedule(dynamic,4) shared(status)
379 #endif
380   for (y=0; y < (ssize_t) image->rows; y++)
381   {
382     register const IndexPacket
383       *restrict indexes;
384
385     register const PixelPacket
386       *restrict p;
387
388     register ssize_t
389       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);
399     for (x=0; x < (ssize_t) image->columns; x++)
400     {
401       while (current_depth[id] < MAGICKCORE_QUANTUM_DEPTH)
402       {
403         MagickStatusType
404           status;
405
406         QuantumAny
407           scale;
408
409         status=0;
410         scale=1;
411         if (current_depth[id] < QuantumDepth)
412           scale=QuantumRange/((QuantumAny) QuantumRange >> (QuantumDepth-
413             current_depth[id]));
414         if ((channel & RedChannel) != 0)
415           status|=(QuantumAny) p->red != GetPixelDepth(p->red,scale);
416         if ((channel & GreenChannel) != 0)
417           status|=(QuantumAny) p->green != GetPixelDepth(p->green,scale);
418         if ((channel & BlueChannel) != 0)
419           status|=(QuantumAny) p->blue != GetPixelDepth(p->blue,scale);
420         if (((channel & OpacityChannel) != 0) && (image->matte != MagickFalse))
421           status|=(QuantumAny) p->opacity != GetPixelDepth(p->opacity,scale);
422         if (((channel & IndexChannel) != 0) &&
423             (image->colorspace == CMYKColorspace))
424           status|=(QuantumAny) indexes[x] != GetPixelDepth(indexes[x],scale);
425         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];
436   for (id=1; id < (ssize_t) number_threads; id++)
437     if (depth < current_depth[id])
438       depth=current_depth[id];
439   current_depth=(size_t *) RelinquishMagickMemory(current_depth);
440   return(depth);
441 }
442 \f
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 %
459 %      size_t GetImageQuantumDepth(const Image *image,
460 %        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
471 static inline double MagickMin(const double x,const double y)
472 {
473   if (x < y)
474     return(x);
475   return(y);
476 }
477
478 MagickExport size_t GetImageQuantumDepth(const Image *image,
479   const MagickBooleanType constrain)
480 {
481   size_t
482     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)
497     depth=(size_t) MagickMin((double) depth,(double)
498       MAGICKCORE_QUANTUM_DEPTH);
499   return(depth);
500 }
501 \f
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 */
534 MagickExport 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 \f
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 */
591 MagickExport MagickBooleanType IsGrayImage(const Image *image,
592   ExceptionInfo *exception)
593 {
594   CacheView
595     *image_view;
596
597   ImageType
598     type;
599
600   ssize_t
601     y;
602
603   register const PixelPacket
604     *p;
605
606   register ssize_t
607     x;
608
609   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;
619   image_view=AcquireCacheView(image);
620   for (y=0; y < (ssize_t) image->rows; y++)
621   {
622     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
623     if (p == (const PixelPacket *) NULL)
624       break;
625     for (x=0; x < (ssize_t) image->columns; x++)
626     {
627       if (IsGrayPixel(p) == MagickFalse)
628         {
629           type=UndefinedType;
630           break;
631         }
632       if ((type == BilevelType) && (IsMonochromePixel(p) == MagickFalse))
633         type=GrayscaleType;
634       p++;
635     }
636     if (type == UndefinedType)
637       break;
638   }
639   image_view=DestroyCacheView(image_view);
640   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 \f
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 */
675 MagickExport MagickBooleanType IsMonochromeImage(const Image *image,
676   ExceptionInfo *exception)
677 {
678   CacheView
679     *image_view;
680
681   ImageType
682     type;
683
684   ssize_t
685     y;
686
687   register ssize_t
688     x;
689
690   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;
702   image_view=AcquireCacheView(image);
703   for (y=0; y < (ssize_t) image->rows; y++)
704   {
705     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
706     if (p == (const PixelPacket *) NULL)
707       break;
708     for (x=0; x < (ssize_t) image->columns; x++)
709     {
710       if (IsMonochromePixel(p) == MagickFalse)
711         {
712           type=UndefinedType;
713           break;
714         }
715       p++;
716     }
717     if (type == UndefinedType)
718       break;
719   }
720   image_view=DestroyCacheView(image_view);
721   if (type == UndefinedType)
722     return(MagickFalse);
723   ((Image *) image)->type=type;
724   return(MagickTrue);
725 }
726 \f
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 */
753 MagickExport MagickBooleanType IsOpaqueImage(const Image *image,
754   ExceptionInfo *exception)
755 {
756   CacheView
757     *image_view;
758
759   ssize_t
760     y;
761
762   register const PixelPacket
763     *p;
764
765   register ssize_t
766     x;
767
768   /*
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);
778   for (y=0; y < (ssize_t) image->rows; y++)
779   {
780     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
781     if (p == (const PixelPacket *) NULL)
782       break;
783     for (x=0; x < (ssize_t) image->columns; x++)
784     {
785       if (p->opacity != OpaqueOpacity)
786         break;
787       p++;
788     }
789     if (x < (ssize_t) image->columns)
790      break;
791   }
792   image_view=DestroyCacheView(image_view);
793   return(y < (ssize_t) image->rows ? MagickFalse : MagickTrue);
794 }
795 \f
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 %
811 %      MagickBooleanType SetImageDepth(Image *image,const size_t depth)
812 %      MagickBooleanType SetImageChannelDepth(Image *image,
813 %        const ChannelType channel,const size_t depth)
814 %
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
825 static inline Quantum SetPixelDepth(const Quantum pixel,
826   const QuantumAny scale)
827 {
828   return((Quantum) (scale*(pixel/scale)));
829 }
830
831 MagickExport MagickBooleanType SetImageDepth(Image *image,
832   const size_t depth)
833 {
834   return(SetImageChannelDepth(image,AllChannels,depth));
835 }
836
837 MagickExport MagickBooleanType SetImageChannelDepth(Image *image,
838   const ChannelType channel,const size_t depth)
839 {
840   CacheView
841     *image_view;
842
843   ExceptionInfo
844     *exception;
845
846   ssize_t
847     y;
848
849   MagickBooleanType
850     status;
851
852   QuantumAny
853     scale;
854
855   assert(image != (Image *) NULL);
856   if (image->debug != MagickFalse)
857     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
858   assert(image->signature == MagickSignature);
859   if (GetImageDepth(image,&image->exception) <= (size_t)
860       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;
869   scale=1;
870   if (depth < QuantumDepth)
871     scale=QuantumRange/((QuantumAny) QuantumRange >> (QuantumDepth-depth));
872   exception=(&image->exception);
873   image_view=AcquireCacheView(image);
874 #if defined(MAGICKCORE_OPENMP_SUPPORT)
875   #pragma omp parallel for schedule(dynamic,4) shared(status)
876 #endif
877   for (y=0; y < (ssize_t) image->rows; y++)
878   {
879     register IndexPacket
880       *restrict indexes;
881
882     register ssize_t
883       x;
884
885     register PixelPacket
886       *restrict q;
887
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);
898     for (x=0; x < (ssize_t) image->columns; x++)
899     {
900       if ((channel & RedChannel) != 0)
901         q->red=SetPixelDepth(q->red,scale);
902       if ((channel & GreenChannel) != 0)
903         q->green=SetPixelDepth(q->green,scale);
904       if ((channel & BlueChannel) != 0)
905         q->green=SetPixelDepth(q->blue,scale);
906       if (((channel & OpacityChannel) != 0) && (image->matte != MagickFalse))
907         q->opacity=SetPixelDepth(q->opacity,scale);
908       if (((channel & IndexChannel) != 0) &&
909           (image->colorspace == CMYKColorspace))
910         indexes[x]=SetPixelDepth(indexes[x],scale);
911       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
925       register ssize_t
926         i;
927
928       register PixelPacket
929         *restrict p;
930
931       p=image->colormap;
932       range=GetQuantumRange(depth);
933 #if defined(MAGICKCORE_OPENMP_SUPPORT)
934   #pragma omp parallel for schedule(dynamic,4) shared(status)
935 #endif
936       for (i=0; i < (ssize_t) image->colors; i++)
937       {
938         if ((channel & RedChannel) != 0)
939           p->red=SetPixelDepth(p->red,scale);
940         if ((channel & GreenChannel) != 0)
941           p->green=SetPixelDepth(p->green,scale);
942         if ((channel & BlueChannel) != 0)
943           p->blue=SetPixelDepth(p->blue,scale);
944         if ((channel & OpacityChannel) != 0)
945           p->opacity=SetPixelDepth(p->opacity,scale);
946         p++;
947       }
948     }
949   image->depth=depth;
950   return(status);
951 }