176#include "MagickCore/studio.h"
177#include "MagickCore/artifact.h"
178#include "MagickCore/attribute.h"
179#include "MagickCore/cache-view.h"
180#include "MagickCore/color.h"
181#include "MagickCore/color-private.h"
182#include "MagickCore/colormap.h"
183#include "MagickCore/colorspace.h"
184#include "MagickCore/colorspace-private.h"
185#include "MagickCore/compare.h"
186#include "MagickCore/enhance.h"
187#include "MagickCore/exception.h"
188#include "MagickCore/exception-private.h"
189#include "MagickCore/histogram.h"
190#include "MagickCore/image.h"
191#include "MagickCore/image-private.h"
192#include "MagickCore/list.h"
193#include "MagickCore/memory_.h"
194#include "MagickCore/memory-private.h"
195#include "MagickCore/monitor.h"
196#include "MagickCore/monitor-private.h"
197#include "MagickCore/option.h"
198#include "MagickCore/pixel-accessor.h"
199#include "MagickCore/property.h"
200#include "MagickCore/quantize.h"
201#include "MagickCore/quantum.h"
202#include "MagickCore/quantum-private.h"
203#include "MagickCore/random_.h"
204#include "MagickCore/resource_.h"
205#include "MagickCore/string_.h"
206#include "MagickCore/string-private.h"
207#include "MagickCore/thread-private.h"
212#if !defined(__APPLE__) && !defined(TARGET_OS_IPHONE)
217#define ErrorQueueLength 16
218#define ErrorRelativeWeight PerceptibleReciprocal(16)
219#define MaxQNodes 266817
220#define MaxTreeDepth 8
221#define QNodesInAList 1920
306 error[ErrorQueueLength];
310 weights[ErrorQueueLength];
336 *GetQCubeInfo(
const QuantizeInfo *,
const size_t,
const size_t);
341static MagickBooleanType
383 quantize_info=(
QuantizeInfo *) AcquireCriticalMemory(
sizeof(*quantize_info));
384 GetQuantizeInfo(quantize_info);
390 quantize_info->dither_method=image_info->dither == MagickFalse ?
391 NoDitherMethod : RiemersmaDitherMethod;
392 option=GetImageOption(image_info,
"dither");
393 if (option != (
const char *) NULL)
394 quantize_info->dither_method=(DitherMethod) ParseCommandOption(
395 MagickDitherOptions,MagickFalse,option);
396 quantize_info->measure_error=image_info->verbose;
398 return(quantize_info);
441static inline void AssociateAlphaPixel(
const Image *image,
442 const QCubeInfo *cube_info,
const Quantum *pixel,
448 if ((cube_info->associate_alpha == MagickFalse) ||
449 (GetPixelAlpha(image,pixel) == OpaqueAlpha))
451 alpha_pixel->red=(double) GetPixelRed(image,pixel);
452 alpha_pixel->green=(double) GetPixelGreen(image,pixel);
453 alpha_pixel->blue=(double) GetPixelBlue(image,pixel);
454 alpha_pixel->alpha=(double) GetPixelAlpha(image,pixel);
457 alpha=QuantumScale*(double) GetPixelAlpha(image,pixel);
458 alpha_pixel->red=alpha*(double) GetPixelRed(image,pixel);
459 alpha_pixel->green=alpha*(double) GetPixelGreen(image,pixel);
460 alpha_pixel->blue=alpha*(double) GetPixelBlue(image,pixel);
461 alpha_pixel->alpha=(double) GetPixelAlpha(image,pixel);
464static inline void AssociateAlphaPixelInfo(
const QCubeInfo *cube_info,
470 if ((cube_info->associate_alpha == MagickFalse) ||
471 (pixel->alpha == (
double) OpaqueAlpha))
473 alpha_pixel->red=(double) pixel->red;
474 alpha_pixel->green=(double) pixel->green;
475 alpha_pixel->blue=(double) pixel->blue;
476 alpha_pixel->alpha=(double) pixel->alpha;
479 alpha=(double) (QuantumScale*pixel->alpha);
480 alpha_pixel->red=alpha*pixel->red;
481 alpha_pixel->green=alpha*pixel->green;
482 alpha_pixel->blue=alpha*pixel->blue;
483 alpha_pixel->alpha=(double) pixel->alpha;
486static inline size_t ColorToQNodeId(
const QCubeInfo *cube_info,
492 id=(size_t) (((ScaleQuantumToChar(ClampPixel(pixel->red)) >> index) & 0x01) |
493 ((ScaleQuantumToChar(ClampPixel(pixel->green)) >> index) & 0x01) << 1 |
494 ((ScaleQuantumToChar(ClampPixel(pixel->blue)) >> index) & 0x01) << 2);
495 if (cube_info->associate_alpha != MagickFalse)
496 id|=((((size_t) ScaleQuantumToChar(ClampPixel(pixel->alpha)) >> index) &
501static MagickBooleanType AssignImageColors(
Image *image,
QCubeInfo *cube_info,
504#define AssignImageTag "Assign/Image"
515 colorspace=image->colorspace;
516 if (cube_info->quantize_info->colorspace != UndefinedColorspace)
517 (void) TransformImageColorspace(image,cube_info->quantize_info->colorspace,
519 cube_info->transparent_pixels=0;
520 cube_info->transparent_index=(-1);
521 if (SetImageColormap(image,cube_info,exception) == MagickFalse)
526 if (cube_info->quantize_info->dither_method != NoDitherMethod)
527 (void) DitherImage(image,cube_info,exception);
537 image_view=AcquireAuthenticCacheView(image,exception);
538#if defined(MAGICKCORE_OPENMP_SUPPORT)
539 #pragma omp parallel for schedule(static) shared(status) \
540 magick_number_threads(image,image,image->rows,1)
542 for (y=0; y < (ssize_t) image->rows; y++)
554 if (status == MagickFalse)
556 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
558 if (q == (Quantum *) NULL)
564 for (x=0; x < (ssize_t) image->columns; x+=count)
582 for (count=1; (x+count) < (ssize_t) image->columns; count++)
587 GetPixelInfoPixel(image,q+count*(ssize_t) GetPixelChannels(image),
589 if (IsPixelEquivalent(image,q,&packet) == MagickFalse)
592 AssociateAlphaPixel(image,&cube,q,&pixel);
594 for (index=MaxTreeDepth-1; (ssize_t) index > 0; index--)
596 id=ColorToQNodeId(&cube,&pixel,index);
597 if (node_info->child[
id] == (
QNodeInfo *) NULL)
599 node_info=node_info->child[id];
605 cube.distance=(double) (4.0*((
double) QuantumRange+1.0)*
606 ((
double) QuantumRange+1.0)+1.0);
607 ClosestColor(image,&cube,node_info->parent);
608 index=cube.color_number;
609 for (i=0; i < (ssize_t) count; i++)
611 if (image->storage_class == PseudoClass)
612 SetPixelIndex(image,(Quantum) index,q);
613 if (cube.quantize_info->measure_error == MagickFalse)
615 SetPixelRed(image,ClampToQuantum(
616 image->colormap[index].red),q);
617 SetPixelGreen(image,ClampToQuantum(
618 image->colormap[index].green),q);
619 SetPixelBlue(image,ClampToQuantum(
620 image->colormap[index].blue),q);
621 if (cube.associate_alpha != MagickFalse)
622 SetPixelAlpha(image,ClampToQuantum(
623 image->colormap[index].alpha),q);
625 q+=GetPixelChannels(image);
628 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
630 if (image->progress_monitor != (MagickProgressMonitor) NULL)
635 proceed=SetImageProgress(image,AssignImageTag,(MagickOffsetType) y,
637 if (proceed == MagickFalse)
641 image_view=DestroyCacheView(image_view);
643 if (cube_info->quantize_info->measure_error != MagickFalse)
644 (void) GetImageQuantizeError(image,exception);
645 if ((cube_info->quantize_info->number_colors == 2) &&
646 (IsGrayColorspace(cube_info->quantize_info->colorspace)))
654 intensity=GetPixelInfoLuma(image->colormap+0) < (double)
655 QuantumRange/2.0 ? 0.0 : (double) QuantumRange;
656 if (image->colors > 1)
659 if (GetPixelInfoLuma(image->colormap+0) >
660 GetPixelInfoLuma(image->colormap+1))
661 intensity=(double) QuantumRange;
663 image->colormap[0].red=intensity;
664 image->colormap[0].green=intensity;
665 image->colormap[0].blue=intensity;
666 if (image->colors > 1)
668 image->colormap[1].red=(double) QuantumRange-intensity;
669 image->colormap[1].green=(double) QuantumRange-intensity;
670 image->colormap[1].blue=(double) QuantumRange-intensity;
673 (void) SyncImage(image,exception);
674 if ((cube_info->quantize_info->colorspace != UndefinedColorspace) &&
675 (IssRGBCompatibleColorspace(colorspace) == MagickFalse))
676 (void) TransformImageColorspace(image,colorspace,exception);
741static inline void SetAssociatedAlpha(
const Image *image,
QCubeInfo *cube_info)
746 associate_alpha=image->alpha_trait != UndefinedPixelTrait ? MagickTrue :
748 if ((cube_info->quantize_info->number_colors == 2) &&
749 ((cube_info->quantize_info->colorspace == LinearGRAYColorspace) ||
750 (cube_info->quantize_info->colorspace == GRAYColorspace)))
751 associate_alpha=MagickFalse;
752 cube_info->associate_alpha=associate_alpha;
755static MagickBooleanType ClassifyImageColors(
QCubeInfo *cube_info,
758#define ClassifyImageTag "Classify/Image"
790 SetAssociatedAlpha(image,cube_info);
791 if (cube_info->quantize_info->colorspace != image->colorspace)
793 if ((cube_info->quantize_info->colorspace != UndefinedColorspace) &&
794 (cube_info->quantize_info->colorspace != CMYKColorspace))
795 (void) TransformImageColorspace((
Image *) image,
796 cube_info->quantize_info->colorspace,exception);
798 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
799 (void) TransformImageColorspace((
Image *) image,sRGBColorspace,
802 midpoint.red=(double) QuantumRange/2.0;
803 midpoint.green=(double) QuantumRange/2.0;
804 midpoint.blue=(double) QuantumRange/2.0;
805 midpoint.alpha=(double) QuantumRange/2.0;
807 image_view=AcquireVirtualCacheView(image,exception);
808 for (y=0; y < (ssize_t) image->rows; y++)
816 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
817 if (p == (
const Quantum *) NULL)
819 if (cube_info->nodes > MaxQNodes)
824 PruneLevel(cube_info,cube_info->root);
827 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) count)
832 for (count=1; (x+(ssize_t) count) < (ssize_t) image->columns; count++)
837 GetPixelInfoPixel(image,p+count*(ssize_t) GetPixelChannels(image),
839 if (IsPixelEquivalent(image,p,&packet) == MagickFalse)
842 AssociateAlphaPixel(image,cube_info,p,&pixel);
843 index=MaxTreeDepth-1;
844 bisect=((double) QuantumRange+1.0)/2.0;
846 node_info=cube_info->root;
847 for (level=1; level <= MaxTreeDepth; level++)
853 id=ColorToQNodeId(cube_info,&pixel,index);
854 mid.red+=(
id & 1) != 0 ? bisect : -bisect;
855 mid.green+=(
id & 2) != 0 ? bisect : -bisect;
856 mid.blue+=(
id & 4) != 0 ? bisect : -bisect;
857 mid.alpha+=(
id & 8) != 0 ? bisect : -bisect;
858 if (node_info->child[
id] == (
QNodeInfo *) NULL)
863 node_info->child[id]=GetQNodeInfo(cube_info,
id,level,node_info);
864 if (node_info->child[
id] == (
QNodeInfo *) NULL)
866 (void) ThrowMagickException(exception,GetMagickModule(),
867 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",
871 if (level == MaxTreeDepth)
877 node_info=node_info->child[id];
878 error.red=QuantumScale*(pixel.red-mid.red);
879 error.green=QuantumScale*(pixel.green-mid.green);
880 error.blue=QuantumScale*(pixel.blue-mid.blue);
881 if (cube_info->associate_alpha != MagickFalse)
882 error.alpha=QuantumScale*(pixel.alpha-mid.alpha);
883 distance=(double) (error.red*error.red+error.green*error.green+
884 error.blue*error.blue+error.alpha*error.alpha);
885 if (IsNaN(distance) != 0)
887 node_info->quantize_error+=count*sqrt(distance);
888 cube_info->root->quantize_error+=node_info->quantize_error;
894 node_info->number_unique=(size_t) ((ssize_t) node_info->number_unique+
896 node_info->total_color.red+=count*QuantumScale*(double)
897 ClampPixel(pixel.red);
898 node_info->total_color.green+=count*QuantumScale*(double)
899 ClampPixel(pixel.green);
900 node_info->total_color.blue+=count*QuantumScale*(double)
901 ClampPixel(pixel.blue);
902 if (cube_info->associate_alpha != MagickFalse)
903 node_info->total_color.alpha+=count*QuantumScale*(double)
904 ClampPixel(pixel.alpha);
906 node_info->total_color.alpha+=count*QuantumScale*(double)
907 ClampPixel((
double) OpaqueAlpha);
908 p+=count*(ssize_t) GetPixelChannels(image);
910 if (cube_info->colors > cube_info->maximum_colors)
912 PruneToCubeDepth(cube_info,cube_info->root);
915 proceed=SetImageProgress(image,ClassifyImageTag,(MagickOffsetType) y,
917 if (proceed == MagickFalse)
920 for (y++; y < (ssize_t) image->rows; y++)
928 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
929 if (p == (
const Quantum *) NULL)
931 if (cube_info->nodes > MaxQNodes)
936 PruneLevel(cube_info,cube_info->root);
939 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) count)
944 for (count=1; (x+(ssize_t) count) < (ssize_t) image->columns; count++)
949 GetPixelInfoPixel(image,p+count*(ssize_t) GetPixelChannels(image),
951 if (IsPixelEquivalent(image,p,&packet) == MagickFalse)
954 AssociateAlphaPixel(image,cube_info,p,&pixel);
955 index=MaxTreeDepth-1;
956 bisect=((double) QuantumRange+1.0)/2.0;
958 node_info=cube_info->root;
959 for (level=1; level <= cube_info->depth; level++)
965 id=ColorToQNodeId(cube_info,&pixel,index);
966 mid.red+=(
id & 1) != 0 ? bisect : -bisect;
967 mid.green+=(
id & 2) != 0 ? bisect : -bisect;
968 mid.blue+=(
id & 4) != 0 ? bisect : -bisect;
969 mid.alpha+=(
id & 8) != 0 ? bisect : -bisect;
970 if (node_info->child[
id] == (
QNodeInfo *) NULL)
975 node_info->child[id]=GetQNodeInfo(cube_info,
id,level,node_info);
976 if (node_info->child[
id] == (
QNodeInfo *) NULL)
978 (void) ThrowMagickException(exception,GetMagickModule(),
979 ResourceLimitError,
"MemoryAllocationFailed",
"%s",
983 if (level == cube_info->depth)
989 node_info=node_info->child[id];
990 error.red=QuantumScale*(pixel.red-mid.red);
991 error.green=QuantumScale*(pixel.green-mid.green);
992 error.blue=QuantumScale*(pixel.blue-mid.blue);
993 if (cube_info->associate_alpha != MagickFalse)
994 error.alpha=QuantumScale*(pixel.alpha-mid.alpha);
995 distance=(double) (error.red*error.red+error.green*error.green+
996 error.blue*error.blue+error.alpha*error.alpha);
997 if (IsNaN(distance) != 0)
999 node_info->quantize_error+=count*sqrt(distance);
1000 cube_info->root->quantize_error+=node_info->quantize_error;
1006 node_info->number_unique=(size_t) ((ssize_t) node_info->number_unique+
1008 node_info->total_color.red+=count*QuantumScale*(double)
1009 ClampPixel(pixel.red);
1010 node_info->total_color.green+=count*QuantumScale*(double)
1011 ClampPixel(pixel.green);
1012 node_info->total_color.blue+=count*QuantumScale*(double)
1013 ClampPixel(pixel.blue);
1014 if (cube_info->associate_alpha != MagickFalse)
1015 node_info->total_color.alpha+=count*QuantumScale*(double)
1016 ClampPixel(pixel.alpha);
1018 node_info->total_color.alpha+=count*QuantumScale*(double)
1019 ClampPixel((MagickRealType) OpaqueAlpha);
1020 p+=count*(ssize_t) GetPixelChannels(image);
1022 proceed=SetImageProgress(image,ClassifyImageTag,(MagickOffsetType) y,
1024 if (proceed == MagickFalse)
1027 image_view=DestroyCacheView(image_view);
1028 if (cube_info->quantize_info->colorspace != image->colorspace)
1029 if ((cube_info->quantize_info->colorspace != UndefinedColorspace) &&
1030 (cube_info->quantize_info->colorspace != CMYKColorspace))
1031 (void) TransformImageColorspace((
Image *) image,sRGBColorspace,exception);
1032 return(y < (ssize_t) image->rows ? MagickFalse : MagickTrue);
1066 clone_info=(
QuantizeInfo *) AcquireCriticalMemory(
sizeof(*clone_info));
1067 GetQuantizeInfo(clone_info);
1070 clone_info->number_colors=quantize_info->number_colors;
1071 clone_info->tree_depth=quantize_info->tree_depth;
1072 clone_info->dither_method=quantize_info->dither_method;
1073 clone_info->colorspace=quantize_info->colorspace;
1074 clone_info->measure_error=quantize_info->measure_error;
1119 number_children=cube_info->associate_alpha == MagickFalse ? 8UL : 16UL;
1120 for (i=0; i < (ssize_t) number_children; i++)
1121 if (node_info->child[i] != (
QNodeInfo *) NULL)
1122 ClosestColor(image,cube_info,node_info->child[i]);
1123 if (node_info->number_unique != 0)
1140 p=image->colormap+node_info->color_number;
1141 q=(&cube_info->target);
1144 if (cube_info->associate_alpha != MagickFalse)
1146 alpha=(MagickRealType) (QuantumScale*p->alpha);
1147 beta=(MagickRealType) (QuantumScale*q->alpha);
1149 pixel=alpha*p->red-beta*q->red;
1150 distance=pixel*pixel;
1151 if (distance <= cube_info->distance)
1153 pixel=alpha*p->green-beta*q->green;
1154 distance+=pixel*pixel;
1155 if (distance <= cube_info->distance)
1157 pixel=alpha*p->blue-beta*q->blue;
1158 distance+=pixel*pixel;
1159 if (distance <= cube_info->distance)
1161 if (cube_info->associate_alpha != MagickFalse)
1163 pixel=p->alpha-q->alpha;
1164 distance+=pixel*pixel;
1166 if (distance <= cube_info->distance)
1168 cube_info->distance=distance;
1169 cube_info->color_number=node_info->color_number;
1203MagickExport MagickBooleanType CompressImageColormap(
Image *image,
1209 assert(image != (
Image *) NULL);
1210 assert(image->signature == MagickCoreSignature);
1211 if (IsEventLogging() != MagickFalse)
1212 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
1213 if (IsPaletteImage(image) == MagickFalse)
1214 return(MagickFalse);
1215 GetQuantizeInfo(&quantize_info);
1216 quantize_info.number_colors=image->colors;
1217 quantize_info.tree_depth=MaxTreeDepth;
1218 return(QuantizeImage(&quantize_info,image,exception));
1251static void DefineImageColormap(
Image *image,
QCubeInfo *cube_info,
1263 number_children=cube_info->associate_alpha == MagickFalse ? 8UL : 16UL;
1264 for (i=0; i < (ssize_t) number_children; i++)
1265 if (node_info->child[i] != (
QNodeInfo *) NULL)
1266 DefineImageColormap(image,cube_info,node_info->child[i]);
1267 if (node_info->number_unique != 0)
1278 q=image->colormap+image->colors;
1279 alpha=(double) ((MagickOffsetType) node_info->number_unique);
1280 alpha=PerceptibleReciprocal(alpha);
1281 if (cube_info->associate_alpha == MagickFalse)
1283 q->red=(double) ClampToQuantum(alpha*(
double) QuantumRange*
1284 node_info->total_color.red);
1285 q->green=(double) ClampToQuantum(alpha*(
double) QuantumRange*
1286 node_info->total_color.green);
1287 q->blue=(double) ClampToQuantum(alpha*(
double) QuantumRange*
1288 node_info->total_color.blue);
1289 q->alpha=(double) OpaqueAlpha;
1296 opacity=(double) (alpha*(
double) QuantumRange*
1297 node_info->total_color.alpha);
1298 q->alpha=(double) ClampToQuantum(opacity);
1299 if (q->alpha == (
double) OpaqueAlpha)
1301 q->red=(double) ClampToQuantum(alpha*(
double) QuantumRange*
1302 node_info->total_color.red);
1303 q->green=(double) ClampToQuantum(alpha*(
double) QuantumRange*
1304 node_info->total_color.green);
1305 q->blue=(double) ClampToQuantum(alpha*(
double) QuantumRange*
1306 node_info->total_color.blue);
1313 gamma=(double) (QuantumScale*q->alpha);
1314 gamma=PerceptibleReciprocal(gamma);
1315 q->red=(double) ClampToQuantum(alpha*gamma*(
double) QuantumRange*
1316 node_info->total_color.red);
1317 q->green=(double) ClampToQuantum(alpha*gamma*(
double)
1318 QuantumRange*node_info->total_color.green);
1319 q->blue=(double) ClampToQuantum(alpha*gamma*(
double) QuantumRange*
1320 node_info->total_color.blue);
1321 if (node_info->number_unique > cube_info->transparent_pixels)
1323 cube_info->transparent_pixels=node_info->number_unique;
1324 cube_info->transparent_index=(ssize_t) image->colors;
1328 node_info->color_number=image->colors++;
1354static void DestroyQCubeInfo(
QCubeInfo *cube_info)
1364 nodes=cube_info->node_queue->next;
1365 cube_info->node_queue->nodes=(
QNodeInfo *) RelinquishMagickMemory(
1366 cube_info->node_queue->nodes);
1367 cube_info->node_queue=(
QNodes *) RelinquishMagickMemory(
1368 cube_info->node_queue);
1369 cube_info->node_queue=nodes;
1370 }
while (cube_info->node_queue != (
QNodes *) NULL);
1371 if (cube_info->memory_info != (
MemoryInfo *) NULL)
1372 cube_info->memory_info=RelinquishVirtualMemory(cube_info->memory_info);
1373 cube_info->quantize_info=DestroyQuantizeInfo(cube_info->quantize_info);
1374 cube_info=(
QCubeInfo *) RelinquishMagickMemory(cube_info);
1403 assert(quantize_info->signature == MagickCoreSignature);
1404 if (IsEventLogging() != MagickFalse)
1405 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"...");
1406 quantize_info->signature=(~MagickCoreSignature);
1407 quantize_info=(
QuantizeInfo *) RelinquishMagickMemory(quantize_info);
1408 return(quantize_info);
1448 for (i=0; i < (ssize_t) GetMagickResourceLimit(ThreadResource); i++)
1466 number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
1471 (void) memset(pixels,0,number_threads*
sizeof(*pixels));
1472 for (i=0; i < (ssize_t) number_threads; i++)
1477 return(DestroyPixelTLS(pixels));
1482static inline ssize_t CacheOffset(
QCubeInfo *cube_info,
1485#define RedShift(pixel) (((pixel) >> CacheShift) << (0*(8-CacheShift)))
1486#define GreenShift(pixel) (((pixel) >> CacheShift) << (1*(8-CacheShift)))
1487#define BlueShift(pixel) (((pixel) >> CacheShift) << (2*(8-CacheShift)))
1488#define AlphaShift(pixel) (((pixel) >> CacheShift) << (3*(8-CacheShift)))
1493 offset=(ssize_t) (RedShift(ScaleQuantumToChar(ClampPixel(pixel->red))) |
1494 GreenShift(ScaleQuantumToChar(ClampPixel(pixel->green))) |
1495 BlueShift(ScaleQuantumToChar(ClampPixel(pixel->blue))));
1496 if (cube_info->associate_alpha != MagickFalse)
1497 offset|=AlphaShift(ScaleQuantumToChar(ClampPixel(pixel->alpha)));
1501static MagickBooleanType FloydSteinbergDither(
Image *image,
QCubeInfo *cube_info,
1504#define DitherImageTag "Dither/Image"
1521 pixels=AcquirePixelTLS(image->columns);
1523 return(MagickFalse);
1525 image_view=AcquireAuthenticCacheView(image,exception);
1526 for (y=0; y < (ssize_t) image->rows; y++)
1529 id = GetOpenMPThreadId();
1548 if (status == MagickFalse)
1550 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1551 if (q == (Quantum *) NULL)
1557 current=pixels[id]+(y & 0x01)*image->columns;
1558 previous=pixels[id]+((y+1) & 0x01)*image->columns;
1559 v=(ssize_t) ((y & 0x01) != 0 ? -1 : 1);
1560 for (x=0; x < (ssize_t) image->columns; x++)
1572 u=(y & 0x01) != 0 ? (ssize_t) image->columns-1-x : x;
1573 AssociateAlphaPixel(image,&cube,q+u*(ssize_t) GetPixelChannels(image),
1577 pixel.red+=7.0*cube_info->diffusion*current[u-v].red/16;
1578 pixel.green+=7.0*cube_info->diffusion*current[u-v].green/16;
1579 pixel.blue+=7.0*cube_info->diffusion*current[u-v].blue/16;
1580 if (cube.associate_alpha != MagickFalse)
1581 pixel.alpha+=7.0*cube_info->diffusion*current[u-v].alpha/16;
1585 if (x < (ssize_t) (image->columns-1))
1587 pixel.red+=cube_info->diffusion*previous[u+v].red/16;
1588 pixel.green+=cube_info->diffusion*previous[u+v].green/16;
1589 pixel.blue+=cube_info->diffusion*previous[u+v].blue/16;
1590 if (cube.associate_alpha != MagickFalse)
1591 pixel.alpha+=cube_info->diffusion*previous[u+v].alpha/16;
1593 pixel.red+=5.0*cube_info->diffusion*previous[u].red/16;
1594 pixel.green+=5.0*cube_info->diffusion*previous[u].green/16;
1595 pixel.blue+=5.0*cube_info->diffusion*previous[u].blue/16;
1596 if (cube.associate_alpha != MagickFalse)
1597 pixel.alpha+=5.0*cube_info->diffusion*previous[u].alpha/16;
1600 pixel.red+=3.0*cube_info->diffusion*previous[u-v].red/16;
1601 pixel.green+=3.0*cube_info->diffusion*previous[u-v].green/16;
1602 pixel.blue+=3.0*cube_info->diffusion*previous[u-v].blue/16;
1603 if (cube.associate_alpha != MagickFalse)
1604 pixel.alpha+=3.0*cube_info->diffusion*previous[u-v].alpha/16;
1607 pixel.red=(double) ClampPixel(pixel.red);
1608 pixel.green=(double) ClampPixel(pixel.green);
1609 pixel.blue=(double) ClampPixel(pixel.blue);
1610 if (cube.associate_alpha != MagickFalse)
1611 pixel.alpha=(double) ClampPixel(pixel.alpha);
1612 i=CacheOffset(&cube,&pixel);
1613 if (cube.cache[i] < 0)
1624 node_info=cube.root;
1625 for (index=MaxTreeDepth-1; (ssize_t) index > 0; index--)
1627 node_id=ColorToQNodeId(&cube,&pixel,index);
1628 if (node_info->child[node_id] == (
QNodeInfo *) NULL)
1630 node_info=node_info->child[node_id];
1636 cube.distance=(double) (4.0*((
double) QuantumRange+1.0)*((
double)
1637 QuantumRange+1.0)+1.0);
1638 ClosestColor(image,&cube,node_info->parent);
1639 cube.cache[i]=(ssize_t) cube.color_number;
1644 index=(size_t) cube.cache[i];
1645 if (image->storage_class == PseudoClass)
1646 SetPixelIndex(image,(Quantum) index,q+u*(ssize_t)
1647 GetPixelChannels(image));
1648 if (cube.quantize_info->measure_error == MagickFalse)
1650 SetPixelRed(image,ClampToQuantum(image->colormap[index].red),
1651 q+u*(ssize_t) GetPixelChannels(image));
1652 SetPixelGreen(image,ClampToQuantum(image->colormap[index].green),
1653 q+u*(ssize_t) GetPixelChannels(image));
1654 SetPixelBlue(image,ClampToQuantum(image->colormap[index].blue),
1655 q+u*(ssize_t) GetPixelChannels(image));
1656 if (cube.associate_alpha != MagickFalse)
1657 SetPixelAlpha(image,ClampToQuantum(image->colormap[index].alpha),
1658 q+u*(ssize_t) GetPixelChannels(image));
1660 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1665 AssociateAlphaPixelInfo(&cube,image->colormap+index,&color);
1666 current[u].red=pixel.red-color.red;
1667 current[u].green=pixel.green-color.green;
1668 current[u].blue=pixel.blue-color.blue;
1669 if (cube.associate_alpha != MagickFalse)
1670 current[u].alpha=pixel.alpha-color.alpha;
1671 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1676 proceed=SetImageProgress(image,DitherImageTag,(MagickOffsetType) y,
1678 if (proceed == MagickFalse)
1683 image_view=DestroyCacheView(image_view);
1684 pixels=DestroyPixelTLS(pixels);
1688static MagickBooleanType RiemersmaDither(
Image *image,
CacheView *image_view,
1691#define DitherImageTag "Dither/Image"
1707 if ((p->x >= 0) && (p->x < (ssize_t) image->columns) &&
1708 (p->y >= 0) && (p->y < (ssize_t) image->rows))
1719 q=GetCacheViewAuthenticPixels(image_view,p->x,p->y,1,1,exception);
1720 if (q == (Quantum *) NULL)
1721 return(MagickFalse);
1722 AssociateAlphaPixel(image,cube_info,q,&pixel);
1723 for (i=0; i < ErrorQueueLength; i++)
1725 pixel.red+=ErrorRelativeWeight*cube_info->diffusion*p->weights[i]*
1727 pixel.green+=ErrorRelativeWeight*cube_info->diffusion*p->weights[i]*
1729 pixel.blue+=ErrorRelativeWeight*cube_info->diffusion*p->weights[i]*
1731 if (cube_info->associate_alpha != MagickFalse)
1732 pixel.alpha+=ErrorRelativeWeight*cube_info->diffusion*p->weights[i]*
1735 pixel.red=(double) ClampPixel(pixel.red);
1736 pixel.green=(double) ClampPixel(pixel.green);
1737 pixel.blue=(double) ClampPixel(pixel.blue);
1738 if (cube_info->associate_alpha != MagickFalse)
1739 pixel.alpha=(double) ClampPixel(pixel.alpha);
1740 i=CacheOffset(cube_info,&pixel);
1741 if (p->cache[i] < 0)
1753 for (index=MaxTreeDepth-1; (ssize_t) index > 0; index--)
1755 id=ColorToQNodeId(cube_info,&pixel,index);
1756 if (node_info->child[
id] == (
QNodeInfo *) NULL)
1758 node_info=node_info->child[id];
1764 p->distance=(double) (4.0*((
double) QuantumRange+1.0)*((
double)
1765 QuantumRange+1.0)+1.0);
1766 ClosestColor(image,p,node_info->parent);
1767 p->cache[i]=(ssize_t) p->color_number;
1772 index=(size_t) p->cache[i];
1773 if (image->storage_class == PseudoClass)
1774 SetPixelIndex(image,(Quantum) index,q);
1775 if (cube_info->quantize_info->measure_error == MagickFalse)
1777 SetPixelRed(image,ClampToQuantum(image->colormap[index].red),q);
1778 SetPixelGreen(image,ClampToQuantum(image->colormap[index].green),q);
1779 SetPixelBlue(image,ClampToQuantum(image->colormap[index].blue),q);
1780 if (cube_info->associate_alpha != MagickFalse)
1781 SetPixelAlpha(image,ClampToQuantum(image->colormap[index].alpha),q);
1783 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1784 return(MagickFalse);
1788 (void) memmove(p->error,p->error+1,(ErrorQueueLength-1)*
1789 sizeof(p->error[0]));
1790 AssociateAlphaPixelInfo(cube_info,image->colormap+index,&color);
1791 p->error[ErrorQueueLength-1].red=pixel.red-color.red;
1792 p->error[ErrorQueueLength-1].green=pixel.green-color.green;
1793 p->error[ErrorQueueLength-1].blue=pixel.blue-color.blue;
1794 if (cube_info->associate_alpha != MagickFalse)
1795 p->error[ErrorQueueLength-1].alpha=pixel.alpha-color.alpha;
1796 proceed=SetImageProgress(image,DitherImageTag,p->offset,p->span);
1797 if (proceed == MagickFalse)
1798 return(MagickFalse);
1803 case WestGravity: p->x--;
break;
1804 case EastGravity: p->x++;
break;
1805 case NorthGravity: p->y--;
break;
1806 case SouthGravity: p->y++;
break;
1811static MagickBooleanType Riemersma(
Image *image,
CacheView *image_view,
1812 QCubeInfo *cube_info,
const size_t level,
const unsigned int direction,
1824 status=RiemersmaDither(image,image_view,cube_info,EastGravity,
1826 if (status != MagickFalse)
1827 status=RiemersmaDither(image,image_view,cube_info,SouthGravity,
1829 if (status != MagickFalse)
1830 status=RiemersmaDither(image,image_view,cube_info,WestGravity,
1836 status=RiemersmaDither(image,image_view,cube_info,WestGravity,
1838 if (status != MagickFalse)
1839 status=RiemersmaDither(image,image_view,cube_info,NorthGravity,
1841 if (status != MagickFalse)
1842 status=RiemersmaDither(image,image_view,cube_info,EastGravity,
1848 status=RiemersmaDither(image,image_view,cube_info,SouthGravity,
1850 if (status != MagickFalse)
1851 status=RiemersmaDither(image,image_view,cube_info,EastGravity,
1853 if (status != MagickFalse)
1854 status=RiemersmaDither(image,image_view,cube_info,NorthGravity,
1860 status=RiemersmaDither(image,image_view,cube_info,NorthGravity,
1862 if (status != MagickFalse)
1863 status=RiemersmaDither(image,image_view,cube_info,WestGravity,
1865 if (status != MagickFalse)
1866 status=RiemersmaDither(image,image_view,cube_info,SouthGravity,
1878 status=Riemersma(image,image_view,cube_info,level-1,NorthGravity,
1880 if (status != MagickFalse)
1881 status=RiemersmaDither(image,image_view,cube_info,EastGravity,
1883 if (status != MagickFalse)
1884 status=Riemersma(image,image_view,cube_info,level-1,WestGravity,
1886 if (status != MagickFalse)
1887 status=RiemersmaDither(image,image_view,cube_info,SouthGravity,
1889 if (status != MagickFalse)
1890 status=Riemersma(image,image_view,cube_info,level-1,WestGravity,
1892 if (status != MagickFalse)
1893 status=RiemersmaDither(image,image_view,cube_info,WestGravity,
1895 if (status != MagickFalse)
1896 status=Riemersma(image,image_view,cube_info,level-1,SouthGravity,
1902 status=Riemersma(image,image_view,cube_info,level-1,SouthGravity,
1904 if (status != MagickFalse)
1905 status=RiemersmaDither(image,image_view,cube_info,WestGravity,
1907 if (status != MagickFalse)
1908 status=Riemersma(image,image_view,cube_info,level-1,EastGravity,
1910 if (status != MagickFalse)
1911 status=RiemersmaDither(image,image_view,cube_info,NorthGravity,
1913 if (status != MagickFalse)
1914 status=Riemersma(image,image_view,cube_info,level-1,EastGravity,
1916 if (status != MagickFalse)
1917 status=RiemersmaDither(image,image_view,cube_info,EastGravity,
1919 if (status != MagickFalse)
1920 status=Riemersma(image,image_view,cube_info,level-1,NorthGravity,
1926 status=Riemersma(image,image_view,cube_info,level-1,WestGravity,
1928 if (status != MagickFalse)
1929 status=RiemersmaDither(image,image_view,cube_info,SouthGravity,
1931 if (status != MagickFalse)
1932 status=Riemersma(image,image_view,cube_info,level-1,NorthGravity,
1934 if (status != MagickFalse)
1935 status=RiemersmaDither(image,image_view,cube_info,EastGravity,
1937 if (status != MagickFalse)
1938 status=Riemersma(image,image_view,cube_info,level-1,NorthGravity,
1940 if (status != MagickFalse)
1941 status=RiemersmaDither(image,image_view,cube_info,NorthGravity,
1943 if (status != MagickFalse)
1944 status=Riemersma(image,image_view,cube_info,level-1,EastGravity,
1950 status=Riemersma(image,image_view,cube_info,level-1,EastGravity,
1952 if (status != MagickFalse)
1953 status=RiemersmaDither(image,image_view,cube_info,NorthGravity,
1955 if (status != MagickFalse)
1956 status=Riemersma(image,image_view,cube_info,level-1,SouthGravity,
1958 if (status != MagickFalse)
1959 status=RiemersmaDither(image,image_view,cube_info,WestGravity,
1961 if (status != MagickFalse)
1962 status=Riemersma(image,image_view,cube_info,level-1,SouthGravity,
1964 if (status != MagickFalse)
1965 status=RiemersmaDither(image,image_view,cube_info,SouthGravity,
1967 if (status != MagickFalse)
1968 status=Riemersma(image,image_view,cube_info,level-1,WestGravity,
1978static MagickBooleanType DitherImage(
Image *image,
QCubeInfo *cube_info,
1994 artifact=GetImageArtifact(image,
"dither:diffusion-amount");
1995 if (artifact != (
const char *) NULL)
1996 cube_info->diffusion=StringToDoubleInterval(artifact,1.0);
1997 if (cube_info->quantize_info->dither_method != RiemersmaDitherMethod)
1998 return(FloydSteinbergDither(image,cube_info,exception));
2002 (void) memset(cube_info->error,0,ErrorQueueLength*
sizeof(*cube_info->error));
2005 extent=MagickMax(image->columns,image->rows);
2006 level=(size_t) log2((
double) extent);
2007 if (((
size_t) 1UL << level) < extent)
2009 cube_info->offset=0;
2010 cube_info->span=(MagickSizeType) image->columns*image->rows;
2011 image_view=AcquireAuthenticCacheView(image,exception);
2014 status=Riemersma(image,image_view,cube_info,level,NorthGravity,exception);
2015 if (status != MagickFalse)
2016 status=RiemersmaDither(image,image_view,cube_info,ForgetGravity,exception);
2017 image_view=DestroyCacheView(image_view);
2056 const size_t depth,
const size_t maximum_colors)
2073 cube_info=(
QCubeInfo *) AcquireMagickMemory(
sizeof(*cube_info));
2076 (void) memset(cube_info,0,
sizeof(*cube_info));
2077 cube_info->depth=depth;
2078 if (cube_info->depth > MaxTreeDepth)
2079 cube_info->depth=MaxTreeDepth;
2080 if (cube_info->depth < 2)
2082 cube_info->maximum_colors=maximum_colors;
2086 cube_info->root=GetQNodeInfo(cube_info,0,0,(
QNodeInfo *) NULL);
2087 if (cube_info->root == (
QNodeInfo *) NULL)
2089 cube_info->root->parent=cube_info->root;
2090 cube_info->quantize_info=CloneQuantizeInfo(quantize_info);
2091 if (cube_info->quantize_info->dither_method == NoDitherMethod)
2096 length=(size_t) (1UL << (4*(8-CacheShift)));
2097 cube_info->memory_info=AcquireVirtualMemory(length,
sizeof(*cube_info->cache));
2098 if (cube_info->memory_info == (
MemoryInfo *) NULL)
2100 cube_info->cache=(ssize_t *) GetVirtualMemoryBlob(cube_info->memory_info);
2104 (void) memset(cube_info->cache,(-1),
sizeof(*cube_info->cache)*length);
2109 for (i=0; i < ErrorQueueLength; i++)
2111 cube_info->weights[i]=PerceptibleReciprocal(weight);
2112 weight*=exp(log(1.0/ErrorRelativeWeight)/(ErrorQueueLength-1.0));
2114 cube_info->diffusion=1.0;
2152 if (cube_info->free_nodes == 0)
2160 nodes=(
QNodes *) AcquireMagickMemory(
sizeof(*nodes));
2161 if (nodes == (
QNodes *) NULL)
2163 nodes->nodes=(
QNodeInfo *) AcquireQuantumMemory(QNodesInAList,
2164 sizeof(*nodes->nodes));
2167 nodes->next=cube_info->node_queue;
2168 cube_info->node_queue=nodes;
2169 cube_info->next_node=nodes->nodes;
2170 cube_info->free_nodes=QNodesInAList;
2173 cube_info->free_nodes--;
2174 node_info=cube_info->next_node++;
2175 (void) memset(node_info,0,
sizeof(*node_info));
2176 node_info->parent=parent;
2178 node_info->level=level;
2224MagickExport MagickBooleanType GetImageQuantizeError(
Image *image,
2237 mean_error_per_pixel;
2243 assert(image != (
Image *) NULL);
2244 assert(image->signature == MagickCoreSignature);
2245 if (IsEventLogging() != MagickFalse)
2246 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
2247 image->total_colors=GetNumberColors(image,(FILE *) NULL,exception);
2248 (void) memset(&image->error,0,
sizeof(image->error));
2249 if (image->storage_class == DirectClass)
2253 area=3.0*image->columns*image->rows;
2255 mean_error_per_pixel=0.0;
2257 image_view=AcquireVirtualCacheView(image,exception);
2258 for (y=0; y < (ssize_t) image->rows; y++)
2266 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
2267 if (p == (
const Quantum *) NULL)
2269 for (x=0; x < (ssize_t) image->columns; x++)
2271 index=(ssize_t) GetPixelIndex(image,p);
2272 if (image->alpha_trait != UndefinedPixelTrait)
2274 alpha=(double) (QuantumScale*(
double) GetPixelAlpha(image,p));
2275 beta=(double) (QuantumScale*image->colormap[index].alpha);
2277 distance=fabs((
double) (alpha*(
double) GetPixelRed(image,p)-beta*
2278 image->colormap[index].red));
2279 mean_error_per_pixel+=distance;
2280 mean_error+=distance*distance;
2281 if (distance > maximum_error)
2282 maximum_error=distance;
2283 distance=fabs((
double) (alpha*(
double) GetPixelGreen(image,p)-beta*
2284 image->colormap[index].green));
2285 mean_error_per_pixel+=distance;
2286 mean_error+=distance*distance;
2287 if (distance > maximum_error)
2288 maximum_error=distance;
2289 distance=fabs((
double) (alpha*(
double) GetPixelBlue(image,p)-beta*
2290 image->colormap[index].blue));
2291 mean_error_per_pixel+=distance;
2292 mean_error+=distance*distance;
2293 if (distance > maximum_error)
2294 maximum_error=distance;
2295 p+=GetPixelChannels(image);
2298 image_view=DestroyCacheView(image_view);
2299 image->error.mean_error_per_pixel=(double) mean_error_per_pixel/area;
2300 image->error.normalized_mean_error=(double) QuantumScale*QuantumScale*
2302 image->error.normalized_maximum_error=(double) QuantumScale*maximum_error;
2328MagickExport
void GetQuantizeInfo(
QuantizeInfo *quantize_info)
2331 if (IsEventLogging() != MagickFalse)
2332 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"...");
2333 (void) memset(quantize_info,0,
sizeof(*quantize_info));
2334 quantize_info->number_colors=256;
2335 quantize_info->dither_method=RiemersmaDitherMethod;
2336 quantize_info->colorspace=UndefinedColorspace;
2337 quantize_info->measure_error=MagickFalse;
2338 quantize_info->signature=MagickCoreSignature;
2393 for (i=0; i < (ssize_t) GetMagickResourceLimit(ThreadResource); i++)
2395 kmeans_info[i]=(
KmeansInfo *) RelinquishMagickMemory(kmeans_info[i]);
2396 kmeans_info=(
KmeansInfo **) RelinquishMagickMemory(kmeans_info);
2397 return(kmeans_info);
2400static int DominantColorCompare(
const void *x,
const void *y)
2408 return((
int) pixel_2->count-(
int) pixel_1->count);
2411static KmeansInfo **AcquireKmeansTLS(
const size_t number_colors)
2422 number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
2423 kmeans_info=(
KmeansInfo **) AcquireQuantumMemory(number_threads,
2424 sizeof(*kmeans_info));
2427 (void) memset(kmeans_info,0,number_threads*
sizeof(*kmeans_info));
2428 for (i=0; i < (ssize_t) number_threads; i++)
2430 kmeans_info[i]=(
KmeansInfo *) AcquireQuantumMemory(number_colors,
2431 sizeof(**kmeans_info));
2433 return(DestroyKmeansTLS(kmeans_info));
2435 return(kmeans_info);
2438static inline double KmeansMetric(
const Image *magick_restrict image,
2439 const Quantum *magick_restrict p,
const PixelInfo *magick_restrict q)
2448 if ((image->alpha_trait != UndefinedPixelTrait) ||
2449 (q->alpha_trait != UndefinedPixelTrait))
2451 pixel=(double) GetPixelAlpha(image,p)-(q->alpha_trait !=
2452 UndefinedPixelTrait ? q->alpha : (double) OpaqueAlpha);
2453 metric+=pixel*pixel;
2454 if (image->alpha_trait != UndefinedPixelTrait)
2455 gamma*=QuantumScale*(double) GetPixelAlpha(image,p);
2456 if (q->alpha_trait != UndefinedPixelTrait)
2457 gamma*=QuantumScale*q->alpha;
2459 if (image->colorspace == CMYKColorspace)
2461 pixel=QuantumScale*((double) GetPixelBlack(image,p)-q->black);
2462 metric+=gamma*pixel*pixel;
2463 gamma*=QuantumScale*((double) QuantumRange-(double)
2464 GetPixelBlack(image,p));
2465 gamma*=QuantumScale*((double) QuantumRange-q->black);
2468 pixel=QuantumScale*((double) GetPixelRed(image,p)-q->red);
2469 if (IsHueCompatibleColorspace(image->colorspace) != MagickFalse)
2471 if (fabs((
double) pixel) > 0.5)
2475 metric+=gamma*pixel*pixel;
2476 pixel=QuantumScale*((double) GetPixelGreen(image,p)-q->green);
2477 metric+=gamma*pixel*pixel;
2478 pixel=QuantumScale*((double) GetPixelBlue(image,p)-q->blue);
2479 metric+=gamma*pixel*pixel;
2483MagickExport MagickBooleanType KmeansImage(
Image *image,
2484 const size_t number_colors,
const size_t max_iterations,
const double tolerance,
2487#define KmeansImageTag "Kmeans/Image"
2488#define RandomColorComponent(info) \
2489 ((double) QuantumRange*GetPseudoRandomValue(info))
2495 tuple[MagickPathExtent];
2519 assert(image != (
Image *) NULL);
2520 assert(image->signature == MagickCoreSignature);
2522 assert(exception->signature == MagickCoreSignature);
2523 if (IsEventLogging() != MagickFalse)
2524 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
2525 if (max_iterations == 0)
2526 return(MagickFalse);
2527 colors=GetImageArtifact(image,
"kmeans:seed-colors");
2528 if (colors == (
const char *) NULL)
2542 quantize_info=AcquireQuantizeInfo((
ImageInfo *) NULL);
2543 quantize_info->colorspace=image->colorspace;
2544 quantize_info->number_colors=number_colors;
2545 quantize_info->dither_method=NoDitherMethod;
2546 n=(ssize_t) number_colors;
2547 for (depth=1; n != 0; depth++)
2549 cube_info=GetQCubeInfo(quantize_info,depth,number_colors);
2552 quantize_info=DestroyQuantizeInfo(quantize_info);
2553 ThrowBinaryException(ResourceLimitError,
"MemoryAllocationFailed",
2556 status=ClassifyImageColors(cube_info,image,exception);
2557 if (status != MagickFalse)
2559 if (cube_info->colors > cube_info->maximum_colors)
2560 ReduceImageColors(image,cube_info);
2561 status=SetImageColormap(image,cube_info,exception);
2563 DestroyQCubeInfo(cube_info);
2564 quantize_info=DestroyQuantizeInfo(quantize_info);
2565 if (status == MagickFalse)
2571 color[MagickPathExtent];
2579 status=AcquireImageColormap(image,number_colors,exception);
2580 if (status == MagickFalse)
2582 for (n=0, p=colors; n < (ssize_t) image->colors; n++)
2587 for (q=p; *q !=
'\0'; q++)
2590 (void) CopyMagickString(color,p,(
size_t) MagickMin(q-p+1,
2592 (void) QueryColorCompliance(color,AllCompliance,image->colormap+n,
2601 if (n < (ssize_t) image->colors)
2609 random_info=AcquireRandomInfo();
2610 for ( ; n < (ssize_t) image->colors; n++)
2612 (void) QueryColorCompliance(
"#000",AllCompliance,image->colormap+n,
2614 image->colormap[n].red=RandomColorComponent(random_info);
2615 image->colormap[n].green=RandomColorComponent(random_info);
2616 image->colormap[n].blue=RandomColorComponent(random_info);
2617 if (image->alpha_trait != UndefinedPixelTrait)
2618 image->colormap[n].alpha=RandomColorComponent(random_info);
2619 if (image->colorspace == CMYKColorspace)
2620 image->colormap[n].black=RandomColorComponent(random_info);
2622 random_info=DestroyRandomInfo(random_info);
2628 kmeans_pixels=AcquireKmeansTLS(number_colors);
2630 ThrowBinaryException(ResourceLimitError,
"MemoryAllocationFailed",
2632 previous_tolerance=0.0;
2633 verbose=IsStringTrue(GetImageArtifact(image,
"verbose"));
2634 number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
2635 image_view=AcquireAuthenticCacheView(image,exception);
2636 for (n=0; n < (ssize_t) max_iterations; n++)
2645 for (j=0; j < (ssize_t) number_threads; j++)
2646 (
void) memset(kmeans_pixels[j],0,image->colors*
sizeof(*kmeans_pixels[j]));
2647#if defined(MAGICKCORE_OPENMP_SUPPORT)
2648 #pragma omp parallel for schedule(dynamic) shared(status) \
2649 magick_number_threads(image,image,image->rows,1)
2651 for (y=0; y < (ssize_t) image->rows; y++)
2654 id = GetOpenMPThreadId();
2662 if (status == MagickFalse)
2664 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2665 if (q == (Quantum *) NULL)
2670 for (x=0; x < (ssize_t) image->columns; x++)
2683 min_distance=KmeansMetric(image,q,image->colormap+0);
2684 for (i=1; i < (ssize_t) image->colors; i++)
2689 if (min_distance <= MagickEpsilon)
2691 distance=KmeansMetric(image,q,image->colormap+i);
2692 if (distance < min_distance)
2694 min_distance=distance;
2698 kmeans_pixels[id][k].red+=QuantumScale*(double) GetPixelRed(image,q);
2699 kmeans_pixels[id][k].green+=QuantumScale*(double)
2700 GetPixelGreen(image,q);
2701 kmeans_pixels[id][k].blue+=QuantumScale*(double) GetPixelBlue(image,q);
2702 if (image->alpha_trait != UndefinedPixelTrait)
2703 kmeans_pixels[id][k].alpha+=QuantumScale*(double)
2704 GetPixelAlpha(image,q);
2705 if (image->colorspace == CMYKColorspace)
2706 kmeans_pixels[id][k].black+=QuantumScale*(double)
2707 GetPixelBlack(image,q);
2708 kmeans_pixels[id][k].count++;
2709 kmeans_pixels[id][k].distortion+=min_distance;
2710 SetPixelIndex(image,(Quantum) k,q);
2711 q+=GetPixelChannels(image);
2713 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2716 if (status == MagickFalse)
2721 for (j=1; j < (ssize_t) number_threads; j++)
2726 for (k=0; k < (ssize_t) image->colors; k++)
2728 kmeans_pixels[0][k].red+=kmeans_pixels[j][k].red;
2729 kmeans_pixels[0][k].green+=kmeans_pixels[j][k].green;
2730 kmeans_pixels[0][k].blue+=kmeans_pixels[j][k].blue;
2731 if (image->alpha_trait != UndefinedPixelTrait)
2732 kmeans_pixels[0][k].alpha+=kmeans_pixels[j][k].alpha;
2733 if (image->colorspace == CMYKColorspace)
2734 kmeans_pixels[0][k].black+=kmeans_pixels[j][k].black;
2735 kmeans_pixels[0][k].count+=kmeans_pixels[j][k].count;
2736 kmeans_pixels[0][k].distortion+=kmeans_pixels[j][k].distortion;
2743 for (j=0; j < (ssize_t) image->colors; j++)
2748 gamma=PerceptibleReciprocal((
double) kmeans_pixels[0][j].count);
2749 image->colormap[j].red=gamma*(double) QuantumRange*
2750 kmeans_pixels[0][j].red;
2751 image->colormap[j].green=gamma*(double) QuantumRange*
2752 kmeans_pixels[0][j].green;
2753 image->colormap[j].blue=gamma*(double) QuantumRange*
2754 kmeans_pixels[0][j].blue;
2755 if (image->alpha_trait != UndefinedPixelTrait)
2756 image->colormap[j].alpha=gamma*(double) QuantumRange*
2757 kmeans_pixels[0][j].alpha;
2758 if (image->colorspace == CMYKColorspace)
2759 image->colormap[j].black=gamma*(double) QuantumRange*
2760 kmeans_pixels[0][j].black;
2761 image->colormap[j].count=(MagickSizeType) kmeans_pixels[0][j].count;
2762 distortion+=kmeans_pixels[0][j].distortion;
2764 if (image->debug != MagickFalse)
2765 (void) LogMagickEvent(ImageEvent,GetMagickModule(),
2766 "distortion[%.20g]: %*g %*g\n",(
double) n,GetMagickPrecision(),
2767 distortion,GetMagickPrecision(),fabs(distortion-previous_tolerance));
2768 if (fabs(distortion-previous_tolerance) <= tolerance)
2770 previous_tolerance=distortion;
2771 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2776 proceed=SetImageProgress(image,KmeansImageTag,(MagickOffsetType) n,
2778 if (proceed == MagickFalse)
2782 image_view=DestroyCacheView(image_view);
2783 if (verbose != MagickFalse)
2784 for (n=0; n < (ssize_t) image->colors; n++)
2786 GetColorTuple(image->colormap+n,MagickTrue,tuple);
2787 (void) FormatLocaleFile(stderr,
"%s %.20g\n",tuple,(
double)
2788 image->colormap[n].count);
2790 dominant_image=CloneImage(image,0,0,MagickTrue,exception);
2791 if (dominant_image != (
Image *) NULL)
2796 qsort((
void *) dominant_image->colormap,dominant_image->colors,
2797 sizeof(*dominant_image->colormap),DominantColorCompare);
2798 GetColorTuple(dominant_image->colormap,MagickTrue,tuple);
2799 dominant_image=DestroyImage(dominant_image);
2800 (void) SetImageProperty(image,
"dominant-color",tuple,exception);
2802 kmeans_pixels=DestroyKmeansTLS(kmeans_pixels);
2803 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2804 (void) SetImageProgress(image,KmeansImageTag,(MagickOffsetType)
2805 max_iterations-1,max_iterations);
2806 if (status == MagickFalse)
2808 return(SyncImage(image,exception));
2844static inline double MagickRound(
double x)
2849 if ((x-floor(x)) < (ceil(x)-x))
2854static inline Quantum PosterizePixel(
const Quantum pixel,
const size_t levels)
2856 double posterize_pixel = QuantumRange*MagickRound(QuantumScale*(
double) pixel*
2857 (levels-1.0))/MagickMax(levels-1.0,1.0);
2858 return(ClampToQuantum((MagickRealType) posterize_pixel));
2861MagickExport MagickBooleanType PosterizeImage(
Image *image,
const size_t levels,
2864#define PosterizeImageTag "Posterize/Image"
2870 status = MagickTrue;
2878 assert(image != (
Image *) NULL);
2879 assert(image->signature == MagickCoreSignature);
2881 assert(exception->signature == MagickCoreSignature);
2882 if (IsEventLogging() != MagickFalse)
2883 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
2884 if ((dither_method != NoDitherMethod) && (levels > 1) && (levels < 17))
2885 for (y=0; y < 1; y++)
2897 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2899 PixelChannel channel = GetPixelChannelChannel(image,i);
2900 PixelTrait traits = GetPixelChannelTraits(image,channel);
2901 if ((traits & UpdatePixelTrait) != 0)
2904 number_columns=(size_t) pow(levels,channels);
2905 map_image=CloneImage(image,number_columns,1,MagickTrue,exception);
2906 if (map_image == (
Image *) NULL)
2911 if (SetImageStorageClass(map_image,DirectClass,exception) == MagickFalse)
2921 scale = QuantumRange/(levels-1.0);
2933 map_image_view=AcquireAuthenticCacheView (map_image,exception);
2934 q=GetCacheViewAuthenticPixels(map_image_view,0,0,number_columns,1,
2936 if (q == (
const Quantum *) NULL)
2938 map_image_view=DestroyCacheView(map_image_view);
2942 for (x=0; x < (ssize_t) number_columns; x++)
2944 size_t remainder = (size_t) x;
2945 for (c=0; c < (ssize_t) GetPixelChannels(image); c++)
2947 PixelChannel channel = GetPixelChannelChannel(image,c);
2948 PixelTrait traits = GetPixelChannelTraits(image,channel);
2949 if ((traits & UpdatePixelTrait) != 0)
2951 size_t value = remainder % levels;
2952 SetPixelChannel(map_image,channel,scale*value,q);
2953 remainder=(remainder-value)/levels;
2956 q+=GetPixelChannels(map_image);
2958 if (SyncCacheViewAuthenticPixels(map_image_view,exception) == MagickFalse)
2960 map_image_view=DestroyCacheView(map_image_view);
2964 map_image_view=DestroyCacheView(map_image_view);
2966 if (status != MagickFalse)
2972 quantize_info->dither_method=dither_method;
2973 (void) RemapImage(quantize_info,image,map_image,exception);
2974 quantize_info=DestroyQuantizeInfo(quantize_info);
2976 map_image=DestroyImage(map_image);
2983 if (image->storage_class == PseudoClass)
2988#if defined(MAGICKCORE_OPENMP_SUPPORT)
2989 #pragma omp parallel for schedule(static) shared(progress,status) \
2990 magick_number_threads(image,image,image->colors,1)
2992 for (i=0; i < (ssize_t) image->colors; i++)
2997 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2998 image->colormap[i].red=(double)
2999 PosterizePixel(image->colormap[i].red,levels);
3000 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3001 image->colormap[i].green=(double)
3002 PosterizePixel(image->colormap[i].green,levels);
3003 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3004 image->colormap[i].blue=(double)
3005 PosterizePixel(image->colormap[i].blue,levels);
3006 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
3007 image->colormap[i].alpha=(double)
3008 PosterizePixel(image->colormap[i].alpha,levels);
3015 image_view=AcquireAuthenticCacheView(image,exception);
3016#if defined(MAGICKCORE_OPENMP_SUPPORT)
3017 #pragma omp parallel for schedule(static) shared(progress,status) \
3018 magick_number_threads(image,image,image->rows,1)
3020 for (y=0; y < (ssize_t) image->rows; y++)
3028 if (status == MagickFalse)
3030 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
3032 if (q == (Quantum *) NULL)
3037 for (x=0; x < (ssize_t) image->columns; x++)
3042 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3044 PixelChannel channel = GetPixelChannelChannel(image,i);
3045 PixelTrait traits = GetPixelChannelTraits(image,channel);
3046 if ((traits & UpdatePixelTrait) == 0)
3048 SetPixelChannel(image,channel,PosterizePixel(q[i],levels),q);
3050 q+=GetPixelChannels(image);
3052 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3054 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3059#if defined(MAGICKCORE_OPENMP_SUPPORT)
3063 proceed=SetImageProgress(image,PosterizeImageTag,progress,
3065 if (proceed == MagickFalse)
3069 image_view=DestroyCacheView(image_view);
3072 quantize_info->number_colors=(size_t) MagickMin(levels*levels*levels,
3074 quantize_info->dither_method=dither_method;
3075 status=QuantizeImage(quantize_info,image,exception);
3076 quantize_info=DestroyQuantizeInfo(quantize_info);
3121 number_children=cube_info->associate_alpha == MagickFalse ? 8UL : 16UL;
3122 for (i=0; i < (ssize_t) number_children; i++)
3123 if (node_info->child[i] != (
QNodeInfo *) NULL)
3124 PruneChild(cube_info,node_info->child[i]);
3125 if (cube_info->nodes > cube_info->maximum_colors)
3130 parent=node_info->parent;
3131 parent->number_unique+=node_info->number_unique;
3132 parent->total_color.red+=node_info->total_color.red;
3133 parent->total_color.green+=node_info->total_color.green;
3134 parent->total_color.blue+=node_info->total_color.blue;
3135 parent->total_color.alpha+=node_info->total_color.alpha;
3136 parent->child[node_info->id]=(
QNodeInfo *) NULL;
3177 number_children=cube_info->associate_alpha == MagickFalse ? 8UL : 16UL;
3178 for (i=0; i < (ssize_t) number_children; i++)
3179 if (node_info->child[i] != (
QNodeInfo *) NULL)
3180 PruneLevel(cube_info,node_info->child[i]);
3181 if (node_info->level == cube_info->depth)
3182 PruneChild(cube_info,node_info);
3222 number_children=cube_info->associate_alpha == MagickFalse ? 8UL : 16UL;
3223 for (i=0; i < (ssize_t) number_children; i++)
3224 if (node_info->child[i] != (
QNodeInfo *) NULL)
3225 PruneToCubeDepth(cube_info,node_info->child[i]);
3226 if (node_info->level > cube_info->depth)
3227 PruneChild(cube_info,node_info);
3260MagickExport MagickBooleanType QuantizeImage(
const QuantizeInfo *quantize_info,
3277 assert(quantize_info->signature == MagickCoreSignature);
3278 assert(image != (
Image *) NULL);
3279 assert(image->signature == MagickCoreSignature);
3281 assert(exception->signature == MagickCoreSignature);
3282 if (IsEventLogging() != MagickFalse)
3283 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
3284 maximum_colors=quantize_info->number_colors;
3285 if (maximum_colors == 0)
3286 maximum_colors=MaxColormapSize;
3287 if (maximum_colors > MaxColormapSize)
3288 maximum_colors=MaxColormapSize;
3289 type=IdentifyImageGray(image,exception);
3290 if (IsGrayImageType(type) != MagickFalse)
3291 (void) SetGrayscaleImage(image,exception);
3292 depth=quantize_info->tree_depth;
3301 colors=maximum_colors;
3302 for (depth=1; colors != 0; depth++)
3304 if ((quantize_info->dither_method != NoDitherMethod) && (depth > 2))
3306 if ((image->alpha_trait != UndefinedPixelTrait) && (depth > 5))
3308 if (IsGrayImageType(type) != MagickFalse)
3314 cube_info=GetQCubeInfo(quantize_info,depth,maximum_colors);
3316 ThrowBinaryException(ResourceLimitError,
"MemoryAllocationFailed",
3318 status=ClassifyImageColors(cube_info,image,exception);
3319 if (status != MagickFalse)
3324 if (cube_info->colors > cube_info->maximum_colors)
3325 ReduceImageColors(image,cube_info);
3326 status=AssignImageColors(image,cube_info,exception);
3328 DestroyQCubeInfo(cube_info);
3362MagickExport MagickBooleanType QuantizeImages(
const QuantizeInfo *quantize_info,
3372 MagickProgressMonitor
3387 assert(quantize_info->signature == MagickCoreSignature);
3388 assert(images != (
Image *) NULL);
3389 assert(images->signature == MagickCoreSignature);
3391 assert(exception->signature == MagickCoreSignature);
3392 if (IsEventLogging() != MagickFalse)
3393 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",images->filename);
3394 if (GetNextImageInList(images) == (
Image *) NULL)
3399 status=QuantizeImage(quantize_info,images,exception);
3403 maximum_colors=quantize_info->number_colors;
3404 if (maximum_colors == 0)
3405 maximum_colors=MaxColormapSize;
3406 if (maximum_colors > MaxColormapSize)
3407 maximum_colors=MaxColormapSize;
3408 depth=quantize_info->tree_depth;
3417 colors=maximum_colors;
3418 for (depth=1; colors != 0; depth++)
3420 if (quantize_info->dither_method != NoDitherMethod)
3426 cube_info=GetQCubeInfo(quantize_info,depth,maximum_colors);
3429 (void) ThrowMagickException(exception,GetMagickModule(),
3430 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",images->filename);
3431 return(MagickFalse);
3433 number_images=GetImageListLength(images);
3435 for (i=0; image != (
Image *) NULL; i++)
3437 progress_monitor=SetImageProgressMonitor(image,(MagickProgressMonitor) NULL,
3438 image->client_data);
3439 status=ClassifyImageColors(cube_info,image,exception);
3440 if (status == MagickFalse)
3442 (void) SetImageProgressMonitor(image,progress_monitor,image->client_data);
3443 proceed=SetImageProgress(image,AssignImageTag,(MagickOffsetType) i,
3445 if (proceed == MagickFalse)
3447 image=GetNextImageInList(image);
3449 if (status != MagickFalse)
3454 ReduceImageColors(images,cube_info);
3456 for (i=0; image != (
Image *) NULL; i++)
3458 progress_monitor=SetImageProgressMonitor(image,(MagickProgressMonitor)
3459 NULL,image->client_data);
3460 status=AssignImageColors(image,cube_info,exception);
3461 if (status == MagickFalse)
3463 (void) SetImageProgressMonitor(image,progress_monitor,
3464 image->client_data);
3465 proceed=SetImageProgress(image,AssignImageTag,(MagickOffsetType) i,
3467 if (proceed == MagickFalse)
3469 image=GetNextImageInList(image);
3472 DestroyQCubeInfo(cube_info);
3509static size_t QuantizeErrorFlatten(
const QCubeInfo *cube_info,
3510 const QNodeInfo *node_info,
const ssize_t offset,
double *quantize_error)
3519 if (offset >= (ssize_t) cube_info->nodes)
3521 quantize_error[offset]=node_info->quantize_error;
3523 number_children=cube_info->associate_alpha == MagickFalse ? 8UL : 16UL;
3524 for (i=0; i < (ssize_t) number_children ; i++)
3525 if (node_info->child[i] != (
QNodeInfo *) NULL)
3526 n+=QuantizeErrorFlatten(cube_info,node_info->child[i],offset+(ssize_t) n,
3567 number_children=cube_info->associate_alpha == MagickFalse ? 8UL : 16UL;
3568 for (i=0; i < (ssize_t) number_children; i++)
3569 if (node_info->child[i] != (
QNodeInfo *) NULL)
3570 Reduce(cube_info,node_info->child[i]);
3571 if (node_info->quantize_error <= cube_info->pruning_threshold)
3572 PruneChild(cube_info,node_info);
3578 if (node_info->number_unique > 0)
3579 cube_info->colors++;
3580 if (node_info->quantize_error < cube_info->next_threshold)
3581 cube_info->next_threshold=node_info->quantize_error;
3640static int QuantizeErrorCompare(
const void *error_p,
const void *error_q)
3646 p=(
double *) error_p;
3647 q=(
double *) error_q;
3650 if (fabs(*q-*p) <= MagickEpsilon)
3655static void ReduceImageColors(
const Image *image,
QCubeInfo *cube_info)
3657#define ReduceImageTag "Reduce/Image"
3668 cube_info->next_threshold=0.0;
3669 if (cube_info->colors > cube_info->maximum_colors)
3677 quantize_error=(
double *) AcquireQuantumMemory(cube_info->nodes,
3678 sizeof(*quantize_error));
3679 if (quantize_error != (
double *) NULL)
3681 (void) QuantizeErrorFlatten(cube_info,cube_info->root,0,
3683 qsort(quantize_error,cube_info->nodes,
sizeof(
double),
3684 QuantizeErrorCompare);
3685 if (cube_info->nodes > (110*(cube_info->maximum_colors+1)/100))
3686 cube_info->next_threshold=quantize_error[cube_info->nodes-110*
3687 (cube_info->maximum_colors+1)/100];
3688 quantize_error=(
double *) RelinquishMagickMemory(quantize_error);
3691 for (span=cube_info->colors; cube_info->colors > cube_info->maximum_colors; )
3693 cube_info->pruning_threshold=cube_info->next_threshold;
3694 cube_info->next_threshold=cube_info->root->quantize_error-1;
3695 cube_info->colors=0;
3696 Reduce(cube_info,cube_info->root);
3697 offset=(MagickOffsetType) span-(MagickOffsetType) cube_info->colors;
3698 proceed=SetImageProgress(image,ReduceImageTag,offset,span-
3699 cube_info->maximum_colors+1);
3700 if (proceed == MagickFalse)
3735MagickExport MagickBooleanType RemapImage(
const QuantizeInfo *quantize_info,
3747 assert(image != (
Image *) NULL);
3748 assert(image->signature == MagickCoreSignature);
3749 assert(remap_image != (
Image *) NULL);
3750 assert(remap_image->signature == MagickCoreSignature);
3752 assert(exception->signature == MagickCoreSignature);
3753 if (IsEventLogging() != MagickFalse)
3754 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
3755 cube_info=GetQCubeInfo(quantize_info,MaxTreeDepth,MaxColormapSize);
3757 ThrowBinaryException(ResourceLimitError,
"MemoryAllocationFailed",
3759 cube_info->quantize_info->colorspace=remap_image->colorspace;
3760 status=ClassifyImageColors(cube_info,remap_image,exception);
3761 if (status != MagickFalse)
3766 cube_info->quantize_info->number_colors=cube_info->colors;
3767 if (cube_info->colors > cube_info->maximum_colors)
3768 ReduceImageColors(image,cube_info);
3769 status=AssignImageColors(image,cube_info,exception);
3771 DestroyQCubeInfo(cube_info);
3805MagickExport MagickBooleanType RemapImages(
const QuantizeInfo *quantize_info,
3817 assert(images != (
Image *) NULL);
3818 assert(images->signature == MagickCoreSignature);
3820 assert(exception->signature == MagickCoreSignature);
3821 if (IsEventLogging() != MagickFalse)
3822 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",images->filename);
3824 if (remap_image == (
Image *) NULL)
3829 status=QuantizeImages(quantize_info,images,exception);
3835 cube_info=GetQCubeInfo(quantize_info,MaxTreeDepth,
3836 quantize_info->number_colors);
3838 ThrowBinaryException(ResourceLimitError,
"MemoryAllocationFailed",
3840 status=ClassifyImageColors(cube_info,remap_image,exception);
3841 if (status != MagickFalse)
3846 cube_info->quantize_info->number_colors=cube_info->colors;
3848 for ( ; image != (
Image *) NULL; image=GetNextImageInList(image))
3850 status=AssignImageColors(image,cube_info,exception);
3851 if (status == MagickFalse)
3855 DestroyQCubeInfo(cube_info);
3885#if defined(__cplusplus) || defined(c_plusplus)
3889static int IntensityCompare(
const void *x,
const void *y)
3900 intensity=GetPixelInfoIntensity((
const Image *) NULL,color_1)-
3901 GetPixelInfoIntensity((
const Image *) NULL,color_2);
3902 if (intensity < (
double) INT_MIN)
3903 intensity=(double) INT_MIN;
3904 if (intensity > (
double) INT_MAX)
3905 intensity=(double) INT_MAX;
3906 return((
int) intensity);
3909#if defined(__cplusplus) || defined(c_plusplus)
3913static MagickBooleanType SetGrayscaleImage(
Image *image,
3934 assert(image != (
Image *) NULL);
3935 assert(image->signature == MagickCoreSignature);
3936 if (image->type != GrayscaleType)
3937 (void) TransformImageColorspace(image,GRAYColorspace,exception);
3938 extent=MagickMax(image->colors+1,MagickMax(MaxColormapSize,MaxMap+1));
3939 colormap_index=(ssize_t *) AcquireQuantumMemory(extent,
3940 sizeof(*colormap_index));
3941 if (colormap_index == (ssize_t *) NULL)
3942 ThrowBinaryException(ResourceLimitError,
"MemoryAllocationFailed",
3944 if (image->storage_class != PseudoClass)
3946 (void) memset(colormap_index,(-1),extent*
sizeof(*colormap_index));
3947 if (AcquireImageColormap(image,MaxColormapSize,exception) == MagickFalse)
3949 colormap_index=(ssize_t *) RelinquishMagickMemory(colormap_index);
3950 ThrowBinaryException(ResourceLimitError,
"MemoryAllocationFailed",
3955 image_view=AcquireAuthenticCacheView(image,exception);
3956#if defined(MAGICKCORE_OPENMP_SUPPORT)
3957 #pragma omp parallel for schedule(static) shared(status) \
3958 magick_number_threads(image,image,image->rows,1)
3960 for (y=0; y < (ssize_t) image->rows; y++)
3968 if (status == MagickFalse)
3970 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
3972 if (q == (Quantum *) NULL)
3977 for (x=0; x < (ssize_t) image->columns; x++)
3982 intensity=ScaleQuantumToMap(GetPixelRed(image,q));
3983 if (colormap_index[intensity] < 0)
3985#if defined(MAGICKCORE_OPENMP_SUPPORT)
3986 #pragma omp critical (MagickCore_SetGrayscaleImage)
3988 if (colormap_index[intensity] < 0)
3990 colormap_index[intensity]=(ssize_t) image->colors;
3991 image->colormap[image->colors].red=(double)
3992 GetPixelRed(image,q);
3993 image->colormap[image->colors].green=(double)
3994 GetPixelGreen(image,q);
3995 image->colormap[image->colors].blue=(double)
3996 GetPixelBlue(image,q);
4000 SetPixelIndex(image,(Quantum) colormap_index[intensity],q);
4001 q+=GetPixelChannels(image);
4003 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4006 image_view=DestroyCacheView(image_view);
4008 (void) memset(colormap_index,0,extent*
sizeof(*colormap_index));
4009 for (i=0; i < (ssize_t) image->colors; i++)
4010 image->colormap[i].alpha=(
double) i;
4011 qsort((
void *) image->colormap,image->colors,
sizeof(
PixelInfo),
4013 colormap=(
PixelInfo *) AcquireQuantumMemory(image->colors,
sizeof(*colormap));
4016 colormap_index=(ssize_t *) RelinquishMagickMemory(colormap_index);
4017 ThrowBinaryException(ResourceLimitError,
"MemoryAllocationFailed",
4021 colormap[j]=image->colormap[0];
4022 for (i=0; i < (ssize_t) image->colors; i++)
4024 if (IsPixelInfoEquivalent(&colormap[j],&image->colormap[i]) == MagickFalse)
4027 colormap[j]=image->colormap[i];
4029 colormap_index[(ssize_t) image->colormap[i].alpha]=j;
4031 image->colors=(size_t) (j+1);
4032 image->colormap=(
PixelInfo *) RelinquishMagickMemory(image->colormap);
4033 image->colormap=colormap;
4035 image_view=AcquireAuthenticCacheView(image,exception);
4036#if defined(MAGICKCORE_OPENMP_SUPPORT)
4037 #pragma omp parallel for schedule(static) shared(status) \
4038 magick_number_threads(image,image,image->rows,1)
4040 for (y=0; y < (ssize_t) image->rows; y++)
4048 if (status == MagickFalse)
4050 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
4051 if (q == (Quantum *) NULL)
4056 for (x=0; x < (ssize_t) image->columns; x++)
4058 SetPixelIndex(image,(Quantum) colormap_index[ScaleQuantumToMap(
4059 GetPixelIndex(image,q))],q);
4060 q+=GetPixelChannels(image);
4062 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4065 image_view=DestroyCacheView(image_view);
4066 colormap_index=(ssize_t *) RelinquishMagickMemory(colormap_index);
4067 image->type=GrayscaleType;
4068 if (SetImageMonochrome(image,exception) != MagickFalse)
4069 image->type=BilevelType;
4102MagickBooleanType SetImageColormap(
Image *image,
QCubeInfo *cube_info,
4108 number_colors=MagickMax(cube_info->maximum_colors,cube_info->colors);
4109 if (AcquireImageColormap(image,number_colors,exception) == MagickFalse)
4110 ThrowBinaryException(ResourceLimitError,
"MemoryAllocationFailed",
4113 DefineImageColormap(image,cube_info,cube_info->root);
4114 if (image->colors != number_colors)
4116 image->colormap=(
PixelInfo *) ResizeQuantumMemory(image->colormap,
4117 image->colors+1,
sizeof(*image->colormap));
4118 if (image->colormap == (
PixelInfo *) NULL)
4119 ThrowBinaryException(ResourceLimitError,
"MemoryAllocationFailed",