blob: ff62b828bf63234d66fcf1b8d88e8bc46d99a056 [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
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% %
cristy7e41fe82010-12-04 23:12:08 +000021% Copyright 1999-2011 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000022% 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
40/*
41 Include declarations.
42*/
43#include "magick/studio.h"
44#include "magick/cache-view.h"
cristyf2e11662009-10-14 01:24:43 +000045#include "magick/color-private.h"
cristy3ed852e2009-09-05 21:47:34 +000046#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"
cristyf2e11662009-10-14 01:24:43 +000054#include "magick/monitor-private.h"
55#include "magick/pixel-private.h"
cristy3ed852e2009-09-05 21:47:34 +000056#include "magick/prepress.h"
cristyf2e11662009-10-14 01:24:43 +000057#include "magick/quantize.h"
cristy3ed852e2009-09-05 21:47:34 +000058#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/*
cristyf2e11662009-10-14 01:24:43 +000065 Define declarations.
66*/
67#define MaxTreeDepth 8
68#define NodesInAList 1536
69
70/*
71 Typedef declarations.
72*/
73typedef struct _NodeInfo
74{
75 struct _NodeInfo
76 *child[16];
77
78 ColorPacket
79 *list;
80
81 MagickSizeType
82 number_unique;
83
cristybb503372010-05-27 20:51:26 +000084 size_t
cristyf2e11662009-10-14 01:24:43 +000085 level;
86} NodeInfo;
87
88typedef struct _Nodes
89{
90 NodeInfo
91 nodes[NodesInAList];
92
93 struct _Nodes
94 *next;
95} Nodes;
96
97typedef struct _CubeInfo
98{
99 NodeInfo
100 *root;
101
cristybb503372010-05-27 20:51:26 +0000102 ssize_t
cristycee97112010-05-28 00:44:52 +0000103 x;
104
105 MagickOffsetType
cristyf2e11662009-10-14 01:24:43 +0000106 progress;
107
cristybb503372010-05-27 20:51:26 +0000108 size_t
cristyf2e11662009-10-14 01:24:43 +0000109 colors,
110 free_nodes;
111
112 NodeInfo
113 *node_info;
114
115 Nodes
116 *node_queue;
117} CubeInfo;
118
119/*
120 Forward declarations.
121*/
122static CubeInfo
123 *GetCubeInfo(void);
124
125static NodeInfo
cristybb503372010-05-27 20:51:26 +0000126 *GetNodeInfo(CubeInfo *,const size_t);
cristyf2e11662009-10-14 01:24:43 +0000127
128static void
129 DestroyColorCube(const Image *,NodeInfo *);
130
131/*
132%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
133% %
134% %
135% %
136+ C l a s s i f y I m a g e C o l o r s %
137% %
138% %
139% %
140%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
141%
142% ClassifyImageColors() builds a populated CubeInfo tree for the specified
143% image. The returned tree should be deallocated using DestroyCubeInfo()
cristya03e7992010-06-25 12:18:06 +0000144% once it is no longer needed.
cristyf2e11662009-10-14 01:24:43 +0000145%
146% The format of the ClassifyImageColors() method is:
147%
148% CubeInfo *ClassifyImageColors(const Image *image,
149% ExceptionInfo *exception)
150%
151% A description of each parameter follows.
152%
153% o image: the image.
154%
155% o exception: return any errors or warnings in this structure.
156%
157*/
158
cristybb503372010-05-27 20:51:26 +0000159static inline size_t ColorToNodeId(const Image *image,
160 const MagickPixelPacket *pixel,size_t index)
cristyf2e11662009-10-14 01:24:43 +0000161{
cristybb503372010-05-27 20:51:26 +0000162 size_t
cristyf2e11662009-10-14 01:24:43 +0000163 id;
164
cristybb503372010-05-27 20:51:26 +0000165 id=(size_t) (
cristyce70c172010-01-07 17:15:30 +0000166 ((ScaleQuantumToChar(ClampToQuantum(pixel->red)) >> index) & 0x01) |
167 ((ScaleQuantumToChar(ClampToQuantum(pixel->green)) >> index) & 0x01) << 1 |
168 ((ScaleQuantumToChar(ClampToQuantum(pixel->blue)) >> index) & 0x01) << 2);
cristyf2e11662009-10-14 01:24:43 +0000169 if (image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +0000170 id|=((ScaleQuantumToChar(ClampToQuantum(pixel->opacity)) >> index) &
cristyf2e11662009-10-14 01:24:43 +0000171 0x01) << 3;
172 return(id);
173}
174
175static CubeInfo *ClassifyImageColors(const Image *image,
176 ExceptionInfo *exception)
177{
178#define EvaluateImageTag " Compute image colors... "
179
cristyc4c8d132010-01-07 01:58:38 +0000180 CacheView
181 *image_view;
182
cristyf2e11662009-10-14 01:24:43 +0000183 CubeInfo
184 *cube_info;
185
cristybb503372010-05-27 20:51:26 +0000186 ssize_t
cristyf2e11662009-10-14 01:24:43 +0000187 y;
188
189 MagickBooleanType
190 proceed;
191
192 MagickPixelPacket
193 pixel,
194 target;
195
196 NodeInfo
197 *node_info;
198
199 register const IndexPacket
200 *indexes;
201
202 register const PixelPacket
203 *p;
204
cristybb503372010-05-27 20:51:26 +0000205 register ssize_t
cristyf2e11662009-10-14 01:24:43 +0000206 i,
207 x;
208
cristybb503372010-05-27 20:51:26 +0000209 register size_t
cristyf2e11662009-10-14 01:24:43 +0000210 id,
211 index,
212 level;
213
cristyf2e11662009-10-14 01:24:43 +0000214 /*
215 Initialize color description tree.
216 */
217 assert(image != (const Image *) NULL);
218 assert(image->signature == MagickSignature);
219 if (image->debug != MagickFalse)
220 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
221 cube_info=GetCubeInfo();
222 if (cube_info == (CubeInfo *) NULL)
223 {
224 (void) ThrowMagickException(exception,GetMagickModule(),
225 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
226 return(cube_info);
227 }
228 GetMagickPixelPacket(image,&pixel);
229 GetMagickPixelPacket(image,&target);
230 image_view=AcquireCacheView(image);
cristybb503372010-05-27 20:51:26 +0000231 for (y=0; y < (ssize_t) image->rows; y++)
cristyf2e11662009-10-14 01:24:43 +0000232 {
233 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
234 if (p == (const PixelPacket *) NULL)
235 break;
236 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +0000237 for (x=0; x < (ssize_t) image->columns; x++)
cristyf2e11662009-10-14 01:24:43 +0000238 {
239 /*
240 Start at the root and proceed level by level.
241 */
242 node_info=cube_info->root;
243 index=MaxTreeDepth-1;
244 for (level=1; level < MaxTreeDepth; level++)
245 {
246 SetMagickPixelPacket(image,p,indexes+x,&pixel);
247 id=ColorToNodeId(image,&pixel,index);
248 if (node_info->child[id] == (NodeInfo *) NULL)
249 {
250 node_info->child[id]=GetNodeInfo(cube_info,level);
251 if (node_info->child[id] == (NodeInfo *) NULL)
252 {
253 (void) ThrowMagickException(exception,GetMagickModule(),
254 ResourceLimitError,"MemoryAllocationFailed","`%s'",
255 image->filename);
256 return(0);
257 }
258 }
259 node_info=node_info->child[id];
260 index--;
261 }
cristybb503372010-05-27 20:51:26 +0000262 for (i=0; i < (ssize_t) node_info->number_unique; i++)
cristyf2e11662009-10-14 01:24:43 +0000263 {
264 SetMagickPixelPacket(image,&node_info->list[i].pixel,
265 &node_info->list[i].index,&target);
266 if (IsMagickColorEqual(&pixel,&target) != MagickFalse)
267 break;
268 }
cristybb503372010-05-27 20:51:26 +0000269 if (i < (ssize_t) node_info->number_unique)
cristyf2e11662009-10-14 01:24:43 +0000270 node_info->list[i].count++;
271 else
272 {
273 if (node_info->number_unique == 0)
274 node_info->list=(ColorPacket *) AcquireMagickMemory(
275 sizeof(*node_info->list));
276 else
277 node_info->list=(ColorPacket *) ResizeQuantumMemory(node_info->list,
278 (size_t) (i+1),sizeof(*node_info->list));
279 if (node_info->list == (ColorPacket *) NULL)
280 {
281 (void) ThrowMagickException(exception,GetMagickModule(),
282 ResourceLimitError,"MemoryAllocationFailed","`%s'",
283 image->filename);
284 return(0);
285 }
286 node_info->list[i].pixel=(*p);
287 if ((image->colorspace == CMYKColorspace) ||
288 (image->storage_class == PseudoClass))
289 node_info->list[i].index=indexes[x];
290 node_info->list[i].count=1;
291 node_info->number_unique++;
292 cube_info->colors++;
293 }
294 p++;
295 }
cristycee97112010-05-28 00:44:52 +0000296 proceed=SetImageProgress(image,EvaluateImageTag,(MagickOffsetType) y,
297 image->rows);
cristyf2e11662009-10-14 01:24:43 +0000298 if (proceed == MagickFalse)
299 break;
300 }
301 image_view=DestroyCacheView(image_view);
302 return(cube_info);
303}
304
305/*
306%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
307% %
308% %
309% %
310+ D e f i n e I m a g e H i s t o g r a m %
311% %
312% %
313% %
314%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
315%
316% DefineImageHistogram() traverses the color cube tree and notes each colormap
317% entry. A colormap entry is any node in the color cube tree where the
318% of unique colors is not zero.
319%
320% The format of the DefineImageHistogram method is:
321%
322% DefineImageHistogram(const Image *image,NodeInfo *node_info,
323% ColorPacket **unique_colors)
324%
325% A description of each parameter follows.
326%
327% o image: the image.
328%
329% o node_info: the address of a structure of type NodeInfo which points to a
330% node in the color cube tree that is to be pruned.
331%
332% o histogram: the image histogram.
333%
334*/
335static void DefineImageHistogram(const Image *image,NodeInfo *node_info,
336 ColorPacket **histogram)
337{
cristybb503372010-05-27 20:51:26 +0000338 register ssize_t
cristyf2e11662009-10-14 01:24:43 +0000339 i;
340
cristybb503372010-05-27 20:51:26 +0000341 size_t
cristyf2e11662009-10-14 01:24:43 +0000342 number_children;
343
344 /*
345 Traverse any children.
346 */
347 number_children=image->matte == MagickFalse ? 8UL : 16UL;
cristybb503372010-05-27 20:51:26 +0000348 for (i=0; i < (ssize_t) number_children; i++)
cristyf2e11662009-10-14 01:24:43 +0000349 if (node_info->child[i] != (NodeInfo *) NULL)
350 DefineImageHistogram(image,node_info->child[i],histogram);
351 if (node_info->level == (MaxTreeDepth-1))
352 {
353 register ColorPacket
354 *p;
355
356 p=node_info->list;
cristybb503372010-05-27 20:51:26 +0000357 for (i=0; i < (ssize_t) node_info->number_unique; i++)
cristyf2e11662009-10-14 01:24:43 +0000358 {
359 (*histogram)->pixel=p->pixel;
360 (*histogram)->index=p->index;
361 (*histogram)->count=p->count;
362 (*histogram)++;
363 p++;
364 }
365 }
366}
367
368/*
369%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
370% %
371% %
372% %
373+ D e s t r o y C u b e I n f o %
374% %
375% %
376% %
377%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
378%
379% DestroyCubeInfo() deallocates memory associated with a CubeInfo structure.
380%
381% The format of the DestroyCubeInfo method is:
382%
383% DestroyCubeInfo(const Image *image,CubeInfo *cube_info)
384%
385% A description of each parameter follows:
386%
387% o image: the image.
388%
389% o cube_info: the address of a structure of type CubeInfo.
390%
391*/
392static CubeInfo *DestroyCubeInfo(const Image *image,CubeInfo *cube_info)
393{
394 register Nodes
395 *nodes;
396
397 /*
398 Release color cube tree storage.
399 */
400 DestroyColorCube(image,cube_info->root);
401 do
402 {
403 nodes=cube_info->node_queue->next;
404 cube_info->node_queue=(Nodes *)
405 RelinquishMagickMemory(cube_info->node_queue);
406 cube_info->node_queue=nodes;
407 } while (cube_info->node_queue != (Nodes *) NULL);
408 return((CubeInfo *) RelinquishMagickMemory(cube_info));
409}
410
411/*
412%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
413% %
414% %
415% %
416+ D e s t r o y C o l o r C u b e %
417% %
418% %
419% %
420%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
421%
422% DestroyColorCube() traverses the color cube tree and frees the list of
423% unique colors.
424%
425% The format of the DestroyColorCube method is:
426%
427% void DestroyColorCube(const Image *image,const NodeInfo *node_info)
428%
429% A description of each parameter follows.
430%
431% o image: the image.
432%
433% o node_info: the address of a structure of type NodeInfo which points to a
434% node in the color cube tree that is to be pruned.
435%
436*/
437static void DestroyColorCube(const Image *image,NodeInfo *node_info)
438{
cristybb503372010-05-27 20:51:26 +0000439 register ssize_t
cristyf2e11662009-10-14 01:24:43 +0000440 i;
441
cristybb503372010-05-27 20:51:26 +0000442 size_t
cristyf2e11662009-10-14 01:24:43 +0000443 number_children;
444
445 /*
446 Traverse any children.
447 */
448 number_children=image->matte == MagickFalse ? 8UL : 16UL;
cristybb503372010-05-27 20:51:26 +0000449 for (i=0; i < (ssize_t) number_children; i++)
cristyf2e11662009-10-14 01:24:43 +0000450 if (node_info->child[i] != (NodeInfo *) NULL)
451 DestroyColorCube(image,node_info->child[i]);
452 if (node_info->list != (ColorPacket *) NULL)
453 node_info->list=(ColorPacket *) RelinquishMagickMemory(node_info->list);
454}
455
456/*
457%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
458% %
459% %
460% %
461+ G e t C u b e I n f o %
462% %
463% %
464% %
465%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
466%
467% GetCubeInfo() initializes the CubeInfo data structure.
468%
469% The format of the GetCubeInfo method is:
470%
471% cube_info=GetCubeInfo()
472%
473% A description of each parameter follows.
474%
475% o cube_info: A pointer to the Cube structure.
476%
477*/
478static CubeInfo *GetCubeInfo(void)
479{
480 CubeInfo
481 *cube_info;
482
483 /*
484 Initialize tree to describe color cube.
485 */
cristy73bd4a52010-10-05 11:24:23 +0000486 cube_info=(CubeInfo *) AcquireMagickMemory(sizeof(*cube_info));
cristyf2e11662009-10-14 01:24:43 +0000487 if (cube_info == (CubeInfo *) NULL)
488 return((CubeInfo *) NULL);
489 (void) ResetMagickMemory(cube_info,0,sizeof(*cube_info));
490 /*
491 Initialize root node.
492 */
493 cube_info->root=GetNodeInfo(cube_info,0);
494 if (cube_info->root == (NodeInfo *) NULL)
495 return((CubeInfo *) NULL);
496 return(cube_info);
497}
498
499/*
500%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
501% %
502% %
503% %
504% G e t I m a g e H i s t o g r a m %
505% %
506% %
507% %
508%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
509%
510% GetImageHistogram() returns the unique colors in an image.
511%
512% The format of the GetImageHistogram method is:
513%
cristybb503372010-05-27 20:51:26 +0000514% size_t GetImageHistogram(const Image *image,
515% size_t *number_colors,ExceptionInfo *exception)
cristyf2e11662009-10-14 01:24:43 +0000516%
517% A description of each parameter follows.
518%
519% o image: the image.
520%
521% o file: Write a histogram of the color distribution to this file handle.
522%
523% o exception: return any errors or warnings in this structure.
524%
525*/
526MagickExport ColorPacket *GetImageHistogram(const Image *image,
cristybb503372010-05-27 20:51:26 +0000527 size_t *number_colors,ExceptionInfo *exception)
cristyf2e11662009-10-14 01:24:43 +0000528{
529 ColorPacket
530 *histogram;
531
532 CubeInfo
533 *cube_info;
534
535 *number_colors=0;
536 histogram=(ColorPacket *) NULL;
537 cube_info=ClassifyImageColors(image,exception);
538 if (cube_info != (CubeInfo *) NULL)
539 {
540 histogram=(ColorPacket *) AcquireQuantumMemory((size_t) cube_info->colors,
541 sizeof(*histogram));
542 if (histogram == (ColorPacket *) NULL)
543 (void) ThrowMagickException(exception,GetMagickModule(),
544 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
545 else
546 {
547 ColorPacket
548 *root;
549
550 *number_colors=cube_info->colors;
551 root=histogram;
552 DefineImageHistogram(image,cube_info->root,&root);
553 }
554 }
555 cube_info=DestroyCubeInfo(image,cube_info);
556 return(histogram);
557}
558
559/*
560%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
561% %
562% %
563% %
564+ G e t N o d e I n f o %
565% %
566% %
567% %
568%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
569%
570% GetNodeInfo() allocates memory for a new node in the color cube tree and
571% presets all fields to zero.
572%
573% The format of the GetNodeInfo method is:
574%
cristybb503372010-05-27 20:51:26 +0000575% NodeInfo *GetNodeInfo(CubeInfo *cube_info,const size_t level)
cristyf2e11662009-10-14 01:24:43 +0000576%
577% A description of each parameter follows.
578%
579% o cube_info: A pointer to the CubeInfo structure.
580%
581% o level: Specifies the level in the storage_class the node resides.
582%
583*/
cristybb503372010-05-27 20:51:26 +0000584static NodeInfo *GetNodeInfo(CubeInfo *cube_info,const size_t level)
cristyf2e11662009-10-14 01:24:43 +0000585{
586 NodeInfo
587 *node_info;
588
589 if (cube_info->free_nodes == 0)
590 {
591 Nodes
592 *nodes;
593
594 /*
595 Allocate a new nodes of nodes.
596 */
cristy73bd4a52010-10-05 11:24:23 +0000597 nodes=(Nodes *) AcquireMagickMemory(sizeof(*nodes));
cristyf2e11662009-10-14 01:24:43 +0000598 if (nodes == (Nodes *) NULL)
599 return((NodeInfo *) NULL);
600 nodes->next=cube_info->node_queue;
601 cube_info->node_queue=nodes;
602 cube_info->node_info=nodes->nodes;
603 cube_info->free_nodes=NodesInAList;
604 }
605 cube_info->free_nodes--;
606 node_info=cube_info->node_info++;
607 (void) ResetMagickMemory(node_info,0,sizeof(*node_info));
608 node_info->level=level;
609 return(node_info);
610}
611
612/*
613%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
614% %
615% %
616% %
617% I s H i s t o g r a m I m a g e %
618% %
619% %
620% %
621%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
622%
623% IsHistogramImage() returns MagickTrue if the image has 1024 unique colors or
624% less.
625%
626% The format of the IsHistogramImage method is:
627%
628% MagickBooleanType IsHistogramImage(const Image *image,
629% ExceptionInfo *exception)
630%
631% A description of each parameter follows.
632%
633% o image: the image.
634%
635% o exception: return any errors or warnings in this structure.
636%
637*/
638MagickExport MagickBooleanType IsHistogramImage(const Image *image,
639 ExceptionInfo *exception)
640{
641#define MaximumUniqueColors 1024
642
cristyc4c8d132010-01-07 01:58:38 +0000643 CacheView
644 *image_view;
645
cristyf2e11662009-10-14 01:24:43 +0000646 CubeInfo
647 *cube_info;
648
cristybb503372010-05-27 20:51:26 +0000649 ssize_t
cristyf2e11662009-10-14 01:24:43 +0000650 y;
651
652 MagickPixelPacket
653 pixel,
654 target;
655
656 register const IndexPacket
657 *indexes;
658
659 register const PixelPacket
660 *p;
661
cristybb503372010-05-27 20:51:26 +0000662 register ssize_t
cristyf2e11662009-10-14 01:24:43 +0000663 x;
664
665 register NodeInfo
666 *node_info;
667
cristybb503372010-05-27 20:51:26 +0000668 register ssize_t
cristyf2e11662009-10-14 01:24:43 +0000669 i;
670
cristybb503372010-05-27 20:51:26 +0000671 size_t
cristyf2e11662009-10-14 01:24:43 +0000672 id,
673 index,
674 level;
675
cristyf2e11662009-10-14 01:24:43 +0000676 assert(image != (Image *) NULL);
677 assert(image->signature == MagickSignature);
678 if (image->debug != MagickFalse)
679 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
680 if ((image->storage_class == PseudoClass) && (image->colors <= 256))
681 return(MagickTrue);
682 if (image->storage_class == PseudoClass)
683 return(MagickFalse);
684 /*
685 Initialize color description tree.
686 */
687 cube_info=GetCubeInfo();
688 if (cube_info == (CubeInfo *) NULL)
689 {
690 (void) ThrowMagickException(exception,GetMagickModule(),
691 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
692 return(MagickFalse);
693 }
694 GetMagickPixelPacket(image,&pixel);
695 GetMagickPixelPacket(image,&target);
696 image_view=AcquireCacheView(image);
cristybb503372010-05-27 20:51:26 +0000697 for (y=0; y < (ssize_t) image->rows; y++)
cristyf2e11662009-10-14 01:24:43 +0000698 {
699 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
700 if (p == (const PixelPacket *) NULL)
701 break;
702 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +0000703 for (x=0; x < (ssize_t) image->columns; x++)
cristyf2e11662009-10-14 01:24:43 +0000704 {
705 /*
706 Start at the root and proceed level by level.
707 */
708 node_info=cube_info->root;
709 index=MaxTreeDepth-1;
710 for (level=1; level < MaxTreeDepth; level++)
711 {
712 SetMagickPixelPacket(image,p,indexes+x,&pixel);
713 id=ColorToNodeId(image,&pixel,index);
714 if (node_info->child[id] == (NodeInfo *) NULL)
715 {
716 node_info->child[id]=GetNodeInfo(cube_info,level);
717 if (node_info->child[id] == (NodeInfo *) NULL)
718 {
719 (void) ThrowMagickException(exception,GetMagickModule(),
720 ResourceLimitError,"MemoryAllocationFailed","`%s'",
721 image->filename);
722 break;
723 }
724 }
725 node_info=node_info->child[id];
726 index--;
727 }
728 if (level < MaxTreeDepth)
729 break;
cristybb503372010-05-27 20:51:26 +0000730 for (i=0; i < (ssize_t) node_info->number_unique; i++)
cristyf2e11662009-10-14 01:24:43 +0000731 {
732 SetMagickPixelPacket(image,&node_info->list[i].pixel,
733 &node_info->list[i].index,&target);
734 if (IsMagickColorEqual(&pixel,&target) != MagickFalse)
735 break;
736 }
cristybb503372010-05-27 20:51:26 +0000737 if (i < (ssize_t) node_info->number_unique)
cristyf2e11662009-10-14 01:24:43 +0000738 node_info->list[i].count++;
739 else
740 {
741 /*
742 Add this unique color to the color list.
743 */
744 if (node_info->number_unique == 0)
745 node_info->list=(ColorPacket *) AcquireMagickMemory(
746 sizeof(*node_info->list));
747 else
748 node_info->list=(ColorPacket *) ResizeQuantumMemory(node_info->list,
749 (size_t) (i+1),sizeof(*node_info->list));
750 if (node_info->list == (ColorPacket *) NULL)
751 {
752 (void) ThrowMagickException(exception,GetMagickModule(),
753 ResourceLimitError,"MemoryAllocationFailed","`%s'",
754 image->filename);
755 break;
756 }
757 node_info->list[i].pixel=(*p);
758 if ((image->colorspace == CMYKColorspace) ||
759 (image->storage_class == PseudoClass))
760 node_info->list[i].index=indexes[x];
761 node_info->list[i].count=1;
762 node_info->number_unique++;
763 cube_info->colors++;
764 if (cube_info->colors > MaximumUniqueColors)
765 break;
766 }
767 p++;
768 }
cristybb503372010-05-27 20:51:26 +0000769 if (x < (ssize_t) image->columns)
cristyf2e11662009-10-14 01:24:43 +0000770 break;
771 }
772 image_view=DestroyCacheView(image_view);
773 cube_info=DestroyCubeInfo(image,cube_info);
cristybb503372010-05-27 20:51:26 +0000774 return(y < (ssize_t) image->rows ? MagickFalse : MagickTrue);
cristyf2e11662009-10-14 01:24:43 +0000775}
776
777/*
778%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
779% %
780% %
781% %
782% I s P a l e t t e I m a g e %
783% %
784% %
785% %
786%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
787%
788% IsPaletteImage() returns MagickTrue if the image is PseudoClass and has 256
789% unique colors or less.
790%
791% The format of the IsPaletteImage method is:
792%
793% MagickBooleanType IsPaletteImage(const Image *image,
794% ExceptionInfo *exception)
795%
796% A description of each parameter follows.
797%
798% o image: the image.
799%
800% o exception: return any errors or warnings in this structure.
801%
802*/
803MagickExport MagickBooleanType IsPaletteImage(const Image *image,
804 ExceptionInfo *exception)
805{
cristyc4c8d132010-01-07 01:58:38 +0000806 CacheView
807 *image_view;
808
cristyf2e11662009-10-14 01:24:43 +0000809 CubeInfo
810 *cube_info;
811
cristybb503372010-05-27 20:51:26 +0000812 ssize_t
cristyf2e11662009-10-14 01:24:43 +0000813 y;
814
815 MagickPixelPacket
816 pixel,
817 target;
818
819 register const IndexPacket
820 *indexes;
821
822 register const PixelPacket
823 *p;
824
cristybb503372010-05-27 20:51:26 +0000825 register ssize_t
cristyf2e11662009-10-14 01:24:43 +0000826 x;
827
828 register NodeInfo
829 *node_info;
830
cristybb503372010-05-27 20:51:26 +0000831 register ssize_t
cristyf2e11662009-10-14 01:24:43 +0000832 i;
833
cristybb503372010-05-27 20:51:26 +0000834 size_t
cristyf2e11662009-10-14 01:24:43 +0000835 id,
836 index,
837 level;
838
cristyf2e11662009-10-14 01:24:43 +0000839 assert(image != (Image *) NULL);
840 assert(image->signature == MagickSignature);
841 if (image->debug != MagickFalse)
842 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
843 if ((image->storage_class == PseudoClass) && (image->colors <= 256))
844 return(MagickTrue);
845 if (image->storage_class == PseudoClass)
846 return(MagickFalse);
847 /*
848 Initialize color description tree.
849 */
850 cube_info=GetCubeInfo();
851 if (cube_info == (CubeInfo *) NULL)
852 {
853 (void) ThrowMagickException(exception,GetMagickModule(),
854 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
855 return(MagickFalse);
856 }
857 GetMagickPixelPacket(image,&pixel);
858 GetMagickPixelPacket(image,&target);
859 image_view=AcquireCacheView(image);
cristybb503372010-05-27 20:51:26 +0000860 for (y=0; y < (ssize_t) image->rows; y++)
cristyf2e11662009-10-14 01:24:43 +0000861 {
862 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
863 if (p == (const PixelPacket *) NULL)
864 break;
865 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +0000866 for (x=0; x < (ssize_t) image->columns; x++)
cristyf2e11662009-10-14 01:24:43 +0000867 {
868 /*
869 Start at the root and proceed level by level.
870 */
871 node_info=cube_info->root;
872 index=MaxTreeDepth-1;
873 for (level=1; level < MaxTreeDepth; level++)
874 {
875 SetMagickPixelPacket(image,p,indexes+x,&pixel);
876 id=ColorToNodeId(image,&pixel,index);
877 if (node_info->child[id] == (NodeInfo *) NULL)
878 {
879 node_info->child[id]=GetNodeInfo(cube_info,level);
880 if (node_info->child[id] == (NodeInfo *) NULL)
881 {
882 (void) ThrowMagickException(exception,GetMagickModule(),
883 ResourceLimitError,"MemoryAllocationFailed","`%s'",
884 image->filename);
885 break;
886 }
887 }
888 node_info=node_info->child[id];
889 index--;
890 }
891 if (level < MaxTreeDepth)
892 break;
cristybb503372010-05-27 20:51:26 +0000893 for (i=0; i < (ssize_t) node_info->number_unique; i++)
cristyf2e11662009-10-14 01:24:43 +0000894 {
895 SetMagickPixelPacket(image,&node_info->list[i].pixel,
896 &node_info->list[i].index,&target);
897 if (IsMagickColorEqual(&pixel,&target) != MagickFalse)
898 break;
899 }
cristybb503372010-05-27 20:51:26 +0000900 if (i < (ssize_t) node_info->number_unique)
cristyf2e11662009-10-14 01:24:43 +0000901 node_info->list[i].count++;
902 else
903 {
904 /*
905 Add this unique color to the color list.
906 */
907 if (node_info->number_unique == 0)
908 node_info->list=(ColorPacket *) AcquireMagickMemory(
909 sizeof(*node_info->list));
910 else
911 node_info->list=(ColorPacket *) ResizeQuantumMemory(node_info->list,
912 (size_t) (i+1),sizeof(*node_info->list));
913 if (node_info->list == (ColorPacket *) NULL)
914 {
915 (void) ThrowMagickException(exception,GetMagickModule(),
916 ResourceLimitError,"MemoryAllocationFailed","`%s'",
917 image->filename);
918 break;
919 }
920 node_info->list[i].pixel=(*p);
921 if ((image->colorspace == CMYKColorspace) ||
922 (image->storage_class == PseudoClass))
923 node_info->list[i].index=indexes[x];
924 node_info->list[i].count=1;
925 node_info->number_unique++;
926 cube_info->colors++;
927 if (cube_info->colors > 256)
928 break;
929 }
930 p++;
931 }
cristybb503372010-05-27 20:51:26 +0000932 if (x < (ssize_t) image->columns)
cristyf2e11662009-10-14 01:24:43 +0000933 break;
934 }
935 image_view=DestroyCacheView(image_view);
936 cube_info=DestroyCubeInfo(image,cube_info);
cristybb503372010-05-27 20:51:26 +0000937 return(y < (ssize_t) image->rows ? MagickFalse : MagickTrue);
cristyf2e11662009-10-14 01:24:43 +0000938}
939
940/*
cristy3ed852e2009-09-05 21:47:34 +0000941%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
942% %
943% %
944% %
945% M i n M a x S t r e t c h I m a g e %
946% %
947% %
948% %
949%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
950%
951% MinMaxStretchImage() uses the exact minimum and maximum values found in
952% each of the channels given, as the BlackPoint and WhitePoint to linearly
953% stretch the colors (and histogram) of the image. The stretch points are
954% also moved further inward by the adjustment values given.
955%
956% If the adjustment values are both zero this function is equivelent to a
957% perfect normalization (or autolevel) of the image.
958%
959% Each channel is stretched independantally of each other (producing color
960% distortion) unless the special 'SyncChannels' flag is also provided in the
961% channels setting. If this flag is present the minimum and maximum point
962% will be extracted from all the given channels, and those channels will be
963% stretched by exactly the same amount (preventing color distortion).
964%
anthony7fe39fc2010-04-06 03:19:20 +0000965% In the special case that only ONE value is found in a channel of the image
966% that value is not stretched, that value is left as is.
967%
cristy3ed852e2009-09-05 21:47:34 +0000968% The 'SyncChannels' is turned on in the 'DefaultChannels' setting by
969% default.
970%
971% The format of the MinMaxStretchImage method is:
972%
973% MagickBooleanType MinMaxStretchImage(Image *image,
974% const ChannelType channel, const double black_adjust,
975% const double white_adjust)
976%
977% A description of each parameter follows:
978%
979% o image: The image to auto-level
980%
981% o channel: The channels to auto-level. If the special 'SyncChannels'
982% flag is set, all the given channels are stretched by the same amount.
983%
984% o black_adjust, white_adjust: Move the Black/White Point inward
985% from the minimum and maximum points by this color value.
986%
987*/
anthony7fe39fc2010-04-06 03:19:20 +0000988
cristy3ed852e2009-09-05 21:47:34 +0000989MagickExport MagickBooleanType MinMaxStretchImage(Image *image,
cristyf2e11662009-10-14 01:24:43 +0000990 const ChannelType channel,const double black_value,const double white_value)
cristy3ed852e2009-09-05 21:47:34 +0000991{
992 double
cristy19eb6412010-04-23 14:42:29 +0000993 min,
994 max;
cristy3ed852e2009-09-05 21:47:34 +0000995
996 MagickStatusType
997 status;
998
anthony7fe39fc2010-04-06 03:19:20 +0000999 status=MagickTrue;
cristyf2e11662009-10-14 01:24:43 +00001000 if ((channel & SyncChannels) != 0)
cristy3ed852e2009-09-05 21:47:34 +00001001 {
1002 /*
cristyf2e11662009-10-14 01:24:43 +00001003 Auto-level all channels equally.
cristy3ed852e2009-09-05 21:47:34 +00001004 */
cristyf2e11662009-10-14 01:24:43 +00001005 (void) GetImageChannelRange(image,channel,&min,&max,&image->exception);
1006 min+=black_value;
1007 max-=white_value;
cristy19eb6412010-04-23 14:42:29 +00001008 if (fabs(min-max) >= MagickEpsilon)
1009 status&=LevelImageChannel(image,channel,min,max,1.0);
1010 return(status != 0 ? MagickTrue : MagickFalse);
cristy3ed852e2009-09-05 21:47:34 +00001011 }
cristy3ed852e2009-09-05 21:47:34 +00001012 /*
cristyf2e11662009-10-14 01:24:43 +00001013 Auto-level each channel separately.
cristy3ed852e2009-09-05 21:47:34 +00001014 */
cristy3ed852e2009-09-05 21:47:34 +00001015 if ((channel & RedChannel) != 0)
1016 {
cristyf2e11662009-10-14 01:24:43 +00001017 (void) GetImageChannelRange(image,RedChannel,&min,&max,&image->exception);
1018 min+=black_value;
1019 max-=white_value;
cristy19eb6412010-04-23 14:42:29 +00001020 if (fabs(min-max) >= MagickEpsilon)
anthony7fe39fc2010-04-06 03:19:20 +00001021 status&=LevelImageChannel(image,RedChannel,min,max,1.0);
cristy3ed852e2009-09-05 21:47:34 +00001022 }
1023 if ((channel & GreenChannel) != 0)
1024 {
cristyf2e11662009-10-14 01:24:43 +00001025 (void) GetImageChannelRange(image,GreenChannel,&min,&max,
1026 &image->exception);
1027 min+=black_value;
1028 max-=white_value;
cristy19eb6412010-04-23 14:42:29 +00001029 if (fabs(min-max) >= MagickEpsilon)
anthony7fe39fc2010-04-06 03:19:20 +00001030 status&=LevelImageChannel(image,GreenChannel,min,max,1.0);
cristy3ed852e2009-09-05 21:47:34 +00001031 }
1032 if ((channel & BlueChannel) != 0)
1033 {
cristyf2e11662009-10-14 01:24:43 +00001034 (void) GetImageChannelRange(image,BlueChannel,&min,&max,
1035 &image->exception);
1036 min+=black_value;
1037 max-=white_value;
cristy19eb6412010-04-23 14:42:29 +00001038 if (fabs(min-max) >= MagickEpsilon)
anthony7fe39fc2010-04-06 03:19:20 +00001039 status&=LevelImageChannel(image,BlueChannel,min,max,1.0);
cristy3ed852e2009-09-05 21:47:34 +00001040 }
1041 if (((channel & OpacityChannel) != 0) &&
cristyf2e11662009-10-14 01:24:43 +00001042 (image->matte == MagickTrue))
cristy3ed852e2009-09-05 21:47:34 +00001043 {
cristyf2e11662009-10-14 01:24:43 +00001044 (void) GetImageChannelRange(image,OpacityChannel,&min,&max,
1045 &image->exception);
1046 min+=black_value;
1047 max-=white_value;
cristy19eb6412010-04-23 14:42:29 +00001048 if (fabs(min-max) >= MagickEpsilon)
anthony7fe39fc2010-04-06 03:19:20 +00001049 status&=LevelImageChannel(image,OpacityChannel,min,max,1.0);
cristy3ed852e2009-09-05 21:47:34 +00001050 }
1051 if (((channel & IndexChannel) != 0) &&
cristyf2e11662009-10-14 01:24:43 +00001052 (image->colorspace == CMYKColorspace))
cristy3ed852e2009-09-05 21:47:34 +00001053 {
cristyf2e11662009-10-14 01:24:43 +00001054 (void) GetImageChannelRange(image,IndexChannel,&min,&max,
1055 &image->exception);
1056 min+=black_value;
1057 max-=white_value;
cristy19eb6412010-04-23 14:42:29 +00001058 if (fabs(min-max) >= MagickEpsilon)
anthony7fe39fc2010-04-06 03:19:20 +00001059 status&=LevelImageChannel(image,IndexChannel,min,max,1.0);
cristy3ed852e2009-09-05 21:47:34 +00001060 }
cristy19eb6412010-04-23 14:42:29 +00001061 return(status != 0 ? MagickTrue : MagickFalse);
cristy3ed852e2009-09-05 21:47:34 +00001062}
1063
cristyf2e11662009-10-14 01:24:43 +00001064/*
1065%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1066% %
1067% %
1068% %
1069% G e t N u m b e r C o l o r s %
1070% %
1071% %
1072% %
1073%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1074%
1075% GetNumberColors() returns the number of unique colors in an image.
1076%
1077% The format of the GetNumberColors method is:
1078%
cristybb503372010-05-27 20:51:26 +00001079% size_t GetNumberColors(const Image *image,FILE *file,
cristyf2e11662009-10-14 01:24:43 +00001080% ExceptionInfo *exception)
1081%
1082% A description of each parameter follows.
1083%
1084% o image: the image.
1085%
1086% o file: Write a histogram of the color distribution to this file handle.
1087%
1088% o exception: return any errors or warnings in this structure.
1089%
1090*/
1091
1092#if defined(__cplusplus) || defined(c_plusplus)
1093extern "C" {
1094#endif
1095
1096static int HistogramCompare(const void *x,const void *y)
1097{
1098 const ColorPacket
1099 *color_1,
1100 *color_2;
1101
1102 color_1=(const ColorPacket *) x;
1103 color_2=(const ColorPacket *) y;
1104 if (color_2->pixel.red != color_1->pixel.red)
1105 return((int) color_1->pixel.red-(int) color_2->pixel.red);
1106 if (color_2->pixel.green != color_1->pixel.green)
1107 return((int) color_1->pixel.green-(int) color_2->pixel.green);
1108 if (color_2->pixel.blue != color_1->pixel.blue)
1109 return((int) color_1->pixel.blue-(int) color_2->pixel.blue);
1110 return((int) color_2->count-(int) color_1->count);
1111}
1112
1113#if defined(__cplusplus) || defined(c_plusplus)
1114}
1115#endif
1116
cristybb503372010-05-27 20:51:26 +00001117MagickExport size_t GetNumberColors(const Image *image,FILE *file,
cristyf2e11662009-10-14 01:24:43 +00001118 ExceptionInfo *exception)
1119{
1120#define HistogramImageTag "Histogram/Image"
1121
1122 char
1123 color[MaxTextExtent],
1124 hex[MaxTextExtent],
1125 tuple[MaxTextExtent];
1126
1127 ColorPacket
1128 *histogram;
1129
cristy8b27a6d2010-02-14 03:31:15 +00001130 MagickBooleanType
1131 status;
1132
cristyf2e11662009-10-14 01:24:43 +00001133 MagickPixelPacket
1134 pixel;
1135
1136 register ColorPacket
1137 *p;
1138
cristybb503372010-05-27 20:51:26 +00001139 register ssize_t
cristyf2e11662009-10-14 01:24:43 +00001140 i;
1141
cristybb503372010-05-27 20:51:26 +00001142 size_t
cristyf2e11662009-10-14 01:24:43 +00001143 number_colors;
1144
1145 number_colors=0;
1146 if (file == (FILE *) NULL)
1147 {
1148 CubeInfo
1149 *cube_info;
1150
1151 cube_info=ClassifyImageColors(image,exception);
1152 if (cube_info != (CubeInfo *) NULL)
1153 number_colors=cube_info->colors;
1154 cube_info=DestroyCubeInfo(image,cube_info);
1155 return(number_colors);
1156 }
1157 histogram=GetImageHistogram(image,&number_colors,exception);
1158 if (histogram == (ColorPacket *) NULL)
1159 return(number_colors);
1160 qsort((void *) histogram,(size_t) number_colors,sizeof(*histogram),
1161 HistogramCompare);
1162 GetMagickPixelPacket(image,&pixel);
1163 p=histogram;
cristybb503372010-05-27 20:51:26 +00001164 for (i=0; i < (ssize_t) number_colors; i++)
cristyf2e11662009-10-14 01:24:43 +00001165 {
1166 SetMagickPixelPacket(image,&p->pixel,&p->index,&pixel);
1167 (void) CopyMagickString(tuple,"(",MaxTextExtent);
1168 ConcatenateColorComponent(&pixel,RedChannel,X11Compliance,tuple);
1169 (void) ConcatenateMagickString(tuple,",",MaxTextExtent);
1170 ConcatenateColorComponent(&pixel,GreenChannel,X11Compliance,tuple);
1171 (void) ConcatenateMagickString(tuple,",",MaxTextExtent);
1172 ConcatenateColorComponent(&pixel,BlueChannel,X11Compliance,tuple);
1173 if (pixel.colorspace == CMYKColorspace)
1174 {
1175 (void) ConcatenateMagickString(tuple,",",MaxTextExtent);
1176 ConcatenateColorComponent(&pixel,IndexChannel,X11Compliance,tuple);
1177 }
1178 if (pixel.matte != MagickFalse)
1179 {
1180 (void) ConcatenateMagickString(tuple,",",MaxTextExtent);
1181 ConcatenateColorComponent(&pixel,OpacityChannel,X11Compliance,tuple);
1182 }
1183 (void) ConcatenateMagickString(tuple,")",MaxTextExtent);
1184 (void) QueryMagickColorname(image,&pixel,SVGCompliance,color,exception);
1185 GetColorTuple(&pixel,MagickTrue,hex);
cristy24fb41a2010-06-02 14:01:14 +00001186 (void) fprintf(file,"%10" MagickSizeFormat,p->count);
cristyf2e11662009-10-14 01:24:43 +00001187 (void) fprintf(file,": %s %s %s\n",tuple,hex,color);
cristy8b27a6d2010-02-14 03:31:15 +00001188 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1189 {
1190 MagickBooleanType
1191 proceed;
1192
cristycee97112010-05-28 00:44:52 +00001193 proceed=SetImageProgress(image,HistogramImageTag,(MagickOffsetType) i,
1194 number_colors);
cristy8b27a6d2010-02-14 03:31:15 +00001195 if (proceed == MagickFalse)
1196 status=MagickFalse;
1197 }
cristyf2e11662009-10-14 01:24:43 +00001198 p++;
1199 }
1200 (void) fflush(file);
1201 histogram=(ColorPacket *) RelinquishMagickMemory(histogram);
1202 return(number_colors);
1203}
1204
1205/*
1206%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1207% %
1208% %
1209% %
1210% U n i q u e I m a g e C o l o r s %
1211% %
1212% %
1213% %
1214%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1215%
1216% UniqueImageColors() returns the unique colors of an image.
1217%
1218% The format of the UniqueImageColors method is:
1219%
1220% Image *UniqueImageColors(const Image *image,ExceptionInfo *exception)
1221%
1222% A description of each parameter follows.
1223%
1224% o image: the image.
1225%
1226% o exception: return any errors or warnings in this structure.
1227%
1228*/
1229
cristyc5c6f662010-09-22 14:23:02 +00001230static void UniqueColorsToImage(Image *unique_image,CacheView *unique_view,
1231 CubeInfo *cube_info,const NodeInfo *node_info,ExceptionInfo *exception)
cristyf2e11662009-10-14 01:24:43 +00001232{
1233#define UniqueColorsImageTag "UniqueColors/Image"
1234
cristy8b27a6d2010-02-14 03:31:15 +00001235 MagickBooleanType
1236 status;
1237
cristybb503372010-05-27 20:51:26 +00001238 register ssize_t
cristyf2e11662009-10-14 01:24:43 +00001239 i;
1240
cristybb503372010-05-27 20:51:26 +00001241 size_t
cristyf2e11662009-10-14 01:24:43 +00001242 number_children;
1243
1244 /*
1245 Traverse any children.
1246 */
cristyc5c6f662010-09-22 14:23:02 +00001247 number_children=unique_image->matte == MagickFalse ? 8UL : 16UL;
cristybb503372010-05-27 20:51:26 +00001248 for (i=0; i < (ssize_t) number_children; i++)
cristyf2e11662009-10-14 01:24:43 +00001249 if (node_info->child[i] != (NodeInfo *) NULL)
cristyc5c6f662010-09-22 14:23:02 +00001250 UniqueColorsToImage(unique_image,unique_view,cube_info,
1251 node_info->child[i],exception);
cristyf2e11662009-10-14 01:24:43 +00001252 if (node_info->level == (MaxTreeDepth-1))
1253 {
1254 register ColorPacket
1255 *p;
1256
1257 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001258 *restrict indexes;
cristyf2e11662009-10-14 01:24:43 +00001259
1260 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001261 *restrict q;
cristyf2e11662009-10-14 01:24:43 +00001262
1263 p=node_info->list;
cristybb503372010-05-27 20:51:26 +00001264 for (i=0; i < (ssize_t) node_info->number_unique; i++)
cristyf2e11662009-10-14 01:24:43 +00001265 {
cristyc5c6f662010-09-22 14:23:02 +00001266 q=QueueCacheViewAuthenticPixels(unique_view,cube_info->x,0,1,1,
1267 exception);
cristyf2e11662009-10-14 01:24:43 +00001268 if (q == (PixelPacket *) NULL)
1269 continue;
cristyc5c6f662010-09-22 14:23:02 +00001270 indexes=GetCacheViewAuthenticIndexQueue(unique_view);
cristyf2e11662009-10-14 01:24:43 +00001271 *q=p->pixel;
cristyc5c6f662010-09-22 14:23:02 +00001272 if (unique_image->colorspace == CMYKColorspace)
cristyf2e11662009-10-14 01:24:43 +00001273 *indexes=p->index;
cristyc5c6f662010-09-22 14:23:02 +00001274 if (SyncCacheViewAuthenticPixels(unique_view,exception) == MagickFalse)
cristyf2e11662009-10-14 01:24:43 +00001275 break;
1276 cube_info->x++;
1277 p++;
1278 }
cristyc5c6f662010-09-22 14:23:02 +00001279 if (unique_image->progress_monitor != (MagickProgressMonitor) NULL)
cristy8b27a6d2010-02-14 03:31:15 +00001280 {
1281 MagickBooleanType
1282 proceed;
1283
cristyc5c6f662010-09-22 14:23:02 +00001284 proceed=SetImageProgress(unique_image,UniqueColorsImageTag,
cristy8b27a6d2010-02-14 03:31:15 +00001285 cube_info->progress,cube_info->colors);
1286 if (proceed == MagickFalse)
1287 status=MagickFalse;
1288 }
cristyf2e11662009-10-14 01:24:43 +00001289 cube_info->progress++;
1290 }
1291}
1292
1293MagickExport Image *UniqueImageColors(const Image *image,
1294 ExceptionInfo *exception)
1295{
cristyc5c6f662010-09-22 14:23:02 +00001296 CacheView
1297 *unique_view;
1298
cristyf2e11662009-10-14 01:24:43 +00001299 CubeInfo
1300 *cube_info;
1301
1302 Image
1303 *unique_image;
1304
1305 cube_info=ClassifyImageColors(image,exception);
1306 if (cube_info == (CubeInfo *) NULL)
1307 return((Image *) NULL);
1308 unique_image=CloneImage(image,cube_info->colors,1,MagickTrue,exception);
1309 if (unique_image == (Image *) NULL)
1310 return(unique_image);
1311 if (SetImageStorageClass(unique_image,DirectClass) == MagickFalse)
1312 {
1313 InheritException(exception,&unique_image->exception);
1314 unique_image=DestroyImage(unique_image);
1315 return((Image *) NULL);
1316 }
cristyc5c6f662010-09-22 14:23:02 +00001317 unique_view=AcquireCacheView(unique_image);
1318 UniqueColorsToImage(unique_image,unique_view,cube_info,cube_info->root,
1319 exception);
1320 unique_view=DestroyCacheView(unique_view);
cristyf2e11662009-10-14 01:24:43 +00001321 if (cube_info->colors < MaxColormapSize)
1322 {
1323 QuantizeInfo
1324 *quantize_info;
1325
1326 quantize_info=AcquireQuantizeInfo((ImageInfo *) NULL);
1327 quantize_info->number_colors=MaxColormapSize;
1328 quantize_info->dither=MagickFalse;
1329 quantize_info->tree_depth=8;
1330 (void) QuantizeImage(quantize_info,unique_image);
1331 quantize_info=DestroyQuantizeInfo(quantize_info);
1332 }
1333 cube_info=DestroyCubeInfo(image,cube_info);
1334 return(unique_image);
1335}