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