blob: 54bde2b68a2f0b944096b69ba132bca3bac2217a [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% %
21% Copyright 1999-2009 ImageMagick Studio LLC, a non-profit organization %
22% dedicated to making software imaging solutions freely available. %
23% %
24% You may not use this file except in compliance with the License. You may %
25% obtain a copy of the License at %
26% %
27% http://www.imagemagick.org/script/license.php %
28% %
29% Unless required by applicable law or agreed to in writing, software %
30% distributed under the License is distributed on an "AS IS" BASIS, %
31% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
32% See the License for the specific language governing permissions and %
33% limitations under the License. %
34% %
35%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
36%
37%
38*/
39
40/*
41 Include declarations.
42*/
43#include "magick/studio.h"
44#include "magick/cache-view.h"
cristyf2e11662009-10-14 01:24:43 +000045#include "magick/color-private.h"
cristy3ed852e2009-09-05 21:47:34 +000046#include "magick/enhance.h"
47#include "magick/exception.h"
48#include "magick/exception-private.h"
49#include "magick/hashmap.h"
50#include "magick/histogram.h"
51#include "magick/image.h"
52#include "magick/list.h"
53#include "magick/memory_.h"
cristyf2e11662009-10-14 01:24:43 +000054#include "magick/monitor-private.h"
55#include "magick/pixel-private.h"
cristy3ed852e2009-09-05 21:47:34 +000056#include "magick/prepress.h"
cristyf2e11662009-10-14 01:24:43 +000057#include "magick/quantize.h"
cristy3ed852e2009-09-05 21:47:34 +000058#include "magick/registry.h"
59#include "magick/semaphore.h"
60#include "magick/splay-tree.h"
61#include "magick/statistic.h"
62#include "magick/string_.h"
63
64/*
cristyf2e11662009-10-14 01:24:43 +000065 Define declarations.
66*/
67#define MaxTreeDepth 8
68#define NodesInAList 1536
69
70/*
71 Typedef declarations.
72*/
73typedef struct _NodeInfo
74{
75 struct _NodeInfo
76 *child[16];
77
78 ColorPacket
79 *list;
80
81 MagickSizeType
82 number_unique;
83
84 unsigned long
85 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
102 long
103 x,
104 progress;
105
106 unsigned long
107 colors,
108 free_nodes;
109
110 NodeInfo
111 *node_info;
112
113 Nodes
114 *node_queue;
115} CubeInfo;
116
117/*
118 Forward declarations.
119*/
120static CubeInfo
121 *GetCubeInfo(void);
122
123static NodeInfo
124 *GetNodeInfo(CubeInfo *,const unsigned long);
125
126static void
127 DestroyColorCube(const Image *,NodeInfo *);
128
129/*
130%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
131% %
132% %
133% %
134+ C l a s s i f y I m a g e C o l o r s %
135% %
136% %
137% %
138%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
139%
140% ClassifyImageColors() builds a populated CubeInfo tree for the specified
141% image. The returned tree should be deallocated using DestroyCubeInfo()
142% once it is no longer needed.
143%
144% The format of the ClassifyImageColors() method is:
145%
146% CubeInfo *ClassifyImageColors(const Image *image,
147% ExceptionInfo *exception)
148%
149% A description of each parameter follows.
150%
151% o image: the image.
152%
153% o exception: return any errors or warnings in this structure.
154%
155*/
156
157static inline unsigned long ColorToNodeId(const Image *image,
158 const MagickPixelPacket *pixel,unsigned long index)
159{
160 unsigned long
161 id;
162
163 id=(unsigned long) (
164 ((ScaleQuantumToChar(RoundToQuantum(pixel->red)) >> index) & 0x01) |
165 ((ScaleQuantumToChar(RoundToQuantum(pixel->green)) >> index) & 0x01) << 1 |
166 ((ScaleQuantumToChar(RoundToQuantum(pixel->blue)) >> index) & 0x01) << 2);
167 if (image->matte != MagickFalse)
168 id|=((ScaleQuantumToChar(RoundToQuantum(pixel->opacity)) >> index) &
169 0x01) << 3;
170 return(id);
171}
172
173static CubeInfo *ClassifyImageColors(const Image *image,
174 ExceptionInfo *exception)
175{
176#define EvaluateImageTag " Compute image colors... "
177
178 CubeInfo
179 *cube_info;
180
181 long
182 y;
183
184 MagickBooleanType
185 proceed;
186
187 MagickPixelPacket
188 pixel,
189 target;
190
191 NodeInfo
192 *node_info;
193
194 register const IndexPacket
195 *indexes;
196
197 register const PixelPacket
198 *p;
199
200 register long
201 i,
202 x;
203
204 register unsigned long
205 id,
206 index,
207 level;
208
209 CacheView
210 *image_view;
211
212 /*
213 Initialize color description tree.
214 */
215 assert(image != (const Image *) NULL);
216 assert(image->signature == MagickSignature);
217 if (image->debug != MagickFalse)
218 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
219 cube_info=GetCubeInfo();
220 if (cube_info == (CubeInfo *) NULL)
221 {
222 (void) ThrowMagickException(exception,GetMagickModule(),
223 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
224 return(cube_info);
225 }
226 GetMagickPixelPacket(image,&pixel);
227 GetMagickPixelPacket(image,&target);
228 image_view=AcquireCacheView(image);
229 for (y=0; y < (long) image->rows; y++)
230 {
231 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
232 if (p == (const PixelPacket *) NULL)
233 break;
234 indexes=GetCacheViewVirtualIndexQueue(image_view);
235 for (x=0; x < (long) image->columns; x++)
236 {
237 /*
238 Start at the root and proceed level by level.
239 */
240 node_info=cube_info->root;
241 index=MaxTreeDepth-1;
242 for (level=1; level < MaxTreeDepth; level++)
243 {
244 SetMagickPixelPacket(image,p,indexes+x,&pixel);
245 id=ColorToNodeId(image,&pixel,index);
246 if (node_info->child[id] == (NodeInfo *) NULL)
247 {
248 node_info->child[id]=GetNodeInfo(cube_info,level);
249 if (node_info->child[id] == (NodeInfo *) NULL)
250 {
251 (void) ThrowMagickException(exception,GetMagickModule(),
252 ResourceLimitError,"MemoryAllocationFailed","`%s'",
253 image->filename);
254 return(0);
255 }
256 }
257 node_info=node_info->child[id];
258 index--;
259 }
260 for (i=0; i < (long) node_info->number_unique; i++)
261 {
262 SetMagickPixelPacket(image,&node_info->list[i].pixel,
263 &node_info->list[i].index,&target);
264 if (IsMagickColorEqual(&pixel,&target) != MagickFalse)
265 break;
266 }
267 if (i < (long) node_info->number_unique)
268 node_info->list[i].count++;
269 else
270 {
271 if (node_info->number_unique == 0)
272 node_info->list=(ColorPacket *) AcquireMagickMemory(
273 sizeof(*node_info->list));
274 else
275 node_info->list=(ColorPacket *) ResizeQuantumMemory(node_info->list,
276 (size_t) (i+1),sizeof(*node_info->list));
277 if (node_info->list == (ColorPacket *) NULL)
278 {
279 (void) ThrowMagickException(exception,GetMagickModule(),
280 ResourceLimitError,"MemoryAllocationFailed","`%s'",
281 image->filename);
282 return(0);
283 }
284 node_info->list[i].pixel=(*p);
285 if ((image->colorspace == CMYKColorspace) ||
286 (image->storage_class == PseudoClass))
287 node_info->list[i].index=indexes[x];
288 node_info->list[i].count=1;
289 node_info->number_unique++;
290 cube_info->colors++;
291 }
292 p++;
293 }
294 proceed=SetImageProgress(image,EvaluateImageTag,y,image->rows);
295 if (proceed == MagickFalse)
296 break;
297 }
298 image_view=DestroyCacheView(image_view);
299 return(cube_info);
300}
301
302/*
303%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
304% %
305% %
306% %
307+ D e f i n e I m a g e H i s t o g r a m %
308% %
309% %
310% %
311%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
312%
313% DefineImageHistogram() traverses the color cube tree and notes each colormap
314% entry. A colormap entry is any node in the color cube tree where the
315% of unique colors is not zero.
316%
317% The format of the DefineImageHistogram method is:
318%
319% DefineImageHistogram(const Image *image,NodeInfo *node_info,
320% ColorPacket **unique_colors)
321%
322% A description of each parameter follows.
323%
324% o image: the image.
325%
326% o node_info: the address of a structure of type NodeInfo which points to a
327% node in the color cube tree that is to be pruned.
328%
329% o histogram: the image histogram.
330%
331*/
332static void DefineImageHistogram(const Image *image,NodeInfo *node_info,
333 ColorPacket **histogram)
334{
335 register long
336 i;
337
338 unsigned long
339 number_children;
340
341 /*
342 Traverse any children.
343 */
344 number_children=image->matte == MagickFalse ? 8UL : 16UL;
345 for (i=0; i < (long) number_children; i++)
346 if (node_info->child[i] != (NodeInfo *) NULL)
347 DefineImageHistogram(image,node_info->child[i],histogram);
348 if (node_info->level == (MaxTreeDepth-1))
349 {
350 register ColorPacket
351 *p;
352
353 p=node_info->list;
354 for (i=0; i < (long) node_info->number_unique; i++)
355 {
356 (*histogram)->pixel=p->pixel;
357 (*histogram)->index=p->index;
358 (*histogram)->count=p->count;
359 (*histogram)++;
360 p++;
361 }
362 }
363}
364
365/*
366%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
367% %
368% %
369% %
370+ D e s t r o y C u b e I n f o %
371% %
372% %
373% %
374%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
375%
376% DestroyCubeInfo() deallocates memory associated with a CubeInfo structure.
377%
378% The format of the DestroyCubeInfo method is:
379%
380% DestroyCubeInfo(const Image *image,CubeInfo *cube_info)
381%
382% A description of each parameter follows:
383%
384% o image: the image.
385%
386% o cube_info: the address of a structure of type CubeInfo.
387%
388*/
389static CubeInfo *DestroyCubeInfo(const Image *image,CubeInfo *cube_info)
390{
391 register Nodes
392 *nodes;
393
394 /*
395 Release color cube tree storage.
396 */
397 DestroyColorCube(image,cube_info->root);
398 do
399 {
400 nodes=cube_info->node_queue->next;
401 cube_info->node_queue=(Nodes *)
402 RelinquishMagickMemory(cube_info->node_queue);
403 cube_info->node_queue=nodes;
404 } while (cube_info->node_queue != (Nodes *) NULL);
405 return((CubeInfo *) RelinquishMagickMemory(cube_info));
406}
407
408/*
409%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
410% %
411% %
412% %
413+ D e s t r o y C o l o r C u b e %
414% %
415% %
416% %
417%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
418%
419% DestroyColorCube() traverses the color cube tree and frees the list of
420% unique colors.
421%
422% The format of the DestroyColorCube method is:
423%
424% void DestroyColorCube(const Image *image,const NodeInfo *node_info)
425%
426% A description of each parameter follows.
427%
428% o image: the image.
429%
430% o node_info: the address of a structure of type NodeInfo which points to a
431% node in the color cube tree that is to be pruned.
432%
433*/
434static void DestroyColorCube(const Image *image,NodeInfo *node_info)
435{
436 register long
437 i;
438
439 unsigned long
440 number_children;
441
442 /*
443 Traverse any children.
444 */
445 number_children=image->matte == MagickFalse ? 8UL : 16UL;
446 for (i=0; i < (long) number_children; i++)
447 if (node_info->child[i] != (NodeInfo *) NULL)
448 DestroyColorCube(image,node_info->child[i]);
449 if (node_info->list != (ColorPacket *) NULL)
450 node_info->list=(ColorPacket *) RelinquishMagickMemory(node_info->list);
451}
452
453/*
454%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
455% %
456% %
457% %
458+ G e t C u b e I n f o %
459% %
460% %
461% %
462%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
463%
464% GetCubeInfo() initializes the CubeInfo data structure.
465%
466% The format of the GetCubeInfo method is:
467%
468% cube_info=GetCubeInfo()
469%
470% A description of each parameter follows.
471%
472% o cube_info: A pointer to the Cube structure.
473%
474*/
475static CubeInfo *GetCubeInfo(void)
476{
477 CubeInfo
478 *cube_info;
479
480 /*
481 Initialize tree to describe color cube.
482 */
483 cube_info=(CubeInfo *) AcquireMagickMemory(sizeof(*cube_info));
484 if (cube_info == (CubeInfo *) NULL)
485 return((CubeInfo *) NULL);
486 (void) ResetMagickMemory(cube_info,0,sizeof(*cube_info));
487 /*
488 Initialize root node.
489 */
490 cube_info->root=GetNodeInfo(cube_info,0);
491 if (cube_info->root == (NodeInfo *) NULL)
492 return((CubeInfo *) NULL);
493 return(cube_info);
494}
495
496/*
497%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
498% %
499% %
500% %
501% G e t I m a g e H i s t o g r a m %
502% %
503% %
504% %
505%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
506%
507% GetImageHistogram() returns the unique colors in an image.
508%
509% The format of the GetImageHistogram method is:
510%
511% unsigned long GetImageHistogram(const Image *image,
512% unsigned long *number_colors,ExceptionInfo *exception)
513%
514% A description of each parameter follows.
515%
516% o image: the image.
517%
518% o file: Write a histogram of the color distribution to this file handle.
519%
520% o exception: return any errors or warnings in this structure.
521%
522*/
523MagickExport ColorPacket *GetImageHistogram(const Image *image,
524 unsigned long *number_colors,ExceptionInfo *exception)
525{
526 ColorPacket
527 *histogram;
528
529 CubeInfo
530 *cube_info;
531
532 *number_colors=0;
533 histogram=(ColorPacket *) NULL;
534 cube_info=ClassifyImageColors(image,exception);
535 if (cube_info != (CubeInfo *) NULL)
536 {
537 histogram=(ColorPacket *) AcquireQuantumMemory((size_t) cube_info->colors,
538 sizeof(*histogram));
539 if (histogram == (ColorPacket *) NULL)
540 (void) ThrowMagickException(exception,GetMagickModule(),
541 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
542 else
543 {
544 ColorPacket
545 *root;
546
547 *number_colors=cube_info->colors;
548 root=histogram;
549 DefineImageHistogram(image,cube_info->root,&root);
550 }
551 }
552 cube_info=DestroyCubeInfo(image,cube_info);
553 return(histogram);
554}
555
556/*
557%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
558% %
559% %
560% %
561+ G e t N o d e I n f o %
562% %
563% %
564% %
565%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
566%
567% GetNodeInfo() allocates memory for a new node in the color cube tree and
568% presets all fields to zero.
569%
570% The format of the GetNodeInfo method is:
571%
572% NodeInfo *GetNodeInfo(CubeInfo *cube_info,const unsigned long level)
573%
574% A description of each parameter follows.
575%
576% o cube_info: A pointer to the CubeInfo structure.
577%
578% o level: Specifies the level in the storage_class the node resides.
579%
580*/
581static NodeInfo *GetNodeInfo(CubeInfo *cube_info,const unsigned long level)
582{
583 NodeInfo
584 *node_info;
585
586 if (cube_info->free_nodes == 0)
587 {
588 Nodes
589 *nodes;
590
591 /*
592 Allocate a new nodes of nodes.
593 */
594 nodes=(Nodes *) AcquireMagickMemory(sizeof(*nodes));
595 if (nodes == (Nodes *) NULL)
596 return((NodeInfo *) NULL);
597 nodes->next=cube_info->node_queue;
598 cube_info->node_queue=nodes;
599 cube_info->node_info=nodes->nodes;
600 cube_info->free_nodes=NodesInAList;
601 }
602 cube_info->free_nodes--;
603 node_info=cube_info->node_info++;
604 (void) ResetMagickMemory(node_info,0,sizeof(*node_info));
605 node_info->level=level;
606 return(node_info);
607}
608
609/*
610%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
611% %
612% %
613% %
614% I s H i s t o g r a m I m a g e %
615% %
616% %
617% %
618%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
619%
620% IsHistogramImage() returns MagickTrue if the image has 1024 unique colors or
621% less.
622%
623% The format of the IsHistogramImage method is:
624%
625% MagickBooleanType IsHistogramImage(const Image *image,
626% ExceptionInfo *exception)
627%
628% A description of each parameter follows.
629%
630% o image: the image.
631%
632% o exception: return any errors or warnings in this structure.
633%
634*/
635MagickExport MagickBooleanType IsHistogramImage(const Image *image,
636 ExceptionInfo *exception)
637{
638#define MaximumUniqueColors 1024
639
640 CubeInfo
641 *cube_info;
642
643 long
644 y;
645
646 MagickPixelPacket
647 pixel,
648 target;
649
650 register const IndexPacket
651 *indexes;
652
653 register const PixelPacket
654 *p;
655
656 register long
657 x;
658
659 register NodeInfo
660 *node_info;
661
662 register long
663 i;
664
665 unsigned long
666 id,
667 index,
668 level;
669
670 CacheView
671 *image_view;
672
673 assert(image != (Image *) NULL);
674 assert(image->signature == MagickSignature);
675 if (image->debug != MagickFalse)
676 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
677 if ((image->storage_class == PseudoClass) && (image->colors <= 256))
678 return(MagickTrue);
679 if (image->storage_class == PseudoClass)
680 return(MagickFalse);
681 /*
682 Initialize color description tree.
683 */
684 cube_info=GetCubeInfo();
685 if (cube_info == (CubeInfo *) NULL)
686 {
687 (void) ThrowMagickException(exception,GetMagickModule(),
688 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
689 return(MagickFalse);
690 }
691 GetMagickPixelPacket(image,&pixel);
692 GetMagickPixelPacket(image,&target);
693 image_view=AcquireCacheView(image);
694 for (y=0; y < (long) image->rows; y++)
695 {
696 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
697 if (p == (const PixelPacket *) NULL)
698 break;
699 indexes=GetCacheViewVirtualIndexQueue(image_view);
700 for (x=0; x < (long) image->columns; x++)
701 {
702 /*
703 Start at the root and proceed level by level.
704 */
705 node_info=cube_info->root;
706 index=MaxTreeDepth-1;
707 for (level=1; level < MaxTreeDepth; level++)
708 {
709 SetMagickPixelPacket(image,p,indexes+x,&pixel);
710 id=ColorToNodeId(image,&pixel,index);
711 if (node_info->child[id] == (NodeInfo *) NULL)
712 {
713 node_info->child[id]=GetNodeInfo(cube_info,level);
714 if (node_info->child[id] == (NodeInfo *) NULL)
715 {
716 (void) ThrowMagickException(exception,GetMagickModule(),
717 ResourceLimitError,"MemoryAllocationFailed","`%s'",
718 image->filename);
719 break;
720 }
721 }
722 node_info=node_info->child[id];
723 index--;
724 }
725 if (level < MaxTreeDepth)
726 break;
727 for (i=0; i < (long) node_info->number_unique; i++)
728 {
729 SetMagickPixelPacket(image,&node_info->list[i].pixel,
730 &node_info->list[i].index,&target);
731 if (IsMagickColorEqual(&pixel,&target) != MagickFalse)
732 break;
733 }
734 if (i < (long) node_info->number_unique)
735 node_info->list[i].count++;
736 else
737 {
738 /*
739 Add this unique color to the color list.
740 */
741 if (node_info->number_unique == 0)
742 node_info->list=(ColorPacket *) AcquireMagickMemory(
743 sizeof(*node_info->list));
744 else
745 node_info->list=(ColorPacket *) ResizeQuantumMemory(node_info->list,
746 (size_t) (i+1),sizeof(*node_info->list));
747 if (node_info->list == (ColorPacket *) NULL)
748 {
749 (void) ThrowMagickException(exception,GetMagickModule(),
750 ResourceLimitError,"MemoryAllocationFailed","`%s'",
751 image->filename);
752 break;
753 }
754 node_info->list[i].pixel=(*p);
755 if ((image->colorspace == CMYKColorspace) ||
756 (image->storage_class == PseudoClass))
757 node_info->list[i].index=indexes[x];
758 node_info->list[i].count=1;
759 node_info->number_unique++;
760 cube_info->colors++;
761 if (cube_info->colors > MaximumUniqueColors)
762 break;
763 }
764 p++;
765 }
766 if (x < (long) image->columns)
767 break;
768 }
769 image_view=DestroyCacheView(image_view);
770 cube_info=DestroyCubeInfo(image,cube_info);
771 return(y < (long) image->rows ? MagickFalse : MagickTrue);
772}
773
774/*
775%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
776% %
777% %
778% %
779% I s P a l e t t e I m a g e %
780% %
781% %
782% %
783%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
784%
785% IsPaletteImage() returns MagickTrue if the image is PseudoClass and has 256
786% unique colors or less.
787%
788% The format of the IsPaletteImage method is:
789%
790% MagickBooleanType IsPaletteImage(const Image *image,
791% ExceptionInfo *exception)
792%
793% A description of each parameter follows.
794%
795% o image: the image.
796%
797% o exception: return any errors or warnings in this structure.
798%
799*/
800MagickExport MagickBooleanType IsPaletteImage(const Image *image,
801 ExceptionInfo *exception)
802{
803 CubeInfo
804 *cube_info;
805
806 long
807 y;
808
809 MagickPixelPacket
810 pixel,
811 target;
812
813 register const IndexPacket
814 *indexes;
815
816 register const PixelPacket
817 *p;
818
819 register long
820 x;
821
822 register NodeInfo
823 *node_info;
824
825 register long
826 i;
827
828 unsigned long
829 id,
830 index,
831 level;
832
833 CacheView
834 *image_view;
835
836 assert(image != (Image *) NULL);
837 assert(image->signature == MagickSignature);
838 if (image->debug != MagickFalse)
839 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
840 if ((image->storage_class == PseudoClass) && (image->colors <= 256))
841 return(MagickTrue);
842 if (image->storage_class == PseudoClass)
843 return(MagickFalse);
844 /*
845 Initialize color description tree.
846 */
847 cube_info=GetCubeInfo();
848 if (cube_info == (CubeInfo *) NULL)
849 {
850 (void) ThrowMagickException(exception,GetMagickModule(),
851 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
852 return(MagickFalse);
853 }
854 GetMagickPixelPacket(image,&pixel);
855 GetMagickPixelPacket(image,&target);
856 image_view=AcquireCacheView(image);
857 for (y=0; y < (long) image->rows; y++)
858 {
859 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
860 if (p == (const PixelPacket *) NULL)
861 break;
862 indexes=GetCacheViewVirtualIndexQueue(image_view);
863 for (x=0; x < (long) image->columns; x++)
864 {
865 /*
866 Start at the root and proceed level by level.
867 */
868 node_info=cube_info->root;
869 index=MaxTreeDepth-1;
870 for (level=1; level < MaxTreeDepth; level++)
871 {
872 SetMagickPixelPacket(image,p,indexes+x,&pixel);
873 id=ColorToNodeId(image,&pixel,index);
874 if (node_info->child[id] == (NodeInfo *) NULL)
875 {
876 node_info->child[id]=GetNodeInfo(cube_info,level);
877 if (node_info->child[id] == (NodeInfo *) NULL)
878 {
879 (void) ThrowMagickException(exception,GetMagickModule(),
880 ResourceLimitError,"MemoryAllocationFailed","`%s'",
881 image->filename);
882 break;
883 }
884 }
885 node_info=node_info->child[id];
886 index--;
887 }
888 if (level < MaxTreeDepth)
889 break;
890 for (i=0; i < (long) node_info->number_unique; i++)
891 {
892 SetMagickPixelPacket(image,&node_info->list[i].pixel,
893 &node_info->list[i].index,&target);
894 if (IsMagickColorEqual(&pixel,&target) != MagickFalse)
895 break;
896 }
897 if (i < (long) node_info->number_unique)
898 node_info->list[i].count++;
899 else
900 {
901 /*
902 Add this unique color to the color list.
903 */
904 if (node_info->number_unique == 0)
905 node_info->list=(ColorPacket *) AcquireMagickMemory(
906 sizeof(*node_info->list));
907 else
908 node_info->list=(ColorPacket *) ResizeQuantumMemory(node_info->list,
909 (size_t) (i+1),sizeof(*node_info->list));
910 if (node_info->list == (ColorPacket *) NULL)
911 {
912 (void) ThrowMagickException(exception,GetMagickModule(),
913 ResourceLimitError,"MemoryAllocationFailed","`%s'",
914 image->filename);
915 break;
916 }
917 node_info->list[i].pixel=(*p);
918 if ((image->colorspace == CMYKColorspace) ||
919 (image->storage_class == PseudoClass))
920 node_info->list[i].index=indexes[x];
921 node_info->list[i].count=1;
922 node_info->number_unique++;
923 cube_info->colors++;
924 if (cube_info->colors > 256)
925 break;
926 }
927 p++;
928 }
929 if (x < (long) image->columns)
930 break;
931 }
932 image_view=DestroyCacheView(image_view);
933 cube_info=DestroyCubeInfo(image,cube_info);
934 return(y < (long) image->rows ? MagickFalse : MagickTrue);
935}
936
937/*
cristy3ed852e2009-09-05 21:47:34 +0000938%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
939% %
940% %
941% %
942% M i n M a x S t r e t c h I m a g e %
943% %
944% %
945% %
946%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
947%
948% MinMaxStretchImage() uses the exact minimum and maximum values found in
949% each of the channels given, as the BlackPoint and WhitePoint to linearly
950% stretch the colors (and histogram) of the image. The stretch points are
951% also moved further inward by the adjustment values given.
952%
953% If the adjustment values are both zero this function is equivelent to a
954% perfect normalization (or autolevel) of the image.
955%
956% Each channel is stretched independantally of each other (producing color
957% distortion) unless the special 'SyncChannels' flag is also provided in the
958% channels setting. If this flag is present the minimum and maximum point
959% will be extracted from all the given channels, and those channels will be
960% stretched by exactly the same amount (preventing color distortion).
961%
962% The 'SyncChannels' is turned on in the 'DefaultChannels' setting by
963% default.
964%
965% The format of the MinMaxStretchImage method is:
966%
967% MagickBooleanType MinMaxStretchImage(Image *image,
968% const ChannelType channel, const double black_adjust,
969% const double white_adjust)
970%
971% A description of each parameter follows:
972%
973% o image: The image to auto-level
974%
975% o channel: The channels to auto-level. If the special 'SyncChannels'
976% flag is set, all the given channels are stretched by the same amount.
977%
978% o black_adjust, white_adjust: Move the Black/White Point inward
979% from the minimum and maximum points by this color value.
980%
981*/
cristy3ed852e2009-09-05 21:47:34 +0000982MagickExport MagickBooleanType MinMaxStretchImage(Image *image,
cristyf2e11662009-10-14 01:24:43 +0000983 const ChannelType channel,const double black_value,const double white_value)
cristy3ed852e2009-09-05 21:47:34 +0000984{
985 double
986 min,max;
987
988 MagickStatusType
989 status;
990
cristyf2e11662009-10-14 01:24:43 +0000991 if ((channel & SyncChannels) != 0)
cristy3ed852e2009-09-05 21:47:34 +0000992 {
993 /*
cristyf2e11662009-10-14 01:24:43 +0000994 Auto-level all channels equally.
cristy3ed852e2009-09-05 21:47:34 +0000995 */
cristyf2e11662009-10-14 01:24:43 +0000996 (void) GetImageChannelRange(image,channel,&min,&max,&image->exception);
997 min+=black_value;
998 max-=white_value;
999 return(LevelImageChannel(image,channel,min,max,1.0));
cristy3ed852e2009-09-05 21:47:34 +00001000 }
cristy3ed852e2009-09-05 21:47:34 +00001001 /*
cristyf2e11662009-10-14 01:24:43 +00001002 Auto-level each channel separately.
cristy3ed852e2009-09-05 21:47:34 +00001003 */
cristyf2e11662009-10-14 01:24:43 +00001004 status=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00001005 if ((channel & RedChannel) != 0)
1006 {
cristyf2e11662009-10-14 01:24:43 +00001007 (void) GetImageChannelRange(image,RedChannel,&min,&max,&image->exception);
1008 min+=black_value;
1009 max-=white_value;
1010 status&=LevelImageChannel(image,RedChannel,min,max,1.0);
cristy3ed852e2009-09-05 21:47:34 +00001011 }
1012 if ((channel & GreenChannel) != 0)
1013 {
cristyf2e11662009-10-14 01:24:43 +00001014 (void) GetImageChannelRange(image,GreenChannel,&min,&max,
1015 &image->exception);
1016 min+=black_value;
1017 max-=white_value;
1018 status&=LevelImageChannel(image,GreenChannel,min,max,1.0);
cristy3ed852e2009-09-05 21:47:34 +00001019 }
1020 if ((channel & BlueChannel) != 0)
1021 {
cristyf2e11662009-10-14 01:24:43 +00001022 (void) GetImageChannelRange(image,BlueChannel,&min,&max,
1023 &image->exception);
1024 min+=black_value;
1025 max-=white_value;
1026 status&=LevelImageChannel(image,BlueChannel,min,max,1.0);
cristy3ed852e2009-09-05 21:47:34 +00001027 }
1028 if (((channel & OpacityChannel) != 0) &&
cristyf2e11662009-10-14 01:24:43 +00001029 (image->matte == MagickTrue))
cristy3ed852e2009-09-05 21:47:34 +00001030 {
cristyf2e11662009-10-14 01:24:43 +00001031 (void) GetImageChannelRange(image,OpacityChannel,&min,&max,
1032 &image->exception);
1033 min+=black_value;
1034 max-=white_value;
1035 status&=LevelImageChannel(image,OpacityChannel,min,max,1.0);
cristy3ed852e2009-09-05 21:47:34 +00001036 }
1037 if (((channel & IndexChannel) != 0) &&
cristyf2e11662009-10-14 01:24:43 +00001038 (image->colorspace == CMYKColorspace))
cristy3ed852e2009-09-05 21:47:34 +00001039 {
cristyf2e11662009-10-14 01:24:43 +00001040 (void) GetImageChannelRange(image,IndexChannel,&min,&max,
1041 &image->exception);
1042 min+=black_value;
1043 max-=white_value;
1044 status&=LevelImageChannel(image,IndexChannel,min,max,1.0);
cristy3ed852e2009-09-05 21:47:34 +00001045 }
1046 return(status != 0 ? MagickTrue : MagickFalse);
1047}
1048
cristyf2e11662009-10-14 01:24:43 +00001049/*
1050%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1051% %
1052% %
1053% %
1054% G e t N u m b e r C o l o r s %
1055% %
1056% %
1057% %
1058%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1059%
1060% GetNumberColors() returns the number of unique colors in an image.
1061%
1062% The format of the GetNumberColors method is:
1063%
1064% unsigned long GetNumberColors(const Image *image,FILE *file,
1065% ExceptionInfo *exception)
1066%
1067% A description of each parameter follows.
1068%
1069% o image: the image.
1070%
1071% o file: Write a histogram of the color distribution to this file handle.
1072%
1073% o exception: return any errors or warnings in this structure.
1074%
1075*/
1076
1077#if defined(__cplusplus) || defined(c_plusplus)
1078extern "C" {
1079#endif
1080
1081static int HistogramCompare(const void *x,const void *y)
1082{
1083 const ColorPacket
1084 *color_1,
1085 *color_2;
1086
1087 color_1=(const ColorPacket *) x;
1088 color_2=(const ColorPacket *) y;
1089 if (color_2->pixel.red != color_1->pixel.red)
1090 return((int) color_1->pixel.red-(int) color_2->pixel.red);
1091 if (color_2->pixel.green != color_1->pixel.green)
1092 return((int) color_1->pixel.green-(int) color_2->pixel.green);
1093 if (color_2->pixel.blue != color_1->pixel.blue)
1094 return((int) color_1->pixel.blue-(int) color_2->pixel.blue);
1095 return((int) color_2->count-(int) color_1->count);
1096}
1097
1098#if defined(__cplusplus) || defined(c_plusplus)
1099}
1100#endif
1101
1102MagickExport unsigned long GetNumberColors(const Image *image,FILE *file,
1103 ExceptionInfo *exception)
1104{
1105#define HistogramImageTag "Histogram/Image"
1106
1107 char
1108 color[MaxTextExtent],
1109 hex[MaxTextExtent],
1110 tuple[MaxTextExtent];
1111
1112 ColorPacket
1113 *histogram;
1114
1115 MagickPixelPacket
1116 pixel;
1117
1118 register ColorPacket
1119 *p;
1120
1121 register long
1122 i;
1123
1124 unsigned long
1125 number_colors;
1126
1127 number_colors=0;
1128 if (file == (FILE *) NULL)
1129 {
1130 CubeInfo
1131 *cube_info;
1132
1133 cube_info=ClassifyImageColors(image,exception);
1134 if (cube_info != (CubeInfo *) NULL)
1135 number_colors=cube_info->colors;
1136 cube_info=DestroyCubeInfo(image,cube_info);
1137 return(number_colors);
1138 }
1139 histogram=GetImageHistogram(image,&number_colors,exception);
1140 if (histogram == (ColorPacket *) NULL)
1141 return(number_colors);
1142 qsort((void *) histogram,(size_t) number_colors,sizeof(*histogram),
1143 HistogramCompare);
1144 GetMagickPixelPacket(image,&pixel);
1145 p=histogram;
1146 for (i=0; i < (long) number_colors; i++)
1147 {
1148 SetMagickPixelPacket(image,&p->pixel,&p->index,&pixel);
1149 (void) CopyMagickString(tuple,"(",MaxTextExtent);
1150 ConcatenateColorComponent(&pixel,RedChannel,X11Compliance,tuple);
1151 (void) ConcatenateMagickString(tuple,",",MaxTextExtent);
1152 ConcatenateColorComponent(&pixel,GreenChannel,X11Compliance,tuple);
1153 (void) ConcatenateMagickString(tuple,",",MaxTextExtent);
1154 ConcatenateColorComponent(&pixel,BlueChannel,X11Compliance,tuple);
1155 if (pixel.colorspace == CMYKColorspace)
1156 {
1157 (void) ConcatenateMagickString(tuple,",",MaxTextExtent);
1158 ConcatenateColorComponent(&pixel,IndexChannel,X11Compliance,tuple);
1159 }
1160 if (pixel.matte != MagickFalse)
1161 {
1162 (void) ConcatenateMagickString(tuple,",",MaxTextExtent);
1163 ConcatenateColorComponent(&pixel,OpacityChannel,X11Compliance,tuple);
1164 }
1165 (void) ConcatenateMagickString(tuple,")",MaxTextExtent);
1166 (void) QueryMagickColorname(image,&pixel,SVGCompliance,color,exception);
1167 GetColorTuple(&pixel,MagickTrue,hex);
1168 (void) fprintf(file,MagickSizeFormat,p->count);
1169 (void) fprintf(file,": %s %s %s\n",tuple,hex,color);
1170 if ((image->progress_monitor != (MagickProgressMonitor) NULL) &&
1171 (QuantumTick(i,number_colors) != MagickFalse))
1172 (void) image->progress_monitor(HistogramImageTag,i,number_colors,
1173 image->client_data);
1174 p++;
1175 }
1176 (void) fflush(file);
1177 histogram=(ColorPacket *) RelinquishMagickMemory(histogram);
1178 return(number_colors);
1179}
1180
1181/*
1182%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1183% %
1184% %
1185% %
1186% U n i q u e I m a g e C o l o r s %
1187% %
1188% %
1189% %
1190%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1191%
1192% UniqueImageColors() returns the unique colors of an image.
1193%
1194% The format of the UniqueImageColors method is:
1195%
1196% Image *UniqueImageColors(const Image *image,ExceptionInfo *exception)
1197%
1198% A description of each parameter follows.
1199%
1200% o image: the image.
1201%
1202% o exception: return any errors or warnings in this structure.
1203%
1204*/
1205
1206static void UniqueColorsToImage(Image *image,CubeInfo *cube_info,
1207 const NodeInfo *node_info,ExceptionInfo *exception)
1208{
1209#define UniqueColorsImageTag "UniqueColors/Image"
1210
1211 register long
1212 i;
1213
1214 unsigned long
1215 number_children;
1216
1217 /*
1218 Traverse any children.
1219 */
1220 number_children=image->matte == MagickFalse ? 8UL : 16UL;
1221 for (i=0; i < (long) number_children; i++)
1222 if (node_info->child[i] != (NodeInfo *) NULL)
1223 UniqueColorsToImage(image,cube_info,node_info->child[i],exception);
1224 if (node_info->level == (MaxTreeDepth-1))
1225 {
1226 register ColorPacket
1227 *p;
1228
1229 register IndexPacket
1230 *__restrict indexes;
1231
1232 register PixelPacket
1233 *__restrict q;
1234
1235 p=node_info->list;
1236 for (i=0; i < (long) node_info->number_unique; i++)
1237 {
1238 q=QueueAuthenticPixels(image,cube_info->x,0,1,1,exception);
1239 if (q == (PixelPacket *) NULL)
1240 continue;
1241 indexes=GetAuthenticIndexQueue(image);
1242 *q=p->pixel;
1243 if (image->colorspace == CMYKColorspace)
1244 *indexes=p->index;
1245 if (SyncAuthenticPixels(image,exception) == MagickFalse)
1246 break;
1247 cube_info->x++;
1248 p++;
1249 }
1250 if ((image->progress_monitor != (MagickProgressMonitor) NULL) &&
1251 (QuantumTick(cube_info->progress,cube_info->colors) != MagickFalse))
1252 (void) image->progress_monitor(UniqueColorsImageTag,cube_info->progress,
1253 cube_info->colors,image->client_data);
1254 cube_info->progress++;
1255 }
1256}
1257
1258MagickExport Image *UniqueImageColors(const Image *image,
1259 ExceptionInfo *exception)
1260{
1261 CubeInfo
1262 *cube_info;
1263
1264 Image
1265 *unique_image;
1266
1267 cube_info=ClassifyImageColors(image,exception);
1268 if (cube_info == (CubeInfo *) NULL)
1269 return((Image *) NULL);
1270 unique_image=CloneImage(image,cube_info->colors,1,MagickTrue,exception);
1271 if (unique_image == (Image *) NULL)
1272 return(unique_image);
1273 if (SetImageStorageClass(unique_image,DirectClass) == MagickFalse)
1274 {
1275 InheritException(exception,&unique_image->exception);
1276 unique_image=DestroyImage(unique_image);
1277 return((Image *) NULL);
1278 }
1279 UniqueColorsToImage(unique_image,cube_info,cube_info->root,exception);
1280 if (cube_info->colors < MaxColormapSize)
1281 {
1282 QuantizeInfo
1283 *quantize_info;
1284
1285 quantize_info=AcquireQuantizeInfo((ImageInfo *) NULL);
1286 quantize_info->number_colors=MaxColormapSize;
1287 quantize_info->dither=MagickFalse;
1288 quantize_info->tree_depth=8;
1289 (void) QuantizeImage(quantize_info,unique_image);
1290 quantize_info=DestroyQuantizeInfo(quantize_info);
1291 }
1292 cube_info=DestroyCubeInfo(image,cube_info);
1293 return(unique_image);
1294}