MagickCore 7.1.2-32
Convert, Edit, Or Compose Bitmap Images
Loading...
Searching...
No Matches
quantize.c
1/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% QQQ U U AAA N N TTTTT IIIII ZZZZZ EEEEE %
7% Q Q U U A A NN N T I ZZ E %
8% Q Q U U AAAAA N N N T I ZZZ EEEEE %
9% Q QQ U U A A N NN T I ZZ E %
10% QQQQ UUU A A N N T IIIII ZZZZZ EEEEE %
11% %
12% %
13% MagickCore Methods to Reduce the Number of Unique Colors in an Image %
14% %
15% Software Design %
16% Cristy %
17% July 1992 %
18% %
19% %
20% Copyright @ 1999 ImageMagick Studio LLC, a non-profit organization %
21% dedicated to making software imaging solutions freely available. %
22% %
23% You may not use this file except in compliance with the License. You may %
24% obtain a copy of the License at %
25% %
26% https://imagemagick.org/license/ %
27% %
28% Unless required by applicable law or agreed to in writing, software %
29% distributed under the License is distributed on an "AS IS" BASIS, %
30% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31% See the License for the specific language governing permissions and %
32% limitations under the License. %
33% %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36% Realism in computer graphics typically requires using 24 bits/pixel to
37% generate an image. Yet many graphic display devices do not contain the
38% amount of memory necessary to match the spatial and color resolution of
39% the human eye. The Quantize methods takes a 24 bit image and reduces
40% the number of colors so it can be displayed on raster device with less
41% bits per pixel. In most instances, the quantized image closely
42% resembles the original reference image.
43%
44% A reduction of colors in an image is also desirable for image
45% transmission and real-time animation.
46%
47% QuantizeImage() takes a standard RGB or monochrome images and quantizes
48% them down to some fixed number of colors.
49%
50% For purposes of color allocation, an image is a set of n pixels, where
51% each pixel is a point in RGB space. RGB space is a 3-dimensional
52% vector space, and each pixel, Pi, is defined by an ordered triple of
53% red, green, and blue coordinates, (Ri, Gi, Bi).
54%
55% Each primary color component (red, green, or blue) represents an
56% intensity which varies linearly from 0 to a maximum value, Cmax, which
57% corresponds to full saturation of that color. Color allocation is
58% defined over a domain consisting of the cube in RGB space with opposite
59% vertices at (0,0,0) and (Cmax, Cmax, Cmax). QUANTIZE requires Cmax =
60% 255.
61%
62% The algorithm maps this domain onto a tree in which each node
63% represents a cube within that domain. In the following discussion
64% these cubes are defined by the coordinate of two opposite vertices (vertex
65% nearest the origin in RGB space and the vertex farthest from the origin).
66%
67% The tree's root node represents the entire domain, (0,0,0) through
68% (Cmax,Cmax,Cmax). Each lower level in the tree is generated by
69% subdividing one node's cube into eight smaller cubes of equal size.
70% This corresponds to bisecting the parent cube with planes passing
71% through the midpoints of each edge.
72%
73% The basic algorithm operates in three phases: Classification,
74% Reduction, and Assignment. Classification builds a color description
75% tree for the image. Reduction collapses the tree until the number it
76% represents, at most, the number of colors desired in the output image.
77% Assignment defines the output image's color map and sets each pixel's
78% color by restorage_class in the reduced tree. Our goal is to minimize
79% the numerical discrepancies between the original colors and quantized
80% colors (quantization error).
81%
82% Classification begins by initializing a color description tree of
83% sufficient depth to represent each possible input color in a leaf.
84% However, it is impractical to generate a fully-formed color description
85% tree in the storage_class phase for realistic values of Cmax. If
86% colors components in the input image are quantized to k-bit precision,
87% so that Cmax= 2k-1, the tree would need k levels below the root node to
88% allow representing each possible input color in a leaf. This becomes
89% prohibitive because the tree's total number of nodes is 1 +
90% sum(i=1, k, 8k).
91%
92% A complete tree would require 19,173,961 nodes for k = 8, Cmax = 255.
93% Therefore, to avoid building a fully populated tree, QUANTIZE: (1)
94% Initializes data structures for nodes only as they are needed; (2)
95% Chooses a maximum depth for the tree as a function of the desired
96% number of colors in the output image (currently log2(colormap size)).
97%
98% For each pixel in the input image, storage_class scans downward from
99% the root of the color description tree. At each level of the tree it
100% identifies the single node which represents a cube in RGB space
101% containing the pixel's color. It updates the following data for each
102% such node:
103%
104% n1: Number of pixels whose color is contained in the RGB cube which
105% this node represents;
106%
107% n2: Number of pixels whose color is not represented in a node at
108% lower depth in the tree; initially, n2 = 0 for all nodes except
109% leaves of the tree.
110%
111% Sr, Sg, Sb: Sums of the red, green, and blue component values for all
112% pixels not classified at a lower depth. The combination of these sums
113% and n2 will ultimately characterize the mean color of a set of pixels
114% represented by this node.
115%
116% E: the distance squared in RGB space between each pixel contained
117% within a node and the nodes' center. This represents the
118% quantization error for a node.
119%
120% Reduction repeatedly prunes the tree until the number of nodes with n2
121% > 0 is less than or equal to the maximum number of colors allowed in
122% the output image. On any given iteration over the tree, it selects
123% those nodes whose E count is minimal for pruning and merges their color
124% statistics upward. It uses a pruning threshold, Ep, to govern node
125% selection as follows:
126%
127% Ep = 0
128% while number of nodes with (n2 > 0) > required maximum number of colors
129% prune all nodes such that E <= Ep
130% Set Ep to minimum E in remaining nodes
131%
132% This has the effect of minimizing any quantization error when merging
133% two nodes together.
134%
135% When a node to be pruned has offspring, the pruning procedure invokes
136% itself recursively in order to prune the tree from the leaves upward.
137% n2, Sr, Sg, and Sb in a node being pruned are always added to the
138% corresponding data in that node's parent. This retains the pruned
139% node's color characteristics for later averaging.
140%
141% For each node, n2 pixels exist for which that node represents the
142% smallest volume in RGB space containing those pixel's colors. When n2
143% > 0 the node will uniquely define a color in the output image. At the
144% beginning of reduction, n2 = 0 for all nodes except a the leaves of
145% the tree which represent colors present in the input image.
146%
147% The other pixel count, n1, indicates the total number of colors within
148% the cubic volume which the node represents. This includes n1 - n2
149% pixels whose colors should be defined by nodes at a lower level in the
150% tree.
151%
152% Assignment generates the output image from the pruned tree. The output
153% image consists of two parts: (1) A color map, which is an array of
154% color descriptions (RGB triples) for each color present in the output
155% image; (2) A pixel array, which represents each pixel as an index
156% into the color map array.
157%
158% First, the assignment phase makes one pass over the pruned color
159% description tree to establish the image's color map. For each node
160% with n2 > 0, it divides Sr, Sg, and Sb by n2 . This produces the mean
161% color of all pixels that classify no lower than this node. Each of
162% these colors becomes an entry in the color map.
163%
164% Finally, the assignment phase reclassifies each pixel in the pruned
165% tree to identify the deepest node containing the pixel's color. The
166% pixel's value in the pixel array becomes the index of this node's mean
167% color in the color map.
168%
169% This method is based on a similar algorithm written by Paul Raveling.
170%
171*/
172␌
173/*
174 Include declarations.
175*/
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"
208␌
209/*
210 Define declarations.
211*/
212#if !defined(__APPLE__) && !defined(TARGET_OS_IPHONE)
213#define CacheShift 2
214#else
215#define CacheShift 3
216#endif
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
224␌
225/*
226 Typedef declarations.
227*/
228typedef struct _DoublePixelPacket
229{
230 double
231 red,
232 green,
233 blue,
234 alpha;
235} DoublePixelPacket;
236
237typedef struct _QNodeInfo
238{
239 struct _QNodeInfo
240 *parent,
241 *child[16];
242
243 MagickSizeType
244 number_unique;
245
246 DoublePixelPacket
247 total_color;
248
249 double
250 quantize_error;
251
252 size_t
253 color_number,
254 id,
255 level;
256} QNodeInfo;
257
258typedef struct _QNodes
259{
260 QNodeInfo
261 *nodes;
262
263 struct _QNodes
264 *next;
265} QNodes;
266
267typedef struct _QCubeInfo
268{
269 QNodeInfo
270 *root;
271
272 size_t
273 colors,
274 maximum_colors;
275
276 ssize_t
277 transparent_index;
278
279 MagickSizeType
280 transparent_pixels;
281
282 DoublePixelPacket
283 target;
284
285 double
286 distance,
287 pruning_threshold,
288 next_threshold;
289
290 size_t
291 nodes,
292 free_nodes,
293 color_number;
294
295 QNodeInfo
296 *next_node;
297
298 QNodes
299 *node_queue;
300
301 ssize_t
302 **cache;
303
304 DoublePixelPacket
305 error[ErrorQueueLength];
306
307 double
308 diffusion,
309 weights[ErrorQueueLength];
310
311 QuantizeInfo
312 *quantize_info;
313
314 MagickBooleanType
315 associate_alpha;
316
317 ssize_t
318 x,
319 y;
320
321 size_t
322 depth;
323
324 MagickOffsetType
325 offset;
326
327 MagickSizeType
328 span;
329} QCubeInfo;
330␌
331/*
332 Method prototypes.
333*/
334static QCubeInfo
335 *GetQCubeInfo(const QuantizeInfo *,const size_t,const size_t);
336
337static QNodeInfo
338 *GetQNodeInfo(QCubeInfo *,const size_t,const size_t,QNodeInfo *);
339
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 *);
346
347static void
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 *);
354␌
355/*
356%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
357% %
358% %
359% %
360% A c q u i r e Q u a n t i z e I n f o %
361% %
362% %
363% %
364%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
365%
366% AcquireQuantizeInfo() allocates the QuantizeInfo structure.
367%
368% The format of the AcquireQuantizeInfo method is:
369%
370% QuantizeInfo *AcquireQuantizeInfo(const ImageInfo *image_info)
371%
372% A description of each parameter follows:
373%
374% o image_info: the image info.
375%
376*/
377MagickExport QuantizeInfo *AcquireQuantizeInfo(const ImageInfo *image_info)
378{
379 QuantizeInfo
380 *quantize_info;
381
382 quantize_info=(QuantizeInfo *) AcquireCriticalMemory(sizeof(*quantize_info));
383 GetQuantizeInfo(quantize_info);
384 if (image_info != (ImageInfo *) NULL)
385 {
386 const char
387 *option;
388
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;
396 }
397 return(quantize_info);
398}
399␌
400/*
401%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
402% %
403% %
404% %
405+ A s s i g n I m a g e C o l o r s %
406% %
407% %
408% %
409%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
410%
411% AssignImageColors() generates the output image from the pruned tree. The
412% output image consists of two parts: (1) A color map, which is an array
413% of color descriptions (RGB triples) for each color present in the
414% output image; (2) A pixel array, which represents each pixel as an
415% index into the color map array.
416%
417% First, the assignment phase makes one pass over the pruned color
418% description tree to establish the image's color map. For each node
419% with n2 > 0, it divides Sr, Sg, and Sb by n2 . This produces the mean
420% color of all pixels that classify no lower than this node. Each of
421% these colors becomes an entry in the color map.
422%
423% Finally, the assignment phase reclassifies each pixel in the pruned
424% tree to identify the deepest node containing the pixel's color. The
425% pixel's value in the pixel array becomes the index of this node's mean
426% color in the color map.
427%
428% The format of the AssignImageColors() method is:
429%
430% MagickBooleanType AssignImageColors(Image *image,QCubeInfo *cube_info)
431%
432% A description of each parameter follows.
433%
434% o image: the image.
435%
436% o cube_info: A pointer to the Cube structure.
437%
438*/
439
440static inline void AssociateAlphaPixel(const Image *image,
441 const QCubeInfo *cube_info,const Quantum *pixel,
442 DoublePixelPacket *alpha_pixel)
443{
444 double
445 alpha;
446
447 if ((cube_info->associate_alpha == MagickFalse) ||
448 (GetPixelAlpha(image,pixel) == OpaqueAlpha))
449 {
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);
454 return;
455 }
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);
461}
462
463static inline void AssociateAlphaPixelInfo(const QCubeInfo *cube_info,
464 const PixelInfo *pixel,DoublePixelPacket *alpha_pixel)
465{
466 double
467 alpha;
468
469 if ((cube_info->associate_alpha == MagickFalse) ||
470 (pixel->alpha == (double) OpaqueAlpha))
471 {
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;
476 return;
477 }
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;
483}
484
485static inline size_t ColorToQNodeId(const QCubeInfo *cube_info,
486 const DoublePixelPacket *pixel,size_t index)
487{
488 size_t
489 id;
490
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) &
496 0x1) << 3);
497 return(id);
498}
499
500static MagickBooleanType AssignImageColors(Image *image,QCubeInfo *cube_info,
501 ExceptionInfo *exception)
502{
503#define AssignImageTag "Assign/Image"
504
505 ColorspaceType
506 colorspace;
507
508 ssize_t
509 y;
510
511 /*
512 Allocate image colormap.
513 */
514 colorspace=image->colorspace;
515 if (cube_info->quantize_info->colorspace != UndefinedColorspace)
516 (void) TransformImageColorspace(image,cube_info->quantize_info->colorspace,
517 exception);
518 cube_info->transparent_pixels=0;
519 cube_info->transparent_index=(-1);
520 if (SetImageColormap(image,cube_info,exception) == MagickFalse)
521 return(MagickFalse);
522 /*
523 Create a reduced color image.
524 */
525 if (cube_info->quantize_info->dither_method != NoDitherMethod)
526 (void) DitherImage(image,cube_info,exception);
527 else
528 {
529 CacheView
530 *image_view;
531
532 MagickBooleanType
533 status;
534
535 status=MagickTrue;
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)
540#endif
541 for (y=0; y < (ssize_t) image->rows; y++)
542 {
543 QCubeInfo
544 cube;
545
546 Quantum
547 *magick_restrict q;
548
549 ssize_t
550 count,
551 x;
552
553 if (status == MagickFalse)
554 continue;
555 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
556 exception);
557 if (q == (Quantum *) NULL)
558 {
559 status=MagickFalse;
560 continue;
561 }
562 cube=(*cube_info);
563 for (x=0; x < (ssize_t) image->columns; x+=count)
564 {
565 DoublePixelPacket
566 pixel;
567
568 const QNodeInfo
569 *node_info;
570
571 ssize_t
572 i;
573
574 size_t
575 id,
576 index;
577
578 /*
579 Identify the deepest node containing the pixel's color.
580 */
581 for (count=1; (x+count) < (ssize_t) image->columns; count++)
582 {
583 PixelInfo
584 packet;
585
586 GetPixelInfoPixel(image,q+count*(ssize_t) GetPixelChannels(image),
587 &packet);
588 if (IsPixelEquivalent(image,q,&packet) == MagickFalse)
589 break;
590 }
591 AssociateAlphaPixel(image,&cube,q,&pixel);
592 node_info=cube.root;
593 for (index=MaxTreeDepth-1; (ssize_t) index > 0; index--)
594 {
595 id=ColorToQNodeId(&cube,&pixel,index);
596 if (node_info->child[id] == (QNodeInfo *) NULL)
597 break;
598 node_info=node_info->child[id];
599 }
600 /*
601 Find closest color among siblings and their children.
602 */
603 cube.target=pixel;
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++)
609 {
610 if (image->storage_class == PseudoClass)
611 SetPixelIndex(image,(Quantum) index,q);
612 if (cube.quantize_info->measure_error == MagickFalse)
613 {
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);
623 }
624 q+=(ptrdiff_t) GetPixelChannels(image);
625 }
626 }
627 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
628 status=MagickFalse;
629 if (image->progress_monitor != (MagickProgressMonitor) NULL)
630 {
631 MagickBooleanType
632 proceed;
633
634 proceed=SetImageProgress(image,AssignImageTag,(MagickOffsetType) y,
635 image->rows);
636 if (proceed == MagickFalse)
637 status=MagickFalse;
638 }
639 }
640 image_view=DestroyCacheView(image_view);
641 }
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)))
646 {
647 double
648 intensity;
649
650 /*
651 Monochrome image.
652 */
653 intensity=GetPixelInfoLuma(image->colormap+0) < (double)
654 QuantumRange/2.0 ? 0.0 : (double) QuantumRange;
655 if (image->colors > 1)
656 {
657 intensity=0.0;
658 if (GetPixelInfoLuma(image->colormap+0) >
659 GetPixelInfoLuma(image->colormap+1))
660 intensity=(double) QuantumRange;
661 }
662 image->colormap[0].red=intensity;
663 image->colormap[0].green=intensity;
664 image->colormap[0].blue=intensity;
665 if (image->colors > 1)
666 {
667 image->colormap[1].red=(double) QuantumRange-intensity;
668 image->colormap[1].green=(double) QuantumRange-intensity;
669 image->colormap[1].blue=(double) QuantumRange-intensity;
670 }
671 }
672 (void) SyncImage(image,exception);
673 if ((cube_info->quantize_info->colorspace != UndefinedColorspace) &&
674 (IssRGBCompatibleColorspace(colorspace) == MagickFalse))
675 (void) TransformImageColorspace(image,colorspace,exception);
676 return(MagickTrue);
677}
678␌
679/*
680%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
681% %
682% %
683% %
684+ C l a s s i f y I m a g e C o l o r s %
685% %
686% %
687% %
688%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
689%
690% ClassifyImageColors() begins by initializing a color description tree
691% of sufficient depth to represent each possible input color in a leaf.
692% However, it is impractical to generate a fully-formed color
693% description tree in the storage_class phase for realistic values of
694% Cmax. If colors components in the input image are quantized to k-bit
695% precision, so that Cmax= 2k-1, the tree would need k levels below the
696% root node to allow representing each possible input color in a leaf.
697% This becomes prohibitive because the tree's total number of nodes is
698% 1 + sum(i=1,k,8k).
699%
700% A complete tree would require 19,173,961 nodes for k = 8, Cmax = 255.
701% Therefore, to avoid building a fully populated tree, QUANTIZE: (1)
702% Initializes data structures for nodes only as they are needed; (2)
703% Chooses a maximum depth for the tree as a function of the desired
704% number of colors in the output image (currently log2(colormap size)).
705%
706% For each pixel in the input image, storage_class scans downward from
707% the root of the color description tree. At each level of the tree it
708% identifies the single node which represents a cube in RGB space
709% containing It updates the following data for each such node:
710%
711% n1 : Number of pixels whose color is contained in the RGB cube
712% which this node represents;
713%
714% n2 : Number of pixels whose color is not represented in a node at
715% lower depth in the tree; initially, n2 = 0 for all nodes except
716% leaves of the tree.
717%
718% Sr, Sg, Sb : Sums of the red, green, and blue component values for
719% all pixels not classified at a lower depth. The combination of
720% these sums and n2 will ultimately characterize the mean color of a
721% set of pixels represented by this node.
722%
723% E: the distance squared in RGB space between each pixel contained
724% within a node and the nodes' center. This represents the quantization
725% error for a node.
726%
727% The format of the ClassifyImageColors() method is:
728%
729% MagickBooleanType ClassifyImageColors(QCubeInfo *cube_info,
730% const Image *image,ExceptionInfo *exception)
731%
732% A description of each parameter follows.
733%
734% o cube_info: A pointer to the Cube structure.
735%
736% o image: the image.
737%
738*/
739
740static inline void SetAssociatedAlpha(const Image *image,QCubeInfo *cube_info)
741{
742 MagickBooleanType
743 associate_alpha;
744
745 associate_alpha=image->alpha_trait != UndefinedPixelTrait ? MagickTrue :
746 MagickFalse;
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;
752}
753
754static MagickBooleanType ClassifyImageColors(QCubeInfo *cube_info,
755 const Image *image,ExceptionInfo *exception)
756{
757#define ClassifyImageTag "Classify/Image"
758
759 CacheView
760 *image_view;
761
762 double
763 bisect;
764
765 DoublePixelPacket
766 error,
767 mid,
768 midpoint,
769 pixel;
770
771 MagickBooleanType
772 proceed;
773
774 QNodeInfo
775 *node_info;
776
777 size_t
778 id,
779 index,
780 level;
781
782 ssize_t
783 count,
784 y;
785
786 /*
787 Classify the first cube_info->maximum_colors colors to a tree depth of 8.
788 */
789 SetAssociatedAlpha(image,cube_info);
790 if (cube_info->quantize_info->colorspace != image->colorspace)
791 {
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);
796 else
797 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
798 (void) TransformImageColorspace((Image *) image,sRGBColorspace,
799 exception);
800 }
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;
805 error.alpha=0.0;
806 image_view=AcquireVirtualCacheView(image,exception);
807 for (y=0; y < (ssize_t) image->rows; y++)
808 {
809 const Quantum
810 *magick_restrict p;
811
812 ssize_t
813 x;
814
815 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
816 if (p == (const Quantum *) NULL)
817 break;
818 if (cube_info->nodes > MaxQNodes)
819 {
820 /*
821 Prune one level if the color tree is too large.
822 */
823 PruneLevel(cube_info,cube_info->root);
824 cube_info->depth--;
825 }
826 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) count)
827 {
828 /*
829 Start at the root and descend the color cube tree.
830 */
831 for (count=1; (x+(ssize_t) count) < (ssize_t) image->columns; count++)
832 {
833 PixelInfo
834 packet;
835
836 GetPixelInfoPixel(image,p+count*(ssize_t) GetPixelChannels(image),
837 &packet);
838 if (IsPixelEquivalent(image,p,&packet) == MagickFalse)
839 break;
840 }
841 AssociateAlphaPixel(image,cube_info,p,&pixel);
842 index=MaxTreeDepth-1;
843 bisect=((double) QuantumRange+1.0)/2.0;
844 mid=midpoint;
845 node_info=cube_info->root;
846 for (level=1; level <= MaxTreeDepth; level++)
847 {
848 double
849 distance;
850
851 bisect*=0.5;
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)
858 {
859 /*
860 Set colors of new node to contain pixel.
861 */
862 node_info->child[id]=GetQNodeInfo(cube_info,id,level,node_info);
863 if (node_info->child[id] == (QNodeInfo *) NULL)
864 {
865 (void) ThrowMagickException(exception,GetMagickModule(),
866 ResourceLimitError,"MemoryAllocationFailed","`%s'",
867 image->filename);
868 continue;
869 }
870 if (level == MaxTreeDepth)
871 cube_info->colors++;
872 }
873 /*
874 Approximate the quantization error represented by this node.
875 */
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)
885 distance=0.0;
886 node_info->quantize_error+=count*sqrt(distance);
887 cube_info->root->quantize_error+=node_info->quantize_error;
888 index--;
889 }
890 /*
891 Sum RGB for this leaf for later derivation of the mean cube color.
892 */
893 node_info->number_unique=(size_t) ((ssize_t) node_info->number_unique+
894 count);
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);
904 else
905 node_info->total_color.alpha+=count*QuantumScale*(double)
906 ClampPixel((double) OpaqueAlpha);
907 p+=(ptrdiff_t) count*(ssize_t) GetPixelChannels(image);
908 }
909 if (cube_info->colors > cube_info->maximum_colors)
910 {
911 PruneToCubeDepth(cube_info,cube_info->root);
912 break;
913 }
914 proceed=SetImageProgress(image,ClassifyImageTag,(MagickOffsetType) y,
915 image->rows);
916 if (proceed == MagickFalse)
917 break;
918 }
919 for (y++; y < (ssize_t) image->rows; y++)
920 {
921 const Quantum
922 *magick_restrict p;
923
924 ssize_t
925 x;
926
927 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
928 if (p == (const Quantum *) NULL)
929 break;
930 if (cube_info->nodes > MaxQNodes)
931 {
932 /*
933 Prune one level if the color tree is too large.
934 */
935 PruneLevel(cube_info,cube_info->root);
936 cube_info->depth--;
937 }
938 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) count)
939 {
940 /*
941 Start at the root and descend the color cube tree.
942 */
943 for (count=1; (x+(ssize_t) count) < (ssize_t) image->columns; count++)
944 {
945 PixelInfo
946 packet;
947
948 GetPixelInfoPixel(image,p+count*(ssize_t) GetPixelChannels(image),
949 &packet);
950 if (IsPixelEquivalent(image,p,&packet) == MagickFalse)
951 break;
952 }
953 AssociateAlphaPixel(image,cube_info,p,&pixel);
954 index=MaxTreeDepth-1;
955 bisect=((double) QuantumRange+1.0)/2.0;
956 mid=midpoint;
957 node_info=cube_info->root;
958 for (level=1; level <= cube_info->depth; level++)
959 {
960 double
961 distance;
962
963 bisect*=0.5;
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)
970 {
971 /*
972 Set colors of new node to contain pixel.
973 */
974 node_info->child[id]=GetQNodeInfo(cube_info,id,level,node_info);
975 if (node_info->child[id] == (QNodeInfo *) NULL)
976 {
977 (void) ThrowMagickException(exception,GetMagickModule(),
978 ResourceLimitError,"MemoryAllocationFailed","%s",
979 image->filename);
980 continue;
981 }
982 if (level == cube_info->depth)
983 cube_info->colors++;
984 }
985 /*
986 Approximate the quantization error represented by this node.
987 */
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)
997 distance=0.0;
998 node_info->quantize_error+=count*sqrt(distance);
999 cube_info->root->quantize_error+=node_info->quantize_error;
1000 index--;
1001 }
1002 /*
1003 Sum RGB for this leaf for later derivation of the mean cube color.
1004 */
1005 node_info->number_unique=(size_t) ((ssize_t) node_info->number_unique+
1006 count);
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);
1016 else
1017 node_info->total_color.alpha+=count*QuantumScale*(double)
1018 ClampPixel((MagickRealType) OpaqueAlpha);
1019 p+=(ptrdiff_t) count*(ssize_t) GetPixelChannels(image);
1020 }
1021 proceed=SetImageProgress(image,ClassifyImageTag,(MagickOffsetType) y,
1022 image->rows);
1023 if (proceed == MagickFalse)
1024 break;
1025 }
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);
1032}
1033␌
1034/*
1035%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1036% %
1037% %
1038% %
1039% C l o n e Q u a n t i z e I n f o %
1040% %
1041% %
1042% %
1043%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1044%
1045% CloneQuantizeInfo() makes a duplicate of the given quantize info structure,
1046% or if quantize info is NULL, a new one.
1047%
1048% The format of the CloneQuantizeInfo method is:
1049%
1050% QuantizeInfo *CloneQuantizeInfo(const QuantizeInfo *quantize_info)
1051%
1052% A description of each parameter follows:
1053%
1054% o clone_info: Method CloneQuantizeInfo returns a duplicate of the given
1055% quantize info, or if image info is NULL a new one.
1056%
1057% o quantize_info: a structure of type info.
1058%
1059*/
1060MagickExport QuantizeInfo *CloneQuantizeInfo(const QuantizeInfo *quantize_info)
1061{
1062 QuantizeInfo
1063 *clone_info;
1064
1065 clone_info=(QuantizeInfo *) AcquireCriticalMemory(sizeof(*clone_info));
1066 GetQuantizeInfo(clone_info);
1067 if (quantize_info == (QuantizeInfo *) NULL)
1068 return(clone_info);
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;
1074 return(clone_info);
1075}
1076␌
1077/*
1078%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1079% %
1080% %
1081% %
1082+ C l o s e s t C o l o r %
1083% %
1084% %
1085% %
1086%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1087%
1088% ClosestColor() traverses the color cube tree at a particular node and
1089% determines which colormap entry best represents the input color.
1090%
1091% The format of the ClosestColor method is:
1092%
1093% void ClosestColor(const Image *image,QCubeInfo *cube_info,
1094% const QNodeInfo *node_info)
1095%
1096% A description of each parameter follows.
1097%
1098% o image: the image.
1099%
1100% o cube_info: A pointer to the Cube structure.
1101%
1102% o node_info: the address of a structure of type QNodeInfo which points to a
1103% node in the color cube tree that is to be pruned.
1104%
1105*/
1106static void ClosestColor(const Image *image,QCubeInfo *cube_info,
1107 const QNodeInfo *node_info)
1108{
1109 size_t
1110 number_children;
1111
1112 ssize_t
1113 i;
1114
1115 /*
1116 Traverse any children.
1117 */
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)
1123 {
1124 double
1125 alpha,
1126 beta,
1127 distance,
1128 pixel;
1129
1130 DoublePixelPacket
1131 *magick_restrict q;
1132
1133 PixelInfo
1134 *magick_restrict p;
1135
1136 /*
1137 Determine if this color is "closest".
1138 */
1139 p=image->colormap+node_info->color_number;
1140 q=(&cube_info->target);
1141 alpha=1.0;
1142 beta=1.0;
1143 if (cube_info->associate_alpha != MagickFalse)
1144 {
1145 alpha=(MagickRealType) (QuantumScale*p->alpha);
1146 beta=(MagickRealType) (QuantumScale*q->alpha);
1147 }
1148 pixel=alpha*p->red-beta*q->red;
1149 distance=pixel*pixel;
1150 if (distance <= cube_info->distance)
1151 {
1152 pixel=alpha*p->green-beta*q->green;
1153 distance+=pixel*pixel;
1154 if (distance <= cube_info->distance)
1155 {
1156 pixel=alpha*p->blue-beta*q->blue;
1157 distance+=pixel*pixel;
1158 if (distance <= cube_info->distance)
1159 {
1160 if (cube_info->associate_alpha != MagickFalse)
1161 {
1162 pixel=p->alpha-q->alpha;
1163 distance+=pixel*pixel;
1164 }
1165 if (distance <= cube_info->distance)
1166 {
1167 cube_info->distance=distance;
1168 cube_info->color_number=node_info->color_number;
1169 }
1170 }
1171 }
1172 }
1173 }
1174}
1175␌
1176/*
1177%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1178% %
1179% %
1180% %
1181% C o m p r e s s I m a g e C o l o r m a p %
1182% %
1183% %
1184% %
1185%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1186%
1187% CompressImageColormap() compresses an image colormap by removing any
1188% duplicate or unused color entries.
1189%
1190% The format of the CompressImageColormap method is:
1191%
1192% MagickBooleanType CompressImageColormap(Image *image,
1193% ExceptionInfo *exception)
1194%
1195% A description of each parameter follows:
1196%
1197% o image: the image.
1198%
1199% o exception: return any errors or warnings in this structure.
1200%
1201*/
1202MagickExport MagickBooleanType CompressImageColormap(Image *image,
1203 ExceptionInfo *exception)
1204{
1205 QuantizeInfo
1206 quantize_info;
1207
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));
1218}
1219␌
1220/*
1221%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1222% %
1223% %
1224% %
1225+ D e f i n e I m a g e C o l o r m a p %
1226% %
1227% %
1228% %
1229%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1230%
1231% DefineImageColormap() traverses the color cube tree and notes each colormap
1232% entry. A colormap entry is any node in the color cube tree where the
1233% of unique colors is not zero.
1234%
1235% The format of the DefineImageColormap method is:
1236%
1237% void DefineImageColormap(Image *image,QCubeInfo *cube_info,
1238% QNodeInfo *node_info)
1239%
1240% A description of each parameter follows.
1241%
1242% o image: the image.
1243%
1244% o cube_info: A pointer to the Cube structure.
1245%
1246% o node_info: the address of a structure of type QNodeInfo which points to a
1247% node in the color cube tree that is to be pruned.
1248%
1249*/
1250static void DefineImageColormap(Image *image,QCubeInfo *cube_info,
1251 QNodeInfo *node_info)
1252{
1253 size_t
1254 number_children;
1255
1256 ssize_t
1257 i;
1258
1259 /*
1260 Traverse any children.
1261 */
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)
1267 {
1268 double
1269 alpha;
1270
1271 PixelInfo
1272 *magick_restrict q;
1273
1274 /*
1275 Colormap entry is defined by the mean color in this cube.
1276 */
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)
1281 {
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;
1289 }
1290 else
1291 {
1292 double
1293 opacity;
1294
1295 opacity=(double) (alpha*(double) QuantumRange*
1296 node_info->total_color.alpha);
1297 q->alpha=(double) ClampToQuantum(opacity);
1298 if (q->alpha == (double) OpaqueAlpha)
1299 {
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);
1306 }
1307 else
1308 {
1309 double
1310 gamma;
1311
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)
1321 {
1322 cube_info->transparent_pixels=node_info->number_unique;
1323 cube_info->transparent_index=(ssize_t) image->colors;
1324 }
1325 }
1326 }
1327 node_info->color_number=image->colors++;
1328 }
1329}
1330␌
1331/*
1332%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1333% %
1334% %
1335% %
1336+ D e s t r o y Q C u b e I n f o %
1337% %
1338% %
1339% %
1340%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1341%
1342% DestroyQCubeInfo() deallocates memory associated with an image.
1343%
1344% The format of the DestroyQCubeInfo method is:
1345%
1346% DestroyQCubeInfo(QCubeInfo *cube_info)
1347%
1348% A description of each parameter follows:
1349%
1350% o cube_info: the address of a structure of type QCubeInfo.
1351%
1352*/
1353static void DestroyQCubeInfo(QCubeInfo *cube_info)
1354{
1355 QNodes
1356 *nodes;
1357
1358 ssize_t
1359 i;
1360
1361 /*
1362 Release color cube tree storage.
1363 */
1364 do
1365 {
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)
1374 {
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);
1380 }
1381 cube_info->quantize_info=DestroyQuantizeInfo(cube_info->quantize_info);
1382 cube_info=(QCubeInfo *) RelinquishMagickMemory(cube_info);
1383}
1384␌
1385/*
1386%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1387% %
1388% %
1389% %
1390% D e s t r o y Q u a n t i z e I n f o %
1391% %
1392% %
1393% %
1394%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1395%
1396% DestroyQuantizeInfo() deallocates memory associated with an QuantizeInfo
1397% structure.
1398%
1399% The format of the DestroyQuantizeInfo method is:
1400%
1401% QuantizeInfo *DestroyQuantizeInfo(QuantizeInfo *quantize_info)
1402%
1403% A description of each parameter follows:
1404%
1405% o quantize_info: Specifies a pointer to an QuantizeInfo structure.
1406%
1407*/
1408MagickExport QuantizeInfo *DestroyQuantizeInfo(QuantizeInfo *quantize_info)
1409{
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);
1417}
1418␌
1419/*
1420%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1421% %
1422% %
1423% %
1424+ D i t h e r I m a g e %
1425% %
1426% %
1427% %
1428%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1429%
1430% DitherImage() distributes the difference between an original image and
1431% the corresponding color reduced algorithm to neighboring pixels using
1432% serpentine-scan Floyd-Steinberg error diffusion. DitherImage returns
1433% MagickTrue if the image is dithered otherwise MagickFalse.
1434%
1435% The format of the DitherImage method is:
1436%
1437% MagickBooleanType DitherImage(Image *image,QCubeInfo *cube_info,
1438% ExceptionInfo *exception)
1439%
1440% A description of each parameter follows.
1441%
1442% o image: the image.
1443%
1444% o cube_info: A pointer to the Cube structure.
1445%
1446% o exception: return any errors or warnings in this structure.
1447%
1448*/
1449
1450static DoublePixelPacket **DestroyPixelTLS(DoublePixelPacket **pixels)
1451{
1452 ssize_t
1453 i;
1454
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);
1460 return(pixels);
1461}
1462
1463static DoublePixelPacket **AcquirePixelTLS(const size_t count)
1464{
1465 DoublePixelPacket
1466 **pixels;
1467
1468 size_t
1469 number_threads;
1470
1471 ssize_t
1472 i;
1473
1474 number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
1475 pixels=(DoublePixelPacket **) AcquireQuantumMemory(number_threads,
1476 sizeof(*pixels));
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++)
1481 {
1482 pixels[i]=(DoublePixelPacket *) AcquireQuantumMemory(count,2*
1483 sizeof(**pixels));
1484 if (pixels[i] == (DoublePixelPacket *) NULL)
1485 return(DestroyPixelTLS(pixels));
1486 }
1487 return(pixels);
1488}
1489
1490static inline ssize_t CacheOffset(QCubeInfo *cube_info,
1491 const DoublePixelPacket *pixel)
1492{
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)))
1497
1498 ssize_t
1499 offset;
1500
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)));
1506 return(offset);
1507}
1508
1509static inline ssize_t *GetCacheEntry(QCubeInfo *cube_info,
1510 const DoublePixelPacket *pixel)
1511{
1512 ssize_t
1513 **block,
1514 offset;
1515
1516 /*
1517 Cache blocks are allocated on first use, -1 marks an entry not set yet.
1518 */
1519 offset=CacheOffset(cube_info,pixel);
1520 block=cube_info->cache+(offset >> CacheBlockShift);
1521 if (*block == (ssize_t *) NULL)
1522 {
1523 *block=(ssize_t *) AcquireQuantumMemory((size_t) 1UL << CacheBlockShift,
1524 sizeof(**block));
1525 if (*block == (ssize_t *) NULL)
1526 return((ssize_t *) NULL);
1527 (void) memset(*block,(-1),sizeof(**block) << CacheBlockShift);
1528 }
1529 return(*block+(offset & ((1L << CacheBlockShift)-1)));
1530}
1531
1532static MagickBooleanType FloydSteinbergDither(Image *image,QCubeInfo *cube_info,
1533 ExceptionInfo *exception)
1534{
1535#define DitherImageTag "Dither/Image"
1536
1537 CacheView
1538 *image_view;
1539
1540 DoublePixelPacket
1541 **pixels;
1542
1543 MagickBooleanType
1544 status;
1545
1546 ssize_t
1547 y;
1548
1549 /*
1550 Distribute quantization error using Floyd-Steinberg.
1551 */
1552 pixels=AcquirePixelTLS(image->columns);
1553 if (pixels == (DoublePixelPacket **) NULL)
1554 return(MagickFalse);
1555 status=MagickTrue;
1556 image_view=AcquireAuthenticCacheView(image,exception);
1557 for (y=0; y < (ssize_t) image->rows; y++)
1558 {
1559 const int
1560 id = GetOpenMPThreadId();
1561
1562 DoublePixelPacket
1563 *current,
1564 *previous;
1565
1566 QCubeInfo
1567 cube;
1568
1569 Quantum
1570 *magick_restrict q;
1571
1572 size_t
1573 index;
1574
1575 ssize_t
1576 x,
1577 v;
1578
1579 if (status == MagickFalse)
1580 continue;
1581 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1582 if (q == (Quantum *) NULL)
1583 {
1584 status=MagickFalse;
1585 continue;
1586 }
1587 cube=(*cube_info);
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++)
1592 {
1593 DoublePixelPacket
1594 color,
1595 pixel;
1596
1597 ssize_t
1598 *entry;
1599
1600 ssize_t
1601 u;
1602
1603 u=(y & 0x01) != 0 ? (ssize_t) image->columns-1-x : x;
1604 AssociateAlphaPixel(image,&cube,q+u*(ssize_t) GetPixelChannels(image),
1605 &pixel);
1606 if (x > 0)
1607 {
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;
1613 }
1614 if (y > 0)
1615 {
1616 if (x < (ssize_t) (image->columns-1))
1617 {
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;
1623 }
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;
1629 if (x > 0)
1630 {
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;
1636 }
1637 }
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))
1645 {
1646 QNodeInfo
1647 *node_info;
1648
1649 size_t
1650 node_id;
1651
1652 /*
1653 Identify the deepest node containing the pixel's color.
1654 */
1655 node_info=cube.root;
1656 for (index=MaxTreeDepth-1; (ssize_t) index > 0; index--)
1657 {
1658 node_id=ColorToQNodeId(&cube,&pixel,index);
1659 if (node_info->child[node_id] == (QNodeInfo *) NULL)
1660 break;
1661 node_info=node_info->child[node_id];
1662 }
1663 /*
1664 Find closest color among siblings and their children.
1665 */
1666 cube.target=pixel;
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;
1672 }
1673 /*
1674 Assign pixel to closest colormap entry.
1675 */
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)
1681 {
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));
1691 }
1692 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1693 status=MagickFalse;
1694 /*
1695 Store the error.
1696 */
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)
1704 {
1705 MagickBooleanType
1706 proceed;
1707
1708 proceed=SetImageProgress(image,DitherImageTag,(MagickOffsetType) y,
1709 image->rows);
1710 if (proceed == MagickFalse)
1711 status=MagickFalse;
1712 }
1713 }
1714 }
1715 image_view=DestroyCacheView(image_view);
1716 pixels=DestroyPixelTLS(pixels);
1717 return(MagickTrue);
1718}
1719
1720static MagickBooleanType RiemersmaDither(Image *image,CacheView *image_view,
1721 QCubeInfo *cube_info,const unsigned int direction,ExceptionInfo *exception)
1722{
1723#define DitherImageTag "Dither/Image"
1724
1725 QCubeInfo
1726 *p;
1727
1728 DoublePixelPacket
1729 color,
1730 pixel;
1731
1732 MagickBooleanType
1733 proceed;
1734
1735 size_t
1736 index;
1737
1738 p=cube_info;
1739 if ((p->x >= 0) && (p->x < (ssize_t) image->columns) &&
1740 (p->y >= 0) && (p->y < (ssize_t) image->rows))
1741 {
1742 Quantum
1743 *magick_restrict q;
1744
1745 ssize_t
1746 *entry,
1747 i;
1748
1749 /*
1750 Distribute error.
1751 */
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++)
1757 {
1758 pixel.red+=ErrorRelativeWeight*cube_info->diffusion*p->weights[i]*
1759 p->error[i].red;
1760 pixel.green+=ErrorRelativeWeight*cube_info->diffusion*p->weights[i]*
1761 p->error[i].green;
1762 pixel.blue+=ErrorRelativeWeight*cube_info->diffusion*p->weights[i]*
1763 p->error[i].blue;
1764 if (cube_info->associate_alpha != MagickFalse)
1765 pixel.alpha+=ErrorRelativeWeight*cube_info->diffusion*p->weights[i]*
1766 p->error[i].alpha;
1767 }
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))
1775 {
1776 QNodeInfo
1777 *node_info;
1778
1779 size_t
1780 id;
1781
1782 /*
1783 Identify the deepest node containing the pixel's color.
1784 */
1785 node_info=p->root;
1786 for (index=MaxTreeDepth-1; (ssize_t) index > 0; index--)
1787 {
1788 id=ColorToQNodeId(cube_info,&pixel,index);
1789 if (node_info->child[id] == (QNodeInfo *) NULL)
1790 break;
1791 node_info=node_info->child[id];
1792 }
1793 /*
1794 Find closest color among siblings and their children.
1795 */
1796 p->target=pixel;
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;
1802 }
1803 /*
1804 Assign pixel to closest colormap entry.
1805 */
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)
1810 {
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);
1816 }
1817 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1818 return(MagickFalse);
1819 /*
1820 Propagate the error as the last entry of the error queue.
1821 */
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);
1833 p->offset++;
1834 }
1835 switch (direction)
1836 {
1837 case WestGravity: p->x--; break;
1838 case EastGravity: p->x++; break;
1839 case NorthGravity: p->y--; break;
1840 case SouthGravity: p->y++; break;
1841 }
1842 return(MagickTrue);
1843}
1844
1845static MagickBooleanType Riemersma(Image *image,CacheView *image_view,
1846 QCubeInfo *cube_info,const size_t level,const unsigned int direction,
1847 ExceptionInfo *exception)
1848{
1849 MagickBooleanType
1850 status;
1851
1852 status=MagickTrue;
1853 if (level == 1)
1854 switch (direction)
1855 {
1856 case WestGravity:
1857 {
1858 status=RiemersmaDither(image,image_view,cube_info,EastGravity,
1859 exception);
1860 if (status != MagickFalse)
1861 status=RiemersmaDither(image,image_view,cube_info,SouthGravity,
1862 exception);
1863 if (status != MagickFalse)
1864 status=RiemersmaDither(image,image_view,cube_info,WestGravity,
1865 exception);
1866 break;
1867 }
1868 case EastGravity:
1869 {
1870 status=RiemersmaDither(image,image_view,cube_info,WestGravity,
1871 exception);
1872 if (status != MagickFalse)
1873 status=RiemersmaDither(image,image_view,cube_info,NorthGravity,
1874 exception);
1875 if (status != MagickFalse)
1876 status=RiemersmaDither(image,image_view,cube_info,EastGravity,
1877 exception);
1878 break;
1879 }
1880 case NorthGravity:
1881 {
1882 status=RiemersmaDither(image,image_view,cube_info,SouthGravity,
1883 exception);
1884 if (status != MagickFalse)
1885 status=RiemersmaDither(image,image_view,cube_info,EastGravity,
1886 exception);
1887 if (status != MagickFalse)
1888 status=RiemersmaDither(image,image_view,cube_info,NorthGravity,
1889 exception);
1890 break;
1891 }
1892 case SouthGravity:
1893 {
1894 status=RiemersmaDither(image,image_view,cube_info,NorthGravity,
1895 exception);
1896 if (status != MagickFalse)
1897 status=RiemersmaDither(image,image_view,cube_info,WestGravity,
1898 exception);
1899 if (status != MagickFalse)
1900 status=RiemersmaDither(image,image_view,cube_info,SouthGravity,
1901 exception);
1902 break;
1903 }
1904 default:
1905 break;
1906 }
1907 else
1908 switch (direction)
1909 {
1910 case WestGravity:
1911 {
1912 status=Riemersma(image,image_view,cube_info,level-1,NorthGravity,
1913 exception);
1914 if (status != MagickFalse)
1915 status=RiemersmaDither(image,image_view,cube_info,EastGravity,
1916 exception);
1917 if (status != MagickFalse)
1918 status=Riemersma(image,image_view,cube_info,level-1,WestGravity,
1919 exception);
1920 if (status != MagickFalse)
1921 status=RiemersmaDither(image,image_view,cube_info,SouthGravity,
1922 exception);
1923 if (status != MagickFalse)
1924 status=Riemersma(image,image_view,cube_info,level-1,WestGravity,
1925 exception);
1926 if (status != MagickFalse)
1927 status=RiemersmaDither(image,image_view,cube_info,WestGravity,
1928 exception);
1929 if (status != MagickFalse)
1930 status=Riemersma(image,image_view,cube_info,level-1,SouthGravity,
1931 exception);
1932 break;
1933 }
1934 case EastGravity:
1935 {
1936 status=Riemersma(image,image_view,cube_info,level-1,SouthGravity,
1937 exception);
1938 if (status != MagickFalse)
1939 status=RiemersmaDither(image,image_view,cube_info,WestGravity,
1940 exception);
1941 if (status != MagickFalse)
1942 status=Riemersma(image,image_view,cube_info,level-1,EastGravity,
1943 exception);
1944 if (status != MagickFalse)
1945 status=RiemersmaDither(image,image_view,cube_info,NorthGravity,
1946 exception);
1947 if (status != MagickFalse)
1948 status=Riemersma(image,image_view,cube_info,level-1,EastGravity,
1949 exception);
1950 if (status != MagickFalse)
1951 status=RiemersmaDither(image,image_view,cube_info,EastGravity,
1952 exception);
1953 if (status != MagickFalse)
1954 status=Riemersma(image,image_view,cube_info,level-1,NorthGravity,
1955 exception);
1956 break;
1957 }
1958 case NorthGravity:
1959 {
1960 status=Riemersma(image,image_view,cube_info,level-1,WestGravity,
1961 exception);
1962 if (status != MagickFalse)
1963 status=RiemersmaDither(image,image_view,cube_info,SouthGravity,
1964 exception);
1965 if (status != MagickFalse)
1966 status=Riemersma(image,image_view,cube_info,level-1,NorthGravity,
1967 exception);
1968 if (status != MagickFalse)
1969 status=RiemersmaDither(image,image_view,cube_info,EastGravity,
1970 exception);
1971 if (status != MagickFalse)
1972 status=Riemersma(image,image_view,cube_info,level-1,NorthGravity,
1973 exception);
1974 if (status != MagickFalse)
1975 status=RiemersmaDither(image,image_view,cube_info,NorthGravity,
1976 exception);
1977 if (status != MagickFalse)
1978 status=Riemersma(image,image_view,cube_info,level-1,EastGravity,
1979 exception);
1980 break;
1981 }
1982 case SouthGravity:
1983 {
1984 status=Riemersma(image,image_view,cube_info,level-1,EastGravity,
1985 exception);
1986 if (status != MagickFalse)
1987 status=RiemersmaDither(image,image_view,cube_info,NorthGravity,
1988 exception);
1989 if (status != MagickFalse)
1990 status=Riemersma(image,image_view,cube_info,level-1,SouthGravity,
1991 exception);
1992 if (status != MagickFalse)
1993 status=RiemersmaDither(image,image_view,cube_info,WestGravity,
1994 exception);
1995 if (status != MagickFalse)
1996 status=Riemersma(image,image_view,cube_info,level-1,SouthGravity,
1997 exception);
1998 if (status != MagickFalse)
1999 status=RiemersmaDither(image,image_view,cube_info,SouthGravity,
2000 exception);
2001 if (status != MagickFalse)
2002 status=Riemersma(image,image_view,cube_info,level-1,WestGravity,
2003 exception);
2004 break;
2005 }
2006 default:
2007 break;
2008 }
2009 return(status);
2010}
2011
2012static MagickBooleanType DitherImage(Image *image,QCubeInfo *cube_info,
2013 ExceptionInfo *exception)
2014{
2015 CacheView
2016 *image_view;
2017
2018 const char
2019 *artifact;
2020
2021 MagickBooleanType
2022 status;
2023
2024 size_t
2025 extent,
2026 level;
2027
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));
2033 /*
2034 Distribute quantization error along a Hilbert curve.
2035 */
2036 (void) memset(cube_info->error,0,ErrorQueueLength*sizeof(*cube_info->error));
2037 cube_info->x=0;
2038 cube_info->y=0;
2039 extent=MagickMax(image->columns,image->rows);
2040 level=(size_t) log2((double) extent);
2041 if (((size_t) 1UL << level) < extent)
2042 level++;
2043 cube_info->offset=0;
2044 cube_info->span=(MagickSizeType) image->columns*image->rows;
2045 image_view=AcquireAuthenticCacheView(image,exception);
2046 status=MagickTrue;
2047 if (level > 0)
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);
2052 return(status);
2053}
2054␌
2055/*
2056%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2057% %
2058% %
2059% %
2060+ G e t Q C u b e I n f o %
2061% %
2062% %
2063% %
2064%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2065%
2066% GetQCubeInfo() initialize the Cube data structure.
2067%
2068% The format of the GetQCubeInfo method is:
2069%
2070% QCubeInfo GetQCubeInfo(const QuantizeInfo *quantize_info,
2071% const size_t depth,const size_t maximum_colors)
2072%
2073% A description of each parameter follows.
2074%
2075% o quantize_info: Specifies a pointer to an QuantizeInfo structure.
2076%
2077% o depth: Normally, this integer value is zero or one. A zero or
2078% one tells Quantize to choose a optimal tree depth of Log4(number_colors).
2079% A tree of this depth generally allows the best representation of the
2080% reference image with the least amount of memory and the fastest
2081% computational speed. In some cases, such as an image with low color
2082% dispersion (a few number of colors), a value other than
2083% Log4(number_colors) is required. To expand the color tree completely,
2084% use a value of 8.
2085%
2086% o maximum_colors: maximum colors.
2087%
2088*/
2089static QCubeInfo *GetQCubeInfo(const QuantizeInfo *quantize_info,
2090 const size_t depth,const size_t maximum_colors)
2091{
2092 double
2093 weight;
2094
2095 QCubeInfo
2096 *cube_info;
2097
2098 ssize_t
2099 i;
2100
2101 /*
2102 Initialize tree to describe color cube_info.
2103 */
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)
2112 cube_info->depth=2;
2113 cube_info->maximum_colors=maximum_colors;
2114 /*
2115 Initialize root node.
2116 */
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)
2123 return(cube_info);
2124 /*
2125 Initialize dither resources.
2126 */
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));
2132 /*
2133 Distribute weights along a curve of exponential decay.
2134 */
2135 weight=1.0;
2136 for (i=0; i < ErrorQueueLength; i++)
2137 {
2138 cube_info->weights[i]=MagickSafeReciprocal(weight);
2139 weight*=exp(log(1.0/ErrorRelativeWeight)/(ErrorQueueLength-1.0));
2140 }
2141 cube_info->diffusion=1.0;
2142 return(cube_info);
2143}
2144␌
2145/*
2146%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2147% %
2148% %
2149% %
2150+ G e t N o d e I n f o %
2151% %
2152% %
2153% %
2154%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2155%
2156% GetQNodeInfo() allocates memory for a new node in the color cube tree and
2157% presets all fields to zero.
2158%
2159% The format of the GetQNodeInfo method is:
2160%
2161% QNodeInfo *GetQNodeInfo(QCubeInfo *cube_info,const size_t id,
2162% const size_t level,QNodeInfo *parent)
2163%
2164% A description of each parameter follows.
2165%
2166% o node: The GetQNodeInfo method returns a pointer to a queue of nodes.
2167%
2168% o id: Specifies the child number of the node.
2169%
2170% o level: Specifies the level in the storage_class the node resides.
2171%
2172*/
2173static QNodeInfo *GetQNodeInfo(QCubeInfo *cube_info,const size_t id,
2174 const size_t level,QNodeInfo *parent)
2175{
2176 QNodeInfo
2177 *node_info;
2178
2179 if (cube_info->free_nodes == 0)
2180 {
2181 QNodes
2182 *nodes;
2183
2184 /*
2185 Allocate a new queue of nodes.
2186 */
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;
2198 }
2199 cube_info->nodes++;
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;
2204 node_info->id=id;
2205 node_info->level=level;
2206 return(node_info);
2207}
2208␌
2209/*
2210%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2211% %
2212% %
2213% %
2214% G e t I m a g e Q u a n t i z e E r r o r %
2215% %
2216% %
2217% %
2218%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2219%
2220% GetImageQuantizeError() measures the difference between the original
2221% and quantized images. This difference is the total quantization error.
2222% The error is computed by summing over all pixels in an image the distance
2223% squared in RGB space between each reference pixel value and its quantized
2224% value. These values are computed:
2225%
2226% o mean_error_per_pixel: This value is the mean error for any single
2227% pixel in the image.
2228%
2229% o normalized_mean_square_error: This value is the normalized mean
2230% quantization error for any single pixel in the image. This distance
2231% measure is normalized to a range between 0 and 1. It is independent
2232% of the range of red, green, and blue values in the image.
2233%
2234% o normalized_maximum_square_error: This value is the normalized
2235% maximum quantization error for any single pixel in the image. This
2236% distance measure is normalized to a range between 0 and 1. It is
2237% independent of the range of red, green, and blue values in your image.
2238%
2239% The format of the GetImageQuantizeError method is:
2240%
2241% MagickBooleanType GetImageQuantizeError(Image *image,
2242% ExceptionInfo *exception)
2243%
2244% A description of each parameter follows.
2245%
2246% o image: the image.
2247%
2248% o exception: return any errors or warnings in this structure.
2249%
2250*/
2251MagickExport MagickBooleanType GetImageQuantizeError(Image *image,
2252 ExceptionInfo *exception)
2253{
2254 CacheView
2255 *image_view;
2256
2257 double
2258 alpha,
2259 area,
2260 beta,
2261 distance,
2262 maximum_error,
2263 mean_error,
2264 mean_error_per_pixel;
2265
2266 ssize_t
2267 index,
2268 y;
2269
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)
2277 return(MagickTrue);
2278 alpha=1.0;
2279 beta=1.0;
2280 area=3.0*image->columns*image->rows;
2281 maximum_error=0.0;
2282 mean_error_per_pixel=0.0;
2283 mean_error=0.0;
2284 image_view=AcquireVirtualCacheView(image,exception);
2285 for (y=0; y < (ssize_t) image->rows; y++)
2286 {
2287 const Quantum
2288 *magick_restrict p;
2289
2290 ssize_t
2291 x;
2292
2293 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
2294 if (p == (const Quantum *) NULL)
2295 break;
2296 for (x=0; x < (ssize_t) image->columns; x++)
2297 {
2298 index=(ssize_t) GetPixelIndex(image,p);
2299 if (image->alpha_trait != UndefinedPixelTrait)
2300 {
2301 alpha=(double) (QuantumScale*(double) GetPixelAlpha(image,p));
2302 beta=(double) (QuantumScale*image->colormap[index].alpha);
2303 }
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);
2323 }
2324 }
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*
2328 mean_error/area;
2329 image->error.normalized_maximum_error=(double) QuantumScale*maximum_error;
2330 return(MagickTrue);
2331}
2332␌
2333/*
2334%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2335% %
2336% %
2337% %
2338% G e t Q u a n t i z e I n f o %
2339% %
2340% %
2341% %
2342%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2343%
2344% GetQuantizeInfo() initializes the QuantizeInfo structure.
2345%
2346% The format of the GetQuantizeInfo method is:
2347%
2348% void GetQuantizeInfo(QuantizeInfo *quantize_info)
2349%
2350% A description of each parameter follows:
2351%
2352% o quantize_info: Specifies a pointer to a QuantizeInfo structure.
2353%
2354*/
2355MagickExport void GetQuantizeInfo(QuantizeInfo *quantize_info)
2356{
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;
2366}
2367␌
2368/*
2369%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2370% %
2371% %
2372% %
2373% K m e a n s I m a g e %
2374% %
2375% %
2376% %
2377%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2378%
2379% KmeansImage() applies k-means color reduction to an image. This is a
2380% colorspace clustering or segmentation technique.
2381%
2382% The format of the KmeansImage method is:
2383%
2384% MagickBooleanType KmeansImage(Image *image,const size_t number_colors,
2385% const size_t max_iterations,const double tolerance,
2386% ExceptionInfo *exception)
2387%
2388% A description of each parameter follows:
2389%
2390% o image: the image.
2391%
2392% o number_colors: number of colors to use as seeds.
2393%
2394% o max_iterations: maximum number of iterations while converging.
2395%
2396% o tolerance: the maximum tolerance.
2397%
2398% o exception: return any errors or warnings in this structure.
2399%
2400*/
2401
2402typedef struct _KmeansInfo
2403{
2404 double
2405 red,
2406 green,
2407 blue,
2408 alpha,
2409 black,
2410 count,
2411 distortion;
2412} KmeansInfo;
2413
2414static KmeansInfo **DestroyKmeansTLS(KmeansInfo **kmeans_info)
2415{
2416 ssize_t
2417 i;
2418
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);
2425}
2426
2427static int DominantColorCompare(const void *x,const void *y)
2428{
2429 PixelInfo
2430 *pixel_1,
2431 *pixel_2;
2432
2433 pixel_1=(PixelInfo *) x;
2434 pixel_2=(PixelInfo *) y;
2435 return((int) pixel_2->count-(int) pixel_1->count);
2436}
2437
2438static KmeansInfo **AcquireKmeansTLS(const size_t number_colors)
2439{
2440 KmeansInfo
2441 **kmeans_info;
2442
2443 size_t
2444 number_threads;
2445
2446 ssize_t
2447 i;
2448
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++)
2456 {
2457 kmeans_info[i]=(KmeansInfo *) AcquireQuantumMemory(number_colors,
2458 sizeof(**kmeans_info));
2459 if (kmeans_info[i] == (KmeansInfo *) NULL)
2460 return(DestroyKmeansTLS(kmeans_info));
2461 }
2462 return(kmeans_info);
2463}
2464
2465static inline double KmeansMetric(const Image *magick_restrict image,
2466 const Quantum *magick_restrict p,const PixelInfo *magick_restrict q)
2467{
2468 double
2469 gamma,
2470 metric,
2471 pixel;
2472
2473 gamma=1.0;
2474 metric=0.0;
2475 if ((image->alpha_trait != UndefinedPixelTrait) ||
2476 (q->alpha_trait != UndefinedPixelTrait))
2477 {
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;
2485 }
2486 if (image->colorspace == CMYKColorspace)
2487 {
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);
2493 }
2494 metric*=3.0;
2495 pixel=QuantumScale*((double) GetPixelRed(image,p)-q->red);
2496 if (IsHueCompatibleColorspace(image->colorspace) != MagickFalse)
2497 {
2498 if (fabs((double) pixel) > 0.5)
2499 pixel-=0.5;
2500 pixel*=2.0;
2501 }
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;
2507 return(metric);
2508}
2509
2510MagickExport MagickBooleanType KmeansImage(Image *image,
2511 const size_t number_colors,const size_t max_iterations,const double tolerance,
2512 ExceptionInfo *exception)
2513{
2514#define KmeansImageTag "Kmeans/Image"
2515#define RandomColorComponent(info) \
2516 ((double) QuantumRange*GetPseudoRandomValue(info))
2517
2518 CacheView
2519 *image_view;
2520
2521 char
2522 tuple[MagickPathExtent];
2523
2524 const char
2525 *colors;
2526
2527 double
2528 previous_tolerance;
2529
2530 Image
2531 *dominant_image;
2532
2533 KmeansInfo
2534 **kmeans_pixels;
2535
2536 MagickBooleanType
2537 verbose,
2538 status;
2539
2540 size_t
2541 number_threads;
2542
2543 ssize_t
2544 n;
2545
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)
2556 {
2557 QCubeInfo
2558 *cube_info;
2559
2560 QuantizeInfo
2561 *quantize_info;
2562
2563 size_t
2564 depth;
2565
2566 /*
2567 Seed clusters from color quantization.
2568 */
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++)
2575 n>>=2;
2576 cube_info=GetQCubeInfo(quantize_info,depth,number_colors);
2577 if (cube_info == (QCubeInfo *) NULL)
2578 {
2579 quantize_info=DestroyQuantizeInfo(quantize_info);
2580 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
2581 image->filename);
2582 }
2583 status=ClassifyImageColors(cube_info,image,exception);
2584 if (status != MagickFalse)
2585 {
2586 if (cube_info->colors > cube_info->maximum_colors)
2587 ReduceImageColors(image,cube_info);
2588 status=SetImageColormap(image,cube_info,exception);
2589 }
2590 DestroyQCubeInfo(cube_info);
2591 quantize_info=DestroyQuantizeInfo(quantize_info);
2592 if (status == MagickFalse)
2593 return(status);
2594 }
2595 else
2596 {
2597 char
2598 color[MagickPathExtent];
2599
2600 const char
2601 *p;
2602
2603 /*
2604 Seed clusters from color list (e.g. red;green;blue).
2605 */
2606 status=AcquireImageColormap(image,number_colors,exception);
2607 if (status == MagickFalse)
2608 return(status);
2609 for (n=0, p=colors; n < (ssize_t) image->colors; n++)
2610 {
2611 const char
2612 *q;
2613
2614 for (q=p; *q != '\0'; q++)
2615 if (*q == ';')
2616 break;
2617 (void) CopyMagickString(color,p,(size_t) MagickMin(q-p+1,
2618 MagickPathExtent));
2619 (void) QueryColorCompliance(color,AllCompliance,image->colormap+n,
2620 exception);
2621 if (*q == '\0')
2622 {
2623 n++;
2624 break;
2625 }
2626 p=q+1;
2627 }
2628 if (n < (ssize_t) image->colors)
2629 {
2630 RandomInfo
2631 *random_info;
2632
2633 /*
2634 Seed clusters from random values.
2635 */
2636 random_info=AcquireRandomInfo();
2637 for ( ; n < (ssize_t) image->colors; n++)
2638 {
2639 (void) QueryColorCompliance("#000",AllCompliance,image->colormap+n,
2640 exception);
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);
2648 }
2649 random_info=DestroyRandomInfo(random_info);
2650 }
2651 }
2652 /*
2653 Iterative refinement.
2654 */
2655 kmeans_pixels=AcquireKmeansTLS(number_colors);
2656 if (kmeans_pixels == (KmeansInfo **) NULL)
2657 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
2658 image->filename);
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++)
2664 {
2665 double
2666 distortion;
2667
2668 ssize_t
2669 j,
2670 y;
2671
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)
2677#endif
2678 for (y=0; y < (ssize_t) image->rows; y++)
2679 {
2680 const int
2681 id = GetOpenMPThreadId();
2682
2683 Quantum
2684 *magick_restrict q;
2685
2686 ssize_t
2687 x;
2688
2689 if (status == MagickFalse)
2690 continue;
2691 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2692 if (q == (Quantum *) NULL)
2693 {
2694 status=MagickFalse;
2695 continue;
2696 }
2697 for (x=0; x < (ssize_t) image->columns; x++)
2698 {
2699 double
2700 min_distance;
2701
2702 ssize_t
2703 i,
2704 k;
2705
2706 /*
2707 Assign each pixel whose mean has the least squared color distance.
2708 */
2709 k=0;
2710 min_distance=KmeansMetric(image,q,image->colormap+0);
2711 for (i=1; i < (ssize_t) image->colors; i++)
2712 {
2713 double
2714 distance;
2715
2716 if (min_distance <= MagickEpsilon)
2717 break;
2718 distance=KmeansMetric(image,q,image->colormap+i);
2719 if (distance < min_distance)
2720 {
2721 min_distance=distance;
2722 k=i;
2723 }
2724 }
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);
2739 }
2740 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2741 status=MagickFalse;
2742 }
2743 if (status == MagickFalse)
2744 break;
2745 /*
2746 Reduce sums to [0] entry.
2747 */
2748 for (j=1; j < (ssize_t) number_threads; j++)
2749 {
2750 ssize_t
2751 k;
2752
2753 for (k=0; k < (ssize_t) image->colors; k++)
2754 {
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;
2764 }
2765 }
2766 /*
2767 Calculate the new means (centroids) of the pixels in the new clusters.
2768 */
2769 distortion=0.0;
2770 for (j=0; j < (ssize_t) image->colors; j++)
2771 {
2772 double
2773 gamma;
2774
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;
2790 }
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)
2796 break;
2797 previous_tolerance=distortion;
2798 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2799 {
2800 MagickBooleanType
2801 proceed;
2802
2803 proceed=SetImageProgress(image,KmeansImageTag,(MagickOffsetType) n,
2804 max_iterations);
2805 if (proceed == MagickFalse)
2806 status=MagickFalse;
2807 }
2808 }
2809 image_view=DestroyCacheView(image_view);
2810 if (verbose != MagickFalse)
2811 for (n=0; n < (ssize_t) image->colors; n++)
2812 {
2813 GetColorTuple(image->colormap+n,MagickTrue,tuple);
2814 (void) FormatLocaleFile(stderr,"%s %.17g\n",tuple,(double)
2815 image->colormap[n].count);
2816 }
2817 dominant_image=CloneImage(image,0,0,MagickTrue,exception);
2818 if (dominant_image != (Image *) NULL)
2819 {
2820 /*
2821 Note dominant color.
2822 */
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);
2828 }
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)
2834 return(status);
2835 return(SyncImage(image,exception));
2836}
2837␌
2838/*
2839%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2840% %
2841% %
2842% %
2843% P o s t e r i z e I m a g e %
2844% %
2845% %
2846% %
2847%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2848%
2849% PosterizeImage() reduces the image to a limited number of colors for a
2850% "poster" effect.
2851%
2852% The format of the PosterizeImage method is:
2853%
2854% MagickBooleanType PosterizeImage(Image *image,const size_t levels,
2855% const DitherMethod dither_method,ExceptionInfo *exception)
2856%
2857% A description of each parameter follows:
2858%
2859% o image: Specifies a pointer to an Image structure.
2860%
2861% o levels: Number of color levels allowed in each channel. Very low values
2862% (2, 3, or 4) have the most visible effect.
2863%
2864% o dither_method: choose from UndefinedDitherMethod, NoDitherMethod,
2865% RiemersmaDitherMethod, FloydSteinbergDitherMethod.
2866%
2867% o exception: return any errors or warnings in this structure.
2868%
2869*/
2870
2871static inline double MagickRound(double x)
2872{
2873 /*
2874 Round the fraction to nearest integer.
2875 */
2876 if ((x-floor(x)) < (ceil(x)-x))
2877 return(floor(x));
2878 return(ceil(x));
2879}
2880
2881static inline Quantum PosterizePixel(const Quantum pixel,const size_t levels)
2882{
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));
2886}
2887
2888MagickExport MagickBooleanType PosterizeImage(Image *image,const size_t levels,
2889 const DitherMethod dither_method,ExceptionInfo *exception)
2890{
2891#define PosterizeImageTag "Posterize/Image"
2892
2893 CacheView
2894 *image_view;
2895
2896 MagickBooleanType
2897 status = MagickTrue;
2898
2899 MagickOffsetType
2900 progress;
2901
2902 ssize_t
2903 y;
2904
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++)
2913 {
2914 Image
2915 *map_image;
2916
2917 size_t
2918 channels = 0,
2919 number_columns;
2920
2921 ssize_t
2922 i;
2923
2924 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2925 {
2926 PixelChannel channel = GetPixelChannelChannel(image,i);
2927 PixelTrait traits = GetPixelChannelTraits(image,channel);
2928 if ((traits & UpdatePixelTrait) != 0)
2929 channels++;
2930 }
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)
2934 {
2935 status=MagickFalse;
2936 break;
2937 }
2938 if (SetImageStorageClass(map_image,DirectClass,exception) == MagickFalse)
2939 {
2940 status=MagickFalse;
2941 break;
2942 }
2943 {
2944 CacheView
2945 *map_image_view;
2946
2947 MagickRealType
2948 scale = (MagickRealType) QuantumRange/(levels-1.0);
2949
2950 Quantum
2951 *magick_restrict q;
2952
2953 ssize_t
2954 c,
2955 x;
2956
2957 /*
2958 Populate the map image.
2959 */
2960 map_image_view=AcquireAuthenticCacheView (map_image,exception);
2961 q=GetCacheViewAuthenticPixels(map_image_view,0,0,number_columns,1,
2962 exception);
2963 if (q == (const Quantum *) NULL)
2964 {
2965 map_image_view=DestroyCacheView(map_image_view);
2966 status=MagickFalse;
2967 break;
2968 }
2969 for (x=0; x < (ssize_t) number_columns; x++)
2970 {
2971 size_t remainder = (size_t) x;
2972 for (c=0; c < (ssize_t) GetPixelChannels(image); c++)
2973 {
2974 PixelChannel channel = GetPixelChannelChannel(image,c);
2975 PixelTrait traits = GetPixelChannelTraits(image,channel);
2976 if ((traits & UpdatePixelTrait) != 0)
2977 {
2978 size_t value = remainder % levels;
2979 SetPixelChannel(map_image,channel,(const Quantum) (scale*value),q);
2980 remainder=(remainder-value)/levels;
2981 }
2982 }
2983 q+=(ptrdiff_t) GetPixelChannels(map_image);
2984 }
2985 if (SyncCacheViewAuthenticPixels(map_image_view,exception) == MagickFalse)
2986 {
2987 map_image_view=DestroyCacheView(map_image_view);
2988 status=MagickFalse;
2989 break;
2990 }
2991 map_image_view=DestroyCacheView(map_image_view);
2992 }
2993 if (status != MagickFalse)
2994 {
2995 /*
2996 Remap to the map image.
2997 */
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);
3002 }
3003 map_image=DestroyImage(map_image);
3004 }
3005 else
3006 {
3007 /*
3008 No dither or too many levels.
3009 */
3010 if (image->storage_class == PseudoClass)
3011 {
3012 ssize_t
3013 i;
3014
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)
3018#endif
3019 for (i=0; i < (ssize_t) image->colors; i++)
3020 {
3021 /*
3022 Posterize colormap.
3023 */
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);
3036 }
3037 }
3038 /*
3039 Posterize image.
3040 */
3041 progress=0;
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)
3046#endif
3047 for (y=0; y < (ssize_t) image->rows; y++)
3048 {
3049 Quantum
3050 *magick_restrict q;
3051
3052 ssize_t
3053 x;
3054
3055 if (status == MagickFalse)
3056 continue;
3057 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
3058 exception);
3059 if (q == (Quantum *) NULL)
3060 {
3061 status=MagickFalse;
3062 continue;
3063 }
3064 for (x=0; x < (ssize_t) image->columns; x++)
3065 {
3066 ssize_t
3067 i;
3068
3069 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3070 {
3071 PixelChannel channel = GetPixelChannelChannel(image,i);
3072 PixelTrait traits = GetPixelChannelTraits(image,channel);
3073 if ((traits & UpdatePixelTrait) == 0)
3074 continue;
3075 SetPixelChannel(image,channel,PosterizePixel(q[i],levels),q);
3076 }
3077 q+=(ptrdiff_t) GetPixelChannels(image);
3078 }
3079 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3080 status=MagickFalse;
3081 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3082 {
3083 MagickBooleanType
3084 proceed;
3085
3086#if defined(MAGICKCORE_OPENMP_SUPPORT)
3087 #pragma omp atomic
3088#endif
3089 progress++;
3090 proceed=SetImageProgress(image,PosterizeImageTag,progress,
3091 image->rows);
3092 if (proceed == MagickFalse)
3093 status=MagickFalse;
3094 }
3095 }
3096 image_view=DestroyCacheView(image_view);
3097 {
3098 QuantizeInfo *quantize_info=AcquireQuantizeInfo((ImageInfo *) NULL);
3099 quantize_info->number_colors=(size_t) MagickMin(levels*levels*levels,
3100 MaxColormapSize);
3101 quantize_info->dither_method=dither_method;
3102 status=QuantizeImage(quantize_info,image,exception);
3103 quantize_info=DestroyQuantizeInfo(quantize_info);
3104 }
3105 }
3106 return(status);
3107}
3108␌
3109/*
3110%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3111% %
3112% %
3113% %
3114+ P r u n e C h i l d %
3115% %
3116% %
3117% %
3118%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3119%
3120% PruneChild() deletes the given node and merges its statistics into its
3121% parent.
3122%
3123% The format of the PruneSubtree method is:
3124%
3125% PruneChild(QCubeInfo *cube_info,const QNodeInfo *node_info)
3126%
3127% A description of each parameter follows.
3128%
3129% o cube_info: A pointer to the Cube structure.
3130%
3131% o node_info: pointer to node in color cube tree that is to be pruned.
3132%
3133*/
3134static void PruneChild(QCubeInfo *cube_info,const QNodeInfo *node_info)
3135{
3136 QNodeInfo
3137 *parent;
3138
3139 size_t
3140 number_children;
3141
3142 ssize_t
3143 i;
3144
3145 /*
3146 Traverse any children.
3147 */
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)
3153 {
3154 /*
3155 Merge color statistics into parent.
3156 */
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;
3164 cube_info->nodes--;
3165 }
3166}
3167␌
3168/*
3169%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3170% %
3171% %
3172% %
3173+ P r u n e L e v e l %
3174% %
3175% %
3176% %
3177%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3178%
3179% PruneLevel() deletes all nodes at the bottom level of the color tree merging
3180% their color statistics into their parent node.
3181%
3182% The format of the PruneLevel method is:
3183%
3184% PruneLevel(QCubeInfo *cube_info,const QNodeInfo *node_info)
3185%
3186% A description of each parameter follows.
3187%
3188% o cube_info: A pointer to the Cube structure.
3189%
3190% o node_info: pointer to node in color cube tree that is to be pruned.
3191%
3192*/
3193static void PruneLevel(QCubeInfo *cube_info,const QNodeInfo *node_info)
3194{
3195 size_t
3196 number_children;
3197
3198 ssize_t
3199 i;
3200
3201 /*
3202 Traverse any children.
3203 */
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);
3210}
3211␌
3212/*
3213%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3214% %
3215% %
3216% %
3217+ P r u n e T o C u b e D e p t h %
3218% %
3219% %
3220% %
3221%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3222%
3223% PruneToCubeDepth() deletes any nodes at a depth greater than
3224% cube_info->depth while merging their color statistics into their parent
3225% node.
3226%
3227% The format of the PruneToCubeDepth method is:
3228%
3229% PruneToCubeDepth(QCubeInfo *cube_info,const QNodeInfo *node_info)
3230%
3231% A description of each parameter follows.
3232%
3233% o cube_info: A pointer to the Cube structure.
3234%
3235% o node_info: pointer to node in color cube tree that is to be pruned.
3236%
3237*/
3238static void PruneToCubeDepth(QCubeInfo *cube_info,const QNodeInfo *node_info)
3239{
3240 size_t
3241 number_children;
3242
3243 ssize_t
3244 i;
3245
3246 /*
3247 Traverse any children.
3248 */
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);
3255}
3256␌
3257/*
3258%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3259% %
3260% %
3261% %
3262% Q u a n t i z e I m a g e %
3263% %
3264% %
3265% %
3266%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3267%
3268% QuantizeImage() analyzes the colors within a reference image and chooses a
3269% fixed number of colors to represent the image. The goal of the algorithm
3270% is to minimize the color difference between the input and output image while
3271% minimizing the processing time.
3272%
3273% The format of the QuantizeImage method is:
3274%
3275% MagickBooleanType QuantizeImage(const QuantizeInfo *quantize_info,
3276% Image *image,ExceptionInfo *exception)
3277%
3278% A description of each parameter follows:
3279%
3280% o quantize_info: Specifies a pointer to an QuantizeInfo structure.
3281%
3282% o image: the image.
3283%
3284% o exception: return any errors or warnings in this structure.
3285%
3286*/
3287MagickExport MagickBooleanType QuantizeImage(const QuantizeInfo *quantize_info,
3288 Image *image,ExceptionInfo *exception)
3289{
3290 QCubeInfo
3291 *cube_info;
3292
3293 ImageType
3294 type;
3295
3296 MagickBooleanType
3297 status;
3298
3299 size_t
3300 depth,
3301 maximum_colors;
3302
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;
3320 if (depth == 0)
3321 {
3322 size_t
3323 colors;
3324
3325 /*
3326 Depth of color tree is: Log4(colormap size)+2.
3327 */
3328 colors=maximum_colors;
3329 for (depth=1; colors != 0; depth++)
3330 colors>>=2;
3331 if ((quantize_info->dither_method != NoDitherMethod) && (depth > 2))
3332 depth--;
3333 if ((image->alpha_trait != UndefinedPixelTrait) && (depth > 5))
3334 depth--;
3335 if (IsGrayImageType(type) != MagickFalse)
3336 depth=MaxTreeDepth;
3337 }
3338 /*
3339 Initialize color cube.
3340 */
3341 cube_info=GetQCubeInfo(quantize_info,depth,maximum_colors);
3342 if (cube_info == (QCubeInfo *) NULL)
3343 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
3344 image->filename);
3345 status=ClassifyImageColors(cube_info,image,exception);
3346 if (status != MagickFalse)
3347 {
3348 /*
3349 Reduce the number of colors in the image.
3350 */
3351 if (cube_info->colors > cube_info->maximum_colors)
3352 ReduceImageColors(image,cube_info);
3353 status=AssignImageColors(image,cube_info,exception);
3354 }
3355 DestroyQCubeInfo(cube_info);
3356 return(status);
3357}
3358␌
3359/*
3360%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3361% %
3362% %
3363% %
3364% Q u a n t i z e I m a g e s %
3365% %
3366% %
3367% %
3368%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3369%
3370% QuantizeImages() analyzes the colors within a set of reference images and
3371% chooses a fixed number of colors to represent the set. The goal of the
3372% algorithm is to minimize the color difference between the input and output
3373% images while minimizing the processing time.
3374%
3375% The format of the QuantizeImages method is:
3376%
3377% MagickBooleanType QuantizeImages(const QuantizeInfo *quantize_info,
3378% Image *images,ExceptionInfo *exception)
3379%
3380% A description of each parameter follows:
3381%
3382% o quantize_info: Specifies a pointer to an QuantizeInfo structure.
3383%
3384% o images: Specifies a pointer to a list of Image structures.
3385%
3386% o exception: return any errors or warnings in this structure.
3387%
3388*/
3389MagickExport MagickBooleanType QuantizeImages(const QuantizeInfo *quantize_info,
3390 Image *images,ExceptionInfo *exception)
3391{
3392 Image
3393 *image;
3394
3395 MagickBooleanType
3396 proceed,
3397 status;
3398
3399 MagickProgressMonitor
3400 progress_monitor;
3401
3402 QCubeInfo
3403 *cube_info;
3404
3405 size_t
3406 depth,
3407 maximum_colors,
3408 number_images;
3409
3410 ssize_t
3411 i;
3412
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)
3422 {
3423 /*
3424 Handle a single image with QuantizeImage.
3425 */
3426 status=QuantizeImage(quantize_info,images,exception);
3427 return(status);
3428 }
3429 status=MagickFalse;
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;
3436 if (depth == 0)
3437 {
3438 size_t
3439 colors;
3440
3441 /*
3442 Depth of color tree is: Log4(colormap size)+2.
3443 */
3444 colors=maximum_colors;
3445 for (depth=1; colors != 0; depth++)
3446 colors>>=2;
3447 if (quantize_info->dither_method != NoDitherMethod)
3448 depth--;
3449 }
3450 /*
3451 Initialize color cube.
3452 */
3453 cube_info=GetQCubeInfo(quantize_info,depth,maximum_colors);
3454 if (cube_info == (QCubeInfo *) NULL)
3455 {
3456 (void) ThrowMagickException(exception,GetMagickModule(),
3457 ResourceLimitError,"MemoryAllocationFailed","`%s'",images->filename);
3458 return(MagickFalse);
3459 }
3460 number_images=GetImageListLength(images);
3461 image=images;
3462 for (i=0; image != (Image *) NULL; i++)
3463 {
3464 progress_monitor=SetImageProgressMonitor(image,(MagickProgressMonitor) NULL,
3465 image->client_data);
3466 status=ClassifyImageColors(cube_info,image,exception);
3467 if (status == MagickFalse)
3468 break;
3469 (void) SetImageProgressMonitor(image,progress_monitor,image->client_data);
3470 proceed=SetImageProgress(image,AssignImageTag,(MagickOffsetType) i,
3471 number_images);
3472 if (proceed == MagickFalse)
3473 break;
3474 image=GetNextImageInList(image);
3475 }
3476 if (status != MagickFalse)
3477 {
3478 /*
3479 Reduce the number of colors in an image sequence.
3480 */
3481 ReduceImageColors(images,cube_info);
3482 image=images;
3483 for (i=0; image != (Image *) NULL; i++)
3484 {
3485 progress_monitor=SetImageProgressMonitor(image,(MagickProgressMonitor)
3486 NULL,image->client_data);
3487 status=AssignImageColors(image,cube_info,exception);
3488 if (status == MagickFalse)
3489 break;
3490 (void) SetImageProgressMonitor(image,progress_monitor,
3491 image->client_data);
3492 proceed=SetImageProgress(image,AssignImageTag,(MagickOffsetType) i,
3493 number_images);
3494 if (proceed == MagickFalse)
3495 break;
3496 image=GetNextImageInList(image);
3497 }
3498 }
3499 DestroyQCubeInfo(cube_info);
3500 return(status);
3501}
3502␌
3503/*
3504%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3505% %
3506% %
3507% %
3508+ Q u a n t i z e E r r o r F l a t t e n %
3509% %
3510% %
3511% %
3512%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3513%
3514% QuantizeErrorFlatten() traverses the color cube and flattens the quantization
3515% error into a sorted 1D array. This accelerates the color reduction process.
3516%
3517% Contributed by Yoya.
3518%
3519% The format of the QuantizeErrorFlatten method is:
3520%
3521% size_t QuantizeErrorFlatten(const QCubeInfo *cube_info,
3522% const QNodeInfo *node_info,const ssize_t offset,
3523% double *quantize_error)
3524%
3525% A description of each parameter follows.
3526%
3527% o cube_info: A pointer to the Cube structure.
3528%
3529% o node_info: pointer to node in color cube tree that is current pointer.
3530%
3531% o offset: quantize error offset.
3532%
3533% o quantize_error: the quantization error vector.
3534%
3535*/
3536static size_t QuantizeErrorFlatten(const QCubeInfo *cube_info,
3537 const QNodeInfo *node_info,const ssize_t offset,double *quantize_error)
3538{
3539 size_t
3540 n,
3541 number_children;
3542
3543 ssize_t
3544 i;
3545
3546 if (offset >= (ssize_t) cube_info->nodes)
3547 return(0);
3548 quantize_error[offset]=node_info->quantize_error;
3549 n=1;
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,
3554 quantize_error);
3555 return(n);
3556}
3557␌
3558/*
3559%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3560% %
3561% %
3562% %
3563+ R e d u c e %
3564% %
3565% %
3566% %
3567%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3568%
3569% Reduce() traverses the color cube tree and prunes any node whose
3570% quantization error falls below a particular threshold.
3571%
3572% The format of the Reduce method is:
3573%
3574% Reduce(QCubeInfo *cube_info,const QNodeInfo *node_info)
3575%
3576% A description of each parameter follows.
3577%
3578% o cube_info: A pointer to the Cube structure.
3579%
3580% o node_info: pointer to node in color cube tree that is to be pruned.
3581%
3582*/
3583static void Reduce(QCubeInfo *cube_info,const QNodeInfo *node_info)
3584{
3585 size_t
3586 number_children;
3587
3588 ssize_t
3589 i;
3590
3591 /*
3592 Traverse any children.
3593 */
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);
3600 else
3601 {
3602 /*
3603 Find minimum pruning threshold.
3604 */
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;
3609 }
3610}
3611␌
3612/*
3613%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3614% %
3615% %
3616% %
3617+ R e d u c e I m a g e C o l o r s %
3618% %
3619% %
3620% %
3621%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3622%
3623% ReduceImageColors() repeatedly prunes the tree until the number of nodes
3624% with n2 > 0 is less than or equal to the maximum number of colors allowed
3625% in the output image. On any given iteration over the tree, it selects
3626% those nodes whose E value is minimal for pruning and merges their
3627% color statistics upward. It uses a pruning threshold, Ep, to govern
3628% node selection as follows:
3629%
3630% Ep = 0
3631% while number of nodes with (n2 > 0) > required maximum number of colors
3632% prune all nodes such that E <= Ep
3633% Set Ep to minimum E in remaining nodes
3634%
3635% This has the effect of minimizing any quantization error when merging
3636% two nodes together.
3637%
3638% When a node to be pruned has offspring, the pruning procedure invokes
3639% itself recursively in order to prune the tree from the leaves upward.
3640% n2, Sr, Sg, and Sb in a node being pruned are always added to the
3641% corresponding data in that node's parent. This retains the pruned
3642% node's color characteristics for later averaging.
3643%
3644% For each node, n2 pixels exist for which that node represents the
3645% smallest volume in RGB space containing those pixel's colors. When n2
3646% > 0 the node will uniquely define a color in the output image. At the
3647% beginning of reduction, n2 = 0 for all nodes except a the leaves of
3648% the tree which represent colors present in the input image.
3649%
3650% The other pixel count, n1, indicates the total number of colors
3651% within the cubic volume which the node represents. This includes n1 -
3652% n2 pixels whose colors should be defined by nodes at a lower level in
3653% the tree.
3654%
3655% The format of the ReduceImageColors method is:
3656%
3657% ReduceImageColors(const Image *image,QCubeInfo *cube_info)
3658%
3659% A description of each parameter follows.
3660%
3661% o image: the image.
3662%
3663% o cube_info: A pointer to the Cube structure.
3664%
3665*/
3666
3667static int QuantizeErrorCompare(const void *error_p,const void *error_q)
3668{
3669 double
3670 *p,
3671 *q;
3672
3673 p=(double *) error_p;
3674 q=(double *) error_q;
3675 if (*p > *q)
3676 return(1);
3677 if (fabs(*q-*p) <= MagickEpsilon)
3678 return(0);
3679 return(-1);
3680}
3681
3682static void ReduceImageColors(const Image *image,QCubeInfo *cube_info)
3683{
3684#define ReduceImageTag "Reduce/Image"
3685
3686 MagickBooleanType
3687 proceed;
3688
3689 MagickOffsetType
3690 offset;
3691
3692 size_t
3693 span;
3694
3695 cube_info->next_threshold=0.0;
3696 if (cube_info->colors > cube_info->maximum_colors)
3697 {
3698 double
3699 *quantize_error;
3700
3701 /*
3702 Enable rapid reduction of the number of unique colors.
3703 */
3704 quantize_error=(double *) AcquireQuantumMemory(cube_info->nodes,
3705 sizeof(*quantize_error));
3706 if (quantize_error != (double *) NULL)
3707 {
3708 (void) QuantizeErrorFlatten(cube_info,cube_info->root,0,
3709 quantize_error);
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);
3716 }
3717 }
3718 for (span=cube_info->colors; cube_info->colors > cube_info->maximum_colors; )
3719 {
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)
3728 break;
3729 }
3730}
3731␌
3732/*
3733%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3734% %
3735% %
3736% %
3737% R e m a p I m a g e %
3738% %
3739% %
3740% %
3741%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3742%
3743% RemapImage() replaces the colors of an image with the closest of the colors
3744% from the reference image.
3745%
3746% The format of the RemapImage method is:
3747%
3748% MagickBooleanType RemapImage(const QuantizeInfo *quantize_info,
3749% Image *image,const Image *remap_image,ExceptionInfo *exception)
3750%
3751% A description of each parameter follows:
3752%
3753% o quantize_info: Specifies a pointer to an QuantizeInfo structure.
3754%
3755% o image: the image.
3756%
3757% o remap_image: the reference image.
3758%
3759% o exception: return any errors or warnings in this structure.
3760%
3761*/
3762MagickExport MagickBooleanType RemapImage(const QuantizeInfo *quantize_info,
3763 Image *image,const Image *remap_image,ExceptionInfo *exception)
3764{
3765 QCubeInfo
3766 *cube_info;
3767
3768 MagickBooleanType
3769 status;
3770
3771 /*
3772 Initialize color cube.
3773 */
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",
3785 image->filename);
3786 cube_info->quantize_info->colorspace=remap_image->colorspace;
3787 status=ClassifyImageColors(cube_info,remap_image,exception);
3788 if (status != MagickFalse)
3789 {
3790 /*
3791 Classify image colors from the reference image.
3792 */
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);
3797 }
3798 DestroyQCubeInfo(cube_info);
3799 return(status);
3800}
3801␌
3802/*
3803%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3804% %
3805% %
3806% %
3807% R e m a p I m a g e s %
3808% %
3809% %
3810% %
3811%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3812%
3813% RemapImages() replaces the colors of a sequence of images with the
3814% closest color from a reference image.
3815%
3816% The format of the RemapImage method is:
3817%
3818% MagickBooleanType RemapImages(const QuantizeInfo *quantize_info,
3819% Image *images,Image *remap_image,ExceptionInfo *exception)
3820%
3821% A description of each parameter follows:
3822%
3823% o quantize_info: Specifies a pointer to an QuantizeInfo structure.
3824%
3825% o images: the image sequence.
3826%
3827% o remap_image: the reference image.
3828%
3829% o exception: return any errors or warnings in this structure.
3830%
3831*/
3832MagickExport MagickBooleanType RemapImages(const QuantizeInfo *quantize_info,
3833 Image *images,const Image *remap_image,ExceptionInfo *exception)
3834{
3835 Image
3836 *image;
3837
3838 MagickBooleanType
3839 status;
3840
3841 QCubeInfo
3842 *cube_info;
3843
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);
3850 image=images;
3851 if (remap_image == (Image *) NULL)
3852 {
3853 /*
3854 Create a global colormap for an image sequence.
3855 */
3856 status=QuantizeImages(quantize_info,images,exception);
3857 return(status);
3858 }
3859 /*
3860 Classify image colors from the reference image.
3861 */
3862 cube_info=GetQCubeInfo(quantize_info,MaxTreeDepth,
3863 quantize_info->number_colors);
3864 if (cube_info == (QCubeInfo *) NULL)
3865 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
3866 image->filename);
3867 status=ClassifyImageColors(cube_info,remap_image,exception);
3868 if (status != MagickFalse)
3869 {
3870 /*
3871 Classify image colors from the reference image.
3872 */
3873 cube_info->quantize_info->number_colors=cube_info->colors;
3874 image=images;
3875 for ( ; image != (Image *) NULL; image=GetNextImageInList(image))
3876 {
3877 status=AssignImageColors(image,cube_info,exception);
3878 if (status == MagickFalse)
3879 break;
3880 }
3881 }
3882 DestroyQCubeInfo(cube_info);
3883 return(status);
3884}
3885␌
3886/*
3887%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3888% %
3889% %
3890% %
3891% S e t G r a y s c a l e I m a g e %
3892% %
3893% %
3894% %
3895%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3896%
3897% SetGrayscaleImage() converts an image to a PseudoClass grayscale image.
3898%
3899% The format of the SetGrayscaleImage method is:
3900%
3901% MagickBooleanType SetGrayscaleImage(Image *image,
3902% ExceptionInfo *exception)
3903%
3904% A description of each parameter follows:
3905%
3906% o image: The image.
3907%
3908% o exception: return any errors or warnings in this structure.
3909%
3910*/
3911
3912#if defined(__cplusplus) || defined(c_plusplus)
3913extern "C" {
3914#endif
3915
3916static int IntensityCompare(const void *x,const void *y)
3917{
3918 double
3919 intensity;
3920
3921 PixelInfo
3922 *color_1,
3923 *color_2;
3924
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);
3934}
3935
3936#if defined(__cplusplus) || defined(c_plusplus)
3937}
3938#endif
3939
3940static MagickBooleanType SetGrayscaleImage(Image *image,
3941 ExceptionInfo *exception)
3942{
3943 CacheView
3944 *image_view;
3945
3946 MagickBooleanType
3947 status;
3948
3949 PixelInfo
3950 *colormap;
3951
3952 size_t
3953 extent;
3954
3955 ssize_t
3956 *colormap_index,
3957 i,
3958 j,
3959 y;
3960
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",
3970 image->filename);
3971 if (image->storage_class != PseudoClass)
3972 {
3973 (void) memset(colormap_index,(-1),extent*sizeof(*colormap_index));
3974 if (AcquireImageColormap(image,MaxColormapSize,exception) == MagickFalse)
3975 {
3976 colormap_index=(ssize_t *) RelinquishMagickMemory(colormap_index);
3977 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
3978 image->filename);
3979 }
3980 image->colors=0;
3981 status=MagickTrue;
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)
3986#endif
3987 for (y=0; y < (ssize_t) image->rows; y++)
3988 {
3989 Quantum
3990 *magick_restrict q;
3991
3992 ssize_t
3993 x;
3994
3995 if (status == MagickFalse)
3996 continue;
3997 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
3998 exception);
3999 if (q == (Quantum *) NULL)
4000 {
4001 status=MagickFalse;
4002 continue;
4003 }
4004 for (x=0; x < (ssize_t) image->columns; x++)
4005 {
4006 size_t
4007 intensity;
4008
4009 intensity=ScaleQuantumToMap(GetPixelRed(image,q));
4010 if (colormap_index[intensity] < 0)
4011 {
4012#if defined(MAGICKCORE_OPENMP_SUPPORT)
4013 #pragma omp critical (MagickCore_SetGrayscaleImage)
4014#endif
4015 if (colormap_index[intensity] < 0)
4016 {
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);
4024 image->colors++;
4025 }
4026 }
4027 SetPixelIndex(image,(Quantum) colormap_index[intensity],q);
4028 q+=(ptrdiff_t) GetPixelChannels(image);
4029 }
4030 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4031 status=MagickFalse;
4032 }
4033 image_view=DestroyCacheView(image_view);
4034 }
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),
4039 IntensityCompare);
4040 colormap=(PixelInfo *) AcquireQuantumMemory(image->colors,sizeof(*colormap));
4041 if (colormap == (PixelInfo *) NULL)
4042 {
4043 colormap_index=(ssize_t *) RelinquishMagickMemory(colormap_index);
4044 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
4045 image->filename);
4046 }
4047 j=0;
4048 colormap[j]=image->colormap[0];
4049 for (i=0; i < (ssize_t) image->colors; i++)
4050 {
4051 if (IsPixelInfoEquivalent(&colormap[j],&image->colormap[i]) == MagickFalse)
4052 {
4053 j++;
4054 colormap[j]=image->colormap[i];
4055 }
4056 colormap_index[(ssize_t) image->colormap[i].alpha]=j;
4057 }
4058 image->colors=(size_t) (j+1);
4059 image->colormap=(PixelInfo *) RelinquishMagickMemory(image->colormap);
4060 image->colormap=colormap;
4061 status=MagickTrue;
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)
4066#endif
4067 for (y=0; y < (ssize_t) image->rows; y++)
4068 {
4069 Quantum
4070 *magick_restrict q;
4071
4072 ssize_t
4073 x;
4074
4075 if (status == MagickFalse)
4076 continue;
4077 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
4078 if (q == (Quantum *) NULL)
4079 {
4080 status=MagickFalse;
4081 continue;
4082 }
4083 for (x=0; x < (ssize_t) image->columns; x++)
4084 {
4085 SetPixelIndex(image,(Quantum) colormap_index[ScaleQuantumToMap(
4086 GetPixelIndex(image,q))],q);
4087 q+=(ptrdiff_t) GetPixelChannels(image);
4088 }
4089 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4090 status=MagickFalse;
4091 }
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;
4097 return(status);
4098}
4099␌
4100/*
4101%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4102% %
4103% %
4104% %
4105+ S e t I m a g e C o l o r m a p %
4106% %
4107% %
4108% %
4109%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4110%
4111% SetImageColormap() traverses the color cube tree and sets the colormap of
4112% the image. A colormap entry is any node in the color cube tree where the
4113% of unique colors is not zero.
4114%
4115% The format of the SetImageColormap method is:
4116%
4117% MagickBooleanType SetImageColormap(Image *image,QCubeInfo *cube_info,
4118% ExceptionInfo *node_info)
4119%
4120% A description of each parameter follows.
4121%
4122% o image: the image.
4123%
4124% o cube_info: A pointer to the Cube structure.
4125%
4126% o exception: return any errors or warnings in this structure.
4127%
4128*/
4129MagickBooleanType SetImageColormap(Image *image,QCubeInfo *cube_info,
4130 ExceptionInfo *exception)
4131{
4132 size_t
4133 number_colors;
4134
4135 number_colors=MagickMax(cube_info->maximum_colors,cube_info->colors);
4136 if (AcquireImageColormap(image,number_colors,exception) == MagickFalse)
4137 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
4138 image->filename);
4139 image->colors=0;
4140 DefineImageColormap(image,cube_info,cube_info->root);
4141 if (image->colors != number_colors)
4142 {
4143 image->colormap=(PixelInfo *) ResizeQuantumMemory(image->colormap,
4144 image->colors+1,sizeof(*image->colormap));
4145 if (image->colormap == (PixelInfo *) NULL)
4146 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
4147 image->filename);
4148 }
4149 return(MagickTrue);
4150}