blob: f33189cdfcfc2dc91d1a0b43c6d49317408e985e [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
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% John Cristy %
17% July 1992 %
18% %
19% %
cristy16af1cb2009-12-11 21:38:29 +000020% Copyright 1999-2010 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000021% 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% http://www.imagemagick.org/script/license.php %
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:
65% The vertex nearest the origin in RGB space and the vertex farthest from
66% the origin.
67%
68% The tree's root node represents the entire domain, (0,0,0) through
69% (Cmax,Cmax,Cmax). Each lower level in the tree is generated by
70% subdividing one node's cube into eight smaller cubes of equal size.
71% This corresponds to bisecting the parent cube with planes passing
72% through the midpoints of each edge.
73%
74% The basic algorithm operates in three phases: Classification,
75% Reduction, and Assignment. Classification builds a color description
76% tree for the image. Reduction collapses the tree until the number it
77% represents, at most, the number of colors desired in the output image.
78% Assignment defines the output image's color map and sets each pixel's
79% color by restorage_class in the reduced tree. Our goal is to minimize
80% the numerical discrepancies between the original colors and quantized
81% colors (quantization error).
82%
83% Classification begins by initializing a color description tree of
84% sufficient depth to represent each possible input color in a leaf.
85% However, it is impractical to generate a fully-formed color description
86% tree in the storage_class phase for realistic values of Cmax. If
87% colors components in the input image are quantized to k-bit precision,
88% so that Cmax= 2k-1, the tree would need k levels below the root node to
89% allow representing each possible input color in a leaf. This becomes
90% prohibitive because the tree's total number of nodes is 1 +
91% sum(i=1, k, 8k).
92%
93% A complete tree would require 19,173,961 nodes for k = 8, Cmax = 255.
94% Therefore, to avoid building a fully populated tree, QUANTIZE: (1)
95% Initializes data structures for nodes only as they are needed; (2)
96% Chooses a maximum depth for the tree as a function of the desired
97% number of colors in the output image (currently log2(colormap size)).
98%
99% For each pixel in the input image, storage_class scans downward from
100% the root of the color description tree. At each level of the tree it
101% identifies the single node which represents a cube in RGB space
102% containing the pixel's color. It updates the following data for each
103% such node:
104%
105% n1: Number of pixels whose color is contained in the RGB cube which
106% this node represents;
107%
108% n2: Number of pixels whose color is not represented in a node at
109% lower depth in the tree; initially, n2 = 0 for all nodes except
110% leaves of the tree.
111%
112% Sr, Sg, Sb: Sums of the red, green, and blue component values for all
113% pixels not classified at a lower depth. The combination of these sums
114% and n2 will ultimately characterize the mean color of a set of
115% pixels represented by this node.
116%
117% E: the distance squared in RGB space between each pixel contained
118% within a node and the nodes' center. This represents the
119% quantization error for a node.
120%
121% Reduction repeatedly prunes the tree until the number of nodes with n2
122% > 0 is less than or equal to the maximum number of colors allowed in
123% the output image. On any given iteration over the tree, it selects
124% those nodes whose E count is minimal for pruning and merges their color
125% statistics upward. It uses a pruning threshold, Ep, to govern node
126% selection as follows:
127%
128% Ep = 0
129% while number of nodes with (n2 > 0) > required maximum number of colors
130% prune all nodes such that E <= Ep
131% Set Ep to minimum E in remaining nodes
132%
133% This has the effect of minimizing any quantization error when merging
134% two nodes together.
135%
136% When a node to be pruned has offspring, the pruning procedure invokes
137% itself recursively in order to prune the tree from the leaves upward.
138% n2, Sr, Sg, and Sb in a node being pruned are always added to the
139% corresponding data in that node's parent. This retains the pruned
140% node's color characteristics for later averaging.
141%
142% For each node, n2 pixels exist for which that node represents the
143% smallest volume in RGB space containing those pixel's colors. When n2
144% > 0 the node will uniquely define a color in the output image. At the
145% beginning of reduction, n2 = 0 for all nodes except a the leaves of
146% the tree which represent colors present in the input image.
147%
148% The other pixel count, n1, indicates the total number of colors within
149% the cubic volume which the node represents. This includes n1 - n2
150% pixels whose colors should be defined by nodes at a lower level in the
151% tree.
152%
153% Assignment generates the output image from the pruned tree. The output
154% image consists of two parts: (1) A color map, which is an array of
155% color descriptions (RGB triples) for each color present in the output
156% image; (2) A pixel array, which represents each pixel as an index
157% into the color map array.
158%
159% First, the assignment phase makes one pass over the pruned color
160% description tree to establish the image's color map. For each node
161% with n2 > 0, it divides Sr, Sg, and Sb by n2 . This produces the mean
162% color of all pixels that classify no lower than this node. Each of
163% these colors becomes an entry in the color map.
164%
165% Finally, the assignment phase reclassifies each pixel in the pruned
166% tree to identify the deepest node containing the pixel's color. The
167% pixel's value in the pixel array becomes the index of this node's mean
168% color in the color map.
169%
170% This method is based on a similar algorithm written by Paul Raveling.
171%
172*/
173
174/*
175 Include declarations.
176*/
177#include "magick/studio.h"
178#include "magick/cache-view.h"
179#include "magick/color.h"
180#include "magick/color-private.h"
cristye7e40552010-04-24 21:34:22 +0000181#include "magick/colormap.h"
cristy3ed852e2009-09-05 21:47:34 +0000182#include "magick/colorspace.h"
183#include "magick/enhance.h"
184#include "magick/exception.h"
185#include "magick/exception-private.h"
cristyf2e11662009-10-14 01:24:43 +0000186#include "magick/histogram.h"
cristy3ed852e2009-09-05 21:47:34 +0000187#include "magick/image.h"
188#include "magick/image-private.h"
189#include "magick/list.h"
190#include "magick/memory_.h"
191#include "magick/monitor.h"
192#include "magick/monitor-private.h"
193#include "magick/option.h"
194#include "magick/pixel-private.h"
195#include "magick/quantize.h"
196#include "magick/quantum.h"
197#include "magick/string_.h"
198
199/*
200 Define declarations.
201*/
202#define CacheShift 2
203#define ErrorQueueLength 16
204#define MaxNodes 266817
205#define MaxTreeDepth 8
206#define NodesInAList 1920
207
208/*
209 Typdef declarations.
210*/
211typedef struct _RealPixelPacket
212{
213 MagickRealType
214 red,
215 green,
216 blue,
217 opacity;
218} RealPixelPacket;
219
220typedef struct _NodeInfo
221{
222 struct _NodeInfo
223 *parent,
224 *child[16];
225
226 MagickSizeType
227 number_unique;
228
229 RealPixelPacket
230 total_color;
231
232 MagickRealType
233 quantize_error;
234
cristybb503372010-05-27 20:51:26 +0000235 size_t
cristy3ed852e2009-09-05 21:47:34 +0000236 color_number,
237 id,
238 level;
239} NodeInfo;
240
241typedef struct _Nodes
242{
243 NodeInfo
244 *nodes;
245
246 struct _Nodes
247 *next;
248} Nodes;
249
250typedef struct _CubeInfo
251{
252 NodeInfo
253 *root;
254
cristybb503372010-05-27 20:51:26 +0000255 size_t
cristy3ed852e2009-09-05 21:47:34 +0000256 colors,
257 maximum_colors;
258
cristybb503372010-05-27 20:51:26 +0000259 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000260 transparent_index;
261
262 MagickSizeType
263 transparent_pixels;
264
265 RealPixelPacket
266 target;
267
268 MagickRealType
269 distance,
270 pruning_threshold,
271 next_threshold;
272
cristybb503372010-05-27 20:51:26 +0000273 size_t
cristy3ed852e2009-09-05 21:47:34 +0000274 nodes,
275 free_nodes,
276 color_number;
277
278 NodeInfo
279 *next_node;
280
281 Nodes
282 *node_queue;
283
cristybb503372010-05-27 20:51:26 +0000284 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000285 *cache;
286
287 RealPixelPacket
288 error[ErrorQueueLength];
289
290 MagickRealType
291 weights[ErrorQueueLength];
292
293 QuantizeInfo
294 *quantize_info;
295
296 MagickBooleanType
297 associate_alpha;
298
cristybb503372010-05-27 20:51:26 +0000299 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000300 x,
301 y;
302
cristybb503372010-05-27 20:51:26 +0000303 size_t
cristy3ed852e2009-09-05 21:47:34 +0000304 depth;
305
306 MagickOffsetType
307 offset;
308
309 MagickSizeType
310 span;
311} CubeInfo;
312
313/*
314 Method prototypes.
315*/
316static CubeInfo
cristybb503372010-05-27 20:51:26 +0000317 *GetCubeInfo(const QuantizeInfo *,const size_t,const size_t);
cristy3ed852e2009-09-05 21:47:34 +0000318
319static NodeInfo
cristybb503372010-05-27 20:51:26 +0000320 *GetNodeInfo(CubeInfo *,const size_t,const size_t,NodeInfo *);
cristy3ed852e2009-09-05 21:47:34 +0000321
322static MagickBooleanType
323 AssignImageColors(Image *,CubeInfo *),
324 ClassifyImageColors(CubeInfo *,const Image *,ExceptionInfo *),
325 DitherImage(Image *,CubeInfo *),
326 SetGrayscaleImage(Image *);
327
cristybb503372010-05-27 20:51:26 +0000328static size_t
cristy3ed852e2009-09-05 21:47:34 +0000329 DefineImageColormap(Image *,CubeInfo *,NodeInfo *);
330
331static void
332 ClosestColor(const Image *,CubeInfo *,const NodeInfo *),
333 DestroyCubeInfo(CubeInfo *),
334 PruneLevel(const Image *,CubeInfo *,const NodeInfo *),
335 PruneToCubeDepth(const Image *,CubeInfo *,const NodeInfo *),
336 ReduceImageColors(const Image *,CubeInfo *);
337
338/*
339%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
340% %
341% %
342% %
343% A c q u i r e Q u a n t i z e I n f o %
344% %
345% %
346% %
347%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
348%
349% AcquireQuantizeInfo() allocates the QuantizeInfo structure.
350%
351% The format of the AcquireQuantizeInfo method is:
352%
353% QuantizeInfo *AcquireQuantizeInfo(const ImageInfo *image_info)
354%
355% A description of each parameter follows:
356%
357% o image_info: the image info.
358%
359*/
360MagickExport QuantizeInfo *AcquireQuantizeInfo(const ImageInfo *image_info)
361{
362 QuantizeInfo
363 *quantize_info;
364
cristy90823212009-12-12 20:48:33 +0000365 quantize_info=(QuantizeInfo *) AcquireAlignedMemory(1,sizeof(*quantize_info));
cristy3ed852e2009-09-05 21:47:34 +0000366 if (quantize_info == (QuantizeInfo *) NULL)
367 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
368 GetQuantizeInfo(quantize_info);
369 if (image_info != (ImageInfo *) NULL)
370 {
371 const char
372 *option;
373
374 quantize_info->dither=image_info->dither;
375 option=GetImageOption(image_info,"dither");
376 if (option != (const char *) NULL)
377 quantize_info->dither_method=(DitherMethod) ParseMagickOption(
378 MagickDitherOptions,MagickFalse,option);
379 quantize_info->measure_error=image_info->verbose;
380 }
381 return(quantize_info);
382}
383
384/*
385%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
386% %
387% %
388% %
389+ A s s i g n I m a g e C o l o r s %
390% %
391% %
392% %
393%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
394%
395% AssignImageColors() generates the output image from the pruned tree. The
396% output image consists of two parts: (1) A color map, which is an array
397% of color descriptions (RGB triples) for each color present in the
398% output image; (2) A pixel array, which represents each pixel as an
399% index into the color map array.
400%
401% First, the assignment phase makes one pass over the pruned color
402% description tree to establish the image's color map. For each node
403% with n2 > 0, it divides Sr, Sg, and Sb by n2 . This produces the mean
404% color of all pixels that classify no lower than this node. Each of
405% these colors becomes an entry in the color map.
406%
407% Finally, the assignment phase reclassifies each pixel in the pruned
408% tree to identify the deepest node containing the pixel's color. The
409% pixel's value in the pixel array becomes the index of this node's mean
410% color in the color map.
411%
412% The format of the AssignImageColors() method is:
413%
414% MagickBooleanType AssignImageColors(Image *image,CubeInfo *cube_info)
415%
416% A description of each parameter follows.
417%
418% o image: the image.
419%
420% o cube_info: A pointer to the Cube structure.
421%
422*/
423
424static inline void AssociateAlphaPixel(const CubeInfo *cube_info,
425 const PixelPacket *pixel,RealPixelPacket *alpha_pixel)
426{
427 MagickRealType
428 alpha;
429
430 if ((cube_info->associate_alpha == MagickFalse) ||
431 (pixel->opacity == OpaqueOpacity))
432 {
433 alpha_pixel->red=(MagickRealType) pixel->red;
434 alpha_pixel->green=(MagickRealType) pixel->green;
435 alpha_pixel->blue=(MagickRealType) pixel->blue;
436 alpha_pixel->opacity=(MagickRealType) pixel->opacity;
437 return;
438 }
439 alpha=(MagickRealType) (QuantumScale*(QuantumRange-pixel->opacity));
440 alpha_pixel->red=alpha*pixel->red;
441 alpha_pixel->green=alpha*pixel->green;
442 alpha_pixel->blue=alpha*pixel->blue;
443 alpha_pixel->opacity=(MagickRealType) pixel->opacity;
444}
445
cristy75ffdb72010-01-07 17:40:12 +0000446static inline Quantum ClampToUnsignedQuantum(const MagickRealType value)
cristy3ed852e2009-09-05 21:47:34 +0000447{
448 if (value <= 0.0)
449 return((Quantum) 0);
450 if (value >= QuantumRange)
451 return((Quantum) QuantumRange);
452 return((Quantum) (value+0.5));
453}
454
cristybb503372010-05-27 20:51:26 +0000455static inline size_t ColorToNodeId(const CubeInfo *cube_info,
456 const RealPixelPacket *pixel,size_t index)
cristy3ed852e2009-09-05 21:47:34 +0000457{
cristybb503372010-05-27 20:51:26 +0000458 size_t
cristy3ed852e2009-09-05 21:47:34 +0000459 id;
460
cristybb503372010-05-27 20:51:26 +0000461 id=(size_t) (
cristy75ffdb72010-01-07 17:40:12 +0000462 ((ScaleQuantumToChar(ClampToUnsignedQuantum(pixel->red)) >> index) & 0x1) |
463 ((ScaleQuantumToChar(ClampToUnsignedQuantum(pixel->green)) >> index) & 0x1) << 1 |
464 ((ScaleQuantumToChar(ClampToUnsignedQuantum(pixel->blue)) >> index) & 0x1) << 2);
cristy3ed852e2009-09-05 21:47:34 +0000465 if (cube_info->associate_alpha != MagickFalse)
cristy75ffdb72010-01-07 17:40:12 +0000466 id|=((ScaleQuantumToChar(ClampToUnsignedQuantum(pixel->opacity)) >> index) & 0x1)
cristy3ed852e2009-09-05 21:47:34 +0000467 << 3;
468 return(id);
469}
470
471static inline MagickBooleanType IsSameColor(const Image *image,
472 const PixelPacket *p,const PixelPacket *q)
473{
474 if ((p->red != q->red) || (p->green != q->green) || (p->blue != q->blue))
475 return(MagickFalse);
476 if ((image->matte != MagickFalse) && (p->opacity != q->opacity))
477 return(MagickFalse);
478 return(MagickTrue);
479}
480
481static MagickBooleanType AssignImageColors(Image *image,CubeInfo *cube_info)
482{
483#define AssignImageTag "Assign/Image"
484
cristybb503372010-05-27 20:51:26 +0000485 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000486 y;
487
488 MagickBooleanType
489 proceed;
490
491 RealPixelPacket
492 pixel;
493
cristybb503372010-05-27 20:51:26 +0000494 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000495 i,
496 x;
497
498 register const NodeInfo
499 *node_info;
500
501 ssize_t
502 count;
503
cristybb503372010-05-27 20:51:26 +0000504 size_t
cristy3ed852e2009-09-05 21:47:34 +0000505 id,
506 index;
507
508 /*
509 Allocate image colormap.
510 */
511 if ((cube_info->quantize_info->colorspace != UndefinedColorspace) &&
512 (cube_info->quantize_info->colorspace != CMYKColorspace))
513 (void) TransformImageColorspace((Image *) image,
514 cube_info->quantize_info->colorspace);
515 else
516 if ((image->colorspace != GRAYColorspace) &&
517 (image->colorspace != RGBColorspace) &&
518 (image->colorspace != CMYColorspace))
519 (void) TransformImageColorspace((Image *) image,RGBColorspace);
520 if (AcquireImageColormap(image,cube_info->colors) == MagickFalse)
521 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
522 image->filename);
523 image->colors=0;
524 cube_info->transparent_pixels=0;
525 cube_info->transparent_index=(-1);
526 (void) DefineImageColormap(image,cube_info,cube_info->root);
527 /*
528 Create a reduced color image.
529 */
530 if ((cube_info->quantize_info->dither != MagickFalse) &&
cristyfd117592010-06-14 23:40:02 +0000531 (cube_info->quantize_info->dither_method != NoDitherMethod) &&
532 (cube_info->quantize_info->dither_method != UndefinedDitherMethod))
cristy3ed852e2009-09-05 21:47:34 +0000533 (void) DitherImage(image,cube_info);
534 else
535 {
536 ExceptionInfo
537 *exception;
538
539 CacheView
540 *image_view;
541
542 exception=(&image->exception);
543 image_view=AcquireCacheView(image);
cristybb503372010-05-27 20:51:26 +0000544 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000545 {
546 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000547 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +0000548
549 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000550 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000551
552 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
553 exception);
554 if (q == (PixelPacket *) NULL)
555 break;
556 indexes=GetCacheViewAuthenticIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +0000557 for (x=0; x < (ssize_t) image->columns; x+=count)
cristy3ed852e2009-09-05 21:47:34 +0000558 {
559 /*
560 Identify the deepest node containing the pixel's color.
561 */
cristybb503372010-05-27 20:51:26 +0000562 for (count=1; (x+count) < (ssize_t) image->columns; count++)
cristy3ed852e2009-09-05 21:47:34 +0000563 if (IsSameColor(image,q,q+count) == MagickFalse)
564 break;
565 AssociateAlphaPixel(cube_info,q,&pixel);
566 node_info=cube_info->root;
cristybb503372010-05-27 20:51:26 +0000567 for (index=MaxTreeDepth-1; (ssize_t) index > 0; index--)
cristy3ed852e2009-09-05 21:47:34 +0000568 {
569 id=ColorToNodeId(cube_info,&pixel,index);
570 if (node_info->child[id] == (NodeInfo *) NULL)
571 break;
572 node_info=node_info->child[id];
573 }
574 /*
575 Find closest color among siblings and their children.
576 */
577 cube_info->target=pixel;
578 cube_info->distance=(MagickRealType) (4.0*(QuantumRange+1.0)*
579 (QuantumRange+1.0)+1.0);
580 ClosestColor(image,cube_info,node_info->parent);
581 index=cube_info->color_number;
cristybb503372010-05-27 20:51:26 +0000582 for (i=0; i < (ssize_t) count; i++)
cristy3ed852e2009-09-05 21:47:34 +0000583 {
584 if (image->storage_class == PseudoClass)
585 indexes[x+i]=(IndexPacket) index;
586 if (cube_info->quantize_info->measure_error == MagickFalse)
587 {
588 q->red=image->colormap[index].red;
589 q->green=image->colormap[index].green;
590 q->blue=image->colormap[index].blue;
591 if (cube_info->associate_alpha != MagickFalse)
592 q->opacity=image->colormap[index].opacity;
593 }
594 q++;
595 }
596 }
597 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
598 break;
cristycee97112010-05-28 00:44:52 +0000599 proceed=SetImageProgress(image,AssignImageTag,(MagickOffsetType) y,
600 image->rows);
cristy3ed852e2009-09-05 21:47:34 +0000601 if (proceed == MagickFalse)
602 break;
603 }
604 image_view=DestroyCacheView(image_view);
605 }
606 if (cube_info->quantize_info->measure_error != MagickFalse)
607 (void) GetImageQuantizeError(image);
608 if ((cube_info->quantize_info->number_colors == 2) &&
609 (cube_info->quantize_info->colorspace == GRAYColorspace))
610 {
611 Quantum
612 intensity;
613
614 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000615 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000616
617 /*
618 Monochrome image.
619 */
620 q=image->colormap;
cristybb503372010-05-27 20:51:26 +0000621 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +0000622 {
623 intensity=(Quantum) (PixelIntensity(q) < ((MagickRealType)
624 QuantumRange/2.0) ? 0 : QuantumRange);
625 q->red=intensity;
626 q->green=intensity;
627 q->blue=intensity;
628 q++;
629 }
630 }
631 (void) SyncImage(image);
632 if ((cube_info->quantize_info->colorspace != UndefinedColorspace) &&
633 (cube_info->quantize_info->colorspace != CMYKColorspace))
634 (void) TransformImageColorspace((Image *) image,RGBColorspace);
635 return(MagickTrue);
636}
637
638/*
639%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
640% %
641% %
642% %
643+ C l a s s i f y I m a g e C o l o r s %
644% %
645% %
646% %
647%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
648%
649% ClassifyImageColors() begins by initializing a color description tree
650% of sufficient depth to represent each possible input color in a leaf.
651% However, it is impractical to generate a fully-formed color
652% description tree in the storage_class phase for realistic values of
653% Cmax. If colors components in the input image are quantized to k-bit
654% precision, so that Cmax= 2k-1, the tree would need k levels below the
655% root node to allow representing each possible input color in a leaf.
656% This becomes prohibitive because the tree's total number of nodes is
657% 1 + sum(i=1,k,8k).
658%
659% A complete tree would require 19,173,961 nodes for k = 8, Cmax = 255.
660% Therefore, to avoid building a fully populated tree, QUANTIZE: (1)
661% Initializes data structures for nodes only as they are needed; (2)
662% Chooses a maximum depth for the tree as a function of the desired
663% number of colors in the output image (currently log2(colormap size)).
664%
665% For each pixel in the input image, storage_class scans downward from
666% the root of the color description tree. At each level of the tree it
667% identifies the single node which represents a cube in RGB space
668% containing It updates the following data for each such node:
669%
670% n1 : Number of pixels whose color is contained in the RGB cube
671% which this node represents;
672%
673% n2 : Number of pixels whose color is not represented in a node at
674% lower depth in the tree; initially, n2 = 0 for all nodes except
675% leaves of the tree.
676%
677% Sr, Sg, Sb : Sums of the red, green, and blue component values for
678% all pixels not classified at a lower depth. The combination of
679% these sums and n2 will ultimately characterize the mean color of a
680% set of pixels represented by this node.
681%
682% E: the distance squared in RGB space between each pixel contained
683% within a node and the nodes' center. This represents the quantization
684% error for a node.
685%
686% The format of the ClassifyImageColors() method is:
687%
688% MagickBooleanType ClassifyImageColors(CubeInfo *cube_info,
689% const Image *image,ExceptionInfo *exception)
690%
691% A description of each parameter follows.
692%
693% o cube_info: A pointer to the Cube structure.
694%
695% o image: the image.
696%
697*/
698
699static inline void SetAssociatedAlpha(const Image *image,CubeInfo *cube_info)
700{
701 MagickBooleanType
702 associate_alpha;
703
704 associate_alpha=image->matte;
705 if (cube_info->quantize_info->colorspace == TransparentColorspace)
706 associate_alpha=MagickFalse;
707 if ((cube_info->quantize_info->number_colors == 2) &&
708 (cube_info->quantize_info->colorspace == GRAYColorspace))
709 associate_alpha=MagickFalse;
710 cube_info->associate_alpha=associate_alpha;
711}
712
713static MagickBooleanType ClassifyImageColors(CubeInfo *cube_info,
714 const Image *image,ExceptionInfo *exception)
715{
716#define ClassifyImageTag "Classify/Image"
717
cristyc4c8d132010-01-07 01:58:38 +0000718 CacheView
719 *image_view;
720
cristybb503372010-05-27 20:51:26 +0000721 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000722 y;
723
724 MagickBooleanType
725 proceed;
726
727 MagickRealType
728 bisect;
729
730 NodeInfo
731 *node_info;
732
733 RealPixelPacket
734 error,
735 mid,
736 midpoint,
737 pixel;
738
739 size_t
740 count;
741
cristybb503372010-05-27 20:51:26 +0000742 size_t
cristy3ed852e2009-09-05 21:47:34 +0000743 id,
744 index,
745 level;
746
cristy3ed852e2009-09-05 21:47:34 +0000747 /*
748 Classify the first cube_info->maximum_colors colors to a tree depth of 8.
749 */
750 SetAssociatedAlpha(image,cube_info);
751 if ((cube_info->quantize_info->colorspace != UndefinedColorspace) &&
752 (cube_info->quantize_info->colorspace != CMYKColorspace))
753 (void) TransformImageColorspace((Image *) image,
754 cube_info->quantize_info->colorspace);
755 else
756 if ((image->colorspace != GRAYColorspace) &&
757 (image->colorspace != CMYColorspace) &&
758 (image->colorspace != RGBColorspace))
759 (void) TransformImageColorspace((Image *) image,RGBColorspace);
760 midpoint.red=(MagickRealType) QuantumRange/2.0;
761 midpoint.green=(MagickRealType) QuantumRange/2.0;
762 midpoint.blue=(MagickRealType) QuantumRange/2.0;
763 midpoint.opacity=(MagickRealType) QuantumRange/2.0;
764 error.opacity=0.0;
765 image_view=AcquireCacheView(image);
cristybb503372010-05-27 20:51:26 +0000766 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000767 {
768 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000769 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000770
cristybb503372010-05-27 20:51:26 +0000771 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000772 x;
773
774 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
775 if (p == (const PixelPacket *) NULL)
776 break;
777 if (cube_info->nodes > MaxNodes)
778 {
779 /*
780 Prune one level if the color tree is too large.
781 */
782 PruneLevel(image,cube_info,cube_info->root);
783 cube_info->depth--;
784 }
cristybb503372010-05-27 20:51:26 +0000785 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) count)
cristy3ed852e2009-09-05 21:47:34 +0000786 {
787 /*
788 Start at the root and descend the color cube tree.
789 */
cristyecd0ab52010-05-30 14:59:20 +0000790 for (count=1; (x+count) < (ssize_t) image->columns; count++)
cristy3ed852e2009-09-05 21:47:34 +0000791 if (IsSameColor(image,p,p+count) == MagickFalse)
792 break;
793 AssociateAlphaPixel(cube_info,p,&pixel);
794 index=MaxTreeDepth-1;
795 bisect=((MagickRealType) QuantumRange+1.0)/2.0;
796 mid=midpoint;
797 node_info=cube_info->root;
798 for (level=1; level <= MaxTreeDepth; level++)
799 {
800 bisect*=0.5;
801 id=ColorToNodeId(cube_info,&pixel,index);
802 mid.red+=(id & 1) != 0 ? bisect : -bisect;
803 mid.green+=(id & 2) != 0 ? bisect : -bisect;
804 mid.blue+=(id & 4) != 0 ? bisect : -bisect;
805 mid.opacity+=(id & 8) != 0 ? bisect : -bisect;
806 if (node_info->child[id] == (NodeInfo *) NULL)
807 {
808 /*
809 Set colors of new node to contain pixel.
810 */
811 node_info->child[id]=GetNodeInfo(cube_info,id,level,node_info);
812 if (node_info->child[id] == (NodeInfo *) NULL)
813 (void) ThrowMagickException(exception,GetMagickModule(),
814 ResourceLimitError,"MemoryAllocationFailed","`%s'",
815 image->filename);
816 if (level == MaxTreeDepth)
817 cube_info->colors++;
818 }
819 /*
820 Approximate the quantization error represented by this node.
821 */
822 node_info=node_info->child[id];
823 error.red=QuantumScale*(pixel.red-mid.red);
824 error.green=QuantumScale*(pixel.green-mid.green);
825 error.blue=QuantumScale*(pixel.blue-mid.blue);
826 if (cube_info->associate_alpha != MagickFalse)
827 error.opacity=QuantumScale*(pixel.opacity-mid.opacity);
828 node_info->quantize_error+=sqrt((double) (count*error.red*error.red+
829 count*error.green*error.green+count*error.blue*error.blue+
830 count*error.opacity*error.opacity));
831 cube_info->root->quantize_error+=node_info->quantize_error;
832 index--;
833 }
834 /*
835 Sum RGB for this leaf for later derivation of the mean cube color.
836 */
837 node_info->number_unique+=count;
838 node_info->total_color.red+=count*QuantumScale*pixel.red;
839 node_info->total_color.green+=count*QuantumScale*pixel.green;
840 node_info->total_color.blue+=count*QuantumScale*pixel.blue;
841 if (cube_info->associate_alpha != MagickFalse)
842 node_info->total_color.opacity+=count*QuantumScale*pixel.opacity;
843 p+=count;
844 }
845 if (cube_info->colors > cube_info->maximum_colors)
846 {
847 PruneToCubeDepth(image,cube_info,cube_info->root);
848 break;
849 }
cristycee97112010-05-28 00:44:52 +0000850 proceed=SetImageProgress(image,ClassifyImageTag,(MagickOffsetType) y,
851 image->rows);
cristy3ed852e2009-09-05 21:47:34 +0000852 if (proceed == MagickFalse)
853 break;
854 }
cristybb503372010-05-27 20:51:26 +0000855 for (y++; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000856 {
857 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000858 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000859
cristybb503372010-05-27 20:51:26 +0000860 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000861 x;
862
863 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
864 if (p == (const PixelPacket *) NULL)
865 break;
866 if (cube_info->nodes > MaxNodes)
867 {
868 /*
869 Prune one level if the color tree is too large.
870 */
871 PruneLevel(image,cube_info,cube_info->root);
872 cube_info->depth--;
873 }
cristybb503372010-05-27 20:51:26 +0000874 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) count)
cristy3ed852e2009-09-05 21:47:34 +0000875 {
876 /*
877 Start at the root and descend the color cube tree.
878 */
cristyecd0ab52010-05-30 14:59:20 +0000879 for (count=1; (x+count) < (ssize_t) image->columns; count++)
cristy3ed852e2009-09-05 21:47:34 +0000880 if (IsSameColor(image,p,p+count) == MagickFalse)
881 break;
882 AssociateAlphaPixel(cube_info,p,&pixel);
883 index=MaxTreeDepth-1;
884 bisect=((MagickRealType) QuantumRange+1.0)/2.0;
885 mid=midpoint;
886 node_info=cube_info->root;
887 for (level=1; level <= cube_info->depth; level++)
888 {
889 bisect*=0.5;
890 id=ColorToNodeId(cube_info,&pixel,index);
891 mid.red+=(id & 1) != 0 ? bisect : -bisect;
892 mid.green+=(id & 2) != 0 ? bisect : -bisect;
893 mid.blue+=(id & 4) != 0 ? bisect : -bisect;
894 mid.opacity+=(id & 8) != 0 ? bisect : -bisect;
895 if (node_info->child[id] == (NodeInfo *) NULL)
896 {
897 /*
898 Set colors of new node to contain pixel.
899 */
900 node_info->child[id]=GetNodeInfo(cube_info,id,level,node_info);
901 if (node_info->child[id] == (NodeInfo *) NULL)
902 (void) ThrowMagickException(exception,GetMagickModule(),
903 ResourceLimitError,"MemoryAllocationFailed","%s",
904 image->filename);
905 if (level == cube_info->depth)
906 cube_info->colors++;
907 }
908 /*
909 Approximate the quantization error represented by this node.
910 */
911 node_info=node_info->child[id];
912 error.red=QuantumScale*(pixel.red-mid.red);
913 error.green=QuantumScale*(pixel.green-mid.green);
914 error.blue=QuantumScale*(pixel.blue-mid.blue);
915 if (cube_info->associate_alpha != MagickFalse)
916 error.opacity=QuantumScale*(pixel.opacity-mid.opacity);
917 node_info->quantize_error+=sqrt((double) (count*error.red*error.red+
918 count*error.green*error.green+error.blue*error.blue+
919 count*error.opacity*error.opacity));
920 cube_info->root->quantize_error+=node_info->quantize_error;
921 index--;
922 }
923 /*
924 Sum RGB for this leaf for later derivation of the mean cube color.
925 */
926 node_info->number_unique+=count;
927 node_info->total_color.red+=count*QuantumScale*pixel.red;
928 node_info->total_color.green+=count*QuantumScale*pixel.green;
929 node_info->total_color.blue+=count*QuantumScale*pixel.blue;
930 if (cube_info->associate_alpha != MagickFalse)
931 node_info->total_color.opacity+=count*QuantumScale*pixel.opacity;
932 p+=count;
933 }
cristycee97112010-05-28 00:44:52 +0000934 proceed=SetImageProgress(image,ClassifyImageTag,(MagickOffsetType) y,
935 image->rows);
cristy3ed852e2009-09-05 21:47:34 +0000936 if (proceed == MagickFalse)
937 break;
938 }
939 image_view=DestroyCacheView(image_view);
940 if ((cube_info->quantize_info->colorspace != UndefinedColorspace) &&
941 (cube_info->quantize_info->colorspace != CMYKColorspace))
942 (void) TransformImageColorspace((Image *) image,RGBColorspace);
943 return(MagickTrue);
944}
945
946/*
947%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
948% %
949% %
950% %
951% C l o n e Q u a n t i z e I n f o %
952% %
953% %
954% %
955%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
956%
957% CloneQuantizeInfo() makes a duplicate of the given quantize info structure,
958% or if quantize info is NULL, a new one.
959%
960% The format of the CloneQuantizeInfo method is:
961%
962% QuantizeInfo *CloneQuantizeInfo(const QuantizeInfo *quantize_info)
963%
964% A description of each parameter follows:
965%
966% o clone_info: Method CloneQuantizeInfo returns a duplicate of the given
967% quantize info, or if image info is NULL a new one.
968%
969% o quantize_info: a structure of type info.
970%
971*/
972MagickExport QuantizeInfo *CloneQuantizeInfo(const QuantizeInfo *quantize_info)
973{
974 QuantizeInfo
975 *clone_info;
976
cristy90823212009-12-12 20:48:33 +0000977 clone_info=(QuantizeInfo *) AcquireAlignedMemory(1,sizeof(*clone_info));
cristy3ed852e2009-09-05 21:47:34 +0000978 if (clone_info == (QuantizeInfo *) NULL)
979 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
980 GetQuantizeInfo(clone_info);
981 if (quantize_info == (QuantizeInfo *) NULL)
982 return(clone_info);
983 clone_info->number_colors=quantize_info->number_colors;
984 clone_info->tree_depth=quantize_info->tree_depth;
985 clone_info->dither=quantize_info->dither;
986 clone_info->dither_method=quantize_info->dither_method;
987 clone_info->colorspace=quantize_info->colorspace;
988 clone_info->measure_error=quantize_info->measure_error;
989 return(clone_info);
990}
991
992/*
993%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
994% %
995% %
996% %
997+ C l o s e s t C o l o r %
998% %
999% %
1000% %
1001%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1002%
1003% ClosestColor() traverses the color cube tree at a particular node and
1004% determines which colormap entry best represents the input color.
1005%
1006% The format of the ClosestColor method is:
1007%
1008% void ClosestColor(const Image *image,CubeInfo *cube_info,
1009% const NodeInfo *node_info)
1010%
1011% A description of each parameter follows.
1012%
1013% o image: the image.
1014%
1015% o cube_info: A pointer to the Cube structure.
1016%
1017% o node_info: the address of a structure of type NodeInfo which points to a
1018% node in the color cube tree that is to be pruned.
1019%
1020*/
1021static void ClosestColor(const Image *image,CubeInfo *cube_info,
1022 const NodeInfo *node_info)
1023{
cristybb503372010-05-27 20:51:26 +00001024 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001025 i;
1026
cristybb503372010-05-27 20:51:26 +00001027 size_t
cristy3ed852e2009-09-05 21:47:34 +00001028 number_children;
1029
1030 /*
1031 Traverse any children.
1032 */
1033 number_children=cube_info->associate_alpha == MagickFalse ? 8UL : 16UL;
cristybb503372010-05-27 20:51:26 +00001034 for (i=0; i < (ssize_t) number_children; i++)
cristy3ed852e2009-09-05 21:47:34 +00001035 if (node_info->child[i] != (NodeInfo *) NULL)
1036 ClosestColor(image,cube_info,node_info->child[i]);
1037 if (node_info->number_unique != 0)
1038 {
1039 MagickRealType
1040 pixel;
1041
1042 register MagickRealType
1043 alpha,
1044 beta,
1045 distance;
1046
1047 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001048 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001049
1050 register RealPixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001051 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001052
1053 /*
1054 Determine if this color is "closest".
1055 */
1056 p=image->colormap+node_info->color_number;
1057 q=(&cube_info->target);
1058 alpha=1.0;
1059 beta=1.0;
1060 if (cube_info->associate_alpha == MagickFalse)
1061 {
cristy46f08202010-01-10 04:04:21 +00001062 alpha=(MagickRealType) (QuantumScale*GetAlphaPixelComponent(p));
1063 beta=(MagickRealType) (QuantumScale*GetAlphaPixelComponent(q));
cristy3ed852e2009-09-05 21:47:34 +00001064 }
1065 pixel=alpha*p->red-beta*q->red;
1066 distance=pixel*pixel;
1067 if (distance < cube_info->distance)
1068 {
1069 pixel=alpha*p->green-beta*q->green;
1070 distance+=pixel*pixel;
1071 if (distance < cube_info->distance)
1072 {
1073 pixel=alpha*p->blue-beta*q->blue;
1074 distance+=pixel*pixel;
1075 if (distance < cube_info->distance)
1076 {
1077 pixel=alpha-beta;
1078 distance+=pixel*pixel;
1079 if (distance < cube_info->distance)
1080 {
1081 cube_info->distance=distance;
1082 cube_info->color_number=node_info->color_number;
1083 }
1084 }
1085 }
1086 }
1087 }
1088}
1089
1090/*
1091%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1092% %
1093% %
1094% %
1095% C o m p r e s s I m a g e C o l o r m a p %
1096% %
1097% %
1098% %
1099%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1100%
1101% CompressImageColormap() compresses an image colormap by removing any
1102% duplicate or unused color entries.
1103%
1104% The format of the CompressImageColormap method is:
1105%
1106% MagickBooleanType CompressImageColormap(Image *image)
1107%
1108% A description of each parameter follows:
1109%
1110% o image: the image.
1111%
1112*/
1113MagickExport MagickBooleanType CompressImageColormap(Image *image)
1114{
1115 QuantizeInfo
1116 quantize_info;
1117
1118 assert(image != (Image *) NULL);
1119 assert(image->signature == MagickSignature);
1120 if (image->debug != MagickFalse)
1121 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1122 if (IsPaletteImage(image,&image->exception) == MagickFalse)
1123 return(MagickFalse);
1124 GetQuantizeInfo(&quantize_info);
1125 quantize_info.number_colors=image->colors;
1126 quantize_info.tree_depth=MaxTreeDepth;
1127 return(QuantizeImage(&quantize_info,image));
1128}
1129
1130/*
1131%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1132% %
1133% %
1134% %
1135+ D e f i n e I m a g e C o l o r m a p %
1136% %
1137% %
1138% %
1139%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1140%
1141% DefineImageColormap() traverses the color cube tree and notes each colormap
1142% entry. A colormap entry is any node in the color cube tree where the
1143% of unique colors is not zero. DefineImageColormap() returns the number of
1144% colors in the image colormap.
1145%
1146% The format of the DefineImageColormap method is:
1147%
cristybb503372010-05-27 20:51:26 +00001148% size_t DefineImageColormap(Image *image,CubeInfo *cube_info,
cristy3ed852e2009-09-05 21:47:34 +00001149% NodeInfo *node_info)
1150%
1151% A description of each parameter follows.
1152%
1153% o image: the image.
1154%
1155% o cube_info: A pointer to the Cube structure.
1156%
1157% o node_info: the address of a structure of type NodeInfo which points to a
1158% node in the color cube tree that is to be pruned.
1159%
1160*/
cristybb503372010-05-27 20:51:26 +00001161static size_t DefineImageColormap(Image *image,CubeInfo *cube_info,
cristy3ed852e2009-09-05 21:47:34 +00001162 NodeInfo *node_info)
1163{
cristybb503372010-05-27 20:51:26 +00001164 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001165 i;
1166
cristybb503372010-05-27 20:51:26 +00001167 size_t
cristy3ed852e2009-09-05 21:47:34 +00001168 number_children;
1169
1170 /*
1171 Traverse any children.
1172 */
1173 number_children=cube_info->associate_alpha == MagickFalse ? 8UL : 16UL;
cristybb503372010-05-27 20:51:26 +00001174 for (i=0; i < (ssize_t) number_children; i++)
cristy3ed852e2009-09-05 21:47:34 +00001175 if (node_info->child[i] != (NodeInfo *) NULL)
cristycee97112010-05-28 00:44:52 +00001176 (void) DefineImageColormap(image,cube_info,node_info->child[i]);
cristy3ed852e2009-09-05 21:47:34 +00001177 if (node_info->number_unique != 0)
1178 {
1179 register MagickRealType
1180 alpha;
1181
1182 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001183 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001184
1185 /*
1186 Colormap entry is defined by the mean color in this cube.
1187 */
1188 q=image->colormap+image->colors;
1189 alpha=(MagickRealType) ((MagickOffsetType) node_info->number_unique);
1190 alpha=1.0/(fabs(alpha) <= MagickEpsilon ? 1.0 : alpha);
1191 if (cube_info->associate_alpha == MagickFalse)
1192 {
cristyce70c172010-01-07 17:15:30 +00001193 q->red=ClampToQuantum((MagickRealType) (alpha*QuantumRange*
cristy3ed852e2009-09-05 21:47:34 +00001194 node_info->total_color.red));
cristyce70c172010-01-07 17:15:30 +00001195 q->green=ClampToQuantum((MagickRealType) (alpha*QuantumRange*
cristy3ed852e2009-09-05 21:47:34 +00001196 node_info->total_color.green));
cristyce70c172010-01-07 17:15:30 +00001197 q->blue=ClampToQuantum((MagickRealType) (alpha*QuantumRange*
cristy3ed852e2009-09-05 21:47:34 +00001198 node_info->total_color.blue));
cristyce70c172010-01-07 17:15:30 +00001199 SetOpacityPixelComponent(q,OpaqueOpacity);
cristy3ed852e2009-09-05 21:47:34 +00001200 }
1201 else
1202 {
1203 MagickRealType
1204 opacity;
1205
1206 opacity=(MagickRealType) (alpha*QuantumRange*
1207 node_info->total_color.opacity);
cristyce70c172010-01-07 17:15:30 +00001208 q->opacity=ClampToQuantum(opacity);
cristy3ed852e2009-09-05 21:47:34 +00001209 if (q->opacity == OpaqueOpacity)
1210 {
cristyce70c172010-01-07 17:15:30 +00001211 q->red=ClampToQuantum((MagickRealType) (alpha*QuantumRange*
cristy3ed852e2009-09-05 21:47:34 +00001212 node_info->total_color.red));
cristyce70c172010-01-07 17:15:30 +00001213 q->green=ClampToQuantum((MagickRealType) (alpha*QuantumRange*
cristy3ed852e2009-09-05 21:47:34 +00001214 node_info->total_color.green));
cristyce70c172010-01-07 17:15:30 +00001215 q->blue=ClampToQuantum((MagickRealType) (alpha*QuantumRange*
cristy3ed852e2009-09-05 21:47:34 +00001216 node_info->total_color.blue));
1217 }
1218 else
1219 {
1220 MagickRealType
1221 gamma;
1222
1223 gamma=(MagickRealType) (QuantumScale*(QuantumRange-
1224 (MagickRealType) q->opacity));
1225 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyce70c172010-01-07 17:15:30 +00001226 q->red=ClampToQuantum((MagickRealType) (alpha*gamma*QuantumRange*
cristy3ed852e2009-09-05 21:47:34 +00001227 node_info->total_color.red));
cristyce70c172010-01-07 17:15:30 +00001228 q->green=ClampToQuantum((MagickRealType) (alpha*gamma*
cristy3ed852e2009-09-05 21:47:34 +00001229 QuantumRange*node_info->total_color.green));
cristyce70c172010-01-07 17:15:30 +00001230 q->blue=ClampToQuantum((MagickRealType) (alpha*gamma*QuantumRange*
cristy3ed852e2009-09-05 21:47:34 +00001231 node_info->total_color.blue));
1232 if (node_info->number_unique > cube_info->transparent_pixels)
1233 {
1234 cube_info->transparent_pixels=node_info->number_unique;
cristybb503372010-05-27 20:51:26 +00001235 cube_info->transparent_index=(ssize_t) image->colors;
cristy3ed852e2009-09-05 21:47:34 +00001236 }
1237 }
1238 }
1239 node_info->color_number=image->colors++;
1240 }
1241 return(image->colors);
1242}
1243
1244/*
1245%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1246% %
1247% %
1248% %
1249+ D e s t r o y C u b e I n f o %
1250% %
1251% %
1252% %
1253%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1254%
1255% DestroyCubeInfo() deallocates memory associated with an image.
1256%
1257% The format of the DestroyCubeInfo method is:
1258%
1259% DestroyCubeInfo(CubeInfo *cube_info)
1260%
1261% A description of each parameter follows:
1262%
1263% o cube_info: the address of a structure of type CubeInfo.
1264%
1265*/
1266static void DestroyCubeInfo(CubeInfo *cube_info)
1267{
1268 register Nodes
1269 *nodes;
1270
1271 /*
1272 Release color cube tree storage.
1273 */
1274 do
1275 {
1276 nodes=cube_info->node_queue->next;
1277 cube_info->node_queue->nodes=(NodeInfo *) RelinquishMagickMemory(
1278 cube_info->node_queue->nodes);
1279 cube_info->node_queue=(Nodes *) RelinquishMagickMemory(
1280 cube_info->node_queue);
1281 cube_info->node_queue=nodes;
1282 } while (cube_info->node_queue != (Nodes *) NULL);
cristybb503372010-05-27 20:51:26 +00001283 if (cube_info->cache != (ssize_t *) NULL)
1284 cube_info->cache=(ssize_t *) RelinquishMagickMemory(cube_info->cache);
cristy3ed852e2009-09-05 21:47:34 +00001285 cube_info->quantize_info=DestroyQuantizeInfo(cube_info->quantize_info);
1286 cube_info=(CubeInfo *) RelinquishMagickMemory(cube_info);
1287}
1288
1289/*
1290%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1291% %
1292% %
1293% %
1294% D e s t r o y Q u a n t i z e I n f o %
1295% %
1296% %
1297% %
1298%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1299%
1300% DestroyQuantizeInfo() deallocates memory associated with an QuantizeInfo
1301% structure.
1302%
1303% The format of the DestroyQuantizeInfo method is:
1304%
1305% QuantizeInfo *DestroyQuantizeInfo(QuantizeInfo *quantize_info)
1306%
1307% A description of each parameter follows:
1308%
1309% o quantize_info: Specifies a pointer to an QuantizeInfo structure.
1310%
1311*/
1312MagickExport QuantizeInfo *DestroyQuantizeInfo(QuantizeInfo *quantize_info)
1313{
1314 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
1315 assert(quantize_info != (QuantizeInfo *) NULL);
1316 assert(quantize_info->signature == MagickSignature);
1317 quantize_info->signature=(~MagickSignature);
1318 quantize_info=(QuantizeInfo *) RelinquishMagickMemory(quantize_info);
1319 return(quantize_info);
1320}
1321
1322/*
1323%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1324% %
1325% %
1326% %
1327+ D i t h e r I m a g e %
1328% %
1329% %
1330% %
1331%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1332%
1333% DitherImage() distributes the difference between an original image and
1334% the corresponding color reduced algorithm to neighboring pixels using
1335% serpentine-scan Floyd-Steinberg error diffusion. DitherImage returns
1336% MagickTrue if the image is dithered otherwise MagickFalse.
1337%
1338% The format of the DitherImage method is:
1339%
1340% MagickBooleanType DitherImage(Image *image,CubeInfo *cube_info)
1341%
1342% A description of each parameter follows.
1343%
1344% o image: the image.
1345%
1346% o cube_info: A pointer to the Cube structure.
1347%
1348*/
1349
1350static MagickBooleanType FloydSteinbergDither(Image *image,CubeInfo *cube_info)
1351{
1352#define DitherImageTag "Dither/Image"
1353
cristyc4c8d132010-01-07 01:58:38 +00001354 CacheView
1355 *image_view;
1356
cristy3ed852e2009-09-05 21:47:34 +00001357 ExceptionInfo
1358 *exception;
1359
cristybb503372010-05-27 20:51:26 +00001360 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001361 u,
1362 v,
1363 y;
1364
1365 MagickBooleanType
1366 proceed;
1367
1368 RealPixelPacket
1369 color,
1370 *current,
1371 pixel,
1372 *previous,
1373 *scanlines;
1374
1375 register CubeInfo
1376 *p;
1377
cristybb503372010-05-27 20:51:26 +00001378 size_t
cristy3ed852e2009-09-05 21:47:34 +00001379 index;
1380
cristy3ed852e2009-09-05 21:47:34 +00001381 /*
1382 Distribute quantization error using Floyd-Steinberg.
1383 */
1384 scanlines=(RealPixelPacket *) AcquireQuantumMemory(image->columns,
1385 2*sizeof(*scanlines));
1386 if (scanlines == (RealPixelPacket *) NULL)
1387 return(MagickFalse);
1388 p=cube_info;
1389 exception=(&image->exception);
1390 image_view=AcquireCacheView(image);
cristybb503372010-05-27 20:51:26 +00001391 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001392 {
1393 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001394 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00001395
cristybb503372010-05-27 20:51:26 +00001396 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001397 i,
1398 x;
1399
1400 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001401 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001402
1403 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1404 if (q == (PixelPacket *) NULL)
1405 return(MagickFalse);
1406 indexes=GetCacheViewAuthenticIndexQueue(image_view);
1407 current=scanlines+(y & 0x01)*image->columns;
1408 previous=scanlines+((y+1) & 0x01)*image->columns;
cristycee97112010-05-28 00:44:52 +00001409 v=(ssize_t) ((y & 0x01) ? -1 : 1);
cristybb503372010-05-27 20:51:26 +00001410 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001411 {
cristybb503372010-05-27 20:51:26 +00001412 u=(y & 0x01) ? (ssize_t) image->columns-1-x : x;
cristy3ed852e2009-09-05 21:47:34 +00001413 AssociateAlphaPixel(cube_info,q+u,&pixel);
1414 if (x > 0)
1415 {
1416 pixel.red+=7*current[u-v].red/16;
1417 pixel.green+=7*current[u-v].green/16;
1418 pixel.blue+=7*current[u-v].blue/16;
1419 if (cube_info->associate_alpha != MagickFalse)
1420 pixel.opacity+=7*current[u-v].opacity/16;
1421 }
1422 if (y > 0)
1423 {
cristybb503372010-05-27 20:51:26 +00001424 if (x < (ssize_t) (image->columns-1))
cristy3ed852e2009-09-05 21:47:34 +00001425 {
1426 pixel.red+=previous[u+v].red/16;
1427 pixel.green+=previous[u+v].green/16;
1428 pixel.blue+=previous[u+v].blue/16;
1429 if (cube_info->associate_alpha != MagickFalse)
1430 pixel.opacity+=previous[u+v].opacity/16;
1431 }
1432 pixel.red+=5*previous[u].red/16;
1433 pixel.green+=5*previous[u].green/16;
1434 pixel.blue+=5*previous[u].blue/16;
1435 if (cube_info->associate_alpha != MagickFalse)
1436 pixel.opacity+=5*previous[u].opacity/16;
1437 if (x > 0)
1438 {
1439 pixel.red+=3*previous[u-v].red/16;
1440 pixel.green+=3*previous[u-v].green/16;
1441 pixel.blue+=3*previous[u-v].blue/16;
1442 if (cube_info->associate_alpha != MagickFalse)
1443 pixel.opacity+=3*previous[u-v].opacity/16;
1444 }
1445 }
cristy75ffdb72010-01-07 17:40:12 +00001446 pixel.red=(MagickRealType) ClampToUnsignedQuantum(pixel.red);
1447 pixel.green=(MagickRealType) ClampToUnsignedQuantum(pixel.green);
1448 pixel.blue=(MagickRealType) ClampToUnsignedQuantum(pixel.blue);
cristy3ed852e2009-09-05 21:47:34 +00001449 if (cube_info->associate_alpha != MagickFalse)
cristy75ffdb72010-01-07 17:40:12 +00001450 pixel.opacity=(MagickRealType) ClampToUnsignedQuantum(pixel.opacity);
cristybb503372010-05-27 20:51:26 +00001451 i=(ssize_t) ((ScaleQuantumToChar(ClampToUnsignedQuantum(pixel.red)) >> CacheShift) |
cristy75ffdb72010-01-07 17:40:12 +00001452 (ScaleQuantumToChar(ClampToUnsignedQuantum(pixel.green)) >> CacheShift) << 6 |
1453 (ScaleQuantumToChar(ClampToUnsignedQuantum(pixel.blue)) >> CacheShift) << 12);
cristy3ed852e2009-09-05 21:47:34 +00001454 if (cube_info->associate_alpha != MagickFalse)
cristy75ffdb72010-01-07 17:40:12 +00001455 i|=((ScaleQuantumToChar(ClampToUnsignedQuantum(pixel.opacity)) >> CacheShift)
cristy3ed852e2009-09-05 21:47:34 +00001456 << 18);
1457 if (p->cache[i] < 0)
1458 {
1459 register NodeInfo
1460 *node_info;
1461
cristybb503372010-05-27 20:51:26 +00001462 register size_t
cristy3ed852e2009-09-05 21:47:34 +00001463 id;
1464
1465 /*
1466 Identify the deepest node containing the pixel's color.
1467 */
1468 node_info=p->root;
cristybb503372010-05-27 20:51:26 +00001469 for (index=MaxTreeDepth-1; (ssize_t) index > 0; index--)
cristy3ed852e2009-09-05 21:47:34 +00001470 {
1471 id=ColorToNodeId(cube_info,&pixel,index);
1472 if (node_info->child[id] == (NodeInfo *) NULL)
1473 break;
1474 node_info=node_info->child[id];
1475 }
1476 /*
1477 Find closest color among siblings and their children.
1478 */
1479 p->target=pixel;
1480 p->distance=(MagickRealType) (4.0*(QuantumRange+1.0)*(QuantumRange+
1481 1.0)+1.0);
1482 ClosestColor(image,p,node_info->parent);
cristybb503372010-05-27 20:51:26 +00001483 p->cache[i]=(ssize_t) p->color_number;
cristy3ed852e2009-09-05 21:47:34 +00001484 }
1485 /*
1486 Assign pixel to closest colormap entry.
1487 */
cristybb503372010-05-27 20:51:26 +00001488 index=(size_t) p->cache[i];
cristy3ed852e2009-09-05 21:47:34 +00001489 if (image->storage_class == PseudoClass)
1490 indexes[u]=(IndexPacket) index;
1491 if (cube_info->quantize_info->measure_error == MagickFalse)
1492 {
1493 (q+u)->red=image->colormap[index].red;
1494 (q+u)->green=image->colormap[index].green;
1495 (q+u)->blue=image->colormap[index].blue;
1496 if (cube_info->associate_alpha != MagickFalse)
1497 (q+u)->opacity=image->colormap[index].opacity;
1498 }
1499 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1500 return(MagickFalse);
1501 /*
1502 Store the error.
1503 */
1504 AssociateAlphaPixel(cube_info,image->colormap+index,&color);
1505 current[u].red=pixel.red-color.red;
1506 current[u].green=pixel.green-color.green;
1507 current[u].blue=pixel.blue-color.blue;
1508 if (cube_info->associate_alpha != MagickFalse)
1509 current[u].opacity=pixel.opacity-color.opacity;
1510 proceed=SetImageProgress(image,DitherImageTag,p->offset,p->span);
1511 if (proceed == MagickFalse)
1512 return(MagickFalse);
1513 p->offset++;
1514 }
1515 }
1516 scanlines=(RealPixelPacket *) RelinquishMagickMemory(scanlines);
1517 image_view=DestroyCacheView(image_view);
1518 return(MagickTrue);
1519}
1520
1521static MagickBooleanType
1522 RiemersmaDither(Image *,CacheView *,CubeInfo *,const unsigned int);
1523
1524static void Riemersma(Image *image,CacheView *image_view,CubeInfo *cube_info,
cristybb503372010-05-27 20:51:26 +00001525 const size_t level,const unsigned int direction)
cristy3ed852e2009-09-05 21:47:34 +00001526{
1527 if (level == 1)
1528 switch (direction)
1529 {
1530 case WestGravity:
1531 {
1532 (void) RiemersmaDither(image,image_view,cube_info,EastGravity);
1533 (void) RiemersmaDither(image,image_view,cube_info,SouthGravity);
1534 (void) RiemersmaDither(image,image_view,cube_info,WestGravity);
1535 break;
1536 }
1537 case EastGravity:
1538 {
1539 (void) RiemersmaDither(image,image_view,cube_info,WestGravity);
1540 (void) RiemersmaDither(image,image_view,cube_info,NorthGravity);
1541 (void) RiemersmaDither(image,image_view,cube_info,EastGravity);
1542 break;
1543 }
1544 case NorthGravity:
1545 {
1546 (void) RiemersmaDither(image,image_view,cube_info,SouthGravity);
1547 (void) RiemersmaDither(image,image_view,cube_info,EastGravity);
1548 (void) RiemersmaDither(image,image_view,cube_info,NorthGravity);
1549 break;
1550 }
1551 case SouthGravity:
1552 {
1553 (void) RiemersmaDither(image,image_view,cube_info,NorthGravity);
1554 (void) RiemersmaDither(image,image_view,cube_info,WestGravity);
1555 (void) RiemersmaDither(image,image_view,cube_info,SouthGravity);
1556 break;
1557 }
1558 default:
1559 break;
1560 }
1561 else
1562 switch (direction)
1563 {
1564 case WestGravity:
1565 {
1566 Riemersma(image,image_view,cube_info,level-1,NorthGravity);
1567 (void) RiemersmaDither(image,image_view,cube_info,EastGravity);
1568 Riemersma(image,image_view,cube_info,level-1,WestGravity);
1569 (void) RiemersmaDither(image,image_view,cube_info,SouthGravity);
1570 Riemersma(image,image_view,cube_info,level-1,WestGravity);
1571 (void) RiemersmaDither(image,image_view,cube_info,WestGravity);
1572 Riemersma(image,image_view,cube_info,level-1,SouthGravity);
1573 break;
1574 }
1575 case EastGravity:
1576 {
1577 Riemersma(image,image_view,cube_info,level-1,SouthGravity);
1578 (void) RiemersmaDither(image,image_view,cube_info,WestGravity);
1579 Riemersma(image,image_view,cube_info,level-1,EastGravity);
1580 (void) RiemersmaDither(image,image_view,cube_info,NorthGravity);
1581 Riemersma(image,image_view,cube_info,level-1,EastGravity);
1582 (void) RiemersmaDither(image,image_view,cube_info,EastGravity);
1583 Riemersma(image,image_view,cube_info,level-1,NorthGravity);
1584 break;
1585 }
1586 case NorthGravity:
1587 {
1588 Riemersma(image,image_view,cube_info,level-1,WestGravity);
1589 (void) RiemersmaDither(image,image_view,cube_info,SouthGravity);
1590 Riemersma(image,image_view,cube_info,level-1,NorthGravity);
1591 (void) RiemersmaDither(image,image_view,cube_info,EastGravity);
1592 Riemersma(image,image_view,cube_info,level-1,NorthGravity);
1593 (void) RiemersmaDither(image,image_view,cube_info,NorthGravity);
1594 Riemersma(image,image_view,cube_info,level-1,EastGravity);
1595 break;
1596 }
1597 case SouthGravity:
1598 {
1599 Riemersma(image,image_view,cube_info,level-1,EastGravity);
1600 (void) RiemersmaDither(image,image_view,cube_info,NorthGravity);
1601 Riemersma(image,image_view,cube_info,level-1,SouthGravity);
1602 (void) RiemersmaDither(image,image_view,cube_info,WestGravity);
1603 Riemersma(image,image_view,cube_info,level-1,SouthGravity);
1604 (void) RiemersmaDither(image,image_view,cube_info,SouthGravity);
1605 Riemersma(image,image_view,cube_info,level-1,WestGravity);
1606 break;
1607 }
1608 default:
1609 break;
1610 }
1611}
1612
1613static MagickBooleanType RiemersmaDither(Image *image,CacheView *image_view,
1614 CubeInfo *cube_info,const unsigned int direction)
1615{
1616#define DitherImageTag "Dither/Image"
1617
1618 MagickBooleanType
1619 proceed;
1620
1621 RealPixelPacket
1622 color,
1623 pixel;
1624
1625 register CubeInfo
1626 *p;
1627
cristybb503372010-05-27 20:51:26 +00001628 size_t
cristy3ed852e2009-09-05 21:47:34 +00001629 index;
1630
1631 p=cube_info;
cristybb503372010-05-27 20:51:26 +00001632 if ((p->x >= 0) && (p->x < (ssize_t) image->columns) &&
1633 (p->y >= 0) && (p->y < (ssize_t) image->rows))
cristy3ed852e2009-09-05 21:47:34 +00001634 {
1635 ExceptionInfo
1636 *exception;
1637
1638 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001639 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00001640
cristybb503372010-05-27 20:51:26 +00001641 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001642 i;
1643
1644 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001645 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001646
1647 /*
1648 Distribute error.
1649 */
1650 exception=(&image->exception);
1651 q=GetCacheViewAuthenticPixels(image_view,p->x,p->y,1,1,exception);
1652 if (q == (PixelPacket *) NULL)
1653 return(MagickFalse);
1654 indexes=GetCacheViewAuthenticIndexQueue(image_view);
1655 AssociateAlphaPixel(cube_info,q,&pixel);
1656 for (i=0; i < ErrorQueueLength; i++)
1657 {
1658 pixel.red+=p->weights[i]*p->error[i].red;
1659 pixel.green+=p->weights[i]*p->error[i].green;
1660 pixel.blue+=p->weights[i]*p->error[i].blue;
1661 if (cube_info->associate_alpha != MagickFalse)
1662 pixel.opacity+=p->weights[i]*p->error[i].opacity;
1663 }
cristy75ffdb72010-01-07 17:40:12 +00001664 pixel.red=(MagickRealType) ClampToUnsignedQuantum(pixel.red);
1665 pixel.green=(MagickRealType) ClampToUnsignedQuantum(pixel.green);
1666 pixel.blue=(MagickRealType) ClampToUnsignedQuantum(pixel.blue);
cristy3ed852e2009-09-05 21:47:34 +00001667 if (cube_info->associate_alpha != MagickFalse)
cristy75ffdb72010-01-07 17:40:12 +00001668 pixel.opacity=(MagickRealType) ClampToUnsignedQuantum(pixel.opacity);
cristybb503372010-05-27 20:51:26 +00001669 i=(ssize_t) ((ScaleQuantumToChar(ClampToUnsignedQuantum(pixel.red)) >> CacheShift) |
cristy75ffdb72010-01-07 17:40:12 +00001670 (ScaleQuantumToChar(ClampToUnsignedQuantum(pixel.green)) >> CacheShift) << 6 |
1671 (ScaleQuantumToChar(ClampToUnsignedQuantum(pixel.blue)) >> CacheShift) << 12);
cristy3ed852e2009-09-05 21:47:34 +00001672 if (cube_info->associate_alpha != MagickFalse)
cristy75ffdb72010-01-07 17:40:12 +00001673 i|=((ScaleQuantumToChar(ClampToUnsignedQuantum(pixel.opacity)) >> CacheShift)
cristy3ed852e2009-09-05 21:47:34 +00001674 << 18);
1675 if (p->cache[i] < 0)
1676 {
1677 register NodeInfo
1678 *node_info;
1679
cristybb503372010-05-27 20:51:26 +00001680 register size_t
cristy3ed852e2009-09-05 21:47:34 +00001681 id;
1682
1683 /*
1684 Identify the deepest node containing the pixel's color.
1685 */
1686 node_info=p->root;
cristybb503372010-05-27 20:51:26 +00001687 for (index=MaxTreeDepth-1; (ssize_t) index > 0; index--)
cristy3ed852e2009-09-05 21:47:34 +00001688 {
1689 id=ColorToNodeId(cube_info,&pixel,index);
1690 if (node_info->child[id] == (NodeInfo *) NULL)
1691 break;
1692 node_info=node_info->child[id];
1693 }
1694 /*
1695 Find closest color among siblings and their children.
1696 */
1697 p->target=pixel;
1698 p->distance=(MagickRealType) (4.0*(QuantumRange+1.0)*((MagickRealType)
1699 QuantumRange+1.0)+1.0);
1700 ClosestColor(image,p,node_info->parent);
cristybb503372010-05-27 20:51:26 +00001701 p->cache[i]=(ssize_t) p->color_number;
cristy3ed852e2009-09-05 21:47:34 +00001702 }
1703 /*
1704 Assign pixel to closest colormap entry.
1705 */
cristybb503372010-05-27 20:51:26 +00001706 index=(size_t) (1*p->cache[i]);
cristy3ed852e2009-09-05 21:47:34 +00001707 if (image->storage_class == PseudoClass)
1708 *indexes=(IndexPacket) index;
1709 if (cube_info->quantize_info->measure_error == MagickFalse)
1710 {
1711 q->red=image->colormap[index].red;
1712 q->green=image->colormap[index].green;
1713 q->blue=image->colormap[index].blue;
1714 if (cube_info->associate_alpha != MagickFalse)
1715 q->opacity=image->colormap[index].opacity;
1716 }
1717 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1718 return(MagickFalse);
1719 /*
1720 Propagate the error as the last entry of the error queue.
1721 */
1722 (void) CopyMagickMemory(p->error,p->error+1,(ErrorQueueLength-1)*
1723 sizeof(p->error[0]));
1724 AssociateAlphaPixel(cube_info,image->colormap+index,&color);
1725 p->error[ErrorQueueLength-1].red=pixel.red-color.red;
1726 p->error[ErrorQueueLength-1].green=pixel.green-color.green;
1727 p->error[ErrorQueueLength-1].blue=pixel.blue-color.blue;
1728 if (cube_info->associate_alpha != MagickFalse)
1729 p->error[ErrorQueueLength-1].opacity=pixel.opacity-color.opacity;
1730 proceed=SetImageProgress(image,DitherImageTag,p->offset,p->span);
1731 if (proceed == MagickFalse)
1732 return(MagickFalse);
1733 p->offset++;
1734 }
1735 switch (direction)
1736 {
1737 case WestGravity: p->x--; break;
1738 case EastGravity: p->x++; break;
1739 case NorthGravity: p->y--; break;
1740 case SouthGravity: p->y++; break;
1741 }
1742 return(MagickTrue);
1743}
1744
cristybb503372010-05-27 20:51:26 +00001745static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +00001746{
1747 if (x > y)
1748 return(x);
1749 return(y);
1750}
1751
cristybb503372010-05-27 20:51:26 +00001752static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +00001753{
1754 if (x < y)
1755 return(x);
1756 return(y);
1757}
1758
1759static MagickBooleanType DitherImage(Image *image,CubeInfo *cube_info)
1760{
cristyc4c8d132010-01-07 01:58:38 +00001761 CacheView
1762 *image_view;
1763
cristy3ed852e2009-09-05 21:47:34 +00001764 MagickBooleanType
1765 status;
1766
cristybb503372010-05-27 20:51:26 +00001767 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001768 i;
1769
cristybb503372010-05-27 20:51:26 +00001770 size_t
cristy3ed852e2009-09-05 21:47:34 +00001771 depth;
1772
cristy3ed852e2009-09-05 21:47:34 +00001773 if (cube_info->quantize_info->dither_method == FloydSteinbergDitherMethod)
1774 return(FloydSteinbergDither(image,cube_info));
1775 /*
cristycee97112010-05-28 00:44:52 +00001776 Distribute quantization error along a Hilbert curve.
cristy3ed852e2009-09-05 21:47:34 +00001777 */
1778 (void) ResetMagickMemory(cube_info->error,0,ErrorQueueLength*
1779 sizeof(*cube_info->error));
1780 cube_info->x=0;
1781 cube_info->y=0;
cristybb503372010-05-27 20:51:26 +00001782 i=MagickMax((ssize_t) image->columns,(ssize_t) image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001783 for (depth=1; i != 0; depth++)
1784 i>>=1;
cristybb503372010-05-27 20:51:26 +00001785 if ((ssize_t) (1L << depth) < MagickMax((ssize_t) image->columns,(ssize_t) image->rows))
cristy3ed852e2009-09-05 21:47:34 +00001786 depth++;
1787 cube_info->offset=0;
1788 cube_info->span=(MagickSizeType) image->columns*image->rows;
1789 image_view=AcquireCacheView(image);
1790 if (depth > 1)
1791 Riemersma(image,image_view,cube_info,depth-1,NorthGravity);
1792 status=RiemersmaDither(image,image_view,cube_info,ForgetGravity);
1793 image_view=DestroyCacheView(image_view);
1794 return(status);
1795}
1796
1797/*
1798%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1799% %
1800% %
1801% %
1802+ G e t C u b e I n f o %
1803% %
1804% %
1805% %
1806%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1807%
1808% GetCubeInfo() initialize the Cube data structure.
1809%
1810% The format of the GetCubeInfo method is:
1811%
1812% CubeInfo GetCubeInfo(const QuantizeInfo *quantize_info,
cristybb503372010-05-27 20:51:26 +00001813% const size_t depth,const size_t maximum_colors)
cristy3ed852e2009-09-05 21:47:34 +00001814%
1815% A description of each parameter follows.
1816%
1817% o quantize_info: Specifies a pointer to an QuantizeInfo structure.
1818%
1819% o depth: Normally, this integer value is zero or one. A zero or
1820% one tells Quantize to choose a optimal tree depth of Log4(number_colors).
1821% A tree of this depth generally allows the best representation of the
1822% reference image with the least amount of memory and the fastest
1823% computational speed. In some cases, such as an image with low color
1824% dispersion (a few number of colors), a value other than
1825% Log4(number_colors) is required. To expand the color tree completely,
1826% use a value of 8.
1827%
1828% o maximum_colors: maximum colors.
1829%
1830*/
1831static CubeInfo *GetCubeInfo(const QuantizeInfo *quantize_info,
cristybb503372010-05-27 20:51:26 +00001832 const size_t depth,const size_t maximum_colors)
cristy3ed852e2009-09-05 21:47:34 +00001833{
1834 CubeInfo
1835 *cube_info;
1836
1837 MagickRealType
1838 sum,
1839 weight;
1840
1841 size_t
1842 length;
1843
cristybb503372010-05-27 20:51:26 +00001844 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001845 i;
1846
1847 /*
1848 Initialize tree to describe color cube_info.
1849 */
cristy90823212009-12-12 20:48:33 +00001850 cube_info=(CubeInfo *) AcquireAlignedMemory(1,sizeof(*cube_info));
cristy3ed852e2009-09-05 21:47:34 +00001851 if (cube_info == (CubeInfo *) NULL)
1852 return((CubeInfo *) NULL);
1853 (void) ResetMagickMemory(cube_info,0,sizeof(*cube_info));
1854 cube_info->depth=depth;
1855 if (cube_info->depth > MaxTreeDepth)
1856 cube_info->depth=MaxTreeDepth;
1857 if (cube_info->depth < 2)
1858 cube_info->depth=2;
1859 cube_info->maximum_colors=maximum_colors;
1860 /*
1861 Initialize root node.
1862 */
1863 cube_info->root=GetNodeInfo(cube_info,0,0,(NodeInfo *) NULL);
1864 if (cube_info->root == (NodeInfo *) NULL)
1865 return((CubeInfo *) NULL);
1866 cube_info->root->parent=cube_info->root;
1867 cube_info->quantize_info=CloneQuantizeInfo(quantize_info);
1868 if (cube_info->quantize_info->dither == MagickFalse)
1869 return(cube_info);
1870 /*
1871 Initialize dither resources.
1872 */
1873 length=(size_t) (1UL << (4*(8-CacheShift)));
cristybb503372010-05-27 20:51:26 +00001874 cube_info->cache=(ssize_t *) AcquireQuantumMemory(length,
cristy3ed852e2009-09-05 21:47:34 +00001875 sizeof(*cube_info->cache));
cristybb503372010-05-27 20:51:26 +00001876 if (cube_info->cache == (ssize_t *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001877 return((CubeInfo *) NULL);
1878 /*
1879 Initialize color cache.
1880 */
cristybb503372010-05-27 20:51:26 +00001881 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00001882 cube_info->cache[i]=(-1);
1883 /*
cristycee97112010-05-28 00:44:52 +00001884 Distribute weights along a curve of exponential decay.
cristy3ed852e2009-09-05 21:47:34 +00001885 */
1886 weight=1.0;
1887 for (i=0; i < ErrorQueueLength; i++)
1888 {
1889 cube_info->weights[ErrorQueueLength-i-1]=1.0/weight;
1890 weight*=exp(log(((double) QuantumRange+1.0))/(ErrorQueueLength-1.0));
1891 }
1892 /*
1893 Normalize the weighting factors.
1894 */
1895 weight=0.0;
1896 for (i=0; i < ErrorQueueLength; i++)
1897 weight+=cube_info->weights[i];
1898 sum=0.0;
1899 for (i=0; i < ErrorQueueLength; i++)
1900 {
1901 cube_info->weights[i]/=weight;
1902 sum+=cube_info->weights[i];
1903 }
1904 cube_info->weights[0]+=1.0-sum;
1905 return(cube_info);
1906}
1907
1908/*
1909%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1910% %
1911% %
1912% %
1913+ G e t N o d e I n f o %
1914% %
1915% %
1916% %
1917%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1918%
1919% GetNodeInfo() allocates memory for a new node in the color cube tree and
1920% presets all fields to zero.
1921%
1922% The format of the GetNodeInfo method is:
1923%
cristybb503372010-05-27 20:51:26 +00001924% NodeInfo *GetNodeInfo(CubeInfo *cube_info,const size_t id,
1925% const size_t level,NodeInfo *parent)
cristy3ed852e2009-09-05 21:47:34 +00001926%
1927% A description of each parameter follows.
1928%
1929% o node: The GetNodeInfo method returns a pointer to a queue of nodes.
1930%
1931% o id: Specifies the child number of the node.
1932%
1933% o level: Specifies the level in the storage_class the node resides.
1934%
1935*/
cristybb503372010-05-27 20:51:26 +00001936static NodeInfo *GetNodeInfo(CubeInfo *cube_info,const size_t id,
1937 const size_t level,NodeInfo *parent)
cristy3ed852e2009-09-05 21:47:34 +00001938{
1939 NodeInfo
1940 *node_info;
1941
1942 if (cube_info->free_nodes == 0)
1943 {
1944 Nodes
1945 *nodes;
1946
1947 /*
1948 Allocate a new queue of nodes.
1949 */
cristy90823212009-12-12 20:48:33 +00001950 nodes=(Nodes *) AcquireAlignedMemory(1,sizeof(*nodes));
cristy3ed852e2009-09-05 21:47:34 +00001951 if (nodes == (Nodes *) NULL)
1952 return((NodeInfo *) NULL);
1953 nodes->nodes=(NodeInfo *) AcquireQuantumMemory(NodesInAList,
1954 sizeof(*nodes->nodes));
1955 if (nodes->nodes == (NodeInfo *) NULL)
1956 return((NodeInfo *) NULL);
1957 nodes->next=cube_info->node_queue;
1958 cube_info->node_queue=nodes;
1959 cube_info->next_node=nodes->nodes;
1960 cube_info->free_nodes=NodesInAList;
1961 }
1962 cube_info->nodes++;
1963 cube_info->free_nodes--;
1964 node_info=cube_info->next_node++;
1965 (void) ResetMagickMemory(node_info,0,sizeof(*node_info));
1966 node_info->parent=parent;
1967 node_info->id=id;
1968 node_info->level=level;
1969 return(node_info);
1970}
1971
1972/*
1973%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1974% %
1975% %
1976% %
1977% G e t I m a g e Q u a n t i z e E r r o r %
1978% %
1979% %
1980% %
1981%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1982%
1983% GetImageQuantizeError() measures the difference between the original
1984% and quantized images. This difference is the total quantization error.
1985% The error is computed by summing over all pixels in an image the distance
1986% squared in RGB space between each reference pixel value and its quantized
1987% value. These values are computed:
1988%
1989% o mean_error_per_pixel: This value is the mean error for any single
1990% pixel in the image.
1991%
1992% o normalized_mean_square_error: This value is the normalized mean
1993% quantization error for any single pixel in the image. This distance
1994% measure is normalized to a range between 0 and 1. It is independent
1995% of the range of red, green, and blue values in the image.
1996%
1997% o normalized_maximum_square_error: Thsi value is the normalized
1998% maximum quantization error for any single pixel in the image. This
1999% distance measure is normalized to a range between 0 and 1. It is
2000% independent of the range of red, green, and blue values in your image.
2001%
2002% The format of the GetImageQuantizeError method is:
2003%
2004% MagickBooleanType GetImageQuantizeError(Image *image)
2005%
2006% A description of each parameter follows.
2007%
2008% o image: the image.
2009%
2010*/
2011MagickExport MagickBooleanType GetImageQuantizeError(Image *image)
2012{
cristyc4c8d132010-01-07 01:58:38 +00002013 CacheView
2014 *image_view;
2015
cristy3ed852e2009-09-05 21:47:34 +00002016 ExceptionInfo
2017 *exception;
2018
2019 IndexPacket
2020 *indexes;
2021
cristybb503372010-05-27 20:51:26 +00002022 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002023 y;
2024
2025 MagickRealType
2026 alpha,
2027 area,
2028 beta,
2029 distance,
2030 maximum_error,
2031 mean_error,
2032 mean_error_per_pixel;
2033
cristybb503372010-05-27 20:51:26 +00002034 size_t
cristy3ed852e2009-09-05 21:47:34 +00002035 index;
2036
cristy3ed852e2009-09-05 21:47:34 +00002037 assert(image != (Image *) NULL);
2038 assert(image->signature == MagickSignature);
2039 if (image->debug != MagickFalse)
2040 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2041 image->total_colors=GetNumberColors(image,(FILE *) NULL,&image->exception);
2042 (void) ResetMagickMemory(&image->error,0,sizeof(image->error));
2043 if (image->storage_class == DirectClass)
2044 return(MagickTrue);
2045 alpha=1.0;
2046 beta=1.0;
2047 area=3.0*image->columns*image->rows;
2048 maximum_error=0.0;
2049 mean_error_per_pixel=0.0;
2050 mean_error=0.0;
2051 exception=(&image->exception);
2052 image_view=AcquireCacheView(image);
cristybb503372010-05-27 20:51:26 +00002053 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002054 {
2055 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002056 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002057
cristybb503372010-05-27 20:51:26 +00002058 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002059 x;
2060
2061 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
2062 if (p == (const PixelPacket *) NULL)
2063 break;
2064 indexes=GetCacheViewAuthenticIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00002065 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002066 {
2067 index=1UL*indexes[x];
2068 if (image->matte != MagickFalse)
2069 {
cristy46f08202010-01-10 04:04:21 +00002070 alpha=(MagickRealType) (QuantumScale*(GetAlphaPixelComponent(p)));
cristy3ed852e2009-09-05 21:47:34 +00002071 beta=(MagickRealType) (QuantumScale*(QuantumRange-
2072 image->colormap[index].opacity));
2073 }
2074 distance=fabs(alpha*p->red-beta*image->colormap[index].red);
2075 mean_error_per_pixel+=distance;
2076 mean_error+=distance*distance;
2077 if (distance > maximum_error)
2078 maximum_error=distance;
2079 distance=fabs(alpha*p->green-beta*image->colormap[index].green);
2080 mean_error_per_pixel+=distance;
2081 mean_error+=distance*distance;
2082 if (distance > maximum_error)
2083 maximum_error=distance;
2084 distance=fabs(alpha*p->blue-beta*image->colormap[index].blue);
2085 mean_error_per_pixel+=distance;
2086 mean_error+=distance*distance;
2087 if (distance > maximum_error)
2088 maximum_error=distance;
2089 p++;
2090 }
2091 }
2092 image_view=DestroyCacheView(image_view);
2093 image->error.mean_error_per_pixel=(double) mean_error_per_pixel/area;
2094 image->error.normalized_mean_error=(double) QuantumScale*QuantumScale*
2095 mean_error/area;
2096 image->error.normalized_maximum_error=(double) QuantumScale*maximum_error;
2097 return(MagickTrue);
2098}
2099
2100/*
2101%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2102% %
2103% %
2104% %
2105% G e t Q u a n t i z e I n f o %
2106% %
2107% %
2108% %
2109%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2110%
2111% GetQuantizeInfo() initializes the QuantizeInfo structure.
2112%
2113% The format of the GetQuantizeInfo method is:
2114%
2115% GetQuantizeInfo(QuantizeInfo *quantize_info)
2116%
2117% A description of each parameter follows:
2118%
2119% o quantize_info: Specifies a pointer to a QuantizeInfo structure.
2120%
2121*/
2122MagickExport void GetQuantizeInfo(QuantizeInfo *quantize_info)
2123{
2124 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2125 assert(quantize_info != (QuantizeInfo *) NULL);
2126 (void) ResetMagickMemory(quantize_info,0,sizeof(*quantize_info));
2127 quantize_info->number_colors=256;
2128 quantize_info->dither=MagickTrue;
2129 quantize_info->dither_method=RiemersmaDitherMethod;
2130 quantize_info->colorspace=UndefinedColorspace;
2131 quantize_info->measure_error=MagickFalse;
2132 quantize_info->signature=MagickSignature;
2133}
2134
2135/*
2136%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2137% %
2138% %
2139% %
2140% P o s t e r i z e I m a g e %
2141% %
2142% %
2143% %
2144%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2145%
2146% PosterizeImage() reduces the image to a limited number of colors for a
2147% "poster" effect.
2148%
2149% The format of the PosterizeImage method is:
2150%
cristybb503372010-05-27 20:51:26 +00002151% MagickBooleanType PosterizeImage(Image *image,const size_t levels,
cristy3ed852e2009-09-05 21:47:34 +00002152% const MagickBooleanType dither)
2153%
2154% A description of each parameter follows:
2155%
2156% o image: Specifies a pointer to an Image structure.
2157%
2158% o levels: Number of color levels allowed in each channel. Very low values
2159% (2, 3, or 4) have the most visible effect.
2160%
2161% o dither: Set this integer value to something other than zero to
2162% dither the mapped image.
2163%
2164*/
2165MagickExport MagickBooleanType PosterizeImage(Image *image,
cristybb503372010-05-27 20:51:26 +00002166 const size_t levels,const MagickBooleanType dither)
cristy3ed852e2009-09-05 21:47:34 +00002167{
cristyc4c8d132010-01-07 01:58:38 +00002168 CacheView
2169 *posterize_view;
2170
cristy3ed852e2009-09-05 21:47:34 +00002171 ExceptionInfo
2172 *exception;
2173
2174 Image
2175 *posterize_image;
2176
2177 IndexPacket
2178 *indexes;
2179
cristybb503372010-05-27 20:51:26 +00002180 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002181 j,
2182 k,
2183 l,
2184 n;
2185
2186 MagickBooleanType
2187 status;
2188
2189 QuantizeInfo
2190 *quantize_info;
2191
cristybb503372010-05-27 20:51:26 +00002192 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002193 i;
2194
2195 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002196 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002197
cristyd99b0962010-05-29 23:14:26 +00002198 size_t
2199 length;
2200
cristy3ed852e2009-09-05 21:47:34 +00002201 /*
2202 Posterize image.
2203 */
2204 assert(image != (Image *) NULL);
2205 assert(image->signature == MagickSignature);
2206 if (image->debug != MagickFalse)
2207 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2208 posterize_image=AcquireImage((ImageInfo *) NULL);
2209 if (posterize_image == (Image *) NULL)
2210 return(MagickFalse);
2211 l=1;
cristyd99b0962010-05-29 23:14:26 +00002212 length=(size_t) (levels*levels*levels);
cristyeaedf062010-05-29 22:36:02 +00002213 while ((l*l*l) < (ssize_t) MagickMin((ssize_t) length,MaxColormapSize+1))
cristy3ed852e2009-09-05 21:47:34 +00002214 l++;
cristybb503372010-05-27 20:51:26 +00002215 status=SetImageExtent(posterize_image,(size_t) (l*l*l),1);
cristy3ed852e2009-09-05 21:47:34 +00002216 if (status == MagickFalse)
2217 {
2218 posterize_image=DestroyImage(posterize_image);
2219 return(MagickFalse);
2220 }
2221 status=AcquireImageColormap(posterize_image,levels*levels*levels);
2222 if (status == MagickFalse)
2223 {
2224 posterize_image=DestroyImage(posterize_image);
2225 return(MagickFalse);
2226 }
2227 posterize_view=AcquireCacheView(posterize_image);
2228 exception=(&image->exception);
2229 q=QueueCacheViewAuthenticPixels(posterize_view,0,0,posterize_image->columns,1,
2230 exception);
2231 if (q == (PixelPacket *) NULL)
2232 {
2233 posterize_view=DestroyCacheView(posterize_view);
2234 posterize_image=DestroyImage(posterize_image);
2235 return(MagickFalse);
2236 }
2237 indexes=GetCacheViewAuthenticIndexQueue(posterize_view);
2238 n=0;
2239 for (i=0; i < l; i++)
2240 for (j=0; j < l; j++)
2241 for (k=0; k < l; k++)
2242 {
2243 posterize_image->colormap[n].red=(Quantum) (QuantumRange*i/
2244 MagickMax(l-1L,1L));
2245 posterize_image->colormap[n].green=(Quantum)
2246 (QuantumRange*j/MagickMax(l-1L,1L));
2247 posterize_image->colormap[n].blue=(Quantum) (QuantumRange*k/
2248 MagickMax(l-1L,1L));
2249 posterize_image->colormap[n].opacity=OpaqueOpacity;
2250 *q++=posterize_image->colormap[n];
2251 indexes[n]=(IndexPacket) n;
2252 n++;
2253 }
2254 if (SyncCacheViewAuthenticPixels(posterize_view,exception) == MagickFalse)
2255 {
2256 posterize_view=DestroyCacheView(posterize_view);
2257 posterize_image=DestroyImage(posterize_image);
2258 return(MagickFalse);
2259 }
2260 posterize_view=DestroyCacheView(posterize_view);
2261 quantize_info=AcquireQuantizeInfo((ImageInfo *) NULL);
2262 quantize_info->dither=dither;
2263 status=RemapImage(quantize_info,image,posterize_image);
2264 quantize_info=DestroyQuantizeInfo(quantize_info);
2265 posterize_image=DestroyImage(posterize_image);
2266 return(status);
2267}
2268
2269/*
2270%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2271% %
2272% %
2273% %
2274+ P r u n e C h i l d %
2275% %
2276% %
2277% %
2278%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2279%
2280% PruneChild() deletes the given node and merges its statistics into its
2281% parent.
2282%
2283% The format of the PruneSubtree method is:
2284%
2285% PruneChild(const Image *image,CubeInfo *cube_info,
2286% const NodeInfo *node_info)
2287%
2288% A description of each parameter follows.
2289%
2290% o image: the image.
2291%
2292% o cube_info: A pointer to the Cube structure.
2293%
2294% o node_info: pointer to node in color cube tree that is to be pruned.
2295%
2296*/
2297static void PruneChild(const Image *image,CubeInfo *cube_info,
2298 const NodeInfo *node_info)
2299{
2300 NodeInfo
2301 *parent;
2302
cristybb503372010-05-27 20:51:26 +00002303 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002304 i;
2305
cristybb503372010-05-27 20:51:26 +00002306 size_t
cristy3ed852e2009-09-05 21:47:34 +00002307 number_children;
2308
2309 /*
2310 Traverse any children.
2311 */
2312 number_children=cube_info->associate_alpha == MagickFalse ? 8UL : 16UL;
cristybb503372010-05-27 20:51:26 +00002313 for (i=0; i < (ssize_t) number_children; i++)
cristy3ed852e2009-09-05 21:47:34 +00002314 if (node_info->child[i] != (NodeInfo *) NULL)
2315 PruneChild(image,cube_info,node_info->child[i]);
2316 /*
2317 Merge color statistics into parent.
2318 */
2319 parent=node_info->parent;
2320 parent->number_unique+=node_info->number_unique;
2321 parent->total_color.red+=node_info->total_color.red;
2322 parent->total_color.green+=node_info->total_color.green;
2323 parent->total_color.blue+=node_info->total_color.blue;
2324 parent->total_color.opacity+=node_info->total_color.opacity;
2325 parent->child[node_info->id]=(NodeInfo *) NULL;
2326 cube_info->nodes--;
2327}
2328
2329/*
2330%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2331% %
2332% %
2333% %
2334+ P r u n e L e v e l %
2335% %
2336% %
2337% %
2338%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2339%
2340% PruneLevel() deletes all nodes at the bottom level of the color tree merging
2341% their color statistics into their parent node.
2342%
2343% The format of the PruneLevel method is:
2344%
2345% PruneLevel(const Image *image,CubeInfo *cube_info,
2346% const NodeInfo *node_info)
2347%
2348% A description of each parameter follows.
2349%
2350% o image: the image.
2351%
2352% o cube_info: A pointer to the Cube structure.
2353%
2354% o node_info: pointer to node in color cube tree that is to be pruned.
2355%
2356*/
2357static void PruneLevel(const Image *image,CubeInfo *cube_info,
2358 const NodeInfo *node_info)
2359{
cristybb503372010-05-27 20:51:26 +00002360 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002361 i;
2362
cristybb503372010-05-27 20:51:26 +00002363 size_t
cristy3ed852e2009-09-05 21:47:34 +00002364 number_children;
2365
2366 /*
2367 Traverse any children.
2368 */
2369 number_children=cube_info->associate_alpha == MagickFalse ? 8UL : 16UL;
cristybb503372010-05-27 20:51:26 +00002370 for (i=0; i < (ssize_t) number_children; i++)
cristy3ed852e2009-09-05 21:47:34 +00002371 if (node_info->child[i] != (NodeInfo *) NULL)
2372 PruneLevel(image,cube_info,node_info->child[i]);
2373 if (node_info->level == cube_info->depth)
2374 PruneChild(image,cube_info,node_info);
2375}
2376
2377/*
2378%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2379% %
2380% %
2381% %
2382+ P r u n e T o C u b e D e p t h %
2383% %
2384% %
2385% %
2386%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2387%
2388% PruneToCubeDepth() deletes any nodes at a depth greater than
2389% cube_info->depth while merging their color statistics into their parent
2390% node.
2391%
2392% The format of the PruneToCubeDepth method is:
2393%
2394% PruneToCubeDepth(const Image *image,CubeInfo *cube_info,
2395% const NodeInfo *node_info)
2396%
2397% A description of each parameter follows.
2398%
2399% o cube_info: A pointer to the Cube structure.
2400%
2401% o node_info: pointer to node in color cube tree that is to be pruned.
2402%
2403*/
2404static void PruneToCubeDepth(const Image *image,CubeInfo *cube_info,
2405 const NodeInfo *node_info)
2406{
cristybb503372010-05-27 20:51:26 +00002407 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002408 i;
2409
cristybb503372010-05-27 20:51:26 +00002410 size_t
cristy3ed852e2009-09-05 21:47:34 +00002411 number_children;
2412
2413 /*
2414 Traverse any children.
2415 */
2416 number_children=cube_info->associate_alpha == MagickFalse ? 8UL : 16UL;
cristybb503372010-05-27 20:51:26 +00002417 for (i=0; i < (ssize_t) number_children; i++)
cristy3ed852e2009-09-05 21:47:34 +00002418 if (node_info->child[i] != (NodeInfo *) NULL)
2419 PruneToCubeDepth(image,cube_info,node_info->child[i]);
2420 if (node_info->level > cube_info->depth)
2421 PruneChild(image,cube_info,node_info);
2422}
2423
2424/*
2425%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2426% %
2427% %
2428% %
2429% Q u a n t i z e I m a g e %
2430% %
2431% %
2432% %
2433%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2434%
2435% QuantizeImage() analyzes the colors within a reference image and chooses a
2436% fixed number of colors to represent the image. The goal of the algorithm
2437% is to minimize the color difference between the input and output image while
2438% minimizing the processing time.
2439%
2440% The format of the QuantizeImage method is:
2441%
2442% MagickBooleanType QuantizeImage(const QuantizeInfo *quantize_info,
2443% Image *image)
2444%
2445% A description of each parameter follows:
2446%
2447% o quantize_info: Specifies a pointer to an QuantizeInfo structure.
2448%
2449% o image: the image.
2450%
2451*/
cristy0157aea2010-04-24 21:12:18 +00002452static MagickBooleanType DirectToColormapImage(Image *image,
2453 ExceptionInfo *exception)
2454{
2455 CacheView
2456 *image_view;
2457
cristybb503372010-05-27 20:51:26 +00002458 ssize_t
cristy0157aea2010-04-24 21:12:18 +00002459 y;
2460
2461 MagickBooleanType
2462 status;
2463
cristybb503372010-05-27 20:51:26 +00002464 register ssize_t
cristy0157aea2010-04-24 21:12:18 +00002465 i;
2466
cristybb503372010-05-27 20:51:26 +00002467 size_t
cristy0157aea2010-04-24 21:12:18 +00002468 number_colors;
2469
2470 status=MagickTrue;
cristybb503372010-05-27 20:51:26 +00002471 number_colors=(size_t) (image->columns*image->rows);
cristy0157aea2010-04-24 21:12:18 +00002472 if (AcquireImageColormap(image,number_colors) == MagickFalse)
2473 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
2474 image->filename);
2475 i=0;
2476 image_view=AcquireCacheView(image);
cristybb503372010-05-27 20:51:26 +00002477 for (y=0; y < (ssize_t) image->rows; y++)
cristy0157aea2010-04-24 21:12:18 +00002478 {
cristy07a20312010-04-25 00:36:07 +00002479 MagickBooleanType
2480 proceed;
2481
2482 register IndexPacket
2483 *restrict indexes;
2484
2485 register PixelPacket
2486 *restrict q;
cristy0157aea2010-04-24 21:12:18 +00002487
cristybb503372010-05-27 20:51:26 +00002488 register ssize_t
cristy0157aea2010-04-24 21:12:18 +00002489 x;
2490
cristy07a20312010-04-25 00:36:07 +00002491 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2492 if (q == (const PixelPacket *) NULL)
cristy0157aea2010-04-24 21:12:18 +00002493 break;
cristy07a20312010-04-25 00:36:07 +00002494 indexes=GetCacheViewAuthenticIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00002495 for (x=0; x < (ssize_t) image->columns; x++)
cristy07a20312010-04-25 00:36:07 +00002496 {
cristycee97112010-05-28 00:44:52 +00002497 indexes[x]=(IndexPacket) i;
cristy07a20312010-04-25 00:36:07 +00002498 image->colormap[i++]=(*q++);
2499 }
2500 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2501 break;
cristycee97112010-05-28 00:44:52 +00002502 proceed=SetImageProgress(image,AssignImageTag,(MagickOffsetType) y,
2503 image->rows);
cristy07a20312010-04-25 00:36:07 +00002504 if (proceed == MagickFalse)
2505 status=MagickFalse;
cristy0157aea2010-04-24 21:12:18 +00002506 }
2507 image_view=DestroyCacheView(image_view);
2508 return(status);
2509}
2510
cristy3ed852e2009-09-05 21:47:34 +00002511MagickExport MagickBooleanType QuantizeImage(const QuantizeInfo *quantize_info,
2512 Image *image)
2513{
2514 CubeInfo
2515 *cube_info;
2516
2517 MagickBooleanType
2518 status;
2519
cristybb503372010-05-27 20:51:26 +00002520 size_t
cristy3ed852e2009-09-05 21:47:34 +00002521 depth,
2522 maximum_colors;
2523
2524 assert(quantize_info != (const QuantizeInfo *) NULL);
2525 assert(quantize_info->signature == MagickSignature);
2526 assert(image != (Image *) NULL);
2527 assert(image->signature == MagickSignature);
2528 if (image->debug != MagickFalse)
2529 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2530 maximum_colors=quantize_info->number_colors;
2531 if (maximum_colors == 0)
2532 maximum_colors=MaxColormapSize;
2533 if (maximum_colors > MaxColormapSize)
2534 maximum_colors=MaxColormapSize;
cristy4ccd4c02010-04-25 00:43:15 +00002535 if ((image->columns*image->rows) <= maximum_colors)
2536 return(DirectToColormapImage(image,&image->exception));
cristy3ed852e2009-09-05 21:47:34 +00002537 if ((IsGrayImage(image,&image->exception) != MagickFalse) &&
2538 (image->matte == MagickFalse))
2539 (void) SetGrayscaleImage(image);
2540 if ((image->storage_class == PseudoClass) &&
2541 (image->colors <= maximum_colors))
2542 return(MagickTrue);
2543 depth=quantize_info->tree_depth;
2544 if (depth == 0)
2545 {
cristybb503372010-05-27 20:51:26 +00002546 size_t
cristy3ed852e2009-09-05 21:47:34 +00002547 colors;
2548
2549 /*
2550 Depth of color tree is: Log4(colormap size)+2.
2551 */
2552 colors=maximum_colors;
2553 for (depth=1; colors != 0; depth++)
2554 colors>>=2;
2555 if ((quantize_info->dither != MagickFalse) && (depth > 2))
2556 depth--;
2557 if ((image->matte != MagickFalse) && (depth > 5))
2558 depth--;
2559 }
2560 /*
2561 Initialize color cube.
2562 */
2563 cube_info=GetCubeInfo(quantize_info,depth,maximum_colors);
2564 if (cube_info == (CubeInfo *) NULL)
2565 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
2566 image->filename);
2567 status=ClassifyImageColors(cube_info,image,&image->exception);
2568 if (status != MagickFalse)
2569 {
2570 /*
2571 Reduce the number of colors in the image.
2572 */
2573 ReduceImageColors(image,cube_info);
2574 status=AssignImageColors(image,cube_info);
2575 }
2576 DestroyCubeInfo(cube_info);
2577 return(status);
2578}
2579
2580/*
2581%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2582% %
2583% %
2584% %
2585% Q u a n t i z e I m a g e s %
2586% %
2587% %
2588% %
2589%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2590%
2591% QuantizeImages() analyzes the colors within a set of reference images and
2592% chooses a fixed number of colors to represent the set. The goal of the
2593% algorithm is to minimize the color difference between the input and output
2594% images while minimizing the processing time.
2595%
2596% The format of the QuantizeImages method is:
2597%
2598% MagickBooleanType QuantizeImages(const QuantizeInfo *quantize_info,
2599% Image *images)
2600%
2601% A description of each parameter follows:
2602%
2603% o quantize_info: Specifies a pointer to an QuantizeInfo structure.
2604%
2605% o images: Specifies a pointer to a list of Image structures.
2606%
2607*/
2608MagickExport MagickBooleanType QuantizeImages(const QuantizeInfo *quantize_info,
2609 Image *images)
2610{
2611 CubeInfo
2612 *cube_info;
2613
2614 Image
2615 *image;
2616
2617 MagickBooleanType
2618 proceed,
2619 status;
2620
2621 MagickProgressMonitor
2622 progress_monitor;
2623
cristybb503372010-05-27 20:51:26 +00002624 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002625 i;
2626
cristybb503372010-05-27 20:51:26 +00002627 size_t
cristy3ed852e2009-09-05 21:47:34 +00002628 depth,
2629 maximum_colors,
2630 number_images;
2631
2632 assert(quantize_info != (const QuantizeInfo *) NULL);
2633 assert(quantize_info->signature == MagickSignature);
2634 assert(images != (Image *) NULL);
2635 assert(images->signature == MagickSignature);
2636 if (images->debug != MagickFalse)
2637 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
2638 if (GetNextImageInList(images) == (Image *) NULL)
2639 {
2640 /*
2641 Handle a single image with QuantizeImage.
2642 */
2643 status=QuantizeImage(quantize_info,images);
2644 return(status);
2645 }
2646 status=MagickFalse;
2647 maximum_colors=quantize_info->number_colors;
2648 if (maximum_colors == 0)
2649 maximum_colors=MaxColormapSize;
2650 if (maximum_colors > MaxColormapSize)
2651 maximum_colors=MaxColormapSize;
2652 depth=quantize_info->tree_depth;
2653 if (depth == 0)
2654 {
cristybb503372010-05-27 20:51:26 +00002655 size_t
cristy3ed852e2009-09-05 21:47:34 +00002656 colors;
2657
2658 /*
2659 Depth of color tree is: Log4(colormap size)+2.
2660 */
2661 colors=maximum_colors;
2662 for (depth=1; colors != 0; depth++)
2663 colors>>=2;
2664 if (quantize_info->dither != MagickFalse)
2665 depth--;
2666 }
2667 /*
2668 Initialize color cube.
2669 */
2670 cube_info=GetCubeInfo(quantize_info,depth,maximum_colors);
2671 if (cube_info == (CubeInfo *) NULL)
2672 {
2673 (void) ThrowMagickException(&images->exception,GetMagickModule(),
2674 ResourceLimitError,"MemoryAllocationFailed","`%s'",images->filename);
2675 return(MagickFalse);
2676 }
2677 number_images=GetImageListLength(images);
2678 image=images;
2679 for (i=0; image != (Image *) NULL; i++)
2680 {
2681 progress_monitor=SetImageProgressMonitor(image,(MagickProgressMonitor) NULL,
2682 image->client_data);
2683 status=ClassifyImageColors(cube_info,image,&image->exception);
2684 if (status == MagickFalse)
2685 break;
2686 (void) SetImageProgressMonitor(image,progress_monitor,image->client_data);
cristycee97112010-05-28 00:44:52 +00002687 proceed=SetImageProgress(image,AssignImageTag,(MagickOffsetType) i,
2688 number_images);
cristy3ed852e2009-09-05 21:47:34 +00002689 if (proceed == MagickFalse)
2690 break;
2691 image=GetNextImageInList(image);
2692 }
2693 if (status != MagickFalse)
2694 {
2695 /*
2696 Reduce the number of colors in an image sequence.
2697 */
2698 ReduceImageColors(images,cube_info);
2699 image=images;
2700 for (i=0; image != (Image *) NULL; i++)
2701 {
2702 progress_monitor=SetImageProgressMonitor(image,(MagickProgressMonitor)
2703 NULL,image->client_data);
2704 status=AssignImageColors(image,cube_info);
2705 if (status == MagickFalse)
2706 break;
2707 (void) SetImageProgressMonitor(image,progress_monitor,
2708 image->client_data);
cristycee97112010-05-28 00:44:52 +00002709 proceed=SetImageProgress(image,AssignImageTag,(MagickOffsetType) i,
2710 number_images);
cristy3ed852e2009-09-05 21:47:34 +00002711 if (proceed == MagickFalse)
2712 break;
2713 image=GetNextImageInList(image);
2714 }
2715 }
2716 DestroyCubeInfo(cube_info);
2717 return(status);
2718}
2719
2720/*
2721%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2722% %
2723% %
2724% %
2725+ R e d u c e %
2726% %
2727% %
2728% %
2729%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2730%
2731% Reduce() traverses the color cube tree and prunes any node whose
2732% quantization error falls below a particular threshold.
2733%
2734% The format of the Reduce method is:
2735%
2736% Reduce(const Image *image,CubeInfo *cube_info,const NodeInfo *node_info)
2737%
2738% A description of each parameter follows.
2739%
2740% o image: the image.
2741%
2742% o cube_info: A pointer to the Cube structure.
2743%
2744% o node_info: pointer to node in color cube tree that is to be pruned.
2745%
2746*/
2747static void Reduce(const Image *image,CubeInfo *cube_info,
2748 const NodeInfo *node_info)
2749{
cristybb503372010-05-27 20:51:26 +00002750 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002751 i;
2752
cristybb503372010-05-27 20:51:26 +00002753 size_t
cristy3ed852e2009-09-05 21:47:34 +00002754 number_children;
2755
2756 /*
2757 Traverse any children.
2758 */
2759 number_children=cube_info->associate_alpha == MagickFalse ? 8UL : 16UL;
cristybb503372010-05-27 20:51:26 +00002760 for (i=0; i < (ssize_t) number_children; i++)
cristy3ed852e2009-09-05 21:47:34 +00002761 if (node_info->child[i] != (NodeInfo *) NULL)
2762 Reduce(image,cube_info,node_info->child[i]);
2763 if (node_info->quantize_error <= cube_info->pruning_threshold)
2764 PruneChild(image,cube_info,node_info);
2765 else
2766 {
2767 /*
2768 Find minimum pruning threshold.
2769 */
2770 if (node_info->number_unique > 0)
2771 cube_info->colors++;
2772 if (node_info->quantize_error < cube_info->next_threshold)
2773 cube_info->next_threshold=node_info->quantize_error;
2774 }
2775}
2776
2777/*
2778%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2779% %
2780% %
2781% %
2782+ R e d u c e I m a g e C o l o r s %
2783% %
2784% %
2785% %
2786%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2787%
2788% ReduceImageColors() repeatedly prunes the tree until the number of nodes
2789% with n2 > 0 is less than or equal to the maximum number of colors allowed
2790% in the output image. On any given iteration over the tree, it selects
2791% those nodes whose E value is minimal for pruning and merges their
2792% color statistics upward. It uses a pruning threshold, Ep, to govern
2793% node selection as follows:
2794%
2795% Ep = 0
2796% while number of nodes with (n2 > 0) > required maximum number of colors
2797% prune all nodes such that E <= Ep
2798% Set Ep to minimum E in remaining nodes
2799%
2800% This has the effect of minimizing any quantization error when merging
2801% two nodes together.
2802%
2803% When a node to be pruned has offspring, the pruning procedure invokes
2804% itself recursively in order to prune the tree from the leaves upward.
2805% n2, Sr, Sg, and Sb in a node being pruned are always added to the
2806% corresponding data in that node's parent. This retains the pruned
2807% node's color characteristics for later averaging.
2808%
2809% For each node, n2 pixels exist for which that node represents the
2810% smallest volume in RGB space containing those pixel's colors. When n2
2811% > 0 the node will uniquely define a color in the output image. At the
2812% beginning of reduction, n2 = 0 for all nodes except a the leaves of
2813% the tree which represent colors present in the input image.
2814%
2815% The other pixel count, n1, indicates the total number of colors
2816% within the cubic volume which the node represents. This includes n1 -
2817% n2 pixels whose colors should be defined by nodes at a lower level in
2818% the tree.
2819%
2820% The format of the ReduceImageColors method is:
2821%
2822% ReduceImageColors(const Image *image,CubeInfo *cube_info)
2823%
2824% A description of each parameter follows.
2825%
2826% o image: the image.
2827%
2828% o cube_info: A pointer to the Cube structure.
2829%
2830*/
2831static void ReduceImageColors(const Image *image,CubeInfo *cube_info)
2832{
2833#define ReduceImageTag "Reduce/Image"
2834
2835 MagickBooleanType
2836 proceed;
2837
2838 MagickOffsetType
2839 offset;
2840
cristybb503372010-05-27 20:51:26 +00002841 size_t
cristy3ed852e2009-09-05 21:47:34 +00002842 span;
2843
2844 cube_info->next_threshold=0.0;
2845 for (span=cube_info->colors; cube_info->colors > cube_info->maximum_colors; )
2846 {
2847 cube_info->pruning_threshold=cube_info->next_threshold;
2848 cube_info->next_threshold=cube_info->root->quantize_error-1;
2849 cube_info->colors=0;
2850 Reduce(image,cube_info,cube_info->root);
2851 offset=(MagickOffsetType) span-cube_info->colors;
2852 proceed=SetImageProgress(image,ReduceImageTag,offset,span-
2853 cube_info->maximum_colors+1);
2854 if (proceed == MagickFalse)
2855 break;
2856 }
2857}
2858
2859/*
2860%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2861% %
2862% %
2863% %
2864% R e m a p I m a g e %
2865% %
2866% %
2867% %
2868%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2869%
2870% RemapImage() replaces the colors of an image with the closest color from
2871% a reference image.
2872%
2873% The format of the RemapImage method is:
2874%
2875% MagickBooleanType RemapImage(const QuantizeInfo *quantize_info,
2876% Image *image,const Image *remap_image)
2877%
2878% A description of each parameter follows:
2879%
2880% o quantize_info: Specifies a pointer to an QuantizeInfo structure.
2881%
2882% o image: the image.
2883%
2884% o remap_image: the reference image.
2885%
2886*/
2887MagickExport MagickBooleanType RemapImage(const QuantizeInfo *quantize_info,
2888 Image *image,const Image *remap_image)
2889{
2890 CubeInfo
2891 *cube_info;
2892
2893 MagickBooleanType
2894 status;
2895
2896 /*
2897 Initialize color cube.
2898 */
2899 assert(image != (Image *) NULL);
2900 assert(image->signature == MagickSignature);
2901 if (image->debug != MagickFalse)
2902 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2903 assert(remap_image != (Image *) NULL);
2904 assert(remap_image->signature == MagickSignature);
2905 cube_info=GetCubeInfo(quantize_info,MaxTreeDepth,
2906 quantize_info->number_colors);
2907 if (cube_info == (CubeInfo *) NULL)
2908 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
2909 image->filename);
2910 status=ClassifyImageColors(cube_info,remap_image,&image->exception);
2911 if (status != MagickFalse)
2912 {
2913 /*
2914 Classify image colors from the reference image.
2915 */
2916 cube_info->quantize_info->number_colors=cube_info->colors;
2917 status=AssignImageColors(image,cube_info);
2918 }
2919 DestroyCubeInfo(cube_info);
2920 return(status);
2921}
2922
2923/*
2924%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2925% %
2926% %
2927% %
2928% R e m a p I m a g e s %
2929% %
2930% %
2931% %
2932%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2933%
2934% RemapImages() replaces the colors of a sequence of images with the
2935% closest color from a reference image.
2936%
2937% The format of the RemapImage method is:
2938%
2939% MagickBooleanType RemapImages(const QuantizeInfo *quantize_info,
2940% Image *images,Image *remap_image)
2941%
2942% A description of each parameter follows:
2943%
2944% o quantize_info: Specifies a pointer to an QuantizeInfo structure.
2945%
2946% o images: the image sequence.
2947%
2948% o remap_image: the reference image.
2949%
2950*/
2951MagickExport MagickBooleanType RemapImages(const QuantizeInfo *quantize_info,
2952 Image *images,const Image *remap_image)
2953{
2954 CubeInfo
2955 *cube_info;
2956
2957 Image
2958 *image;
2959
2960 MagickBooleanType
2961 status;
2962
2963 assert(images != (Image *) NULL);
2964 assert(images->signature == MagickSignature);
2965 if (images->debug != MagickFalse)
2966 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
2967 image=images;
2968 if (remap_image == (Image *) NULL)
2969 {
2970 /*
2971 Create a global colormap for an image sequence.
2972 */
2973 status=QuantizeImages(quantize_info,images);
2974 return(status);
2975 }
2976 /*
2977 Classify image colors from the reference image.
2978 */
2979 cube_info=GetCubeInfo(quantize_info,MaxTreeDepth,
2980 quantize_info->number_colors);
2981 if (cube_info == (CubeInfo *) NULL)
2982 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
2983 image->filename);
2984 status=ClassifyImageColors(cube_info,remap_image,&image->exception);
2985 if (status != MagickFalse)
2986 {
2987 /*
2988 Classify image colors from the reference image.
2989 */
2990 cube_info->quantize_info->number_colors=cube_info->colors;
2991 image=images;
2992 for ( ; image != (Image *) NULL; image=GetNextImageInList(image))
2993 {
2994 status=AssignImageColors(image,cube_info);
2995 if (status == MagickFalse)
2996 break;
2997 }
2998 }
2999 DestroyCubeInfo(cube_info);
3000 return(status);
3001}
3002
3003/*
3004%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3005% %
3006% %
3007% %
3008% S e t G r a y s c a l e I m a g e %
3009% %
3010% %
3011% %
3012%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3013%
3014% SetGrayscaleImage() converts an image to a PseudoClass grayscale image.
3015%
3016% The format of the SetGrayscaleImage method is:
3017%
3018% MagickBooleanType SetGrayscaleImage(Image *image)
3019%
3020% A description of each parameter follows:
3021%
3022% o image: The image.
3023%
3024*/
3025
3026#if defined(__cplusplus) || defined(c_plusplus)
3027extern "C" {
3028#endif
3029
3030static int IntensityCompare(const void *x,const void *y)
3031{
cristybb503372010-05-27 20:51:26 +00003032 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003033 intensity;
3034
3035 PixelPacket
3036 *color_1,
3037 *color_2;
3038
3039 color_1=(PixelPacket *) x;
3040 color_2=(PixelPacket *) y;
cristybb503372010-05-27 20:51:26 +00003041 intensity=PixelIntensityToQuantum(color_1)-(ssize_t)
cristy3ed852e2009-09-05 21:47:34 +00003042 PixelIntensityToQuantum(color_2);
cristycee97112010-05-28 00:44:52 +00003043 return((int) intensity);
cristy3ed852e2009-09-05 21:47:34 +00003044}
3045
3046#if defined(__cplusplus) || defined(c_plusplus)
3047}
3048#endif
3049
3050static MagickBooleanType SetGrayscaleImage(Image *image)
3051{
cristyc4c8d132010-01-07 01:58:38 +00003052 CacheView
3053 *image_view;
3054
cristy3ed852e2009-09-05 21:47:34 +00003055 ExceptionInfo
3056 *exception;
3057
cristybb503372010-05-27 20:51:26 +00003058 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003059 j,
3060 y;
3061
3062 PixelPacket
3063 *colormap;
3064
cristybb503372010-05-27 20:51:26 +00003065 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003066 *colormap_index;
3067
cristybb503372010-05-27 20:51:26 +00003068 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003069 i;
3070
3071 MagickBooleanType
3072 status;
3073
cristy3ed852e2009-09-05 21:47:34 +00003074 assert(image != (Image *) NULL);
3075 assert(image->signature == MagickSignature);
3076 if (image->type != GrayscaleType)
3077 (void) TransformImageColorspace(image,GRAYColorspace);
cristybb503372010-05-27 20:51:26 +00003078 colormap_index=(ssize_t *) AcquireQuantumMemory(MaxMap+1,
cristy3ed852e2009-09-05 21:47:34 +00003079 sizeof(*colormap_index));
cristybb503372010-05-27 20:51:26 +00003080 if (colormap_index == (ssize_t *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003081 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
3082 image->filename);
3083 if (image->storage_class != PseudoClass)
3084 {
3085 ExceptionInfo
3086 *exception;
3087
cristybb503372010-05-27 20:51:26 +00003088 for (i=0; i <= (ssize_t) MaxMap; i++)
cristy3ed852e2009-09-05 21:47:34 +00003089 colormap_index[i]=(-1);
3090 if (AcquireImageColormap(image,MaxMap+1) == MagickFalse)
3091 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
3092 image->filename);
3093 image->colors=0;
3094 status=MagickTrue;
3095 exception=(&image->exception);
3096 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +00003097#if defined(MAGICKCORE_OPENMP_SUPPORT)
3098 #pragma omp parallel for schedule(dynamic,4) shared(status)
cristy3ed852e2009-09-05 21:47:34 +00003099#endif
cristybb503372010-05-27 20:51:26 +00003100 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003101 {
3102 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00003103 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00003104
cristybb503372010-05-27 20:51:26 +00003105 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003106 x;
3107
3108 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00003109 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003110
3111 if (status == MagickFalse)
3112 continue;
3113 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
3114 exception);
3115 if (q == (PixelPacket *) NULL)
3116 {
3117 status=MagickFalse;
3118 continue;
3119 }
3120 indexes=GetCacheViewAuthenticIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00003121 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003122 {
cristybb503372010-05-27 20:51:26 +00003123 register size_t
cristy3ed852e2009-09-05 21:47:34 +00003124 intensity;
3125
3126 intensity=ScaleQuantumToMap(q->red);
3127 if (colormap_index[intensity] < 0)
3128 {
cristyb5d5f722009-11-04 03:03:49 +00003129#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00003130 #pragma omp critical (MagickCore_SetGrayscaleImage)
3131#endif
3132 if (colormap_index[intensity] < 0)
3133 {
cristybb503372010-05-27 20:51:26 +00003134 colormap_index[intensity]=(ssize_t) image->colors;
cristy3ed852e2009-09-05 21:47:34 +00003135 image->colormap[image->colors]=(*q);
3136 image->colors++;
3137 }
3138 }
3139 indexes[x]=(IndexPacket) colormap_index[intensity];
3140 q++;
3141 }
3142 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3143 status=MagickFalse;
3144 }
3145 image_view=DestroyCacheView(image_view);
3146 }
cristybb503372010-05-27 20:51:26 +00003147 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00003148 image->colormap[i].opacity=(unsigned short) i;
3149 qsort((void *) image->colormap,image->colors,sizeof(PixelPacket),
3150 IntensityCompare);
3151 colormap=(PixelPacket *) AcquireQuantumMemory(image->colors,
3152 sizeof(*colormap));
3153 if (colormap == (PixelPacket *) NULL)
3154 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
3155 image->filename);
3156 j=0;
3157 colormap[j]=image->colormap[0];
cristybb503372010-05-27 20:51:26 +00003158 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00003159 {
3160 if (IsSameColor(image,&colormap[j],&image->colormap[i]) == MagickFalse)
3161 {
3162 j++;
3163 colormap[j]=image->colormap[i];
3164 }
cristybb503372010-05-27 20:51:26 +00003165 colormap_index[(ssize_t) image->colormap[i].opacity]=j;
cristy3ed852e2009-09-05 21:47:34 +00003166 }
cristybb503372010-05-27 20:51:26 +00003167 image->colors=(size_t) (j+1);
cristy3ed852e2009-09-05 21:47:34 +00003168 image->colormap=(PixelPacket *) RelinquishMagickMemory(image->colormap);
3169 image->colormap=colormap;
3170 status=MagickTrue;
3171 exception=(&image->exception);
3172 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +00003173#if defined(MAGICKCORE_OPENMP_SUPPORT)
3174 #pragma omp parallel for schedule(dynamic,4) shared(status)
cristy3ed852e2009-09-05 21:47:34 +00003175#endif
cristybb503372010-05-27 20:51:26 +00003176 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003177 {
3178 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00003179 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00003180
cristybb503372010-05-27 20:51:26 +00003181 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003182 x;
3183
3184 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00003185 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003186
3187 if (status == MagickFalse)
3188 continue;
3189 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3190 if (q == (PixelPacket *) NULL)
3191 {
3192 status=MagickFalse;
3193 continue;
3194 }
3195 indexes=GetCacheViewAuthenticIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00003196 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003197 indexes[x]=(IndexPacket) colormap_index[ScaleQuantumToMap(indexes[x])];
3198 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3199 status=MagickFalse;
3200 }
3201 image_view=DestroyCacheView(image_view);
cristybb503372010-05-27 20:51:26 +00003202 colormap_index=(ssize_t *) RelinquishMagickMemory(colormap_index);
cristy3ed852e2009-09-05 21:47:34 +00003203 image->type=GrayscaleType;
3204 if (IsMonochromeImage(image,&image->exception) != MagickFalse)
3205 image->type=BilevelType;
3206 return(status);
3207}