blob: 5a822915c1ac88d29b2a3eb73605df36a5534983 [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% %
Cristy7ce65e72015-12-12 18:03:16 -050021% Copyright 1999-2016 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);
cristy17f11b02014-12-20 19:37:04 +0000169 if (image->alpha_trait != UndefinedPixelTrait)
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);
cristye1c94d92015-06-28 12:16:33 +0000215 assert(image->signature == MagickCoreSignature);
cristyf2e11662009-10-14 01:24:43 +0000216 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(),
cristyefe601c2013-01-05 17:51:12 +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);
cristy46ff2672012-12-14 15:32:26 +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(),
cristyefe601c2013-01-05 17:51:12 +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(),
cristyefe601c2013-01-05 17:51:12 +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 */
cristy17f11b02014-12-20 19:37:04 +0000345 number_children=image->alpha_trait == UndefinedPixelTrait ? 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 */
cristy17f11b02014-12-20 19:37:04 +0000444 number_children=image->alpha_trait == UndefinedPixelTrait ? 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(),
cristyefe601c2013-01-05 17:51:12 +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% %
dirkab4f0bb2015-07-25 11:46:32 +0000613% I d e n t i f y P a l e t t e I m a g e %
614% %
615% %
616% %
617%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
618%
619% IdentifyPaletteImage() returns MagickTrue if the image has 256 unique colors
620% or less.
621%
622% The format of the IdentifyPaletteImage method is:
623%
624% MagickBooleanType IdentifyPaletteImage(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*/
634
635static MagickBooleanType CheckImageColors(const Image *image,
636 ExceptionInfo *exception,size_t max_colors)
637{
638 CacheView
639 *image_view;
640
641 CubeInfo
642 *cube_info;
643
644 PixelInfo
645 pixel,
646 target;
647
648 register const Quantum
649 *p;
650
651 register ssize_t
652 x;
653
654 register NodeInfo
655 *node_info;
656
657 register ssize_t
658 i;
659
660 size_t
661 id,
662 index,
663 level;
664
665 ssize_t
666 y;
667
668 if (image->storage_class == PseudoClass)
669 return((image->colors <= max_colors) ? MagickTrue : MagickFalse);
670 /*
671 Initialize color description tree.
672 */
673 cube_info=GetCubeInfo();
674 if (cube_info == (CubeInfo *) NULL)
675 {
676 (void) ThrowMagickException(exception,GetMagickModule(),
677 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
678 return(MagickFalse);
679 }
680 GetPixelInfo(image,&pixel);
681 GetPixelInfo(image,&target);
682 image_view=AcquireVirtualCacheView(image,exception);
683 for (y=0; y < (ssize_t) image->rows; y++)
684 {
685 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
686 if (p == (const Quantum *) NULL)
687 break;
688 for (x=0; x < (ssize_t) image->columns; x++)
689 {
690 /*
691 Start at the root and proceed level by level.
692 */
693 node_info=cube_info->root;
694 index=MaxTreeDepth-1;
695 for (level=1; level < MaxTreeDepth; level++)
696 {
697 GetPixelInfoPixel(image,p,&pixel);
698 id=ColorToNodeId(image,&pixel,index);
699 if (node_info->child[id] == (NodeInfo *) NULL)
700 {
701 node_info->child[id]=GetNodeInfo(cube_info,level);
702 if (node_info->child[id] == (NodeInfo *) NULL)
703 {
704 (void) ThrowMagickException(exception,GetMagickModule(),
705 ResourceLimitError,"MemoryAllocationFailed","`%s'",
706 image->filename);
707 break;
708 }
709 }
710 node_info=node_info->child[id];
711 index--;
712 }
713 if (level < MaxTreeDepth)
714 break;
715 for (i=0; i < (ssize_t) node_info->number_unique; i++)
716 {
717 target=node_info->list[i];
718 if (IsPixelInfoEquivalent(&pixel,&target) != MagickFalse)
719 break;
720 }
721 if (i < (ssize_t) node_info->number_unique)
722 node_info->list[i].count++;
723 else
724 {
725 /*
726 Add this unique color to the color list.
727 */
728 if (node_info->number_unique == 0)
729 node_info->list=(PixelInfo *) AcquireMagickMemory(
730 sizeof(*node_info->list));
731 else
732 node_info->list=(PixelInfo *) ResizeQuantumMemory(node_info->list,
733 (size_t) (i+1),sizeof(*node_info->list));
734 if (node_info->list == (PixelInfo *) NULL)
735 {
736 (void) ThrowMagickException(exception,GetMagickModule(),
737 ResourceLimitError,"MemoryAllocationFailed","`%s'",
738 image->filename);
739 break;
740 }
741 node_info->list[i].red=(double) GetPixelRed(image,p);
742 node_info->list[i].green=(double) GetPixelGreen(image,p);
743 node_info->list[i].blue=(double) GetPixelBlue(image,p);
744 if (image->colorspace == CMYKColorspace)
745 node_info->list[i].black=(double) GetPixelBlack(image,p);
746 node_info->list[i].alpha=(double) GetPixelAlpha(image,p);
747 node_info->list[i].count=1;
748 node_info->number_unique++;
749 cube_info->colors++;
750 if (cube_info->colors > max_colors)
751 break;
752 }
753 p+=GetPixelChannels(image);
754 }
755 if (x < (ssize_t) image->columns)
756 break;
757 }
758 image_view=DestroyCacheView(image_view);
759 cube_info=DestroyCubeInfo(image,cube_info);
760 return(y < (ssize_t) image->rows ? MagickFalse : MagickTrue);
761}
762
763MagickExport MagickBooleanType IdentifyPaletteImage(const Image *image,
764 ExceptionInfo *exception)
765{
766 assert(image != (Image *) NULL);
767 assert(image->signature == MagickCoreSignature);
768 if (image->debug != MagickFalse)
769 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
770 return(CheckImageColors(image,exception,256));
771}
Cristybcc9f052015-09-26 19:21:38 -0400772
dirkab4f0bb2015-07-25 11:46:32 +0000773/*
774%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
775% %
776% %
777% %
cristyf2e11662009-10-14 01:24:43 +0000778% I s H i s t o g r a m I m a g e %
779% %
780% %
781% %
782%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
783%
784% IsHistogramImage() returns MagickTrue if the image has 1024 unique colors or
785% less.
786%
787% The format of the IsHistogramImage method is:
788%
789% MagickBooleanType IsHistogramImage(const Image *image,
790% ExceptionInfo *exception)
791%
792% A description of each parameter follows.
793%
794% o image: the image.
795%
796% o exception: return any errors or warnings in this structure.
797%
798*/
799MagickExport MagickBooleanType IsHistogramImage(const Image *image,
800 ExceptionInfo *exception)
801{
802#define MaximumUniqueColors 1024
803
cristyf2e11662009-10-14 01:24:43 +0000804 assert(image != (Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000805 assert(image->signature == MagickCoreSignature);
cristyf2e11662009-10-14 01:24:43 +0000806 if (image->debug != MagickFalse)
807 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
dirkab4f0bb2015-07-25 11:46:32 +0000808 return(CheckImageColors(image,exception,MaximumUniqueColors));
cristyf2e11662009-10-14 01:24:43 +0000809}
810
811/*
812%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
813% %
814% %
815% %
816% I s P a l e t t e I m a g e %
817% %
818% %
819% %
820%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
821%
822% IsPaletteImage() returns MagickTrue if the image is PseudoClass and has 256
823% unique colors or less.
824%
825% The format of the IsPaletteImage method is:
826%
dirkab4f0bb2015-07-25 11:46:32 +0000827% MagickBooleanType IsPaletteImage(const Image *image)
cristyf2e11662009-10-14 01:24:43 +0000828%
829% A description of each parameter follows.
830%
831% o image: the image.
832%
cristyf2e11662009-10-14 01:24:43 +0000833*/
dirkab4f0bb2015-07-25 11:46:32 +0000834MagickExport MagickBooleanType IsPaletteImage(const Image *image)
cristyf2e11662009-10-14 01:24:43 +0000835{
cristyf2e11662009-10-14 01:24:43 +0000836 assert(image != (Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000837 assert(image->signature == MagickCoreSignature);
cristyf2e11662009-10-14 01:24:43 +0000838 if (image->debug != MagickFalse)
839 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
dirkab4f0bb2015-07-25 11:46:32 +0000840 if (image->storage_class != PseudoClass)
cristyf2e11662009-10-14 01:24:43 +0000841 return(MagickFalse);
dirkab4f0bb2015-07-25 11:46:32 +0000842 return((image->colors <= 256) ? MagickTrue : MagickFalse);
cristyf2e11662009-10-14 01:24:43 +0000843}
844
845/*
cristy3ed852e2009-09-05 21:47:34 +0000846%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
847% %
848% %
849% %
850% M i n M a x S t r e t c h I m a g e %
851% %
852% %
853% %
854%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
855%
856% MinMaxStretchImage() uses the exact minimum and maximum values found in
857% each of the channels given, as the BlackPoint and WhitePoint to linearly
858% stretch the colors (and histogram) of the image. The stretch points are
859% also moved further inward by the adjustment values given.
860%
glennrpf0a92fd2011-04-27 13:17:21 +0000861% If the adjustment values are both zero this function is equivalent to a
cristy3ed852e2009-09-05 21:47:34 +0000862% perfect normalization (or autolevel) of the image.
863%
864% Each channel is stretched independantally of each other (producing color
865% distortion) unless the special 'SyncChannels' flag is also provided in the
866% channels setting. If this flag is present the minimum and maximum point
867% will be extracted from all the given channels, and those channels will be
868% stretched by exactly the same amount (preventing color distortion).
869%
anthony7fe39fc2010-04-06 03:19:20 +0000870% In the special case that only ONE value is found in a channel of the image
871% that value is not stretched, that value is left as is.
872%
cristy3ed852e2009-09-05 21:47:34 +0000873% The 'SyncChannels' is turned on in the 'DefaultChannels' setting by
874% default.
875%
876% The format of the MinMaxStretchImage method is:
877%
cristy490408a2011-07-07 14:42:05 +0000878% MagickBooleanType MinMaxStretchImage(Image *image,const double black,
cristya63e4a92011-09-09 00:47:59 +0000879% const double white,const double gamma,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000880%
881% A description of each parameter follows:
882%
883% o image: The image to auto-level
884%
cristy490408a2011-07-07 14:42:05 +0000885% o black, white: move the black / white point inward from the minimum and
886% maximum points by this color value.
cristy3ed852e2009-09-05 21:47:34 +0000887%
cristya63e4a92011-09-09 00:47:59 +0000888% o gamma: the gamma.
889%
cristy95111202011-08-09 19:41:42 +0000890% o exception: return any errors or warnings in this structure.
891%
cristy3ed852e2009-09-05 21:47:34 +0000892*/
cristy3ed852e2009-09-05 21:47:34 +0000893MagickExport MagickBooleanType MinMaxStretchImage(Image *image,
cristya63e4a92011-09-09 00:47:59 +0000894 const double black,const double white,const double gamma,
895 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000896{
897 double
cristy19eb6412010-04-23 14:42:29 +0000898 min,
899 max;
cristy3ed852e2009-09-05 21:47:34 +0000900
cristya63e4a92011-09-09 00:47:59 +0000901 register ssize_t
902 i;
903
cristy3ed852e2009-09-05 21:47:34 +0000904 MagickStatusType
905 status;
906
Cristy8f919592015-09-26 03:22:29 -0400907 status=MagickTrue;
908 if (image->channel_mask == DefaultChannels)
909 {
910 /*
911 Auto-level all channels equally.
912 */
913 (void) GetImageRange(image,&min,&max,exception);
914 min+=black;
915 max-=white;
916 if (fabs(min-max) >= MagickEpsilon)
917 status&=LevelImage(image,min,max,gamma,exception);
918 return(status != 0 ? MagickTrue : MagickFalse);
919 }
cristy3ed852e2009-09-05 21:47:34 +0000920 /*
Cristyd580bbb2015-08-17 18:55:47 -0400921 Auto-level each channel.
cristy3ed852e2009-09-05 21:47:34 +0000922 */
cristya63e4a92011-09-09 00:47:59 +0000923 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
924 {
925 ChannelType
926 channel_mask;
927
cristy5a23c552013-02-13 14:34:28 +0000928 PixelChannel channel=GetPixelChannelChannel(image,i);
929 PixelTrait traits=GetPixelChannelTraits(image,channel);
cristya63e4a92011-09-09 00:47:59 +0000930 if ((traits & UpdatePixelTrait) == 0)
931 continue;
cristycf1296e2012-08-26 23:40:49 +0000932 channel_mask=SetImageChannelMask(image,(ChannelType) (1 << i));
cristya63e4a92011-09-09 00:47:59 +0000933 status&=GetImageRange(image,&min,&max,exception);
934 min+=black;
935 max-=white;
936 if (fabs(min-max) >= MagickEpsilon)
937 status&=LevelImage(image,min,max,gamma,exception);
cristycf1296e2012-08-26 23:40:49 +0000938 (void) SetImageChannelMask(image,channel_mask);
cristya63e4a92011-09-09 00:47:59 +0000939 }
cristy19eb6412010-04-23 14:42:29 +0000940 return(status != 0 ? MagickTrue : MagickFalse);
cristy3ed852e2009-09-05 21:47:34 +0000941}
942
cristyf2e11662009-10-14 01:24:43 +0000943/*
944%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
945% %
946% %
947% %
948% G e t N u m b e r C o l o r s %
949% %
950% %
951% %
952%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
953%
954% GetNumberColors() returns the number of unique colors in an image.
955%
956% The format of the GetNumberColors method is:
957%
cristybb503372010-05-27 20:51:26 +0000958% size_t GetNumberColors(const Image *image,FILE *file,
cristyf2e11662009-10-14 01:24:43 +0000959% ExceptionInfo *exception)
960%
961% A description of each parameter follows.
962%
963% o image: the image.
964%
965% o file: Write a histogram of the color distribution to this file handle.
966%
967% o exception: return any errors or warnings in this structure.
968%
969*/
970
971#if defined(__cplusplus) || defined(c_plusplus)
972extern "C" {
973#endif
974
975static int HistogramCompare(const void *x,const void *y)
976{
cristy101ab702011-10-13 13:06:32 +0000977 const PixelInfo
cristyf2e11662009-10-14 01:24:43 +0000978 *color_1,
979 *color_2;
980
cristy101ab702011-10-13 13:06:32 +0000981 color_1=(const PixelInfo *) x;
982 color_2=(const PixelInfo *) y;
cristy4c08aed2011-07-01 19:47:50 +0000983 if (color_2->red != color_1->red)
984 return((int) color_1->red-(int) color_2->red);
985 if (color_2->green != color_1->green)
986 return((int) color_1->green-(int) color_2->green);
987 if (color_2->blue != color_1->blue)
988 return((int) color_1->blue-(int) color_2->blue);
cristyf2e11662009-10-14 01:24:43 +0000989 return((int) color_2->count-(int) color_1->count);
990}
991
992#if defined(__cplusplus) || defined(c_plusplus)
993}
994#endif
995
cristybb503372010-05-27 20:51:26 +0000996MagickExport size_t GetNumberColors(const Image *image,FILE *file,
cristyf2e11662009-10-14 01:24:43 +0000997 ExceptionInfo *exception)
998{
999#define HistogramImageTag "Histogram/Image"
1000
1001 char
cristy151b66d2015-04-15 10:50:31 +00001002 color[MagickPathExtent],
1003 hex[MagickPathExtent],
1004 tuple[MagickPathExtent];
cristyf2e11662009-10-14 01:24:43 +00001005
cristy101ab702011-10-13 13:06:32 +00001006 PixelInfo
cristyf2e11662009-10-14 01:24:43 +00001007 *histogram;
1008
cristy8b27a6d2010-02-14 03:31:15 +00001009 MagickBooleanType
1010 status;
1011
cristy4c08aed2011-07-01 19:47:50 +00001012 PixelInfo
cristyf2e11662009-10-14 01:24:43 +00001013 pixel;
1014
cristy101ab702011-10-13 13:06:32 +00001015 register PixelInfo
cristyf2e11662009-10-14 01:24:43 +00001016 *p;
1017
cristybb503372010-05-27 20:51:26 +00001018 register ssize_t
cristyf2e11662009-10-14 01:24:43 +00001019 i;
1020
cristybb503372010-05-27 20:51:26 +00001021 size_t
cristyf2e11662009-10-14 01:24:43 +00001022 number_colors;
1023
1024 number_colors=0;
1025 if (file == (FILE *) NULL)
1026 {
1027 CubeInfo
1028 *cube_info;
1029
1030 cube_info=ClassifyImageColors(image,exception);
1031 if (cube_info != (CubeInfo *) NULL)
1032 number_colors=cube_info->colors;
1033 cube_info=DestroyCubeInfo(image,cube_info);
1034 return(number_colors);
1035 }
1036 histogram=GetImageHistogram(image,&number_colors,exception);
cristy101ab702011-10-13 13:06:32 +00001037 if (histogram == (PixelInfo *) NULL)
cristyf2e11662009-10-14 01:24:43 +00001038 return(number_colors);
1039 qsort((void *) histogram,(size_t) number_colors,sizeof(*histogram),
1040 HistogramCompare);
cristy4c08aed2011-07-01 19:47:50 +00001041 GetPixelInfo(image,&pixel);
cristyf2e11662009-10-14 01:24:43 +00001042 p=histogram;
cristyda16f162011-02-19 23:52:17 +00001043 status=MagickTrue;
cristybb503372010-05-27 20:51:26 +00001044 for (i=0; i < (ssize_t) number_colors; i++)
cristyf2e11662009-10-14 01:24:43 +00001045 {
cristy9d8c8ce2011-10-25 16:13:52 +00001046 pixel=(*p);
cristy151b66d2015-04-15 10:50:31 +00001047 (void) CopyMagickString(tuple,"(",MagickPathExtent);
cristyed231572011-07-14 02:18:59 +00001048 ConcatenateColorComponent(&pixel,RedPixelChannel,X11Compliance,tuple);
cristy151b66d2015-04-15 10:50:31 +00001049 (void) ConcatenateMagickString(tuple,",",MagickPathExtent);
cristyed231572011-07-14 02:18:59 +00001050 ConcatenateColorComponent(&pixel,GreenPixelChannel,X11Compliance,tuple);
cristy151b66d2015-04-15 10:50:31 +00001051 (void) ConcatenateMagickString(tuple,",",MagickPathExtent);
cristyed231572011-07-14 02:18:59 +00001052 ConcatenateColorComponent(&pixel,BluePixelChannel,X11Compliance,tuple);
cristyf2e11662009-10-14 01:24:43 +00001053 if (pixel.colorspace == CMYKColorspace)
1054 {
cristy151b66d2015-04-15 10:50:31 +00001055 (void) ConcatenateMagickString(tuple,",",MagickPathExtent);
cristyed231572011-07-14 02:18:59 +00001056 ConcatenateColorComponent(&pixel,BlackPixelChannel,X11Compliance,
cristyfa806a72011-07-04 02:06:13 +00001057 tuple);
cristyf2e11662009-10-14 01:24:43 +00001058 }
cristy17f11b02014-12-20 19:37:04 +00001059 if (pixel.alpha_trait != UndefinedPixelTrait)
cristyf2e11662009-10-14 01:24:43 +00001060 {
cristy151b66d2015-04-15 10:50:31 +00001061 (void) ConcatenateMagickString(tuple,",",MagickPathExtent);
cristyed231572011-07-14 02:18:59 +00001062 ConcatenateColorComponent(&pixel,AlphaPixelChannel,X11Compliance,
cristyfa806a72011-07-04 02:06:13 +00001063 tuple);
cristyf2e11662009-10-14 01:24:43 +00001064 }
cristy151b66d2015-04-15 10:50:31 +00001065 (void) ConcatenateMagickString(tuple,")",MagickPathExtent);
cristy269c9412011-10-13 23:41:15 +00001066 (void) QueryColorname(image,&pixel,SVGCompliance,color,exception);
cristyf2e11662009-10-14 01:24:43 +00001067 GetColorTuple(&pixel,MagickTrue,hex);
cristyaeded782012-09-11 23:39:36 +00001068 (void) FormatLocaleFile(file,"%10.20g",(double) ((MagickOffsetType)
cristybcb0f452012-02-12 19:26:56 +00001069 p->count));
cristyb51dff52011-05-19 16:55:47 +00001070 (void) FormatLocaleFile(file,": %s %s %s\n",tuple,hex,color);
cristy8b27a6d2010-02-14 03:31:15 +00001071 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1072 {
1073 MagickBooleanType
1074 proceed;
1075
cristycee97112010-05-28 00:44:52 +00001076 proceed=SetImageProgress(image,HistogramImageTag,(MagickOffsetType) i,
1077 number_colors);
cristy8b27a6d2010-02-14 03:31:15 +00001078 if (proceed == MagickFalse)
1079 status=MagickFalse;
1080 }
cristyf2e11662009-10-14 01:24:43 +00001081 p++;
1082 }
1083 (void) fflush(file);
cristy101ab702011-10-13 13:06:32 +00001084 histogram=(PixelInfo *) RelinquishMagickMemory(histogram);
cristyda16f162011-02-19 23:52:17 +00001085 if (status == MagickFalse)
1086 return(0);
cristyf2e11662009-10-14 01:24:43 +00001087 return(number_colors);
1088}
1089
1090/*
1091%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1092% %
1093% %
1094% %
1095% U n i q u e I m a g e C o l o r s %
1096% %
1097% %
1098% %
1099%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1100%
1101% UniqueImageColors() returns the unique colors of an image.
1102%
1103% The format of the UniqueImageColors method is:
1104%
1105% Image *UniqueImageColors(const Image *image,ExceptionInfo *exception)
1106%
1107% A description of each parameter follows.
1108%
1109% o image: the image.
1110%
1111% o exception: return any errors or warnings in this structure.
1112%
1113*/
1114
cristyc5c6f662010-09-22 14:23:02 +00001115static void UniqueColorsToImage(Image *unique_image,CacheView *unique_view,
1116 CubeInfo *cube_info,const NodeInfo *node_info,ExceptionInfo *exception)
cristyf2e11662009-10-14 01:24:43 +00001117{
1118#define UniqueColorsImageTag "UniqueColors/Image"
1119
cristy8b27a6d2010-02-14 03:31:15 +00001120 MagickBooleanType
1121 status;
1122
cristybb503372010-05-27 20:51:26 +00001123 register ssize_t
cristyf2e11662009-10-14 01:24:43 +00001124 i;
1125
cristybb503372010-05-27 20:51:26 +00001126 size_t
cristyf2e11662009-10-14 01:24:43 +00001127 number_children;
1128
1129 /*
1130 Traverse any children.
1131 */
cristy17f11b02014-12-20 19:37:04 +00001132 number_children=unique_image->alpha_trait == UndefinedPixelTrait ? 8UL : 16UL;
cristybb503372010-05-27 20:51:26 +00001133 for (i=0; i < (ssize_t) number_children; i++)
cristyf2e11662009-10-14 01:24:43 +00001134 if (node_info->child[i] != (NodeInfo *) NULL)
cristyc5c6f662010-09-22 14:23:02 +00001135 UniqueColorsToImage(unique_image,unique_view,cube_info,
1136 node_info->child[i],exception);
cristyf2e11662009-10-14 01:24:43 +00001137 if (node_info->level == (MaxTreeDepth-1))
1138 {
cristy101ab702011-10-13 13:06:32 +00001139 register PixelInfo
cristyf2e11662009-10-14 01:24:43 +00001140 *p;
1141
cristy4c08aed2011-07-01 19:47:50 +00001142 register Quantum
dirk05d2ff72015-11-18 23:13:43 +01001143 *magick_restrict q;
cristyf2e11662009-10-14 01:24:43 +00001144
cristyda16f162011-02-19 23:52:17 +00001145 status=MagickTrue;
cristyf2e11662009-10-14 01:24:43 +00001146 p=node_info->list;
cristybb503372010-05-27 20:51:26 +00001147 for (i=0; i < (ssize_t) node_info->number_unique; i++)
cristyf2e11662009-10-14 01:24:43 +00001148 {
cristyc5c6f662010-09-22 14:23:02 +00001149 q=QueueCacheViewAuthenticPixels(unique_view,cube_info->x,0,1,1,
1150 exception);
cristyacd2ed22011-08-30 01:44:23 +00001151 if (q == (Quantum *) NULL)
cristyf2e11662009-10-14 01:24:43 +00001152 continue;
cristye42f6582012-02-11 17:59:50 +00001153 SetPixelRed(unique_image,ClampToQuantum(p->red),q);
1154 SetPixelGreen(unique_image,ClampToQuantum(p->green),q);
1155 SetPixelBlue(unique_image,ClampToQuantum(p->blue),q);
1156 SetPixelAlpha(unique_image,ClampToQuantum(p->alpha),q);
cristyc5c6f662010-09-22 14:23:02 +00001157 if (unique_image->colorspace == CMYKColorspace)
cristye42f6582012-02-11 17:59:50 +00001158 SetPixelBlack(unique_image,ClampToQuantum(p->black),q);
cristyc5c6f662010-09-22 14:23:02 +00001159 if (SyncCacheViewAuthenticPixels(unique_view,exception) == MagickFalse)
cristyf2e11662009-10-14 01:24:43 +00001160 break;
1161 cube_info->x++;
1162 p++;
1163 }
cristyc5c6f662010-09-22 14:23:02 +00001164 if (unique_image->progress_monitor != (MagickProgressMonitor) NULL)
cristy8b27a6d2010-02-14 03:31:15 +00001165 {
1166 MagickBooleanType
1167 proceed;
1168
cristyc5c6f662010-09-22 14:23:02 +00001169 proceed=SetImageProgress(unique_image,UniqueColorsImageTag,
cristy8b27a6d2010-02-14 03:31:15 +00001170 cube_info->progress,cube_info->colors);
1171 if (proceed == MagickFalse)
1172 status=MagickFalse;
1173 }
cristyf2e11662009-10-14 01:24:43 +00001174 cube_info->progress++;
cristyda16f162011-02-19 23:52:17 +00001175 if (status == MagickFalse)
1176 return;
cristyf2e11662009-10-14 01:24:43 +00001177 }
1178}
1179
1180MagickExport Image *UniqueImageColors(const Image *image,
1181 ExceptionInfo *exception)
1182{
cristyc5c6f662010-09-22 14:23:02 +00001183 CacheView
1184 *unique_view;
1185
cristyf2e11662009-10-14 01:24:43 +00001186 CubeInfo
1187 *cube_info;
1188
1189 Image
1190 *unique_image;
1191
1192 cube_info=ClassifyImageColors(image,exception);
1193 if (cube_info == (CubeInfo *) NULL)
1194 return((Image *) NULL);
1195 unique_image=CloneImage(image,cube_info->colors,1,MagickTrue,exception);
1196 if (unique_image == (Image *) NULL)
1197 return(unique_image);
cristy574cc262011-08-05 01:23:58 +00001198 if (SetImageStorageClass(unique_image,DirectClass,exception) == MagickFalse)
cristyf2e11662009-10-14 01:24:43 +00001199 {
cristyf2e11662009-10-14 01:24:43 +00001200 unique_image=DestroyImage(unique_image);
1201 return((Image *) NULL);
1202 }
cristy46ff2672012-12-14 15:32:26 +00001203 unique_view=AcquireAuthenticCacheView(unique_image,exception);
cristyc5c6f662010-09-22 14:23:02 +00001204 UniqueColorsToImage(unique_image,unique_view,cube_info,cube_info->root,
1205 exception);
1206 unique_view=DestroyCacheView(unique_view);
cristyf2e11662009-10-14 01:24:43 +00001207 cube_info=DestroyCubeInfo(image,cube_info);
1208 return(unique_image);
1209}