43#include "magick/studio.h"
44#include "magick/cache-view.h"
45#include "magick/color-private.h"
46#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/locale_.h"
54#include "magick/memory_.h"
55#include "magick/monitor-private.h"
56#include "magick/pixel-private.h"
57#include "magick/prepress.h"
58#include "magick/quantize.h"
59#include "magick/registry.h"
60#include "magick/semaphore.h"
61#include "magick/splay-tree.h"
62#include "magick/statistic.h"
63#include "magick/string_.h"
69#define HNodesInAList 1536
163static inline size_t ColorToHNodeId(
const Image *image,
170 ((ScaleQuantumToChar(ClampToQuantum(pixel->red)) >> index) & 0x01) |
171 ((ScaleQuantumToChar(ClampToQuantum(pixel->green)) >> index) & 0x01) << 1 |
172 ((ScaleQuantumToChar(ClampToQuantum(pixel->blue)) >> index) & 0x01) << 2);
173 if (image->matte != MagickFalse)
174 id|=((ScaleQuantumToChar(ClampToQuantum(pixel->opacity)) >> index) &
186 alpha=p->matte == MagickFalse ? (MagickRealType) OpaqueOpacity : p->opacity;
187 beta=q->matte == MagickFalse ? (MagickRealType) OpaqueOpacity : q->opacity;
188 if (AbsolutePixelValue(alpha-beta) >= MagickEpsilon)
190 if ((AbsolutePixelValue(alpha-(MagickRealType) TransparentOpacity) < MagickEpsilon) ||
191 (AbsolutePixelValue(beta-(MagickRealType) TransparentOpacity) < MagickEpsilon))
193 if (AbsolutePixelValue(p->red-q->red) >= MagickEpsilon)
195 if (AbsolutePixelValue(p->green-q->green) >= MagickEpsilon)
197 if (AbsolutePixelValue(p->blue-q->blue) >= MagickEpsilon)
199 if (p->colorspace == CMYKColorspace)
201 if (AbsolutePixelValue(p->index-q->index) >= MagickEpsilon)
211#define EvaluateImageTag " Compute image colors... "
250 assert(image != (
const Image *) NULL);
251 assert(image->signature == MagickCoreSignature);
252 if (IsEventLogging() != MagickFalse)
253 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
254 cube_info=GetHCubeInfo();
257 (void) ThrowMagickException(exception,GetMagickModule(),
258 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",image->filename);
261 GetMagickPixelPacket(image,&pixel);
262 GetMagickPixelPacket(image,&target);
263 image_view=AcquireVirtualCacheView(image,exception);
264 for (y=0; y < (ssize_t) image->rows; y++)
266 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
269 indexes=GetCacheViewVirtualIndexQueue(image_view);
270 for (x=0; x < (ssize_t) image->columns; x++)
275 node_info=cube_info->root;
276 index=MaxTreeDepth-1;
277 for (level=1; level < MaxTreeDepth; level++)
279 SetMagickPixelPacket(image,p,indexes+x,&pixel);
280 id=ColorToHNodeId(image,&pixel,index);
281 if (node_info->child[
id] == (
HNodeInfo *) NULL)
283 node_info->child[id]=GetHNodeInfo(cube_info,level);
284 if (node_info->child[
id] == (
HNodeInfo *) NULL)
286 (void) ThrowMagickException(exception,GetMagickModule(),
287 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",
292 node_info=node_info->child[id];
295 for (i=0; i < (ssize_t) node_info->number_unique; i++)
297 SetMagickPixelPacket(image,&node_info->list[i].pixel,
298 &node_info->list[i].index,&target);
299 if (IsMagickColorMatch(&pixel,&target) != MagickFalse)
302 if (i < (ssize_t) node_info->number_unique)
303 node_info->list[i].count++;
306 if (node_info->number_unique == 0)
309 node_info->list=(
ColorPacket *) AcquireQuantumMemory(
310 node_info->extent,
sizeof(*node_info->list));
313 if (i >= (ssize_t) node_info->extent)
315 node_info->extent<<=1;
316 node_info->list=(
ColorPacket *) ResizeQuantumMemory(
317 node_info->list,node_info->extent,
sizeof(*node_info->list));
321 (void) ThrowMagickException(exception,GetMagickModule(),
322 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",
326 node_info->list[i].pixel=(*p);
327 if ((image->colorspace == CMYKColorspace) ||
328 (image->storage_class == PseudoClass))
329 node_info->list[i].index=GetPixelIndex(indexes+x);
330 node_info->list[i].count=1;
331 node_info->number_unique++;
336 proceed=SetImageProgress(image,EvaluateImageTag,(MagickOffsetType) y,
338 if (proceed == MagickFalse)
341 image_view=DestroyCacheView(image_view);
375static void DefineImageHistogram(
const Image *image,
HNodeInfo *node_info,
387 number_children=image->matte == MagickFalse ? 8UL : 16UL;
388 for (i=0; i < (ssize_t) number_children; i++)
389 if (node_info->child[i] != (
HNodeInfo *) NULL)
390 DefineImageHistogram(image,node_info->child[i],histogram);
391 if (node_info->level == (MaxTreeDepth-1))
397 for (i=0; i < (ssize_t) node_info->number_unique; i++)
399 (*histogram)->pixel=p->pixel;
400 (*histogram)->index=p->index;
401 (*histogram)->count=p->count;
440 DestroyColorCube(image,cube_info->root);
443 nodes=cube_info->node_queue->next;
444 cube_info->node_queue=(
HNodes *)
445 RelinquishMagickMemory(cube_info->node_queue);
446 cube_info->node_queue=nodes;
447 }
while (cube_info->node_queue != (
HNodes *) NULL);
448 return((
HCubeInfo *) RelinquishMagickMemory(cube_info));
477static void DestroyColorCube(
const Image *image,
HNodeInfo *node_info)
488 number_children=image->matte == MagickFalse ? 8UL : 16UL;
489 for (i=0; i < (ssize_t) number_children; i++)
490 if (node_info->child[i] != (
HNodeInfo *) NULL)
491 DestroyColorCube(image,node_info->child[i]);
493 node_info->list=(
ColorPacket *) RelinquishMagickMemory(node_info->list);
526 cube_info=(
HCubeInfo *) AcquireMagickMemory(
sizeof(*cube_info));
529 (void) memset(cube_info,0,
sizeof(*cube_info));
533 cube_info->root=GetHNodeInfo(cube_info,0);
534 if (cube_info->root == (
HNodeInfo *) NULL)
577 cube_info=ClassifyImageColors(image,exception);
580 histogram=(
ColorPacket *) AcquireQuantumMemory((
size_t)
581 cube_info->colors+1,
sizeof(*histogram));
583 (void) ThrowMagickException(exception,GetMagickModule(),
584 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",image->filename);
590 *number_colors=cube_info->colors;
592 DefineImageHistogram(image,cube_info->root,&root);
594 cube_info=DestroyHCubeInfo(image,cube_info);
629 if (cube_info->free_nodes == 0)
637 nodes=(
HNodes *) AcquireMagickMemory(
sizeof(*nodes));
638 if (nodes == (
HNodes *) NULL)
640 nodes->next=cube_info->node_queue;
641 cube_info->node_queue=nodes;
642 cube_info->node_info=nodes->nodes;
643 cube_info->free_nodes=HNodesInAList;
645 cube_info->free_nodes--;
646 node_info=cube_info->node_info++;
647 (void) memset(node_info,0,
sizeof(*node_info));
648 node_info->level=level;
679static MagickBooleanType CheckImageColors(
const Image *image,
715 if (image->storage_class == PseudoClass)
716 return((image->colors <= max_colors) ? MagickTrue : MagickFalse);
720 cube_info=GetHCubeInfo();
723 (void) ThrowMagickException(exception,GetMagickModule(),
724 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",image->filename);
727 GetMagickPixelPacket(image,&pixel);
728 GetMagickPixelPacket(image,&target);
729 image_view=AcquireVirtualCacheView(image,exception);
730 for (y=0; y < (ssize_t) image->rows; y++)
732 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
735 indexes=GetCacheViewVirtualIndexQueue(image_view);
736 if (indexes == (
const IndexPacket *) NULL)
738 for (x=0; x < (ssize_t) image->columns; x++)
743 node_info=cube_info->root;
744 index=MaxTreeDepth-1;
745 for (level=1; level < MaxTreeDepth; level++)
747 SetMagickPixelPacket(image,p,indexes+x,&pixel);
748 id=ColorToHNodeId(image,&pixel,index);
749 if (node_info->child[
id] == (
HNodeInfo *) NULL)
751 node_info->child[id]=GetHNodeInfo(cube_info,level);
752 if (node_info->child[
id] == (
HNodeInfo *) NULL)
754 (void) ThrowMagickException(exception,GetMagickModule(),
755 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",
760 node_info=node_info->child[id];
763 if (level < MaxTreeDepth)
765 for (i=0; i < (ssize_t) node_info->number_unique; i++)
767 SetMagickPixelPacket(image,&node_info->list[i].pixel,
768 &node_info->list[i].index,&target);
769 if (IsMagickColorMatch(&pixel,&target) != MagickFalse)
772 if (i < (ssize_t) node_info->number_unique)
773 node_info->list[i].count++;
780 node_info->list=(
ColorPacket *) AcquireQuantumMemory(1,
781 sizeof(*node_info->list));
783 node_info->list=(
ColorPacket *) ResizeQuantumMemory(node_info->list,
784 (
size_t) (i+1),
sizeof(*node_info->list));
787 (void) ThrowMagickException(exception,GetMagickModule(),
788 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",
792 node_info->list[i].pixel=(*p);
793 if ((image->colorspace == CMYKColorspace) ||
794 (image->storage_class == PseudoClass))
795 node_info->list[i].index=GetPixelIndex(indexes+x);
796 node_info->list[i].count=1;
797 node_info->number_unique++;
799 if (cube_info->colors > max_colors)
804 if (x < (ssize_t) image->columns)
807 image_view=DestroyCacheView(image_view);
808 cube_info=DestroyHCubeInfo(image,cube_info);
809 return(y < (ssize_t) image->rows ? MagickFalse : MagickTrue);
812MagickExport MagickBooleanType IdentifyPaletteImage(
const Image *image,
815 assert(image != (
Image *) NULL);
816 assert(image->signature == MagickCoreSignature);
817 if (IsEventLogging() != MagickFalse)
818 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
819 return(CheckImageColors(image,exception,256));
848MagickExport MagickBooleanType IsHistogramImage(
const Image *image,
851#define MaximumUniqueColors 1024
886 assert(image != (
Image *) NULL);
887 assert(image->signature == MagickCoreSignature);
888 if (IsEventLogging() != MagickFalse)
889 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
890 if ((image->storage_class == PseudoClass) &&
891 (image->colors <= MaximumUniqueColors))
893 if (image->storage_class == PseudoClass)
898 cube_info=GetHCubeInfo();
901 (void) ThrowMagickException(exception,GetMagickModule(),
902 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",image->filename);
905 GetMagickPixelPacket(image,&pixel);
906 GetMagickPixelPacket(image,&target);
907 image_view=AcquireVirtualCacheView(image,exception);
908 for (y=0; y < (ssize_t) image->rows; y++)
910 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
913 indexes=GetCacheViewVirtualIndexQueue(image_view);
914 for (x=0; x < (ssize_t) image->columns; x++)
919 node_info=cube_info->root;
920 index=MaxTreeDepth-1;
921 for (level=1; level < MaxTreeDepth; level++)
923 SetMagickPixelPacket(image,p,indexes+x,&pixel);
924 id=ColorToHNodeId(image,&pixel,index);
925 if (node_info->child[
id] == (
HNodeInfo *) NULL)
927 node_info->child[id]=GetHNodeInfo(cube_info,level);
928 if (node_info->child[
id] == (
HNodeInfo *) NULL)
930 (void) ThrowMagickException(exception,GetMagickModule(),
931 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",
936 node_info=node_info->child[id];
939 if (level < MaxTreeDepth)
941 for (i=0; i < (ssize_t) node_info->number_unique; i++)
943 SetMagickPixelPacket(image,&node_info->list[i].pixel,
944 &node_info->list[i].index,&target);
945 if (IsMagickColorMatch(&pixel,&target) != MagickFalse)
948 if (i < (ssize_t) node_info->number_unique)
949 node_info->list[i].count++;
955 if (node_info->number_unique == 0)
956 node_info->list=(
ColorPacket *) AcquireQuantumMemory(1,
957 sizeof(*node_info->list));
959 node_info->list=(
ColorPacket *) ResizeQuantumMemory(node_info->list,
960 (
size_t) (i+1),
sizeof(*node_info->list));
963 (void) ThrowMagickException(exception,GetMagickModule(),
964 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",
968 node_info->list[i].pixel=(*p);
969 if ((image->colorspace == CMYKColorspace) ||
970 (image->storage_class == PseudoClass))
971 node_info->list[i].index=GetPixelIndex(indexes+x);
972 node_info->list[i].count=1;
973 node_info->number_unique++;
975 if (cube_info->colors > MaximumUniqueColors)
980 if (x < (ssize_t) image->columns)
983 image_view=DestroyCacheView(image_view);
984 cube_info=DestroyHCubeInfo(image,cube_info);
985 return(y < (ssize_t) image->rows ? MagickFalse : MagickTrue);
1014MagickExport MagickBooleanType IsPaletteImage(
const Image *image,
1017 assert(image != (
Image *) NULL);
1018 assert(image->signature == MagickCoreSignature);
1019 magick_unreferenced(exception);
1020 if (IsEventLogging() != MagickFalse)
1021 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
1022 if (image->storage_class != PseudoClass)
1023 return(MagickFalse);
1024 return((image->colors <= 256) ? MagickTrue : MagickFalse);
1076MagickExport MagickBooleanType MinMaxStretchImage(
Image *image,
1077 const ChannelType channel,
const double black_value,
const double white_value)
1087 if ((channel & SyncChannels) != 0)
1092 (void) GetImageChannelRange(image,channel,&min,&max,&image->exception);
1095 if (fabs(min-max) >= MagickEpsilon)
1096 status&=LevelImageChannel(image,channel,min,max,1.0);
1097 return(status != 0 ? MagickTrue : MagickFalse);
1102 if ((channel & RedChannel) != 0)
1104 (void) GetImageChannelRange(image,RedChannel,&min,&max,&image->exception);
1107 if (fabs(min-max) >= MagickEpsilon)
1108 status&=LevelImageChannel(image,RedChannel,min,max,1.0);
1110 if ((channel & GreenChannel) != 0)
1112 (void) GetImageChannelRange(image,GreenChannel,&min,&max,
1116 if (fabs(min-max) >= MagickEpsilon)
1117 status&=LevelImageChannel(image,GreenChannel,min,max,1.0);
1119 if ((channel & BlueChannel) != 0)
1121 (void) GetImageChannelRange(image,BlueChannel,&min,&max,
1125 if (fabs(min-max) >= MagickEpsilon)
1126 status&=LevelImageChannel(image,BlueChannel,min,max,1.0);
1128 if (((channel & OpacityChannel) != 0) &&
1129 (image->matte != MagickFalse))
1131 (void) GetImageChannelRange(image,OpacityChannel,&min,&max,
1135 if (fabs(min-max) >= MagickEpsilon)
1136 status&=LevelImageChannel(image,OpacityChannel,min,max,1.0);
1138 if (((channel & IndexChannel) != 0) &&
1139 (image->colorspace == CMYKColorspace))
1141 (void) GetImageChannelRange(image,IndexChannel,&min,&max,
1145 if (fabs(min-max) >= MagickEpsilon)
1146 status&=LevelImageChannel(image,IndexChannel,min,max,1.0);
1148 return(status != 0 ? MagickTrue : MagickFalse);
1179#if defined(__cplusplus) || defined(c_plusplus)
1183static int HistogramCompare(
const void *x,
const void *y)
1191 if (color_2->pixel.red != color_1->pixel.red)
1192 return((
int) ((ssize_t) color_1->pixel.red-(ssize_t) color_2->pixel.red));
1193 if (color_2->pixel.green != color_1->pixel.green)
1194 return((
int) ((ssize_t) color_1->pixel.green-(ssize_t) color_2->pixel.green));
1195 if (color_2->pixel.blue != color_1->pixel.blue)
1196 return((
int) ((ssize_t) color_1->pixel.blue-(ssize_t) color_2->pixel.blue));
1197 return((
int) ((ssize_t) color_2->count-(ssize_t) color_1->count));
1200#if defined(__cplusplus) || defined(c_plusplus)
1204MagickExport
size_t GetNumberColors(
const Image *image,FILE *file,
1207#define HistogramImageTag "Histogram/Image"
1210 color[MaxTextExtent],
1211 count[MaxTextExtent],
1213 tuple[MaxTextExtent];
1234 if (file == (FILE *) NULL)
1239 cube_info=ClassifyImageColors(image,exception);
1242 number_colors=cube_info->colors;
1243 cube_info=DestroyHCubeInfo(image,cube_info);
1245 return(number_colors);
1247 histogram=GetImageHistogram(image,&number_colors,exception);
1249 return(number_colors);
1250 qsort((
void *) histogram,(
size_t) number_colors,
sizeof(*histogram),
1252 GetMagickPixelPacket(image,&pixel);
1255 for (i=0; i < (ssize_t) number_colors; i++)
1257 SetMagickPixelPacket(image,&p->pixel,&p->index,&pixel);
1258 (void) CopyMagickString(tuple,
"(",MaxTextExtent);
1259 ConcatenateColorComponent(&pixel,RedChannel,NoCompliance,tuple);
1260 (void) ConcatenateMagickString(tuple,
",",MaxTextExtent);
1261 ConcatenateColorComponent(&pixel,GreenChannel,NoCompliance,tuple);
1262 (void) ConcatenateMagickString(tuple,
",",MaxTextExtent);
1263 ConcatenateColorComponent(&pixel,BlueChannel,NoCompliance,tuple);
1264 if (pixel.colorspace == CMYKColorspace)
1266 (void) ConcatenateMagickString(tuple,
",",MaxTextExtent);
1267 ConcatenateColorComponent(&pixel,IndexChannel,NoCompliance,tuple);
1269 if (pixel.matte != MagickFalse)
1271 (void) ConcatenateMagickString(tuple,
",",MaxTextExtent);
1272 ConcatenateColorComponent(&pixel,OpacityChannel,NoCompliance,tuple);
1274 (void) ConcatenateMagickString(tuple,
")",MaxTextExtent);
1275 (void) QueryMagickColorname(image,&pixel,SVGCompliance,color,exception);
1276 GetColorTuple(&pixel,MagickTrue,hex);
1277 (void) FormatLocaleString(count,MagickPathExtent,
"%10.20g:",(
double)
1278 ((MagickOffsetType) p->count));
1279 (void) FormatLocaleFile(file,
" %s %s %s %s\n",count,tuple,hex,color);
1280 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1285 proceed=SetImageProgress(image,HistogramImageTag,(MagickOffsetType) i,
1287 if (proceed == MagickFalse)
1292 (void) fflush(file);
1293 histogram=(
ColorPacket *) RelinquishMagickMemory(histogram);
1294 if (status == MagickFalse)
1296 return(number_colors);
1324static void UniqueColorsToImage(
Image *unique_image,
CacheView *unique_view,
1327#define UniqueColorsImageTag "UniqueColors/Image"
1341 number_children=unique_image->matte == MagickFalse ? 8UL : 16UL;
1342 for (i=0; i < (ssize_t) number_children; i++)
1343 if (node_info->child[i] != (
HNodeInfo *) NULL)
1344 UniqueColorsToImage(unique_image,unique_view,cube_info,
1345 node_info->child[i],exception);
1346 if (node_info->level == (MaxTreeDepth-1))
1352 *magick_restrict indexes;
1359 for (i=0; i < (ssize_t) node_info->number_unique; i++)
1361 q=QueueCacheViewAuthenticPixels(unique_view,cube_info->x,0,1,1,
1365 indexes=GetCacheViewAuthenticIndexQueue(unique_view);
1367 if (unique_image->colorspace == CMYKColorspace)
1369 if (SyncCacheViewAuthenticPixels(unique_view,exception) == MagickFalse)
1374 if (unique_image->progress_monitor != (MagickProgressMonitor) NULL)
1379 proceed=SetImageProgress(unique_image,UniqueColorsImageTag,
1380 cube_info->progress,cube_info->colors);
1381 if (proceed == MagickFalse)
1384 cube_info->progress++;
1385 if (status == MagickFalse)
1390MagickExport
Image *UniqueImageColors(
const Image *image,
1402 cube_info=ClassifyImageColors(image,exception);
1404 return((
Image *) NULL);
1405 unique_image=CloneImage(image,cube_info->colors,1,MagickTrue,exception);
1406 if (unique_image == (
Image *) NULL)
1407 return(unique_image);
1408 if (SetImageStorageClass(unique_image,DirectClass) == MagickFalse)
1410 InheritException(exception,&unique_image->exception);
1411 unique_image=DestroyImage(unique_image);
1412 return((
Image *) NULL);
1414 unique_view=AcquireVirtualCacheView(unique_image,exception);
1415 UniqueColorsToImage(unique_image,unique_view,cube_info,cube_info->root,
1417 unique_view=DestroyCacheView(unique_view);
1418 cube_info=DestroyHCubeInfo(image,cube_info);
1419 return(unique_image);