43#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/histogram.h"
50#include "MagickCore/image.h"
51#include "MagickCore/linked-list.h"
52#include "MagickCore/list.h"
53#include "MagickCore/locale_.h"
54#include "MagickCore/memory_.h"
55#include "MagickCore/monitor-private.h"
56#include "MagickCore/pixel-accessor.h"
57#include "MagickCore/prepress.h"
58#include "MagickCore/quantize.h"
59#include "MagickCore/registry.h"
60#include "MagickCore/semaphore.h"
61#include "MagickCore/splay-tree.h"
62#include "MagickCore/statistic.h"
63#include "MagickCore/string_.h"
69#define HNodesInAList 1536
163static inline size_t ColorToNodeId(
const PixelInfo *pixel,
size_t index)
169 ((ScaleQuantumToChar(ClampToQuantum(pixel->red)) >> index) & 0x01) |
170 ((ScaleQuantumToChar(ClampToQuantum(pixel->green)) >> index) & 0x01) << 1 |
171 ((ScaleQuantumToChar(ClampToQuantum(pixel->blue)) >> index) & 0x01) << 2);
172 if (pixel->alpha_trait != UndefinedPixelTrait)
173 id|=((size_t) ((ScaleQuantumToChar(ClampToQuantum(pixel->alpha)) >> index) &
178static inline MagickBooleanType IsPixelInfoColorMatch(
185 alpha=p->alpha_trait == UndefinedPixelTrait ? (MagickRealType) OpaqueAlpha :
187 beta=q->alpha_trait == UndefinedPixelTrait ? (MagickRealType) OpaqueAlpha :
189 if (AbsolutePixelValue(alpha-beta) >= MagickEpsilon)
191 if (AbsolutePixelValue(p->red-q->red) >= MagickEpsilon)
193 if (AbsolutePixelValue(p->green-q->green) >= MagickEpsilon)
195 if (AbsolutePixelValue(p->blue-q->blue) >= MagickEpsilon)
197 if (p->colorspace == CMYKColorspace)
199 if (AbsolutePixelValue(p->black-q->black) >= MagickEpsilon)
209#define EvaluateImageTag " Compute image colors... "
244 assert(image != (
const Image *) NULL);
245 assert(image->signature == MagickCoreSignature);
246 if (IsEventLogging() != MagickFalse)
247 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
248 cube_info=GetHCubeInfo();
251 (void) ThrowMagickException(exception,GetMagickModule(),
252 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",image->filename);
255 GetPixelInfo(image,&pixel);
256 image_view=AcquireVirtualCacheView(image,exception);
257 for (y=0; y < (ssize_t) image->rows; y++)
259 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
260 if (p == (
const Quantum *) NULL)
262 for (x=0; x < (ssize_t) image->columns; x++)
267 node_info=cube_info->root;
268 index=MaxTreeDepth-1;
269 for (level=1; level < MaxTreeDepth; level++)
271 GetPixelInfoPixel(image,p,&pixel);
272 id=ColorToNodeId(&pixel,index);
273 if (node_info->child[
id] == (
HNodeInfo *) NULL)
275 node_info->child[id]=GetHNodeInfo(cube_info,level);
276 if (node_info->child[
id] == (
HNodeInfo *) NULL)
278 (void) ThrowMagickException(exception,GetMagickModule(),
279 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",
284 node_info=node_info->child[id];
287 for (i=0; i < (ssize_t) node_info->number_unique; i++)
288 if (IsPixelInfoColorMatch(&pixel,node_info->list+i) != MagickFalse)
290 if (i < (ssize_t) node_info->number_unique)
291 node_info->list[i].count++;
294 if (node_info->number_unique == 0)
297 node_info->list=(
PixelInfo *) AcquireQuantumMemory(
298 node_info->extent,
sizeof(*node_info->list));
301 if (i >= (ssize_t) node_info->extent)
303 node_info->extent<<=1;
304 node_info->list=(
PixelInfo *) ResizeQuantumMemory(
305 node_info->list,node_info->extent,
sizeof(*node_info->list));
307 if (node_info->list == (
PixelInfo *) NULL)
309 (void) ThrowMagickException(exception,GetMagickModule(),
310 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",
314 node_info->list[i]=pixel;
315 node_info->list[i].red=(double) GetPixelRed(image,p);
316 node_info->list[i].green=(double) GetPixelGreen(image,p);
317 node_info->list[i].blue=(double) GetPixelBlue(image,p);
318 if (image->colorspace == CMYKColorspace)
319 node_info->list[i].black=(double) GetPixelBlack(image,p);
320 node_info->list[i].alpha=(double) GetPixelAlpha(image,p);
321 node_info->list[i].count=1;
322 node_info->number_unique++;
325 p+=GetPixelChannels(image);
327 proceed=SetImageProgress(image,EvaluateImageTag,(MagickOffsetType) y,
329 if (proceed == MagickFalse)
332 image_view=DestroyCacheView(image_view);
366static void DefineImageHistogram(
const Image *image,
HNodeInfo *node_info,
378 number_children=image->alpha_trait == UndefinedPixelTrait ? 8UL : 16UL;
379 for (i=0; i < (ssize_t) number_children; i++)
380 if (node_info->child[i] != (
HNodeInfo *) NULL)
381 DefineImageHistogram(image,node_info->child[i],histogram);
382 if (node_info->level == (MaxTreeDepth-1))
388 for (i=0; i < (ssize_t) node_info->number_unique; i++)
429 DestroyColorCube(image,cube_info->root);
432 nodes=cube_info->node_queue->next;
433 cube_info->node_queue=(
HNodes *)
434 RelinquishMagickMemory(cube_info->node_queue);
435 cube_info->node_queue=nodes;
436 }
while (cube_info->node_queue != (
HNodes *) NULL);
437 return((
HCubeInfo *) RelinquishMagickMemory(cube_info));
466static void DestroyColorCube(
const Image *image,
HNodeInfo *node_info)
477 number_children=image->alpha_trait == UndefinedPixelTrait ? 8UL : 16UL;
478 for (i=0; i < (ssize_t) number_children; i++)
479 if (node_info->child[i] != (
HNodeInfo *) NULL)
480 DestroyColorCube(image,node_info->child[i]);
481 if (node_info->list != (
PixelInfo *) NULL)
482 node_info->list=(
PixelInfo *) RelinquishMagickMemory(node_info->list);
515 cube_info=(
HCubeInfo *) AcquireMagickMemory(
sizeof(*cube_info));
518 (void) memset(cube_info,0,
sizeof(*cube_info));
522 cube_info->root=GetHNodeInfo(cube_info,0);
523 if (cube_info->root == (
HNodeInfo *) NULL)
566 cube_info=ClassifyImageColors(image,exception);
569 histogram=(
PixelInfo *) AcquireQuantumMemory((
size_t) cube_info->colors+1,
572 (void) ThrowMagickException(exception,GetMagickModule(),
573 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",image->filename);
579 *number_colors=cube_info->colors;
581 DefineImageHistogram(image,cube_info->root,&root);
583 cube_info=DestroyHCubeInfo(image,cube_info);
618 if (cube_info->free_nodes == 0)
626 nodes=(
HNodes *) AcquireMagickMemory(
sizeof(*nodes));
627 if (nodes == (
HNodes *) NULL)
629 nodes->next=cube_info->node_queue;
630 cube_info->node_queue=nodes;
631 cube_info->node_info=nodes->nodes;
632 cube_info->free_nodes=HNodesInAList;
634 cube_info->free_nodes--;
635 node_info=cube_info->node_info++;
636 (void) memset(node_info,0,
sizeof(*node_info));
637 node_info->level=level;
670static MagickBooleanType CheckImageColors(
const Image *image,
698 if (image->storage_class == PseudoClass)
699 return((image->colors <= max_colors) ? MagickTrue : MagickFalse);
703 cube_info=GetHCubeInfo();
706 (void) ThrowMagickException(exception,GetMagickModule(),
707 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",image->filename);
710 GetPixelInfo(image,&pixel);
711 GetPixelInfo(image,&target);
712 image_view=AcquireVirtualCacheView(image,exception);
713 for (y=0; y < (ssize_t) image->rows; y++)
718 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
719 if (p == (
const Quantum *) NULL)
721 for (x=0; x < (ssize_t) image->columns; x++)
726 node_info=cube_info->root;
727 index=MaxTreeDepth-1;
728 for (level=1; level < MaxTreeDepth; level++)
730 GetPixelInfoPixel(image,p,&pixel);
731 id=ColorToNodeId(&pixel,index);
732 if (node_info->child[
id] == (
HNodeInfo *) NULL)
734 node_info->child[id]=GetHNodeInfo(cube_info,level);
735 if (node_info->child[
id] == (
HNodeInfo *) NULL)
737 (void) ThrowMagickException(exception,GetMagickModule(),
738 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",
743 node_info=node_info->child[id];
746 if (level < MaxTreeDepth)
748 for (i=0; i < (ssize_t) node_info->number_unique; i++)
750 target=node_info->list[i];
751 if (IsPixelInfoColorMatch(&pixel,&target) != MagickFalse)
754 if (i < (ssize_t) node_info->number_unique)
755 node_info->list[i].count++;
761 if (node_info->list == (
PixelInfo *) NULL)
762 node_info->list=(
PixelInfo *) AcquireQuantumMemory(1,
763 sizeof(*node_info->list));
765 node_info->list=(
PixelInfo *) ResizeQuantumMemory(node_info->list,
766 (
size_t) (i+1),
sizeof(*node_info->list));
767 if (node_info->list == (
PixelInfo *) NULL)
769 (void) ThrowMagickException(exception,GetMagickModule(),
770 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",
774 GetPixelInfo(image,&node_info->list[i]);
775 node_info->list[i].red=(double) GetPixelRed(image,p);
776 node_info->list[i].green=(double) GetPixelGreen(image,p);
777 node_info->list[i].blue=(double) GetPixelBlue(image,p);
778 if (image->colorspace == CMYKColorspace)
779 node_info->list[i].black=(double) GetPixelBlack(image,p);
780 node_info->list[i].alpha=(double) GetPixelAlpha(image,p);
781 node_info->list[i].count=1;
782 node_info->number_unique++;
784 if (cube_info->colors > max_colors)
787 p+=GetPixelChannels(image);
789 if (x < (ssize_t) image->columns)
792 image_view=DestroyCacheView(image_view);
793 cube_info=DestroyHCubeInfo(image,cube_info);
794 return(y < (ssize_t) image->rows ? MagickFalse : MagickTrue);
797MagickExport MagickBooleanType IdentifyPaletteImage(
const Image *image,
800 assert(image != (
Image *) NULL);
801 assert(image->signature == MagickCoreSignature);
802 if (IsEventLogging() != MagickFalse)
803 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
804 return(CheckImageColors(image,256,exception));
833MagickExport MagickBooleanType IsHistogramImage(
const Image *image,
836#define MaximumUniqueColors 1024
838 assert(image != (
Image *) NULL);
839 assert(image->signature == MagickCoreSignature);
840 if (IsEventLogging() != MagickFalse)
841 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
842 return(CheckImageColors(image,MaximumUniqueColors,exception));
868MagickExport MagickBooleanType IsPaletteImage(
const Image *image)
870 assert(image != (
Image *) NULL);
871 assert(image->signature == MagickCoreSignature);
872 if (IsEventLogging() != MagickFalse)
873 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
874 if (image->storage_class != PseudoClass)
876 return((image->colors <= 256) ? MagickTrue : MagickFalse);
927MagickExport MagickBooleanType MinMaxStretchImage(
Image *image,
928 const double black,
const double white,
const double gamma,
942 if (image->channel_mask == AllChannels)
947 (void) GetImageRange(image,&min,&max,exception);
950 if (fabs(min-max) >= MagickEpsilon)
951 status&=(MagickStatusType) LevelImage(image,min,max,gamma,exception);
952 return(status != 0 ? MagickTrue : MagickFalse);
957 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
962 PixelChannel channel = GetPixelChannelChannel(image,i);
963 PixelTrait traits = GetPixelChannelTraits(image,channel);
964 if ((traits & UpdatePixelTrait) == 0)
966 channel_mask=SetImageChannelMask(image,(ChannelType) (1UL << i));
967 status&=(MagickStatusType) GetImageRange(image,&min,&max,exception);
970 if (fabs(min-max) >= MagickEpsilon)
971 status&=(MagickStatusType) LevelImage(image,min,max,gamma,exception);
972 (void) SetImageChannelMask(image,channel_mask);
974 return(status != 0 ? MagickTrue : MagickFalse);
1005#if defined(__cplusplus) || defined(c_plusplus)
1009static int HistogramCompare(
const void *x,
const void *y)
1017 if (color_2->red != color_1->red)
1018 return((
int) ((ssize_t) color_1->red-(ssize_t) color_2->red));
1019 if (color_2->green != color_1->green)
1020 return((
int) ((ssize_t) color_1->green-(ssize_t) color_2->green));
1021 if (color_2->blue != color_1->blue)
1022 return((
int) ((ssize_t) color_1->blue-(ssize_t) color_2->blue));
1023 return((
int) ((ssize_t) color_2->count-(ssize_t) color_1->count));
1026#if defined(__cplusplus) || defined(c_plusplus)
1030MagickExport
size_t GetNumberColors(
const Image *image,FILE *file,
1033#define HistogramImageTag "Histogram/Image"
1036 color[MagickPathExtent],
1037 count[MagickPathExtent],
1038 hex[MagickPathExtent],
1039 tuple[MagickPathExtent];
1060 if (file == (FILE *) NULL)
1065 cube_info=ClassifyImageColors(image,exception);
1068 number_colors=cube_info->colors;
1069 cube_info=DestroyHCubeInfo(image,cube_info);
1071 return(number_colors);
1073 histogram=GetImageHistogram(image,&number_colors,exception);
1075 return(number_colors);
1076 qsort((
void *) histogram,(
size_t) number_colors,
sizeof(*histogram),
1078 GetPixelInfo(image,&pixel);
1081 for (i=0; i < (ssize_t) number_colors; i++)
1084 (void) CopyMagickString(tuple,
"(",MagickPathExtent);
1085 ConcatenateColorComponent(&pixel,RedPixelChannel,NoCompliance,tuple);
1086 (void) ConcatenateMagickString(tuple,
",",MagickPathExtent);
1087 ConcatenateColorComponent(&pixel,GreenPixelChannel,NoCompliance,tuple);
1088 (void) ConcatenateMagickString(tuple,
",",MagickPathExtent);
1089 ConcatenateColorComponent(&pixel,BluePixelChannel,NoCompliance,tuple);
1090 if (pixel.colorspace == CMYKColorspace)
1092 (void) ConcatenateMagickString(tuple,
",",MagickPathExtent);
1093 ConcatenateColorComponent(&pixel,BlackPixelChannel,NoCompliance,
1096 if (pixel.alpha_trait != UndefinedPixelTrait)
1098 (void) ConcatenateMagickString(tuple,
",",MagickPathExtent);
1099 ConcatenateColorComponent(&pixel,AlphaPixelChannel,NoCompliance,
1102 (void) ConcatenateMagickString(tuple,
")",MagickPathExtent);
1103 (void) QueryColorname(image,&pixel,SVGCompliance,color,exception);
1104 GetColorTuple(&pixel,MagickTrue,hex);
1105 (void) FormatLocaleString(count,MagickPathExtent,
"%10.20g:",(
double)
1106 ((MagickOffsetType) p->count));
1107 (void) FormatLocaleFile(file,
" %s %s %s %s\n",count,tuple,hex,color);
1108 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1113 proceed=SetImageProgress(image,HistogramImageTag,(MagickOffsetType) i,
1115 if (proceed == MagickFalse)
1120 (void) fflush(file);
1121 histogram=(
PixelInfo *) RelinquishMagickMemory(histogram);
1122 if (status == MagickFalse)
1124 return(number_colors);
1152static void UniqueColorsToImage(
Image *unique_image,
CacheView *unique_view,
1155#define UniqueColorsImageTag "UniqueColors/Image"
1169 number_children=unique_image->alpha_trait == UndefinedPixelTrait ? 8UL : 16UL;
1170 for (i=0; i < (ssize_t) number_children; i++)
1171 if (node_info->child[i] != (
HNodeInfo *) NULL)
1172 UniqueColorsToImage(unique_image,unique_view,cube_info,
1173 node_info->child[i],exception);
1174 if (node_info->level == (MaxTreeDepth-1))
1184 for (i=0; i < (ssize_t) node_info->number_unique; i++)
1186 q=QueueCacheViewAuthenticPixels(unique_view,cube_info->x,0,1,1,
1188 if (q == (Quantum *) NULL)
1190 SetPixelRed(unique_image,ClampToQuantum(p->red),q);
1191 SetPixelGreen(unique_image,ClampToQuantum(p->green),q);
1192 SetPixelBlue(unique_image,ClampToQuantum(p->blue),q);
1193 SetPixelAlpha(unique_image,ClampToQuantum(p->alpha),q);
1194 if (unique_image->colorspace == CMYKColorspace)
1195 SetPixelBlack(unique_image,ClampToQuantum(p->black),q);
1196 if (SyncCacheViewAuthenticPixels(unique_view,exception) == MagickFalse)
1201 if (unique_image->progress_monitor != (MagickProgressMonitor) NULL)
1206 proceed=SetImageProgress(unique_image,UniqueColorsImageTag,
1207 cube_info->progress,cube_info->colors);
1208 if (proceed == MagickFalse)
1211 cube_info->progress++;
1212 if (status == MagickFalse)
1217MagickExport
Image *UniqueImageColors(
const Image *image,
1229 cube_info=ClassifyImageColors(image,exception);
1231 return((
Image *) NULL);
1232 unique_image=CloneImage(image,cube_info->colors,1,MagickTrue,exception);
1233 if (unique_image == (
Image *) NULL)
1234 return(unique_image);
1235 if (SetImageStorageClass(unique_image,DirectClass,exception) == MagickFalse)
1237 unique_image=DestroyImage(unique_image);
1238 return((
Image *) NULL);
1240 unique_view=AcquireAuthenticCacheView(unique_image,exception);
1241 UniqueColorsToImage(unique_image,unique_view,cube_info,cube_info->root,
1243 unique_view=DestroyCacheView(unique_view);
1244 cube_info=DestroyHCubeInfo(image,cube_info);
1245 return(unique_image);