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