176#include "magick/studio.h"
177#include "magick/artifact.h"
178#include "magick/attribute.h"
179#include "magick/cache-view.h"
180#include "magick/color.h"
181#include "magick/color-private.h"
182#include "magick/colormap.h"
183#include "magick/colorspace.h"
184#include "magick/colorspace-private.h"
185#include "magick/enhance.h"
186#include "magick/exception.h"
187#include "magick/exception-private.h"
188#include "magick/histogram.h"
189#include "magick/image.h"
190#include "magick/image-private.h"
191#include "magick/list.h"
192#include "magick/memory_.h"
193#include "magick/monitor.h"
194#include "magick/monitor-private.h"
195#include "magick/option.h"
196#include "magick/pixel-private.h"
197#include "magick/quantize.h"
198#include "magick/quantum.h"
199#include "magick/resource_.h"
200#include "magick/string_.h"
201#include "magick/string-private.h"
202#include "magick/thread-private.h"
207#if !defined(__APPLE__) && !defined(TARGET_OS_IPHONE)
212#define ErrorQueueLength 16
213#define ErrorRelativeWeight PerceptibleReciprocal(16)
214#define MaxQNodes 266817
215#define MaxTreeDepth 8
216#define QNodesInAList 1920
292 error[ErrorQueueLength];
296 weights[ErrorQueueLength];
322 *GetQCubeInfo(
const QuantizeInfo *,
const size_t,
const size_t);
327static MagickBooleanType
331 SetGrayscaleImage(
Image *);
368 quantize_info=(
QuantizeInfo *) AcquireMagickMemory(
sizeof(*quantize_info));
370 ThrowFatalException(ResourceLimitFatalError,
"MemoryAllocationFailed");
371 GetQuantizeInfo(quantize_info);
377 quantize_info->dither=image_info->dither;
378 option=GetImageOption(image_info,
"dither");
379 if (option != (
const char *) NULL)
380 quantize_info->dither_method=(DitherMethod) ParseCommandOption(
381 MagickDitherOptions,MagickFalse,option);
382 quantize_info->measure_error=image_info->verbose;
384 return(quantize_info);
427static inline void AssociateAlphaPixel(
const QCubeInfo *cube_info,
433 alpha_pixel->index=0;
434 if ((cube_info->associate_alpha == MagickFalse) ||
435 (pixel->opacity == OpaqueOpacity))
437 alpha_pixel->red=(MagickRealType) GetPixelRed(pixel);
438 alpha_pixel->green=(MagickRealType) GetPixelGreen(pixel);
439 alpha_pixel->blue=(MagickRealType) GetPixelBlue(pixel);
440 alpha_pixel->opacity=(MagickRealType) GetPixelOpacity(pixel);
443 alpha=(MagickRealType) (QuantumScale*((MagickRealType) QuantumRange-
444 (MagickRealType) GetPixelOpacity(pixel)));
445 alpha_pixel->red=alpha*(MagickRealType) GetPixelRed(pixel);
446 alpha_pixel->green=alpha*(MagickRealType) GetPixelGreen(pixel);
447 alpha_pixel->blue=alpha*(MagickRealType) GetPixelBlue(pixel);
448 alpha_pixel->opacity=(MagickRealType) GetPixelOpacity(pixel);
451static inline size_t ColorToQNodeId(
const QCubeInfo *cube_info,
457 id=(size_t) (((ScaleQuantumToChar(ClampPixel(GetPixelRed(pixel))) >> index) &
458 0x01) | ((ScaleQuantumToChar(ClampPixel(GetPixelGreen(pixel))) >> index) &
459 0x01) << 1 | ((ScaleQuantumToChar(ClampPixel(GetPixelBlue(pixel))) >>
460 index) & 0x01) << 2);
461 if (cube_info->associate_alpha != MagickFalse)
462 id|=((ScaleQuantumToChar(ClampPixel(GetPixelOpacity(pixel))) >> index) &
467static inline MagickBooleanType IsSameColor(
const Image *image,
470 if ((GetPixelRed(p) != GetPixelRed(q)) ||
471 (GetPixelGreen(p) != GetPixelGreen(q)) ||
472 (GetPixelBlue(p) != GetPixelBlue(q)))
474 if ((image->matte != MagickFalse) &&
475 (GetPixelOpacity(p) != GetPixelOpacity(q)))
480static MagickBooleanType AssignImageColors(
Image *image,
QCubeInfo *cube_info)
482#define AssignImageTag "Assign/Image"
496 colorspace=image->colorspace;
497 if (cube_info->quantize_info->colorspace != UndefinedColorspace)
498 (void) TransformImageColorspace(image,cube_info->quantize_info->colorspace);
499 number_colors=MagickMax(cube_info->colors,cube_info->maximum_colors);
500 if (AcquireImageColormap(image,number_colors) == MagickFalse)
501 ThrowBinaryImageException(ResourceLimitError,
"MemoryAllocationFailed",
504 cube_info->transparent_pixels=0;
505 cube_info->transparent_index=(-1);
506 DefineImageColormap(image,cube_info,cube_info->root);
510 if ((cube_info->quantize_info->dither != MagickFalse) &&
511 (cube_info->quantize_info->dither_method != NoDitherMethod))
512 (void) DitherImage(image,cube_info);
525 exception=(&image->exception);
526 image_view=AcquireAuthenticCacheView(image,exception);
527#if defined(MAGICKCORE_OPENMP_SUPPORT)
528 #pragma omp parallel for schedule(static) shared(status) \
529 magick_number_threads(image,image,image->rows,1)
531 for (y=0; y < (ssize_t) image->rows; y++)
534 *magick_restrict indexes;
548 if (status == MagickFalse)
550 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
557 indexes=GetCacheViewAuthenticIndexQueue(image_view);
559 for (x=0; x < (ssize_t) image->columns; x+=count)
577 for (count=1; (x+count) < (ssize_t) image->columns; count++)
578 if (IsSameColor(image,q,q+count) == MagickFalse)
580 AssociateAlphaPixel(&cube,q,&pixel);
582 for (index=MaxTreeDepth-1; (ssize_t) index > 0; index--)
584 id=ColorToQNodeId(&cube,&pixel,index);
585 if (node_info->child[
id] == (
QNodeInfo *) NULL)
587 node_info=node_info->child[id];
593 cube.distance=(MagickRealType) (4.0*((MagickRealType) QuantumRange+
594 1.0)*((MagickRealType) QuantumRange+1.0)+1.0);
595 ClosestColor(image,&cube,node_info->parent);
596 index=cube.color_number;
597 for (i=0; i < (ssize_t) count; i++)
599 if (image->storage_class == PseudoClass)
600 SetPixelIndex(indexes+x+i,index);
601 if (cube.quantize_info->measure_error == MagickFalse)
603 SetPixelRgb(q,image->colormap+index);
604 if (cube.associate_alpha != MagickFalse)
605 SetPixelOpacity(q,image->colormap[index].opacity);
610 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
612 if (image->progress_monitor != (MagickProgressMonitor) NULL)
617 proceed=SetImageProgress(image,AssignImageTag,(MagickOffsetType) y,
619 if (proceed == MagickFalse)
623 image_view=DestroyCacheView(image_view);
625 if (cube_info->quantize_info->measure_error != MagickFalse)
626 (void) GetImageQuantizeError(image);
627 if ((cube_info->quantize_info->number_colors == 2) &&
628 (IsGrayColorspace(cube_info->quantize_info->colorspace)))
636 intensity=GetPixelLuma(image,image->colormap+0) <
637 (MagickRealType) QuantumRange/2.0 ? 0.0 : (double) QuantumRange;
638 if ((image->colors > 1) &&
639 (GetPixelLuma(image,image->colormap+0) >
640 GetPixelLuma(image,image->colormap+1)))
641 intensity=(
double) QuantumRange;
642 image->colormap[0].red=intensity;
643 image->colormap[0].green=intensity;
644 image->colormap[0].blue=intensity;
645 if (image->colors > 1)
647 image->colormap[1].red=(double) QuantumRange-intensity;
648 image->colormap[1].green=(double) QuantumRange-intensity;
649 image->colormap[1].blue=(double) QuantumRange-intensity;
652 (void) SyncImage(image);
653 if ((cube_info->quantize_info->colorspace != UndefinedColorspace) &&
654 (IssRGBCompatibleColorspace(colorspace) == MagickFalse))
655 (void) TransformImageColorspace(image,colorspace);
720static inline void SetAssociatedAlpha(
const Image *image,
QCubeInfo *cube_info)
725 associate_alpha=image->matte;
726 if ((cube_info->quantize_info->number_colors == 2) &&
727 ((cube_info->quantize_info->colorspace == LinearGRAYColorspace) ||
728 (cube_info->quantize_info->colorspace == GRAYColorspace)))
729 associate_alpha=MagickFalse;
730 cube_info->associate_alpha=associate_alpha;
733static MagickBooleanType ClassifyImageColors(
QCubeInfo *cube_info,
736#define ClassifyImageTag "Classify/Image"
768 SetAssociatedAlpha(image,cube_info);
769 if (cube_info->quantize_info->colorspace != image->colorspace)
771 if ((cube_info->quantize_info->colorspace != UndefinedColorspace) &&
772 (cube_info->quantize_info->colorspace != CMYKColorspace))
773 (void) TransformImageColorspace((
Image *) image,
774 cube_info->quantize_info->colorspace);
776 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
777 (void) TransformImageColorspace((
Image *) image,sRGBColorspace);
779 midpoint.red=(MagickRealType) QuantumRange/2.0;
780 midpoint.green=(MagickRealType) QuantumRange/2.0;
781 midpoint.blue=(MagickRealType) QuantumRange/2.0;
782 midpoint.opacity=(MagickRealType) QuantumRange/2.0;
783 midpoint.index=(MagickRealType) QuantumRange/2.0;
785 image_view=AcquireVirtualCacheView(image,exception);
786 for (y=0; y < (ssize_t) image->rows; y++)
794 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
797 if (cube_info->nodes > MaxQNodes)
802 PruneLevel(cube_info,cube_info->root);
805 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) count)
810 for (count=1; (x+(ssize_t) count) < (ssize_t) image->columns; count++)
811 if (IsSameColor(image,p,p+count) == MagickFalse)
813 AssociateAlphaPixel(cube_info,p,&pixel);
814 index=MaxTreeDepth-1;
815 bisect=((MagickRealType) QuantumRange+1.0)/2.0;
817 node_info=cube_info->root;
818 for (level=1; level <= MaxTreeDepth; level++)
824 id=ColorToQNodeId(cube_info,&pixel,index);
825 mid.red+=(
id & 1) != 0 ? bisect : -bisect;
826 mid.green+=(
id & 2) != 0 ? bisect : -bisect;
827 mid.blue+=(
id & 4) != 0 ? bisect : -bisect;
828 mid.opacity+=(
id & 8) != 0 ? bisect : -bisect;
829 if (node_info->child[
id] == (
QNodeInfo *) NULL)
834 node_info->child[id]=GetQNodeInfo(cube_info,
id,level,node_info);
835 if (node_info->child[
id] == (
QNodeInfo *) NULL)
837 (void) ThrowMagickException(exception,GetMagickModule(),
838 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",
842 if (level == MaxTreeDepth)
848 node_info=node_info->child[id];
849 error.red=QuantumScale*(pixel.red-mid.red);
850 error.green=QuantumScale*(pixel.green-mid.green);
851 error.blue=QuantumScale*(pixel.blue-mid.blue);
852 if (cube_info->associate_alpha != MagickFalse)
853 error.opacity=QuantumScale*(pixel.opacity-mid.opacity);
854 distance=(double) (error.red*error.red+error.green*error.green+
855 error.blue*error.blue+error.opacity*error.opacity);
856 if (IsNaN(distance) != 0)
858 node_info->quantize_error+=count*sqrt(distance);
859 cube_info->root->quantize_error+=node_info->quantize_error;
865 node_info->number_unique+=count;
866 node_info->total_color.red+=count*QuantumScale*(MagickRealType)
867 ClampPixel(pixel.red);
868 node_info->total_color.green+=count*QuantumScale*(MagickRealType)
869 ClampPixel(pixel.green);
870 node_info->total_color.blue+=count*QuantumScale*(MagickRealType)
871 ClampPixel(pixel.blue);
872 if (cube_info->associate_alpha != MagickFalse)
873 node_info->total_color.opacity+=count*QuantumScale*(MagickRealType)
874 ClampPixel(pixel.opacity);
876 node_info->total_color.opacity+=count*QuantumScale*(MagickRealType)
877 ClampPixel(OpaqueOpacity);
878 p+=(ptrdiff_t) count;
880 if (cube_info->colors > cube_info->maximum_colors)
882 PruneToCubeDepth(cube_info,cube_info->root);
885 proceed=SetImageProgress(image,ClassifyImageTag,(MagickOffsetType) y,
887 if (proceed == MagickFalse)
890 for (y++; y < (ssize_t) image->rows; y++)
898 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
901 if (cube_info->nodes > MaxQNodes)
906 PruneLevel(cube_info,cube_info->root);
909 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) count)
914 for (count=1; (x+(ssize_t) count) < (ssize_t) image->columns; count++)
915 if (IsSameColor(image,p,p+count) == MagickFalse)
917 AssociateAlphaPixel(cube_info,p,&pixel);
918 index=MaxTreeDepth-1;
919 bisect=((MagickRealType) QuantumRange+1.0)/2.0;
921 node_info=cube_info->root;
922 for (level=1; level <= cube_info->depth; level++)
928 id=ColorToQNodeId(cube_info,&pixel,index);
929 mid.red+=(
id & 1) != 0 ? bisect : -bisect;
930 mid.green+=(
id & 2) != 0 ? bisect : -bisect;
931 mid.blue+=(
id & 4) != 0 ? bisect : -bisect;
932 mid.opacity+=(
id & 8) != 0 ? bisect : -bisect;
933 if (node_info->child[
id] == (
QNodeInfo *) NULL)
938 node_info->child[id]=GetQNodeInfo(cube_info,
id,level,node_info);
939 if (node_info->child[
id] == (
QNodeInfo *) NULL)
941 (void) ThrowMagickException(exception,GetMagickModule(),
942 ResourceLimitError,
"MemoryAllocationFailed",
"%s",
946 if (level == cube_info->depth)
952 node_info=node_info->child[id];
953 error.red=QuantumScale*(pixel.red-mid.red);
954 error.green=QuantumScale*(pixel.green-mid.green);
955 error.blue=QuantumScale*(pixel.blue-mid.blue);
956 if (cube_info->associate_alpha != MagickFalse)
957 error.opacity=QuantumScale*(pixel.opacity-mid.opacity);
958 distance=(double) (error.red*error.red+error.green*error.green+
959 error.blue*error.blue+error.opacity*error.opacity);
960 if (IsNaN(distance) != 0)
962 node_info->quantize_error+=count*sqrt(distance);
963 cube_info->root->quantize_error+=node_info->quantize_error;
969 node_info->number_unique+=count;
970 node_info->total_color.red+=count*QuantumScale*(MagickRealType)
971 ClampPixel(pixel.red);
972 node_info->total_color.green+=count*QuantumScale*(MagickRealType)
973 ClampPixel(pixel.green);
974 node_info->total_color.blue+=count*QuantumScale*(MagickRealType)
975 ClampPixel(pixel.blue);
976 if (cube_info->associate_alpha != MagickFalse)
977 node_info->total_color.opacity+=count*QuantumScale*(MagickRealType)
978 ClampPixel(pixel.opacity);
980 node_info->total_color.opacity+=count*QuantumScale*(MagickRealType)
981 ClampPixel(OpaqueOpacity);
982 p+=(ptrdiff_t) count;
984 proceed=SetImageProgress(image,ClassifyImageTag,(MagickOffsetType) y,
986 if (proceed == MagickFalse)
989 image_view=DestroyCacheView(image_view);
990 if (cube_info->quantize_info->colorspace != image->colorspace)
991 if ((cube_info->quantize_info->colorspace != UndefinedColorspace) &&
992 (cube_info->quantize_info->colorspace != CMYKColorspace))
993 (void) TransformImageColorspace((
Image *) image,sRGBColorspace);
994 return(y < (ssize_t) image->rows ? MagickFalse : MagickTrue);
1028 clone_info=(
QuantizeInfo *) AcquireMagickMemory(
sizeof(*clone_info));
1030 ThrowFatalException(ResourceLimitFatalError,
"MemoryAllocationFailed");
1031 GetQuantizeInfo(clone_info);
1034 clone_info->number_colors=quantize_info->number_colors;
1035 clone_info->tree_depth=quantize_info->tree_depth;
1036 clone_info->dither=quantize_info->dither;
1037 clone_info->dither_method=quantize_info->dither_method;
1038 clone_info->colorspace=quantize_info->colorspace;
1039 clone_info->measure_error=quantize_info->measure_error;
1084 number_children=cube_info->associate_alpha == MagickFalse ? 8UL : 16UL;
1085 for (i=0; i < (ssize_t) number_children; i++)
1086 if (node_info->child[i] != (
QNodeInfo *) NULL)
1087 ClosestColor(image,cube_info,node_info->child[i]);
1088 if (node_info->number_unique != 0)
1105 p=image->colormap+node_info->color_number;
1106 q=(&cube_info->target);
1109 if (cube_info->associate_alpha != MagickFalse)
1111 alpha=(MagickRealType) QuantumScale*GetPixelAlpha(p);
1112 beta=(MagickRealType) QuantumScale*GetPixelAlpha(q);
1114 pixel=alpha*GetPixelRed(p)-beta*GetPixelRed(q);
1115 distance=pixel*pixel;
1116 if (distance <= cube_info->distance)
1118 pixel=(MagickRealType) alpha*GetPixelGreen(p)-beta*GetPixelGreen(q);
1119 distance+=pixel*pixel;
1120 if (distance <= cube_info->distance)
1122 pixel=(MagickRealType) alpha*GetPixelBlue(p)-beta*GetPixelBlue(q);
1123 distance+=pixel*pixel;
1124 if (distance <= cube_info->distance)
1126 if (cube_info->associate_alpha != MagickFalse)
1128 pixel=(MagickRealType) GetPixelAlpha(p)-GetPixelAlpha(q);
1129 distance+=pixel*pixel;
1131 if (distance <= cube_info->distance)
1133 cube_info->distance=distance;
1134 cube_info->color_number=node_info->color_number;
1165MagickExport MagickBooleanType CompressImageColormap(
Image *image)
1170 assert(image != (
Image *) NULL);
1171 assert(image->signature == MagickCoreSignature);
1172 if (IsEventLogging() != MagickFalse)
1173 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
1174 if (IsPaletteImage(image,&image->exception) == MagickFalse)
1175 return(MagickFalse);
1176 GetQuantizeInfo(&quantize_info);
1177 quantize_info.number_colors=image->colors;
1178 quantize_info.tree_depth=MaxTreeDepth;
1179 return(QuantizeImage(&quantize_info,image));
1212static void DefineImageColormap(
Image *image,
QCubeInfo *cube_info,
1224 number_children=cube_info->associate_alpha == MagickFalse ? 8UL : 16UL;
1225 for (i=0; i < (ssize_t) number_children; i++)
1226 if (node_info->child[i] != (
QNodeInfo *) NULL)
1227 DefineImageColormap(image,cube_info,node_info->child[i]);
1228 if (node_info->number_unique != 0)
1239 q=image->colormap+image->colors;
1240 alpha=(MagickRealType) ((MagickOffsetType) node_info->number_unique);
1241 alpha=PerceptibleReciprocal(alpha);
1242 if (cube_info->associate_alpha == MagickFalse)
1244 SetPixelRed(q,ClampToQuantum(alpha*(MagickRealType) QuantumRange*
1245 node_info->total_color.red));
1246 SetPixelGreen(q,ClampToQuantum(alpha*(MagickRealType) QuantumRange*
1247 node_info->total_color.green));
1248 SetPixelBlue(q,ClampToQuantum(alpha*(MagickRealType) QuantumRange*
1249 node_info->total_color.blue));
1250 SetPixelOpacity(q,OpaqueOpacity);
1257 opacity=(MagickRealType) (alpha*(MagickRealType) QuantumRange*
1258 node_info->total_color.opacity);
1259 SetPixelOpacity(q,ClampToQuantum(opacity));
1260 if (q->opacity == OpaqueOpacity)
1262 SetPixelRed(q,ClampToQuantum(alpha*(MagickRealType)
1263 QuantumRange*node_info->total_color.red));
1264 SetPixelGreen(q,ClampToQuantum(alpha*(MagickRealType)
1265 QuantumRange*node_info->total_color.green));
1266 SetPixelBlue(q,ClampToQuantum(alpha*(MagickRealType)
1267 QuantumRange*node_info->total_color.blue));
1274 gamma=(double) (QuantumScale*((
double) QuantumRange-(
double)
1276 gamma=PerceptibleReciprocal(gamma);
1277 SetPixelRed(q,ClampToQuantum(alpha*gamma*(MagickRealType)
1278 QuantumRange*node_info->total_color.red));
1279 SetPixelGreen(q,ClampToQuantum(alpha*gamma*(MagickRealType)
1280 QuantumRange*node_info->total_color.green));
1281 SetPixelBlue(q,ClampToQuantum(alpha*gamma*(MagickRealType)
1282 QuantumRange*node_info->total_color.blue));
1283 if (node_info->number_unique > cube_info->transparent_pixels)
1285 cube_info->transparent_pixels=node_info->number_unique;
1286 cube_info->transparent_index=(ssize_t) image->colors;
1290 node_info->color_number=image->colors++;
1316static void DestroyQCubeInfo(
QCubeInfo *cube_info)
1326 nodes=cube_info->node_queue->next;
1327 cube_info->node_queue->nodes=(
QNodeInfo *) RelinquishMagickMemory(
1328 cube_info->node_queue->nodes);
1329 cube_info->node_queue=(
QNodes *) RelinquishMagickMemory(
1330 cube_info->node_queue);
1331 cube_info->node_queue=nodes;
1332 }
while (cube_info->node_queue != (
QNodes *) NULL);
1333 if (cube_info->memory_info != (
MemoryInfo *) NULL)
1334 cube_info->memory_info=RelinquishVirtualMemory(cube_info->memory_info);
1335 cube_info->quantize_info=DestroyQuantizeInfo(cube_info->quantize_info);
1336 cube_info=(
QCubeInfo *) RelinquishMagickMemory(cube_info);
1365 assert(quantize_info->signature == MagickCoreSignature);
1366 if (IsEventLogging() != MagickFalse)
1367 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"...");
1368 quantize_info->signature=(~MagickCoreSignature);
1369 quantize_info=(
QuantizeInfo *) RelinquishMagickMemory(quantize_info);
1370 return(quantize_info);
1407 for (i=0; i < (ssize_t) GetMagickResourceLimit(ThreadResource); i++)
1425 number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
1430 (void) memset(pixels,0,number_threads*
sizeof(*pixels));
1431 for (i=0; i < (ssize_t) number_threads; i++)
1434 2*
sizeof(**pixels));
1436 return(DestroyPixelTLS(pixels));
1441static inline ssize_t CacheOffset(
QCubeInfo *cube_info,
1444#define RedShift(pixel) (((pixel) >> CacheShift) << (0*(8-CacheShift)))
1445#define GreenShift(pixel) (((pixel) >> CacheShift) << (1*(8-CacheShift)))
1446#define BlueShift(pixel) (((pixel) >> CacheShift) << (2*(8-CacheShift)))
1447#define AlphaShift(pixel) (((pixel) >> CacheShift) << (3*(8-CacheShift)))
1452 offset=(ssize_t) (RedShift(ScaleQuantumToChar(ClampPixel(pixel->red))) |
1453 GreenShift(ScaleQuantumToChar(ClampPixel(pixel->green))) |
1454 BlueShift(ScaleQuantumToChar(ClampPixel(pixel->blue))));
1455 if (cube_info->associate_alpha != MagickFalse)
1456 offset|=AlphaShift(ScaleQuantumToChar(ClampPixel(pixel->opacity)));
1460static MagickBooleanType FloydSteinbergDither(
Image *image,
QCubeInfo *cube_info)
1462#define DitherImageTag "Dither/Image"
1482 pixels=AcquirePixelTLS(image->columns);
1484 return(MagickFalse);
1485 exception=(&image->exception);
1487 image_view=AcquireAuthenticCacheView(image,exception);
1488 for (y=0; y < (ssize_t) image->rows; y++)
1491 id = GetOpenMPThreadId();
1498 *magick_restrict indexes;
1513 if (status == MagickFalse)
1515 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1521 indexes=GetCacheViewAuthenticIndexQueue(image_view);
1523 current=pixels[id]+(y & 0x01)*image->columns;
1524 previous=pixels[id]+((y+1) & 0x01)*image->columns;
1525 v=(ssize_t) ((y & 0x01) ? -1 : 1);
1526 for (x=0; x < (ssize_t) image->columns; x++)
1538 u=(y & 0x01) ? (ssize_t) image->columns-1-x : x;
1539 AssociateAlphaPixel(&cube,q+u,&pixel);
1542 pixel.red+=7.0*cube_info->diffusion*current[u-v].red/16;
1543 pixel.green+=7.0*cube_info->diffusion*current[u-v].green/16;
1544 pixel.blue+=7.0*cube_info->diffusion*current[u-v].blue/16;
1545 if (cube.associate_alpha != MagickFalse)
1546 pixel.opacity+=7.0*cube_info->diffusion*current[u-v].opacity/16;
1550 if (x < (ssize_t) (image->columns-1))
1552 pixel.red+=cube_info->diffusion*previous[u+v].red/16;
1553 pixel.green+=cube_info->diffusion*previous[u+v].green/16;
1554 pixel.blue+=cube_info->diffusion*previous[u+v].blue/16;
1555 if (cube.associate_alpha != MagickFalse)
1556 pixel.opacity+=cube_info->diffusion*previous[u+v].opacity/16;
1558 pixel.red+=5.0*cube_info->diffusion*previous[u].red/16;
1559 pixel.green+=5.0*cube_info->diffusion*previous[u].green/16;
1560 pixel.blue+=5.0*cube_info->diffusion*previous[u].blue/16;
1561 if (cube.associate_alpha != MagickFalse)
1562 pixel.opacity+=5.0*cube_info->diffusion*previous[u].opacity/16;
1565 pixel.red+=3.0*cube_info->diffusion*previous[u-v].red/16;
1566 pixel.green+=3.0*cube_info->diffusion*previous[u-v].green/16;
1567 pixel.blue+=3.0*cube_info->diffusion*previous[u-v].blue/16;
1568 if (cube.associate_alpha != MagickFalse)
1569 pixel.opacity+=3.0*cube_info->diffusion*
1570 previous[u-v].opacity/16;
1573 pixel.red=(MagickRealType) ClampPixel(pixel.red);
1574 pixel.green=(MagickRealType) ClampPixel(pixel.green);
1575 pixel.blue=(MagickRealType) ClampPixel(pixel.blue);
1576 if (cube.associate_alpha != MagickFalse)
1577 pixel.opacity=(MagickRealType) ClampPixel(pixel.opacity);
1578 i=CacheOffset(&cube,&pixel);
1579 if (cube.cache[i] < 0)
1590 node_info=cube.root;
1591 for (index=MaxTreeDepth-1; (ssize_t) index > 0; index--)
1593 id=ColorToQNodeId(&cube,&pixel,index);
1594 if (node_info->child[
id] == (
QNodeInfo *) NULL)
1596 node_info=node_info->child[id];
1602 cube.distance=(MagickRealType) (4.0*((MagickRealType) QuantumRange+
1603 1.0)*((MagickRealType) QuantumRange+1.0)+1.0);
1604 ClosestColor(image,&cube,node_info->parent);
1605 cube.cache[i]=(ssize_t) cube.color_number;
1610 index=(size_t) cube.cache[i];
1611 if (image->storage_class == PseudoClass)
1612 SetPixelIndex(indexes+u,index);
1613 if (cube.quantize_info->measure_error == MagickFalse)
1615 SetPixelRgb(q+u,image->colormap+index);
1616 if (cube.associate_alpha != MagickFalse)
1617 SetPixelOpacity(q+u,image->colormap[index].opacity);
1619 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1624 AssociateAlphaPixel(&cube,image->colormap+index,&color);
1625 current[u].red=pixel.red-color.red;
1626 current[u].green=pixel.green-color.green;
1627 current[u].blue=pixel.blue-color.blue;
1628 if (cube.associate_alpha != MagickFalse)
1629 current[u].opacity=pixel.opacity-color.opacity;
1630 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1635 proceed=SetImageProgress(image,DitherImageTag,(MagickOffsetType) y,
1637 if (proceed == MagickFalse)
1642 image_view=DestroyCacheView(image_view);
1643 pixels=DestroyPixelTLS(pixels);
1647static MagickBooleanType
1650static MagickBooleanType Riemersma(
Image *image,
CacheView *image_view,
1651 QCubeInfo *cube_info,
const size_t level,
const unsigned int direction)
1662 status=RiemersmaDither(image,image_view,cube_info,EastGravity);
1663 if (status != MagickFalse)
1664 status=RiemersmaDither(image,image_view,cube_info,SouthGravity);
1665 if (status != MagickFalse)
1666 status=RiemersmaDither(image,image_view,cube_info,WestGravity);
1671 status=RiemersmaDither(image,image_view,cube_info,WestGravity);
1672 if (status != MagickFalse)
1673 status=RiemersmaDither(image,image_view,cube_info,NorthGravity);
1674 if (status != MagickFalse)
1675 status=RiemersmaDither(image,image_view,cube_info,EastGravity);
1680 status=RiemersmaDither(image,image_view,cube_info,SouthGravity);
1681 if (status != MagickFalse)
1682 status=RiemersmaDither(image,image_view,cube_info,EastGravity);
1683 if (status != MagickFalse)
1684 status=RiemersmaDither(image,image_view,cube_info,NorthGravity);
1689 status=RiemersmaDither(image,image_view,cube_info,NorthGravity);
1690 if (status != MagickFalse)
1691 status=RiemersmaDither(image,image_view,cube_info,WestGravity);
1692 if (status != MagickFalse)
1693 status=RiemersmaDither(image,image_view,cube_info,SouthGravity);
1704 status=Riemersma(image,image_view,cube_info,level-1,NorthGravity);
1705 if (status != MagickFalse)
1706 status=RiemersmaDither(image,image_view,cube_info,EastGravity);
1707 if (status != MagickFalse)
1708 status=Riemersma(image,image_view,cube_info,level-1,WestGravity);
1709 if (status != MagickFalse)
1710 status=RiemersmaDither(image,image_view,cube_info,SouthGravity);
1711 if (status != MagickFalse)
1712 status=Riemersma(image,image_view,cube_info,level-1,WestGravity);
1713 if (status != MagickFalse)
1714 status=RiemersmaDither(image,image_view,cube_info,WestGravity);
1715 if (status != MagickFalse)
1716 status=Riemersma(image,image_view,cube_info,level-1,SouthGravity);
1721 status=Riemersma(image,image_view,cube_info,level-1,SouthGravity);
1722 if (status != MagickFalse)
1723 status=RiemersmaDither(image,image_view,cube_info,WestGravity);
1724 if (status != MagickFalse)
1725 status=Riemersma(image,image_view,cube_info,level-1,EastGravity);
1726 if (status != MagickFalse)
1727 status=RiemersmaDither(image,image_view,cube_info,NorthGravity);
1728 if (status != MagickFalse)
1729 status=Riemersma(image,image_view,cube_info,level-1,EastGravity);
1730 if (status != MagickFalse)
1731 status=RiemersmaDither(image,image_view,cube_info,EastGravity);
1732 if (status != MagickFalse)
1733 status=Riemersma(image,image_view,cube_info,level-1,NorthGravity);
1738 status=Riemersma(image,image_view,cube_info,level-1,WestGravity);
1739 if (status != MagickFalse)
1740 status=RiemersmaDither(image,image_view,cube_info,SouthGravity);
1741 if (status != MagickFalse)
1742 status=Riemersma(image,image_view,cube_info,level-1,NorthGravity);
1743 if (status != MagickFalse)
1744 status=RiemersmaDither(image,image_view,cube_info,EastGravity);
1745 if (status != MagickFalse)
1746 status=Riemersma(image,image_view,cube_info,level-1,NorthGravity);
1747 if (status != MagickFalse)
1748 status=RiemersmaDither(image,image_view,cube_info,NorthGravity);
1749 if (status != MagickFalse)
1750 status=Riemersma(image,image_view,cube_info,level-1,EastGravity);
1755 status=Riemersma(image,image_view,cube_info,level-1,EastGravity);
1756 if (status != MagickFalse)
1757 status=RiemersmaDither(image,image_view,cube_info,NorthGravity);
1758 if (status != MagickFalse)
1759 status=Riemersma(image,image_view,cube_info,level-1,SouthGravity);
1760 if (status != MagickFalse)
1761 status=RiemersmaDither(image,image_view,cube_info,WestGravity);
1762 if (status != MagickFalse)
1763 status=Riemersma(image,image_view,cube_info,level-1,SouthGravity);
1764 if (status != MagickFalse)
1765 status=RiemersmaDither(image,image_view,cube_info,SouthGravity);
1766 if (status != MagickFalse)
1767 status=Riemersma(image,image_view,cube_info,level-1,WestGravity);
1773 return(status != 0 ? MagickTrue : MagickFalse);
1776static MagickBooleanType RiemersmaDither(
Image *image,
CacheView *image_view,
1777 QCubeInfo *cube_info,
const unsigned int direction)
1779#define DitherImageTag "Dither/Image"
1795 if ((p->x >= 0) && (p->x < (ssize_t) image->columns) &&
1796 (p->y >= 0) && (p->y < (ssize_t) image->rows))
1802 *magick_restrict indexes;
1813 exception=(&image->exception);
1814 q=GetCacheViewAuthenticPixels(image_view,p->x,p->y,1,1,exception);
1816 return(MagickFalse);
1817 indexes=GetCacheViewAuthenticIndexQueue(image_view);
1818 AssociateAlphaPixel(cube_info,q,&pixel);
1819 for (i=0; i < ErrorQueueLength; i++)
1821 pixel.red+=ErrorRelativeWeight*cube_info->diffusion*p->weights[i]*
1823 pixel.green+=ErrorRelativeWeight*cube_info->diffusion*p->weights[i]*
1825 pixel.blue+=ErrorRelativeWeight*cube_info->diffusion*p->weights[i]*
1827 if (cube_info->associate_alpha != MagickFalse)
1828 pixel.opacity+=ErrorRelativeWeight*cube_info->diffusion*p->weights[i]*
1829 p->error[i].opacity;
1831 pixel.red=(MagickRealType) ClampPixel(pixel.red);
1832 pixel.green=(MagickRealType) ClampPixel(pixel.green);
1833 pixel.blue=(MagickRealType) ClampPixel(pixel.blue);
1834 if (cube_info->associate_alpha != MagickFalse)
1835 pixel.opacity=(MagickRealType) ClampPixel(pixel.opacity);
1836 i=CacheOffset(cube_info,&pixel);
1837 if (p->cache[i] < 0)
1849 for (index=MaxTreeDepth-1; (ssize_t) index > 0; index--)
1851 id=ColorToQNodeId(cube_info,&pixel,index);
1852 if (node_info->child[
id] == (
QNodeInfo *) NULL)
1854 node_info=node_info->child[id];
1860 p->distance=(MagickRealType) (4.0*((MagickRealType) QuantumRange+
1861 1.0)*((MagickRealType) QuantumRange+1.0)+1.0);
1862 ClosestColor(image,p,node_info->parent);
1863 p->cache[i]=(ssize_t) p->color_number;
1868 index=(size_t) (1*p->cache[i]);
1869 if (image->storage_class == PseudoClass)
1870 *indexes=(IndexPacket) index;
1871 if (cube_info->quantize_info->measure_error == MagickFalse)
1873 SetPixelRgb(q,image->colormap+index);
1874 if (cube_info->associate_alpha != MagickFalse)
1875 SetPixelOpacity(q,image->colormap[index].opacity);
1877 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1878 return(MagickFalse);
1882 (void) memmove(p->error,p->error+1,(ErrorQueueLength-1)*
1883 sizeof(p->error[0]));
1884 AssociateAlphaPixel(cube_info,image->colormap+index,&color);
1885 p->error[ErrorQueueLength-1].red=pixel.red-color.red;
1886 p->error[ErrorQueueLength-1].green=pixel.green-color.green;
1887 p->error[ErrorQueueLength-1].blue=pixel.blue-color.blue;
1888 if (cube_info->associate_alpha != MagickFalse)
1889 p->error[ErrorQueueLength-1].opacity=pixel.opacity-color.opacity;
1890 proceed=SetImageProgress(image,DitherImageTag,p->offset,p->span);
1891 if (proceed == MagickFalse)
1892 return(MagickFalse);
1897 case WestGravity: p->x--;
break;
1898 case EastGravity: p->x++;
break;
1899 case NorthGravity: p->y--;
break;
1900 case SouthGravity: p->y++;
break;
1905static MagickBooleanType DitherImage(
Image *image,
QCubeInfo *cube_info)
1920 artifact=GetImageArtifact(image,
"dither:diffusion-amount");
1921 if (artifact != (
const char *) NULL)
1922 cube_info->diffusion=StringToDoubleInterval(artifact,1.0);
1923 if (cube_info->quantize_info->dither_method != RiemersmaDitherMethod)
1924 return(FloydSteinbergDither(image,cube_info));
1928 (void) memset(cube_info->error,0,ErrorQueueLength*
sizeof(*cube_info->error));
1931 extent=MagickMax(image->columns,image->rows);
1932 level=(size_t) log2((
double) extent);
1933 if ((1UL << level) < extent)
1935 cube_info->offset=0;
1936 cube_info->span=(MagickSizeType) image->columns*image->rows;
1937 image_view=AcquireAuthenticCacheView(image,&image->exception);
1940 status=Riemersma(image,image_view,cube_info,level,NorthGravity);
1941 if (status != MagickFalse)
1942 status=RiemersmaDither(image,image_view,cube_info,ForgetGravity);
1943 image_view=DestroyCacheView(image_view);
1982 const size_t depth,
const size_t maximum_colors)
1999 cube_info=(
QCubeInfo *) AcquireMagickMemory(
sizeof(*cube_info));
2002 (void) memset(cube_info,0,
sizeof(*cube_info));
2003 cube_info->depth=depth;
2004 if (cube_info->depth > MaxTreeDepth)
2005 cube_info->depth=MaxTreeDepth;
2006 if (cube_info->depth < 2)
2008 cube_info->maximum_colors=maximum_colors;
2012 cube_info->root=GetQNodeInfo(cube_info,0,0,(
QNodeInfo *) NULL);
2013 if (cube_info->root == (
QNodeInfo *) NULL)
2015 cube_info->root->parent=cube_info->root;
2016 cube_info->quantize_info=CloneQuantizeInfo(quantize_info);
2017 if (cube_info->quantize_info->dither == MagickFalse)
2022 length=(size_t) (1UL << (4*(8-CacheShift)));
2023 cube_info->memory_info=AcquireVirtualMemory(length,
sizeof(*cube_info->cache));
2024 if (cube_info->memory_info == (
MemoryInfo *) NULL)
2026 cube_info->cache=(ssize_t *) GetVirtualMemoryBlob(cube_info->memory_info);
2030 (void) memset(cube_info->cache,(-1),
sizeof(*cube_info->cache)*length);
2035 for (i=0; i < ErrorQueueLength; i++)
2037 cube_info->weights[i]=PerceptibleReciprocal(weight);
2038 weight*=exp(log(1.0/ErrorRelativeWeight)/(ErrorQueueLength-1.0));
2040 cube_info->diffusion=1.0;
2078 if (cube_info->free_nodes == 0)
2086 nodes=(
QNodes *) AcquireMagickMemory(
sizeof(*nodes));
2087 if (nodes == (
QNodes *) NULL)
2089 nodes->nodes=(
QNodeInfo *) AcquireQuantumMemory(QNodesInAList,
2090 sizeof(*nodes->nodes));
2093 nodes->next=cube_info->node_queue;
2094 cube_info->node_queue=nodes;
2095 cube_info->next_node=nodes->nodes;
2096 cube_info->free_nodes=QNodesInAList;
2099 cube_info->free_nodes--;
2100 node_info=cube_info->next_node++;
2101 (void) memset(node_info,0,
sizeof(*node_info));
2102 node_info->parent=parent;
2104 node_info->level=level;
2147MagickExport MagickBooleanType GetImageQuantizeError(
Image *image)
2166 mean_error_per_pixel;
2172 assert(image != (
Image *) NULL);
2173 assert(image->signature == MagickCoreSignature);
2174 if (IsEventLogging() != MagickFalse)
2175 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
2176 image->total_colors=GetNumberColors(image,(FILE *) NULL,&image->exception);
2177 (void) memset(&image->error,0,
sizeof(image->error));
2178 if (image->storage_class == DirectClass)
2182 area=3.0*image->columns*image->rows;
2184 mean_error_per_pixel=0.0;
2186 exception=(&image->exception);
2187 image_view=AcquireVirtualCacheView(image,exception);
2188 for (y=0; y < (ssize_t) image->rows; y++)
2196 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
2199 indexes=GetCacheViewAuthenticIndexQueue(image_view);
2200 for (x=0; x < (ssize_t) image->columns; x++)
2202 index=(ssize_t) GetPixelIndex(indexes+x);
2203 if (image->matte != MagickFalse)
2205 alpha=QuantumScale*(MagickRealType) GetPixelAlpha(p);
2206 beta=QuantumScale*((MagickRealType) QuantumRange-
2207 (MagickRealType) image->colormap[index].opacity);
2209 distance=fabs((
double) (alpha*(MagickRealType) GetPixelRed(p)-beta*
2210 (MagickRealType) image->colormap[index].red));
2211 mean_error_per_pixel+=distance;
2212 mean_error+=distance*distance;
2213 if (distance > maximum_error)
2214 maximum_error=distance;
2215 distance=fabs((
double) (alpha*(MagickRealType) GetPixelGreen(p)-beta*
2216 (MagickRealType) image->colormap[index].green));
2217 mean_error_per_pixel+=distance;
2218 mean_error+=distance*distance;
2219 if (distance > maximum_error)
2220 maximum_error=distance;
2221 distance=fabs((
double) (alpha*(MagickRealType) GetPixelBlue(p)-beta*
2222 (MagickRealType) image->colormap[index].blue));
2223 mean_error_per_pixel+=distance;
2224 mean_error+=distance*distance;
2225 if (distance > maximum_error)
2226 maximum_error=distance;
2230 image_view=DestroyCacheView(image_view);
2231 gamma=PerceptibleReciprocal(area);
2232 image->error.mean_error_per_pixel=gamma*mean_error_per_pixel;
2233 image->error.normalized_mean_error=gamma*QuantumScale*QuantumScale*mean_error;
2234 image->error.normalized_maximum_error=QuantumScale*maximum_error;
2260MagickExport
void GetQuantizeInfo(
QuantizeInfo *quantize_info)
2263 if (IsEventLogging() != MagickFalse)
2264 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"...");
2265 (void) memset(quantize_info,0,
sizeof(*quantize_info));
2266 quantize_info->number_colors=256;
2267 quantize_info->dither=MagickTrue;
2268 quantize_info->dither_method=RiemersmaDitherMethod;
2269 quantize_info->colorspace=UndefinedColorspace;
2270 quantize_info->measure_error=MagickFalse;
2271 quantize_info->signature=MagickCoreSignature;
2308static inline double MagickRound(
double x)
2313 if ((x-floor(x)) < (ceil(x)-x))
2318MagickExport MagickBooleanType PosterizeImage(
Image *image,
const size_t levels,
2319 const MagickBooleanType dither)
2324 status=PosterizeImageChannel(image,DefaultChannels,levels,dither);
2328static inline Quantum PosterizePixel(
const Quantum pixel,
const size_t levels)
2338 step_size=1.0/(levels-1.0);
2339 level_index=(step_size == 0.0) ? 0 : floor((double) pixel/step_size);
2340 return(ClampToQuantum(level_index*step_size));
2343MagickExport MagickBooleanType PosterizeImageChannel(
Image *image,
2344 const ChannelType channel,
const size_t levels,
const MagickBooleanType dither)
2346#define PosterizeImageTag "Posterize/Image"
2367 assert(image != (
Image *) NULL);
2368 assert(image->signature == MagickCoreSignature);
2369 if (IsEventLogging() != MagickFalse)
2370 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
2371 if (image->storage_class == PseudoClass)
2372#if defined(MAGICKCORE_OPENMP_SUPPORT)
2373 #pragma omp parallel for schedule(static) shared(progress,status) \
2374 magick_number_threads(image,image,image->colors,1)
2376 for (i=0; i < (ssize_t) image->colors; i++)
2381 if ((channel & RedChannel) != 0)
2382 image->colormap[i].red=(MagickRealType)
2383 PosterizePixel(image->colormap[i].red,levels);
2384 if ((channel & GreenChannel) != 0)
2385 image->colormap[i].green=(MagickRealType)
2386 PosterizePixel(image->colormap[i].green,levels);
2387 if ((channel & BlueChannel) != 0)
2388 image->colormap[i].blue=(MagickRealType)
2389 PosterizePixel(image->colormap[i].blue,levels);
2390 if ((channel & OpacityChannel) != 0)
2391 image->colormap[i].opacity=(MagickRealType)
2392 PosterizePixel(image->colormap[i].opacity,levels);
2399 exception=(&image->exception);
2400 image_view=AcquireAuthenticCacheView(image,exception);
2401#if defined(MAGICKCORE_OPENMP_SUPPORT)
2402 #pragma omp parallel for schedule(static) shared(progress,status) \
2403 magick_number_threads(image,image,image->rows,1)
2405 for (y=0; y < (ssize_t) image->rows; y++)
2408 *magick_restrict indexes;
2416 if (status == MagickFalse)
2418 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2424 indexes=GetCacheViewAuthenticIndexQueue(image_view);
2425 for (x=0; x < (ssize_t) image->columns; x++)
2427 if ((channel & RedChannel) != 0)
2428 SetPixelRed(q,PosterizePixel(GetPixelRed(q),levels));
2429 if ((channel & GreenChannel) != 0)
2430 SetPixelGreen(q,PosterizePixel(GetPixelGreen(q),levels));
2431 if ((channel & BlueChannel) != 0)
2432 SetPixelBlue(q,PosterizePixel(GetPixelBlue(q),levels));
2433 if (((channel & OpacityChannel) != 0) &&
2434 (image->matte != MagickFalse))
2435 SetPixelOpacity(q,PosterizePixel(GetPixelOpacity(q),levels));
2436 if (((channel & IndexChannel) != 0) &&
2437 (image->colorspace == CMYKColorspace))
2438 SetPixelIndex(indexes+x,PosterizePixel(GetPixelIndex(indexes+x),
2442 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2444 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2449#if defined(MAGICKCORE_OPENMP_SUPPORT)
2453 proceed=SetImageProgress(image,PosterizeImageTag,progress,image->rows);
2454 if (proceed == MagickFalse)
2458 image_view=DestroyCacheView(image_view);
2459 quantize_info=AcquireQuantizeInfo((
ImageInfo *) NULL);
2460 quantize_info->number_colors=(size_t) MagickMin((ssize_t) levels*levels*
2461 levels,MaxColormapSize);
2462 quantize_info->dither=dither;
2463 status=QuantizeImage(quantize_info,image);
2464 quantize_info=DestroyQuantizeInfo(quantize_info);
2507 number_children=cube_info->associate_alpha == MagickFalse ? 8UL : 16UL;
2508 for (i=0; i < (ssize_t) number_children; i++)
2509 if (node_info->child[i] != (
QNodeInfo *) NULL)
2510 PruneChild(cube_info,node_info->child[i]);
2511 if (cube_info->nodes > cube_info->maximum_colors)
2516 parent=node_info->parent;
2517 parent->number_unique+=node_info->number_unique;
2518 parent->total_color.red+=node_info->total_color.red;
2519 parent->total_color.green+=node_info->total_color.green;
2520 parent->total_color.blue+=node_info->total_color.blue;
2521 parent->total_color.opacity+=node_info->total_color.opacity;
2522 parent->child[node_info->id]=(
QNodeInfo *) NULL;
2563 number_children=cube_info->associate_alpha == MagickFalse ? 8UL : 16UL;
2564 for (i=0; i < (ssize_t) number_children; i++)
2565 if (node_info->child[i] != (
QNodeInfo *) NULL)
2566 PruneLevel(cube_info,node_info->child[i]);
2567 if (node_info->level == cube_info->depth)
2568 PruneChild(cube_info,node_info);
2608 number_children=cube_info->associate_alpha == MagickFalse ? 8UL : 16UL;
2609 for (i=0; i < (ssize_t) number_children; i++)
2610 if (node_info->child[i] != (
QNodeInfo *) NULL)
2611 PruneToCubeDepth(cube_info,node_info->child[i]);
2612 if (node_info->level > cube_info->depth)
2613 PruneChild(cube_info,node_info);
2644MagickExport MagickBooleanType QuantizeImage(
const QuantizeInfo *quantize_info,
2658 assert(quantize_info->signature == MagickCoreSignature);
2659 assert(image != (
Image *) NULL);
2660 assert(image->signature == MagickCoreSignature);
2661 if (IsEventLogging() != MagickFalse)
2662 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
2663 maximum_colors=quantize_info->number_colors;
2664 if (maximum_colors == 0)
2665 maximum_colors=MaxColormapSize;
2666 if (maximum_colors > MaxColormapSize)
2667 maximum_colors=MaxColormapSize;
2668 if (image->matte == MagickFalse)
2670 if (SetImageGray(image,&image->exception) != MagickFalse)
2671 (void) SetGrayscaleImage(image);
2673 depth=quantize_info->tree_depth;
2682 colors=maximum_colors;
2683 for (depth=1; colors != 0; depth++)
2685 if ((quantize_info->dither != MagickFalse) && (depth > 2))
2687 if ((image->matte != MagickFalse) && (depth > 5))
2689 if (SetImageGray(image,&image->exception) != MagickFalse)
2695 cube_info=GetQCubeInfo(quantize_info,depth,maximum_colors);
2697 ThrowBinaryImageException(ResourceLimitError,
"MemoryAllocationFailed",
2699 status=ClassifyImageColors(cube_info,image,&image->exception);
2700 if (status != MagickFalse)
2705 if (cube_info->colors > cube_info->maximum_colors)
2706 ReduceImageColors(image,cube_info);
2707 status=AssignImageColors(image,cube_info);
2709 DestroyQCubeInfo(cube_info);
2741MagickExport MagickBooleanType QuantizeImages(
const QuantizeInfo *quantize_info,
2751 MagickProgressMonitor
2766 assert(quantize_info->signature == MagickCoreSignature);
2767 assert(images != (
Image *) NULL);
2768 assert(images->signature == MagickCoreSignature);
2769 if (IsEventLogging() != MagickFalse)
2770 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",images->filename);
2771 if (GetNextImageInList(images) == (
Image *) NULL)
2776 status=QuantizeImage(quantize_info,images);
2780 maximum_colors=quantize_info->number_colors;
2781 if (maximum_colors == 0)
2782 maximum_colors=MaxColormapSize;
2783 if (maximum_colors > MaxColormapSize)
2784 maximum_colors=MaxColormapSize;
2785 depth=quantize_info->tree_depth;
2794 colors=maximum_colors;
2795 for (depth=1; colors != 0; depth++)
2797 if (quantize_info->dither != MagickFalse)
2803 cube_info=GetQCubeInfo(quantize_info,depth,maximum_colors);
2806 (void) ThrowMagickException(&images->exception,GetMagickModule(),
2807 ResourceLimitError,
"MemoryAllocationFailed",
"`%s'",images->filename);
2808 return(MagickFalse);
2810 number_images=GetImageListLength(images);
2812 for (i=0; image != (
Image *) NULL; i++)
2814 progress_monitor=SetImageProgressMonitor(image,(MagickProgressMonitor) NULL,
2815 image->client_data);
2816 status=ClassifyImageColors(cube_info,image,&image->exception);
2817 if (status == MagickFalse)
2819 (void) SetImageProgressMonitor(image,progress_monitor,image->client_data);
2820 proceed=SetImageProgress(image,AssignImageTag,(MagickOffsetType) i,
2822 if (proceed == MagickFalse)
2824 image=GetNextImageInList(image);
2826 if (status != MagickFalse)
2831 ReduceImageColors(images,cube_info);
2833 for (i=0; image != (
Image *) NULL; i++)
2835 progress_monitor=SetImageProgressMonitor(image,(MagickProgressMonitor)
2836 NULL,image->client_data);
2837 status=AssignImageColors(image,cube_info);
2838 if (status == MagickFalse)
2840 (void) SetImageProgressMonitor(image,progress_monitor,
2841 image->client_data);
2842 proceed=SetImageProgress(image,AssignImageTag,(MagickOffsetType) i,
2844 if (proceed == MagickFalse)
2846 image=GetNextImageInList(image);
2849 DestroyQCubeInfo(cube_info);
2886static size_t QuantizeErrorFlatten(
const QCubeInfo *cube_info,
2887 const QNodeInfo *node_info,
const ssize_t offset,
2888 MagickRealType *quantize_error)
2897 if (offset >= (ssize_t) cube_info->nodes)
2899 quantize_error[offset]=node_info->quantize_error;
2901 number_children=cube_info->associate_alpha == MagickFalse ? 8UL : 16UL;
2902 for (i=0; i < (ssize_t) number_children ; i++)
2903 if (node_info->child[i] != (
QNodeInfo *) NULL)
2904 n+=QuantizeErrorFlatten(cube_info,node_info->child[i],offset+n,
2945 number_children=cube_info->associate_alpha == MagickFalse ? 8UL : 16UL;
2946 for (i=0; i < (ssize_t) number_children; i++)
2947 if (node_info->child[i] != (
QNodeInfo *) NULL)
2948 Reduce(cube_info,node_info->child[i]);
2949 if (node_info->quantize_error <= cube_info->pruning_threshold)
2950 PruneChild(cube_info,node_info);
2956 if (node_info->number_unique > 0)
2957 cube_info->colors++;
2958 if (node_info->quantize_error < cube_info->next_threshold)
2959 cube_info->next_threshold=node_info->quantize_error;
3018static int MagickRealTypeCompare(
const void *error_p,
const void *error_q)
3024 p=(MagickRealType *) error_p;
3025 q=(MagickRealType *) error_q;
3028 if (fabs((
double) (*q-*p)) <= MagickEpsilon)
3033static void ReduceImageColors(
const Image *image,
QCubeInfo *cube_info)
3035#define ReduceImageTag "Reduce/Image"
3046 cube_info->next_threshold=0.0;
3047 if (cube_info->colors > cube_info->maximum_colors)
3055 quantize_error=(MagickRealType *) AcquireQuantumMemory(cube_info->nodes,
3056 sizeof(*quantize_error));
3057 if (quantize_error != (MagickRealType *) NULL)
3059 (void) QuantizeErrorFlatten(cube_info,cube_info->root,0,
3061 qsort(quantize_error,cube_info->nodes,
sizeof(MagickRealType),
3062 MagickRealTypeCompare);
3063 if (cube_info->nodes > (110*(cube_info->maximum_colors+1)/100))
3064 cube_info->next_threshold=quantize_error[cube_info->nodes-110*
3065 (cube_info->maximum_colors+1)/100];
3066 quantize_error=(MagickRealType *) RelinquishMagickMemory(
3070 for (span=cube_info->colors; cube_info->colors > cube_info->maximum_colors; )
3072 cube_info->pruning_threshold=cube_info->next_threshold;
3073 cube_info->next_threshold=cube_info->root->quantize_error-1;
3074 cube_info->colors=0;
3075 Reduce(cube_info,cube_info->root);
3076 offset=(MagickOffsetType) span-cube_info->colors;
3077 proceed=SetImageProgress(image,ReduceImageTag,offset,span-
3078 cube_info->maximum_colors+1);
3079 if (proceed == MagickFalse)
3112MagickExport MagickBooleanType RemapImage(
const QuantizeInfo *quantize_info,
3124 assert(image != (
Image *) NULL);
3125 assert(image->signature == MagickCoreSignature);
3126 if (IsEventLogging() != MagickFalse)
3127 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
3128 assert(remap_image != (
Image *) NULL);
3129 assert(remap_image->signature == MagickCoreSignature);
3130 cube_info=GetQCubeInfo(quantize_info,MaxTreeDepth,MaxColormapSize);
3132 ThrowBinaryImageException(ResourceLimitError,
"MemoryAllocationFailed",
3134 cube_info->quantize_info->colorspace=remap_image->colorspace;
3135 status=ClassifyImageColors(cube_info,remap_image,&image->exception);
3136 if (status != MagickFalse)
3141 cube_info->quantize_info->number_colors=cube_info->colors;
3142 if (cube_info->colors > cube_info->maximum_colors)
3143 ReduceImageColors(image,cube_info);
3144 status=AssignImageColors(image,cube_info);
3146 DestroyQCubeInfo(cube_info);
3178MagickExport MagickBooleanType RemapImages(
const QuantizeInfo *quantize_info,
3190 assert(images != (
Image *) NULL);
3191 assert(images->signature == MagickCoreSignature);
3192 if (IsEventLogging() != MagickFalse)
3193 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",images->filename);
3195 if (remap_image == (
Image *) NULL)
3200 status=QuantizeImages(quantize_info,images);
3206 cube_info=GetQCubeInfo(quantize_info,MaxTreeDepth,
3207 quantize_info->number_colors);
3209 ThrowBinaryImageException(ResourceLimitError,
"MemoryAllocationFailed",
3211 status=ClassifyImageColors(cube_info,remap_image,&image->exception);
3212 if (status != MagickFalse)
3217 cube_info->quantize_info->number_colors=cube_info->colors;
3219 for ( ; image != (
Image *) NULL; image=GetNextImageInList(image))
3221 status=AssignImageColors(image,cube_info);
3222 if (status == MagickFalse)
3226 DestroyQCubeInfo(cube_info);
3253#if defined(__cplusplus) || defined(c_plusplus)
3257static int IntensityCompare(
const void *x,
const void *y)
3268 intensity=PixelPacketIntensity(color_1)-PixelPacketIntensity(color_2);
3269 if (intensity < (
double) INT_MIN)
3270 intensity=(double) INT_MIN;
3271 if (intensity > (
double) INT_MAX)
3272 intensity=(double) INT_MAX;
3273 return((
int) intensity);
3276#if defined(__cplusplus) || defined(c_plusplus)
3280static MagickBooleanType SetGrayscaleImage(
Image *image)
3303 assert(image != (
Image *) NULL);
3304 assert(image->signature == MagickCoreSignature);
3305 exception=(&image->exception);
3306 if (image->type != GrayscaleType)
3307 (void) TransformImageColorspace(image,GRAYColorspace);
3308 extent=MagickMax(image->colors+1,MagickMax(MaxColormapSize,MaxMap+1));
3309 colormap_index=(ssize_t *) AcquireQuantumMemory(extent,
3310 sizeof(*colormap_index));
3311 if (colormap_index == (ssize_t *) NULL)
3312 ThrowBinaryException(ResourceLimitError,
"MemoryAllocationFailed",
3314 if (image->storage_class != PseudoClass)
3316 (void) memset(colormap_index,(-1),extent*
sizeof(*colormap_index));
3317 if (AcquireImageColormap(image,MaxColormapSize) == MagickFalse)
3319 colormap_index=(ssize_t *) RelinquishMagickMemory(colormap_index);
3320 ThrowBinaryException(ResourceLimitError,
"MemoryAllocationFailed",
3325 image_view=AcquireAuthenticCacheView(image,exception);
3326#if defined(MAGICKCORE_OPENMP_SUPPORT)
3327 #pragma omp parallel for schedule(static) shared(status) \
3328 magick_number_threads(image,image,image->rows,1)
3330 for (y=0; y < (ssize_t) image->rows; y++)
3333 *magick_restrict indexes;
3341 if (status == MagickFalse)
3343 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
3350 indexes=GetCacheViewAuthenticIndexQueue(image_view);
3351 for (x=0; x < (ssize_t) image->columns; x++)
3356 intensity=ScaleQuantumToMap(GetPixelRed(q));
3357 if (colormap_index[intensity] < 0)
3359#if defined(MAGICKCORE_OPENMP_SUPPORT)
3360 #pragma omp critical (MagickCore_SetGrayscaleImage)
3362 if (colormap_index[intensity] < 0)
3364 colormap_index[intensity]=(ssize_t) image->colors;
3365 image->colormap[image->colors].red=GetPixelRed(q);
3366 image->colormap[image->colors].green=GetPixelGreen(q);
3367 image->colormap[image->colors].blue=GetPixelBlue(q);
3371 SetPixelIndex(indexes+x,colormap_index[intensity]);
3374 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3377 image_view=DestroyCacheView(image_view);
3379 (void) memset(colormap_index,0,extent*
sizeof(*colormap_index));
3380 for (i=0; i < (ssize_t) image->colors; i++)
3381 image->colormap[i].opacity=(Quantum) i;
3382 qsort((
void *) image->colormap,image->colors,
sizeof(
PixelPacket),
3384 colormap=(
PixelPacket *) AcquireQuantumMemory(image->colors,
3388 colormap_index=(ssize_t *) RelinquishMagickMemory(colormap_index);
3389 ThrowBinaryException(ResourceLimitError,
"MemoryAllocationFailed",
3393 colormap[j]=image->colormap[0];
3394 for (i=0; i < (ssize_t) image->colors; i++)
3396 if (IsSameColor(image,&colormap[j],&image->colormap[i]) == MagickFalse)
3399 colormap[j]=image->colormap[i];
3401 colormap_index[(ssize_t) image->colormap[i].opacity]=j;
3403 image->colors=(size_t) (j+1);
3404 image->colormap=(
PixelPacket *) RelinquishMagickMemory(image->colormap);
3405 image->colormap=colormap;
3407 image_view=AcquireAuthenticCacheView(image,exception);
3408#if defined(MAGICKCORE_OPENMP_SUPPORT)
3409 #pragma omp parallel for schedule(static) shared(status) \
3410 magick_number_threads(image,image,image->rows,1)
3412 for (y=0; y < (ssize_t) image->rows; y++)
3415 *magick_restrict indexes;
3423 if (status == MagickFalse)
3425 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3431 indexes=GetCacheViewAuthenticIndexQueue(image_view);
3432 for (x=0; x < (ssize_t) image->columns; x++)
3433 SetPixelIndex(indexes+x,colormap_index[ScaleQuantumToMap(GetPixelIndex(
3435 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3438 image_view=DestroyCacheView(image_view);
3439 colormap_index=(ssize_t *) RelinquishMagickMemory(colormap_index);
3440 image->type=GrayscaleType;
3441 if (SetImageMonochrome(image,&image->exception) != MagickFalse)
3442 image->type=BilevelType;