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 CacheBlockShift 9
218#define CacheBlocks ((size_t) 1UL << (4*(8-CacheShift)-CacheBlockShift))
219#define ErrorQueueLength 16
220#define ErrorRelativeWeight MagickSafeReciprocal(16)
221#define MaxQNodes 266817
222#define MaxTreeDepth 8
223#define QNodesInAList 1920
305 error[ErrorQueueLength];
309 weights[ErrorQueueLength];
335 *GetQCubeInfo(
const QuantizeInfo *,
const size_t,
const size_t);
338 *GetQNodeInfo(QCubeInfo *,
const size_t,
const size_t,QNodeInfo *);
340static MagickBooleanType
341 AssignImageColors(Image *,QCubeInfo *,ExceptionInfo *),
342 ClassifyImageColors(QCubeInfo *,
const Image *,ExceptionInfo *),
343 DitherImage(Image *,QCubeInfo *,ExceptionInfo *),
344 SetGrayscaleImage(Image *,ExceptionInfo *),
345 SetImageColormap(Image *,QCubeInfo *,ExceptionInfo *);
348 ClosestColor(
const Image *,QCubeInfo *,
const QNodeInfo *),
349 DefineImageColormap(Image *,QCubeInfo *,QNodeInfo *),
350 DestroyQCubeInfo(QCubeInfo *),
351 PruneLevel(QCubeInfo *,
const QNodeInfo *),
352 PruneToCubeDepth(QCubeInfo *,
const QNodeInfo *),
353 ReduceImageColors(
const Image *,QCubeInfo *);
377MagickExport QuantizeInfo *AcquireQuantizeInfo(
const ImageInfo *image_info)
382 quantize_info=(QuantizeInfo *) AcquireCriticalMemory(
sizeof(*quantize_info));
383 GetQuantizeInfo(quantize_info);
384 if (image_info != (ImageInfo *) NULL)
389 quantize_info->dither_method=image_info->dither == MagickFalse ?
390 NoDitherMethod : RiemersmaDitherMethod;
391 option=GetImageOption(image_info,
"dither");
392 if (option != (
const char *) NULL)
393 quantize_info->dither_method=(DitherMethod) ParseCommandOption(
394 MagickDitherOptions,MagickFalse,option);
395 quantize_info->measure_error=image_info->verbose;
397 return(quantize_info);
440static inline void AssociateAlphaPixel(
const Image *image,
441 const QCubeInfo *cube_info,
const Quantum *pixel,
442 DoublePixelPacket *alpha_pixel)
447 if ((cube_info->associate_alpha == MagickFalse) ||
448 (GetPixelAlpha(image,pixel) == OpaqueAlpha))
450 alpha_pixel->red=(double) GetPixelRed(image,pixel);
451 alpha_pixel->green=(double) GetPixelGreen(image,pixel);
452 alpha_pixel->blue=(double) GetPixelBlue(image,pixel);
453 alpha_pixel->alpha=(double) GetPixelAlpha(image,pixel);
456 alpha=QuantumScale*(double) GetPixelAlpha(image,pixel);
457 alpha_pixel->red=alpha*(double) GetPixelRed(image,pixel);
458 alpha_pixel->green=alpha*(double) GetPixelGreen(image,pixel);
459 alpha_pixel->blue=alpha*(double) GetPixelBlue(image,pixel);
460 alpha_pixel->alpha=(double) GetPixelAlpha(image,pixel);
463static inline void AssociateAlphaPixelInfo(
const QCubeInfo *cube_info,
464 const PixelInfo *pixel,DoublePixelPacket *alpha_pixel)
469 if ((cube_info->associate_alpha == MagickFalse) ||
470 (pixel->alpha == (
double) OpaqueAlpha))
472 alpha_pixel->red=(double) pixel->red;
473 alpha_pixel->green=(double) pixel->green;
474 alpha_pixel->blue=(double) pixel->blue;
475 alpha_pixel->alpha=(double) pixel->alpha;
478 alpha=(double) (QuantumScale*pixel->alpha);
479 alpha_pixel->red=alpha*pixel->red;
480 alpha_pixel->green=alpha*pixel->green;
481 alpha_pixel->blue=alpha*pixel->blue;
482 alpha_pixel->alpha=(double) pixel->alpha;
485static inline size_t ColorToQNodeId(
const QCubeInfo *cube_info,
486 const DoublePixelPacket *pixel,
size_t index)
491 id=(size_t) (((ScaleQuantumToChar(ClampPixel(pixel->red)) >> index) & 0x01) |
492 ((ScaleQuantumToChar(ClampPixel(pixel->green)) >> index) & 0x01) << 1 |
493 ((ScaleQuantumToChar(ClampPixel(pixel->blue)) >> index) & 0x01) << 2);
494 if (cube_info->associate_alpha != MagickFalse)
495 id|=((((size_t) ScaleQuantumToChar(ClampPixel(pixel->alpha)) >> index) &
500static MagickBooleanType AssignImageColors(Image *image,QCubeInfo *cube_info,
501 ExceptionInfo *exception)
503#define AssignImageTag "Assign/Image"
514 colorspace=image->colorspace;
515 if (cube_info->quantize_info->colorspace != UndefinedColorspace)
516 (void) TransformImageColorspace(image,cube_info->quantize_info->colorspace,
518 cube_info->transparent_pixels=0;
519 cube_info->transparent_index=(-1);
520 if (SetImageColormap(image,cube_info,exception) == MagickFalse)
525 if (cube_info->quantize_info->dither_method != NoDitherMethod)
526 (void) DitherImage(image,cube_info,exception);
536 image_view=AcquireAuthenticCacheView(image,exception);
537#if defined(MAGICKCORE_OPENMP_SUPPORT)
538 #pragma omp parallel for schedule(static) shared(status) \
539 magick_number_threads(image,image,image->rows,1)
541 for (y=0; y < (ssize_t) image->rows; y++)
553 if (status == MagickFalse)
555 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
557 if (q == (Quantum *) NULL)
563 for (x=0; x < (ssize_t) image->columns; x+=count)
581 for (count=1; (x+count) < (ssize_t) image->columns; count++)
586 GetPixelInfoPixel(image,q+count*(ssize_t) GetPixelChannels(image),
588 if (IsPixelEquivalent(image,q,&packet) == MagickFalse)
591 AssociateAlphaPixel(image,&cube,q,&pixel);
593 for (index=MaxTreeDepth-1; (ssize_t) index > 0; index--)
595 id=ColorToQNodeId(&cube,&pixel,index);
596 if (node_info->child[
id] == (QNodeInfo *) NULL)
598 node_info=node_info->child[id];
604 cube.distance=(double) (4.0*((
double) QuantumRange+1.0)*
605 ((
double) QuantumRange+1.0)+1.0);
606 ClosestColor(image,&cube,node_info->parent);
607 index=cube.color_number;
608 for (i=0; i < (ssize_t) count; i++)
610 if (image->storage_class == PseudoClass)
611 SetPixelIndex(image,(Quantum) index,q);
612 if (cube.quantize_info->measure_error == MagickFalse)
614 SetPixelRed(image,ClampToQuantum(
615 image->colormap[index].red),q);
616 SetPixelGreen(image,ClampToQuantum(
617 image->colormap[index].green),q);
618 SetPixelBlue(image,ClampToQuantum(
619 image->colormap[index].blue),q);
620 if (cube.associate_alpha != MagickFalse)
621 SetPixelAlpha(image,ClampToQuantum(
622 image->colormap[index].alpha),q);
624 q+=(ptrdiff_t) GetPixelChannels(image);
627 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
629 if (image->progress_monitor != (MagickProgressMonitor) NULL)
634 proceed=SetImageProgress(image,AssignImageTag,(MagickOffsetType) y,
636 if (proceed == MagickFalse)
640 image_view=DestroyCacheView(image_view);
642 if (cube_info->quantize_info->measure_error != MagickFalse)
643 (void) GetImageQuantizeError(image,exception);
644 if ((cube_info->quantize_info->number_colors == 2) &&
645 (IsGrayColorspace(cube_info->quantize_info->colorspace)))
653 intensity=GetPixelInfoLuma(image->colormap+0) < (double)
654 QuantumRange/2.0 ? 0.0 : (double) QuantumRange;
655 if (image->colors > 1)
658 if (GetPixelInfoLuma(image->colormap+0) >
659 GetPixelInfoLuma(image->colormap+1))
660 intensity=(double) QuantumRange;
662 image->colormap[0].red=intensity;
663 image->colormap[0].green=intensity;
664 image->colormap[0].blue=intensity;
665 if (image->colors > 1)
667 image->colormap[1].red=(double) QuantumRange-intensity;
668 image->colormap[1].green=(double) QuantumRange-intensity;
669 image->colormap[1].blue=(double) QuantumRange-intensity;
672 (void) SyncImage(image,exception);
673 if ((cube_info->quantize_info->colorspace != UndefinedColorspace) &&
674 (IssRGBCompatibleColorspace(colorspace) == MagickFalse))
675 (void) TransformImageColorspace(image,colorspace,exception);
740static inline void SetAssociatedAlpha(
const Image *image,QCubeInfo *cube_info)
745 associate_alpha=image->alpha_trait != UndefinedPixelTrait ? MagickTrue :
747 if ((cube_info->quantize_info->number_colors == 2) &&
748 ((cube_info->quantize_info->colorspace == LinearGRAYColorspace) ||
749 (cube_info->quantize_info->colorspace == GRAYColorspace)))
750 associate_alpha=MagickFalse;
751 cube_info->associate_alpha=associate_alpha;
754static MagickBooleanType ClassifyImageColors(QCubeInfo *cube_info,
755 const Image *image,ExceptionInfo *exception)
757#define ClassifyImageTag "Classify/Image"
789 SetAssociatedAlpha(image,cube_info);
790 if (cube_info->quantize_info->colorspace != image->colorspace)
792 if ((cube_info->quantize_info->colorspace != UndefinedColorspace) &&
793 (cube_info->quantize_info->colorspace != CMYKColorspace))
794 (void) TransformImageColorspace((Image *) image,
795 cube_info->quantize_info->colorspace,exception);
797 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
798 (void) TransformImageColorspace((Image *) image,sRGBColorspace,
801 midpoint.red=(double) QuantumRange/2.0;
802 midpoint.green=(double) QuantumRange/2.0;
803 midpoint.blue=(double) QuantumRange/2.0;
804 midpoint.alpha=(double) QuantumRange/2.0;
806 image_view=AcquireVirtualCacheView(image,exception);
807 for (y=0; y < (ssize_t) image->rows; y++)
815 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
816 if (p == (
const Quantum *) NULL)
818 if (cube_info->nodes > MaxQNodes)
823 PruneLevel(cube_info,cube_info->root);
826 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) count)
831 for (count=1; (x+(ssize_t) count) < (ssize_t) image->columns; count++)
836 GetPixelInfoPixel(image,p+count*(ssize_t) GetPixelChannels(image),
838 if (IsPixelEquivalent(image,p,&packet) == MagickFalse)
841 AssociateAlphaPixel(image,cube_info,p,&pixel);
842 index=MaxTreeDepth-1;
843 bisect=((double) QuantumRange+1.0)/2.0;
845 node_info=cube_info->root;
846 for (level=1; level <= MaxTreeDepth; level++)
852 id=ColorToQNodeId(cube_info,&pixel,index);
853 mid.red+=(
id & 1) != 0 ? bisect : -bisect;
854 mid.green+=(
id & 2) != 0 ? bisect : -bisect;
855 mid.blue+=(
id & 4) != 0 ? bisect : -bisect;
856 mid.alpha+=(
id & 8) != 0 ? bisect : -bisect;
857 if (node_info->child[
id] == (QNodeInfo *) NULL)
862 node_info->child[id]=GetQNodeInfo(cube_info,
id,level,node_info);
863 if (node_info->child[
id] == (QNodeInfo *) NULL)
865 (void) ThrowMagickException(exception,GetMagickModule(),
866 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",
870 if (level == MaxTreeDepth)
876 node_info=node_info->child[id];
877 error.red=QuantumScale*(pixel.red-mid.red);
878 error.green=QuantumScale*(pixel.green-mid.green);
879 error.blue=QuantumScale*(pixel.blue-mid.blue);
880 if (cube_info->associate_alpha != MagickFalse)
881 error.alpha=QuantumScale*(pixel.alpha-mid.alpha);
882 distance=(double) (error.red*error.red+error.green*error.green+
883 error.blue*error.blue+error.alpha*error.alpha);
884 if (IsNaN(distance) != 0)
886 node_info->quantize_error+=count*sqrt(distance);
887 cube_info->root->quantize_error+=node_info->quantize_error;
893 node_info->number_unique=(size_t) ((ssize_t) node_info->number_unique+
895 node_info->total_color.red+=count*QuantumScale*(double)
896 ClampPixel(pixel.red);
897 node_info->total_color.green+=count*QuantumScale*(double)
898 ClampPixel(pixel.green);
899 node_info->total_color.blue+=count*QuantumScale*(double)
900 ClampPixel(pixel.blue);
901 if (cube_info->associate_alpha != MagickFalse)
902 node_info->total_color.alpha+=count*QuantumScale*(double)
903 ClampPixel(pixel.alpha);
905 node_info->total_color.alpha+=count*QuantumScale*(double)
906 ClampPixel((
double) OpaqueAlpha);
907 p+=(ptrdiff_t) count*(ssize_t) GetPixelChannels(image);
909 if (cube_info->colors > cube_info->maximum_colors)
911 PruneToCubeDepth(cube_info,cube_info->root);
914 proceed=SetImageProgress(image,ClassifyImageTag,(MagickOffsetType) y,
916 if (proceed == MagickFalse)
919 for (y++; y < (ssize_t) image->rows; y++)
927 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
928 if (p == (
const Quantum *) NULL)
930 if (cube_info->nodes > MaxQNodes)
935 PruneLevel(cube_info,cube_info->root);
938 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) count)
943 for (count=1; (x+(ssize_t) count) < (ssize_t) image->columns; count++)
948 GetPixelInfoPixel(image,p+count*(ssize_t) GetPixelChannels(image),
950 if (IsPixelEquivalent(image,p,&packet) == MagickFalse)
953 AssociateAlphaPixel(image,cube_info,p,&pixel);
954 index=MaxTreeDepth-1;
955 bisect=((double) QuantumRange+1.0)/2.0;
957 node_info=cube_info->root;
958 for (level=1; level <= cube_info->depth; level++)
964 id=ColorToQNodeId(cube_info,&pixel,index);
965 mid.red+=(
id & 1) != 0 ? bisect : -bisect;
966 mid.green+=(
id & 2) != 0 ? bisect : -bisect;
967 mid.blue+=(
id & 4) != 0 ? bisect : -bisect;
968 mid.alpha+=(
id & 8) != 0 ? bisect : -bisect;
969 if (node_info->child[
id] == (QNodeInfo *) NULL)
974 node_info->child[id]=GetQNodeInfo(cube_info,
id,level,node_info);
975 if (node_info->child[
id] == (QNodeInfo *) NULL)
977 (void) ThrowMagickException(exception,GetMagickModule(),
978 ResourceLimitError,
"MemoryAllocationFailed",
"%s",
982 if (level == cube_info->depth)
988 node_info=node_info->child[id];
989 error.red=QuantumScale*(pixel.red-mid.red);
990 error.green=QuantumScale*(pixel.green-mid.green);
991 error.blue=QuantumScale*(pixel.blue-mid.blue);
992 if (cube_info->associate_alpha != MagickFalse)
993 error.alpha=QuantumScale*(pixel.alpha-mid.alpha);
994 distance=(double) (error.red*error.red+error.green*error.green+
995 error.blue*error.blue+error.alpha*error.alpha);
996 if (IsNaN(distance) != 0)
998 node_info->quantize_error+=count*sqrt(distance);
999 cube_info->root->quantize_error+=node_info->quantize_error;
1005 node_info->number_unique=(size_t) ((ssize_t) node_info->number_unique+
1007 node_info->total_color.red+=count*QuantumScale*(double)
1008 ClampPixel(pixel.red);
1009 node_info->total_color.green+=count*QuantumScale*(double)
1010 ClampPixel(pixel.green);
1011 node_info->total_color.blue+=count*QuantumScale*(double)
1012 ClampPixel(pixel.blue);
1013 if (cube_info->associate_alpha != MagickFalse)
1014 node_info->total_color.alpha+=count*QuantumScale*(double)
1015 ClampPixel(pixel.alpha);
1017 node_info->total_color.alpha+=count*QuantumScale*(double)
1018 ClampPixel((MagickRealType) OpaqueAlpha);
1019 p+=(ptrdiff_t) count*(ssize_t) GetPixelChannels(image);
1021 proceed=SetImageProgress(image,ClassifyImageTag,(MagickOffsetType) y,
1023 if (proceed == MagickFalse)
1026 image_view=DestroyCacheView(image_view);
1027 if (cube_info->quantize_info->colorspace != image->colorspace)
1028 if ((cube_info->quantize_info->colorspace != UndefinedColorspace) &&
1029 (cube_info->quantize_info->colorspace != CMYKColorspace))
1030 (void) TransformImageColorspace((Image *) image,sRGBColorspace,exception);
1031 return(y < (ssize_t) image->rows ? MagickFalse : MagickTrue);
1060MagickExport QuantizeInfo *CloneQuantizeInfo(
const QuantizeInfo *quantize_info)
1065 clone_info=(QuantizeInfo *) AcquireCriticalMemory(
sizeof(*clone_info));
1066 GetQuantizeInfo(clone_info);
1067 if (quantize_info == (QuantizeInfo *) NULL)
1069 clone_info->number_colors=quantize_info->number_colors;
1070 clone_info->tree_depth=quantize_info->tree_depth;
1071 clone_info->dither_method=quantize_info->dither_method;
1072 clone_info->colorspace=quantize_info->colorspace;
1073 clone_info->measure_error=quantize_info->measure_error;
1106static void ClosestColor(
const Image *image,QCubeInfo *cube_info,
1107 const QNodeInfo *node_info)
1118 number_children=cube_info->associate_alpha == MagickFalse ? 8UL : 16UL;
1119 for (i=0; i < (ssize_t) number_children; i++)
1120 if (node_info->child[i] != (QNodeInfo *) NULL)
1121 ClosestColor(image,cube_info,node_info->child[i]);
1122 if (node_info->number_unique != 0)
1139 p=image->colormap+node_info->color_number;
1140 q=(&cube_info->target);
1143 if (cube_info->associate_alpha != MagickFalse)
1145 alpha=(MagickRealType) (QuantumScale*p->alpha);
1146 beta=(MagickRealType) (QuantumScale*q->alpha);
1148 pixel=alpha*p->red-beta*q->red;
1149 distance=pixel*pixel;
1150 if (distance <= cube_info->distance)
1152 pixel=alpha*p->green-beta*q->green;
1153 distance+=pixel*pixel;
1154 if (distance <= cube_info->distance)
1156 pixel=alpha*p->blue-beta*q->blue;
1157 distance+=pixel*pixel;
1158 if (distance <= cube_info->distance)
1160 if (cube_info->associate_alpha != MagickFalse)
1162 pixel=p->alpha-q->alpha;
1163 distance+=pixel*pixel;
1165 if (distance <= cube_info->distance)
1167 cube_info->distance=distance;
1168 cube_info->color_number=node_info->color_number;
1202MagickExport MagickBooleanType CompressImageColormap(Image *image,
1203 ExceptionInfo *exception)
1208 assert(image != (Image *) NULL);
1209 assert(image->signature == MagickCoreSignature);
1210 if (IsEventLogging() != MagickFalse)
1211 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
1212 if (IsPaletteImage(image) == MagickFalse)
1213 return(MagickFalse);
1214 GetQuantizeInfo(&quantize_info);
1215 quantize_info.number_colors=image->colors;
1216 quantize_info.tree_depth=MaxTreeDepth;
1217 return(QuantizeImage(&quantize_info,image,exception));
1250static void DefineImageColormap(Image *image,QCubeInfo *cube_info,
1251 QNodeInfo *node_info)
1262 number_children=cube_info->associate_alpha == MagickFalse ? 8UL : 16UL;
1263 for (i=0; i < (ssize_t) number_children; i++)
1264 if (node_info->child[i] != (QNodeInfo *) NULL)
1265 DefineImageColormap(image,cube_info,node_info->child[i]);
1266 if (node_info->number_unique != 0)
1277 q=image->colormap+image->colors;
1278 alpha=(double) ((MagickOffsetType) node_info->number_unique);
1279 alpha=MagickSafeReciprocal(alpha);
1280 if (cube_info->associate_alpha == MagickFalse)
1282 q->red=(double) ClampToQuantum(alpha*(
double) QuantumRange*
1283 node_info->total_color.red);
1284 q->green=(double) ClampToQuantum(alpha*(
double) QuantumRange*
1285 node_info->total_color.green);
1286 q->blue=(double) ClampToQuantum(alpha*(
double) QuantumRange*
1287 node_info->total_color.blue);
1288 q->alpha=(double) OpaqueAlpha;
1295 opacity=(double) (alpha*(
double) QuantumRange*
1296 node_info->total_color.alpha);
1297 q->alpha=(double) ClampToQuantum(opacity);
1298 if (q->alpha == (
double) OpaqueAlpha)
1300 q->red=(double) ClampToQuantum(alpha*(
double) QuantumRange*
1301 node_info->total_color.red);
1302 q->green=(double) ClampToQuantum(alpha*(
double) QuantumRange*
1303 node_info->total_color.green);
1304 q->blue=(double) ClampToQuantum(alpha*(
double) QuantumRange*
1305 node_info->total_color.blue);
1312 gamma=(double) (QuantumScale*q->alpha);
1313 gamma=MagickSafeReciprocal(gamma);
1314 q->red=(double) ClampToQuantum(alpha*gamma*(
double) QuantumRange*
1315 node_info->total_color.red);
1316 q->green=(double) ClampToQuantum(alpha*gamma*(
double)
1317 QuantumRange*node_info->total_color.green);
1318 q->blue=(double) ClampToQuantum(alpha*gamma*(
double) QuantumRange*
1319 node_info->total_color.blue);
1320 if (node_info->number_unique > cube_info->transparent_pixels)
1322 cube_info->transparent_pixels=node_info->number_unique;
1323 cube_info->transparent_index=(ssize_t) image->colors;
1327 node_info->color_number=image->colors++;
1353static void DestroyQCubeInfo(QCubeInfo *cube_info)
1366 nodes=cube_info->node_queue->next;
1367 cube_info->node_queue->nodes=(QNodeInfo *) RelinquishMagickMemory(
1368 cube_info->node_queue->nodes);
1369 cube_info->node_queue=(QNodes *) RelinquishMagickMemory(
1370 cube_info->node_queue);
1371 cube_info->node_queue=nodes;
1372 }
while (cube_info->node_queue != (QNodes *) NULL);
1373 if (cube_info->cache != (ssize_t **) NULL)
1375 for (i=0; i < (ssize_t) CacheBlocks; i++)
1376 if (cube_info->cache[i] != (ssize_t *) NULL)
1377 cube_info->cache[i]=(ssize_t *) RelinquishMagickMemory(
1378 cube_info->cache[i]);
1379 cube_info->cache=(ssize_t **) RelinquishMagickMemory(cube_info->cache);
1381 cube_info->quantize_info=DestroyQuantizeInfo(cube_info->quantize_info);
1382 cube_info=(QCubeInfo *) RelinquishMagickMemory(cube_info);
1408MagickExport QuantizeInfo *DestroyQuantizeInfo(QuantizeInfo *quantize_info)
1410 assert(quantize_info != (QuantizeInfo *) NULL);
1411 assert(quantize_info->signature == MagickCoreSignature);
1412 if (IsEventLogging() != MagickFalse)
1413 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"...");
1414 quantize_info->signature=(~MagickCoreSignature);
1415 quantize_info=(QuantizeInfo *) RelinquishMagickMemory(quantize_info);
1416 return(quantize_info);
1450static DoublePixelPacket **DestroyPixelTLS(DoublePixelPacket **pixels)
1455 assert(pixels != (DoublePixelPacket **) NULL);
1456 for (i=0; i < (ssize_t) GetMagickResourceLimit(ThreadResource); i++)
1457 if (pixels[i] != (DoublePixelPacket *) NULL)
1458 pixels[i]=(DoublePixelPacket *) RelinquishMagickMemory(pixels[i]);
1459 pixels=(DoublePixelPacket **) RelinquishMagickMemory(pixels);
1463static DoublePixelPacket **AcquirePixelTLS(
const size_t count)
1474 number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
1475 pixels=(DoublePixelPacket **) AcquireQuantumMemory(number_threads,
1477 if (pixels == (DoublePixelPacket **) NULL)
1478 return((DoublePixelPacket **) NULL);
1479 (void) memset(pixels,0,number_threads*
sizeof(*pixels));
1480 for (i=0; i < (ssize_t) number_threads; i++)
1482 pixels[i]=(DoublePixelPacket *) AcquireQuantumMemory(count,2*
1484 if (pixels[i] == (DoublePixelPacket *) NULL)
1485 return(DestroyPixelTLS(pixels));
1490static inline ssize_t CacheOffset(QCubeInfo *cube_info,
1491 const DoublePixelPacket *pixel)
1493#define RedShift(pixel) (((pixel) >> CacheShift) << (0*(8-CacheShift)))
1494#define GreenShift(pixel) (((pixel) >> CacheShift) << (1*(8-CacheShift)))
1495#define BlueShift(pixel) (((pixel) >> CacheShift) << (2*(8-CacheShift)))
1496#define AlphaShift(pixel) (((pixel) >> CacheShift) << (3*(8-CacheShift)))
1501 offset=(ssize_t) (RedShift(ScaleQuantumToChar(ClampPixel(pixel->red))) |
1502 GreenShift(ScaleQuantumToChar(ClampPixel(pixel->green))) |
1503 BlueShift(ScaleQuantumToChar(ClampPixel(pixel->blue))));
1504 if (cube_info->associate_alpha != MagickFalse)
1505 offset|=AlphaShift(ScaleQuantumToChar(ClampPixel(pixel->alpha)));
1509static inline ssize_t *GetCacheEntry(QCubeInfo *cube_info,
1510 const DoublePixelPacket *pixel)
1519 offset=CacheOffset(cube_info,pixel);
1520 block=cube_info->cache+(offset >> CacheBlockShift);
1521 if (*block == (ssize_t *) NULL)
1523 *block=(ssize_t *) AcquireQuantumMemory((
size_t) 1UL << CacheBlockShift,
1525 if (*block == (ssize_t *) NULL)
1526 return((ssize_t *) NULL);
1527 (void) memset(*block,(-1),
sizeof(**block) << CacheBlockShift);
1529 return(*block+(offset & ((1L << CacheBlockShift)-1)));
1532static MagickBooleanType FloydSteinbergDither(Image *image,QCubeInfo *cube_info,
1533 ExceptionInfo *exception)
1535#define DitherImageTag "Dither/Image"
1552 pixels=AcquirePixelTLS(image->columns);
1553 if (pixels == (DoublePixelPacket **) NULL)
1554 return(MagickFalse);
1556 image_view=AcquireAuthenticCacheView(image,exception);
1557 for (y=0; y < (ssize_t) image->rows; y++)
1560 id = GetOpenMPThreadId();
1579 if (status == MagickFalse)
1581 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1582 if (q == (Quantum *) NULL)
1588 current=pixels[id]+(y & 0x01)*image->columns;
1589 previous=pixels[id]+((y+1) & 0x01)*image->columns;
1590 v=(ssize_t) ((y & 0x01) != 0 ? -1 : 1);
1591 for (x=0; x < (ssize_t) image->columns; x++)
1603 u=(y & 0x01) != 0 ? (ssize_t) image->columns-1-x : x;
1604 AssociateAlphaPixel(image,&cube,q+u*(ssize_t) GetPixelChannels(image),
1608 pixel.red+=7.0*cube_info->diffusion*current[u-v].red/16;
1609 pixel.green+=7.0*cube_info->diffusion*current[u-v].green/16;
1610 pixel.blue+=7.0*cube_info->diffusion*current[u-v].blue/16;
1611 if (cube.associate_alpha != MagickFalse)
1612 pixel.alpha+=7.0*cube_info->diffusion*current[u-v].alpha/16;
1616 if (x < (ssize_t) (image->columns-1))
1618 pixel.red+=cube_info->diffusion*previous[u+v].red/16;
1619 pixel.green+=cube_info->diffusion*previous[u+v].green/16;
1620 pixel.blue+=cube_info->diffusion*previous[u+v].blue/16;
1621 if (cube.associate_alpha != MagickFalse)
1622 pixel.alpha+=cube_info->diffusion*previous[u+v].alpha/16;
1624 pixel.red+=5.0*cube_info->diffusion*previous[u].red/16;
1625 pixel.green+=5.0*cube_info->diffusion*previous[u].green/16;
1626 pixel.blue+=5.0*cube_info->diffusion*previous[u].blue/16;
1627 if (cube.associate_alpha != MagickFalse)
1628 pixel.alpha+=5.0*cube_info->diffusion*previous[u].alpha/16;
1631 pixel.red+=3.0*cube_info->diffusion*previous[u-v].red/16;
1632 pixel.green+=3.0*cube_info->diffusion*previous[u-v].green/16;
1633 pixel.blue+=3.0*cube_info->diffusion*previous[u-v].blue/16;
1634 if (cube.associate_alpha != MagickFalse)
1635 pixel.alpha+=3.0*cube_info->diffusion*previous[u-v].alpha/16;
1638 pixel.red=(double) ClampPixel(pixel.red);
1639 pixel.green=(double) ClampPixel(pixel.green);
1640 pixel.blue=(double) ClampPixel(pixel.blue);
1641 if (cube.associate_alpha != MagickFalse)
1642 pixel.alpha=(double) ClampPixel(pixel.alpha);
1643 entry=GetCacheEntry(&cube,&pixel);
1644 if ((entry == (ssize_t *) NULL) || (*entry < 0))
1655 node_info=cube.root;
1656 for (index=MaxTreeDepth-1; (ssize_t) index > 0; index--)
1658 node_id=ColorToQNodeId(&cube,&pixel,index);
1659 if (node_info->child[node_id] == (QNodeInfo *) NULL)
1661 node_info=node_info->child[node_id];
1667 cube.distance=(double) (4.0*((
double) QuantumRange+1.0)*((
double)
1668 QuantumRange+1.0)+1.0);
1669 ClosestColor(image,&cube,node_info->parent);
1670 if (entry != (ssize_t *) NULL)
1671 *entry=(ssize_t) cube.color_number;
1676 index=(entry != (ssize_t *) NULL) ? (size_t) *entry : cube.color_number;
1677 if (image->storage_class == PseudoClass)
1678 SetPixelIndex(image,(Quantum) index,q+u*(ssize_t)
1679 GetPixelChannels(image));
1680 if (cube.quantize_info->measure_error == MagickFalse)
1682 SetPixelRed(image,ClampToQuantum(image->colormap[index].red),
1683 q+u*(ssize_t) GetPixelChannels(image));
1684 SetPixelGreen(image,ClampToQuantum(image->colormap[index].green),
1685 q+u*(ssize_t) GetPixelChannels(image));
1686 SetPixelBlue(image,ClampToQuantum(image->colormap[index].blue),
1687 q+u*(ssize_t) GetPixelChannels(image));
1688 if (cube.associate_alpha != MagickFalse)
1689 SetPixelAlpha(image,ClampToQuantum(image->colormap[index].alpha),
1690 q+u*(ssize_t) GetPixelChannels(image));
1692 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1697 AssociateAlphaPixelInfo(&cube,image->colormap+index,&color);
1698 current[u].red=pixel.red-color.red;
1699 current[u].green=pixel.green-color.green;
1700 current[u].blue=pixel.blue-color.blue;
1701 if (cube.associate_alpha != MagickFalse)
1702 current[u].alpha=pixel.alpha-color.alpha;
1703 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1708 proceed=SetImageProgress(image,DitherImageTag,(MagickOffsetType) y,
1710 if (proceed == MagickFalse)
1715 image_view=DestroyCacheView(image_view);
1716 pixels=DestroyPixelTLS(pixels);
1720static MagickBooleanType RiemersmaDither(Image *image,CacheView *image_view,
1721 QCubeInfo *cube_info,
const unsigned int direction,ExceptionInfo *exception)
1723#define DitherImageTag "Dither/Image"
1739 if ((p->x >= 0) && (p->x < (ssize_t) image->columns) &&
1740 (p->y >= 0) && (p->y < (ssize_t) image->rows))
1752 q=GetCacheViewAuthenticPixels(image_view,p->x,p->y,1,1,exception);
1753 if (q == (Quantum *) NULL)
1754 return(MagickFalse);
1755 AssociateAlphaPixel(image,cube_info,q,&pixel);
1756 for (i=0; i < ErrorQueueLength; i++)
1758 pixel.red+=ErrorRelativeWeight*cube_info->diffusion*p->weights[i]*
1760 pixel.green+=ErrorRelativeWeight*cube_info->diffusion*p->weights[i]*
1762 pixel.blue+=ErrorRelativeWeight*cube_info->diffusion*p->weights[i]*
1764 if (cube_info->associate_alpha != MagickFalse)
1765 pixel.alpha+=ErrorRelativeWeight*cube_info->diffusion*p->weights[i]*
1768 pixel.red=(double) ClampPixel(pixel.red);
1769 pixel.green=(double) ClampPixel(pixel.green);
1770 pixel.blue=(double) ClampPixel(pixel.blue);
1771 if (cube_info->associate_alpha != MagickFalse)
1772 pixel.alpha=(double) ClampPixel(pixel.alpha);
1773 entry=GetCacheEntry(p,&pixel);
1774 if ((entry == (ssize_t *) NULL) || (*entry < 0))
1786 for (index=MaxTreeDepth-1; (ssize_t) index > 0; index--)
1788 id=ColorToQNodeId(cube_info,&pixel,index);
1789 if (node_info->child[
id] == (QNodeInfo *) NULL)
1791 node_info=node_info->child[id];
1797 p->distance=(double) (4.0*((
double) QuantumRange+1.0)*((
double)
1798 QuantumRange+1.0)+1.0);
1799 ClosestColor(image,p,node_info->parent);
1800 if (entry != (ssize_t *) NULL)
1801 *entry=(ssize_t) p->color_number;
1806 index=(entry != (ssize_t *) NULL) ? (size_t) *entry : p->color_number;
1807 if (image->storage_class == PseudoClass)
1808 SetPixelIndex(image,(Quantum) index,q);
1809 if (cube_info->quantize_info->measure_error == MagickFalse)
1811 SetPixelRed(image,ClampToQuantum(image->colormap[index].red),q);
1812 SetPixelGreen(image,ClampToQuantum(image->colormap[index].green),q);
1813 SetPixelBlue(image,ClampToQuantum(image->colormap[index].blue),q);
1814 if (cube_info->associate_alpha != MagickFalse)
1815 SetPixelAlpha(image,ClampToQuantum(image->colormap[index].alpha),q);
1817 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1818 return(MagickFalse);
1822 (void) memmove(p->error,p->error+1,(ErrorQueueLength-1)*
1823 sizeof(p->error[0]));
1824 AssociateAlphaPixelInfo(cube_info,image->colormap+index,&color);
1825 p->error[ErrorQueueLength-1].red=pixel.red-color.red;
1826 p->error[ErrorQueueLength-1].green=pixel.green-color.green;
1827 p->error[ErrorQueueLength-1].blue=pixel.blue-color.blue;
1828 if (cube_info->associate_alpha != MagickFalse)
1829 p->error[ErrorQueueLength-1].alpha=pixel.alpha-color.alpha;
1830 proceed=SetImageProgress(image,DitherImageTag,p->offset,p->span);
1831 if (proceed == MagickFalse)
1832 return(MagickFalse);
1837 case WestGravity: p->x--;
break;
1838 case EastGravity: p->x++;
break;
1839 case NorthGravity: p->y--;
break;
1840 case SouthGravity: p->y++;
break;
1845static MagickBooleanType Riemersma(Image *image,CacheView *image_view,
1846 QCubeInfo *cube_info,
const size_t level,
const unsigned int direction,
1847 ExceptionInfo *exception)
1858 status=RiemersmaDither(image,image_view,cube_info,EastGravity,
1860 if (status != MagickFalse)
1861 status=RiemersmaDither(image,image_view,cube_info,SouthGravity,
1863 if (status != MagickFalse)
1864 status=RiemersmaDither(image,image_view,cube_info,WestGravity,
1870 status=RiemersmaDither(image,image_view,cube_info,WestGravity,
1872 if (status != MagickFalse)
1873 status=RiemersmaDither(image,image_view,cube_info,NorthGravity,
1875 if (status != MagickFalse)
1876 status=RiemersmaDither(image,image_view,cube_info,EastGravity,
1882 status=RiemersmaDither(image,image_view,cube_info,SouthGravity,
1884 if (status != MagickFalse)
1885 status=RiemersmaDither(image,image_view,cube_info,EastGravity,
1887 if (status != MagickFalse)
1888 status=RiemersmaDither(image,image_view,cube_info,NorthGravity,
1894 status=RiemersmaDither(image,image_view,cube_info,NorthGravity,
1896 if (status != MagickFalse)
1897 status=RiemersmaDither(image,image_view,cube_info,WestGravity,
1899 if (status != MagickFalse)
1900 status=RiemersmaDither(image,image_view,cube_info,SouthGravity,
1912 status=Riemersma(image,image_view,cube_info,level-1,NorthGravity,
1914 if (status != MagickFalse)
1915 status=RiemersmaDither(image,image_view,cube_info,EastGravity,
1917 if (status != MagickFalse)
1918 status=Riemersma(image,image_view,cube_info,level-1,WestGravity,
1920 if (status != MagickFalse)
1921 status=RiemersmaDither(image,image_view,cube_info,SouthGravity,
1923 if (status != MagickFalse)
1924 status=Riemersma(image,image_view,cube_info,level-1,WestGravity,
1926 if (status != MagickFalse)
1927 status=RiemersmaDither(image,image_view,cube_info,WestGravity,
1929 if (status != MagickFalse)
1930 status=Riemersma(image,image_view,cube_info,level-1,SouthGravity,
1936 status=Riemersma(image,image_view,cube_info,level-1,SouthGravity,
1938 if (status != MagickFalse)
1939 status=RiemersmaDither(image,image_view,cube_info,WestGravity,
1941 if (status != MagickFalse)
1942 status=Riemersma(image,image_view,cube_info,level-1,EastGravity,
1944 if (status != MagickFalse)
1945 status=RiemersmaDither(image,image_view,cube_info,NorthGravity,
1947 if (status != MagickFalse)
1948 status=Riemersma(image,image_view,cube_info,level-1,EastGravity,
1950 if (status != MagickFalse)
1951 status=RiemersmaDither(image,image_view,cube_info,EastGravity,
1953 if (status != MagickFalse)
1954 status=Riemersma(image,image_view,cube_info,level-1,NorthGravity,
1960 status=Riemersma(image,image_view,cube_info,level-1,WestGravity,
1962 if (status != MagickFalse)
1963 status=RiemersmaDither(image,image_view,cube_info,SouthGravity,
1965 if (status != MagickFalse)
1966 status=Riemersma(image,image_view,cube_info,level-1,NorthGravity,
1968 if (status != MagickFalse)
1969 status=RiemersmaDither(image,image_view,cube_info,EastGravity,
1971 if (status != MagickFalse)
1972 status=Riemersma(image,image_view,cube_info,level-1,NorthGravity,
1974 if (status != MagickFalse)
1975 status=RiemersmaDither(image,image_view,cube_info,NorthGravity,
1977 if (status != MagickFalse)
1978 status=Riemersma(image,image_view,cube_info,level-1,EastGravity,
1984 status=Riemersma(image,image_view,cube_info,level-1,EastGravity,
1986 if (status != MagickFalse)
1987 status=RiemersmaDither(image,image_view,cube_info,NorthGravity,
1989 if (status != MagickFalse)
1990 status=Riemersma(image,image_view,cube_info,level-1,SouthGravity,
1992 if (status != MagickFalse)
1993 status=RiemersmaDither(image,image_view,cube_info,WestGravity,
1995 if (status != MagickFalse)
1996 status=Riemersma(image,image_view,cube_info,level-1,SouthGravity,
1998 if (status != MagickFalse)
1999 status=RiemersmaDither(image,image_view,cube_info,SouthGravity,
2001 if (status != MagickFalse)
2002 status=Riemersma(image,image_view,cube_info,level-1,WestGravity,
2012static MagickBooleanType DitherImage(Image *image,QCubeInfo *cube_info,
2013 ExceptionInfo *exception)
2028 artifact=GetImageArtifact(image,
"dither:diffusion-amount");
2029 if (artifact != (
const char *) NULL)
2030 cube_info->diffusion=StringToDoubleInterval(artifact,1.0);
2031 if (cube_info->quantize_info->dither_method != RiemersmaDitherMethod)
2032 return(FloydSteinbergDither(image,cube_info,exception));
2036 (void) memset(cube_info->error,0,ErrorQueueLength*
sizeof(*cube_info->error));
2039 extent=MagickMax(image->columns,image->rows);
2040 level=(size_t) log2((
double) extent);
2041 if (((
size_t) 1UL << level) < extent)
2043 cube_info->offset=0;
2044 cube_info->span=(MagickSizeType) image->columns*image->rows;
2045 image_view=AcquireAuthenticCacheView(image,exception);
2048 status=Riemersma(image,image_view,cube_info,level,NorthGravity,exception);
2049 if (status != MagickFalse)
2050 status=RiemersmaDither(image,image_view,cube_info,ForgetGravity,exception);
2051 image_view=DestroyCacheView(image_view);
2089static QCubeInfo *GetQCubeInfo(
const QuantizeInfo *quantize_info,
2090 const size_t depth,
const size_t maximum_colors)
2104 cube_info=(QCubeInfo *) AcquireMagickMemory(
sizeof(*cube_info));
2105 if (cube_info == (QCubeInfo *) NULL)
2106 return((QCubeInfo *) NULL);
2107 (void) memset(cube_info,0,
sizeof(*cube_info));
2108 cube_info->depth=depth;
2109 if (cube_info->depth > MaxTreeDepth)
2110 cube_info->depth=MaxTreeDepth;
2111 if (cube_info->depth < 2)
2113 cube_info->maximum_colors=maximum_colors;
2117 cube_info->root=GetQNodeInfo(cube_info,0,0,(QNodeInfo *) NULL);
2118 if (cube_info->root == (QNodeInfo *) NULL)
2119 return((QCubeInfo *) NULL);
2120 cube_info->root->parent=cube_info->root;
2121 cube_info->quantize_info=CloneQuantizeInfo(quantize_info);
2122 if (cube_info->quantize_info->dither_method == NoDitherMethod)
2127 cube_info->cache=(ssize_t **) AcquireQuantumMemory(CacheBlocks,
2128 sizeof(*cube_info->cache));
2129 if (cube_info->cache == (ssize_t **) NULL)
2130 return((QCubeInfo *) NULL);
2131 (void) memset(cube_info->cache,0,CacheBlocks*
sizeof(*cube_info->cache));
2136 for (i=0; i < ErrorQueueLength; i++)
2138 cube_info->weights[i]=MagickSafeReciprocal(weight);
2139 weight*=exp(log(1.0/ErrorRelativeWeight)/(ErrorQueueLength-1.0));
2141 cube_info->diffusion=1.0;
2173static QNodeInfo *GetQNodeInfo(QCubeInfo *cube_info,
const size_t id,
2174 const size_t level,QNodeInfo *parent)
2179 if (cube_info->free_nodes == 0)
2187 nodes=(QNodes *) AcquireMagickMemory(
sizeof(*nodes));
2188 if (nodes == (QNodes *) NULL)
2189 return((QNodeInfo *) NULL);
2190 nodes->nodes=(QNodeInfo *) AcquireQuantumMemory(QNodesInAList,
2191 sizeof(*nodes->nodes));
2192 if (nodes->nodes == (QNodeInfo *) NULL)
2193 return((QNodeInfo *) NULL);
2194 nodes->next=cube_info->node_queue;
2195 cube_info->node_queue=nodes;
2196 cube_info->next_node=nodes->nodes;
2197 cube_info->free_nodes=QNodesInAList;
2200 cube_info->free_nodes--;
2201 node_info=cube_info->next_node++;
2202 (void) memset(node_info,0,
sizeof(*node_info));
2203 node_info->parent=parent;
2205 node_info->level=level;
2251MagickExport MagickBooleanType GetImageQuantizeError(Image *image,
2252 ExceptionInfo *exception)
2264 mean_error_per_pixel;
2270 assert(image != (Image *) NULL);
2271 assert(image->signature == MagickCoreSignature);
2272 if (IsEventLogging() != MagickFalse)
2273 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
2274 image->total_colors=GetNumberColors(image,(FILE *) NULL,exception);
2275 (void) memset(&image->error,0,
sizeof(image->error));
2276 if (image->storage_class == DirectClass)
2280 area=3.0*image->columns*image->rows;
2282 mean_error_per_pixel=0.0;
2284 image_view=AcquireVirtualCacheView(image,exception);
2285 for (y=0; y < (ssize_t) image->rows; y++)
2293 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
2294 if (p == (
const Quantum *) NULL)
2296 for (x=0; x < (ssize_t) image->columns; x++)
2298 index=(ssize_t) GetPixelIndex(image,p);
2299 if (image->alpha_trait != UndefinedPixelTrait)
2301 alpha=(double) (QuantumScale*(
double) GetPixelAlpha(image,p));
2302 beta=(double) (QuantumScale*image->colormap[index].alpha);
2304 distance=fabs((
double) (alpha*(
double) GetPixelRed(image,p)-beta*
2305 image->colormap[index].red));
2306 mean_error_per_pixel+=distance;
2307 mean_error+=distance*distance;
2308 if (distance > maximum_error)
2309 maximum_error=distance;
2310 distance=fabs((
double) (alpha*(
double) GetPixelGreen(image,p)-beta*
2311 image->colormap[index].green));
2312 mean_error_per_pixel+=distance;
2313 mean_error+=distance*distance;
2314 if (distance > maximum_error)
2315 maximum_error=distance;
2316 distance=fabs((
double) (alpha*(
double) GetPixelBlue(image,p)-beta*
2317 image->colormap[index].blue));
2318 mean_error_per_pixel+=distance;
2319 mean_error+=distance*distance;
2320 if (distance > maximum_error)
2321 maximum_error=distance;
2322 p+=(ptrdiff_t) GetPixelChannels(image);
2325 image_view=DestroyCacheView(image_view);
2326 image->error.mean_error_per_pixel=(double) mean_error_per_pixel/area;
2327 image->error.normalized_mean_error=(double) QuantumScale*QuantumScale*
2329 image->error.normalized_maximum_error=(double) QuantumScale*maximum_error;
2355MagickExport
void GetQuantizeInfo(QuantizeInfo *quantize_info)
2357 assert(quantize_info != (QuantizeInfo *) NULL);
2358 if (IsEventLogging() != MagickFalse)
2359 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"...");
2360 (void) memset(quantize_info,0,
sizeof(*quantize_info));
2361 quantize_info->number_colors=256;
2362 quantize_info->dither_method=RiemersmaDitherMethod;
2363 quantize_info->colorspace=UndefinedColorspace;
2364 quantize_info->measure_error=MagickFalse;
2365 quantize_info->signature=MagickCoreSignature;
2414static KmeansInfo **DestroyKmeansTLS(KmeansInfo **kmeans_info)
2419 assert(kmeans_info != (KmeansInfo **) NULL);
2420 for (i=0; i < (ssize_t) GetMagickResourceLimit(ThreadResource); i++)
2421 if (kmeans_info[i] != (KmeansInfo *) NULL)
2422 kmeans_info[i]=(KmeansInfo *) RelinquishMagickMemory(kmeans_info[i]);
2423 kmeans_info=(KmeansInfo **) RelinquishMagickMemory(kmeans_info);
2424 return(kmeans_info);
2427static int DominantColorCompare(
const void *x,
const void *y)
2433 pixel_1=(PixelInfo *) x;
2434 pixel_2=(PixelInfo *) y;
2435 return((
int) pixel_2->count-(
int) pixel_1->count);
2438static KmeansInfo **AcquireKmeansTLS(
const size_t number_colors)
2449 number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
2450 kmeans_info=(KmeansInfo **) AcquireQuantumMemory(number_threads,
2451 sizeof(*kmeans_info));
2452 if (kmeans_info == (KmeansInfo **) NULL)
2453 return((KmeansInfo **) NULL);
2454 (void) memset(kmeans_info,0,number_threads*
sizeof(*kmeans_info));
2455 for (i=0; i < (ssize_t) number_threads; i++)
2457 kmeans_info[i]=(KmeansInfo *) AcquireQuantumMemory(number_colors,
2458 sizeof(**kmeans_info));
2459 if (kmeans_info[i] == (KmeansInfo *) NULL)
2460 return(DestroyKmeansTLS(kmeans_info));
2462 return(kmeans_info);
2465static inline double KmeansMetric(
const Image *magick_restrict image,
2466 const Quantum *magick_restrict p,
const PixelInfo *magick_restrict q)
2475 if ((image->alpha_trait != UndefinedPixelTrait) ||
2476 (q->alpha_trait != UndefinedPixelTrait))
2478 pixel=(double) GetPixelAlpha(image,p)-(q->alpha_trait !=
2479 UndefinedPixelTrait ? q->alpha : (double) OpaqueAlpha);
2480 metric+=pixel*pixel;
2481 if (image->alpha_trait != UndefinedPixelTrait)
2482 gamma*=QuantumScale*(double) GetPixelAlpha(image,p);
2483 if (q->alpha_trait != UndefinedPixelTrait)
2484 gamma*=QuantumScale*q->alpha;
2486 if (image->colorspace == CMYKColorspace)
2488 pixel=QuantumScale*((double) GetPixelBlack(image,p)-q->black);
2489 metric+=gamma*pixel*pixel;
2490 gamma*=QuantumScale*((double) QuantumRange-(double)
2491 GetPixelBlack(image,p));
2492 gamma*=QuantumScale*((double) QuantumRange-q->black);
2495 pixel=QuantumScale*((double) GetPixelRed(image,p)-q->red);
2496 if (IsHueCompatibleColorspace(image->colorspace) != MagickFalse)
2498 if (fabs((
double) pixel) > 0.5)
2502 metric+=gamma*pixel*pixel;
2503 pixel=QuantumScale*((double) GetPixelGreen(image,p)-q->green);
2504 metric+=gamma*pixel*pixel;
2505 pixel=QuantumScale*((double) GetPixelBlue(image,p)-q->blue);
2506 metric+=gamma*pixel*pixel;
2510MagickExport MagickBooleanType KmeansImage(Image *image,
2511 const size_t number_colors,
const size_t max_iterations,
const double tolerance,
2512 ExceptionInfo *exception)
2514#define KmeansImageTag "Kmeans/Image"
2515#define RandomColorComponent(info) \
2516 ((double) QuantumRange*GetPseudoRandomValue(info))
2522 tuple[MagickPathExtent];
2546 assert(image != (Image *) NULL);
2547 assert(image->signature == MagickCoreSignature);
2548 assert(exception != (ExceptionInfo *) NULL);
2549 assert(exception->signature == MagickCoreSignature);
2550 if (IsEventLogging() != MagickFalse)
2551 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
2552 if (max_iterations == 0)
2553 return(MagickFalse);
2554 colors=GetImageArtifact(image,
"kmeans:seed-colors");
2555 if (colors == (
const char *) NULL)
2569 quantize_info=AcquireQuantizeInfo((ImageInfo *) NULL);
2570 quantize_info->colorspace=image->colorspace;
2571 quantize_info->number_colors=number_colors;
2572 quantize_info->dither_method=NoDitherMethod;
2573 n=(ssize_t) number_colors;
2574 for (depth=1; n != 0; depth++)
2576 cube_info=GetQCubeInfo(quantize_info,depth,number_colors);
2577 if (cube_info == (QCubeInfo *) NULL)
2579 quantize_info=DestroyQuantizeInfo(quantize_info);
2580 ThrowBinaryException(ResourceLimitError,
"MemoryAllocationFailed",
2583 status=ClassifyImageColors(cube_info,image,exception);
2584 if (status != MagickFalse)
2586 if (cube_info->colors > cube_info->maximum_colors)
2587 ReduceImageColors(image,cube_info);
2588 status=SetImageColormap(image,cube_info,exception);
2590 DestroyQCubeInfo(cube_info);
2591 quantize_info=DestroyQuantizeInfo(quantize_info);
2592 if (status == MagickFalse)
2598 color[MagickPathExtent];
2606 status=AcquireImageColormap(image,number_colors,exception);
2607 if (status == MagickFalse)
2609 for (n=0, p=colors; n < (ssize_t) image->colors; n++)
2614 for (q=p; *q !=
'\0'; q++)
2617 (void) CopyMagickString(color,p,(
size_t) MagickMin(q-p+1,
2619 (void) QueryColorCompliance(color,AllCompliance,image->colormap+n,
2628 if (n < (ssize_t) image->colors)
2636 random_info=AcquireRandomInfo();
2637 for ( ; n < (ssize_t) image->colors; n++)
2639 (void) QueryColorCompliance(
"#000",AllCompliance,image->colormap+n,
2641 image->colormap[n].red=RandomColorComponent(random_info);
2642 image->colormap[n].green=RandomColorComponent(random_info);
2643 image->colormap[n].blue=RandomColorComponent(random_info);
2644 if (image->alpha_trait != UndefinedPixelTrait)
2645 image->colormap[n].alpha=RandomColorComponent(random_info);
2646 if (image->colorspace == CMYKColorspace)
2647 image->colormap[n].black=RandomColorComponent(random_info);
2649 random_info=DestroyRandomInfo(random_info);
2655 kmeans_pixels=AcquireKmeansTLS(number_colors);
2656 if (kmeans_pixels == (KmeansInfo **) NULL)
2657 ThrowBinaryException(ResourceLimitError,
"MemoryAllocationFailed",
2659 previous_tolerance=0.0;
2660 verbose=IsStringTrue(GetImageArtifact(image,
"verbose"));
2661 number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
2662 image_view=AcquireAuthenticCacheView(image,exception);
2663 for (n=0; n < (ssize_t) max_iterations; n++)
2672 for (j=0; j < (ssize_t) number_threads; j++)
2673 (
void) memset(kmeans_pixels[j],0,image->colors*
sizeof(*kmeans_pixels[j]));
2674#if defined(MAGICKCORE_OPENMP_SUPPORT)
2675 #pragma omp parallel for schedule(dynamic) shared(status) \
2676 magick_number_threads(image,image,image->rows,1)
2678 for (y=0; y < (ssize_t) image->rows; y++)
2681 id = GetOpenMPThreadId();
2689 if (status == MagickFalse)
2691 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2692 if (q == (Quantum *) NULL)
2697 for (x=0; x < (ssize_t) image->columns; x++)
2710 min_distance=KmeansMetric(image,q,image->colormap+0);
2711 for (i=1; i < (ssize_t) image->colors; i++)
2716 if (min_distance <= MagickEpsilon)
2718 distance=KmeansMetric(image,q,image->colormap+i);
2719 if (distance < min_distance)
2721 min_distance=distance;
2725 kmeans_pixels[id][k].red+=QuantumScale*(double) GetPixelRed(image,q);
2726 kmeans_pixels[id][k].green+=QuantumScale*(double)
2727 GetPixelGreen(image,q);
2728 kmeans_pixels[id][k].blue+=QuantumScale*(double) GetPixelBlue(image,q);
2729 if (image->alpha_trait != UndefinedPixelTrait)
2730 kmeans_pixels[id][k].alpha+=QuantumScale*(double)
2731 GetPixelAlpha(image,q);
2732 if (image->colorspace == CMYKColorspace)
2733 kmeans_pixels[id][k].black+=QuantumScale*(double)
2734 GetPixelBlack(image,q);
2735 kmeans_pixels[id][k].count++;
2736 kmeans_pixels[id][k].distortion+=min_distance;
2737 SetPixelIndex(image,(Quantum) k,q);
2738 q+=(ptrdiff_t) GetPixelChannels(image);
2740 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2743 if (status == MagickFalse)
2748 for (j=1; j < (ssize_t) number_threads; j++)
2753 for (k=0; k < (ssize_t) image->colors; k++)
2755 kmeans_pixels[0][k].red+=kmeans_pixels[j][k].red;
2756 kmeans_pixels[0][k].green+=kmeans_pixels[j][k].green;
2757 kmeans_pixels[0][k].blue+=kmeans_pixels[j][k].blue;
2758 if (image->alpha_trait != UndefinedPixelTrait)
2759 kmeans_pixels[0][k].alpha+=kmeans_pixels[j][k].alpha;
2760 if (image->colorspace == CMYKColorspace)
2761 kmeans_pixels[0][k].black+=kmeans_pixels[j][k].black;
2762 kmeans_pixels[0][k].count+=kmeans_pixels[j][k].count;
2763 kmeans_pixels[0][k].distortion+=kmeans_pixels[j][k].distortion;
2770 for (j=0; j < (ssize_t) image->colors; j++)
2775 gamma=MagickSafeReciprocal((
double) kmeans_pixels[0][j].count);
2776 image->colormap[j].red=gamma*(double) QuantumRange*
2777 kmeans_pixels[0][j].red;
2778 image->colormap[j].green=gamma*(double) QuantumRange*
2779 kmeans_pixels[0][j].green;
2780 image->colormap[j].blue=gamma*(double) QuantumRange*
2781 kmeans_pixels[0][j].blue;
2782 if (image->alpha_trait != UndefinedPixelTrait)
2783 image->colormap[j].alpha=gamma*(double) QuantumRange*
2784 kmeans_pixels[0][j].alpha;
2785 if (image->colorspace == CMYKColorspace)
2786 image->colormap[j].black=gamma*(double) QuantumRange*
2787 kmeans_pixels[0][j].black;
2788 image->colormap[j].count=(MagickSizeType) kmeans_pixels[0][j].count;
2789 distortion+=kmeans_pixels[0][j].distortion;
2791 if (image->debug != MagickFalse)
2792 (void) LogMagickEvent(ImageEvent,GetMagickModule(),
2793 "distortion[%.17g]: %*g %*g\n",(
double) n,GetMagickPrecision(),
2794 distortion,GetMagickPrecision(),fabs(distortion-previous_tolerance));
2795 if (fabs(distortion-previous_tolerance) <= tolerance)
2797 previous_tolerance=distortion;
2798 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2803 proceed=SetImageProgress(image,KmeansImageTag,(MagickOffsetType) n,
2805 if (proceed == MagickFalse)
2809 image_view=DestroyCacheView(image_view);
2810 if (verbose != MagickFalse)
2811 for (n=0; n < (ssize_t) image->colors; n++)
2813 GetColorTuple(image->colormap+n,MagickTrue,tuple);
2814 (void) FormatLocaleFile(stderr,
"%s %.17g\n",tuple,(
double)
2815 image->colormap[n].count);
2817 dominant_image=CloneImage(image,0,0,MagickTrue,exception);
2818 if (dominant_image != (Image *) NULL)
2823 qsort((
void *) dominant_image->colormap,dominant_image->colors,
2824 sizeof(*dominant_image->colormap),DominantColorCompare);
2825 GetColorTuple(dominant_image->colormap,MagickTrue,tuple);
2826 dominant_image=DestroyImage(dominant_image);
2827 (void) SetImageProperty(image,
"dominant-color",tuple,exception);
2829 kmeans_pixels=DestroyKmeansTLS(kmeans_pixels);
2830 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2831 (void) SetImageProgress(image,KmeansImageTag,(MagickOffsetType)
2832 max_iterations-1,max_iterations);
2833 if (status == MagickFalse)
2835 return(SyncImage(image,exception));
2871static inline double MagickRound(
double x)
2876 if ((x-floor(x)) < (ceil(x)-x))
2881static inline Quantum PosterizePixel(
const Quantum pixel,
const size_t levels)
2883 double posterize_pixel = QuantumRange*MagickRound(QuantumScale*(
double) pixel*
2884 ((
double) levels-1.0))/MagickMax((
double) levels-1.0,1.0);
2885 return(ClampToQuantum((MagickRealType) posterize_pixel));
2888MagickExport MagickBooleanType PosterizeImage(Image *image,
const size_t levels,
2889 const DitherMethod dither_method,ExceptionInfo *exception)
2891#define PosterizeImageTag "Posterize/Image"
2897 status = MagickTrue;
2905 assert(image != (Image *) NULL);
2906 assert(image->signature == MagickCoreSignature);
2907 assert(exception != (ExceptionInfo *) NULL);
2908 assert(exception->signature == MagickCoreSignature);
2909 if (IsEventLogging() != MagickFalse)
2910 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
2911 if ((dither_method != NoDitherMethod) && (levels > 1) && (levels < 17))
2912 for (y=0; y < 1; y++)
2924 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2926 PixelChannel channel = GetPixelChannelChannel(image,i);
2927 PixelTrait traits = GetPixelChannelTraits(image,channel);
2928 if ((traits & UpdatePixelTrait) != 0)
2931 number_columns=(size_t) pow((
double) levels,(
double) channels);
2932 map_image=CloneImage(image,number_columns,1,MagickTrue,exception);
2933 if (map_image == (Image *) NULL)
2938 if (SetImageStorageClass(map_image,DirectClass,exception) == MagickFalse)
2948 scale = (MagickRealType) QuantumRange/(levels-1.0);
2960 map_image_view=AcquireAuthenticCacheView (map_image,exception);
2961 q=GetCacheViewAuthenticPixels(map_image_view,0,0,number_columns,1,
2963 if (q == (
const Quantum *) NULL)
2965 map_image_view=DestroyCacheView(map_image_view);
2969 for (x=0; x < (ssize_t) number_columns; x++)
2971 size_t remainder = (size_t) x;
2972 for (c=0; c < (ssize_t) GetPixelChannels(image); c++)
2974 PixelChannel channel = GetPixelChannelChannel(image,c);
2975 PixelTrait traits = GetPixelChannelTraits(image,channel);
2976 if ((traits & UpdatePixelTrait) != 0)
2978 size_t value = remainder % levels;
2979 SetPixelChannel(map_image,channel,(
const Quantum) (scale*value),q);
2980 remainder=(remainder-value)/levels;
2983 q+=(ptrdiff_t) GetPixelChannels(map_image);
2985 if (SyncCacheViewAuthenticPixels(map_image_view,exception) == MagickFalse)
2987 map_image_view=DestroyCacheView(map_image_view);
2991 map_image_view=DestroyCacheView(map_image_view);
2993 if (status != MagickFalse)
2998 QuantizeInfo *quantize_info = AcquireQuantizeInfo((ImageInfo *) NULL);
2999 quantize_info->dither_method=dither_method;
3000 (void) RemapImage(quantize_info,image,map_image,exception);
3001 quantize_info=DestroyQuantizeInfo(quantize_info);
3003 map_image=DestroyImage(map_image);
3010 if (image->storage_class == PseudoClass)
3015#if defined(MAGICKCORE_OPENMP_SUPPORT)
3016 #pragma omp parallel for schedule(static) shared(progress,status) \
3017 magick_number_threads(image,image,image->colors,1)
3019 for (i=0; i < (ssize_t) image->colors; i++)
3024 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
3025 image->colormap[i].red=(MagickRealType)
3026 PosterizePixel((
const Quantum) image->colormap[i].red,levels);
3027 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3028 image->colormap[i].green=(MagickRealType)
3029 PosterizePixel((
const Quantum) image->colormap[i].green,levels);
3030 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3031 image->colormap[i].blue=(MagickRealType)
3032 PosterizePixel((
const Quantum) image->colormap[i].blue,levels);
3033 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
3034 image->colormap[i].alpha=(MagickRealType)
3035 PosterizePixel((
const Quantum) image->colormap[i].alpha,levels);
3042 image_view=AcquireAuthenticCacheView(image,exception);
3043#if defined(MAGICKCORE_OPENMP_SUPPORT)
3044 #pragma omp parallel for schedule(static) shared(progress,status) \
3045 magick_number_threads(image,image,image->rows,1)
3047 for (y=0; y < (ssize_t) image->rows; y++)
3055 if (status == MagickFalse)
3057 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
3059 if (q == (Quantum *) NULL)
3064 for (x=0; x < (ssize_t) image->columns; x++)
3069 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3071 PixelChannel channel = GetPixelChannelChannel(image,i);
3072 PixelTrait traits = GetPixelChannelTraits(image,channel);
3073 if ((traits & UpdatePixelTrait) == 0)
3075 SetPixelChannel(image,channel,PosterizePixel(q[i],levels),q);
3077 q+=(ptrdiff_t) GetPixelChannels(image);
3079 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3081 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3086#if defined(MAGICKCORE_OPENMP_SUPPORT)
3090 proceed=SetImageProgress(image,PosterizeImageTag,progress,
3092 if (proceed == MagickFalse)
3096 image_view=DestroyCacheView(image_view);
3098 QuantizeInfo *quantize_info=AcquireQuantizeInfo((ImageInfo *) NULL);
3099 quantize_info->number_colors=(size_t) MagickMin(levels*levels*levels,
3101 quantize_info->dither_method=dither_method;
3102 status=QuantizeImage(quantize_info,image,exception);
3103 quantize_info=DestroyQuantizeInfo(quantize_info);
3134static void PruneChild(QCubeInfo *cube_info,
const QNodeInfo *node_info)
3148 number_children=cube_info->associate_alpha == MagickFalse ? 8UL : 16UL;
3149 for (i=0; i < (ssize_t) number_children; i++)
3150 if (node_info->child[i] != (QNodeInfo *) NULL)
3151 PruneChild(cube_info,node_info->child[i]);
3152 if (cube_info->nodes > cube_info->maximum_colors)
3157 parent=node_info->parent;
3158 parent->number_unique+=node_info->number_unique;
3159 parent->total_color.red+=node_info->total_color.red;
3160 parent->total_color.green+=node_info->total_color.green;
3161 parent->total_color.blue+=node_info->total_color.blue;
3162 parent->total_color.alpha+=node_info->total_color.alpha;
3163 parent->child[node_info->id]=(QNodeInfo *) NULL;
3193static void PruneLevel(QCubeInfo *cube_info,
const QNodeInfo *node_info)
3204 number_children=cube_info->associate_alpha == MagickFalse ? 8UL : 16UL;
3205 for (i=0; i < (ssize_t) number_children; i++)
3206 if (node_info->child[i] != (QNodeInfo *) NULL)
3207 PruneLevel(cube_info,node_info->child[i]);
3208 if (node_info->level == cube_info->depth)
3209 PruneChild(cube_info,node_info);
3238static void PruneToCubeDepth(QCubeInfo *cube_info,
const QNodeInfo *node_info)
3249 number_children=cube_info->associate_alpha == MagickFalse ? 8UL : 16UL;
3250 for (i=0; i < (ssize_t) number_children; i++)
3251 if (node_info->child[i] != (QNodeInfo *) NULL)
3252 PruneToCubeDepth(cube_info,node_info->child[i]);
3253 if (node_info->level > cube_info->depth)
3254 PruneChild(cube_info,node_info);
3287MagickExport MagickBooleanType QuantizeImage(
const QuantizeInfo *quantize_info,
3288 Image *image,ExceptionInfo *exception)
3303 assert(quantize_info != (
const QuantizeInfo *) NULL);
3304 assert(quantize_info->signature == MagickCoreSignature);
3305 assert(image != (Image *) NULL);
3306 assert(image->signature == MagickCoreSignature);
3307 assert(exception != (ExceptionInfo *) NULL);
3308 assert(exception->signature == MagickCoreSignature);
3309 if (IsEventLogging() != MagickFalse)
3310 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
3311 maximum_colors=quantize_info->number_colors;
3312 if (maximum_colors == 0)
3313 maximum_colors=MaxColormapSize;
3314 if (maximum_colors > MaxColormapSize)
3315 maximum_colors=MaxColormapSize;
3316 type=IdentifyImageGray(image,exception);
3317 if (IsGrayImageType(type) != MagickFalse)
3318 (void) SetGrayscaleImage(image,exception);
3319 depth=quantize_info->tree_depth;
3328 colors=maximum_colors;
3329 for (depth=1; colors != 0; depth++)
3331 if ((quantize_info->dither_method != NoDitherMethod) && (depth > 2))
3333 if ((image->alpha_trait != UndefinedPixelTrait) && (depth > 5))
3335 if (IsGrayImageType(type) != MagickFalse)
3341 cube_info=GetQCubeInfo(quantize_info,depth,maximum_colors);
3342 if (cube_info == (QCubeInfo *) NULL)
3343 ThrowBinaryException(ResourceLimitError,
"MemoryAllocationFailed",
3345 status=ClassifyImageColors(cube_info,image,exception);
3346 if (status != MagickFalse)
3351 if (cube_info->colors > cube_info->maximum_colors)
3352 ReduceImageColors(image,cube_info);
3353 status=AssignImageColors(image,cube_info,exception);
3355 DestroyQCubeInfo(cube_info);
3389MagickExport MagickBooleanType QuantizeImages(
const QuantizeInfo *quantize_info,
3390 Image *images,ExceptionInfo *exception)
3399 MagickProgressMonitor
3413 assert(quantize_info != (
const QuantizeInfo *) NULL);
3414 assert(quantize_info->signature == MagickCoreSignature);
3415 assert(images != (Image *) NULL);
3416 assert(images->signature == MagickCoreSignature);
3417 assert(exception != (ExceptionInfo *) NULL);
3418 assert(exception->signature == MagickCoreSignature);
3419 if (IsEventLogging() != MagickFalse)
3420 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",images->filename);
3421 if (GetNextImageInList(images) == (Image *) NULL)
3426 status=QuantizeImage(quantize_info,images,exception);
3430 maximum_colors=quantize_info->number_colors;
3431 if (maximum_colors == 0)
3432 maximum_colors=MaxColormapSize;
3433 if (maximum_colors > MaxColormapSize)
3434 maximum_colors=MaxColormapSize;
3435 depth=quantize_info->tree_depth;
3444 colors=maximum_colors;
3445 for (depth=1; colors != 0; depth++)
3447 if (quantize_info->dither_method != NoDitherMethod)
3453 cube_info=GetQCubeInfo(quantize_info,depth,maximum_colors);
3454 if (cube_info == (QCubeInfo *) NULL)
3456 (void) ThrowMagickException(exception,GetMagickModule(),
3457 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",images->filename);
3458 return(MagickFalse);
3460 number_images=GetImageListLength(images);
3462 for (i=0; image != (Image *) NULL; i++)
3464 progress_monitor=SetImageProgressMonitor(image,(MagickProgressMonitor) NULL,
3465 image->client_data);
3466 status=ClassifyImageColors(cube_info,image,exception);
3467 if (status == MagickFalse)
3469 (void) SetImageProgressMonitor(image,progress_monitor,image->client_data);
3470 proceed=SetImageProgress(image,AssignImageTag,(MagickOffsetType) i,
3472 if (proceed == MagickFalse)
3474 image=GetNextImageInList(image);
3476 if (status != MagickFalse)
3481 ReduceImageColors(images,cube_info);
3483 for (i=0; image != (Image *) NULL; i++)
3485 progress_monitor=SetImageProgressMonitor(image,(MagickProgressMonitor)
3486 NULL,image->client_data);
3487 status=AssignImageColors(image,cube_info,exception);
3488 if (status == MagickFalse)
3490 (void) SetImageProgressMonitor(image,progress_monitor,
3491 image->client_data);
3492 proceed=SetImageProgress(image,AssignImageTag,(MagickOffsetType) i,
3494 if (proceed == MagickFalse)
3496 image=GetNextImageInList(image);
3499 DestroyQCubeInfo(cube_info);
3536static size_t QuantizeErrorFlatten(
const QCubeInfo *cube_info,
3537 const QNodeInfo *node_info,
const ssize_t offset,
double *quantize_error)
3546 if (offset >= (ssize_t) cube_info->nodes)
3548 quantize_error[offset]=node_info->quantize_error;
3550 number_children=cube_info->associate_alpha == MagickFalse ? 8UL : 16UL;
3551 for (i=0; i < (ssize_t) number_children ; i++)
3552 if (node_info->child[i] != (QNodeInfo *) NULL)
3553 n+=QuantizeErrorFlatten(cube_info,node_info->child[i],offset+(ssize_t) n,
3583static void Reduce(QCubeInfo *cube_info,
const QNodeInfo *node_info)
3594 number_children=cube_info->associate_alpha == MagickFalse ? 8UL : 16UL;
3595 for (i=0; i < (ssize_t) number_children; i++)
3596 if (node_info->child[i] != (QNodeInfo *) NULL)
3597 Reduce(cube_info,node_info->child[i]);
3598 if (node_info->quantize_error <= cube_info->pruning_threshold)
3599 PruneChild(cube_info,node_info);
3605 if (node_info->number_unique > 0)
3606 cube_info->colors++;
3607 if (node_info->quantize_error < cube_info->next_threshold)
3608 cube_info->next_threshold=node_info->quantize_error;
3667static int QuantizeErrorCompare(
const void *error_p,
const void *error_q)
3673 p=(
double *) error_p;
3674 q=(
double *) error_q;
3677 if (fabs(*q-*p) <= MagickEpsilon)
3682static void ReduceImageColors(
const Image *image,QCubeInfo *cube_info)
3684#define ReduceImageTag "Reduce/Image"
3695 cube_info->next_threshold=0.0;
3696 if (cube_info->colors > cube_info->maximum_colors)
3704 quantize_error=(
double *) AcquireQuantumMemory(cube_info->nodes,
3705 sizeof(*quantize_error));
3706 if (quantize_error != (
double *) NULL)
3708 (void) QuantizeErrorFlatten(cube_info,cube_info->root,0,
3710 qsort(quantize_error,cube_info->nodes,
sizeof(
double),
3711 QuantizeErrorCompare);
3712 if (cube_info->nodes > (110*(cube_info->maximum_colors+1)/100))
3713 cube_info->next_threshold=quantize_error[cube_info->nodes-110*
3714 (cube_info->maximum_colors+1)/100];
3715 quantize_error=(
double *) RelinquishMagickMemory(quantize_error);
3718 for (span=cube_info->colors; cube_info->colors > cube_info->maximum_colors; )
3720 cube_info->pruning_threshold=cube_info->next_threshold;
3721 cube_info->next_threshold=cube_info->root->quantize_error-1;
3722 cube_info->colors=0;
3723 Reduce(cube_info,cube_info->root);
3724 offset=(MagickOffsetType) span-(MagickOffsetType) cube_info->colors;
3725 proceed=SetImageProgress(image,ReduceImageTag,offset,span-
3726 cube_info->maximum_colors+1);
3727 if (proceed == MagickFalse)
3762MagickExport MagickBooleanType RemapImage(
const QuantizeInfo *quantize_info,
3763 Image *image,
const Image *remap_image,ExceptionInfo *exception)
3774 assert(image != (Image *) NULL);
3775 assert(image->signature == MagickCoreSignature);
3776 assert(remap_image != (Image *) NULL);
3777 assert(remap_image->signature == MagickCoreSignature);
3778 assert(exception != (ExceptionInfo *) NULL);
3779 assert(exception->signature == MagickCoreSignature);
3780 if (IsEventLogging() != MagickFalse)
3781 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
3782 cube_info=GetQCubeInfo(quantize_info,MaxTreeDepth,MaxColormapSize);
3783 if (cube_info == (QCubeInfo *) NULL)
3784 ThrowBinaryException(ResourceLimitError,
"MemoryAllocationFailed",
3786 cube_info->quantize_info->colorspace=remap_image->colorspace;
3787 status=ClassifyImageColors(cube_info,remap_image,exception);
3788 if (status != MagickFalse)
3793 cube_info->quantize_info->number_colors=cube_info->colors;
3794 if (cube_info->colors > cube_info->maximum_colors)
3795 ReduceImageColors(image,cube_info);
3796 status=AssignImageColors(image,cube_info,exception);
3798 DestroyQCubeInfo(cube_info);
3832MagickExport MagickBooleanType RemapImages(
const QuantizeInfo *quantize_info,
3833 Image *images,
const Image *remap_image,ExceptionInfo *exception)
3844 assert(images != (Image *) NULL);
3845 assert(images->signature == MagickCoreSignature);
3846 assert(exception != (ExceptionInfo *) NULL);
3847 assert(exception->signature == MagickCoreSignature);
3848 if (IsEventLogging() != MagickFalse)
3849 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",images->filename);
3851 if (remap_image == (Image *) NULL)
3856 status=QuantizeImages(quantize_info,images,exception);
3862 cube_info=GetQCubeInfo(quantize_info,MaxTreeDepth,
3863 quantize_info->number_colors);
3864 if (cube_info == (QCubeInfo *) NULL)
3865 ThrowBinaryException(ResourceLimitError,
"MemoryAllocationFailed",
3867 status=ClassifyImageColors(cube_info,remap_image,exception);
3868 if (status != MagickFalse)
3873 cube_info->quantize_info->number_colors=cube_info->colors;
3875 for ( ; image != (Image *) NULL; image=GetNextImageInList(image))
3877 status=AssignImageColors(image,cube_info,exception);
3878 if (status == MagickFalse)
3882 DestroyQCubeInfo(cube_info);
3912#if defined(__cplusplus) || defined(c_plusplus)
3916static int IntensityCompare(
const void *x,
const void *y)
3925 color_1=(PixelInfo *) x;
3926 color_2=(PixelInfo *) y;
3927 intensity=GetPixelInfoIntensity((
const Image *) NULL,color_1)-
3928 GetPixelInfoIntensity((
const Image *) NULL,color_2);
3929 if (intensity < (
double) INT_MIN)
3930 intensity=(double) INT_MIN;
3931 if (intensity > (
double) INT_MAX)
3932 intensity=(double) INT_MAX;
3933 return((
int) intensity);
3936#if defined(__cplusplus) || defined(c_plusplus)
3940static MagickBooleanType SetGrayscaleImage(Image *image,
3941 ExceptionInfo *exception)
3961 assert(image != (Image *) NULL);
3962 assert(image->signature == MagickCoreSignature);
3963 if (image->type != GrayscaleType)
3964 (void) TransformImageColorspace(image,GRAYColorspace,exception);
3965 extent=MagickMax(image->colors+1,MagickMax(MaxColormapSize,MaxMap+1));
3966 colormap_index=(ssize_t *) AcquireQuantumMemory(extent,
3967 sizeof(*colormap_index));
3968 if (colormap_index == (ssize_t *) NULL)
3969 ThrowBinaryException(ResourceLimitError,
"MemoryAllocationFailed",
3971 if (image->storage_class != PseudoClass)
3973 (void) memset(colormap_index,(-1),extent*
sizeof(*colormap_index));
3974 if (AcquireImageColormap(image,MaxColormapSize,exception) == MagickFalse)
3976 colormap_index=(ssize_t *) RelinquishMagickMemory(colormap_index);
3977 ThrowBinaryException(ResourceLimitError,
"MemoryAllocationFailed",
3982 image_view=AcquireAuthenticCacheView(image,exception);
3983#if defined(MAGICKCORE_OPENMP_SUPPORT)
3984 #pragma omp parallel for schedule(static) shared(status) \
3985 magick_number_threads(image,image,image->rows,1)
3987 for (y=0; y < (ssize_t) image->rows; y++)
3995 if (status == MagickFalse)
3997 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
3999 if (q == (Quantum *) NULL)
4004 for (x=0; x < (ssize_t) image->columns; x++)
4009 intensity=ScaleQuantumToMap(GetPixelRed(image,q));
4010 if (colormap_index[intensity] < 0)
4012#if defined(MAGICKCORE_OPENMP_SUPPORT)
4013 #pragma omp critical (MagickCore_SetGrayscaleImage)
4015 if (colormap_index[intensity] < 0)
4017 colormap_index[intensity]=(ssize_t) image->colors;
4018 image->colormap[image->colors].red=(double)
4019 GetPixelRed(image,q);
4020 image->colormap[image->colors].green=(double)
4021 GetPixelGreen(image,q);
4022 image->colormap[image->colors].blue=(double)
4023 GetPixelBlue(image,q);
4027 SetPixelIndex(image,(Quantum) colormap_index[intensity],q);
4028 q+=(ptrdiff_t) GetPixelChannels(image);
4030 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4033 image_view=DestroyCacheView(image_view);
4035 (void) memset(colormap_index,0,extent*
sizeof(*colormap_index));
4036 for (i=0; i < (ssize_t) image->colors; i++)
4037 image->colormap[i].alpha=(
double) i;
4038 qsort((
void *) image->colormap,image->colors,
sizeof(PixelInfo),
4040 colormap=(PixelInfo *) AcquireQuantumMemory(image->colors,
sizeof(*colormap));
4041 if (colormap == (PixelInfo *) NULL)
4043 colormap_index=(ssize_t *) RelinquishMagickMemory(colormap_index);
4044 ThrowBinaryException(ResourceLimitError,
"MemoryAllocationFailed",
4048 colormap[j]=image->colormap[0];
4049 for (i=0; i < (ssize_t) image->colors; i++)
4051 if (IsPixelInfoEquivalent(&colormap[j],&image->colormap[i]) == MagickFalse)
4054 colormap[j]=image->colormap[i];
4056 colormap_index[(ssize_t) image->colormap[i].alpha]=j;
4058 image->colors=(size_t) (j+1);
4059 image->colormap=(PixelInfo *) RelinquishMagickMemory(image->colormap);
4060 image->colormap=colormap;
4062 image_view=AcquireAuthenticCacheView(image,exception);
4063#if defined(MAGICKCORE_OPENMP_SUPPORT)
4064 #pragma omp parallel for schedule(static) shared(status) \
4065 magick_number_threads(image,image,image->rows,1)
4067 for (y=0; y < (ssize_t) image->rows; y++)
4075 if (status == MagickFalse)
4077 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
4078 if (q == (Quantum *) NULL)
4083 for (x=0; x < (ssize_t) image->columns; x++)
4085 SetPixelIndex(image,(Quantum) colormap_index[ScaleQuantumToMap(
4086 GetPixelIndex(image,q))],q);
4087 q+=(ptrdiff_t) GetPixelChannels(image);
4089 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4092 image_view=DestroyCacheView(image_view);
4093 colormap_index=(ssize_t *) RelinquishMagickMemory(colormap_index);
4094 image->type=GrayscaleType;
4095 if (SetImageMonochrome(image,exception) != MagickFalse)
4096 image->type=BilevelType;
4129MagickBooleanType SetImageColormap(Image *image,QCubeInfo *cube_info,
4130 ExceptionInfo *exception)
4135 number_colors=MagickMax(cube_info->maximum_colors,cube_info->colors);
4136 if (AcquireImageColormap(image,number_colors,exception) == MagickFalse)
4137 ThrowBinaryException(ResourceLimitError,
"MemoryAllocationFailed",
4140 DefineImageColormap(image,cube_info,cube_info->root);
4141 if (image->colors != number_colors)
4143 image->colormap=(PixelInfo *) ResizeQuantumMemory(image->colormap,
4144 image->colors+1,
sizeof(*image->colormap));
4145 if (image->colormap == (PixelInfo *) NULL)
4146 ThrowBinaryException(ResourceLimitError,
"MemoryAllocationFailed",