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