blob: af17ac2907f4c32feca2a7c586f7b4ec977e1c5f [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*/
cristy4c08aed2011-07-01 19:47:50 +000043#include "MagickCore/studio.h"
44#include "MagickCore/cache-view.h"
45#include "MagickCore/color-private.h"
46#include "MagickCore/enhance.h"
47#include "MagickCore/exception.h"
48#include "MagickCore/exception-private.h"
49#include "MagickCore/hashmap.h"
50#include "MagickCore/histogram.h"
51#include "MagickCore/image.h"
52#include "MagickCore/list.h"
53#include "MagickCore/memory_.h"
54#include "MagickCore/monitor-private.h"
55#include "MagickCore/pixel-accessor.h"
56#include "MagickCore/prepress.h"
57#include "MagickCore/quantize.h"
58#include "MagickCore/registry.h"
59#include "MagickCore/semaphore.h"
60#include "MagickCore/splay-tree.h"
61#include "MagickCore/statistic.h"
62#include "MagickCore/string_.h"
cristy3ed852e2009-09-05 21:47:34 +000063
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
cristy101ab702011-10-13 13:06:32 +000078 PixelInfo
cristyf2e11662009-10-14 01:24:43 +000079 *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,
cristy4c08aed2011-07-01 19:47:50 +0000160 const PixelInfo *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)
cristy4c08aed2011-07-01 19:47:50 +0000170 id|=((ScaleQuantumToChar(ClampToQuantum(pixel->alpha)) >> 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
cristyf2e11662009-10-14 01:24:43 +0000186 MagickBooleanType
187 proceed;
188
cristy4c08aed2011-07-01 19:47:50 +0000189 PixelInfo
cristyf2e11662009-10-14 01:24:43 +0000190 pixel,
191 target;
192
193 NodeInfo
194 *node_info;
195
cristy4c08aed2011-07-01 19:47:50 +0000196 register const Quantum
cristyf2e11662009-10-14 01:24:43 +0000197 *p;
198
cristybb503372010-05-27 20:51:26 +0000199 register size_t
cristyf2e11662009-10-14 01:24:43 +0000200 id,
201 index,
202 level;
203
cristy9d314ff2011-03-09 01:30:28 +0000204 register ssize_t
205 i,
206 x;
207
208 ssize_t
209 y;
210
cristyf2e11662009-10-14 01:24:43 +0000211 /*
212 Initialize color description tree.
213 */
214 assert(image != (const Image *) NULL);
215 assert(image->signature == MagickSignature);
216 if (image->debug != MagickFalse)
217 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
218 cube_info=GetCubeInfo();
219 if (cube_info == (CubeInfo *) NULL)
220 {
221 (void) ThrowMagickException(exception,GetMagickModule(),
222 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
223 return(cube_info);
224 }
cristy4c08aed2011-07-01 19:47:50 +0000225 GetPixelInfo(image,&pixel);
226 GetPixelInfo(image,&target);
cristyf2e11662009-10-14 01:24:43 +0000227 image_view=AcquireCacheView(image);
cristybb503372010-05-27 20:51:26 +0000228 for (y=0; y < (ssize_t) image->rows; y++)
cristyf2e11662009-10-14 01:24:43 +0000229 {
230 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +0000231 if (p == (const Quantum *) NULL)
cristyf2e11662009-10-14 01:24:43 +0000232 break;
cristybb503372010-05-27 20:51:26 +0000233 for (x=0; x < (ssize_t) image->columns; x++)
cristyf2e11662009-10-14 01:24:43 +0000234 {
235 /*
236 Start at the root and proceed level by level.
237 */
238 node_info=cube_info->root;
239 index=MaxTreeDepth-1;
240 for (level=1; level < MaxTreeDepth; level++)
241 {
cristy4c08aed2011-07-01 19:47:50 +0000242 SetPixelInfo(image,p,&pixel);
cristyf2e11662009-10-14 01:24:43 +0000243 id=ColorToNodeId(image,&pixel,index);
244 if (node_info->child[id] == (NodeInfo *) NULL)
245 {
246 node_info->child[id]=GetNodeInfo(cube_info,level);
247 if (node_info->child[id] == (NodeInfo *) NULL)
248 {
249 (void) ThrowMagickException(exception,GetMagickModule(),
250 ResourceLimitError,"MemoryAllocationFailed","`%s'",
251 image->filename);
252 return(0);
253 }
254 }
255 node_info=node_info->child[id];
256 index--;
257 }
cristybb503372010-05-27 20:51:26 +0000258 for (i=0; i < (ssize_t) node_info->number_unique; i++)
cristyf2e11662009-10-14 01:24:43 +0000259 {
cristy4c08aed2011-07-01 19:47:50 +0000260 SetPixelInfoPacket(image,&node_info->list[i],&target);
261 if (IsPixelInfoEquivalent(&pixel,&target) != MagickFalse)
cristyf2e11662009-10-14 01:24:43 +0000262 break;
263 }
cristybb503372010-05-27 20:51:26 +0000264 if (i < (ssize_t) node_info->number_unique)
cristyf2e11662009-10-14 01:24:43 +0000265 node_info->list[i].count++;
266 else
267 {
268 if (node_info->number_unique == 0)
cristy101ab702011-10-13 13:06:32 +0000269 node_info->list=(PixelInfo *) AcquireMagickMemory(
cristyf2e11662009-10-14 01:24:43 +0000270 sizeof(*node_info->list));
271 else
cristy101ab702011-10-13 13:06:32 +0000272 node_info->list=(PixelInfo *) ResizeQuantumMemory(node_info->list,
cristyf2e11662009-10-14 01:24:43 +0000273 (size_t) (i+1),sizeof(*node_info->list));
cristy101ab702011-10-13 13:06:32 +0000274 if (node_info->list == (PixelInfo *) NULL)
cristyf2e11662009-10-14 01:24:43 +0000275 {
276 (void) ThrowMagickException(exception,GetMagickModule(),
277 ResourceLimitError,"MemoryAllocationFailed","`%s'",
278 image->filename);
279 return(0);
280 }
cristy4c08aed2011-07-01 19:47:50 +0000281 node_info->list[i].red=GetPixelRed(image,p);
282 node_info->list[i].green=GetPixelGreen(image,p);
283 node_info->list[i].blue=GetPixelBlue(image,p);
284 if (image->colorspace == CMYKColorspace)
285 node_info->list[i].black=GetPixelBlack(image,p);
286 node_info->list[i].alpha=GetPixelAlpha(image,p);
cristyf2e11662009-10-14 01:24:43 +0000287 node_info->list[i].count=1;
288 node_info->number_unique++;
289 cube_info->colors++;
290 }
cristyed231572011-07-14 02:18:59 +0000291 p+=GetPixelChannels(image);
cristyf2e11662009-10-14 01:24:43 +0000292 }
cristycee97112010-05-28 00:44:52 +0000293 proceed=SetImageProgress(image,EvaluateImageTag,(MagickOffsetType) y,
294 image->rows);
cristyf2e11662009-10-14 01:24:43 +0000295 if (proceed == MagickFalse)
296 break;
297 }
298 image_view=DestroyCacheView(image_view);
299 return(cube_info);
300}
301
302/*
303%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
304% %
305% %
306% %
307+ D e f i n e I m a g e H i s t o g r a m %
308% %
309% %
310% %
311%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
312%
313% DefineImageHistogram() traverses the color cube tree and notes each colormap
314% entry. A colormap entry is any node in the color cube tree where the
315% of unique colors is not zero.
316%
317% The format of the DefineImageHistogram method is:
318%
319% DefineImageHistogram(const Image *image,NodeInfo *node_info,
cristy101ab702011-10-13 13:06:32 +0000320% PixelInfo **unique_colors)
cristyf2e11662009-10-14 01:24:43 +0000321%
322% A description of each parameter follows.
323%
324% o image: the image.
325%
326% o node_info: the address of a structure of type NodeInfo which points to a
327% node in the color cube tree that is to be pruned.
328%
329% o histogram: the image histogram.
330%
331*/
332static void DefineImageHistogram(const Image *image,NodeInfo *node_info,
cristy101ab702011-10-13 13:06:32 +0000333 PixelInfo **histogram)
cristyf2e11662009-10-14 01:24:43 +0000334{
cristybb503372010-05-27 20:51:26 +0000335 register ssize_t
cristyf2e11662009-10-14 01:24:43 +0000336 i;
337
cristybb503372010-05-27 20:51:26 +0000338 size_t
cristyf2e11662009-10-14 01:24:43 +0000339 number_children;
340
341 /*
342 Traverse any children.
343 */
344 number_children=image->matte == MagickFalse ? 8UL : 16UL;
cristybb503372010-05-27 20:51:26 +0000345 for (i=0; i < (ssize_t) number_children; i++)
cristyf2e11662009-10-14 01:24:43 +0000346 if (node_info->child[i] != (NodeInfo *) NULL)
347 DefineImageHistogram(image,node_info->child[i],histogram);
348 if (node_info->level == (MaxTreeDepth-1))
349 {
cristy101ab702011-10-13 13:06:32 +0000350 register PixelInfo
cristyf2e11662009-10-14 01:24:43 +0000351 *p;
352
353 p=node_info->list;
cristybb503372010-05-27 20:51:26 +0000354 for (i=0; i < (ssize_t) node_info->number_unique; i++)
cristyf2e11662009-10-14 01:24:43 +0000355 {
cristy4c08aed2011-07-01 19:47:50 +0000356 (*histogram)->red=p->red;
357 (*histogram)->green=p->green;
358 (*histogram)->blue=p->blue;
359 (*histogram)->black=p->black;
360 (*histogram)->alpha=p->alpha;
cristyf2e11662009-10-14 01:24:43 +0000361 (*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]);
cristy101ab702011-10-13 13:06:32 +0000452 if (node_info->list != (PixelInfo *) NULL)
453 node_info->list=(PixelInfo *) RelinquishMagickMemory(node_info->list);
cristyf2e11662009-10-14 01:24:43 +0000454}
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*/
cristy101ab702011-10-13 13:06:32 +0000526MagickExport PixelInfo *GetImageHistogram(const Image *image,
cristybb503372010-05-27 20:51:26 +0000527 size_t *number_colors,ExceptionInfo *exception)
cristyf2e11662009-10-14 01:24:43 +0000528{
cristy101ab702011-10-13 13:06:32 +0000529 PixelInfo
cristyf2e11662009-10-14 01:24:43 +0000530 *histogram;
531
532 CubeInfo
533 *cube_info;
534
535 *number_colors=0;
cristy101ab702011-10-13 13:06:32 +0000536 histogram=(PixelInfo *) NULL;
cristyf2e11662009-10-14 01:24:43 +0000537 cube_info=ClassifyImageColors(image,exception);
538 if (cube_info != (CubeInfo *) NULL)
539 {
cristy101ab702011-10-13 13:06:32 +0000540 histogram=(PixelInfo *) AcquireQuantumMemory((size_t) cube_info->colors,
cristyf2e11662009-10-14 01:24:43 +0000541 sizeof(*histogram));
cristy101ab702011-10-13 13:06:32 +0000542 if (histogram == (PixelInfo *) NULL)
cristyf2e11662009-10-14 01:24:43 +0000543 (void) ThrowMagickException(exception,GetMagickModule(),
544 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
545 else
546 {
cristy101ab702011-10-13 13:06:32 +0000547 PixelInfo
cristyf2e11662009-10-14 01:24:43 +0000548 *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
cristy4c08aed2011-07-01 19:47:50 +0000649 PixelInfo
cristyf2e11662009-10-14 01:24:43 +0000650 pixel,
651 target;
652
cristy4c08aed2011-07-01 19:47:50 +0000653 register const Quantum
cristyf2e11662009-10-14 01:24:43 +0000654 *p;
655
cristybb503372010-05-27 20:51:26 +0000656 register ssize_t
cristyf2e11662009-10-14 01:24:43 +0000657 x;
658
659 register NodeInfo
660 *node_info;
661
cristybb503372010-05-27 20:51:26 +0000662 register ssize_t
cristyf2e11662009-10-14 01:24:43 +0000663 i;
664
cristybb503372010-05-27 20:51:26 +0000665 size_t
cristyf2e11662009-10-14 01:24:43 +0000666 id,
667 index,
668 level;
669
cristy9d314ff2011-03-09 01:30:28 +0000670 ssize_t
671 y;
672
cristyf2e11662009-10-14 01:24:43 +0000673 assert(image != (Image *) NULL);
674 assert(image->signature == MagickSignature);
675 if (image->debug != MagickFalse)
676 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
677 if ((image->storage_class == PseudoClass) && (image->colors <= 256))
678 return(MagickTrue);
679 if (image->storage_class == PseudoClass)
680 return(MagickFalse);
681 /*
682 Initialize color description tree.
683 */
684 cube_info=GetCubeInfo();
685 if (cube_info == (CubeInfo *) NULL)
686 {
687 (void) ThrowMagickException(exception,GetMagickModule(),
688 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
689 return(MagickFalse);
690 }
cristy4c08aed2011-07-01 19:47:50 +0000691 GetPixelInfo(image,&pixel);
692 GetPixelInfo(image,&target);
cristyf2e11662009-10-14 01:24:43 +0000693 image_view=AcquireCacheView(image);
cristybb503372010-05-27 20:51:26 +0000694 for (y=0; y < (ssize_t) image->rows; y++)
cristyf2e11662009-10-14 01:24:43 +0000695 {
696 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +0000697 if (p == (const Quantum *) NULL)
cristyf2e11662009-10-14 01:24:43 +0000698 break;
cristybb503372010-05-27 20:51:26 +0000699 for (x=0; x < (ssize_t) image->columns; x++)
cristyf2e11662009-10-14 01:24:43 +0000700 {
701 /*
702 Start at the root and proceed level by level.
703 */
704 node_info=cube_info->root;
705 index=MaxTreeDepth-1;
706 for (level=1; level < MaxTreeDepth; level++)
707 {
cristy4c08aed2011-07-01 19:47:50 +0000708 SetPixelInfo(image,p,&pixel);
cristyf2e11662009-10-14 01:24:43 +0000709 id=ColorToNodeId(image,&pixel,index);
710 if (node_info->child[id] == (NodeInfo *) NULL)
711 {
712 node_info->child[id]=GetNodeInfo(cube_info,level);
713 if (node_info->child[id] == (NodeInfo *) NULL)
714 {
715 (void) ThrowMagickException(exception,GetMagickModule(),
716 ResourceLimitError,"MemoryAllocationFailed","`%s'",
717 image->filename);
718 break;
719 }
720 }
721 node_info=node_info->child[id];
722 index--;
723 }
724 if (level < MaxTreeDepth)
725 break;
cristybb503372010-05-27 20:51:26 +0000726 for (i=0; i < (ssize_t) node_info->number_unique; i++)
cristyf2e11662009-10-14 01:24:43 +0000727 {
cristy4c08aed2011-07-01 19:47:50 +0000728 SetPixelInfoPacket(image,&node_info->list[i],&target);
729 if (IsPixelInfoEquivalent(&pixel,&target) != MagickFalse)
cristyf2e11662009-10-14 01:24:43 +0000730 break;
731 }
cristybb503372010-05-27 20:51:26 +0000732 if (i < (ssize_t) node_info->number_unique)
cristyf2e11662009-10-14 01:24:43 +0000733 node_info->list[i].count++;
734 else
735 {
736 /*
737 Add this unique color to the color list.
738 */
739 if (node_info->number_unique == 0)
cristy101ab702011-10-13 13:06:32 +0000740 node_info->list=(PixelInfo *) AcquireMagickMemory(
cristyf2e11662009-10-14 01:24:43 +0000741 sizeof(*node_info->list));
742 else
cristy101ab702011-10-13 13:06:32 +0000743 node_info->list=(PixelInfo *) ResizeQuantumMemory(node_info->list,
cristyf2e11662009-10-14 01:24:43 +0000744 (size_t) (i+1),sizeof(*node_info->list));
cristy101ab702011-10-13 13:06:32 +0000745 if (node_info->list == (PixelInfo *) NULL)
cristyf2e11662009-10-14 01:24:43 +0000746 {
747 (void) ThrowMagickException(exception,GetMagickModule(),
748 ResourceLimitError,"MemoryAllocationFailed","`%s'",
749 image->filename);
750 break;
751 }
cristy4c08aed2011-07-01 19:47:50 +0000752 node_info->list[i].red=GetPixelRed(image,p);
753 node_info->list[i].green=GetPixelGreen(image,p);
754 node_info->list[i].blue=GetPixelBlue(image,p);
755 if (image->colorspace == CMYKColorspace)
756 node_info->list[i].black=GetPixelBlack(image,p);
757 node_info->list[i].alpha=GetPixelAlpha(image,p);
cristyf2e11662009-10-14 01:24:43 +0000758 node_info->list[i].count=1;
759 node_info->number_unique++;
760 cube_info->colors++;
761 if (cube_info->colors > MaximumUniqueColors)
762 break;
763 }
cristyed231572011-07-14 02:18:59 +0000764 p+=GetPixelChannels(image);
cristyf2e11662009-10-14 01:24:43 +0000765 }
cristybb503372010-05-27 20:51:26 +0000766 if (x < (ssize_t) image->columns)
cristyf2e11662009-10-14 01:24:43 +0000767 break;
768 }
769 image_view=DestroyCacheView(image_view);
770 cube_info=DestroyCubeInfo(image,cube_info);
cristybb503372010-05-27 20:51:26 +0000771 return(y < (ssize_t) image->rows ? MagickFalse : MagickTrue);
cristyf2e11662009-10-14 01:24:43 +0000772}
773
774/*
775%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
776% %
777% %
778% %
779% I s P a l e t t e I m a g e %
780% %
781% %
782% %
783%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
784%
785% IsPaletteImage() returns MagickTrue if the image is PseudoClass and has 256
786% unique colors or less.
787%
788% The format of the IsPaletteImage method is:
789%
790% MagickBooleanType IsPaletteImage(const Image *image,
791% ExceptionInfo *exception)
792%
793% A description of each parameter follows.
794%
795% o image: the image.
796%
797% o exception: return any errors or warnings in this structure.
798%
799*/
800MagickExport MagickBooleanType IsPaletteImage(const Image *image,
801 ExceptionInfo *exception)
802{
cristyc4c8d132010-01-07 01:58:38 +0000803 CacheView
804 *image_view;
805
cristyf2e11662009-10-14 01:24:43 +0000806 CubeInfo
807 *cube_info;
808
cristy4c08aed2011-07-01 19:47:50 +0000809 PixelInfo
cristyf2e11662009-10-14 01:24:43 +0000810 pixel,
811 target;
812
cristy4c08aed2011-07-01 19:47:50 +0000813 register const Quantum
cristyf2e11662009-10-14 01:24:43 +0000814 *p;
815
cristybb503372010-05-27 20:51:26 +0000816 register ssize_t
cristyf2e11662009-10-14 01:24:43 +0000817 x;
818
819 register NodeInfo
820 *node_info;
821
cristybb503372010-05-27 20:51:26 +0000822 register ssize_t
cristyf2e11662009-10-14 01:24:43 +0000823 i;
824
cristybb503372010-05-27 20:51:26 +0000825 size_t
cristyf2e11662009-10-14 01:24:43 +0000826 id,
827 index,
828 level;
829
cristy9d314ff2011-03-09 01:30:28 +0000830 ssize_t
831 y;
832
cristyf2e11662009-10-14 01:24:43 +0000833 assert(image != (Image *) NULL);
834 assert(image->signature == MagickSignature);
835 if (image->debug != MagickFalse)
836 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
837 if ((image->storage_class == PseudoClass) && (image->colors <= 256))
838 return(MagickTrue);
839 if (image->storage_class == PseudoClass)
840 return(MagickFalse);
841 /*
842 Initialize color description tree.
843 */
844 cube_info=GetCubeInfo();
845 if (cube_info == (CubeInfo *) NULL)
846 {
847 (void) ThrowMagickException(exception,GetMagickModule(),
848 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
849 return(MagickFalse);
850 }
cristy4c08aed2011-07-01 19:47:50 +0000851 GetPixelInfo(image,&pixel);
852 GetPixelInfo(image,&target);
cristyf2e11662009-10-14 01:24:43 +0000853 image_view=AcquireCacheView(image);
cristybb503372010-05-27 20:51:26 +0000854 for (y=0; y < (ssize_t) image->rows; y++)
cristyf2e11662009-10-14 01:24:43 +0000855 {
856 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +0000857 if (p == (const Quantum *) NULL)
cristyf2e11662009-10-14 01:24:43 +0000858 break;
cristybb503372010-05-27 20:51:26 +0000859 for (x=0; x < (ssize_t) image->columns; x++)
cristyf2e11662009-10-14 01:24:43 +0000860 {
861 /*
862 Start at the root and proceed level by level.
863 */
864 node_info=cube_info->root;
865 index=MaxTreeDepth-1;
866 for (level=1; level < MaxTreeDepth; level++)
867 {
cristy4c08aed2011-07-01 19:47:50 +0000868 SetPixelInfo(image,p,&pixel);
cristyf2e11662009-10-14 01:24:43 +0000869 id=ColorToNodeId(image,&pixel,index);
870 if (node_info->child[id] == (NodeInfo *) NULL)
871 {
872 node_info->child[id]=GetNodeInfo(cube_info,level);
873 if (node_info->child[id] == (NodeInfo *) NULL)
874 {
875 (void) ThrowMagickException(exception,GetMagickModule(),
876 ResourceLimitError,"MemoryAllocationFailed","`%s'",
877 image->filename);
878 break;
879 }
880 }
881 node_info=node_info->child[id];
882 index--;
883 }
884 if (level < MaxTreeDepth)
885 break;
cristybb503372010-05-27 20:51:26 +0000886 for (i=0; i < (ssize_t) node_info->number_unique; i++)
cristyf2e11662009-10-14 01:24:43 +0000887 {
cristy4c08aed2011-07-01 19:47:50 +0000888 SetPixelInfoPacket(image,&node_info->list[i],&target);
889 if (IsPixelInfoEquivalent(&pixel,&target) != MagickFalse)
cristyf2e11662009-10-14 01:24:43 +0000890 break;
891 }
cristybb503372010-05-27 20:51:26 +0000892 if (i < (ssize_t) node_info->number_unique)
cristyf2e11662009-10-14 01:24:43 +0000893 node_info->list[i].count++;
894 else
895 {
896 /*
897 Add this unique color to the color list.
898 */
899 if (node_info->number_unique == 0)
cristy101ab702011-10-13 13:06:32 +0000900 node_info->list=(PixelInfo *) AcquireMagickMemory(
cristyf2e11662009-10-14 01:24:43 +0000901 sizeof(*node_info->list));
902 else
cristy101ab702011-10-13 13:06:32 +0000903 node_info->list=(PixelInfo *) ResizeQuantumMemory(node_info->list,
cristyf2e11662009-10-14 01:24:43 +0000904 (size_t) (i+1),sizeof(*node_info->list));
cristy101ab702011-10-13 13:06:32 +0000905 if (node_info->list == (PixelInfo *) NULL)
cristyf2e11662009-10-14 01:24:43 +0000906 {
907 (void) ThrowMagickException(exception,GetMagickModule(),
908 ResourceLimitError,"MemoryAllocationFailed","`%s'",
909 image->filename);
910 break;
911 }
cristy4c08aed2011-07-01 19:47:50 +0000912 node_info->list[i].red=GetPixelRed(image,p);
913 node_info->list[i].green=GetPixelGreen(image,p);
914 node_info->list[i].blue=GetPixelBlue(image,p);
915 if (image->colorspace == CMYKColorspace)
916 node_info->list[i].black=GetPixelBlack(image,p);
917 node_info->list[i].alpha=GetPixelAlpha(image,p);
cristyf2e11662009-10-14 01:24:43 +0000918 node_info->list[i].count=1;
919 node_info->number_unique++;
920 cube_info->colors++;
921 if (cube_info->colors > 256)
922 break;
923 }
cristyed231572011-07-14 02:18:59 +0000924 p+=GetPixelChannels(image);
cristyf2e11662009-10-14 01:24:43 +0000925 }
cristybb503372010-05-27 20:51:26 +0000926 if (x < (ssize_t) image->columns)
cristyf2e11662009-10-14 01:24:43 +0000927 break;
928 }
929 image_view=DestroyCacheView(image_view);
930 cube_info=DestroyCubeInfo(image,cube_info);
cristybb503372010-05-27 20:51:26 +0000931 return(y < (ssize_t) image->rows ? MagickFalse : MagickTrue);
cristyf2e11662009-10-14 01:24:43 +0000932}
933
934/*
cristy3ed852e2009-09-05 21:47:34 +0000935%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
936% %
937% %
938% %
939% M i n M a x S t r e t c h I m a g e %
940% %
941% %
942% %
943%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
944%
945% MinMaxStretchImage() uses the exact minimum and maximum values found in
946% each of the channels given, as the BlackPoint and WhitePoint to linearly
947% stretch the colors (and histogram) of the image. The stretch points are
948% also moved further inward by the adjustment values given.
949%
glennrpf0a92fd2011-04-27 13:17:21 +0000950% If the adjustment values are both zero this function is equivalent to a
cristy3ed852e2009-09-05 21:47:34 +0000951% perfect normalization (or autolevel) of the image.
952%
953% Each channel is stretched independantally of each other (producing color
954% distortion) unless the special 'SyncChannels' flag is also provided in the
955% channels setting. If this flag is present the minimum and maximum point
956% will be extracted from all the given channels, and those channels will be
957% stretched by exactly the same amount (preventing color distortion).
958%
anthony7fe39fc2010-04-06 03:19:20 +0000959% In the special case that only ONE value is found in a channel of the image
960% that value is not stretched, that value is left as is.
961%
cristy3ed852e2009-09-05 21:47:34 +0000962% The 'SyncChannels' is turned on in the 'DefaultChannels' setting by
963% default.
964%
965% The format of the MinMaxStretchImage method is:
966%
cristy490408a2011-07-07 14:42:05 +0000967% MagickBooleanType MinMaxStretchImage(Image *image,const double black,
cristya63e4a92011-09-09 00:47:59 +0000968% const double white,const double gamma,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000969%
970% A description of each parameter follows:
971%
972% o image: The image to auto-level
973%
cristy490408a2011-07-07 14:42:05 +0000974% o black, white: move the black / white point inward from the minimum and
975% maximum points by this color value.
cristy3ed852e2009-09-05 21:47:34 +0000976%
cristya63e4a92011-09-09 00:47:59 +0000977% o gamma: the gamma.
978%
cristy95111202011-08-09 19:41:42 +0000979% o exception: return any errors or warnings in this structure.
980%
cristy3ed852e2009-09-05 21:47:34 +0000981*/
cristy3ed852e2009-09-05 21:47:34 +0000982MagickExport MagickBooleanType MinMaxStretchImage(Image *image,
cristya63e4a92011-09-09 00:47:59 +0000983 const double black,const double white,const double gamma,
984 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000985{
986 double
cristy19eb6412010-04-23 14:42:29 +0000987 min,
988 max;
cristy3ed852e2009-09-05 21:47:34 +0000989
cristya63e4a92011-09-09 00:47:59 +0000990 register ssize_t
991 i;
992
cristy3ed852e2009-09-05 21:47:34 +0000993 MagickStatusType
994 status;
995
anthony7fe39fc2010-04-06 03:19:20 +0000996 status=MagickTrue;
cristy5f95f4f2011-10-23 01:01:01 +0000997 if (image->channel_mask == DefaultChannels)
cristy3ed852e2009-09-05 21:47:34 +0000998 {
999 /*
cristyf2e11662009-10-14 01:24:43 +00001000 Auto-level all channels equally.
cristy3ed852e2009-09-05 21:47:34 +00001001 */
cristy95111202011-08-09 19:41:42 +00001002 (void) GetImageRange(image,&min,&max,exception);
cristy490408a2011-07-07 14:42:05 +00001003 min+=black;
1004 max-=white;
cristy19eb6412010-04-23 14:42:29 +00001005 if (fabs(min-max) >= MagickEpsilon)
cristya63e4a92011-09-09 00:47:59 +00001006 status&=LevelImage(image,min,max,gamma,exception);
cristy19eb6412010-04-23 14:42:29 +00001007 return(status != 0 ? MagickTrue : MagickFalse);
cristy3ed852e2009-09-05 21:47:34 +00001008 }
cristy3ed852e2009-09-05 21:47:34 +00001009 /*
cristyf2e11662009-10-14 01:24:43 +00001010 Auto-level each channel separately.
cristy3ed852e2009-09-05 21:47:34 +00001011 */
cristya63e4a92011-09-09 00:47:59 +00001012 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1013 {
1014 ChannelType
1015 channel_mask;
1016
1017 PixelTrait
1018 traits;
1019
1020 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
1021 if ((traits & UpdatePixelTrait) == 0)
1022 continue;
cristya13d0822011-09-19 00:19:19 +00001023 channel_mask=SetPixelChannelMask(image,(ChannelType) (1 << i));
cristya63e4a92011-09-09 00:47:59 +00001024 status&=GetImageRange(image,&min,&max,exception);
1025 min+=black;
1026 max-=white;
1027 if (fabs(min-max) >= MagickEpsilon)
1028 status&=LevelImage(image,min,max,gamma,exception);
1029 (void) SetPixelChannelMask(image,channel_mask);
1030 }
cristy19eb6412010-04-23 14:42:29 +00001031 return(status != 0 ? MagickTrue : MagickFalse);
cristy3ed852e2009-09-05 21:47:34 +00001032}
1033
cristyf2e11662009-10-14 01:24:43 +00001034/*
1035%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1036% %
1037% %
1038% %
1039% G e t N u m b e r C o l o r s %
1040% %
1041% %
1042% %
1043%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1044%
1045% GetNumberColors() returns the number of unique colors in an image.
1046%
1047% The format of the GetNumberColors method is:
1048%
cristybb503372010-05-27 20:51:26 +00001049% size_t GetNumberColors(const Image *image,FILE *file,
cristyf2e11662009-10-14 01:24:43 +00001050% ExceptionInfo *exception)
1051%
1052% A description of each parameter follows.
1053%
1054% o image: the image.
1055%
1056% o file: Write a histogram of the color distribution to this file handle.
1057%
1058% o exception: return any errors or warnings in this structure.
1059%
1060*/
1061
1062#if defined(__cplusplus) || defined(c_plusplus)
1063extern "C" {
1064#endif
1065
1066static int HistogramCompare(const void *x,const void *y)
1067{
cristy101ab702011-10-13 13:06:32 +00001068 const PixelInfo
cristyf2e11662009-10-14 01:24:43 +00001069 *color_1,
1070 *color_2;
1071
cristy101ab702011-10-13 13:06:32 +00001072 color_1=(const PixelInfo *) x;
1073 color_2=(const PixelInfo *) y;
cristy4c08aed2011-07-01 19:47:50 +00001074 if (color_2->red != color_1->red)
1075 return((int) color_1->red-(int) color_2->red);
1076 if (color_2->green != color_1->green)
1077 return((int) color_1->green-(int) color_2->green);
1078 if (color_2->blue != color_1->blue)
1079 return((int) color_1->blue-(int) color_2->blue);
cristyf2e11662009-10-14 01:24:43 +00001080 return((int) color_2->count-(int) color_1->count);
1081}
1082
1083#if defined(__cplusplus) || defined(c_plusplus)
1084}
1085#endif
1086
cristybb503372010-05-27 20:51:26 +00001087MagickExport size_t GetNumberColors(const Image *image,FILE *file,
cristyf2e11662009-10-14 01:24:43 +00001088 ExceptionInfo *exception)
1089{
1090#define HistogramImageTag "Histogram/Image"
1091
1092 char
1093 color[MaxTextExtent],
1094 hex[MaxTextExtent],
1095 tuple[MaxTextExtent];
1096
cristy101ab702011-10-13 13:06:32 +00001097 PixelInfo
cristyf2e11662009-10-14 01:24:43 +00001098 *histogram;
1099
cristy8b27a6d2010-02-14 03:31:15 +00001100 MagickBooleanType
1101 status;
1102
cristy4c08aed2011-07-01 19:47:50 +00001103 PixelInfo
cristyf2e11662009-10-14 01:24:43 +00001104 pixel;
1105
cristy101ab702011-10-13 13:06:32 +00001106 register PixelInfo
cristyf2e11662009-10-14 01:24:43 +00001107 *p;
1108
cristybb503372010-05-27 20:51:26 +00001109 register ssize_t
cristyf2e11662009-10-14 01:24:43 +00001110 i;
1111
cristybb503372010-05-27 20:51:26 +00001112 size_t
cristyf2e11662009-10-14 01:24:43 +00001113 number_colors;
1114
1115 number_colors=0;
1116 if (file == (FILE *) NULL)
1117 {
1118 CubeInfo
1119 *cube_info;
1120
1121 cube_info=ClassifyImageColors(image,exception);
1122 if (cube_info != (CubeInfo *) NULL)
1123 number_colors=cube_info->colors;
1124 cube_info=DestroyCubeInfo(image,cube_info);
1125 return(number_colors);
1126 }
1127 histogram=GetImageHistogram(image,&number_colors,exception);
cristy101ab702011-10-13 13:06:32 +00001128 if (histogram == (PixelInfo *) NULL)
cristyf2e11662009-10-14 01:24:43 +00001129 return(number_colors);
1130 qsort((void *) histogram,(size_t) number_colors,sizeof(*histogram),
1131 HistogramCompare);
cristy4c08aed2011-07-01 19:47:50 +00001132 GetPixelInfo(image,&pixel);
cristyf2e11662009-10-14 01:24:43 +00001133 p=histogram;
cristyda16f162011-02-19 23:52:17 +00001134 status=MagickTrue;
cristybb503372010-05-27 20:51:26 +00001135 for (i=0; i < (ssize_t) number_colors; i++)
cristyf2e11662009-10-14 01:24:43 +00001136 {
cristy4c08aed2011-07-01 19:47:50 +00001137 SetPixelInfoPacket(image,p,&pixel);
cristyf2e11662009-10-14 01:24:43 +00001138 (void) CopyMagickString(tuple,"(",MaxTextExtent);
cristyed231572011-07-14 02:18:59 +00001139 ConcatenateColorComponent(&pixel,RedPixelChannel,X11Compliance,tuple);
cristyf2e11662009-10-14 01:24:43 +00001140 (void) ConcatenateMagickString(tuple,",",MaxTextExtent);
cristyed231572011-07-14 02:18:59 +00001141 ConcatenateColorComponent(&pixel,GreenPixelChannel,X11Compliance,tuple);
cristyf2e11662009-10-14 01:24:43 +00001142 (void) ConcatenateMagickString(tuple,",",MaxTextExtent);
cristyed231572011-07-14 02:18:59 +00001143 ConcatenateColorComponent(&pixel,BluePixelChannel,X11Compliance,tuple);
cristyf2e11662009-10-14 01:24:43 +00001144 if (pixel.colorspace == CMYKColorspace)
1145 {
1146 (void) ConcatenateMagickString(tuple,",",MaxTextExtent);
cristyed231572011-07-14 02:18:59 +00001147 ConcatenateColorComponent(&pixel,BlackPixelChannel,X11Compliance,
cristyfa806a72011-07-04 02:06:13 +00001148 tuple);
cristyf2e11662009-10-14 01:24:43 +00001149 }
1150 if (pixel.matte != MagickFalse)
1151 {
1152 (void) ConcatenateMagickString(tuple,",",MaxTextExtent);
cristyed231572011-07-14 02:18:59 +00001153 ConcatenateColorComponent(&pixel,AlphaPixelChannel,X11Compliance,
cristyfa806a72011-07-04 02:06:13 +00001154 tuple);
cristyf2e11662009-10-14 01:24:43 +00001155 }
1156 (void) ConcatenateMagickString(tuple,")",MaxTextExtent);
cristy269c9412011-10-13 23:41:15 +00001157 (void) QueryColorname(image,&pixel,SVGCompliance,color,exception);
cristyf2e11662009-10-14 01:24:43 +00001158 GetColorTuple(&pixel,MagickTrue,hex);
cristyb51dff52011-05-19 16:55:47 +00001159 (void) FormatLocaleFile(file,"%10" MagickSizeFormat,p->count);
1160 (void) FormatLocaleFile(file,": %s %s %s\n",tuple,hex,color);
cristy8b27a6d2010-02-14 03:31:15 +00001161 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1162 {
1163 MagickBooleanType
1164 proceed;
1165
cristycee97112010-05-28 00:44:52 +00001166 proceed=SetImageProgress(image,HistogramImageTag,(MagickOffsetType) i,
1167 number_colors);
cristy8b27a6d2010-02-14 03:31:15 +00001168 if (proceed == MagickFalse)
1169 status=MagickFalse;
1170 }
cristyf2e11662009-10-14 01:24:43 +00001171 p++;
1172 }
1173 (void) fflush(file);
cristy101ab702011-10-13 13:06:32 +00001174 histogram=(PixelInfo *) RelinquishMagickMemory(histogram);
cristyda16f162011-02-19 23:52:17 +00001175 if (status == MagickFalse)
1176 return(0);
cristyf2e11662009-10-14 01:24:43 +00001177 return(number_colors);
1178}
1179
1180/*
1181%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1182% %
1183% %
1184% %
1185% U n i q u e I m a g e C o l o r s %
1186% %
1187% %
1188% %
1189%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1190%
1191% UniqueImageColors() returns the unique colors of an image.
1192%
1193% The format of the UniqueImageColors method is:
1194%
1195% Image *UniqueImageColors(const Image *image,ExceptionInfo *exception)
1196%
1197% A description of each parameter follows.
1198%
1199% o image: the image.
1200%
1201% o exception: return any errors or warnings in this structure.
1202%
1203*/
1204
cristyc5c6f662010-09-22 14:23:02 +00001205static void UniqueColorsToImage(Image *unique_image,CacheView *unique_view,
1206 CubeInfo *cube_info,const NodeInfo *node_info,ExceptionInfo *exception)
cristyf2e11662009-10-14 01:24:43 +00001207{
1208#define UniqueColorsImageTag "UniqueColors/Image"
1209
cristy8b27a6d2010-02-14 03:31:15 +00001210 MagickBooleanType
1211 status;
1212
cristybb503372010-05-27 20:51:26 +00001213 register ssize_t
cristyf2e11662009-10-14 01:24:43 +00001214 i;
1215
cristybb503372010-05-27 20:51:26 +00001216 size_t
cristyf2e11662009-10-14 01:24:43 +00001217 number_children;
1218
1219 /*
1220 Traverse any children.
1221 */
cristyc5c6f662010-09-22 14:23:02 +00001222 number_children=unique_image->matte == MagickFalse ? 8UL : 16UL;
cristybb503372010-05-27 20:51:26 +00001223 for (i=0; i < (ssize_t) number_children; i++)
cristyf2e11662009-10-14 01:24:43 +00001224 if (node_info->child[i] != (NodeInfo *) NULL)
cristyc5c6f662010-09-22 14:23:02 +00001225 UniqueColorsToImage(unique_image,unique_view,cube_info,
1226 node_info->child[i],exception);
cristyf2e11662009-10-14 01:24:43 +00001227 if (node_info->level == (MaxTreeDepth-1))
1228 {
cristy101ab702011-10-13 13:06:32 +00001229 register PixelInfo
cristyf2e11662009-10-14 01:24:43 +00001230 *p;
1231
cristy4c08aed2011-07-01 19:47:50 +00001232 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00001233 *restrict q;
cristyf2e11662009-10-14 01:24:43 +00001234
cristyda16f162011-02-19 23:52:17 +00001235 status=MagickTrue;
cristyf2e11662009-10-14 01:24:43 +00001236 p=node_info->list;
cristybb503372010-05-27 20:51:26 +00001237 for (i=0; i < (ssize_t) node_info->number_unique; i++)
cristyf2e11662009-10-14 01:24:43 +00001238 {
cristyc5c6f662010-09-22 14:23:02 +00001239 q=QueueCacheViewAuthenticPixels(unique_view,cube_info->x,0,1,1,
1240 exception);
cristyacd2ed22011-08-30 01:44:23 +00001241 if (q == (Quantum *) NULL)
cristyf2e11662009-10-14 01:24:43 +00001242 continue;
cristy4c08aed2011-07-01 19:47:50 +00001243 SetPixelRed(unique_image,p->red,q);
1244 SetPixelGreen(unique_image,p->green,q);
1245 SetPixelBlue(unique_image,p->blue,q);
1246 SetPixelAlpha(unique_image,p->alpha,q);
cristyc5c6f662010-09-22 14:23:02 +00001247 if (unique_image->colorspace == CMYKColorspace)
cristy4c08aed2011-07-01 19:47:50 +00001248 SetPixelBlack(unique_image,p->black,q);
cristyc5c6f662010-09-22 14:23:02 +00001249 if (SyncCacheViewAuthenticPixels(unique_view,exception) == MagickFalse)
cristyf2e11662009-10-14 01:24:43 +00001250 break;
1251 cube_info->x++;
1252 p++;
1253 }
cristyc5c6f662010-09-22 14:23:02 +00001254 if (unique_image->progress_monitor != (MagickProgressMonitor) NULL)
cristy8b27a6d2010-02-14 03:31:15 +00001255 {
1256 MagickBooleanType
1257 proceed;
1258
cristyc5c6f662010-09-22 14:23:02 +00001259 proceed=SetImageProgress(unique_image,UniqueColorsImageTag,
cristy8b27a6d2010-02-14 03:31:15 +00001260 cube_info->progress,cube_info->colors);
1261 if (proceed == MagickFalse)
1262 status=MagickFalse;
1263 }
cristyf2e11662009-10-14 01:24:43 +00001264 cube_info->progress++;
cristyda16f162011-02-19 23:52:17 +00001265 if (status == MagickFalse)
1266 return;
cristyf2e11662009-10-14 01:24:43 +00001267 }
1268}
1269
1270MagickExport Image *UniqueImageColors(const Image *image,
1271 ExceptionInfo *exception)
1272{
cristyc5c6f662010-09-22 14:23:02 +00001273 CacheView
1274 *unique_view;
1275
cristyf2e11662009-10-14 01:24:43 +00001276 CubeInfo
1277 *cube_info;
1278
1279 Image
1280 *unique_image;
1281
1282 cube_info=ClassifyImageColors(image,exception);
1283 if (cube_info == (CubeInfo *) NULL)
1284 return((Image *) NULL);
1285 unique_image=CloneImage(image,cube_info->colors,1,MagickTrue,exception);
1286 if (unique_image == (Image *) NULL)
1287 return(unique_image);
cristy574cc262011-08-05 01:23:58 +00001288 if (SetImageStorageClass(unique_image,DirectClass,exception) == MagickFalse)
cristyf2e11662009-10-14 01:24:43 +00001289 {
cristyf2e11662009-10-14 01:24:43 +00001290 unique_image=DestroyImage(unique_image);
1291 return((Image *) NULL);
1292 }
cristyc5c6f662010-09-22 14:23:02 +00001293 unique_view=AcquireCacheView(unique_image);
1294 UniqueColorsToImage(unique_image,unique_view,cube_info,cube_info->root,
1295 exception);
1296 unique_view=DestroyCacheView(unique_view);
cristyf2e11662009-10-14 01:24:43 +00001297 if (cube_info->colors < MaxColormapSize)
1298 {
1299 QuantizeInfo
1300 *quantize_info;
1301
1302 quantize_info=AcquireQuantizeInfo((ImageInfo *) NULL);
1303 quantize_info->number_colors=MaxColormapSize;
1304 quantize_info->dither=MagickFalse;
1305 quantize_info->tree_depth=8;
cristy018f07f2011-09-04 21:15:19 +00001306 (void) QuantizeImage(quantize_info,unique_image,exception);
cristyf2e11662009-10-14 01:24:43 +00001307 quantize_info=DestroyQuantizeInfo(quantize_info);
1308 }
1309 cube_info=DestroyCubeInfo(image,cube_info);
1310 return(unique_image);
1311}