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