]> 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
900 static inline Quantum ClampPixel(const MagickRealType value)
901 {
902 #if !defined(MAGICKCORE_HDRI_SUPPORT)
903   return((Quantum) value);
904 #else
905   if (value < 0.0f)
906     return(0.0f);
907   if (value >= (MagickRealType) QuantumRange)
908     return((Quantum) QuantumRange);
909   return(value);
910 #endif
911 }
912
913 MagickExport MagickBooleanType SetImageDepth(Image *image,
914   const size_t depth,ExceptionInfo *exception)
915 {
916   CacheView
917     *image_view;
918
919   MagickBooleanType
920     status;
921
922   QuantumAny
923     range;
924
925   ssize_t
926     y;
927
928   assert(image != (Image *) NULL);
929   if (image->debug != MagickFalse)
930     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
931   assert(image->signature == MagickSignature);
932   if (depth >= MAGICKCORE_QUANTUM_DEPTH)
933     {
934       image->depth=depth;
935       return(MagickTrue);
936     }
937   range=GetQuantumRange(depth);
938   if (image->storage_class == PseudoClass)
939     {
940       register ssize_t
941         i;
942
943 #if defined(MAGICKCORE_OPENMP_SUPPORT)
944       #pragma omp parallel for schedule(static,4) shared(status) \
945         magick_threads(image,image,1,1)
946 #endif
947       for (i=0; i < (ssize_t) image->colors; i++)
948       {
949         if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
950           image->colormap[i].red=(double) ScaleAnyToQuantum(ScaleQuantumToAny(
951             ClampPixel(image->colormap[i].red),range),range);
952         if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
953           image->colormap[i].green=(double) ScaleAnyToQuantum(ScaleQuantumToAny(
954             ClampPixel(image->colormap[i].green),range),range);
955         if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
956           image->colormap[i].blue=(double) ScaleAnyToQuantum(ScaleQuantumToAny(
957             ClampPixel(image->colormap[i].blue),range),range);
958         if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
959           image->colormap[i].alpha=(double) ScaleAnyToQuantum(ScaleQuantumToAny(
960             ClampPixel(image->colormap[i].alpha),range),range);
961       }
962     }
963   status=MagickTrue;
964   image_view=AcquireAuthenticCacheView(image,exception);
965 #if !defined(MAGICKCORE_HDRI_SUPPORT)
966   if (QuantumRange <= MaxMap)
967     {
968       Quantum
969         *depth_map;
970
971       register ssize_t
972         i;
973
974       /*
975         Scale pixels to desired (optimized with depth map).
976       */
977       depth_map=(Quantum *) AcquireQuantumMemory(MaxMap+1,sizeof(*depth_map));
978       if (depth_map == (Quantum *) NULL)
979         ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
980       for (i=0; i <= (ssize_t) MaxMap; i++)
981         depth_map[i]=ScaleAnyToQuantum(ScaleQuantumToAny((Quantum) i,range),
982           range);
983 #if defined(MAGICKCORE_OPENMP_SUPPORT)
984       #pragma omp parallel for schedule(static,4) shared(status) \
985         magick_threads(image,image,image->rows,1)
986 #endif
987       for (y=0; y < (ssize_t) image->rows; y++)
988       {
989         register ssize_t
990           x;
991
992         register Quantum
993           *restrict q;
994
995         if (status == MagickFalse)
996           continue;
997         q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
998           exception);
999         if (q == (Quantum *) NULL)
1000           {
1001             status=MagickFalse;
1002             continue;
1003           }
1004         for (x=0; x < (ssize_t) image->columns; x++)
1005         {
1006           register ssize_t
1007             i;
1008
1009           if (GetPixelReadMask(image,q) == 0)
1010             {
1011               q+=GetPixelChannels(image);
1012               continue;
1013             }
1014           for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1015           {
1016             PixelChannel
1017               channel;
1018
1019             PixelTrait
1020               traits;
1021
1022             channel=GetPixelChannelChannel(image,i);
1023             traits=GetPixelChannelTraits(image,channel);
1024             if ((traits == UndefinedPixelTrait) ||
1025                 (channel == IndexPixelChannel) ||
1026                 (channel == ReadMaskPixelChannel))
1027               continue;
1028             q[i]=depth_map[ScaleQuantumToMap(q[i])];
1029           }
1030           q+=GetPixelChannels(image);
1031         }
1032         if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1033           {
1034             status=MagickFalse;
1035             continue;
1036           }
1037       }
1038       image_view=DestroyCacheView(image_view);
1039       depth_map=(Quantum *) RelinquishMagickMemory(depth_map);
1040       if (status != MagickFalse)
1041         image->depth=depth;
1042       return(status);
1043     }
1044 #endif
1045   /*
1046     Scale pixels to desired depth.
1047   */
1048 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1049   #pragma omp parallel for schedule(static,4) shared(status) \
1050     magick_threads(image,image,image->rows,1)
1051 #endif
1052   for (y=0; y < (ssize_t) image->rows; y++)
1053   {
1054     register ssize_t
1055       x;
1056
1057     register Quantum
1058       *restrict q;
1059
1060     if (status == MagickFalse)
1061       continue;
1062     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1063     if (q == (Quantum *) NULL)
1064       {
1065         status=MagickFalse;
1066         continue;
1067       }
1068     for (x=0; x < (ssize_t) image->columns; x++)
1069     {
1070       register ssize_t
1071         i;
1072
1073       if (GetPixelReadMask(image,q) == 0)
1074         {
1075           q+=GetPixelChannels(image);
1076           continue;
1077         }
1078       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1079       {
1080         PixelChannel
1081           channel;
1082
1083         PixelTrait
1084           traits;
1085
1086         channel=GetPixelChannelChannel(image,i);
1087         traits=GetPixelChannelTraits(image,channel);
1088         if ((traits == UndefinedPixelTrait) || (channel == IndexPixelChannel) ||
1089             (channel == ReadMaskPixelChannel))
1090           continue;
1091         q[i]=ScaleAnyToQuantum(ScaleQuantumToAny(ClampPixel(q[i]),range),range);
1092       }
1093       q+=GetPixelChannels(image);
1094     }
1095     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1096       {
1097         status=MagickFalse;
1098         continue;
1099       }
1100   }
1101   image_view=DestroyCacheView(image_view);
1102   if (status != MagickFalse)
1103     image->depth=depth;
1104   return(status);
1105 }
1106 \f
1107 /*
1108 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1109 %                                                                             %
1110 %                                                                             %
1111 %                                                                             %
1112 %   S e t I m a g e T y p e                                                   %
1113 %                                                                             %
1114 %                                                                             %
1115 %                                                                             %
1116 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1117 %
1118 %  SetImageType() sets the type of image.  Choose from these types:
1119 %
1120 %        Bilevel        Grayscale       GrayscaleMatte
1121 %        Palette        PaletteMatte    TrueColor
1122 %        TrueColorMatte ColorSeparation ColorSeparationMatte
1123 %        OptimizeType
1124 %
1125 %  The format of the SetImageType method is:
1126 %
1127 %      MagickBooleanType SetImageType(Image *image,const ImageType type,
1128 %        ExceptionInfo *exception)
1129 %
1130 %  A description of each parameter follows:
1131 %
1132 %    o image: the image.
1133 %
1134 %    o type: Image type.
1135 %
1136 %    o exception: return any errors or warnings in this structure.
1137 %
1138 */
1139 MagickExport MagickBooleanType SetImageType(Image *image,const ImageType type,
1140   ExceptionInfo *exception)
1141 {
1142   const char
1143     *artifact;
1144
1145   ImageInfo
1146     *image_info;
1147
1148   MagickBooleanType
1149     status;
1150
1151   QuantizeInfo
1152     *quantize_info;
1153
1154   assert(image != (Image *) NULL);
1155   if (image->debug != MagickFalse)
1156     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
1157   assert(image->signature == MagickSignature);
1158   status=MagickTrue;
1159   image_info=AcquireImageInfo();
1160   image_info->dither=image->dither;
1161   artifact=GetImageArtifact(image,"dither");
1162   if (artifact != (const char *) NULL)
1163     (void) SetImageOption(image_info,"dither",artifact);
1164   switch (type)
1165   {
1166     case BilevelType:
1167     {
1168       if (IsImageGray(image,exception) == MagickFalse)
1169         status=TransformImageColorspace(image,GRAYColorspace,exception);
1170       (void) NormalizeImage(image,exception);
1171       if (IsImageMonochrome(image,exception) == MagickFalse)
1172         {
1173           quantize_info=AcquireQuantizeInfo(image_info);
1174           quantize_info->number_colors=2;
1175           quantize_info->colorspace=GRAYColorspace;
1176           status=QuantizeImage(quantize_info,image,exception);
1177           quantize_info=DestroyQuantizeInfo(quantize_info);
1178         }
1179       image->alpha_trait=UndefinedPixelTrait;
1180       break;
1181     }
1182     case GrayscaleType:
1183     {
1184       if (IsImageGray(image,exception) == MagickFalse)
1185         status=TransformImageColorspace(image,GRAYColorspace,exception);
1186       image->alpha_trait=UndefinedPixelTrait;
1187       break;
1188     }
1189     case GrayscaleMatteType:
1190     {
1191       if (IsImageGray(image,exception) == MagickFalse)
1192         status=TransformImageColorspace(image,GRAYColorspace,exception);
1193       if (image->alpha_trait != BlendPixelTrait)
1194         (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
1195       break;
1196     }
1197     case PaletteType:
1198     {
1199       if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
1200         status=TransformImageColorspace(image,sRGBColorspace,exception);
1201       if ((image->storage_class == DirectClass) || (image->colors > 256))
1202         {
1203           quantize_info=AcquireQuantizeInfo(image_info);
1204           quantize_info->number_colors=256;
1205           status=QuantizeImage(quantize_info,image,exception);
1206           quantize_info=DestroyQuantizeInfo(quantize_info);
1207         }
1208       image->alpha_trait=UndefinedPixelTrait;
1209       break;
1210     }
1211     case PaletteBilevelMatteType:
1212     {
1213       ChannelType
1214         channel_mask;
1215
1216       if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
1217         status=TransformImageColorspace(image,sRGBColorspace,exception);
1218       if (image->alpha_trait != BlendPixelTrait)
1219         (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
1220       channel_mask=SetImageChannelMask(image,AlphaChannel);
1221       (void) BilevelImage(image,(double) QuantumRange/2.0,exception);
1222       (void) SetImageChannelMask(image,channel_mask);
1223       quantize_info=AcquireQuantizeInfo(image_info);
1224       status=QuantizeImage(quantize_info,image,exception);
1225       quantize_info=DestroyQuantizeInfo(quantize_info);
1226       break;
1227     }
1228     case PaletteMatteType:
1229     {
1230       if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
1231         status=TransformImageColorspace(image,sRGBColorspace,exception);
1232       if (image->alpha_trait != BlendPixelTrait)
1233         (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
1234       quantize_info=AcquireQuantizeInfo(image_info);
1235       quantize_info->colorspace=TransparentColorspace;
1236       status=QuantizeImage(quantize_info,image,exception);
1237       quantize_info=DestroyQuantizeInfo(quantize_info);
1238       break;
1239     }
1240     case TrueColorType:
1241     {
1242       if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
1243         status=TransformImageColorspace(image,sRGBColorspace,exception);
1244       if (image->storage_class != DirectClass)
1245         status=SetImageStorageClass(image,DirectClass,exception);
1246       image->alpha_trait=UndefinedPixelTrait;
1247       break;
1248     }
1249     case TrueColorMatteType:
1250     {
1251       if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
1252         status=TransformImageColorspace(image,sRGBColorspace,exception);
1253       if (image->storage_class != DirectClass)
1254         status=SetImageStorageClass(image,DirectClass,exception);
1255       if (image->alpha_trait != BlendPixelTrait)
1256         (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
1257       break;
1258     }
1259     case ColorSeparationType:
1260     {
1261       if (image->colorspace != CMYKColorspace)
1262         {
1263           if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
1264             status=TransformImageColorspace(image,sRGBColorspace,exception);
1265           status=TransformImageColorspace(image,CMYKColorspace,exception);
1266         }
1267       if (image->storage_class != DirectClass)
1268         status=SetImageStorageClass(image,DirectClass,exception);
1269       image->alpha_trait=UndefinedPixelTrait;
1270       break;
1271     }
1272     case ColorSeparationMatteType:
1273     {
1274       if (image->colorspace != CMYKColorspace)
1275         {
1276           if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
1277             status=TransformImageColorspace(image,sRGBColorspace,exception);
1278           status=TransformImageColorspace(image,CMYKColorspace,exception);
1279         }
1280       if (image->storage_class != DirectClass)
1281         status=SetImageStorageClass(image,DirectClass,exception);
1282       if (image->alpha_trait != BlendPixelTrait)
1283         status=SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
1284       break;
1285     }
1286     case OptimizeType:
1287     case UndefinedType:
1288       break;
1289   }
1290   image_info=DestroyImageInfo(image_info);
1291   if (status == MagickFalse)
1292     return(status);
1293   image->type=type;
1294   return(MagickTrue);
1295 }