]> granicus.if.org Git - imagemagick/blob - magick/attribute.c
(no commit message)
[imagemagick] / magick / attribute.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
6 %         AAA   TTTTT  TTTTT  RRRR   IIIII  BBBB   U   U  TTTTT  EEEEE        %
7 %        A   A    T      T    R   R    I    B   B  U   U    T    E            %
8 %        AAAAA    T      T    RRRR     I    BBBB   U   U    T    EEE          %
9 %        A   A    T      T    R R      I    B   B  U   U    T    E            %
10 %        A   A    T      T    R  R   IIIII  BBBB    UUU     T    EEEEE        %
11 %                                                                             %
12 %                                                                             %
13 %                    MagickCore Get / Set Image Attributes                    %
14 %                                                                             %
15 %                              Software Design                                %
16 %                                John Cristy                                  %
17 %                                October 2002                                 %
18 %                                                                             %
19 %                                                                             %
20 %  Copyright 1999-2010 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 "magick/studio.h"
44 #include "magick/attribute.h"
45 #include "magick/blob.h"
46 #include "magick/blob-private.h"
47 #include "magick/cache.h"
48 #include "magick/cache-view.h"
49 #include "magick/client.h"
50 #include "magick/color.h"
51 #include "magick/color-private.h"
52 #include "magick/colormap.h"
53 #include "magick/colormap-private.h"
54 #include "magick/colorspace.h"
55 #include "magick/composite.h"
56 #include "magick/composite-private.h"
57 #include "magick/constitute.h"
58 #include "magick/deprecate.h"
59 #include "magick/draw.h"
60 #include "magick/draw-private.h"
61 #include "magick/effect.h"
62 #include "magick/enhance.h"
63 #include "magick/exception.h"
64 #include "magick/exception-private.h"
65 #include "magick/geometry.h"
66 #include "magick/histogram.h"
67 #include "magick/identify.h"
68 #include "magick/image.h"
69 #include "magick/image-private.h"
70 #include "magick/list.h"
71 #include "magick/log.h"
72 #include "magick/memory_.h"
73 #include "magick/magick.h"
74 #include "magick/monitor.h"
75 #include "magick/monitor-private.h"
76 #include "magick/paint.h"
77 #include "magick/pixel.h"
78 #include "magick/pixel-private.h"
79 #include "magick/property.h"
80 #include "magick/quantize.h"
81 #include "magick/random_.h"
82 #include "magick/resource_.h"
83 #include "magick/semaphore.h"
84 #include "magick/segment.h"
85 #include "magick/splay-tree.h"
86 #include "magick/string_.h"
87 #include "magick/thread-private.h"
88 #include "magick/threshold.h"
89 #include "magick/transform.h"
90 #include "magick/utility.h"
91 \f
92 /*
93 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
94 %                                                                             %
95 %                                                                             %
96 %                                                                             %
97 +   G e t I m a g e B o u n d i n g B o x                                     %
98 %                                                                             %
99 %                                                                             %
100 %                                                                             %
101 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
102 %
103 %  GetImageBoundingBox() returns the bounding box of an image canvas.
104 %
105 %  The format of the GetImageBoundingBox method is:
106 %
107 %      RectangleInfo GetImageBoundingBox(const Image *image,
108 %        ExceptionInfo *exception)
109 %
110 %  A description of each parameter follows:
111 %
112 %    o bounds: Method GetImageBoundingBox returns the bounding box of an
113 %      image canvas.
114 %
115 %    o image: the image.
116 %
117 %    o exception: return any errors or warnings in this structure.
118 %
119 */
120 MagickExport RectangleInfo GetImageBoundingBox(const Image *image,
121   ExceptionInfo *exception)
122 {
123   CacheView
124     *image_view;
125
126   ssize_t
127     y;
128
129   MagickBooleanType
130     status;
131
132   MagickPixelPacket
133     target[3],
134     zero;
135
136   RectangleInfo
137     bounds;
138
139   register const PixelPacket
140     *p;
141
142   assert(image != (Image *) NULL);
143   assert(image->signature == MagickSignature);
144   if (image->debug != MagickFalse)
145     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
146   bounds.width=0;
147   bounds.height=0;
148   bounds.x=(ssize_t) image->columns;
149   bounds.y=(ssize_t) image->rows;
150   GetMagickPixelPacket(image,&target[0]);
151   image_view=AcquireCacheView(image);
152   p=GetCacheViewVirtualPixels(image_view,0,0,1,1,exception);
153   if (p == (const PixelPacket *) NULL)
154     {
155       image_view=DestroyCacheView(image_view);
156       return(bounds);
157     }
158   SetMagickPixelPacket(image,p,GetCacheViewAuthenticIndexQueue(image_view),
159     &target[0]);
160   GetMagickPixelPacket(image,&target[1]);
161   p=GetCacheViewVirtualPixels(image_view,(ssize_t) image->columns-1,0,1,1,
162     exception);
163   SetMagickPixelPacket(image,p,GetCacheViewAuthenticIndexQueue(image_view),
164     &target[1]);
165   GetMagickPixelPacket(image,&target[2]);
166   p=GetCacheViewVirtualPixels(image_view,0,(ssize_t) image->rows-1,1,1,exception);
167   SetMagickPixelPacket(image,p,GetCacheViewAuthenticIndexQueue(image_view),
168     &target[2]);
169   status=MagickTrue;
170   GetMagickPixelPacket(image,&zero);
171 #if defined(MAGICKCORE_OPENMP_SUPPORT)
172   #pragma omp parallel for schedule(dynamic,4) shared(status)
173 #endif
174   for (y=0; y < (ssize_t) image->rows; y++)
175   {
176     MagickPixelPacket
177       pixel;
178
179     RectangleInfo
180       bounding_box;
181
182     register const IndexPacket
183       *restrict indexes;
184
185     register const PixelPacket
186       *restrict p;
187
188     register ssize_t
189       x;
190
191     if (status == MagickFalse)
192       continue;
193 #if defined(MAGICKCORE_OPENMP_SUPPORT)
194 #  pragma omp critical (MagickCore_GetImageBoundingBox)
195 #endif
196     bounding_box=bounds;
197     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
198     if (p == (const PixelPacket *) NULL)
199       {
200         status=MagickFalse;
201         continue;
202       }
203     indexes=GetCacheViewVirtualIndexQueue(image_view);
204     pixel=zero;
205     for (x=0; x < (ssize_t) image->columns; x++)
206     {
207       SetMagickPixelPacket(image,p,indexes+x,&pixel);
208       if ((x < bounding_box.x) &&
209           (IsMagickColorSimilar(&pixel,&target[0]) == MagickFalse))
210         bounding_box.x=x;
211       if ((x > (ssize_t) bounding_box.width) &&
212           (IsMagickColorSimilar(&pixel,&target[1]) == MagickFalse))
213         bounding_box.width=(size_t) x;
214       if ((y < bounding_box.y) &&
215           (IsMagickColorSimilar(&pixel,&target[0]) == MagickFalse))
216         bounding_box.y=y;
217       if ((y > (ssize_t) bounding_box.height) &&
218           (IsMagickColorSimilar(&pixel,&target[2]) == MagickFalse))
219         bounding_box.height=(size_t) y;
220       p++;
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 C h a n n e l D e p t h                                   %
254 %                                                                             %
255 %                                                                             %
256 %                                                                             %
257 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
258 %
259 %  GetImageChannelDepth() returns the depth of a particular image channel.
260 %
261 %  The format of the GetImageChannelDepth method is:
262 %
263 %      size_t GetImageDepth(const Image *image,ExceptionInfo *exception)
264 %      size_t GetImageChannelDepth(const Image *image,
265 %        const ChannelType channel,ExceptionInfo *exception)
266 %
267 %  A description of each parameter follows:
268 %
269 %    o image: the image.
270 %
271 %    o channel: the channel.
272 %
273 %    o exception: return any errors or warnings in this structure.
274 %
275 */
276
277 static inline QuantumAny GetPixelDepth(const Quantum pixel,
278   const QuantumAny scale)
279 {
280 #if !defined(MAGICKCORE_HDRI_SUPPORT)
281   return((QuantumAny) (scale*(pixel/scale)));
282 #else
283   return((QuantumAny) (scale*(pixel/scale)+0.5));
284 #endif
285 }
286
287 MagickExport size_t GetImageDepth(const Image *image,ExceptionInfo *exception)
288 {
289   return(GetImageChannelDepth(image,AllChannels,exception));
290 }
291
292 MagickExport size_t GetImageChannelDepth(const Image *image,
293   const ChannelType channel,ExceptionInfo *exception)
294 {
295   CacheView
296     *image_view;
297
298   ssize_t
299     y;
300
301   MagickBooleanType
302     status;
303
304   register ssize_t
305     id;
306
307   size_t
308     *current_depth,
309     depth,
310     number_threads;
311
312   /*
313     Compute image depth.
314   */
315   assert(image != (Image *) NULL);
316   assert(image->signature == MagickSignature);
317   if (image->debug != MagickFalse)
318     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
319   number_threads=GetOpenMPMaximumThreads();
320   current_depth=(size_t *) AcquireQuantumMemory(number_threads,
321     sizeof(*current_depth));
322   if (current_depth == (size_t *) NULL)
323     ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
324   status=MagickTrue;
325   for (id=0; id < (ssize_t) number_threads; id++)
326     current_depth[id]=1;
327   if ((image->storage_class == PseudoClass) && (image->matte == MagickFalse))
328     {
329       register const PixelPacket
330         *restrict p;
331
332       register ssize_t
333         i;
334
335       p=image->colormap;
336 #if defined(MAGICKCORE_OPENMP_SUPPORT)
337   #pragma omp parallel for schedule(dynamic,4) shared(status)
338 #endif
339       for (i=0; i < (ssize_t) image->colors; i++)
340       {
341         if (status == MagickFalse)
342           continue;
343         id=GetOpenMPThreadId();
344         while (current_depth[id] < MAGICKCORE_QUANTUM_DEPTH)
345         {
346           MagickStatusType
347             status;
348
349           QuantumAny
350             scale;
351
352           status=0;
353           scale=QuantumRange/((QuantumAny) QuantumRange >>
354             (MAGICKCORE_QUANTUM_DEPTH-current_depth[id]));
355           if ((channel & RedChannel) != 0)
356             status|=(QuantumAny) p->red != GetPixelDepth(p->red,scale);
357           if ((channel & GreenChannel) != 0)
358             status|=(QuantumAny) p->green != GetPixelDepth(p->green,scale);
359           if ((channel & BlueChannel) != 0)
360             status|=(QuantumAny) p->blue != GetPixelDepth(p->blue,scale);
361           if (status == 0)
362             break;
363           current_depth[id]++;
364         }
365         p++;
366       }
367       depth=current_depth[0];
368       for (id=1; id < (ssize_t) number_threads; id++)
369         if (depth < current_depth[id])
370           depth=current_depth[id];
371       current_depth=(size_t *) RelinquishMagickMemory(current_depth);
372       return(depth);
373     }
374   image_view=AcquireCacheView(image);
375 #if defined(MAGICKCORE_OPENMP_SUPPORT)
376   #pragma omp parallel for schedule(dynamic,4) shared(status)
377 #endif
378   for (y=0; y < (ssize_t) image->rows; y++)
379   {
380     register const IndexPacket
381       *restrict indexes;
382
383     register const PixelPacket
384       *restrict p;
385
386     register ssize_t
387       id,
388       x;
389
390     if (status == MagickFalse)
391       continue;
392     id=GetOpenMPThreadId();
393     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
394     if (p == (const PixelPacket *) NULL)
395       continue;
396     indexes=GetCacheViewVirtualIndexQueue(image_view);
397     for (x=0; x < (ssize_t) image->columns; x++)
398     {
399       while (current_depth[id] < MAGICKCORE_QUANTUM_DEPTH)
400       {
401         MagickStatusType
402           status;
403
404         QuantumAny
405           scale;
406
407         status=0;
408         scale=QuantumRange/((QuantumAny) QuantumRange >>
409           (MAGICKCORE_QUANTUM_DEPTH-current_depth[id]));
410         if ((channel & RedChannel) != 0)
411           status|=(QuantumAny) p->red != GetPixelDepth(p->red,scale);
412         if ((channel & GreenChannel) != 0)
413           status|=(QuantumAny) p->green != GetPixelDepth(p->green,scale);
414         if ((channel & BlueChannel) != 0)
415           status|=(QuantumAny) p->blue != GetPixelDepth(p->blue,scale);
416         if (((channel & OpacityChannel) != 0) && (image->matte != MagickFalse))
417           status|=(QuantumAny) p->opacity != GetPixelDepth(p->opacity,scale);
418         if (((channel & IndexChannel) != 0) &&
419             (image->colorspace == CMYKColorspace))
420           status|=(QuantumAny) indexes[x] != GetPixelDepth(indexes[x],scale);
421         if (status == 0)
422           break;
423         current_depth[id]++;
424       }
425       p++;
426     }
427     if (current_depth[id] == MAGICKCORE_QUANTUM_DEPTH)
428       status=MagickFalse;
429   }
430   image_view=DestroyCacheView(image_view);
431   depth=current_depth[0];
432   for (id=1; id < (ssize_t) number_threads; id++)
433     if (depth < current_depth[id])
434       depth=current_depth[id];
435   current_depth=(size_t *) RelinquishMagickMemory(current_depth);
436   return(depth);
437 }
438 \f
439 /*
440 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
441 %                                                                             %
442 %                                                                             %
443 %                                                                             %
444 %   G e t I m a g e Q u a n t u m D e p t h                                   %
445 %                                                                             %
446 %                                                                             %
447 %                                                                             %
448 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
449 %
450 %  GetImageQuantumDepth() returns the depth of the image rounded to a legal
451 %  quantum depth: 8, 16, or 32.
452 %
453 %  The format of the GetImageQuantumDepth method is:
454 %
455 %      size_t GetImageQuantumDepth(const Image *image,
456 %        const MagickBooleanType constrain)
457 %
458 %  A description of each parameter follows:
459 %
460 %    o image: the image.
461 %
462 %    o constrain: A value other than MagickFalse, constrains the depth to
463 %      a maximum of MAGICKCORE_QUANTUM_DEPTH.
464 %
465 */
466
467 static inline double MagickMin(const double x,const double y)
468 {
469   if (x < y)
470     return(x);
471   return(y);
472 }
473
474 MagickExport size_t GetImageQuantumDepth(const Image *image,
475   const MagickBooleanType constrain)
476 {
477   size_t
478     depth;
479
480   depth=image->depth;
481   if (depth <= 8)
482     depth=8;
483   else
484     if (depth <= 16)
485       depth=16;
486     else
487       if (depth <= 32)
488         depth=32;
489       else
490         if (depth <= 64)
491           depth=64;
492   if (constrain != MagickFalse)
493     depth=(size_t) MagickMin((double) depth,(double)
494       MAGICKCORE_QUANTUM_DEPTH);
495   return(depth);
496 }
497 \f
498 /*
499 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
500 %                                                                             %
501 %                                                                             %
502 %                                                                             %
503 %   G e t I m a g e T y p e                                                   %
504 %                                                                             %
505 %                                                                             %
506 %                                                                             %
507 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
508 %
509 %  GetImageType() returns the potential type of image:
510 %
511 %        Bilevel         Grayscale        GrayscaleMatte
512 %        Palette         PaletteMatte     TrueColor
513 %        TrueColorMatte  ColorSeparation  ColorSeparationMatte
514 %
515 %  To ensure the image type matches its potential, use SetImageType():
516 %
517 %    (void) SetImageType(image,GetImageType(image));
518 %
519 %  The format of the GetImageType method is:
520 %
521 %      ImageType GetImageType(const Image *image,ExceptionInfo *exception)
522 %
523 %  A description of each parameter follows:
524 %
525 %    o image: the image.
526 %
527 %    o exception: return any errors or warnings in this structure.
528 %
529 */
530 MagickExport ImageType GetImageType(const Image *image,ExceptionInfo *exception)
531 {
532   assert(image != (Image *) NULL);
533   assert(image->signature == MagickSignature);
534   if (image->debug != MagickFalse)
535     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
536   if (image->colorspace == CMYKColorspace)
537     {
538       if (image->matte == MagickFalse)
539         return(ColorSeparationType);
540       return(ColorSeparationMatteType);
541     }
542   if (IsMonochromeImage(image,exception) != MagickFalse)
543     return(BilevelType);
544   if (IsGrayImage(image,exception) != MagickFalse)
545     {
546       if (image->matte != MagickFalse)
547         return(GrayscaleMatteType);
548       return(GrayscaleType);
549     }
550   if (IsPaletteImage(image,exception) != MagickFalse)
551     {
552       if (image->matte != MagickFalse)
553         return(PaletteMatteType);
554       return(PaletteType);
555     }
556   if (image->matte != MagickFalse)
557     return(TrueColorMatteType);
558   return(TrueColorType);
559 }
560 \f
561 /*
562 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
563 %                                                                             %
564 %                                                                             %
565 %                                                                             %
566 %     I s G r a y I m a g e                                                   %
567 %                                                                             %
568 %                                                                             %
569 %                                                                             %
570 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
571 %
572 %  IsGrayImage() returns MagickTrue if all the pixels in the image have the
573 %  same red, green, and blue intensities.
574 %
575 %  The format of the IsGrayImage method is:
576 %
577 %      MagickBooleanType IsGrayImage(const Image *image,
578 %        ExceptionInfo *exception)
579 %
580 %  A description of each parameter follows:
581 %
582 %    o image: the image.
583 %
584 %    o exception: return any errors or warnings in this structure.
585 %
586 */
587 MagickExport MagickBooleanType IsGrayImage(const Image *image,
588   ExceptionInfo *exception)
589 {
590   CacheView
591     *image_view;
592
593   ImageType
594     type;
595
596   ssize_t
597     y;
598
599   register const PixelPacket
600     *p;
601
602   register ssize_t
603     x;
604
605   assert(image != (Image *) NULL);
606   assert(image->signature == MagickSignature);
607   if (image->debug != MagickFalse)
608     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
609   if ((image->type == BilevelType) || (image->type == GrayscaleType) ||
610       (image->type == GrayscaleMatteType))
611     return(MagickTrue);
612   if (image->colorspace == CMYKColorspace)
613     return(MagickFalse);
614   type=BilevelType;
615   image_view=AcquireCacheView(image);
616   for (y=0; y < (ssize_t) image->rows; y++)
617   {
618     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
619     if (p == (const PixelPacket *) NULL)
620       break;
621     for (x=0; x < (ssize_t) image->columns; x++)
622     {
623       if (IsGrayPixel(p) == MagickFalse)
624         {
625           type=UndefinedType;
626           break;
627         }
628       if ((type == BilevelType) && (IsMonochromePixel(p) == MagickFalse))
629         type=GrayscaleType;
630       p++;
631     }
632     if (type == UndefinedType)
633       break;
634   }
635   image_view=DestroyCacheView(image_view);
636   if (type == UndefinedType)
637     return(MagickFalse);
638   ((Image *) image)->type=type;
639   if ((type == GrayscaleType) && (image->matte != MagickFalse))
640     ((Image *) image)->type=GrayscaleMatteType;
641   return(MagickTrue);
642 }
643 \f
644 /*
645 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
646 %                                                                             %
647 %                                                                             %
648 %                                                                             %
649 %   I s M o n o c h r o m e I m a g e                                         %
650 %                                                                             %
651 %                                                                             %
652 %                                                                             %
653 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
654 %
655 %  IsMonochromeImage() returns MagickTrue if all the pixels in the image have
656 %  the same red, green, and blue intensities and the intensity is either
657 %  0 or QuantumRange.
658 %
659 %  The format of the IsMonochromeImage method is:
660 %
661 %      MagickBooleanType IsMonochromeImage(const Image *image,
662 %        ExceptionInfo *exception)
663 %
664 %  A description of each parameter follows:
665 %
666 %    o image: the image.
667 %
668 %    o exception: return any errors or warnings in this structure.
669 %
670 */
671 MagickExport MagickBooleanType IsMonochromeImage(const Image *image,
672   ExceptionInfo *exception)
673 {
674   CacheView
675     *image_view;
676
677   ImageType
678     type;
679
680   ssize_t
681     y;
682
683   register ssize_t
684     x;
685
686   register const PixelPacket
687     *p;
688
689   assert(image != (Image *) NULL);
690   assert(image->signature == MagickSignature);
691   if (image->debug != MagickFalse)
692     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
693   if (image->type == BilevelType)
694     return(MagickTrue);
695   if (image->colorspace == CMYKColorspace)
696     return(MagickFalse);
697   type=BilevelType;
698   image_view=AcquireCacheView(image);
699   for (y=0; y < (ssize_t) image->rows; y++)
700   {
701     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
702     if (p == (const PixelPacket *) NULL)
703       break;
704     for (x=0; x < (ssize_t) image->columns; x++)
705     {
706       if (IsMonochromePixel(p) == MagickFalse)
707         {
708           type=UndefinedType;
709           break;
710         }
711       p++;
712     }
713     if (type == UndefinedType)
714       break;
715   }
716   image_view=DestroyCacheView(image_view);
717   if (type == UndefinedType)
718     return(MagickFalse);
719   ((Image *) image)->type=type;
720   return(MagickTrue);
721 }
722 \f
723 /*
724 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
725 %                                                                             %
726 %                                                                             %
727 %                                                                             %
728 %     I s O p a q u e I m a g e                                               %
729 %                                                                             %
730 %                                                                             %
731 %                                                                             %
732 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
733 %
734 %  IsOpaqueImage() returns MagickTrue if none of the pixels in the image have
735 %  an opacity value other than opaque (0).
736 %
737 %  The format of the IsOpaqueImage method is:
738 %
739 %      MagickBooleanType IsOpaqueImage(const Image *image,
740 %        ExceptionInfo *exception)
741 %
742 %  A description of each parameter follows:
743 %
744 %    o image: the image.
745 %
746 %    o exception: return any errors or warnings in this structure.
747 %
748 */
749 MagickExport MagickBooleanType IsOpaqueImage(const Image *image,
750   ExceptionInfo *exception)
751 {
752   CacheView
753     *image_view;
754
755   ssize_t
756     y;
757
758   register const PixelPacket
759     *p;
760
761   register ssize_t
762     x;
763
764   /*
765     Determine if image is opaque.
766   */
767   assert(image != (Image *) NULL);
768   assert(image->signature == MagickSignature);
769   if (image->debug != MagickFalse)
770     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
771   if (image->matte == MagickFalse)
772     return(MagickTrue);
773   image_view=AcquireCacheView(image);
774   for (y=0; y < (ssize_t) image->rows; y++)
775   {
776     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
777     if (p == (const PixelPacket *) NULL)
778       break;
779     for (x=0; x < (ssize_t) image->columns; x++)
780     {
781       if (p->opacity != OpaqueOpacity)
782         break;
783       p++;
784     }
785     if (x < (ssize_t) image->columns)
786      break;
787   }
788   image_view=DestroyCacheView(image_view);
789   return(y < (ssize_t) image->rows ? MagickFalse : MagickTrue);
790 }
791 \f
792 /*
793 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
794 %                                                                             %
795 %                                                                             %
796 %                                                                             %
797 %   S e t I m a g e C h a n n e l D e p t h                                   %
798 %                                                                             %
799 %                                                                             %
800 %                                                                             %
801 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
802 %
803 %  SetImageChannelDepth() sets the depth of the image.
804 %
805 %  The format of the SetImageChannelDepth method is:
806 %
807 %      MagickBooleanType SetImageDepth(Image *image,const size_t depth)
808 %      MagickBooleanType SetImageChannelDepth(Image *image,
809 %        const ChannelType channel,const size_t depth)
810 %
811 %  A description of each parameter follows:
812 %
813 %    o image: the image.
814 %
815 %    o channel: the channel.
816 %
817 %    o depth: the image depth.
818 %
819 */
820
821 static inline Quantum SetPixelDepth(const Quantum pixel,
822   const QuantumAny scale)
823 {
824   return((Quantum) (scale*(pixel/scale)));
825 }
826
827 MagickExport MagickBooleanType SetImageDepth(Image *image,
828   const size_t depth)
829 {
830   return(SetImageChannelDepth(image,AllChannels,depth));
831 }
832
833 MagickExport MagickBooleanType SetImageChannelDepth(Image *image,
834   const ChannelType channel,const size_t depth)
835 {
836   CacheView
837     *image_view;
838
839   ExceptionInfo
840     *exception;
841
842   ssize_t
843     y;
844
845   MagickBooleanType
846     status;
847
848   QuantumAny
849     scale;
850
851   assert(image != (Image *) NULL);
852   if (image->debug != MagickFalse)
853     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
854   assert(image->signature == MagickSignature);
855   if (GetImageDepth(image,&image->exception) <= (size_t)
856       MagickMin((double) depth,(double) MAGICKCORE_QUANTUM_DEPTH))
857     {
858       image->depth=depth;
859       return(MagickTrue);
860     }
861   /*
862     Scale pixels to desired depth.
863   */
864   status=MagickTrue;
865   scale=QuantumRange/((QuantumAny) QuantumRange >> (MAGICKCORE_QUANTUM_DEPTH-
866     depth));
867   exception=(&image->exception);
868   image_view=AcquireCacheView(image);
869 #if defined(MAGICKCORE_OPENMP_SUPPORT)
870   #pragma omp parallel for schedule(dynamic,4) shared(status)
871 #endif
872   for (y=0; y < (ssize_t) image->rows; y++)
873   {
874     register IndexPacket
875       *restrict indexes;
876
877     register ssize_t
878       x;
879
880     register PixelPacket
881       *restrict q;
882
883     if (status == MagickFalse)
884       continue;
885     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
886       exception);
887     if (q == (PixelPacket *) NULL)
888       {
889         status=MagickFalse;
890         continue;
891       }
892     indexes=GetCacheViewAuthenticIndexQueue(image_view);
893     for (x=0; x < (ssize_t) image->columns; x++)
894     {
895       if ((channel & RedChannel) != 0)
896         q->red=SetPixelDepth(q->red,scale);
897       if ((channel & GreenChannel) != 0)
898         q->green=SetPixelDepth(q->green,scale);
899       if ((channel & BlueChannel) != 0)
900         q->green=SetPixelDepth(q->blue,scale);
901       if (((channel & OpacityChannel) != 0) && (image->matte != MagickFalse))
902         q->opacity=SetPixelDepth(q->opacity,scale);
903       if (((channel & IndexChannel) != 0) &&
904           (image->colorspace == CMYKColorspace))
905         indexes[x]=SetPixelDepth(indexes[x],scale);
906       q++;
907     }
908     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
909       {
910         status=MagickFalse;
911         continue;
912       }
913   }
914   image_view=DestroyCacheView(image_view);
915   if (image->storage_class == PseudoClass)
916     {
917       QuantumAny
918         range;
919
920       register ssize_t
921         i;
922
923       register PixelPacket
924         *restrict p;
925
926       p=image->colormap;
927       range=GetQuantumRange(depth);
928 #if defined(MAGICKCORE_OPENMP_SUPPORT)
929   #pragma omp parallel for schedule(dynamic,4) shared(status)
930 #endif
931       for (i=0; i < (ssize_t) image->colors; i++)
932       {
933         if ((channel & RedChannel) != 0)
934           p->red=SetPixelDepth(p->red,scale);
935         if ((channel & GreenChannel) != 0)
936           p->green=SetPixelDepth(p->green,scale);
937         if ((channel & BlueChannel) != 0)
938           p->blue=SetPixelDepth(p->blue,scale);
939         if ((channel & OpacityChannel) != 0)
940           p->opacity=SetPixelDepth(p->opacity,scale);
941         p++;
942       }
943     }
944   image->depth=depth;
945   return(status);
946 }