blob: 76d82e5ea665b6d089e3a00b8f182ad7ab6e0513 [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% %
cristy1454be72011-12-19 01:52:48 +000021% Copyright 1999-2012 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(),
anthonye5b39652012-04-21 05:37:29 +0000222 ResourceLimitError,"MemoryAllocationFailed","'%s'",image->filename);
cristyf2e11662009-10-14 01:24:43 +0000223 return(cube_info);
224 }
cristy4c08aed2011-07-01 19:47:50 +0000225 GetPixelInfo(image,&pixel);
226 GetPixelInfo(image,&target);
cristydb070952012-04-20 14:33:00 +0000227 image_view=AcquireVirtualCacheView(image,exception);
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 {
cristy803640d2011-11-17 02:11:32 +0000242 GetPixelInfoPixel(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(),
anthonye5b39652012-04-21 05:37:29 +0000250 ResourceLimitError,"MemoryAllocationFailed","'%s'",
cristyf2e11662009-10-14 01:24:43 +0000251 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 {
cristy9d8c8ce2011-10-25 16:13:52 +0000260 target=node_info->list[i];
cristy4c08aed2011-07-01 19:47:50 +0000261 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(),
anthonye5b39652012-04-21 05:37:29 +0000277 ResourceLimitError,"MemoryAllocationFailed","'%s'",
cristyf2e11662009-10-14 01:24:43 +0000278 image->filename);
279 return(0);
280 }
cristy39f8b972012-01-02 18:23:24 +0000281 node_info->list[i]=pixel;
cristye42f6582012-02-11 17:59:50 +0000282 node_info->list[i].red=(double) GetPixelRed(image,p);
283 node_info->list[i].green=(double) GetPixelGreen(image,p);
284 node_info->list[i].blue=(double) GetPixelBlue(image,p);
cristy4c08aed2011-07-01 19:47:50 +0000285 if (image->colorspace == CMYKColorspace)
cristye42f6582012-02-11 17:59:50 +0000286 node_info->list[i].black=(double) GetPixelBlack(image,p);
287 node_info->list[i].alpha=(double) GetPixelAlpha(image,p);
cristyf2e11662009-10-14 01:24:43 +0000288 node_info->list[i].count=1;
289 node_info->number_unique++;
290 cube_info->colors++;
291 }
cristyed231572011-07-14 02:18:59 +0000292 p+=GetPixelChannels(image);
cristyf2e11662009-10-14 01:24:43 +0000293 }
cristycee97112010-05-28 00:44:52 +0000294 proceed=SetImageProgress(image,EvaluateImageTag,(MagickOffsetType) y,
295 image->rows);
cristyf2e11662009-10-14 01:24:43 +0000296 if (proceed == MagickFalse)
297 break;
298 }
299 image_view=DestroyCacheView(image_view);
300 return(cube_info);
301}
302
303/*
304%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
305% %
306% %
307% %
308+ D e f i n e I m a g e H i s t o g r a m %
309% %
310% %
311% %
312%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
313%
314% DefineImageHistogram() traverses the color cube tree and notes each colormap
315% entry. A colormap entry is any node in the color cube tree where the
316% of unique colors is not zero.
317%
318% The format of the DefineImageHistogram method is:
319%
320% DefineImageHistogram(const Image *image,NodeInfo *node_info,
cristy101ab702011-10-13 13:06:32 +0000321% PixelInfo **unique_colors)
cristyf2e11662009-10-14 01:24:43 +0000322%
323% A description of each parameter follows.
324%
325% o image: the image.
326%
327% o node_info: the address of a structure of type NodeInfo which points to a
328% node in the color cube tree that is to be pruned.
329%
330% o histogram: the image histogram.
331%
332*/
333static void DefineImageHistogram(const Image *image,NodeInfo *node_info,
cristy101ab702011-10-13 13:06:32 +0000334 PixelInfo **histogram)
cristyf2e11662009-10-14 01:24:43 +0000335{
cristybb503372010-05-27 20:51:26 +0000336 register ssize_t
cristyf2e11662009-10-14 01:24:43 +0000337 i;
338
cristybb503372010-05-27 20:51:26 +0000339 size_t
cristyf2e11662009-10-14 01:24:43 +0000340 number_children;
341
342 /*
343 Traverse any children.
344 */
345 number_children=image->matte == MagickFalse ? 8UL : 16UL;
cristybb503372010-05-27 20:51:26 +0000346 for (i=0; i < (ssize_t) number_children; i++)
cristyf2e11662009-10-14 01:24:43 +0000347 if (node_info->child[i] != (NodeInfo *) NULL)
348 DefineImageHistogram(image,node_info->child[i],histogram);
349 if (node_info->level == (MaxTreeDepth-1))
350 {
cristy101ab702011-10-13 13:06:32 +0000351 register PixelInfo
cristyf2e11662009-10-14 01:24:43 +0000352 *p;
353
354 p=node_info->list;
cristybb503372010-05-27 20:51:26 +0000355 for (i=0; i < (ssize_t) node_info->number_unique; i++)
cristyf2e11662009-10-14 01:24:43 +0000356 {
cristy39f8b972012-01-02 18:23:24 +0000357 **histogram=(*p);
cristyf2e11662009-10-14 01:24:43 +0000358 (*histogram)++;
359 p++;
360 }
361 }
362}
363
364/*
365%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
366% %
367% %
368% %
369+ D e s t r o y C u b e I n f o %
370% %
371% %
372% %
373%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
374%
375% DestroyCubeInfo() deallocates memory associated with a CubeInfo structure.
376%
377% The format of the DestroyCubeInfo method is:
378%
379% DestroyCubeInfo(const Image *image,CubeInfo *cube_info)
380%
381% A description of each parameter follows:
382%
383% o image: the image.
384%
385% o cube_info: the address of a structure of type CubeInfo.
386%
387*/
388static CubeInfo *DestroyCubeInfo(const Image *image,CubeInfo *cube_info)
389{
390 register Nodes
391 *nodes;
392
393 /*
394 Release color cube tree storage.
395 */
396 DestroyColorCube(image,cube_info->root);
397 do
398 {
399 nodes=cube_info->node_queue->next;
400 cube_info->node_queue=(Nodes *)
401 RelinquishMagickMemory(cube_info->node_queue);
402 cube_info->node_queue=nodes;
403 } while (cube_info->node_queue != (Nodes *) NULL);
404 return((CubeInfo *) RelinquishMagickMemory(cube_info));
405}
406
407/*
408%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
409% %
410% %
411% %
412+ D e s t r o y C o l o r C u b e %
413% %
414% %
415% %
416%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
417%
418% DestroyColorCube() traverses the color cube tree and frees the list of
419% unique colors.
420%
421% The format of the DestroyColorCube method is:
422%
423% void DestroyColorCube(const Image *image,const NodeInfo *node_info)
424%
425% A description of each parameter follows.
426%
427% o image: the image.
428%
429% o node_info: the address of a structure of type NodeInfo which points to a
430% node in the color cube tree that is to be pruned.
431%
432*/
433static void DestroyColorCube(const Image *image,NodeInfo *node_info)
434{
cristybb503372010-05-27 20:51:26 +0000435 register ssize_t
cristyf2e11662009-10-14 01:24:43 +0000436 i;
437
cristybb503372010-05-27 20:51:26 +0000438 size_t
cristyf2e11662009-10-14 01:24:43 +0000439 number_children;
440
441 /*
442 Traverse any children.
443 */
444 number_children=image->matte == MagickFalse ? 8UL : 16UL;
cristybb503372010-05-27 20:51:26 +0000445 for (i=0; i < (ssize_t) number_children; i++)
cristyf2e11662009-10-14 01:24:43 +0000446 if (node_info->child[i] != (NodeInfo *) NULL)
447 DestroyColorCube(image,node_info->child[i]);
cristy101ab702011-10-13 13:06:32 +0000448 if (node_info->list != (PixelInfo *) NULL)
449 node_info->list=(PixelInfo *) RelinquishMagickMemory(node_info->list);
cristyf2e11662009-10-14 01:24:43 +0000450}
451
452/*
453%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
454% %
455% %
456% %
457+ G e t C u b e I n f o %
458% %
459% %
460% %
461%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
462%
463% GetCubeInfo() initializes the CubeInfo data structure.
464%
465% The format of the GetCubeInfo method is:
466%
467% cube_info=GetCubeInfo()
468%
469% A description of each parameter follows.
470%
471% o cube_info: A pointer to the Cube structure.
472%
473*/
474static CubeInfo *GetCubeInfo(void)
475{
476 CubeInfo
477 *cube_info;
478
479 /*
480 Initialize tree to describe color cube.
481 */
cristy73bd4a52010-10-05 11:24:23 +0000482 cube_info=(CubeInfo *) AcquireMagickMemory(sizeof(*cube_info));
cristyf2e11662009-10-14 01:24:43 +0000483 if (cube_info == (CubeInfo *) NULL)
484 return((CubeInfo *) NULL);
485 (void) ResetMagickMemory(cube_info,0,sizeof(*cube_info));
486 /*
487 Initialize root node.
488 */
489 cube_info->root=GetNodeInfo(cube_info,0);
490 if (cube_info->root == (NodeInfo *) NULL)
491 return((CubeInfo *) NULL);
492 return(cube_info);
493}
494
495/*
496%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
497% %
498% %
499% %
500% G e t I m a g e H i s t o g r a m %
501% %
502% %
503% %
504%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
505%
506% GetImageHistogram() returns the unique colors in an image.
507%
508% The format of the GetImageHistogram method is:
509%
cristybb503372010-05-27 20:51:26 +0000510% size_t GetImageHistogram(const Image *image,
511% size_t *number_colors,ExceptionInfo *exception)
cristyf2e11662009-10-14 01:24:43 +0000512%
513% A description of each parameter follows.
514%
515% o image: the image.
516%
517% o file: Write a histogram of the color distribution to this file handle.
518%
519% o exception: return any errors or warnings in this structure.
520%
521*/
cristy101ab702011-10-13 13:06:32 +0000522MagickExport PixelInfo *GetImageHistogram(const Image *image,
cristybb503372010-05-27 20:51:26 +0000523 size_t *number_colors,ExceptionInfo *exception)
cristyf2e11662009-10-14 01:24:43 +0000524{
cristy101ab702011-10-13 13:06:32 +0000525 PixelInfo
cristyf2e11662009-10-14 01:24:43 +0000526 *histogram;
527
528 CubeInfo
529 *cube_info;
530
531 *number_colors=0;
cristy101ab702011-10-13 13:06:32 +0000532 histogram=(PixelInfo *) NULL;
cristyf2e11662009-10-14 01:24:43 +0000533 cube_info=ClassifyImageColors(image,exception);
534 if (cube_info != (CubeInfo *) NULL)
535 {
cristy101ab702011-10-13 13:06:32 +0000536 histogram=(PixelInfo *) AcquireQuantumMemory((size_t) cube_info->colors,
cristyf2e11662009-10-14 01:24:43 +0000537 sizeof(*histogram));
cristy101ab702011-10-13 13:06:32 +0000538 if (histogram == (PixelInfo *) NULL)
cristyf2e11662009-10-14 01:24:43 +0000539 (void) ThrowMagickException(exception,GetMagickModule(),
anthonye5b39652012-04-21 05:37:29 +0000540 ResourceLimitError,"MemoryAllocationFailed","'%s'",image->filename);
cristyf2e11662009-10-14 01:24:43 +0000541 else
542 {
cristy101ab702011-10-13 13:06:32 +0000543 PixelInfo
cristyf2e11662009-10-14 01:24:43 +0000544 *root;
545
546 *number_colors=cube_info->colors;
547 root=histogram;
548 DefineImageHistogram(image,cube_info->root,&root);
549 }
550 }
551 cube_info=DestroyCubeInfo(image,cube_info);
552 return(histogram);
553}
554
555/*
556%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
557% %
558% %
559% %
560+ G e t N o d e I n f o %
561% %
562% %
563% %
564%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
565%
566% GetNodeInfo() allocates memory for a new node in the color cube tree and
567% presets all fields to zero.
568%
569% The format of the GetNodeInfo method is:
570%
cristybb503372010-05-27 20:51:26 +0000571% NodeInfo *GetNodeInfo(CubeInfo *cube_info,const size_t level)
cristyf2e11662009-10-14 01:24:43 +0000572%
573% A description of each parameter follows.
574%
575% o cube_info: A pointer to the CubeInfo structure.
576%
577% o level: Specifies the level in the storage_class the node resides.
578%
579*/
cristybb503372010-05-27 20:51:26 +0000580static NodeInfo *GetNodeInfo(CubeInfo *cube_info,const size_t level)
cristyf2e11662009-10-14 01:24:43 +0000581{
582 NodeInfo
583 *node_info;
584
585 if (cube_info->free_nodes == 0)
586 {
587 Nodes
588 *nodes;
589
590 /*
591 Allocate a new nodes of nodes.
592 */
cristy73bd4a52010-10-05 11:24:23 +0000593 nodes=(Nodes *) AcquireMagickMemory(sizeof(*nodes));
cristyf2e11662009-10-14 01:24:43 +0000594 if (nodes == (Nodes *) NULL)
595 return((NodeInfo *) NULL);
596 nodes->next=cube_info->node_queue;
597 cube_info->node_queue=nodes;
598 cube_info->node_info=nodes->nodes;
599 cube_info->free_nodes=NodesInAList;
600 }
601 cube_info->free_nodes--;
602 node_info=cube_info->node_info++;
603 (void) ResetMagickMemory(node_info,0,sizeof(*node_info));
604 node_info->level=level;
605 return(node_info);
606}
607
608/*
609%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
610% %
611% %
612% %
613% I s H i s t o g r a m I m a g e %
614% %
615% %
616% %
617%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
618%
619% IsHistogramImage() returns MagickTrue if the image has 1024 unique colors or
620% less.
621%
622% The format of the IsHistogramImage method is:
623%
624% MagickBooleanType IsHistogramImage(const Image *image,
625% ExceptionInfo *exception)
626%
627% A description of each parameter follows.
628%
629% o image: the image.
630%
631% o exception: return any errors or warnings in this structure.
632%
633*/
634MagickExport MagickBooleanType IsHistogramImage(const Image *image,
635 ExceptionInfo *exception)
636{
637#define MaximumUniqueColors 1024
638
cristyc4c8d132010-01-07 01:58:38 +0000639 CacheView
640 *image_view;
641
cristyf2e11662009-10-14 01:24:43 +0000642 CubeInfo
643 *cube_info;
644
cristy4c08aed2011-07-01 19:47:50 +0000645 PixelInfo
cristyf2e11662009-10-14 01:24:43 +0000646 pixel,
647 target;
648
cristy4c08aed2011-07-01 19:47:50 +0000649 register const Quantum
cristyf2e11662009-10-14 01:24:43 +0000650 *p;
651
cristybb503372010-05-27 20:51:26 +0000652 register ssize_t
cristyf2e11662009-10-14 01:24:43 +0000653 x;
654
655 register NodeInfo
656 *node_info;
657
cristybb503372010-05-27 20:51:26 +0000658 register ssize_t
cristyf2e11662009-10-14 01:24:43 +0000659 i;
660
cristybb503372010-05-27 20:51:26 +0000661 size_t
cristyf2e11662009-10-14 01:24:43 +0000662 id,
663 index,
664 level;
665
cristy9d314ff2011-03-09 01:30:28 +0000666 ssize_t
667 y;
668
cristyf2e11662009-10-14 01:24:43 +0000669 assert(image != (Image *) NULL);
670 assert(image->signature == MagickSignature);
671 if (image->debug != MagickFalse)
672 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
673 if ((image->storage_class == PseudoClass) && (image->colors <= 256))
674 return(MagickTrue);
675 if (image->storage_class == PseudoClass)
676 return(MagickFalse);
677 /*
678 Initialize color description tree.
679 */
680 cube_info=GetCubeInfo();
681 if (cube_info == (CubeInfo *) NULL)
682 {
683 (void) ThrowMagickException(exception,GetMagickModule(),
anthonye5b39652012-04-21 05:37:29 +0000684 ResourceLimitError,"MemoryAllocationFailed","'%s'",image->filename);
cristyf2e11662009-10-14 01:24:43 +0000685 return(MagickFalse);
686 }
cristy4c08aed2011-07-01 19:47:50 +0000687 GetPixelInfo(image,&pixel);
688 GetPixelInfo(image,&target);
cristydb070952012-04-20 14:33:00 +0000689 image_view=AcquireVirtualCacheView(image,exception);
cristybb503372010-05-27 20:51:26 +0000690 for (y=0; y < (ssize_t) image->rows; y++)
cristyf2e11662009-10-14 01:24:43 +0000691 {
692 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +0000693 if (p == (const Quantum *) NULL)
cristyf2e11662009-10-14 01:24:43 +0000694 break;
cristybb503372010-05-27 20:51:26 +0000695 for (x=0; x < (ssize_t) image->columns; x++)
cristyf2e11662009-10-14 01:24:43 +0000696 {
697 /*
698 Start at the root and proceed level by level.
699 */
700 node_info=cube_info->root;
701 index=MaxTreeDepth-1;
702 for (level=1; level < MaxTreeDepth; level++)
703 {
cristy803640d2011-11-17 02:11:32 +0000704 GetPixelInfoPixel(image,p,&pixel);
cristyf2e11662009-10-14 01:24:43 +0000705 id=ColorToNodeId(image,&pixel,index);
706 if (node_info->child[id] == (NodeInfo *) NULL)
707 {
708 node_info->child[id]=GetNodeInfo(cube_info,level);
709 if (node_info->child[id] == (NodeInfo *) NULL)
710 {
711 (void) ThrowMagickException(exception,GetMagickModule(),
anthonye5b39652012-04-21 05:37:29 +0000712 ResourceLimitError,"MemoryAllocationFailed","'%s'",
cristyf2e11662009-10-14 01:24:43 +0000713 image->filename);
714 break;
715 }
716 }
717 node_info=node_info->child[id];
718 index--;
719 }
720 if (level < MaxTreeDepth)
721 break;
cristybb503372010-05-27 20:51:26 +0000722 for (i=0; i < (ssize_t) node_info->number_unique; i++)
cristyf2e11662009-10-14 01:24:43 +0000723 {
cristy9d8c8ce2011-10-25 16:13:52 +0000724 target=node_info->list[i];
cristy4c08aed2011-07-01 19:47:50 +0000725 if (IsPixelInfoEquivalent(&pixel,&target) != MagickFalse)
cristyf2e11662009-10-14 01:24:43 +0000726 break;
727 }
cristybb503372010-05-27 20:51:26 +0000728 if (i < (ssize_t) node_info->number_unique)
cristyf2e11662009-10-14 01:24:43 +0000729 node_info->list[i].count++;
730 else
731 {
732 /*
733 Add this unique color to the color list.
734 */
735 if (node_info->number_unique == 0)
cristy101ab702011-10-13 13:06:32 +0000736 node_info->list=(PixelInfo *) AcquireMagickMemory(
cristyf2e11662009-10-14 01:24:43 +0000737 sizeof(*node_info->list));
738 else
cristy101ab702011-10-13 13:06:32 +0000739 node_info->list=(PixelInfo *) ResizeQuantumMemory(node_info->list,
cristyf2e11662009-10-14 01:24:43 +0000740 (size_t) (i+1),sizeof(*node_info->list));
cristy101ab702011-10-13 13:06:32 +0000741 if (node_info->list == (PixelInfo *) NULL)
cristyf2e11662009-10-14 01:24:43 +0000742 {
743 (void) ThrowMagickException(exception,GetMagickModule(),
anthonye5b39652012-04-21 05:37:29 +0000744 ResourceLimitError,"MemoryAllocationFailed","'%s'",
cristyf2e11662009-10-14 01:24:43 +0000745 image->filename);
746 break;
747 }
cristye42f6582012-02-11 17:59:50 +0000748 node_info->list[i].red=(double) GetPixelRed(image,p);
749 node_info->list[i].green=(double) GetPixelGreen(image,p);
750 node_info->list[i].blue=(double) GetPixelBlue(image,p);
cristy4c08aed2011-07-01 19:47:50 +0000751 if (image->colorspace == CMYKColorspace)
cristye42f6582012-02-11 17:59:50 +0000752 node_info->list[i].black=(double) GetPixelBlack(image,p);
753 node_info->list[i].alpha=(double) GetPixelAlpha(image,p);
cristyf2e11662009-10-14 01:24:43 +0000754 node_info->list[i].count=1;
755 node_info->number_unique++;
756 cube_info->colors++;
757 if (cube_info->colors > MaximumUniqueColors)
758 break;
759 }
cristyed231572011-07-14 02:18:59 +0000760 p+=GetPixelChannels(image);
cristyf2e11662009-10-14 01:24:43 +0000761 }
cristybb503372010-05-27 20:51:26 +0000762 if (x < (ssize_t) image->columns)
cristyf2e11662009-10-14 01:24:43 +0000763 break;
764 }
765 image_view=DestroyCacheView(image_view);
766 cube_info=DestroyCubeInfo(image,cube_info);
cristybb503372010-05-27 20:51:26 +0000767 return(y < (ssize_t) image->rows ? MagickFalse : MagickTrue);
cristyf2e11662009-10-14 01:24:43 +0000768}
769
770/*
771%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
772% %
773% %
774% %
775% I s P a l e t t e I m a g e %
776% %
777% %
778% %
779%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
780%
781% IsPaletteImage() returns MagickTrue if the image is PseudoClass and has 256
782% unique colors or less.
783%
784% The format of the IsPaletteImage method is:
785%
786% MagickBooleanType IsPaletteImage(const Image *image,
787% ExceptionInfo *exception)
788%
789% A description of each parameter follows.
790%
791% o image: the image.
792%
793% o exception: return any errors or warnings in this structure.
794%
795*/
796MagickExport MagickBooleanType IsPaletteImage(const Image *image,
797 ExceptionInfo *exception)
798{
cristyc4c8d132010-01-07 01:58:38 +0000799 CacheView
800 *image_view;
801
cristyf2e11662009-10-14 01:24:43 +0000802 CubeInfo
803 *cube_info;
804
cristy4c08aed2011-07-01 19:47:50 +0000805 PixelInfo
cristyf2e11662009-10-14 01:24:43 +0000806 pixel,
807 target;
808
cristy4c08aed2011-07-01 19:47:50 +0000809 register const Quantum
cristyf2e11662009-10-14 01:24:43 +0000810 *p;
811
cristybb503372010-05-27 20:51:26 +0000812 register ssize_t
cristyf2e11662009-10-14 01:24:43 +0000813 x;
814
815 register NodeInfo
816 *node_info;
817
cristybb503372010-05-27 20:51:26 +0000818 register ssize_t
cristyf2e11662009-10-14 01:24:43 +0000819 i;
820
cristybb503372010-05-27 20:51:26 +0000821 size_t
cristyf2e11662009-10-14 01:24:43 +0000822 id,
823 index,
824 level;
825
cristy9d314ff2011-03-09 01:30:28 +0000826 ssize_t
827 y;
828
cristyf2e11662009-10-14 01:24:43 +0000829 assert(image != (Image *) NULL);
830 assert(image->signature == MagickSignature);
831 if (image->debug != MagickFalse)
832 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
833 if ((image->storage_class == PseudoClass) && (image->colors <= 256))
834 return(MagickTrue);
835 if (image->storage_class == PseudoClass)
836 return(MagickFalse);
837 /*
838 Initialize color description tree.
839 */
840 cube_info=GetCubeInfo();
841 if (cube_info == (CubeInfo *) NULL)
842 {
843 (void) ThrowMagickException(exception,GetMagickModule(),
anthonye5b39652012-04-21 05:37:29 +0000844 ResourceLimitError,"MemoryAllocationFailed","'%s'",image->filename);
cristyf2e11662009-10-14 01:24:43 +0000845 return(MagickFalse);
846 }
cristy4c08aed2011-07-01 19:47:50 +0000847 GetPixelInfo(image,&pixel);
848 GetPixelInfo(image,&target);
cristydb070952012-04-20 14:33:00 +0000849 image_view=AcquireVirtualCacheView(image,exception);
cristybb503372010-05-27 20:51:26 +0000850 for (y=0; y < (ssize_t) image->rows; y++)
cristyf2e11662009-10-14 01:24:43 +0000851 {
852 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +0000853 if (p == (const Quantum *) NULL)
cristyf2e11662009-10-14 01:24:43 +0000854 break;
cristybb503372010-05-27 20:51:26 +0000855 for (x=0; x < (ssize_t) image->columns; x++)
cristyf2e11662009-10-14 01:24:43 +0000856 {
857 /*
858 Start at the root and proceed level by level.
859 */
860 node_info=cube_info->root;
861 index=MaxTreeDepth-1;
862 for (level=1; level < MaxTreeDepth; level++)
863 {
cristy803640d2011-11-17 02:11:32 +0000864 GetPixelInfoPixel(image,p,&pixel);
cristyf2e11662009-10-14 01:24:43 +0000865 id=ColorToNodeId(image,&pixel,index);
866 if (node_info->child[id] == (NodeInfo *) NULL)
867 {
868 node_info->child[id]=GetNodeInfo(cube_info,level);
869 if (node_info->child[id] == (NodeInfo *) NULL)
870 {
871 (void) ThrowMagickException(exception,GetMagickModule(),
anthonye5b39652012-04-21 05:37:29 +0000872 ResourceLimitError,"MemoryAllocationFailed","'%s'",
cristyf2e11662009-10-14 01:24:43 +0000873 image->filename);
874 break;
875 }
876 }
877 node_info=node_info->child[id];
878 index--;
879 }
880 if (level < MaxTreeDepth)
881 break;
cristybb503372010-05-27 20:51:26 +0000882 for (i=0; i < (ssize_t) node_info->number_unique; i++)
cristyf2e11662009-10-14 01:24:43 +0000883 {
cristy9d8c8ce2011-10-25 16:13:52 +0000884 target=node_info->list[i];
cristy4c08aed2011-07-01 19:47:50 +0000885 if (IsPixelInfoEquivalent(&pixel,&target) != MagickFalse)
cristyf2e11662009-10-14 01:24:43 +0000886 break;
887 }
cristybb503372010-05-27 20:51:26 +0000888 if (i < (ssize_t) node_info->number_unique)
cristyf2e11662009-10-14 01:24:43 +0000889 node_info->list[i].count++;
890 else
891 {
892 /*
893 Add this unique color to the color list.
894 */
895 if (node_info->number_unique == 0)
cristy101ab702011-10-13 13:06:32 +0000896 node_info->list=(PixelInfo *) AcquireMagickMemory(
cristyf2e11662009-10-14 01:24:43 +0000897 sizeof(*node_info->list));
898 else
cristy101ab702011-10-13 13:06:32 +0000899 node_info->list=(PixelInfo *) ResizeQuantumMemory(node_info->list,
cristyf2e11662009-10-14 01:24:43 +0000900 (size_t) (i+1),sizeof(*node_info->list));
cristy101ab702011-10-13 13:06:32 +0000901 if (node_info->list == (PixelInfo *) NULL)
cristyf2e11662009-10-14 01:24:43 +0000902 {
903 (void) ThrowMagickException(exception,GetMagickModule(),
anthonye5b39652012-04-21 05:37:29 +0000904 ResourceLimitError,"MemoryAllocationFailed","'%s'",
cristyf2e11662009-10-14 01:24:43 +0000905 image->filename);
906 break;
907 }
cristy39f8b972012-01-02 18:23:24 +0000908 node_info->list[i]=pixel;
cristye42f6582012-02-11 17:59:50 +0000909 node_info->list[i].red=(double) GetPixelRed(image,p);
910 node_info->list[i].green=(double) GetPixelGreen(image,p);
911 node_info->list[i].blue=(double) GetPixelBlue(image,p);
cristy4c08aed2011-07-01 19:47:50 +0000912 if (image->colorspace == CMYKColorspace)
cristye42f6582012-02-11 17:59:50 +0000913 node_info->list[i].black=(double) GetPixelBlack(image,p);
914 node_info->list[i].alpha=(double) GetPixelAlpha(image,p);
cristyf2e11662009-10-14 01:24:43 +0000915 node_info->list[i].count=1;
916 node_info->number_unique++;
917 cube_info->colors++;
918 if (cube_info->colors > 256)
919 break;
920 }
cristyed231572011-07-14 02:18:59 +0000921 p+=GetPixelChannels(image);
cristyf2e11662009-10-14 01:24:43 +0000922 }
cristybb503372010-05-27 20:51:26 +0000923 if (x < (ssize_t) image->columns)
cristyf2e11662009-10-14 01:24:43 +0000924 break;
925 }
926 image_view=DestroyCacheView(image_view);
927 cube_info=DestroyCubeInfo(image,cube_info);
cristybb503372010-05-27 20:51:26 +0000928 return(y < (ssize_t) image->rows ? MagickFalse : MagickTrue);
cristyf2e11662009-10-14 01:24:43 +0000929}
930
931/*
cristy3ed852e2009-09-05 21:47:34 +0000932%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
933% %
934% %
935% %
936% M i n M a x S t r e t c h I m a g e %
937% %
938% %
939% %
940%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
941%
942% MinMaxStretchImage() uses the exact minimum and maximum values found in
943% each of the channels given, as the BlackPoint and WhitePoint to linearly
944% stretch the colors (and histogram) of the image. The stretch points are
945% also moved further inward by the adjustment values given.
946%
glennrpf0a92fd2011-04-27 13:17:21 +0000947% If the adjustment values are both zero this function is equivalent to a
cristy3ed852e2009-09-05 21:47:34 +0000948% perfect normalization (or autolevel) of the image.
949%
950% Each channel is stretched independantally of each other (producing color
951% distortion) unless the special 'SyncChannels' flag is also provided in the
952% channels setting. If this flag is present the minimum and maximum point
953% will be extracted from all the given channels, and those channels will be
954% stretched by exactly the same amount (preventing color distortion).
955%
anthony7fe39fc2010-04-06 03:19:20 +0000956% In the special case that only ONE value is found in a channel of the image
957% that value is not stretched, that value is left as is.
958%
cristy3ed852e2009-09-05 21:47:34 +0000959% The 'SyncChannels' is turned on in the 'DefaultChannels' setting by
960% default.
961%
962% The format of the MinMaxStretchImage method is:
963%
cristy490408a2011-07-07 14:42:05 +0000964% MagickBooleanType MinMaxStretchImage(Image *image,const double black,
cristya63e4a92011-09-09 00:47:59 +0000965% const double white,const double gamma,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000966%
967% A description of each parameter follows:
968%
969% o image: The image to auto-level
970%
cristy490408a2011-07-07 14:42:05 +0000971% o black, white: move the black / white point inward from the minimum and
972% maximum points by this color value.
cristy3ed852e2009-09-05 21:47:34 +0000973%
cristya63e4a92011-09-09 00:47:59 +0000974% o gamma: the gamma.
975%
cristy95111202011-08-09 19:41:42 +0000976% o exception: return any errors or warnings in this structure.
977%
cristy3ed852e2009-09-05 21:47:34 +0000978*/
cristy3ed852e2009-09-05 21:47:34 +0000979MagickExport MagickBooleanType MinMaxStretchImage(Image *image,
cristya63e4a92011-09-09 00:47:59 +0000980 const double black,const double white,const double gamma,
981 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000982{
983 double
cristy19eb6412010-04-23 14:42:29 +0000984 min,
985 max;
cristy3ed852e2009-09-05 21:47:34 +0000986
cristya63e4a92011-09-09 00:47:59 +0000987 register ssize_t
988 i;
989
cristy3ed852e2009-09-05 21:47:34 +0000990 MagickStatusType
991 status;
992
anthony7fe39fc2010-04-06 03:19:20 +0000993 status=MagickTrue;
cristy5f95f4f2011-10-23 01:01:01 +0000994 if (image->channel_mask == DefaultChannels)
cristy3ed852e2009-09-05 21:47:34 +0000995 {
996 /*
cristyf2e11662009-10-14 01:24:43 +0000997 Auto-level all channels equally.
cristy3ed852e2009-09-05 21:47:34 +0000998 */
cristy95111202011-08-09 19:41:42 +0000999 (void) GetImageRange(image,&min,&max,exception);
cristy490408a2011-07-07 14:42:05 +00001000 min+=black;
1001 max-=white;
cristy19eb6412010-04-23 14:42:29 +00001002 if (fabs(min-max) >= MagickEpsilon)
cristya63e4a92011-09-09 00:47:59 +00001003 status&=LevelImage(image,min,max,gamma,exception);
cristy19eb6412010-04-23 14:42:29 +00001004 return(status != 0 ? MagickTrue : MagickFalse);
cristy3ed852e2009-09-05 21:47:34 +00001005 }
cristy3ed852e2009-09-05 21:47:34 +00001006 /*
cristyf2e11662009-10-14 01:24:43 +00001007 Auto-level each channel separately.
cristy3ed852e2009-09-05 21:47:34 +00001008 */
cristya63e4a92011-09-09 00:47:59 +00001009 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1010 {
1011 ChannelType
1012 channel_mask;
1013
cristyabace412011-12-11 15:56:53 +00001014 PixelChannel
1015 channel;
1016
cristya63e4a92011-09-09 00:47:59 +00001017 PixelTrait
1018 traits;
1019
cristyabace412011-12-11 15:56:53 +00001020 channel=GetPixelChannelMapChannel(image,i);
1021 traits=GetPixelChannelMapTraits(image,channel);
cristya63e4a92011-09-09 00:47:59 +00001022 if ((traits & UpdatePixelTrait) == 0)
1023 continue;
cristya13d0822011-09-19 00:19:19 +00001024 channel_mask=SetPixelChannelMask(image,(ChannelType) (1 << i));
cristya63e4a92011-09-09 00:47:59 +00001025 status&=GetImageRange(image,&min,&max,exception);
1026 min+=black;
1027 max-=white;
1028 if (fabs(min-max) >= MagickEpsilon)
1029 status&=LevelImage(image,min,max,gamma,exception);
1030 (void) SetPixelChannelMask(image,channel_mask);
1031 }
cristy19eb6412010-04-23 14:42:29 +00001032 return(status != 0 ? MagickTrue : MagickFalse);
cristy3ed852e2009-09-05 21:47:34 +00001033}
1034
cristyf2e11662009-10-14 01:24:43 +00001035/*
1036%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1037% %
1038% %
1039% %
1040% G e t N u m b e r C o l o r s %
1041% %
1042% %
1043% %
1044%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1045%
1046% GetNumberColors() returns the number of unique colors in an image.
1047%
1048% The format of the GetNumberColors method is:
1049%
cristybb503372010-05-27 20:51:26 +00001050% size_t GetNumberColors(const Image *image,FILE *file,
cristyf2e11662009-10-14 01:24:43 +00001051% ExceptionInfo *exception)
1052%
1053% A description of each parameter follows.
1054%
1055% o image: the image.
1056%
1057% o file: Write a histogram of the color distribution to this file handle.
1058%
1059% o exception: return any errors or warnings in this structure.
1060%
1061*/
1062
1063#if defined(__cplusplus) || defined(c_plusplus)
1064extern "C" {
1065#endif
1066
1067static int HistogramCompare(const void *x,const void *y)
1068{
cristy101ab702011-10-13 13:06:32 +00001069 const PixelInfo
cristyf2e11662009-10-14 01:24:43 +00001070 *color_1,
1071 *color_2;
1072
cristy101ab702011-10-13 13:06:32 +00001073 color_1=(const PixelInfo *) x;
1074 color_2=(const PixelInfo *) y;
cristy4c08aed2011-07-01 19:47:50 +00001075 if (color_2->red != color_1->red)
1076 return((int) color_1->red-(int) color_2->red);
1077 if (color_2->green != color_1->green)
1078 return((int) color_1->green-(int) color_2->green);
1079 if (color_2->blue != color_1->blue)
1080 return((int) color_1->blue-(int) color_2->blue);
cristyf2e11662009-10-14 01:24:43 +00001081 return((int) color_2->count-(int) color_1->count);
1082}
1083
1084#if defined(__cplusplus) || defined(c_plusplus)
1085}
1086#endif
1087
cristybb503372010-05-27 20:51:26 +00001088MagickExport size_t GetNumberColors(const Image *image,FILE *file,
cristyf2e11662009-10-14 01:24:43 +00001089 ExceptionInfo *exception)
1090{
1091#define HistogramImageTag "Histogram/Image"
1092
1093 char
1094 color[MaxTextExtent],
1095 hex[MaxTextExtent],
1096 tuple[MaxTextExtent];
1097
cristy101ab702011-10-13 13:06:32 +00001098 PixelInfo
cristyf2e11662009-10-14 01:24:43 +00001099 *histogram;
1100
cristy8b27a6d2010-02-14 03:31:15 +00001101 MagickBooleanType
1102 status;
1103
cristy4c08aed2011-07-01 19:47:50 +00001104 PixelInfo
cristyf2e11662009-10-14 01:24:43 +00001105 pixel;
1106
cristy101ab702011-10-13 13:06:32 +00001107 register PixelInfo
cristyf2e11662009-10-14 01:24:43 +00001108 *p;
1109
cristybb503372010-05-27 20:51:26 +00001110 register ssize_t
cristyf2e11662009-10-14 01:24:43 +00001111 i;
1112
cristybb503372010-05-27 20:51:26 +00001113 size_t
cristyf2e11662009-10-14 01:24:43 +00001114 number_colors;
1115
1116 number_colors=0;
1117 if (file == (FILE *) NULL)
1118 {
1119 CubeInfo
1120 *cube_info;
1121
1122 cube_info=ClassifyImageColors(image,exception);
1123 if (cube_info != (CubeInfo *) NULL)
1124 number_colors=cube_info->colors;
1125 cube_info=DestroyCubeInfo(image,cube_info);
1126 return(number_colors);
1127 }
1128 histogram=GetImageHistogram(image,&number_colors,exception);
cristy101ab702011-10-13 13:06:32 +00001129 if (histogram == (PixelInfo *) NULL)
cristyf2e11662009-10-14 01:24:43 +00001130 return(number_colors);
1131 qsort((void *) histogram,(size_t) number_colors,sizeof(*histogram),
1132 HistogramCompare);
cristy4c08aed2011-07-01 19:47:50 +00001133 GetPixelInfo(image,&pixel);
cristyf2e11662009-10-14 01:24:43 +00001134 p=histogram;
cristyda16f162011-02-19 23:52:17 +00001135 status=MagickTrue;
cristybb503372010-05-27 20:51:26 +00001136 for (i=0; i < (ssize_t) number_colors; i++)
cristyf2e11662009-10-14 01:24:43 +00001137 {
cristy9d8c8ce2011-10-25 16:13:52 +00001138 pixel=(*p);
cristyf2e11662009-10-14 01:24:43 +00001139 (void) CopyMagickString(tuple,"(",MaxTextExtent);
cristyed231572011-07-14 02:18:59 +00001140 ConcatenateColorComponent(&pixel,RedPixelChannel,X11Compliance,tuple);
cristyf2e11662009-10-14 01:24:43 +00001141 (void) ConcatenateMagickString(tuple,",",MaxTextExtent);
cristyed231572011-07-14 02:18:59 +00001142 ConcatenateColorComponent(&pixel,GreenPixelChannel,X11Compliance,tuple);
cristyf2e11662009-10-14 01:24:43 +00001143 (void) ConcatenateMagickString(tuple,",",MaxTextExtent);
cristyed231572011-07-14 02:18:59 +00001144 ConcatenateColorComponent(&pixel,BluePixelChannel,X11Compliance,tuple);
cristyf2e11662009-10-14 01:24:43 +00001145 if (pixel.colorspace == CMYKColorspace)
1146 {
1147 (void) ConcatenateMagickString(tuple,",",MaxTextExtent);
cristyed231572011-07-14 02:18:59 +00001148 ConcatenateColorComponent(&pixel,BlackPixelChannel,X11Compliance,
cristyfa806a72011-07-04 02:06:13 +00001149 tuple);
cristyf2e11662009-10-14 01:24:43 +00001150 }
1151 if (pixel.matte != MagickFalse)
1152 {
1153 (void) ConcatenateMagickString(tuple,",",MaxTextExtent);
cristyed231572011-07-14 02:18:59 +00001154 ConcatenateColorComponent(&pixel,AlphaPixelChannel,X11Compliance,
cristyfa806a72011-07-04 02:06:13 +00001155 tuple);
cristyf2e11662009-10-14 01:24:43 +00001156 }
1157 (void) ConcatenateMagickString(tuple,")",MaxTextExtent);
cristy269c9412011-10-13 23:41:15 +00001158 (void) QueryColorname(image,&pixel,SVGCompliance,color,exception);
cristyf2e11662009-10-14 01:24:43 +00001159 GetColorTuple(&pixel,MagickTrue,hex);
cristybcb0f452012-02-12 19:26:56 +00001160 (void) FormatLocaleFile(file,"%10.20g",(double) ((MagickOffsetType)
1161 p->count));
cristyb51dff52011-05-19 16:55:47 +00001162 (void) FormatLocaleFile(file,": %s %s %s\n",tuple,hex,color);
cristy8b27a6d2010-02-14 03:31:15 +00001163 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1164 {
1165 MagickBooleanType
1166 proceed;
1167
cristycee97112010-05-28 00:44:52 +00001168 proceed=SetImageProgress(image,HistogramImageTag,(MagickOffsetType) i,
1169 number_colors);
cristy8b27a6d2010-02-14 03:31:15 +00001170 if (proceed == MagickFalse)
1171 status=MagickFalse;
1172 }
cristyf2e11662009-10-14 01:24:43 +00001173 p++;
1174 }
1175 (void) fflush(file);
cristy101ab702011-10-13 13:06:32 +00001176 histogram=(PixelInfo *) RelinquishMagickMemory(histogram);
cristyda16f162011-02-19 23:52:17 +00001177 if (status == MagickFalse)
1178 return(0);
cristyf2e11662009-10-14 01:24:43 +00001179 return(number_colors);
1180}
1181
1182/*
1183%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1184% %
1185% %
1186% %
1187% U n i q u e I m a g e C o l o r s %
1188% %
1189% %
1190% %
1191%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1192%
1193% UniqueImageColors() returns the unique colors of an image.
1194%
1195% The format of the UniqueImageColors method is:
1196%
1197% Image *UniqueImageColors(const Image *image,ExceptionInfo *exception)
1198%
1199% A description of each parameter follows.
1200%
1201% o image: the image.
1202%
1203% o exception: return any errors or warnings in this structure.
1204%
1205*/
1206
cristyc5c6f662010-09-22 14:23:02 +00001207static void UniqueColorsToImage(Image *unique_image,CacheView *unique_view,
1208 CubeInfo *cube_info,const NodeInfo *node_info,ExceptionInfo *exception)
cristyf2e11662009-10-14 01:24:43 +00001209{
1210#define UniqueColorsImageTag "UniqueColors/Image"
1211
cristy8b27a6d2010-02-14 03:31:15 +00001212 MagickBooleanType
1213 status;
1214
cristybb503372010-05-27 20:51:26 +00001215 register ssize_t
cristyf2e11662009-10-14 01:24:43 +00001216 i;
1217
cristybb503372010-05-27 20:51:26 +00001218 size_t
cristyf2e11662009-10-14 01:24:43 +00001219 number_children;
1220
1221 /*
1222 Traverse any children.
1223 */
cristyc5c6f662010-09-22 14:23:02 +00001224 number_children=unique_image->matte == MagickFalse ? 8UL : 16UL;
cristybb503372010-05-27 20:51:26 +00001225 for (i=0; i < (ssize_t) number_children; i++)
cristyf2e11662009-10-14 01:24:43 +00001226 if (node_info->child[i] != (NodeInfo *) NULL)
cristyc5c6f662010-09-22 14:23:02 +00001227 UniqueColorsToImage(unique_image,unique_view,cube_info,
1228 node_info->child[i],exception);
cristyf2e11662009-10-14 01:24:43 +00001229 if (node_info->level == (MaxTreeDepth-1))
1230 {
cristy101ab702011-10-13 13:06:32 +00001231 register PixelInfo
cristyf2e11662009-10-14 01:24:43 +00001232 *p;
1233
cristy4c08aed2011-07-01 19:47:50 +00001234 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00001235 *restrict q;
cristyf2e11662009-10-14 01:24:43 +00001236
cristyda16f162011-02-19 23:52:17 +00001237 status=MagickTrue;
cristyf2e11662009-10-14 01:24:43 +00001238 p=node_info->list;
cristybb503372010-05-27 20:51:26 +00001239 for (i=0; i < (ssize_t) node_info->number_unique; i++)
cristyf2e11662009-10-14 01:24:43 +00001240 {
cristyc5c6f662010-09-22 14:23:02 +00001241 q=QueueCacheViewAuthenticPixels(unique_view,cube_info->x,0,1,1,
1242 exception);
cristyacd2ed22011-08-30 01:44:23 +00001243 if (q == (Quantum *) NULL)
cristyf2e11662009-10-14 01:24:43 +00001244 continue;
cristye42f6582012-02-11 17:59:50 +00001245 SetPixelRed(unique_image,ClampToQuantum(p->red),q);
1246 SetPixelGreen(unique_image,ClampToQuantum(p->green),q);
1247 SetPixelBlue(unique_image,ClampToQuantum(p->blue),q);
1248 SetPixelAlpha(unique_image,ClampToQuantum(p->alpha),q);
cristyc5c6f662010-09-22 14:23:02 +00001249 if (unique_image->colorspace == CMYKColorspace)
cristye42f6582012-02-11 17:59:50 +00001250 SetPixelBlack(unique_image,ClampToQuantum(p->black),q);
cristyc5c6f662010-09-22 14:23:02 +00001251 if (SyncCacheViewAuthenticPixels(unique_view,exception) == MagickFalse)
cristyf2e11662009-10-14 01:24:43 +00001252 break;
1253 cube_info->x++;
1254 p++;
1255 }
cristyc5c6f662010-09-22 14:23:02 +00001256 if (unique_image->progress_monitor != (MagickProgressMonitor) NULL)
cristy8b27a6d2010-02-14 03:31:15 +00001257 {
1258 MagickBooleanType
1259 proceed;
1260
cristyc5c6f662010-09-22 14:23:02 +00001261 proceed=SetImageProgress(unique_image,UniqueColorsImageTag,
cristy8b27a6d2010-02-14 03:31:15 +00001262 cube_info->progress,cube_info->colors);
1263 if (proceed == MagickFalse)
1264 status=MagickFalse;
1265 }
cristyf2e11662009-10-14 01:24:43 +00001266 cube_info->progress++;
cristyda16f162011-02-19 23:52:17 +00001267 if (status == MagickFalse)
1268 return;
cristyf2e11662009-10-14 01:24:43 +00001269 }
1270}
1271
1272MagickExport Image *UniqueImageColors(const Image *image,
1273 ExceptionInfo *exception)
1274{
cristyc5c6f662010-09-22 14:23:02 +00001275 CacheView
1276 *unique_view;
1277
cristyf2e11662009-10-14 01:24:43 +00001278 CubeInfo
1279 *cube_info;
1280
1281 Image
1282 *unique_image;
1283
1284 cube_info=ClassifyImageColors(image,exception);
1285 if (cube_info == (CubeInfo *) NULL)
1286 return((Image *) NULL);
1287 unique_image=CloneImage(image,cube_info->colors,1,MagickTrue,exception);
1288 if (unique_image == (Image *) NULL)
1289 return(unique_image);
cristy574cc262011-08-05 01:23:58 +00001290 if (SetImageStorageClass(unique_image,DirectClass,exception) == MagickFalse)
cristyf2e11662009-10-14 01:24:43 +00001291 {
cristyf2e11662009-10-14 01:24:43 +00001292 unique_image=DestroyImage(unique_image);
1293 return((Image *) NULL);
1294 }
cristydb070952012-04-20 14:33:00 +00001295 unique_view=AcquireAuthenticCacheView(unique_image,exception);
cristyc5c6f662010-09-22 14:23:02 +00001296 UniqueColorsToImage(unique_image,unique_view,cube_info,cube_info->root,
1297 exception);
1298 unique_view=DestroyCacheView(unique_view);
cristyf2e11662009-10-14 01:24:43 +00001299 if (cube_info->colors < MaxColormapSize)
1300 {
1301 QuantizeInfo
1302 *quantize_info;
1303
1304 quantize_info=AcquireQuantizeInfo((ImageInfo *) NULL);
1305 quantize_info->number_colors=MaxColormapSize;
cristycbda6112012-05-27 20:57:16 +00001306 quantize_info->dither_method=NoDitherMethod;
cristyf2e11662009-10-14 01:24:43 +00001307 quantize_info->tree_depth=8;
cristy018f07f2011-09-04 21:15:19 +00001308 (void) QuantizeImage(quantize_info,unique_image,exception);
cristyf2e11662009-10-14 01:24:43 +00001309 quantize_info=DestroyQuantizeInfo(quantize_info);
1310 }
1311 cube_info=DestroyCubeInfo(image,cube_info);
1312 return(unique_image);
1313}