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