]> granicus.if.org Git - imagemagick/blob - MagickCore/attribute.c
(no commit message)
[imagemagick] / MagickCore / 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-2013 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 "MagickCore/studio.h"
44 #include "MagickCore/artifact.h"
45 #include "MagickCore/attribute.h"
46 #include "MagickCore/blob.h"
47 #include "MagickCore/blob-private.h"
48 #include "MagickCore/cache.h"
49 #include "MagickCore/cache-private.h"
50 #include "MagickCore/cache-view.h"
51 #include "MagickCore/channel.h"
52 #include "MagickCore/client.h"
53 #include "MagickCore/color.h"
54 #include "MagickCore/color-private.h"
55 #include "MagickCore/colormap.h"
56 #include "MagickCore/colormap-private.h"
57 #include "MagickCore/colorspace.h"
58 #include "MagickCore/colorspace-private.h"
59 #include "MagickCore/composite.h"
60 #include "MagickCore/composite-private.h"
61 #include "MagickCore/constitute.h"
62 #include "MagickCore/draw.h"
63 #include "MagickCore/draw-private.h"
64 #include "MagickCore/effect.h"
65 #include "MagickCore/enhance.h"
66 #include "MagickCore/exception.h"
67 #include "MagickCore/exception-private.h"
68 #include "MagickCore/geometry.h"
69 #include "MagickCore/histogram.h"
70 #include "MagickCore/identify.h"
71 #include "MagickCore/image.h"
72 #include "MagickCore/image-private.h"
73 #include "MagickCore/list.h"
74 #include "MagickCore/log.h"
75 #include "MagickCore/memory_.h"
76 #include "MagickCore/magick.h"
77 #include "MagickCore/monitor.h"
78 #include "MagickCore/monitor-private.h"
79 #include "MagickCore/option.h"
80 #include "MagickCore/paint.h"
81 #include "MagickCore/pixel.h"
82 #include "MagickCore/pixel-accessor.h"
83 #include "MagickCore/property.h"
84 #include "MagickCore/quantize.h"
85 #include "MagickCore/quantum-private.h"
86 #include "MagickCore/random_.h"
87 #include "MagickCore/resource_.h"
88 #include "MagickCore/semaphore.h"
89 #include "MagickCore/segment.h"
90 #include "MagickCore/splay-tree.h"
91 #include "MagickCore/string_.h"
92 #include "MagickCore/thread-private.h"
93 #include "MagickCore/threshold.h"
94 #include "MagickCore/transform.h"
95 #include "MagickCore/utility.h"
96 \f
97 /*
98 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
99 %                                                                             %
100 %                                                                             %
101 %                                                                             %
102 +   G e t I m a g e B o u n d i n g B o x                                     %
103 %                                                                             %
104 %                                                                             %
105 %                                                                             %
106 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
107 %
108 %  GetImageBoundingBox() returns the bounding box of an image canvas.
109 %
110 %  The format of the GetImageBoundingBox method is:
111 %
112 %      RectangleInfo GetImageBoundingBox(const Image *image,
113 %        ExceptionInfo *exception)
114 %
115 %  A description of each parameter follows:
116 %
117 %    o bounds: Method GetImageBoundingBox returns the bounding box of an
118 %      image canvas.
119 %
120 %    o image: the image.
121 %
122 %    o exception: return any errors or warnings in this structure.
123 %
124 */
125 MagickExport RectangleInfo GetImageBoundingBox(const Image *image,
126   ExceptionInfo *exception)
127 {
128   CacheView
129     *image_view;
130
131   MagickBooleanType
132     status;
133
134   PixelInfo
135     target[3],
136     zero;
137
138   RectangleInfo
139     bounds;
140
141   register const Quantum
142     *p;
143
144   ssize_t
145     y;
146
147   assert(image != (Image *) NULL);
148   assert(image->signature == MagickSignature);
149   if (image->debug != MagickFalse)
150     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
151   bounds.width=0;
152   bounds.height=0;
153   bounds.x=(ssize_t) image->columns;
154   bounds.y=(ssize_t) image->rows;
155   GetPixelInfo(image,&target[0]);
156   image_view=AcquireVirtualCacheView(image,exception);
157   p=GetCacheViewVirtualPixels(image_view,0,0,1,1,exception);
158   if (p == (const Quantum *) NULL)
159     {
160       image_view=DestroyCacheView(image_view);
161       return(bounds);
162     }
163   GetPixelInfoPixel(image,p,&target[0]);
164   GetPixelInfo(image,&target[1]);
165   p=GetCacheViewVirtualPixels(image_view,(ssize_t) image->columns-1,0,1,1,
166     exception);
167   GetPixelInfoPixel(image,p,&target[1]);
168   GetPixelInfo(image,&target[2]);
169   p=GetCacheViewVirtualPixels(image_view,0,(ssize_t) image->rows-1,1,1,
170     exception);
171   GetPixelInfoPixel(image,p,&target[2]);
172   status=MagickTrue;
173   GetPixelInfo(image,&zero);
174 #if defined(MAGICKCORE_OPENMP_SUPPORT)
175   #pragma omp parallel for schedule(static,4) shared(status) \
176     magick_threads(image,image,image->rows,1)
177 #endif
178   for (y=0; y < (ssize_t) image->rows; y++)
179   {
180     PixelInfo
181       pixel;
182
183     RectangleInfo
184       bounding_box;
185
186     register const Quantum
187       *restrict p;
188
189     register ssize_t
190       x;
191
192     if (status == MagickFalse)
193       continue;
194 #if defined(MAGICKCORE_OPENMP_SUPPORT)
195 #  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 Quantum *) NULL)
200       {
201         status=MagickFalse;
202         continue;
203       }
204     pixel=zero;
205     for (x=0; x < (ssize_t) image->columns; x++)
206     {
207       GetPixelInfoPixel(image,p,&pixel);
208       if ((x < bounding_box.x) &&
209           (IsFuzzyEquivalencePixelInfo(&pixel,&target[0]) == MagickFalse))
210         bounding_box.x=x;
211       if ((x > (ssize_t) bounding_box.width) &&
212           (IsFuzzyEquivalencePixelInfo(&pixel,&target[1]) == MagickFalse))
213         bounding_box.width=(size_t) x;
214       if ((y < bounding_box.y) &&
215           (IsFuzzyEquivalencePixelInfo(&pixel,&target[0]) == MagickFalse))
216         bounding_box.y=y;
217       if ((y > (ssize_t) bounding_box.height) &&
218           (IsFuzzyEquivalencePixelInfo(&pixel,&target[2]) == MagickFalse))
219         bounding_box.height=(size_t) y;
220       p+=GetPixelChannels(image);
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 D e p t h                                                 %
254 %                                                                             %
255 %                                                                             %
256 %                                                                             %
257 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
258 %
259 %  GetImageDepth() returns the depth of a particular image channel.
260 %
261 %  The format of the GetImageDepth method is:
262 %
263 %      size_t GetImageDepth(const Image *image,ExceptionInfo *exception)
264 %
265 %  A description of each parameter follows:
266 %
267 %    o image: the image.
268 %
269 %    o exception: return any errors or warnings in this structure.
270 %
271 */
272 MagickExport size_t GetImageDepth(const Image *image,ExceptionInfo *exception)
273 {
274   CacheView
275     *image_view;
276
277   MagickBooleanType
278     status;
279
280   register ssize_t
281     id;
282
283   size_t
284     *current_depth,
285     depth,
286     number_threads;
287
288   ssize_t
289     y;
290
291   /*
292     Compute image depth.
293   */
294   assert(image != (Image *) NULL);
295   assert(image->signature == MagickSignature);
296   if (image->debug != MagickFalse)
297     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
298   number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
299   current_depth=(size_t *) AcquireQuantumMemory(number_threads,
300     sizeof(*current_depth));
301   if (current_depth == (size_t *) NULL)
302     ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
303   status=MagickTrue;
304   for (id=0; id < (ssize_t) number_threads; id++)
305     current_depth[id]=1;
306   if ((image->storage_class == PseudoClass) && (image->alpha_trait != BlendPixelTrait))
307     {
308       register ssize_t
309         i;
310
311 #if defined(MAGICKCORE_OPENMP_SUPPORT)
312       #pragma omp parallel for schedule(static,4) shared(status) \
313         if ((image->colors) > 256) \
314           num_threads(GetMagickResourceLimit(ThreadResource))
315 #endif
316       for (i=0; i < (ssize_t) image->colors; i++)
317       {
318         const int
319           id = GetOpenMPThreadId();
320
321         while (current_depth[id] < MAGICKCORE_QUANTUM_DEPTH)
322         {
323           MagickStatusType
324             status;
325
326           QuantumAny
327             range;
328
329           status=0;
330           range=GetQuantumRange(current_depth[id]);
331           if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
332             status&=ClampToQuantum(image->colormap[i].red) !=
333               ScaleAnyToQuantum(ScaleQuantumToAny(ClampToQuantum(
334               image->colormap[i].red),range),range);
335           if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
336             status&=ClampToQuantum(image->colormap[i].green) !=
337               ScaleAnyToQuantum(ScaleQuantumToAny(ClampToQuantum(
338               image->colormap[i].green),range),range);
339           if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
340             status&=ClampToQuantum(image->colormap[i].blue) !=
341               ScaleAnyToQuantum(ScaleQuantumToAny(ClampToQuantum(
342               image->colormap[i].blue),range),range);
343           if (status == 0)
344             break;
345           current_depth[id]++;
346         }
347       }
348       depth=current_depth[0];
349       for (id=1; id < (ssize_t) number_threads; id++)
350         if (depth < current_depth[id])
351           depth=current_depth[id];
352       current_depth=(size_t *) RelinquishMagickMemory(current_depth);
353       return(depth);
354     }
355   image_view=AcquireVirtualCacheView(image,exception);
356 #if !defined(MAGICKCORE_HDRI_SUPPORT)
357   if (QuantumRange <= MaxMap)
358     {
359       register ssize_t
360         i;
361
362       size_t
363         *depth_map;
364
365       /*
366         Scale pixels to desired (optimized with depth map).
367       */
368       depth_map=(size_t *) AcquireQuantumMemory(MaxMap+1,sizeof(*depth_map));
369       if (depth_map == (size_t *) NULL)
370         ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
371       for (i=0; i <= (ssize_t) MaxMap; i++)
372       {
373         unsigned int
374           depth;
375
376         for (depth=1; depth < MAGICKCORE_QUANTUM_DEPTH; depth++)
377         {
378           Quantum
379             pixel;
380
381           QuantumAny
382             range;
383
384           range=GetQuantumRange(depth);
385           pixel=(Quantum) i;
386           if (pixel == ScaleAnyToQuantum(ScaleQuantumToAny(pixel,range),range))
387             break;
388         }
389         depth_map[i]=depth;
390       }
391 #if defined(MAGICKCORE_OPENMP_SUPPORT)
392       #pragma omp parallel for schedule(static,4) shared(status) \
393         magick_threads(image,image,image->rows,1)
394 #endif
395       for (y=0; y < (ssize_t) image->rows; y++)
396       {
397         const int
398           id = GetOpenMPThreadId();
399
400         register const Quantum
401           *restrict p;
402
403         register ssize_t
404           x;
405
406         if (status == MagickFalse)
407           continue;
408         p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
409         if (p == (const Quantum *) NULL)
410           continue;
411         for (x=0; x < (ssize_t) image->columns; x++)
412         {
413           register ssize_t
414             i;
415
416           if (GetPixelReadMask(image,p) == 0)
417             {
418               p+=GetPixelChannels(image);
419               continue;
420             }
421           for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
422           {
423             PixelChannel channel=GetPixelChannelChannel(image,i);
424             PixelTrait traits=GetPixelChannelTraits(image,channel);
425             if ((traits == UndefinedPixelTrait) ||
426                 (channel == IndexPixelChannel) ||
427                 (channel == ReadMaskPixelChannel) ||
428                 (channel == MetaPixelChannel))
429               continue;
430             if (depth_map[ScaleQuantumToMap(p[i])] > current_depth[id])
431               current_depth[id]=depth_map[ScaleQuantumToMap(p[i])];
432           }
433           p+=GetPixelChannels(image);
434         }
435         if (current_depth[id] == MAGICKCORE_QUANTUM_DEPTH)
436           status=MagickFalse;
437       }
438       image_view=DestroyCacheView(image_view);
439       depth=current_depth[0];
440       for (id=1; id < (ssize_t) number_threads; id++)
441         if (depth < current_depth[id])
442           depth=current_depth[id];
443       depth_map=(size_t *) RelinquishMagickMemory(depth_map);
444       current_depth=(size_t *) RelinquishMagickMemory(current_depth);
445       return(depth);
446     }
447 #endif
448   /*
449     Compute pixel depth.
450   */
451 #if defined(MAGICKCORE_OPENMP_SUPPORT)
452   #pragma omp parallel for schedule(static,4) shared(status) \
453     magick_threads(image,image,image->rows,1)
454 #endif
455   for (y=0; y < (ssize_t) image->rows; y++)
456   {
457     const int
458       id = GetOpenMPThreadId();
459
460     register const Quantum
461       *restrict p;
462
463     register ssize_t
464       x;
465
466     if (status == MagickFalse)
467       continue;
468     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
469     if (p == (const Quantum *) NULL)
470       continue;
471     for (x=0; x < (ssize_t) image->columns; x++)
472     {
473       register ssize_t
474         i;
475
476       if (GetPixelReadMask(image,p) == 0)
477         {
478           p+=GetPixelChannels(image);
479           continue;
480         }
481       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
482       {
483         PixelChannel
484           channel;
485
486         PixelTrait
487           traits;
488
489         channel=GetPixelChannelChannel(image,i);
490         traits=GetPixelChannelTraits(image,channel);
491         if ((traits == UndefinedPixelTrait) || (channel == IndexPixelChannel) ||
492             (channel == ReadMaskPixelChannel))
493           continue;
494         while (current_depth[id] < MAGICKCORE_QUANTUM_DEPTH)
495         {
496           QuantumAny
497             range;
498
499           range=GetQuantumRange(current_depth[id]);
500           if (p[i] == ScaleAnyToQuantum(ScaleQuantumToAny(p[i],range),range))
501             break;
502           current_depth[id]++;
503         }
504       }
505       p+=GetPixelChannels(image);
506     }
507     if (current_depth[id] == MAGICKCORE_QUANTUM_DEPTH)
508       status=MagickFalse;
509   }
510   image_view=DestroyCacheView(image_view);
511   depth=current_depth[0];
512   for (id=1; id < (ssize_t) number_threads; id++)
513     if (depth < current_depth[id])
514       depth=current_depth[id];
515   current_depth=(size_t *) RelinquishMagickMemory(current_depth);
516   return(depth);
517 }
518 \f
519 /*
520 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
521 %                                                                             %
522 %                                                                             %
523 %                                                                             %
524 %   G e t I m a g e Q u a n t u m D e p t h                                   %
525 %                                                                             %
526 %                                                                             %
527 %                                                                             %
528 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
529 %
530 %  GetImageQuantumDepth() returns the depth of the image rounded to a legal
531 %  quantum depth: 8, 16, or 32.
532 %
533 %  The format of the GetImageQuantumDepth method is:
534 %
535 %      size_t GetImageQuantumDepth(const Image *image,
536 %        const MagickBooleanType constrain)
537 %
538 %  A description of each parameter follows:
539 %
540 %    o image: the image.
541 %
542 %    o constrain: A value other than MagickFalse, constrains the depth to
543 %      a maximum of MAGICKCORE_QUANTUM_DEPTH.
544 %
545 */
546
547 static inline double MagickMin(const double x,const double y)
548 {
549   if (x < y)
550     return(x);
551   return(y);
552 }
553
554 MagickExport size_t GetImageQuantumDepth(const Image *image,
555   const MagickBooleanType constrain)
556 {
557   size_t
558     depth;
559
560   depth=image->depth;
561   if (depth <= 8)
562     depth=8;
563   else
564     if (depth <= 16)
565       depth=16;
566     else
567       if (depth <= 32)
568         depth=32;
569       else
570         if (depth <= 64)
571           depth=64;
572   if (constrain != MagickFalse)
573     depth=(size_t) MagickMin((double) depth,(double) MAGICKCORE_QUANTUM_DEPTH);
574   return(depth);
575 }
576 \f
577 /*
578 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
579 %                                                                             %
580 %                                                                             %
581 %                                                                             %
582 %   G e t I m a g e T y p e                                                   %
583 %                                                                             %
584 %                                                                             %
585 %                                                                             %
586 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
587 %
588 %  GetImageType() returns the potential type of image:
589 %
590 %        Bilevel         Grayscale        GrayscaleMatte
591 %        Palette         PaletteMatte     TrueColor
592 %        TrueColorMatte  ColorSeparation  ColorSeparationMatte
593 %
594 %  To ensure the image type matches its potential, use SetImageType():
595 %
596 %    (void) SetImageType(image,GetImageType(image));
597 %
598 %  The format of the GetImageType method is:
599 %
600 %      ImageType GetImageType(const Image *image,ExceptionInfo *exception)
601 %
602 %  A description of each parameter follows:
603 %
604 %    o image: the image.
605 %
606 %    o exception: return any errors or warnings in this structure.
607 %
608 */
609 MagickExport ImageType GetImageType(const Image *image,ExceptionInfo *exception)
610 {
611   assert(image != (Image *) NULL);
612   assert(image->signature == MagickSignature);
613   if (image->debug != MagickFalse)
614     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
615   if (image->colorspace == CMYKColorspace)
616     {
617       if (image->alpha_trait != BlendPixelTrait)
618         return(ColorSeparationType);
619       return(ColorSeparationMatteType);
620     }
621   if (IsImageMonochrome(image,exception) != MagickFalse)
622     return(BilevelType);
623   if (IsImageGray(image,exception) != MagickFalse)
624     {
625       if (image->alpha_trait == BlendPixelTrait)
626         return(GrayscaleMatteType);
627       return(GrayscaleType);
628     }
629   if (IsPaletteImage(image,exception) != MagickFalse)
630     {
631       if (image->alpha_trait == BlendPixelTrait)
632         return(PaletteMatteType);
633       return(PaletteType);
634     }
635   if (image->alpha_trait == BlendPixelTrait)
636     return(TrueColorMatteType);
637   return(TrueColorType);
638 }
639 \f
640 /*
641 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
642 %                                                                             %
643 %                                                                             %
644 %                                                                             %
645 %     I s I m a g e G r a y                                                   %
646 %                                                                             %
647 %                                                                             %
648 %                                                                             %
649 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
650 %
651 %  IsImageGray() returns MagickTrue if all the pixels in the image have the
652 %  same red, green, and blue intensities.
653 %
654 %  The format of the IsImageGray method is:
655 %
656 %      MagickBooleanType IsImageGray(const Image *image,
657 %        ExceptionInfo *exception)
658 %
659 %  A description of each parameter follows:
660 %
661 %    o image: the image.
662 %
663 %    o exception: return any errors or warnings in this structure.
664 %
665 */
666 MagickExport MagickBooleanType IsImageGray(const Image *image,
667   ExceptionInfo *exception)
668 {
669   CacheView
670     *image_view;
671
672   ImageType
673     type;
674
675   register const Quantum
676     *p;
677
678   register ssize_t
679     x;
680
681   ssize_t
682     y;
683
684   assert(image != (Image *) NULL);
685   assert(image->signature == MagickSignature);
686   if (image->debug != MagickFalse)
687     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
688   if ((image->type == BilevelType) || (image->type == GrayscaleType) ||
689       (image->type == GrayscaleMatteType))
690     return(MagickTrue);
691   if ((IsGrayColorspace(image->colorspace) == MagickFalse) &&
692       (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse))
693     return(MagickFalse);
694   type=BilevelType;
695   image_view=AcquireVirtualCacheView(image,exception);
696   for (y=0; y < (ssize_t) image->rows; y++)
697   {
698     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
699     if (p == (const Quantum *) NULL)
700       break;
701     for (x=0; x < (ssize_t) image->columns; x++)
702     {
703       if (IsPixelGray(image,p) == MagickFalse)
704         {
705           type=UndefinedType;
706           break;
707         }
708       if ((type == BilevelType) &&
709           (IsPixelMonochrome(image,p) == MagickFalse))
710         type=GrayscaleType;
711       p+=GetPixelChannels(image);
712     }
713     if (type == UndefinedType)
714       break;
715   }
716   image_view=DestroyCacheView(image_view);
717   if (type == UndefinedType)
718     return(MagickFalse);
719   ((Image *) image)->colorspace=GRAYColorspace;
720   ((Image *) image)->type=type;
721   if ((type == GrayscaleType) && (image->alpha_trait == BlendPixelTrait))
722     ((Image *) image)->type=GrayscaleMatteType;
723   return(SyncImagePixelCache((Image *) image,exception));
724 }
725 \f
726 /*
727 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
728 %                                                                             %
729 %                                                                             %
730 %                                                                             %
731 %   I s I m a g e M o n o c h r o m e                                         %
732 %                                                                             %
733 %                                                                             %
734 %                                                                             %
735 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
736 %
737 %  IsImageMonochrome() returns MagickTrue if all the pixels in the image have
738 %  the same red, green, and blue intensities and the intensity is either
739 %  0 or QuantumRange.
740 %
741 %  The format of the IsImageMonochrome method is:
742 %
743 %      MagickBooleanType IsImageMonochrome(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 IsImageMonochrome(const Image *image,
754   ExceptionInfo *exception)
755 {
756   CacheView
757     *image_view;
758
759   ImageType
760     type;
761
762   register ssize_t
763     x;
764
765   register const Quantum
766     *p;
767
768   ssize_t
769     y;
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->type == BilevelType)
776     return(MagickTrue);
777   if ((IsGrayColorspace(image->colorspace) == MagickFalse) &&
778       (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse))
779     return(MagickFalse);
780   type=BilevelType;
781   image_view=AcquireVirtualCacheView(image,exception);
782   for (y=0; y < (ssize_t) image->rows; y++)
783   {
784     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
785     if (p == (const Quantum *) NULL)
786       break;
787     for (x=0; x < (ssize_t) image->columns; x++)
788     {
789       if (IsPixelMonochrome(image,p) == MagickFalse)
790         {
791           type=UndefinedType;
792           break;
793         }
794       p+=GetPixelChannels(image);
795     }
796     if (type == UndefinedType)
797       break;
798   }
799   image_view=DestroyCacheView(image_view);
800   if (type == UndefinedType)
801     return(MagickFalse);
802   ((Image *) image)->colorspace=GRAYColorspace;
803   ((Image *) image)->type=type;
804   return(SyncImagePixelCache((Image *) image,exception));
805 }
806 \f
807 /*
808 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
809 %                                                                             %
810 %                                                                             %
811 %                                                                             %
812 %     I s I m a g e O p a q u e                                               %
813 %                                                                             %
814 %                                                                             %
815 %                                                                             %
816 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
817 %
818 %  IsImageOpaque() returns MagickTrue if none of the pixels in the image have
819 %  an alpha value other than OpaqueAlpha (QuantumRange).
820 %
821 %  Will return true immediatally is alpha channel is not available.
822 %
823 %  The format of the IsImageOpaque method is:
824 %
825 %      MagickBooleanType IsImageOpaque(const Image *image,
826 %        ExceptionInfo *exception)
827 %
828 %  A description of each parameter follows:
829 %
830 %    o image: the image.
831 %
832 %    o exception: return any errors or warnings in this structure.
833 %
834 */
835 MagickExport MagickBooleanType IsImageOpaque(const Image *image,
836   ExceptionInfo *exception)
837 {
838   CacheView
839     *image_view;
840
841   register const Quantum
842     *p;
843
844   register ssize_t
845     x;
846
847   ssize_t
848     y;
849
850   /*
851     Determine if image is opaque.
852   */
853   assert(image != (Image *) NULL);
854   assert(image->signature == MagickSignature);
855   if (image->debug != MagickFalse)
856     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
857   if (image->alpha_trait != BlendPixelTrait)
858     return(MagickTrue);
859   image_view=AcquireVirtualCacheView(image,exception);
860   for (y=0; y < (ssize_t) image->rows; y++)
861   {
862     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
863     if (p == (const Quantum *) NULL)
864       break;
865     for (x=0; x < (ssize_t) image->columns; x++)
866     {
867       if (GetPixelAlpha(image,p) != OpaqueAlpha)
868         break;
869       p+=GetPixelChannels(image);
870     }
871     if (x < (ssize_t) image->columns)
872      break;
873   }
874   image_view=DestroyCacheView(image_view);
875   return(y < (ssize_t) image->rows ? MagickFalse : MagickTrue);
876 }
877 \f
878 /*
879 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
880 %                                                                             %
881 %                                                                             %
882 %                                                                             %
883 %   S e t I m a g e D e p t h                                                 %
884 %                                                                             %
885 %                                                                             %
886 %                                                                             %
887 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
888 %
889 %  SetImageDepth() sets the depth of the image.
890 %
891 %  The format of the SetImageDepth method is:
892 %
893 %      MagickBooleanType SetImageDepth(Image *image,const size_t depth,
894 %        ExceptionInfo *exception)
895 %
896 %  A description of each parameter follows:
897 %
898 %    o image: the image.
899 %
900 %    o channel: the channel.
901 %
902 %    o depth: the image depth.
903 %
904 %    o exception: return any errors or warnings in this structure.
905 %
906 */
907 MagickExport MagickBooleanType SetImageDepth(Image *image,
908   const size_t depth,ExceptionInfo *exception)
909 {
910   CacheView
911     *image_view;
912
913   MagickBooleanType
914     status;
915
916   QuantumAny
917     range;
918
919   ssize_t
920     y;
921
922   assert(image != (Image *) NULL);
923   if (image->debug != MagickFalse)
924     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
925   assert(image->signature == MagickSignature);
926   if (depth >= MAGICKCORE_QUANTUM_DEPTH)
927     {
928       image->depth=depth;
929       return(MagickTrue);
930     }
931   range=GetQuantumRange(depth);
932   if (image->storage_class == PseudoClass)
933     {
934       register ssize_t
935         i;
936
937 #if defined(MAGICKCORE_OPENMP_SUPPORT)
938       #pragma omp parallel for schedule(static,4) shared(status) \
939         magick_threads(image,image,1,1)
940 #endif
941       for (i=0; i < (ssize_t) image->colors; i++)
942       {
943         if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
944           image->colormap[i].red=(double) ScaleAnyToQuantum(ScaleQuantumToAny(
945             ClampToQuantum(image->colormap[i].red),range),range);
946         if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
947           image->colormap[i].green=(double) ScaleAnyToQuantum(ScaleQuantumToAny(
948             ClampToQuantum(image->colormap[i].green),range),range);
949         if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
950           image->colormap[i].blue=(double) ScaleAnyToQuantum(ScaleQuantumToAny(
951             ClampToQuantum(image->colormap[i].blue),range),range);
952         if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
953           image->colormap[i].alpha=(double) ScaleAnyToQuantum(ScaleQuantumToAny(
954             ClampToQuantum(image->colormap[i].alpha),range),range);
955       }
956     }
957   status=MagickTrue;
958   image_view=AcquireAuthenticCacheView(image,exception);
959 #if !defined(MAGICKCORE_HDRI_SUPPORT)
960   if (QuantumRange <= MaxMap)
961     {
962       Quantum
963         *depth_map;
964
965       register ssize_t
966         i;
967
968       /*
969         Scale pixels to desired (optimized with depth map).
970       */
971       depth_map=(Quantum *) AcquireQuantumMemory(MaxMap+1,sizeof(*depth_map));
972       if (depth_map == (Quantum *) NULL)
973         ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
974       for (i=0; i <= (ssize_t) MaxMap; i++)
975         depth_map[i]=ScaleAnyToQuantum(ScaleQuantumToAny((Quantum) i,range),
976           range);
977 #if defined(MAGICKCORE_OPENMP_SUPPORT)
978       #pragma omp parallel for schedule(static,4) shared(status) \
979         magick_threads(image,image,image->rows,1)
980 #endif
981       for (y=0; y < (ssize_t) image->rows; y++)
982       {
983         register ssize_t
984           x;
985
986         register Quantum
987           *restrict q;
988
989         if (status == MagickFalse)
990           continue;
991         q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
992           exception);
993         if (q == (Quantum *) NULL)
994           {
995             status=MagickFalse;
996             continue;
997           }
998         for (x=0; x < (ssize_t) image->columns; x++)
999         {
1000           register ssize_t
1001             i;
1002
1003           if (GetPixelReadMask(image,q) == 0)
1004             {
1005               q+=GetPixelChannels(image);
1006               continue;
1007             }
1008           for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1009           {
1010             PixelChannel
1011               channel;
1012
1013             PixelTrait
1014               traits;
1015
1016             channel=GetPixelChannelChannel(image,i);
1017             traits=GetPixelChannelTraits(image,channel);
1018             if ((traits == UndefinedPixelTrait) ||
1019                 (channel == IndexPixelChannel) || (channel == ReadMaskPixelChannel))
1020               continue;
1021             q[i]=depth_map[ScaleQuantumToMap(q[i])];
1022           }
1023           q+=GetPixelChannels(image);
1024         }
1025         if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1026           {
1027             status=MagickFalse;
1028             continue;
1029           }
1030       }
1031       image_view=DestroyCacheView(image_view);
1032       depth_map=(Quantum *) RelinquishMagickMemory(depth_map);
1033       if (status != MagickFalse)
1034         image->depth=depth;
1035       return(status);
1036     }
1037 #endif
1038   /*
1039     Scale pixels to desired depth.
1040   */
1041 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1042   #pragma omp parallel for schedule(static,4) shared(status) \
1043     magick_threads(image,image,image->rows,1)
1044 #endif
1045   for (y=0; y < (ssize_t) image->rows; y++)
1046   {
1047     register ssize_t
1048       x;
1049
1050     register Quantum
1051       *restrict q;
1052
1053     if (status == MagickFalse)
1054       continue;
1055     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1056     if (q == (Quantum *) NULL)
1057       {
1058         status=MagickFalse;
1059         continue;
1060       }
1061     for (x=0; x < (ssize_t) image->columns; x++)
1062     {
1063       register ssize_t
1064         i;
1065
1066       if (GetPixelReadMask(image,q) == 0)
1067         {
1068           q+=GetPixelChannels(image);
1069           continue;
1070         }
1071       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1072       {
1073         PixelChannel
1074           channel;
1075
1076         PixelTrait
1077           traits;
1078
1079         channel=GetPixelChannelChannel(image,i);
1080         traits=GetPixelChannelTraits(image,channel);
1081         if ((traits == UndefinedPixelTrait) || (channel == IndexPixelChannel) ||
1082             (channel == ReadMaskPixelChannel))
1083           continue;
1084         q[i]=ScaleAnyToQuantum(ScaleQuantumToAny(q[i],range),range);
1085       }
1086       q+=GetPixelChannels(image);
1087     }
1088     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1089       {
1090         status=MagickFalse;
1091         continue;
1092       }
1093   }
1094   image_view=DestroyCacheView(image_view);
1095   if (status != MagickFalse)
1096     image->depth=depth;
1097   return(status);
1098 }
1099 \f
1100 /*
1101 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1102 %                                                                             %
1103 %                                                                             %
1104 %                                                                             %
1105 %   S e t I m a g e T y p e                                                   %
1106 %                                                                             %
1107 %                                                                             %
1108 %                                                                             %
1109 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1110 %
1111 %  SetImageType() sets the type of image.  Choose from these types:
1112 %
1113 %        Bilevel        Grayscale       GrayscaleMatte
1114 %        Palette        PaletteMatte    TrueColor
1115 %        TrueColorMatte ColorSeparation ColorSeparationMatte
1116 %        OptimizeType
1117 %
1118 %  The format of the SetImageType method is:
1119 %
1120 %      MagickBooleanType SetImageType(Image *image,const ImageType type,
1121 %        ExceptionInfo *exception)
1122 %
1123 %  A description of each parameter follows:
1124 %
1125 %    o image: the image.
1126 %
1127 %    o type: Image type.
1128 %
1129 %    o exception: return any errors or warnings in this structure.
1130 %
1131 */
1132 MagickExport MagickBooleanType SetImageType(Image *image,const ImageType type,
1133   ExceptionInfo *exception)
1134 {
1135   const char
1136     *artifact;
1137
1138   ImageInfo
1139     *image_info;
1140
1141   MagickBooleanType
1142     status;
1143
1144   QuantizeInfo
1145     *quantize_info;
1146
1147   assert(image != (Image *) NULL);
1148   if (image->debug != MagickFalse)
1149     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
1150   assert(image->signature == MagickSignature);
1151   status=MagickTrue;
1152   image_info=AcquireImageInfo();
1153   image_info->dither=image->dither;
1154   artifact=GetImageArtifact(image,"dither");
1155   if (artifact != (const char *) NULL)
1156     (void) SetImageOption(image_info,"dither",artifact);
1157   switch (type)
1158   {
1159     case BilevelType:
1160     {
1161       if (IsImageMonochrome(image,exception) == MagickFalse)
1162         {
1163           quantize_info=AcquireQuantizeInfo(image_info);
1164           quantize_info->number_colors=2;
1165           quantize_info->colorspace=GRAYColorspace;
1166           status=QuantizeImage(quantize_info,image,exception);
1167           quantize_info=DestroyQuantizeInfo(quantize_info);
1168         }
1169       image->alpha_trait=UndefinedPixelTrait;
1170       break;
1171     }
1172     case GrayscaleType:
1173     {
1174       if (IsImageGray(image,exception) == MagickFalse)
1175         status=TransformImageColorspace(image,GRAYColorspace,exception);
1176       image->alpha_trait=UndefinedPixelTrait;
1177       break;
1178     }
1179     case GrayscaleMatteType:
1180     {
1181       if (IsImageGray(image,exception) == MagickFalse)
1182         status=TransformImageColorspace(image,GRAYColorspace,exception);
1183       if (image->alpha_trait != BlendPixelTrait)
1184         (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
1185       break;
1186     }
1187     case PaletteType:
1188     {
1189       if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
1190         status=TransformImageColorspace(image,sRGBColorspace,exception);
1191       if ((image->storage_class == DirectClass) || (image->colors > 256))
1192         {
1193           quantize_info=AcquireQuantizeInfo(image_info);
1194           quantize_info->number_colors=256;
1195           status=QuantizeImage(quantize_info,image,exception);
1196           quantize_info=DestroyQuantizeInfo(quantize_info);
1197         }
1198       image->alpha_trait=UndefinedPixelTrait;
1199       break;
1200     }
1201     case PaletteBilevelMatteType:
1202     {
1203       ChannelType
1204         channel_mask;
1205
1206       if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
1207         status=TransformImageColorspace(image,sRGBColorspace,exception);
1208       if (image->alpha_trait != BlendPixelTrait)
1209         (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
1210       channel_mask=SetImageChannelMask(image,AlphaChannel);
1211       (void) BilevelImage(image,(double) QuantumRange/2.0,exception);
1212       (void) SetImageChannelMask(image,channel_mask);
1213       quantize_info=AcquireQuantizeInfo(image_info);
1214       status=QuantizeImage(quantize_info,image,exception);
1215       quantize_info=DestroyQuantizeInfo(quantize_info);
1216       break;
1217     }
1218     case PaletteMatteType:
1219     {
1220       if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
1221         status=TransformImageColorspace(image,sRGBColorspace,exception);
1222       if (image->alpha_trait != BlendPixelTrait)
1223         (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
1224       quantize_info=AcquireQuantizeInfo(image_info);
1225       quantize_info->colorspace=TransparentColorspace;
1226       status=QuantizeImage(quantize_info,image,exception);
1227       quantize_info=DestroyQuantizeInfo(quantize_info);
1228       break;
1229     }
1230     case TrueColorType:
1231     {
1232       if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
1233         status=TransformImageColorspace(image,sRGBColorspace,exception);
1234       if (image->storage_class != DirectClass)
1235         status=SetImageStorageClass(image,DirectClass,exception);
1236       image->alpha_trait=UndefinedPixelTrait;
1237       break;
1238     }
1239     case TrueColorMatteType:
1240     {
1241       if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
1242         status=TransformImageColorspace(image,sRGBColorspace,exception);
1243       if (image->storage_class != DirectClass)
1244         status=SetImageStorageClass(image,DirectClass,exception);
1245       if (image->alpha_trait != BlendPixelTrait)
1246         (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
1247       break;
1248     }
1249     case ColorSeparationType:
1250     {
1251       if (image->colorspace != CMYKColorspace)
1252         {
1253           if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
1254             status=TransformImageColorspace(image,sRGBColorspace,exception);
1255           status=TransformImageColorspace(image,CMYKColorspace,exception);
1256         }
1257       if (image->storage_class != DirectClass)
1258         status=SetImageStorageClass(image,DirectClass,exception);
1259       image->alpha_trait=UndefinedPixelTrait;
1260       break;
1261     }
1262     case ColorSeparationMatteType:
1263     {
1264       if (image->colorspace != CMYKColorspace)
1265         {
1266           if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
1267             status=TransformImageColorspace(image,sRGBColorspace,exception);
1268           status=TransformImageColorspace(image,CMYKColorspace,exception);
1269         }
1270       if (image->storage_class != DirectClass)
1271         status=SetImageStorageClass(image,DirectClass,exception);
1272       if (image->alpha_trait != BlendPixelTrait)
1273         status=SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
1274       break;
1275     }
1276     case OptimizeType:
1277     case UndefinedType:
1278       break;
1279   }
1280   image->type=type;
1281   image_info=DestroyImageInfo(image_info);
1282   return(status);
1283 }