]> granicus.if.org Git - imagemagick/blob - magick/histogram.c
(no commit message)
[imagemagick] / magick / histogram.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
6 %      H   H   IIIII   SSSSS  TTTTT   OOO    GGGG  RRRR    AAA   M   M        %
7 %      H   H     I     SS       T    O   O  G      R   R  A   A  MM MM        %
8 %      HHHHH     I      SSS     T    O   O  G  GG  RRRR   AAAAA  M M M        %
9 %      H   H     I        SS    T    O   O  G   G  R R    A   A  M   M        %
10 %      H   H   IIIII   SSSSS    T     OOO    GGG   R  R   A   A  M   M        %
11 %                                                                             %
12 %                                                                             %
13 %                        MagickCore Histogram Methods                         %
14 %                                                                             %
15 %                              Software Design                                %
16 %                              Anthony Thyssen                                %
17 %                               Fred Weinhaus                                 %
18 %                                August 2009                                  %
19 %                                                                             %
20 %                                                                             %
21 %  Copyright 1999-2009 ImageMagick Studio LLC, a non-profit organization      %
22 %  dedicated to making software imaging solutions freely available.           %
23 %                                                                             %
24 %  You may not use this file except in compliance with the License.  You may  %
25 %  obtain a copy of the License at                                            %
26 %                                                                             %
27 %    http://www.imagemagick.org/script/license.php                            %
28 %                                                                             %
29 %  Unless required by applicable law or agreed to in writing, software        %
30 %  distributed under the License is distributed on an "AS IS" BASIS,          %
31 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
32 %  See the License for the specific language governing permissions and        %
33 %  limitations under the License.                                             %
34 %                                                                             %
35 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
36 %
37 %
38 */
39 \f
40 /*
41   Include declarations.
42 */
43 #include "magick/studio.h"
44 #include "magick/cache-view.h"
45 #include "magick/color-private.h"
46 #include "magick/enhance.h"
47 #include "magick/exception.h"
48 #include "magick/exception-private.h"
49 #include "magick/hashmap.h"
50 #include "magick/histogram.h"
51 #include "magick/image.h"
52 #include "magick/list.h"
53 #include "magick/memory_.h"
54 #include "magick/monitor-private.h"
55 #include "magick/pixel-private.h"
56 #include "magick/prepress.h"
57 #include "magick/quantize.h"
58 #include "magick/registry.h"
59 #include "magick/semaphore.h"
60 #include "magick/splay-tree.h"
61 #include "magick/statistic.h"
62 #include "magick/string_.h"
63 \f
64 /*
65   Define declarations.
66 */
67 #define MaxTreeDepth  8
68 #define NodesInAList  1536
69 \f
70 /*
71   Typedef declarations.
72 */
73 typedef struct _NodeInfo
74 {
75   struct _NodeInfo
76     *child[16];
77
78   ColorPacket
79     *list;
80
81   MagickSizeType
82     number_unique;
83
84   unsigned long
85     level;
86 } NodeInfo;
87
88 typedef struct _Nodes
89 {
90   NodeInfo
91     nodes[NodesInAList];
92
93   struct _Nodes
94     *next;
95 } Nodes;
96
97 typedef struct _CubeInfo
98 {
99   NodeInfo
100     *root;
101
102   long
103     x,
104     progress;
105
106   unsigned long
107     colors,
108     free_nodes;
109
110   NodeInfo
111     *node_info;
112
113   Nodes
114     *node_queue;
115 } CubeInfo;
116 \f
117 /*
118   Forward declarations.
119 */
120 static CubeInfo
121   *GetCubeInfo(void);
122
123 static NodeInfo
124   *GetNodeInfo(CubeInfo *,const unsigned long);
125
126 static void
127   DestroyColorCube(const Image *,NodeInfo *);
128 \f
129 /*
130 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
131 %                                                                             %
132 %                                                                             %
133 %                                                                             %
134 +   C l a s s i f y I m a g e C o l o r s                                     %
135 %                                                                             %
136 %                                                                             %
137 %                                                                             %
138 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
139 %
140 %  ClassifyImageColors() builds a populated CubeInfo tree for the specified
141 %  image.  The returned tree should be deallocated using DestroyCubeInfo()
142 %  once it is no longer needed.
143 %
144 %  The format of the ClassifyImageColors() method is:
145 %
146 %      CubeInfo *ClassifyImageColors(const Image *image,
147 %        ExceptionInfo *exception)
148 %
149 %  A description of each parameter follows.
150 %
151 %    o image: the image.
152 %
153 %    o exception: return any errors or warnings in this structure.
154 %
155 */
156
157 static inline unsigned long ColorToNodeId(const Image *image,
158   const MagickPixelPacket *pixel,unsigned long index)
159 {
160   unsigned long
161     id;
162
163   id=(unsigned long) (
164     ((ScaleQuantumToChar(RoundToQuantum(pixel->red)) >> index) & 0x01) |
165     ((ScaleQuantumToChar(RoundToQuantum(pixel->green)) >> index) & 0x01) << 1 |
166     ((ScaleQuantumToChar(RoundToQuantum(pixel->blue)) >> index) & 0x01) << 2);
167   if (image->matte != MagickFalse)
168     id|=((ScaleQuantumToChar(RoundToQuantum(pixel->opacity)) >> index) &
169       0x01) << 3;
170   return(id);
171 }
172
173 static CubeInfo *ClassifyImageColors(const Image *image,
174   ExceptionInfo *exception)
175 {
176 #define EvaluateImageTag  "  Compute image colors...  "
177
178   CubeInfo
179     *cube_info;
180
181   long
182     y;
183
184   MagickBooleanType
185     proceed;
186
187   MagickPixelPacket
188     pixel,
189     target;
190
191   NodeInfo
192     *node_info;
193
194   register const IndexPacket
195     *indexes;
196
197   register const PixelPacket
198     *p;
199
200   register long
201     i,
202     x;
203
204   register unsigned long
205     id,
206     index,
207     level;
208
209   CacheView
210     *image_view;
211
212   /*
213     Initialize color description tree.
214   */
215   assert(image != (const Image *) NULL);
216   assert(image->signature == MagickSignature);
217   if (image->debug != MagickFalse)
218     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
219   cube_info=GetCubeInfo();
220   if (cube_info == (CubeInfo *) NULL)
221     {
222       (void) ThrowMagickException(exception,GetMagickModule(),
223         ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
224       return(cube_info);
225     }
226   GetMagickPixelPacket(image,&pixel);
227   GetMagickPixelPacket(image,&target);
228   image_view=AcquireCacheView(image);
229   for (y=0; y < (long) image->rows; y++)
230   {
231     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
232     if (p == (const PixelPacket *) NULL)
233       break;
234     indexes=GetCacheViewVirtualIndexQueue(image_view);
235     for (x=0; x < (long) image->columns; x++)
236     {
237       /*
238         Start at the root and proceed level by level.
239       */
240       node_info=cube_info->root;
241       index=MaxTreeDepth-1;
242       for (level=1; level < MaxTreeDepth; level++)
243       {
244         SetMagickPixelPacket(image,p,indexes+x,&pixel);
245         id=ColorToNodeId(image,&pixel,index);
246         if (node_info->child[id] == (NodeInfo *) NULL)
247           {
248             node_info->child[id]=GetNodeInfo(cube_info,level);
249             if (node_info->child[id] == (NodeInfo *) NULL)
250               {
251                 (void) ThrowMagickException(exception,GetMagickModule(),
252                   ResourceLimitError,"MemoryAllocationFailed","`%s'",
253                   image->filename);
254                 return(0);
255               }
256           }
257         node_info=node_info->child[id];
258         index--;
259       }
260       for (i=0; i < (long) node_info->number_unique; i++)
261       {
262         SetMagickPixelPacket(image,&node_info->list[i].pixel,
263           &node_info->list[i].index,&target);
264         if (IsMagickColorEqual(&pixel,&target) != MagickFalse)
265           break;
266       }
267       if (i < (long) node_info->number_unique)
268         node_info->list[i].count++;
269       else
270         {
271           if (node_info->number_unique == 0)
272             node_info->list=(ColorPacket *) AcquireMagickMemory(
273               sizeof(*node_info->list));
274           else
275             node_info->list=(ColorPacket *) ResizeQuantumMemory(node_info->list,
276               (size_t) (i+1),sizeof(*node_info->list));
277           if (node_info->list == (ColorPacket *) NULL)
278             {
279               (void) ThrowMagickException(exception,GetMagickModule(),
280                 ResourceLimitError,"MemoryAllocationFailed","`%s'",
281                 image->filename);
282               return(0);
283             }
284           node_info->list[i].pixel=(*p);
285           if ((image->colorspace == CMYKColorspace) ||
286               (image->storage_class == PseudoClass))
287             node_info->list[i].index=indexes[x];
288           node_info->list[i].count=1;
289           node_info->number_unique++;
290           cube_info->colors++;
291         }
292       p++;
293     }
294     proceed=SetImageProgress(image,EvaluateImageTag,y,image->rows);
295     if (proceed == MagickFalse)
296       break;
297   }
298   image_view=DestroyCacheView(image_view);
299   return(cube_info);
300 }
301 \f
302 /*
303 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
304 %                                                                             %
305 %                                                                             %
306 %                                                                             %
307 +   D e f i n e I m a g e H i s t o g r a m                                   %
308 %                                                                             %
309 %                                                                             %
310 %                                                                             %
311 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
312 %
313 %  DefineImageHistogram() traverses the color cube tree and notes each colormap
314 %  entry.  A colormap entry is any node in the color cube tree where the
315 %  of unique colors is not zero.
316 %
317 %  The format of the DefineImageHistogram method is:
318 %
319 %      DefineImageHistogram(const Image *image,NodeInfo *node_info,
320 %        ColorPacket **unique_colors)
321 %
322 %  A description of each parameter follows.
323 %
324 %    o image: the image.
325 %
326 %    o node_info: the address of a structure of type NodeInfo which points to a
327 %      node in the color cube tree that is to be pruned.
328 %
329 %    o histogram: the image histogram.
330 %
331 */
332 static void DefineImageHistogram(const Image *image,NodeInfo *node_info,
333   ColorPacket **histogram)
334 {
335   register long
336     i;
337
338   unsigned long
339     number_children;
340
341   /*
342     Traverse any children.
343   */
344   number_children=image->matte == MagickFalse ? 8UL : 16UL;
345   for (i=0; i < (long) number_children; i++)
346     if (node_info->child[i] != (NodeInfo *) NULL)
347       DefineImageHistogram(image,node_info->child[i],histogram);
348   if (node_info->level == (MaxTreeDepth-1))
349     {
350       register ColorPacket
351         *p;
352
353       p=node_info->list;
354       for (i=0; i < (long) node_info->number_unique; i++)
355       {
356         (*histogram)->pixel=p->pixel;
357         (*histogram)->index=p->index;
358         (*histogram)->count=p->count;
359         (*histogram)++;
360         p++;
361       }
362     }
363 }
364 \f
365 /*
366 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
367 %                                                                             %
368 %                                                                             %
369 %                                                                             %
370 +   D e s t r o y C u b e I n f o                                             %
371 %                                                                             %
372 %                                                                             %
373 %                                                                             %
374 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
375 %
376 %  DestroyCubeInfo() deallocates memory associated with a CubeInfo structure.
377 %
378 %  The format of the DestroyCubeInfo method is:
379 %
380 %      DestroyCubeInfo(const Image *image,CubeInfo *cube_info)
381 %
382 %  A description of each parameter follows:
383 %
384 %    o image: the image.
385 %
386 %    o cube_info: the address of a structure of type CubeInfo.
387 %
388 */
389 static CubeInfo *DestroyCubeInfo(const Image *image,CubeInfo *cube_info)
390 {
391   register Nodes
392     *nodes;
393
394   /*
395     Release color cube tree storage.
396   */
397   DestroyColorCube(image,cube_info->root);
398   do
399   {
400     nodes=cube_info->node_queue->next;
401     cube_info->node_queue=(Nodes *)
402       RelinquishMagickMemory(cube_info->node_queue);
403     cube_info->node_queue=nodes;
404   } while (cube_info->node_queue != (Nodes *) NULL);
405   return((CubeInfo *) RelinquishMagickMemory(cube_info));
406 }
407 \f
408 /*
409 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
410 %                                                                             %
411 %                                                                             %
412 %                                                                             %
413 +  D e s t r o y C o l o r C u b e                                            %
414 %                                                                             %
415 %                                                                             %
416 %                                                                             %
417 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
418 %
419 %  DestroyColorCube() traverses the color cube tree and frees the list of
420 %  unique colors.
421 %
422 %  The format of the DestroyColorCube method is:
423 %
424 %      void DestroyColorCube(const Image *image,const NodeInfo *node_info)
425 %
426 %  A description of each parameter follows.
427 %
428 %    o image: the image.
429 %
430 %    o node_info: the address of a structure of type NodeInfo which points to a
431 %      node in the color cube tree that is to be pruned.
432 %
433 */
434 static void DestroyColorCube(const Image *image,NodeInfo *node_info)
435 {
436   register long
437     i;
438
439   unsigned long
440     number_children;
441
442   /*
443     Traverse any children.
444   */
445   number_children=image->matte == MagickFalse ? 8UL : 16UL;
446   for (i=0; i < (long) number_children; i++)
447     if (node_info->child[i] != (NodeInfo *) NULL)
448       DestroyColorCube(image,node_info->child[i]);
449   if (node_info->list != (ColorPacket *) NULL)
450     node_info->list=(ColorPacket *) RelinquishMagickMemory(node_info->list);
451 }
452 \f
453 /*
454 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
455 %                                                                             %
456 %                                                                             %
457 %                                                                             %
458 +   G e t C u b e I n f o                                                     %
459 %                                                                             %
460 %                                                                             %
461 %                                                                             %
462 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
463 %
464 %  GetCubeInfo() initializes the CubeInfo data structure.
465 %
466 %  The format of the GetCubeInfo method is:
467 %
468 %      cube_info=GetCubeInfo()
469 %
470 %  A description of each parameter follows.
471 %
472 %    o cube_info: A pointer to the Cube structure.
473 %
474 */
475 static CubeInfo *GetCubeInfo(void)
476 {
477   CubeInfo
478     *cube_info;
479
480   /*
481     Initialize tree to describe color cube.
482   */
483   cube_info=(CubeInfo *) AcquireMagickMemory(sizeof(*cube_info));
484   if (cube_info == (CubeInfo *) NULL)
485     return((CubeInfo *) NULL);
486   (void) ResetMagickMemory(cube_info,0,sizeof(*cube_info));
487   /*
488     Initialize root node.
489   */
490   cube_info->root=GetNodeInfo(cube_info,0);
491   if (cube_info->root == (NodeInfo *) NULL)
492     return((CubeInfo *) NULL);
493   return(cube_info);
494 }
495 \f
496 /*
497 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
498 %                                                                             %
499 %                                                                             %
500 %                                                                             %
501 %  G e t I m a g e H i s t o g r a m                                          %
502 %                                                                             %
503 %                                                                             %
504 %                                                                             %
505 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
506 %
507 %  GetImageHistogram() returns the unique colors in an image.
508 %
509 %  The format of the GetImageHistogram method is:
510 %
511 %      unsigned long GetImageHistogram(const Image *image,
512 %        unsigned long *number_colors,ExceptionInfo *exception)
513 %
514 %  A description of each parameter follows.
515 %
516 %    o image: the image.
517 %
518 %    o file:  Write a histogram of the color distribution to this file handle.
519 %
520 %    o exception: return any errors or warnings in this structure.
521 %
522 */
523 MagickExport ColorPacket *GetImageHistogram(const Image *image,
524   unsigned long *number_colors,ExceptionInfo *exception)
525 {
526   ColorPacket
527     *histogram;
528
529   CubeInfo
530     *cube_info;
531
532   *number_colors=0;
533   histogram=(ColorPacket *) NULL;
534   cube_info=ClassifyImageColors(image,exception);
535   if (cube_info != (CubeInfo *) NULL)
536     {
537       histogram=(ColorPacket *) AcquireQuantumMemory((size_t) cube_info->colors,
538         sizeof(*histogram));
539       if (histogram == (ColorPacket *) NULL)
540         (void) ThrowMagickException(exception,GetMagickModule(),
541           ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
542       else
543         {
544           ColorPacket
545             *root;
546
547           *number_colors=cube_info->colors;
548           root=histogram;
549           DefineImageHistogram(image,cube_info->root,&root);
550         }
551     }
552   cube_info=DestroyCubeInfo(image,cube_info);
553   return(histogram);
554 }
555 \f
556 /*
557 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
558 %                                                                             %
559 %                                                                             %
560 %                                                                             %
561 +  G e t N o d e I n f o                                                      %
562 %                                                                             %
563 %                                                                             %
564 %                                                                             %
565 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
566 %
567 %  GetNodeInfo() allocates memory for a new node in the color cube tree and
568 %  presets all fields to zero.
569 %
570 %  The format of the GetNodeInfo method is:
571 %
572 %      NodeInfo *GetNodeInfo(CubeInfo *cube_info,const unsigned long level)
573 %
574 %  A description of each parameter follows.
575 %
576 %    o cube_info: A pointer to the CubeInfo structure.
577 %
578 %    o level: Specifies the level in the storage_class the node resides.
579 %
580 */
581 static NodeInfo *GetNodeInfo(CubeInfo *cube_info,const unsigned long level)
582 {
583   NodeInfo
584     *node_info;
585
586   if (cube_info->free_nodes == 0)
587     {
588       Nodes
589         *nodes;
590
591       /*
592         Allocate a new nodes of nodes.
593       */
594       nodes=(Nodes *) AcquireMagickMemory(sizeof(*nodes));
595       if (nodes == (Nodes *) NULL)
596         return((NodeInfo *) NULL);
597       nodes->next=cube_info->node_queue;
598       cube_info->node_queue=nodes;
599       cube_info->node_info=nodes->nodes;
600       cube_info->free_nodes=NodesInAList;
601     }
602   cube_info->free_nodes--;
603   node_info=cube_info->node_info++;
604   (void) ResetMagickMemory(node_info,0,sizeof(*node_info));
605   node_info->level=level;
606   return(node_info);
607 }
608 \f
609 /*
610 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
611 %                                                                             %
612 %                                                                             %
613 %                                                                             %
614 %  I s H i s t o g r a m I m a g e                                            %
615 %                                                                             %
616 %                                                                             %
617 %                                                                             %
618 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
619 %
620 %  IsHistogramImage() returns MagickTrue if the image has 1024 unique colors or
621 %  less.
622 %
623 %  The format of the IsHistogramImage method is:
624 %
625 %      MagickBooleanType IsHistogramImage(const Image *image,
626 %        ExceptionInfo *exception)
627 %
628 %  A description of each parameter follows.
629 %
630 %    o image: the image.
631 %
632 %    o exception: return any errors or warnings in this structure.
633 %
634 */
635 MagickExport MagickBooleanType IsHistogramImage(const Image *image,
636   ExceptionInfo *exception)
637 {
638 #define MaximumUniqueColors  1024
639
640   CubeInfo
641     *cube_info;
642
643   long
644     y;
645
646   MagickPixelPacket
647     pixel,
648     target;
649
650   register const IndexPacket
651     *indexes;
652
653   register const PixelPacket
654     *p;
655
656   register long
657     x;
658
659   register NodeInfo
660     *node_info;
661
662   register long
663     i;
664
665   unsigned long
666     id,
667     index,
668     level;
669
670   CacheView
671     *image_view;
672
673   assert(image != (Image *) NULL);
674   assert(image->signature == MagickSignature);
675   if (image->debug != MagickFalse)
676     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
677   if ((image->storage_class == PseudoClass) && (image->colors <= 256))
678     return(MagickTrue);
679   if (image->storage_class == PseudoClass)
680     return(MagickFalse);
681   /*
682     Initialize color description tree.
683   */
684   cube_info=GetCubeInfo();
685   if (cube_info == (CubeInfo *) NULL)
686     {
687       (void) ThrowMagickException(exception,GetMagickModule(),
688         ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
689       return(MagickFalse);
690     }
691   GetMagickPixelPacket(image,&pixel);
692   GetMagickPixelPacket(image,&target);
693   image_view=AcquireCacheView(image);
694   for (y=0; y < (long) image->rows; y++)
695   {
696     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
697     if (p == (const PixelPacket *) NULL)
698       break;
699     indexes=GetCacheViewVirtualIndexQueue(image_view);
700     for (x=0; x < (long) image->columns; x++)
701     {
702       /*
703         Start at the root and proceed level by level.
704       */
705       node_info=cube_info->root;
706       index=MaxTreeDepth-1;
707       for (level=1; level < MaxTreeDepth; level++)
708       {
709         SetMagickPixelPacket(image,p,indexes+x,&pixel);
710         id=ColorToNodeId(image,&pixel,index);
711         if (node_info->child[id] == (NodeInfo *) NULL)
712           {
713             node_info->child[id]=GetNodeInfo(cube_info,level);
714             if (node_info->child[id] == (NodeInfo *) NULL)
715               {
716                 (void) ThrowMagickException(exception,GetMagickModule(),
717                   ResourceLimitError,"MemoryAllocationFailed","`%s'",
718                   image->filename);
719                 break;
720               }
721           }
722         node_info=node_info->child[id];
723         index--;
724       }
725       if (level < MaxTreeDepth)
726         break;
727       for (i=0; i < (long) node_info->number_unique; i++)
728       {
729         SetMagickPixelPacket(image,&node_info->list[i].pixel,
730           &node_info->list[i].index,&target);
731         if (IsMagickColorEqual(&pixel,&target) != MagickFalse)
732           break;
733       }
734       if (i < (long) node_info->number_unique)
735         node_info->list[i].count++;
736       else
737         {
738           /*
739             Add this unique color to the color list.
740           */
741           if (node_info->number_unique == 0)
742             node_info->list=(ColorPacket *) AcquireMagickMemory(
743               sizeof(*node_info->list));
744           else
745             node_info->list=(ColorPacket *) ResizeQuantumMemory(node_info->list,
746               (size_t) (i+1),sizeof(*node_info->list));
747           if (node_info->list == (ColorPacket *) NULL)
748             {
749               (void) ThrowMagickException(exception,GetMagickModule(),
750                 ResourceLimitError,"MemoryAllocationFailed","`%s'",
751                 image->filename);
752               break;
753             }
754           node_info->list[i].pixel=(*p);
755           if ((image->colorspace == CMYKColorspace) ||
756               (image->storage_class == PseudoClass))
757             node_info->list[i].index=indexes[x];
758           node_info->list[i].count=1;
759           node_info->number_unique++;
760           cube_info->colors++;
761           if (cube_info->colors > MaximumUniqueColors)
762             break;
763         }
764       p++;
765     }
766     if (x < (long) image->columns)
767       break;
768   }
769   image_view=DestroyCacheView(image_view);
770   cube_info=DestroyCubeInfo(image,cube_info);
771   return(y < (long) image->rows ? MagickFalse : MagickTrue);
772 }
773 \f
774 /*
775 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
776 %                                                                             %
777 %                                                                             %
778 %                                                                             %
779 %  I s P a l e t t e I m a g e                                                %
780 %                                                                             %
781 %                                                                             %
782 %                                                                             %
783 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
784 %
785 %  IsPaletteImage() returns MagickTrue if the image is PseudoClass and has 256
786 %  unique colors or less.
787 %
788 %  The format of the IsPaletteImage method is:
789 %
790 %      MagickBooleanType IsPaletteImage(const Image *image,
791 %        ExceptionInfo *exception)
792 %
793 %  A description of each parameter follows.
794 %
795 %    o image: the image.
796 %
797 %    o exception: return any errors or warnings in this structure.
798 %
799 */
800 MagickExport MagickBooleanType IsPaletteImage(const Image *image,
801   ExceptionInfo *exception)
802 {
803   CubeInfo
804     *cube_info;
805
806   long
807     y;
808
809   MagickPixelPacket
810     pixel,
811     target;
812
813   register const IndexPacket
814     *indexes;
815
816   register const PixelPacket
817     *p;
818
819   register long
820     x;
821
822   register NodeInfo
823     *node_info;
824
825   register long
826     i;
827
828   unsigned long
829     id,
830     index,
831     level;
832
833   CacheView
834     *image_view;
835
836   assert(image != (Image *) NULL);
837   assert(image->signature == MagickSignature);
838   if (image->debug != MagickFalse)
839     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
840   if ((image->storage_class == PseudoClass) && (image->colors <= 256))
841     return(MagickTrue);
842   if (image->storage_class == PseudoClass)
843     return(MagickFalse);
844   /*
845     Initialize color description tree.
846   */
847   cube_info=GetCubeInfo();
848   if (cube_info == (CubeInfo *) NULL)
849     {
850       (void) ThrowMagickException(exception,GetMagickModule(),
851         ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
852       return(MagickFalse);
853     }
854   GetMagickPixelPacket(image,&pixel);
855   GetMagickPixelPacket(image,&target);
856   image_view=AcquireCacheView(image);
857   for (y=0; y < (long) image->rows; y++)
858   {
859     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
860     if (p == (const PixelPacket *) NULL)
861       break;
862     indexes=GetCacheViewVirtualIndexQueue(image_view);
863     for (x=0; x < (long) image->columns; x++)
864     {
865       /*
866         Start at the root and proceed level by level.
867       */
868       node_info=cube_info->root;
869       index=MaxTreeDepth-1;
870       for (level=1; level < MaxTreeDepth; level++)
871       {
872         SetMagickPixelPacket(image,p,indexes+x,&pixel);
873         id=ColorToNodeId(image,&pixel,index);
874         if (node_info->child[id] == (NodeInfo *) NULL)
875           {
876             node_info->child[id]=GetNodeInfo(cube_info,level);
877             if (node_info->child[id] == (NodeInfo *) NULL)
878               {
879                 (void) ThrowMagickException(exception,GetMagickModule(),
880                   ResourceLimitError,"MemoryAllocationFailed","`%s'",
881                   image->filename);
882                 break;
883               }
884           }
885         node_info=node_info->child[id];
886         index--;
887       }
888       if (level < MaxTreeDepth)
889         break;
890       for (i=0; i < (long) node_info->number_unique; i++)
891       {
892         SetMagickPixelPacket(image,&node_info->list[i].pixel,
893           &node_info->list[i].index,&target);
894         if (IsMagickColorEqual(&pixel,&target) != MagickFalse)
895           break;
896       }
897       if (i < (long) node_info->number_unique)
898         node_info->list[i].count++;
899       else
900         {
901           /*
902             Add this unique color to the color list.
903           */
904           if (node_info->number_unique == 0)
905             node_info->list=(ColorPacket *) AcquireMagickMemory(
906               sizeof(*node_info->list));
907           else
908             node_info->list=(ColorPacket *) ResizeQuantumMemory(node_info->list,
909               (size_t) (i+1),sizeof(*node_info->list));
910           if (node_info->list == (ColorPacket *) NULL)
911             {
912               (void) ThrowMagickException(exception,GetMagickModule(),
913                 ResourceLimitError,"MemoryAllocationFailed","`%s'",
914                 image->filename);
915               break;
916             }
917           node_info->list[i].pixel=(*p);
918           if ((image->colorspace == CMYKColorspace) ||
919               (image->storage_class == PseudoClass))
920             node_info->list[i].index=indexes[x];
921           node_info->list[i].count=1;
922           node_info->number_unique++;
923           cube_info->colors++;
924           if (cube_info->colors > 256)
925             break;
926         }
927       p++;
928     }
929     if (x < (long) image->columns)
930       break;
931   }
932   image_view=DestroyCacheView(image_view);
933   cube_info=DestroyCubeInfo(image,cube_info);
934   return(y < (long) image->rows ? MagickFalse : MagickTrue);
935 }
936 \f
937 /*
938 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
939 %                                                                             %
940 %                                                                             %
941 %                                                                             %
942 %     M i n M a x S t r e t c h I m a g e                                     %
943 %                                                                             %
944 %                                                                             %
945 %                                                                             %
946 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
947 %
948 %  MinMaxStretchImage() uses the exact minimum and maximum values found in
949 %  each of the channels given, as the BlackPoint and WhitePoint to linearly
950 %  stretch the colors (and histogram) of the image.  The stretch points are
951 %  also moved further inward by the adjustment values given.
952 %
953 %  If the adjustment values are both zero this function is equivelent to a
954 %  perfect normalization (or autolevel) of the image.
955 %
956 %  Each channel is stretched independantally of each other (producing color
957 %  distortion) unless the special 'SyncChannels' flag is also provided in the
958 %  channels setting. If this flag is present the minimum and maximum point
959 %  will be extracted from all the given channels, and those channels will be
960 %  stretched by exactly the same amount (preventing color distortion).
961 %
962 %  The 'SyncChannels' is turned on in the 'DefaultChannels' setting by
963 %  default.
964 %
965 %  The format of the MinMaxStretchImage method is:
966 %
967 %      MagickBooleanType MinMaxStretchImage(Image *image,
968 %        const ChannelType channel, const double black_adjust,
969 %        const double white_adjust)
970 %
971 %  A description of each parameter follows:
972 %
973 %    o image: The image to auto-level
974 %
975 %    o channel: The channels to auto-level.  If the special 'SyncChannels'
976 %      flag is set, all the given channels are stretched by the same amount.
977 %
978 %    o black_adjust, white_adjust:  Move the Black/White Point inward
979 %      from the minimum and maximum points by this color value.
980 %
981 */
982 MagickExport MagickBooleanType MinMaxStretchImage(Image *image,
983   const ChannelType channel,const double black_value,const double white_value)
984 {
985   double
986     min,max;
987
988   MagickStatusType
989     status;
990
991   if ((channel & SyncChannels) != 0)
992     {
993       /*
994         Auto-level all channels equally.
995       */
996       (void) GetImageChannelRange(image,channel,&min,&max,&image->exception);
997       min+=black_value;
998       max-=white_value;
999       return(LevelImageChannel(image,channel,min,max,1.0));
1000     }
1001   /*
1002     Auto-level each channel separately.
1003   */
1004   status=MagickTrue;
1005   if ((channel & RedChannel) != 0)
1006     {
1007       (void) GetImageChannelRange(image,RedChannel,&min,&max,&image->exception);
1008       min+=black_value;
1009       max-=white_value;
1010       status&=LevelImageChannel(image,RedChannel,min,max,1.0);
1011     }
1012   if ((channel & GreenChannel) != 0)
1013     {
1014       (void) GetImageChannelRange(image,GreenChannel,&min,&max,
1015         &image->exception);
1016       min+=black_value;
1017       max-=white_value;
1018       status&=LevelImageChannel(image,GreenChannel,min,max,1.0);
1019     }
1020   if ((channel & BlueChannel) != 0)
1021     {
1022       (void) GetImageChannelRange(image,BlueChannel,&min,&max,
1023         &image->exception);
1024       min+=black_value;
1025       max-=white_value;
1026       status&=LevelImageChannel(image,BlueChannel,min,max,1.0);
1027     }
1028   if (((channel & OpacityChannel) != 0) &&
1029        (image->matte == MagickTrue))
1030     {
1031       (void) GetImageChannelRange(image,OpacityChannel,&min,&max,
1032         &image->exception);
1033       min+=black_value;
1034       max-=white_value;
1035       status&=LevelImageChannel(image,OpacityChannel,min,max,1.0);
1036     }
1037   if (((channel & IndexChannel) != 0) &&
1038        (image->colorspace == CMYKColorspace))
1039     {
1040       (void) GetImageChannelRange(image,IndexChannel,&min,&max,
1041         &image->exception);
1042       min+=black_value;
1043       max-=white_value;
1044       status&=LevelImageChannel(image,IndexChannel,min,max,1.0);
1045     }
1046   return(status != 0 ? MagickTrue : MagickFalse);
1047 }
1048 \f
1049 /*
1050 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1051 %                                                                             %
1052 %                                                                             %
1053 %                                                                             %
1054 %  G e t N u m b e r C o l o r s                                              %
1055 %                                                                             %
1056 %                                                                             %
1057 %                                                                             %
1058 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1059 %
1060 %  GetNumberColors() returns the number of unique colors in an image.
1061 %
1062 %  The format of the GetNumberColors method is:
1063 %
1064 %      unsigned long GetNumberColors(const Image *image,FILE *file,
1065 %        ExceptionInfo *exception)
1066 %
1067 %  A description of each parameter follows.
1068 %
1069 %    o image: the image.
1070 %
1071 %    o file:  Write a histogram of the color distribution to this file handle.
1072 %
1073 %    o exception: return any errors or warnings in this structure.
1074 %
1075 */
1076
1077 #if defined(__cplusplus) || defined(c_plusplus)
1078 extern "C" {
1079 #endif
1080
1081 static int HistogramCompare(const void *x,const void *y)
1082 {
1083   const ColorPacket
1084     *color_1,
1085     *color_2;
1086
1087   color_1=(const ColorPacket *) x;
1088   color_2=(const ColorPacket *) y;
1089   if (color_2->pixel.red != color_1->pixel.red)
1090     return((int) color_1->pixel.red-(int) color_2->pixel.red);
1091   if (color_2->pixel.green != color_1->pixel.green)
1092     return((int) color_1->pixel.green-(int) color_2->pixel.green);
1093   if (color_2->pixel.blue != color_1->pixel.blue)
1094     return((int) color_1->pixel.blue-(int) color_2->pixel.blue);
1095   return((int) color_2->count-(int) color_1->count);
1096 }
1097
1098 #if defined(__cplusplus) || defined(c_plusplus)
1099 }
1100 #endif
1101
1102 MagickExport unsigned long GetNumberColors(const Image *image,FILE *file,
1103   ExceptionInfo *exception)
1104 {
1105 #define HistogramImageTag  "Histogram/Image"
1106
1107   char
1108     color[MaxTextExtent],
1109     hex[MaxTextExtent],
1110     tuple[MaxTextExtent];
1111
1112   ColorPacket
1113     *histogram;
1114
1115   MagickPixelPacket
1116     pixel;
1117
1118   register ColorPacket
1119     *p;
1120
1121   register long
1122     i;
1123
1124   unsigned long
1125     number_colors;
1126
1127   number_colors=0;
1128   if (file == (FILE *) NULL)
1129     {
1130       CubeInfo
1131         *cube_info;
1132
1133       cube_info=ClassifyImageColors(image,exception);
1134       if (cube_info != (CubeInfo *) NULL)
1135         number_colors=cube_info->colors;
1136       cube_info=DestroyCubeInfo(image,cube_info);
1137       return(number_colors);
1138     }
1139   histogram=GetImageHistogram(image,&number_colors,exception);
1140   if (histogram == (ColorPacket *) NULL)
1141     return(number_colors);
1142   qsort((void *) histogram,(size_t) number_colors,sizeof(*histogram),
1143     HistogramCompare);
1144   GetMagickPixelPacket(image,&pixel);
1145   p=histogram;
1146   for (i=0; i < (long) number_colors; i++)
1147   {
1148     SetMagickPixelPacket(image,&p->pixel,&p->index,&pixel);
1149     (void) CopyMagickString(tuple,"(",MaxTextExtent);
1150     ConcatenateColorComponent(&pixel,RedChannel,X11Compliance,tuple);
1151     (void) ConcatenateMagickString(tuple,",",MaxTextExtent);
1152     ConcatenateColorComponent(&pixel,GreenChannel,X11Compliance,tuple);
1153     (void) ConcatenateMagickString(tuple,",",MaxTextExtent);
1154     ConcatenateColorComponent(&pixel,BlueChannel,X11Compliance,tuple);
1155     if (pixel.colorspace == CMYKColorspace)
1156       {
1157         (void) ConcatenateMagickString(tuple,",",MaxTextExtent);
1158         ConcatenateColorComponent(&pixel,IndexChannel,X11Compliance,tuple);
1159       }
1160     if (pixel.matte != MagickFalse)
1161       {
1162         (void) ConcatenateMagickString(tuple,",",MaxTextExtent);
1163         ConcatenateColorComponent(&pixel,OpacityChannel,X11Compliance,tuple);
1164       }
1165     (void) ConcatenateMagickString(tuple,")",MaxTextExtent);
1166     (void) QueryMagickColorname(image,&pixel,SVGCompliance,color,exception);
1167     GetColorTuple(&pixel,MagickTrue,hex);
1168     (void) fprintf(file,MagickSizeFormat,p->count);
1169     (void) fprintf(file,": %s %s %s\n",tuple,hex,color);
1170     if ((image->progress_monitor != (MagickProgressMonitor) NULL) &&
1171         (QuantumTick(i,number_colors) != MagickFalse))
1172       (void) image->progress_monitor(HistogramImageTag,i,number_colors,
1173         image->client_data);
1174     p++;
1175   }
1176   (void) fflush(file);
1177   histogram=(ColorPacket *) RelinquishMagickMemory(histogram);
1178   return(number_colors);
1179 }
1180 \f
1181 /*
1182 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1183 %                                                                             %
1184 %                                                                             %
1185 %                                                                             %
1186 %  U n i q u e I m a g e C o l o r s                                          %
1187 %                                                                             %
1188 %                                                                             %
1189 %                                                                             %
1190 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1191 %
1192 %  UniqueImageColors() returns the unique colors of an image.
1193 %
1194 %  The format of the UniqueImageColors method is:
1195 %
1196 %      Image *UniqueImageColors(const Image *image,ExceptionInfo *exception)
1197 %
1198 %  A description of each parameter follows.
1199 %
1200 %    o image: the image.
1201 %
1202 %    o exception: return any errors or warnings in this structure.
1203 %
1204 */
1205
1206 static void UniqueColorsToImage(Image *image,CubeInfo *cube_info,
1207   const NodeInfo *node_info,ExceptionInfo *exception)
1208 {
1209 #define UniqueColorsImageTag  "UniqueColors/Image"
1210
1211   register long
1212     i;
1213
1214   unsigned long
1215     number_children;
1216
1217   /*
1218     Traverse any children.
1219   */
1220   number_children=image->matte == MagickFalse ? 8UL : 16UL;
1221   for (i=0; i < (long) number_children; i++)
1222     if (node_info->child[i] != (NodeInfo *) NULL)
1223       UniqueColorsToImage(image,cube_info,node_info->child[i],exception);
1224   if (node_info->level == (MaxTreeDepth-1))
1225     {
1226       register ColorPacket
1227         *p;
1228
1229       register IndexPacket
1230         *__restrict indexes;
1231
1232       register PixelPacket
1233         *__restrict q;
1234
1235       p=node_info->list;
1236       for (i=0; i < (long) node_info->number_unique; i++)
1237       {
1238         q=QueueAuthenticPixels(image,cube_info->x,0,1,1,exception);
1239         if (q == (PixelPacket *) NULL)
1240           continue;
1241         indexes=GetAuthenticIndexQueue(image);
1242         *q=p->pixel;
1243         if (image->colorspace == CMYKColorspace)
1244           *indexes=p->index;
1245         if (SyncAuthenticPixels(image,exception) == MagickFalse)
1246           break;
1247         cube_info->x++;
1248         p++;
1249       }
1250       if ((image->progress_monitor != (MagickProgressMonitor) NULL) &&
1251           (QuantumTick(cube_info->progress,cube_info->colors) != MagickFalse))
1252         (void) image->progress_monitor(UniqueColorsImageTag,cube_info->progress,
1253           cube_info->colors,image->client_data);
1254       cube_info->progress++;
1255     }
1256 }
1257
1258 MagickExport Image *UniqueImageColors(const Image *image,
1259   ExceptionInfo *exception)
1260 {
1261   CubeInfo
1262     *cube_info;
1263
1264   Image
1265     *unique_image;
1266
1267   cube_info=ClassifyImageColors(image,exception);
1268   if (cube_info == (CubeInfo *) NULL)
1269     return((Image *) NULL);
1270   unique_image=CloneImage(image,cube_info->colors,1,MagickTrue,exception);
1271   if (unique_image == (Image *) NULL)
1272     return(unique_image);
1273   if (SetImageStorageClass(unique_image,DirectClass) == MagickFalse)
1274     {
1275       InheritException(exception,&unique_image->exception);
1276       unique_image=DestroyImage(unique_image);
1277       return((Image *) NULL);
1278     }
1279   UniqueColorsToImage(unique_image,cube_info,cube_info->root,exception);
1280   if (cube_info->colors < MaxColormapSize)
1281     {
1282       QuantizeInfo
1283         *quantize_info;
1284
1285       quantize_info=AcquireQuantizeInfo((ImageInfo *) NULL);
1286       quantize_info->number_colors=MaxColormapSize;
1287       quantize_info->dither=MagickFalse;
1288       quantize_info->tree_depth=8;
1289       (void) QuantizeImage(quantize_info,unique_image);
1290       quantize_info=DestroyQuantizeInfo(quantize_info);
1291     }
1292   cube_info=DestroyCubeInfo(image,cube_info);
1293   return(unique_image);
1294 }