blob: 687bd8329793cc852fc7621b3c1d6109db7b0a68 [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
235 unsigned long
236 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
255 unsigned long
256 colors,
257 maximum_colors;
258
259 long
260 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
273 unsigned long
274 nodes,
275 free_nodes,
276 color_number;
277
278 NodeInfo
279 *next_node;
280
281 Nodes
282 *node_queue;
283
284 long
285 *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
299 long
300 x,
301 y;
302
303 unsigned long
304 depth;
305
306 MagickOffsetType
307 offset;
308
309 MagickSizeType
310 span;
311} CubeInfo;
312
313/*
314 Method prototypes.
315*/
316static CubeInfo
317 *GetCubeInfo(const QuantizeInfo *,const unsigned long,const unsigned long);
318
319static NodeInfo
320 *GetNodeInfo(CubeInfo *,const unsigned long,const unsigned long,NodeInfo *);
321
322static MagickBooleanType
323 AssignImageColors(Image *,CubeInfo *),
324 ClassifyImageColors(CubeInfo *,const Image *,ExceptionInfo *),
325 DitherImage(Image *,CubeInfo *),
326 SetGrayscaleImage(Image *);
327
328static unsigned long
329 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
455static inline unsigned long ColorToNodeId(const CubeInfo *cube_info,
456 const RealPixelPacket *pixel,unsigned long index)
457{
458 unsigned long
459 id;
460
461 id=(unsigned long) (
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
485 long
486 y;
487
488 MagickBooleanType
489 proceed;
490
491 RealPixelPacket
492 pixel;
493
494 register long
495 i,
496 x;
497
498 register const NodeInfo
499 *node_info;
500
501 ssize_t
502 count;
503
504 unsigned long
505 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) &&
531 (cube_info->quantize_info->dither_method != NoDitherMethod))
532 (void) DitherImage(image,cube_info);
533 else
534 {
535 ExceptionInfo
536 *exception;
537
538 CacheView
539 *image_view;
540
541 exception=(&image->exception);
542 image_view=AcquireCacheView(image);
543 for (y=0; y < (long) image->rows; y++)
544 {
545 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000546 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +0000547
548 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000549 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000550
551 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
552 exception);
553 if (q == (PixelPacket *) NULL)
554 break;
555 indexes=GetCacheViewAuthenticIndexQueue(image_view);
556 for (x=0; x < (long) image->columns; x+=count)
557 {
558 /*
559 Identify the deepest node containing the pixel's color.
560 */
561 for (count=1; (x+count) < (long) image->columns; count++)
562 if (IsSameColor(image,q,q+count) == MagickFalse)
563 break;
564 AssociateAlphaPixel(cube_info,q,&pixel);
565 node_info=cube_info->root;
566 for (index=MaxTreeDepth-1; (long) index > 0; index--)
567 {
568 id=ColorToNodeId(cube_info,&pixel,index);
569 if (node_info->child[id] == (NodeInfo *) NULL)
570 break;
571 node_info=node_info->child[id];
572 }
573 /*
574 Find closest color among siblings and their children.
575 */
576 cube_info->target=pixel;
577 cube_info->distance=(MagickRealType) (4.0*(QuantumRange+1.0)*
578 (QuantumRange+1.0)+1.0);
579 ClosestColor(image,cube_info,node_info->parent);
580 index=cube_info->color_number;
581 for (i=0; i < (long) count; i++)
582 {
583 if (image->storage_class == PseudoClass)
584 indexes[x+i]=(IndexPacket) index;
585 if (cube_info->quantize_info->measure_error == MagickFalse)
586 {
587 q->red=image->colormap[index].red;
588 q->green=image->colormap[index].green;
589 q->blue=image->colormap[index].blue;
590 if (cube_info->associate_alpha != MagickFalse)
591 q->opacity=image->colormap[index].opacity;
592 }
593 q++;
594 }
595 }
596 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
597 break;
598 proceed=SetImageProgress(image,AssignImageTag,y,image->rows);
599 if (proceed == MagickFalse)
600 break;
601 }
602 image_view=DestroyCacheView(image_view);
603 }
604 if (cube_info->quantize_info->measure_error != MagickFalse)
605 (void) GetImageQuantizeError(image);
606 if ((cube_info->quantize_info->number_colors == 2) &&
607 (cube_info->quantize_info->colorspace == GRAYColorspace))
608 {
609 Quantum
610 intensity;
611
612 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000613 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000614
615 /*
616 Monochrome image.
617 */
618 q=image->colormap;
619 for (i=0; i < (long) image->colors; i++)
620 {
621 intensity=(Quantum) (PixelIntensity(q) < ((MagickRealType)
622 QuantumRange/2.0) ? 0 : QuantumRange);
623 q->red=intensity;
624 q->green=intensity;
625 q->blue=intensity;
626 q++;
627 }
628 }
629 (void) SyncImage(image);
630 if ((cube_info->quantize_info->colorspace != UndefinedColorspace) &&
631 (cube_info->quantize_info->colorspace != CMYKColorspace))
632 (void) TransformImageColorspace((Image *) image,RGBColorspace);
633 return(MagickTrue);
634}
635
636/*
637%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
638% %
639% %
640% %
641+ C l a s s i f y I m a g e C o l o r s %
642% %
643% %
644% %
645%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
646%
647% ClassifyImageColors() begins by initializing a color description tree
648% of sufficient depth to represent each possible input color in a leaf.
649% However, it is impractical to generate a fully-formed color
650% description tree in the storage_class phase for realistic values of
651% Cmax. If colors components in the input image are quantized to k-bit
652% precision, so that Cmax= 2k-1, the tree would need k levels below the
653% root node to allow representing each possible input color in a leaf.
654% This becomes prohibitive because the tree's total number of nodes is
655% 1 + sum(i=1,k,8k).
656%
657% A complete tree would require 19,173,961 nodes for k = 8, Cmax = 255.
658% Therefore, to avoid building a fully populated tree, QUANTIZE: (1)
659% Initializes data structures for nodes only as they are needed; (2)
660% Chooses a maximum depth for the tree as a function of the desired
661% number of colors in the output image (currently log2(colormap size)).
662%
663% For each pixel in the input image, storage_class scans downward from
664% the root of the color description tree. At each level of the tree it
665% identifies the single node which represents a cube in RGB space
666% containing It updates the following data for each such node:
667%
668% n1 : Number of pixels whose color is contained in the RGB cube
669% which this node represents;
670%
671% n2 : Number of pixels whose color is not represented in a node at
672% lower depth in the tree; initially, n2 = 0 for all nodes except
673% leaves of the tree.
674%
675% Sr, Sg, Sb : Sums of the red, green, and blue component values for
676% all pixels not classified at a lower depth. The combination of
677% these sums and n2 will ultimately characterize the mean color of a
678% set of pixels represented by this node.
679%
680% E: the distance squared in RGB space between each pixel contained
681% within a node and the nodes' center. This represents the quantization
682% error for a node.
683%
684% The format of the ClassifyImageColors() method is:
685%
686% MagickBooleanType ClassifyImageColors(CubeInfo *cube_info,
687% const Image *image,ExceptionInfo *exception)
688%
689% A description of each parameter follows.
690%
691% o cube_info: A pointer to the Cube structure.
692%
693% o image: the image.
694%
695*/
696
697static inline void SetAssociatedAlpha(const Image *image,CubeInfo *cube_info)
698{
699 MagickBooleanType
700 associate_alpha;
701
702 associate_alpha=image->matte;
703 if (cube_info->quantize_info->colorspace == TransparentColorspace)
704 associate_alpha=MagickFalse;
705 if ((cube_info->quantize_info->number_colors == 2) &&
706 (cube_info->quantize_info->colorspace == GRAYColorspace))
707 associate_alpha=MagickFalse;
708 cube_info->associate_alpha=associate_alpha;
709}
710
711static MagickBooleanType ClassifyImageColors(CubeInfo *cube_info,
712 const Image *image,ExceptionInfo *exception)
713{
714#define ClassifyImageTag "Classify/Image"
715
cristyc4c8d132010-01-07 01:58:38 +0000716 CacheView
717 *image_view;
718
cristy3ed852e2009-09-05 21:47:34 +0000719 long
720 y;
721
722 MagickBooleanType
723 proceed;
724
725 MagickRealType
726 bisect;
727
728 NodeInfo
729 *node_info;
730
731 RealPixelPacket
732 error,
733 mid,
734 midpoint,
735 pixel;
736
737 size_t
738 count;
739
740 unsigned long
741 id,
742 index,
743 level;
744
cristy3ed852e2009-09-05 21:47:34 +0000745 /*
746 Classify the first cube_info->maximum_colors colors to a tree depth of 8.
747 */
748 SetAssociatedAlpha(image,cube_info);
749 if ((cube_info->quantize_info->colorspace != UndefinedColorspace) &&
750 (cube_info->quantize_info->colorspace != CMYKColorspace))
751 (void) TransformImageColorspace((Image *) image,
752 cube_info->quantize_info->colorspace);
753 else
754 if ((image->colorspace != GRAYColorspace) &&
755 (image->colorspace != CMYColorspace) &&
756 (image->colorspace != RGBColorspace))
757 (void) TransformImageColorspace((Image *) image,RGBColorspace);
758 midpoint.red=(MagickRealType) QuantumRange/2.0;
759 midpoint.green=(MagickRealType) QuantumRange/2.0;
760 midpoint.blue=(MagickRealType) QuantumRange/2.0;
761 midpoint.opacity=(MagickRealType) QuantumRange/2.0;
762 error.opacity=0.0;
763 image_view=AcquireCacheView(image);
764 for (y=0; y < (long) image->rows; y++)
765 {
766 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000767 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000768
769 register long
770 x;
771
772 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
773 if (p == (const PixelPacket *) NULL)
774 break;
775 if (cube_info->nodes > MaxNodes)
776 {
777 /*
778 Prune one level if the color tree is too large.
779 */
780 PruneLevel(image,cube_info,cube_info->root);
781 cube_info->depth--;
782 }
783 for (x=0; x < (long) image->columns; x+=(long) count)
784 {
785 /*
786 Start at the root and descend the color cube tree.
787 */
788 for (count=1; (x+count) < image->columns; count++)
789 if (IsSameColor(image,p,p+count) == MagickFalse)
790 break;
791 AssociateAlphaPixel(cube_info,p,&pixel);
792 index=MaxTreeDepth-1;
793 bisect=((MagickRealType) QuantumRange+1.0)/2.0;
794 mid=midpoint;
795 node_info=cube_info->root;
796 for (level=1; level <= MaxTreeDepth; level++)
797 {
798 bisect*=0.5;
799 id=ColorToNodeId(cube_info,&pixel,index);
800 mid.red+=(id & 1) != 0 ? bisect : -bisect;
801 mid.green+=(id & 2) != 0 ? bisect : -bisect;
802 mid.blue+=(id & 4) != 0 ? bisect : -bisect;
803 mid.opacity+=(id & 8) != 0 ? bisect : -bisect;
804 if (node_info->child[id] == (NodeInfo *) NULL)
805 {
806 /*
807 Set colors of new node to contain pixel.
808 */
809 node_info->child[id]=GetNodeInfo(cube_info,id,level,node_info);
810 if (node_info->child[id] == (NodeInfo *) NULL)
811 (void) ThrowMagickException(exception,GetMagickModule(),
812 ResourceLimitError,"MemoryAllocationFailed","`%s'",
813 image->filename);
814 if (level == MaxTreeDepth)
815 cube_info->colors++;
816 }
817 /*
818 Approximate the quantization error represented by this node.
819 */
820 node_info=node_info->child[id];
821 error.red=QuantumScale*(pixel.red-mid.red);
822 error.green=QuantumScale*(pixel.green-mid.green);
823 error.blue=QuantumScale*(pixel.blue-mid.blue);
824 if (cube_info->associate_alpha != MagickFalse)
825 error.opacity=QuantumScale*(pixel.opacity-mid.opacity);
826 node_info->quantize_error+=sqrt((double) (count*error.red*error.red+
827 count*error.green*error.green+count*error.blue*error.blue+
828 count*error.opacity*error.opacity));
829 cube_info->root->quantize_error+=node_info->quantize_error;
830 index--;
831 }
832 /*
833 Sum RGB for this leaf for later derivation of the mean cube color.
834 */
835 node_info->number_unique+=count;
836 node_info->total_color.red+=count*QuantumScale*pixel.red;
837 node_info->total_color.green+=count*QuantumScale*pixel.green;
838 node_info->total_color.blue+=count*QuantumScale*pixel.blue;
839 if (cube_info->associate_alpha != MagickFalse)
840 node_info->total_color.opacity+=count*QuantumScale*pixel.opacity;
841 p+=count;
842 }
843 if (cube_info->colors > cube_info->maximum_colors)
844 {
845 PruneToCubeDepth(image,cube_info,cube_info->root);
846 break;
847 }
848 proceed=SetImageProgress(image,ClassifyImageTag,y,image->rows);
849 if (proceed == MagickFalse)
850 break;
851 }
852 for (y++; y < (long) image->rows; y++)
853 {
854 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000855 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000856
857 register long
858 x;
859
860 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
861 if (p == (const PixelPacket *) NULL)
862 break;
863 if (cube_info->nodes > MaxNodes)
864 {
865 /*
866 Prune one level if the color tree is too large.
867 */
868 PruneLevel(image,cube_info,cube_info->root);
869 cube_info->depth--;
870 }
871 for (x=0; x < (long) image->columns; x+=(long) count)
872 {
873 /*
874 Start at the root and descend the color cube tree.
875 */
876 for (count=1; (x+count) < image->columns; count++)
877 if (IsSameColor(image,p,p+count) == MagickFalse)
878 break;
879 AssociateAlphaPixel(cube_info,p,&pixel);
880 index=MaxTreeDepth-1;
881 bisect=((MagickRealType) QuantumRange+1.0)/2.0;
882 mid=midpoint;
883 node_info=cube_info->root;
884 for (level=1; level <= cube_info->depth; level++)
885 {
886 bisect*=0.5;
887 id=ColorToNodeId(cube_info,&pixel,index);
888 mid.red+=(id & 1) != 0 ? bisect : -bisect;
889 mid.green+=(id & 2) != 0 ? bisect : -bisect;
890 mid.blue+=(id & 4) != 0 ? bisect : -bisect;
891 mid.opacity+=(id & 8) != 0 ? bisect : -bisect;
892 if (node_info->child[id] == (NodeInfo *) NULL)
893 {
894 /*
895 Set colors of new node to contain pixel.
896 */
897 node_info->child[id]=GetNodeInfo(cube_info,id,level,node_info);
898 if (node_info->child[id] == (NodeInfo *) NULL)
899 (void) ThrowMagickException(exception,GetMagickModule(),
900 ResourceLimitError,"MemoryAllocationFailed","%s",
901 image->filename);
902 if (level == cube_info->depth)
903 cube_info->colors++;
904 }
905 /*
906 Approximate the quantization error represented by this node.
907 */
908 node_info=node_info->child[id];
909 error.red=QuantumScale*(pixel.red-mid.red);
910 error.green=QuantumScale*(pixel.green-mid.green);
911 error.blue=QuantumScale*(pixel.blue-mid.blue);
912 if (cube_info->associate_alpha != MagickFalse)
913 error.opacity=QuantumScale*(pixel.opacity-mid.opacity);
914 node_info->quantize_error+=sqrt((double) (count*error.red*error.red+
915 count*error.green*error.green+error.blue*error.blue+
916 count*error.opacity*error.opacity));
917 cube_info->root->quantize_error+=node_info->quantize_error;
918 index--;
919 }
920 /*
921 Sum RGB for this leaf for later derivation of the mean cube color.
922 */
923 node_info->number_unique+=count;
924 node_info->total_color.red+=count*QuantumScale*pixel.red;
925 node_info->total_color.green+=count*QuantumScale*pixel.green;
926 node_info->total_color.blue+=count*QuantumScale*pixel.blue;
927 if (cube_info->associate_alpha != MagickFalse)
928 node_info->total_color.opacity+=count*QuantumScale*pixel.opacity;
929 p+=count;
930 }
931 proceed=SetImageProgress(image,ClassifyImageTag,y,image->rows);
932 if (proceed == MagickFalse)
933 break;
934 }
935 image_view=DestroyCacheView(image_view);
936 if ((cube_info->quantize_info->colorspace != UndefinedColorspace) &&
937 (cube_info->quantize_info->colorspace != CMYKColorspace))
938 (void) TransformImageColorspace((Image *) image,RGBColorspace);
939 return(MagickTrue);
940}
941
942/*
943%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
944% %
945% %
946% %
947% C l o n e Q u a n t i z e I n f o %
948% %
949% %
950% %
951%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
952%
953% CloneQuantizeInfo() makes a duplicate of the given quantize info structure,
954% or if quantize info is NULL, a new one.
955%
956% The format of the CloneQuantizeInfo method is:
957%
958% QuantizeInfo *CloneQuantizeInfo(const QuantizeInfo *quantize_info)
959%
960% A description of each parameter follows:
961%
962% o clone_info: Method CloneQuantizeInfo returns a duplicate of the given
963% quantize info, or if image info is NULL a new one.
964%
965% o quantize_info: a structure of type info.
966%
967*/
968MagickExport QuantizeInfo *CloneQuantizeInfo(const QuantizeInfo *quantize_info)
969{
970 QuantizeInfo
971 *clone_info;
972
cristy90823212009-12-12 20:48:33 +0000973 clone_info=(QuantizeInfo *) AcquireAlignedMemory(1,sizeof(*clone_info));
cristy3ed852e2009-09-05 21:47:34 +0000974 if (clone_info == (QuantizeInfo *) NULL)
975 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
976 GetQuantizeInfo(clone_info);
977 if (quantize_info == (QuantizeInfo *) NULL)
978 return(clone_info);
979 clone_info->number_colors=quantize_info->number_colors;
980 clone_info->tree_depth=quantize_info->tree_depth;
981 clone_info->dither=quantize_info->dither;
982 clone_info->dither_method=quantize_info->dither_method;
983 clone_info->colorspace=quantize_info->colorspace;
984 clone_info->measure_error=quantize_info->measure_error;
985 return(clone_info);
986}
987
988/*
989%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
990% %
991% %
992% %
993+ C l o s e s t C o l o r %
994% %
995% %
996% %
997%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
998%
999% ClosestColor() traverses the color cube tree at a particular node and
1000% determines which colormap entry best represents the input color.
1001%
1002% The format of the ClosestColor method is:
1003%
1004% void ClosestColor(const Image *image,CubeInfo *cube_info,
1005% const NodeInfo *node_info)
1006%
1007% A description of each parameter follows.
1008%
1009% o image: the image.
1010%
1011% o cube_info: A pointer to the Cube structure.
1012%
1013% o node_info: the address of a structure of type NodeInfo which points to a
1014% node in the color cube tree that is to be pruned.
1015%
1016*/
1017static void ClosestColor(const Image *image,CubeInfo *cube_info,
1018 const NodeInfo *node_info)
1019{
1020 register long
1021 i;
1022
1023 unsigned long
1024 number_children;
1025
1026 /*
1027 Traverse any children.
1028 */
1029 number_children=cube_info->associate_alpha == MagickFalse ? 8UL : 16UL;
1030 for (i=0; i < (long) number_children; i++)
1031 if (node_info->child[i] != (NodeInfo *) NULL)
1032 ClosestColor(image,cube_info,node_info->child[i]);
1033 if (node_info->number_unique != 0)
1034 {
1035 MagickRealType
1036 pixel;
1037
1038 register MagickRealType
1039 alpha,
1040 beta,
1041 distance;
1042
1043 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001044 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00001045
1046 register RealPixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001047 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001048
1049 /*
1050 Determine if this color is "closest".
1051 */
1052 p=image->colormap+node_info->color_number;
1053 q=(&cube_info->target);
1054 alpha=1.0;
1055 beta=1.0;
1056 if (cube_info->associate_alpha == MagickFalse)
1057 {
cristy46f08202010-01-10 04:04:21 +00001058 alpha=(MagickRealType) (QuantumScale*GetAlphaPixelComponent(p));
1059 beta=(MagickRealType) (QuantumScale*GetAlphaPixelComponent(q));
cristy3ed852e2009-09-05 21:47:34 +00001060 }
1061 pixel=alpha*p->red-beta*q->red;
1062 distance=pixel*pixel;
1063 if (distance < cube_info->distance)
1064 {
1065 pixel=alpha*p->green-beta*q->green;
1066 distance+=pixel*pixel;
1067 if (distance < cube_info->distance)
1068 {
1069 pixel=alpha*p->blue-beta*q->blue;
1070 distance+=pixel*pixel;
1071 if (distance < cube_info->distance)
1072 {
1073 pixel=alpha-beta;
1074 distance+=pixel*pixel;
1075 if (distance < cube_info->distance)
1076 {
1077 cube_info->distance=distance;
1078 cube_info->color_number=node_info->color_number;
1079 }
1080 }
1081 }
1082 }
1083 }
1084}
1085
1086/*
1087%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1088% %
1089% %
1090% %
1091% C o m p r e s s I m a g e C o l o r m a p %
1092% %
1093% %
1094% %
1095%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1096%
1097% CompressImageColormap() compresses an image colormap by removing any
1098% duplicate or unused color entries.
1099%
1100% The format of the CompressImageColormap method is:
1101%
1102% MagickBooleanType CompressImageColormap(Image *image)
1103%
1104% A description of each parameter follows:
1105%
1106% o image: the image.
1107%
1108*/
1109MagickExport MagickBooleanType CompressImageColormap(Image *image)
1110{
1111 QuantizeInfo
1112 quantize_info;
1113
1114 assert(image != (Image *) NULL);
1115 assert(image->signature == MagickSignature);
1116 if (image->debug != MagickFalse)
1117 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1118 if (IsPaletteImage(image,&image->exception) == MagickFalse)
1119 return(MagickFalse);
1120 GetQuantizeInfo(&quantize_info);
1121 quantize_info.number_colors=image->colors;
1122 quantize_info.tree_depth=MaxTreeDepth;
1123 return(QuantizeImage(&quantize_info,image));
1124}
1125
1126/*
1127%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1128% %
1129% %
1130% %
1131+ D e f i n e I m a g e C o l o r m a p %
1132% %
1133% %
1134% %
1135%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1136%
1137% DefineImageColormap() traverses the color cube tree and notes each colormap
1138% entry. A colormap entry is any node in the color cube tree where the
1139% of unique colors is not zero. DefineImageColormap() returns the number of
1140% colors in the image colormap.
1141%
1142% The format of the DefineImageColormap method is:
1143%
1144% unsigned long DefineImageColormap(Image *image,CubeInfo *cube_info,
1145% NodeInfo *node_info)
1146%
1147% A description of each parameter follows.
1148%
1149% o image: the image.
1150%
1151% o cube_info: A pointer to the Cube structure.
1152%
1153% o node_info: the address of a structure of type NodeInfo which points to a
1154% node in the color cube tree that is to be pruned.
1155%
1156*/
1157static unsigned long DefineImageColormap(Image *image,CubeInfo *cube_info,
1158 NodeInfo *node_info)
1159{
1160 register long
1161 i;
1162
1163 unsigned long
1164 number_children;
1165
1166 /*
1167 Traverse any children.
1168 */
1169 number_children=cube_info->associate_alpha == MagickFalse ? 8UL : 16UL;
1170 for (i=0; i < (long) number_children; i++)
1171 if (node_info->child[i] != (NodeInfo *) NULL)
1172 DefineImageColormap(image,cube_info,node_info->child[i]);
1173 if (node_info->number_unique != 0)
1174 {
1175 register MagickRealType
1176 alpha;
1177
1178 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001179 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001180
1181 /*
1182 Colormap entry is defined by the mean color in this cube.
1183 */
1184 q=image->colormap+image->colors;
1185 alpha=(MagickRealType) ((MagickOffsetType) node_info->number_unique);
1186 alpha=1.0/(fabs(alpha) <= MagickEpsilon ? 1.0 : alpha);
1187 if (cube_info->associate_alpha == MagickFalse)
1188 {
cristyce70c172010-01-07 17:15:30 +00001189 q->red=ClampToQuantum((MagickRealType) (alpha*QuantumRange*
cristy3ed852e2009-09-05 21:47:34 +00001190 node_info->total_color.red));
cristyce70c172010-01-07 17:15:30 +00001191 q->green=ClampToQuantum((MagickRealType) (alpha*QuantumRange*
cristy3ed852e2009-09-05 21:47:34 +00001192 node_info->total_color.green));
cristyce70c172010-01-07 17:15:30 +00001193 q->blue=ClampToQuantum((MagickRealType) (alpha*QuantumRange*
cristy3ed852e2009-09-05 21:47:34 +00001194 node_info->total_color.blue));
cristyce70c172010-01-07 17:15:30 +00001195 SetOpacityPixelComponent(q,OpaqueOpacity);
cristy3ed852e2009-09-05 21:47:34 +00001196 }
1197 else
1198 {
1199 MagickRealType
1200 opacity;
1201
1202 opacity=(MagickRealType) (alpha*QuantumRange*
1203 node_info->total_color.opacity);
cristyce70c172010-01-07 17:15:30 +00001204 q->opacity=ClampToQuantum(opacity);
cristy3ed852e2009-09-05 21:47:34 +00001205 if (q->opacity == OpaqueOpacity)
1206 {
cristyce70c172010-01-07 17:15:30 +00001207 q->red=ClampToQuantum((MagickRealType) (alpha*QuantumRange*
cristy3ed852e2009-09-05 21:47:34 +00001208 node_info->total_color.red));
cristyce70c172010-01-07 17:15:30 +00001209 q->green=ClampToQuantum((MagickRealType) (alpha*QuantumRange*
cristy3ed852e2009-09-05 21:47:34 +00001210 node_info->total_color.green));
cristyce70c172010-01-07 17:15:30 +00001211 q->blue=ClampToQuantum((MagickRealType) (alpha*QuantumRange*
cristy3ed852e2009-09-05 21:47:34 +00001212 node_info->total_color.blue));
1213 }
1214 else
1215 {
1216 MagickRealType
1217 gamma;
1218
1219 gamma=(MagickRealType) (QuantumScale*(QuantumRange-
1220 (MagickRealType) q->opacity));
1221 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyce70c172010-01-07 17:15:30 +00001222 q->red=ClampToQuantum((MagickRealType) (alpha*gamma*QuantumRange*
cristy3ed852e2009-09-05 21:47:34 +00001223 node_info->total_color.red));
cristyce70c172010-01-07 17:15:30 +00001224 q->green=ClampToQuantum((MagickRealType) (alpha*gamma*
cristy3ed852e2009-09-05 21:47:34 +00001225 QuantumRange*node_info->total_color.green));
cristyce70c172010-01-07 17:15:30 +00001226 q->blue=ClampToQuantum((MagickRealType) (alpha*gamma*QuantumRange*
cristy3ed852e2009-09-05 21:47:34 +00001227 node_info->total_color.blue));
1228 if (node_info->number_unique > cube_info->transparent_pixels)
1229 {
1230 cube_info->transparent_pixels=node_info->number_unique;
1231 cube_info->transparent_index=(long) image->colors;
1232 }
1233 }
1234 }
1235 node_info->color_number=image->colors++;
1236 }
1237 return(image->colors);
1238}
1239
1240/*
1241%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1242% %
1243% %
1244% %
1245+ D e s t r o y C u b e I n f o %
1246% %
1247% %
1248% %
1249%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1250%
1251% DestroyCubeInfo() deallocates memory associated with an image.
1252%
1253% The format of the DestroyCubeInfo method is:
1254%
1255% DestroyCubeInfo(CubeInfo *cube_info)
1256%
1257% A description of each parameter follows:
1258%
1259% o cube_info: the address of a structure of type CubeInfo.
1260%
1261*/
1262static void DestroyCubeInfo(CubeInfo *cube_info)
1263{
1264 register Nodes
1265 *nodes;
1266
1267 /*
1268 Release color cube tree storage.
1269 */
1270 do
1271 {
1272 nodes=cube_info->node_queue->next;
1273 cube_info->node_queue->nodes=(NodeInfo *) RelinquishMagickMemory(
1274 cube_info->node_queue->nodes);
1275 cube_info->node_queue=(Nodes *) RelinquishMagickMemory(
1276 cube_info->node_queue);
1277 cube_info->node_queue=nodes;
1278 } while (cube_info->node_queue != (Nodes *) NULL);
1279 if (cube_info->cache != (long *) NULL)
1280 cube_info->cache=(long *) RelinquishMagickMemory(cube_info->cache);
1281 cube_info->quantize_info=DestroyQuantizeInfo(cube_info->quantize_info);
1282 cube_info=(CubeInfo *) RelinquishMagickMemory(cube_info);
1283}
1284
1285/*
1286%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1287% %
1288% %
1289% %
1290% D e s t r o y Q u a n t i z e I n f o %
1291% %
1292% %
1293% %
1294%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1295%
1296% DestroyQuantizeInfo() deallocates memory associated with an QuantizeInfo
1297% structure.
1298%
1299% The format of the DestroyQuantizeInfo method is:
1300%
1301% QuantizeInfo *DestroyQuantizeInfo(QuantizeInfo *quantize_info)
1302%
1303% A description of each parameter follows:
1304%
1305% o quantize_info: Specifies a pointer to an QuantizeInfo structure.
1306%
1307*/
1308MagickExport QuantizeInfo *DestroyQuantizeInfo(QuantizeInfo *quantize_info)
1309{
1310 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
1311 assert(quantize_info != (QuantizeInfo *) NULL);
1312 assert(quantize_info->signature == MagickSignature);
1313 quantize_info->signature=(~MagickSignature);
1314 quantize_info=(QuantizeInfo *) RelinquishMagickMemory(quantize_info);
1315 return(quantize_info);
1316}
1317
1318/*
1319%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1320% %
1321% %
1322% %
1323+ D i t h e r I m a g e %
1324% %
1325% %
1326% %
1327%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1328%
1329% DitherImage() distributes the difference between an original image and
1330% the corresponding color reduced algorithm to neighboring pixels using
1331% serpentine-scan Floyd-Steinberg error diffusion. DitherImage returns
1332% MagickTrue if the image is dithered otherwise MagickFalse.
1333%
1334% The format of the DitherImage method is:
1335%
1336% MagickBooleanType DitherImage(Image *image,CubeInfo *cube_info)
1337%
1338% A description of each parameter follows.
1339%
1340% o image: the image.
1341%
1342% o cube_info: A pointer to the Cube structure.
1343%
1344*/
1345
1346static MagickBooleanType FloydSteinbergDither(Image *image,CubeInfo *cube_info)
1347{
1348#define DitherImageTag "Dither/Image"
1349
cristyc4c8d132010-01-07 01:58:38 +00001350 CacheView
1351 *image_view;
1352
cristy3ed852e2009-09-05 21:47:34 +00001353 ExceptionInfo
1354 *exception;
1355
1356 long
1357 u,
1358 v,
1359 y;
1360
1361 MagickBooleanType
1362 proceed;
1363
1364 RealPixelPacket
1365 color,
1366 *current,
1367 pixel,
1368 *previous,
1369 *scanlines;
1370
1371 register CubeInfo
1372 *p;
1373
1374 unsigned long
1375 index;
1376
cristy3ed852e2009-09-05 21:47:34 +00001377 /*
1378 Distribute quantization error using Floyd-Steinberg.
1379 */
1380 scanlines=(RealPixelPacket *) AcquireQuantumMemory(image->columns,
1381 2*sizeof(*scanlines));
1382 if (scanlines == (RealPixelPacket *) NULL)
1383 return(MagickFalse);
1384 p=cube_info;
1385 exception=(&image->exception);
1386 image_view=AcquireCacheView(image);
1387 for (y=0; y < (long) image->rows; y++)
1388 {
1389 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001390 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00001391
1392 register long
1393 i,
1394 x;
1395
1396 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001397 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001398
1399 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1400 if (q == (PixelPacket *) NULL)
1401 return(MagickFalse);
1402 indexes=GetCacheViewAuthenticIndexQueue(image_view);
1403 current=scanlines+(y & 0x01)*image->columns;
1404 previous=scanlines+((y+1) & 0x01)*image->columns;
1405 v=(y & 0x01) ? -1 : 1;
1406 for (x=0; x < (long) image->columns; x++)
1407 {
1408 u=(y & 0x01) ? (long) image->columns-1-x : x;
1409 AssociateAlphaPixel(cube_info,q+u,&pixel);
1410 if (x > 0)
1411 {
1412 pixel.red+=7*current[u-v].red/16;
1413 pixel.green+=7*current[u-v].green/16;
1414 pixel.blue+=7*current[u-v].blue/16;
1415 if (cube_info->associate_alpha != MagickFalse)
1416 pixel.opacity+=7*current[u-v].opacity/16;
1417 }
1418 if (y > 0)
1419 {
1420 if (x < (long) (image->columns-1))
1421 {
1422 pixel.red+=previous[u+v].red/16;
1423 pixel.green+=previous[u+v].green/16;
1424 pixel.blue+=previous[u+v].blue/16;
1425 if (cube_info->associate_alpha != MagickFalse)
1426 pixel.opacity+=previous[u+v].opacity/16;
1427 }
1428 pixel.red+=5*previous[u].red/16;
1429 pixel.green+=5*previous[u].green/16;
1430 pixel.blue+=5*previous[u].blue/16;
1431 if (cube_info->associate_alpha != MagickFalse)
1432 pixel.opacity+=5*previous[u].opacity/16;
1433 if (x > 0)
1434 {
1435 pixel.red+=3*previous[u-v].red/16;
1436 pixel.green+=3*previous[u-v].green/16;
1437 pixel.blue+=3*previous[u-v].blue/16;
1438 if (cube_info->associate_alpha != MagickFalse)
1439 pixel.opacity+=3*previous[u-v].opacity/16;
1440 }
1441 }
cristy75ffdb72010-01-07 17:40:12 +00001442 pixel.red=(MagickRealType) ClampToUnsignedQuantum(pixel.red);
1443 pixel.green=(MagickRealType) ClampToUnsignedQuantum(pixel.green);
1444 pixel.blue=(MagickRealType) ClampToUnsignedQuantum(pixel.blue);
cristy3ed852e2009-09-05 21:47:34 +00001445 if (cube_info->associate_alpha != MagickFalse)
cristy75ffdb72010-01-07 17:40:12 +00001446 pixel.opacity=(MagickRealType) ClampToUnsignedQuantum(pixel.opacity);
1447 i=(long) ((ScaleQuantumToChar(ClampToUnsignedQuantum(pixel.red)) >> CacheShift) |
1448 (ScaleQuantumToChar(ClampToUnsignedQuantum(pixel.green)) >> CacheShift) << 6 |
1449 (ScaleQuantumToChar(ClampToUnsignedQuantum(pixel.blue)) >> CacheShift) << 12);
cristy3ed852e2009-09-05 21:47:34 +00001450 if (cube_info->associate_alpha != MagickFalse)
cristy75ffdb72010-01-07 17:40:12 +00001451 i|=((ScaleQuantumToChar(ClampToUnsignedQuantum(pixel.opacity)) >> CacheShift)
cristy3ed852e2009-09-05 21:47:34 +00001452 << 18);
1453 if (p->cache[i] < 0)
1454 {
1455 register NodeInfo
1456 *node_info;
1457
1458 register unsigned long
1459 id;
1460
1461 /*
1462 Identify the deepest node containing the pixel's color.
1463 */
1464 node_info=p->root;
1465 for (index=MaxTreeDepth-1; (long) index > 0; index--)
1466 {
1467 id=ColorToNodeId(cube_info,&pixel,index);
1468 if (node_info->child[id] == (NodeInfo *) NULL)
1469 break;
1470 node_info=node_info->child[id];
1471 }
1472 /*
1473 Find closest color among siblings and their children.
1474 */
1475 p->target=pixel;
1476 p->distance=(MagickRealType) (4.0*(QuantumRange+1.0)*(QuantumRange+
1477 1.0)+1.0);
1478 ClosestColor(image,p,node_info->parent);
1479 p->cache[i]=(long) p->color_number;
1480 }
1481 /*
1482 Assign pixel to closest colormap entry.
1483 */
1484 index=(unsigned long) p->cache[i];
1485 if (image->storage_class == PseudoClass)
1486 indexes[u]=(IndexPacket) index;
1487 if (cube_info->quantize_info->measure_error == MagickFalse)
1488 {
1489 (q+u)->red=image->colormap[index].red;
1490 (q+u)->green=image->colormap[index].green;
1491 (q+u)->blue=image->colormap[index].blue;
1492 if (cube_info->associate_alpha != MagickFalse)
1493 (q+u)->opacity=image->colormap[index].opacity;
1494 }
1495 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1496 return(MagickFalse);
1497 /*
1498 Store the error.
1499 */
1500 AssociateAlphaPixel(cube_info,image->colormap+index,&color);
1501 current[u].red=pixel.red-color.red;
1502 current[u].green=pixel.green-color.green;
1503 current[u].blue=pixel.blue-color.blue;
1504 if (cube_info->associate_alpha != MagickFalse)
1505 current[u].opacity=pixel.opacity-color.opacity;
1506 proceed=SetImageProgress(image,DitherImageTag,p->offset,p->span);
1507 if (proceed == MagickFalse)
1508 return(MagickFalse);
1509 p->offset++;
1510 }
1511 }
1512 scanlines=(RealPixelPacket *) RelinquishMagickMemory(scanlines);
1513 image_view=DestroyCacheView(image_view);
1514 return(MagickTrue);
1515}
1516
1517static MagickBooleanType
1518 RiemersmaDither(Image *,CacheView *,CubeInfo *,const unsigned int);
1519
1520static void Riemersma(Image *image,CacheView *image_view,CubeInfo *cube_info,
1521 const unsigned long level,const unsigned int direction)
1522{
1523 if (level == 1)
1524 switch (direction)
1525 {
1526 case WestGravity:
1527 {
1528 (void) RiemersmaDither(image,image_view,cube_info,EastGravity);
1529 (void) RiemersmaDither(image,image_view,cube_info,SouthGravity);
1530 (void) RiemersmaDither(image,image_view,cube_info,WestGravity);
1531 break;
1532 }
1533 case EastGravity:
1534 {
1535 (void) RiemersmaDither(image,image_view,cube_info,WestGravity);
1536 (void) RiemersmaDither(image,image_view,cube_info,NorthGravity);
1537 (void) RiemersmaDither(image,image_view,cube_info,EastGravity);
1538 break;
1539 }
1540 case NorthGravity:
1541 {
1542 (void) RiemersmaDither(image,image_view,cube_info,SouthGravity);
1543 (void) RiemersmaDither(image,image_view,cube_info,EastGravity);
1544 (void) RiemersmaDither(image,image_view,cube_info,NorthGravity);
1545 break;
1546 }
1547 case SouthGravity:
1548 {
1549 (void) RiemersmaDither(image,image_view,cube_info,NorthGravity);
1550 (void) RiemersmaDither(image,image_view,cube_info,WestGravity);
1551 (void) RiemersmaDither(image,image_view,cube_info,SouthGravity);
1552 break;
1553 }
1554 default:
1555 break;
1556 }
1557 else
1558 switch (direction)
1559 {
1560 case WestGravity:
1561 {
1562 Riemersma(image,image_view,cube_info,level-1,NorthGravity);
1563 (void) RiemersmaDither(image,image_view,cube_info,EastGravity);
1564 Riemersma(image,image_view,cube_info,level-1,WestGravity);
1565 (void) RiemersmaDither(image,image_view,cube_info,SouthGravity);
1566 Riemersma(image,image_view,cube_info,level-1,WestGravity);
1567 (void) RiemersmaDither(image,image_view,cube_info,WestGravity);
1568 Riemersma(image,image_view,cube_info,level-1,SouthGravity);
1569 break;
1570 }
1571 case EastGravity:
1572 {
1573 Riemersma(image,image_view,cube_info,level-1,SouthGravity);
1574 (void) RiemersmaDither(image,image_view,cube_info,WestGravity);
1575 Riemersma(image,image_view,cube_info,level-1,EastGravity);
1576 (void) RiemersmaDither(image,image_view,cube_info,NorthGravity);
1577 Riemersma(image,image_view,cube_info,level-1,EastGravity);
1578 (void) RiemersmaDither(image,image_view,cube_info,EastGravity);
1579 Riemersma(image,image_view,cube_info,level-1,NorthGravity);
1580 break;
1581 }
1582 case NorthGravity:
1583 {
1584 Riemersma(image,image_view,cube_info,level-1,WestGravity);
1585 (void) RiemersmaDither(image,image_view,cube_info,SouthGravity);
1586 Riemersma(image,image_view,cube_info,level-1,NorthGravity);
1587 (void) RiemersmaDither(image,image_view,cube_info,EastGravity);
1588 Riemersma(image,image_view,cube_info,level-1,NorthGravity);
1589 (void) RiemersmaDither(image,image_view,cube_info,NorthGravity);
1590 Riemersma(image,image_view,cube_info,level-1,EastGravity);
1591 break;
1592 }
1593 case SouthGravity:
1594 {
1595 Riemersma(image,image_view,cube_info,level-1,EastGravity);
1596 (void) RiemersmaDither(image,image_view,cube_info,NorthGravity);
1597 Riemersma(image,image_view,cube_info,level-1,SouthGravity);
1598 (void) RiemersmaDither(image,image_view,cube_info,WestGravity);
1599 Riemersma(image,image_view,cube_info,level-1,SouthGravity);
1600 (void) RiemersmaDither(image,image_view,cube_info,SouthGravity);
1601 Riemersma(image,image_view,cube_info,level-1,WestGravity);
1602 break;
1603 }
1604 default:
1605 break;
1606 }
1607}
1608
1609static MagickBooleanType RiemersmaDither(Image *image,CacheView *image_view,
1610 CubeInfo *cube_info,const unsigned int direction)
1611{
1612#define DitherImageTag "Dither/Image"
1613
1614 MagickBooleanType
1615 proceed;
1616
1617 RealPixelPacket
1618 color,
1619 pixel;
1620
1621 register CubeInfo
1622 *p;
1623
1624 unsigned long
1625 index;
1626
1627 p=cube_info;
1628 if ((p->x >= 0) && (p->x < (long) image->columns) &&
1629 (p->y >= 0) && (p->y < (long) image->rows))
1630 {
1631 ExceptionInfo
1632 *exception;
1633
1634 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001635 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00001636
1637 register long
1638 i;
1639
1640 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001641 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001642
1643 /*
1644 Distribute error.
1645 */
1646 exception=(&image->exception);
1647 q=GetCacheViewAuthenticPixels(image_view,p->x,p->y,1,1,exception);
1648 if (q == (PixelPacket *) NULL)
1649 return(MagickFalse);
1650 indexes=GetCacheViewAuthenticIndexQueue(image_view);
1651 AssociateAlphaPixel(cube_info,q,&pixel);
1652 for (i=0; i < ErrorQueueLength; i++)
1653 {
1654 pixel.red+=p->weights[i]*p->error[i].red;
1655 pixel.green+=p->weights[i]*p->error[i].green;
1656 pixel.blue+=p->weights[i]*p->error[i].blue;
1657 if (cube_info->associate_alpha != MagickFalse)
1658 pixel.opacity+=p->weights[i]*p->error[i].opacity;
1659 }
cristy75ffdb72010-01-07 17:40:12 +00001660 pixel.red=(MagickRealType) ClampToUnsignedQuantum(pixel.red);
1661 pixel.green=(MagickRealType) ClampToUnsignedQuantum(pixel.green);
1662 pixel.blue=(MagickRealType) ClampToUnsignedQuantum(pixel.blue);
cristy3ed852e2009-09-05 21:47:34 +00001663 if (cube_info->associate_alpha != MagickFalse)
cristy75ffdb72010-01-07 17:40:12 +00001664 pixel.opacity=(MagickRealType) ClampToUnsignedQuantum(pixel.opacity);
1665 i=(long) ((ScaleQuantumToChar(ClampToUnsignedQuantum(pixel.red)) >> CacheShift) |
1666 (ScaleQuantumToChar(ClampToUnsignedQuantum(pixel.green)) >> CacheShift) << 6 |
1667 (ScaleQuantumToChar(ClampToUnsignedQuantum(pixel.blue)) >> CacheShift) << 12);
cristy3ed852e2009-09-05 21:47:34 +00001668 if (cube_info->associate_alpha != MagickFalse)
cristy75ffdb72010-01-07 17:40:12 +00001669 i|=((ScaleQuantumToChar(ClampToUnsignedQuantum(pixel.opacity)) >> CacheShift)
cristy3ed852e2009-09-05 21:47:34 +00001670 << 18);
1671 if (p->cache[i] < 0)
1672 {
1673 register NodeInfo
1674 *node_info;
1675
1676 register unsigned long
1677 id;
1678
1679 /*
1680 Identify the deepest node containing the pixel's color.
1681 */
1682 node_info=p->root;
1683 for (index=MaxTreeDepth-1; (long) index > 0; index--)
1684 {
1685 id=ColorToNodeId(cube_info,&pixel,index);
1686 if (node_info->child[id] == (NodeInfo *) NULL)
1687 break;
1688 node_info=node_info->child[id];
1689 }
1690 /*
1691 Find closest color among siblings and their children.
1692 */
1693 p->target=pixel;
1694 p->distance=(MagickRealType) (4.0*(QuantumRange+1.0)*((MagickRealType)
1695 QuantumRange+1.0)+1.0);
1696 ClosestColor(image,p,node_info->parent);
1697 p->cache[i]=(long) p->color_number;
1698 }
1699 /*
1700 Assign pixel to closest colormap entry.
1701 */
1702 index=(unsigned long) (1*p->cache[i]);
1703 if (image->storage_class == PseudoClass)
1704 *indexes=(IndexPacket) index;
1705 if (cube_info->quantize_info->measure_error == MagickFalse)
1706 {
1707 q->red=image->colormap[index].red;
1708 q->green=image->colormap[index].green;
1709 q->blue=image->colormap[index].blue;
1710 if (cube_info->associate_alpha != MagickFalse)
1711 q->opacity=image->colormap[index].opacity;
1712 }
1713 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1714 return(MagickFalse);
1715 /*
1716 Propagate the error as the last entry of the error queue.
1717 */
1718 (void) CopyMagickMemory(p->error,p->error+1,(ErrorQueueLength-1)*
1719 sizeof(p->error[0]));
1720 AssociateAlphaPixel(cube_info,image->colormap+index,&color);
1721 p->error[ErrorQueueLength-1].red=pixel.red-color.red;
1722 p->error[ErrorQueueLength-1].green=pixel.green-color.green;
1723 p->error[ErrorQueueLength-1].blue=pixel.blue-color.blue;
1724 if (cube_info->associate_alpha != MagickFalse)
1725 p->error[ErrorQueueLength-1].opacity=pixel.opacity-color.opacity;
1726 proceed=SetImageProgress(image,DitherImageTag,p->offset,p->span);
1727 if (proceed == MagickFalse)
1728 return(MagickFalse);
1729 p->offset++;
1730 }
1731 switch (direction)
1732 {
1733 case WestGravity: p->x--; break;
1734 case EastGravity: p->x++; break;
1735 case NorthGravity: p->y--; break;
1736 case SouthGravity: p->y++; break;
1737 }
1738 return(MagickTrue);
1739}
1740
1741static inline long MagickMax(const long x,const long y)
1742{
1743 if (x > y)
1744 return(x);
1745 return(y);
1746}
1747
1748static inline long MagickMin(const long x,const long y)
1749{
1750 if (x < y)
1751 return(x);
1752 return(y);
1753}
1754
1755static MagickBooleanType DitherImage(Image *image,CubeInfo *cube_info)
1756{
cristyc4c8d132010-01-07 01:58:38 +00001757 CacheView
1758 *image_view;
1759
cristy3ed852e2009-09-05 21:47:34 +00001760 MagickBooleanType
1761 status;
1762
1763 register long
1764 i;
1765
1766 unsigned long
1767 depth;
1768
cristy3ed852e2009-09-05 21:47:34 +00001769 if (cube_info->quantize_info->dither_method == FloydSteinbergDitherMethod)
1770 return(FloydSteinbergDither(image,cube_info));
1771 /*
1772 Distribute quantization error along a Hilbert curve.
1773 */
1774 (void) ResetMagickMemory(cube_info->error,0,ErrorQueueLength*
1775 sizeof(*cube_info->error));
1776 cube_info->x=0;
1777 cube_info->y=0;
1778 i=MagickMax((long) image->columns,(long) image->rows);
1779 for (depth=1; i != 0; depth++)
1780 i>>=1;
1781 if ((long) (1L << depth) < MagickMax((long) image->columns,(long) image->rows))
1782 depth++;
1783 cube_info->offset=0;
1784 cube_info->span=(MagickSizeType) image->columns*image->rows;
1785 image_view=AcquireCacheView(image);
1786 if (depth > 1)
1787 Riemersma(image,image_view,cube_info,depth-1,NorthGravity);
1788 status=RiemersmaDither(image,image_view,cube_info,ForgetGravity);
1789 image_view=DestroyCacheView(image_view);
1790 return(status);
1791}
1792
1793/*
1794%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1795% %
1796% %
1797% %
1798+ G e t C u b e I n f o %
1799% %
1800% %
1801% %
1802%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1803%
1804% GetCubeInfo() initialize the Cube data structure.
1805%
1806% The format of the GetCubeInfo method is:
1807%
1808% CubeInfo GetCubeInfo(const QuantizeInfo *quantize_info,
1809% const unsigned long depth,const unsigned long maximum_colors)
1810%
1811% A description of each parameter follows.
1812%
1813% o quantize_info: Specifies a pointer to an QuantizeInfo structure.
1814%
1815% o depth: Normally, this integer value is zero or one. A zero or
1816% one tells Quantize to choose a optimal tree depth of Log4(number_colors).
1817% A tree of this depth generally allows the best representation of the
1818% reference image with the least amount of memory and the fastest
1819% computational speed. In some cases, such as an image with low color
1820% dispersion (a few number of colors), a value other than
1821% Log4(number_colors) is required. To expand the color tree completely,
1822% use a value of 8.
1823%
1824% o maximum_colors: maximum colors.
1825%
1826*/
1827static CubeInfo *GetCubeInfo(const QuantizeInfo *quantize_info,
1828 const unsigned long depth,const unsigned long maximum_colors)
1829{
1830 CubeInfo
1831 *cube_info;
1832
1833 MagickRealType
1834 sum,
1835 weight;
1836
1837 size_t
1838 length;
1839
1840 register long
1841 i;
1842
1843 /*
1844 Initialize tree to describe color cube_info.
1845 */
cristy90823212009-12-12 20:48:33 +00001846 cube_info=(CubeInfo *) AcquireAlignedMemory(1,sizeof(*cube_info));
cristy3ed852e2009-09-05 21:47:34 +00001847 if (cube_info == (CubeInfo *) NULL)
1848 return((CubeInfo *) NULL);
1849 (void) ResetMagickMemory(cube_info,0,sizeof(*cube_info));
1850 cube_info->depth=depth;
1851 if (cube_info->depth > MaxTreeDepth)
1852 cube_info->depth=MaxTreeDepth;
1853 if (cube_info->depth < 2)
1854 cube_info->depth=2;
1855 cube_info->maximum_colors=maximum_colors;
1856 /*
1857 Initialize root node.
1858 */
1859 cube_info->root=GetNodeInfo(cube_info,0,0,(NodeInfo *) NULL);
1860 if (cube_info->root == (NodeInfo *) NULL)
1861 return((CubeInfo *) NULL);
1862 cube_info->root->parent=cube_info->root;
1863 cube_info->quantize_info=CloneQuantizeInfo(quantize_info);
1864 if (cube_info->quantize_info->dither == MagickFalse)
1865 return(cube_info);
1866 /*
1867 Initialize dither resources.
1868 */
1869 length=(size_t) (1UL << (4*(8-CacheShift)));
1870 cube_info->cache=(long *) AcquireQuantumMemory(length,
1871 sizeof(*cube_info->cache));
1872 if (cube_info->cache == (long *) NULL)
1873 return((CubeInfo *) NULL);
1874 /*
1875 Initialize color cache.
1876 */
1877 for (i=0; i < (long) length; i++)
1878 cube_info->cache[i]=(-1);
1879 /*
1880 Distribute weights along a curve of exponential decay.
1881 */
1882 weight=1.0;
1883 for (i=0; i < ErrorQueueLength; i++)
1884 {
1885 cube_info->weights[ErrorQueueLength-i-1]=1.0/weight;
1886 weight*=exp(log(((double) QuantumRange+1.0))/(ErrorQueueLength-1.0));
1887 }
1888 /*
1889 Normalize the weighting factors.
1890 */
1891 weight=0.0;
1892 for (i=0; i < ErrorQueueLength; i++)
1893 weight+=cube_info->weights[i];
1894 sum=0.0;
1895 for (i=0; i < ErrorQueueLength; i++)
1896 {
1897 cube_info->weights[i]/=weight;
1898 sum+=cube_info->weights[i];
1899 }
1900 cube_info->weights[0]+=1.0-sum;
1901 return(cube_info);
1902}
1903
1904/*
1905%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1906% %
1907% %
1908% %
1909+ G e t N o d e I n f o %
1910% %
1911% %
1912% %
1913%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1914%
1915% GetNodeInfo() allocates memory for a new node in the color cube tree and
1916% presets all fields to zero.
1917%
1918% The format of the GetNodeInfo method is:
1919%
1920% NodeInfo *GetNodeInfo(CubeInfo *cube_info,const unsigned long id,
1921% const unsigned long level,NodeInfo *parent)
1922%
1923% A description of each parameter follows.
1924%
1925% o node: The GetNodeInfo method returns a pointer to a queue of nodes.
1926%
1927% o id: Specifies the child number of the node.
1928%
1929% o level: Specifies the level in the storage_class the node resides.
1930%
1931*/
1932static NodeInfo *GetNodeInfo(CubeInfo *cube_info,const unsigned long id,
1933 const unsigned long level,NodeInfo *parent)
1934{
1935 NodeInfo
1936 *node_info;
1937
1938 if (cube_info->free_nodes == 0)
1939 {
1940 Nodes
1941 *nodes;
1942
1943 /*
1944 Allocate a new queue of nodes.
1945 */
cristy90823212009-12-12 20:48:33 +00001946 nodes=(Nodes *) AcquireAlignedMemory(1,sizeof(*nodes));
cristy3ed852e2009-09-05 21:47:34 +00001947 if (nodes == (Nodes *) NULL)
1948 return((NodeInfo *) NULL);
1949 nodes->nodes=(NodeInfo *) AcquireQuantumMemory(NodesInAList,
1950 sizeof(*nodes->nodes));
1951 if (nodes->nodes == (NodeInfo *) NULL)
1952 return((NodeInfo *) NULL);
1953 nodes->next=cube_info->node_queue;
1954 cube_info->node_queue=nodes;
1955 cube_info->next_node=nodes->nodes;
1956 cube_info->free_nodes=NodesInAList;
1957 }
1958 cube_info->nodes++;
1959 cube_info->free_nodes--;
1960 node_info=cube_info->next_node++;
1961 (void) ResetMagickMemory(node_info,0,sizeof(*node_info));
1962 node_info->parent=parent;
1963 node_info->id=id;
1964 node_info->level=level;
1965 return(node_info);
1966}
1967
1968/*
1969%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1970% %
1971% %
1972% %
1973% G e t I m a g e Q u a n t i z e E r r o r %
1974% %
1975% %
1976% %
1977%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1978%
1979% GetImageQuantizeError() measures the difference between the original
1980% and quantized images. This difference is the total quantization error.
1981% The error is computed by summing over all pixels in an image the distance
1982% squared in RGB space between each reference pixel value and its quantized
1983% value. These values are computed:
1984%
1985% o mean_error_per_pixel: This value is the mean error for any single
1986% pixel in the image.
1987%
1988% o normalized_mean_square_error: This value is the normalized mean
1989% quantization error for any single pixel in the image. This distance
1990% measure is normalized to a range between 0 and 1. It is independent
1991% of the range of red, green, and blue values in the image.
1992%
1993% o normalized_maximum_square_error: Thsi value is the normalized
1994% maximum quantization error for any single pixel in the image. This
1995% distance measure is normalized to a range between 0 and 1. It is
1996% independent of the range of red, green, and blue values in your image.
1997%
1998% The format of the GetImageQuantizeError method is:
1999%
2000% MagickBooleanType GetImageQuantizeError(Image *image)
2001%
2002% A description of each parameter follows.
2003%
2004% o image: the image.
2005%
2006*/
2007MagickExport MagickBooleanType GetImageQuantizeError(Image *image)
2008{
cristyc4c8d132010-01-07 01:58:38 +00002009 CacheView
2010 *image_view;
2011
cristy3ed852e2009-09-05 21:47:34 +00002012 ExceptionInfo
2013 *exception;
2014
2015 IndexPacket
2016 *indexes;
2017
2018 long
2019 y;
2020
2021 MagickRealType
2022 alpha,
2023 area,
2024 beta,
2025 distance,
2026 maximum_error,
2027 mean_error,
2028 mean_error_per_pixel;
2029
2030 unsigned long
2031 index;
2032
cristy3ed852e2009-09-05 21:47:34 +00002033 assert(image != (Image *) NULL);
2034 assert(image->signature == MagickSignature);
2035 if (image->debug != MagickFalse)
2036 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2037 image->total_colors=GetNumberColors(image,(FILE *) NULL,&image->exception);
2038 (void) ResetMagickMemory(&image->error,0,sizeof(image->error));
2039 if (image->storage_class == DirectClass)
2040 return(MagickTrue);
2041 alpha=1.0;
2042 beta=1.0;
2043 area=3.0*image->columns*image->rows;
2044 maximum_error=0.0;
2045 mean_error_per_pixel=0.0;
2046 mean_error=0.0;
2047 exception=(&image->exception);
2048 image_view=AcquireCacheView(image);
2049 for (y=0; y < (long) image->rows; y++)
2050 {
2051 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002052 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002053
2054 register long
2055 x;
2056
2057 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
2058 if (p == (const PixelPacket *) NULL)
2059 break;
2060 indexes=GetCacheViewAuthenticIndexQueue(image_view);
2061 for (x=0; x < (long) image->columns; x++)
2062 {
2063 index=1UL*indexes[x];
2064 if (image->matte != MagickFalse)
2065 {
cristy46f08202010-01-10 04:04:21 +00002066 alpha=(MagickRealType) (QuantumScale*(GetAlphaPixelComponent(p)));
cristy3ed852e2009-09-05 21:47:34 +00002067 beta=(MagickRealType) (QuantumScale*(QuantumRange-
2068 image->colormap[index].opacity));
2069 }
2070 distance=fabs(alpha*p->red-beta*image->colormap[index].red);
2071 mean_error_per_pixel+=distance;
2072 mean_error+=distance*distance;
2073 if (distance > maximum_error)
2074 maximum_error=distance;
2075 distance=fabs(alpha*p->green-beta*image->colormap[index].green);
2076 mean_error_per_pixel+=distance;
2077 mean_error+=distance*distance;
2078 if (distance > maximum_error)
2079 maximum_error=distance;
2080 distance=fabs(alpha*p->blue-beta*image->colormap[index].blue);
2081 mean_error_per_pixel+=distance;
2082 mean_error+=distance*distance;
2083 if (distance > maximum_error)
2084 maximum_error=distance;
2085 p++;
2086 }
2087 }
2088 image_view=DestroyCacheView(image_view);
2089 image->error.mean_error_per_pixel=(double) mean_error_per_pixel/area;
2090 image->error.normalized_mean_error=(double) QuantumScale*QuantumScale*
2091 mean_error/area;
2092 image->error.normalized_maximum_error=(double) QuantumScale*maximum_error;
2093 return(MagickTrue);
2094}
2095
2096/*
2097%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2098% %
2099% %
2100% %
2101% G e t Q u a n t i z e I n f o %
2102% %
2103% %
2104% %
2105%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2106%
2107% GetQuantizeInfo() initializes the QuantizeInfo structure.
2108%
2109% The format of the GetQuantizeInfo method is:
2110%
2111% GetQuantizeInfo(QuantizeInfo *quantize_info)
2112%
2113% A description of each parameter follows:
2114%
2115% o quantize_info: Specifies a pointer to a QuantizeInfo structure.
2116%
2117*/
2118MagickExport void GetQuantizeInfo(QuantizeInfo *quantize_info)
2119{
2120 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2121 assert(quantize_info != (QuantizeInfo *) NULL);
2122 (void) ResetMagickMemory(quantize_info,0,sizeof(*quantize_info));
2123 quantize_info->number_colors=256;
2124 quantize_info->dither=MagickTrue;
2125 quantize_info->dither_method=RiemersmaDitherMethod;
2126 quantize_info->colorspace=UndefinedColorspace;
2127 quantize_info->measure_error=MagickFalse;
2128 quantize_info->signature=MagickSignature;
2129}
2130
2131/*
2132%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2133% %
2134% %
2135% %
2136% P o s t e r i z e I m a g e %
2137% %
2138% %
2139% %
2140%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2141%
2142% PosterizeImage() reduces the image to a limited number of colors for a
2143% "poster" effect.
2144%
2145% The format of the PosterizeImage method is:
2146%
2147% MagickBooleanType PosterizeImage(Image *image,const unsigned long levels,
2148% const MagickBooleanType dither)
2149%
2150% A description of each parameter follows:
2151%
2152% o image: Specifies a pointer to an Image structure.
2153%
2154% o levels: Number of color levels allowed in each channel. Very low values
2155% (2, 3, or 4) have the most visible effect.
2156%
2157% o dither: Set this integer value to something other than zero to
2158% dither the mapped image.
2159%
2160*/
2161MagickExport MagickBooleanType PosterizeImage(Image *image,
2162 const unsigned long levels,const MagickBooleanType dither)
2163{
cristyc4c8d132010-01-07 01:58:38 +00002164 CacheView
2165 *posterize_view;
2166
cristy3ed852e2009-09-05 21:47:34 +00002167 ExceptionInfo
2168 *exception;
2169
2170 Image
2171 *posterize_image;
2172
2173 IndexPacket
2174 *indexes;
2175
2176 long
2177 j,
2178 k,
2179 l,
2180 n;
2181
2182 MagickBooleanType
2183 status;
2184
2185 QuantizeInfo
2186 *quantize_info;
2187
2188 register long
2189 i;
2190
2191 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002192 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002193
cristy3ed852e2009-09-05 21:47:34 +00002194 /*
2195 Posterize image.
2196 */
2197 assert(image != (Image *) NULL);
2198 assert(image->signature == MagickSignature);
2199 if (image->debug != MagickFalse)
2200 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2201 posterize_image=AcquireImage((ImageInfo *) NULL);
2202 if (posterize_image == (Image *) NULL)
2203 return(MagickFalse);
2204 l=1;
2205 while ((l*l*l) < (long) MagickMin((long) levels*levels*levels,MaxColormapSize+1))
2206 l++;
2207 status=SetImageExtent(posterize_image,(unsigned long) (l*l*l),1);
2208 if (status == MagickFalse)
2209 {
2210 posterize_image=DestroyImage(posterize_image);
2211 return(MagickFalse);
2212 }
2213 status=AcquireImageColormap(posterize_image,levels*levels*levels);
2214 if (status == MagickFalse)
2215 {
2216 posterize_image=DestroyImage(posterize_image);
2217 return(MagickFalse);
2218 }
2219 posterize_view=AcquireCacheView(posterize_image);
2220 exception=(&image->exception);
2221 q=QueueCacheViewAuthenticPixels(posterize_view,0,0,posterize_image->columns,1,
2222 exception);
2223 if (q == (PixelPacket *) NULL)
2224 {
2225 posterize_view=DestroyCacheView(posterize_view);
2226 posterize_image=DestroyImage(posterize_image);
2227 return(MagickFalse);
2228 }
2229 indexes=GetCacheViewAuthenticIndexQueue(posterize_view);
2230 n=0;
2231 for (i=0; i < l; i++)
2232 for (j=0; j < l; j++)
2233 for (k=0; k < l; k++)
2234 {
2235 posterize_image->colormap[n].red=(Quantum) (QuantumRange*i/
2236 MagickMax(l-1L,1L));
2237 posterize_image->colormap[n].green=(Quantum)
2238 (QuantumRange*j/MagickMax(l-1L,1L));
2239 posterize_image->colormap[n].blue=(Quantum) (QuantumRange*k/
2240 MagickMax(l-1L,1L));
2241 posterize_image->colormap[n].opacity=OpaqueOpacity;
2242 *q++=posterize_image->colormap[n];
2243 indexes[n]=(IndexPacket) n;
2244 n++;
2245 }
2246 if (SyncCacheViewAuthenticPixels(posterize_view,exception) == MagickFalse)
2247 {
2248 posterize_view=DestroyCacheView(posterize_view);
2249 posterize_image=DestroyImage(posterize_image);
2250 return(MagickFalse);
2251 }
2252 posterize_view=DestroyCacheView(posterize_view);
2253 quantize_info=AcquireQuantizeInfo((ImageInfo *) NULL);
2254 quantize_info->dither=dither;
2255 status=RemapImage(quantize_info,image,posterize_image);
2256 quantize_info=DestroyQuantizeInfo(quantize_info);
2257 posterize_image=DestroyImage(posterize_image);
2258 return(status);
2259}
2260
2261/*
2262%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2263% %
2264% %
2265% %
2266+ P r u n e C h i l d %
2267% %
2268% %
2269% %
2270%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2271%
2272% PruneChild() deletes the given node and merges its statistics into its
2273% parent.
2274%
2275% The format of the PruneSubtree method is:
2276%
2277% PruneChild(const Image *image,CubeInfo *cube_info,
2278% const NodeInfo *node_info)
2279%
2280% A description of each parameter follows.
2281%
2282% o image: the image.
2283%
2284% o cube_info: A pointer to the Cube structure.
2285%
2286% o node_info: pointer to node in color cube tree that is to be pruned.
2287%
2288*/
2289static void PruneChild(const Image *image,CubeInfo *cube_info,
2290 const NodeInfo *node_info)
2291{
2292 NodeInfo
2293 *parent;
2294
2295 register long
2296 i;
2297
2298 unsigned long
2299 number_children;
2300
2301 /*
2302 Traverse any children.
2303 */
2304 number_children=cube_info->associate_alpha == MagickFalse ? 8UL : 16UL;
2305 for (i=0; i < (long) number_children; i++)
2306 if (node_info->child[i] != (NodeInfo *) NULL)
2307 PruneChild(image,cube_info,node_info->child[i]);
2308 /*
2309 Merge color statistics into parent.
2310 */
2311 parent=node_info->parent;
2312 parent->number_unique+=node_info->number_unique;
2313 parent->total_color.red+=node_info->total_color.red;
2314 parent->total_color.green+=node_info->total_color.green;
2315 parent->total_color.blue+=node_info->total_color.blue;
2316 parent->total_color.opacity+=node_info->total_color.opacity;
2317 parent->child[node_info->id]=(NodeInfo *) NULL;
2318 cube_info->nodes--;
2319}
2320
2321/*
2322%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2323% %
2324% %
2325% %
2326+ P r u n e L e v e l %
2327% %
2328% %
2329% %
2330%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2331%
2332% PruneLevel() deletes all nodes at the bottom level of the color tree merging
2333% their color statistics into their parent node.
2334%
2335% The format of the PruneLevel method is:
2336%
2337% PruneLevel(const Image *image,CubeInfo *cube_info,
2338% const NodeInfo *node_info)
2339%
2340% A description of each parameter follows.
2341%
2342% o image: the image.
2343%
2344% o cube_info: A pointer to the Cube structure.
2345%
2346% o node_info: pointer to node in color cube tree that is to be pruned.
2347%
2348*/
2349static void PruneLevel(const Image *image,CubeInfo *cube_info,
2350 const NodeInfo *node_info)
2351{
2352 register long
2353 i;
2354
2355 unsigned long
2356 number_children;
2357
2358 /*
2359 Traverse any children.
2360 */
2361 number_children=cube_info->associate_alpha == MagickFalse ? 8UL : 16UL;
2362 for (i=0; i < (long) number_children; i++)
2363 if (node_info->child[i] != (NodeInfo *) NULL)
2364 PruneLevel(image,cube_info,node_info->child[i]);
2365 if (node_info->level == cube_info->depth)
2366 PruneChild(image,cube_info,node_info);
2367}
2368
2369/*
2370%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2371% %
2372% %
2373% %
2374+ P r u n e T o C u b e D e p t h %
2375% %
2376% %
2377% %
2378%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2379%
2380% PruneToCubeDepth() deletes any nodes at a depth greater than
2381% cube_info->depth while merging their color statistics into their parent
2382% node.
2383%
2384% The format of the PruneToCubeDepth method is:
2385%
2386% PruneToCubeDepth(const Image *image,CubeInfo *cube_info,
2387% const NodeInfo *node_info)
2388%
2389% A description of each parameter follows.
2390%
2391% o cube_info: A pointer to the Cube structure.
2392%
2393% o node_info: pointer to node in color cube tree that is to be pruned.
2394%
2395*/
2396static void PruneToCubeDepth(const Image *image,CubeInfo *cube_info,
2397 const NodeInfo *node_info)
2398{
2399 register long
2400 i;
2401
2402 unsigned long
2403 number_children;
2404
2405 /*
2406 Traverse any children.
2407 */
2408 number_children=cube_info->associate_alpha == MagickFalse ? 8UL : 16UL;
2409 for (i=0; i < (long) number_children; i++)
2410 if (node_info->child[i] != (NodeInfo *) NULL)
2411 PruneToCubeDepth(image,cube_info,node_info->child[i]);
2412 if (node_info->level > cube_info->depth)
2413 PruneChild(image,cube_info,node_info);
2414}
2415
2416/*
2417%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2418% %
2419% %
2420% %
2421% Q u a n t i z e I m a g e %
2422% %
2423% %
2424% %
2425%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2426%
2427% QuantizeImage() analyzes the colors within a reference image and chooses a
2428% fixed number of colors to represent the image. The goal of the algorithm
2429% is to minimize the color difference between the input and output image while
2430% minimizing the processing time.
2431%
2432% The format of the QuantizeImage method is:
2433%
2434% MagickBooleanType QuantizeImage(const QuantizeInfo *quantize_info,
2435% Image *image)
2436%
2437% A description of each parameter follows:
2438%
2439% o quantize_info: Specifies a pointer to an QuantizeInfo structure.
2440%
2441% o image: the image.
2442%
2443*/
cristy0157aea2010-04-24 21:12:18 +00002444static MagickBooleanType DirectToColormapImage(Image *image,
2445 ExceptionInfo *exception)
2446{
2447 CacheView
2448 *image_view;
2449
2450 long
2451 y;
2452
2453 MagickBooleanType
2454 status;
2455
2456 register long
2457 i;
2458
2459 unsigned long
2460 number_colors;
2461
2462 status=MagickTrue;
2463 number_colors=(unsigned long) (image->columns*image->rows);
2464 if (AcquireImageColormap(image,number_colors) == MagickFalse)
2465 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
2466 image->filename);
2467 i=0;
2468 image_view=AcquireCacheView(image);
2469 for (y=0; y < (long) image->rows; y++)
2470 {
cristy07a20312010-04-25 00:36:07 +00002471 MagickBooleanType
2472 proceed;
2473
2474 register IndexPacket
2475 *restrict indexes;
2476
2477 register PixelPacket
2478 *restrict q;
cristy0157aea2010-04-24 21:12:18 +00002479
2480 register long
2481 x;
2482
cristy07a20312010-04-25 00:36:07 +00002483 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2484 if (q == (const PixelPacket *) NULL)
cristy0157aea2010-04-24 21:12:18 +00002485 break;
cristy07a20312010-04-25 00:36:07 +00002486 indexes=GetCacheViewAuthenticIndexQueue(image_view);
cristy0157aea2010-04-24 21:12:18 +00002487 for (x=0; x < (long) image->columns; x++)
cristy07a20312010-04-25 00:36:07 +00002488 {
2489 indexes[x]=i;
2490 image->colormap[i++]=(*q++);
2491 }
2492 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2493 break;
2494 proceed=SetImageProgress(image,AssignImageTag,y,image->rows);
2495 if (proceed == MagickFalse)
2496 status=MagickFalse;
cristy0157aea2010-04-24 21:12:18 +00002497 }
2498 image_view=DestroyCacheView(image_view);
2499 return(status);
2500}
2501
cristy3ed852e2009-09-05 21:47:34 +00002502MagickExport MagickBooleanType QuantizeImage(const QuantizeInfo *quantize_info,
2503 Image *image)
2504{
2505 CubeInfo
2506 *cube_info;
2507
2508 MagickBooleanType
2509 status;
2510
2511 unsigned long
2512 depth,
2513 maximum_colors;
2514
2515 assert(quantize_info != (const QuantizeInfo *) NULL);
2516 assert(quantize_info->signature == MagickSignature);
2517 assert(image != (Image *) NULL);
2518 assert(image->signature == MagickSignature);
2519 if (image->debug != MagickFalse)
2520 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2521 maximum_colors=quantize_info->number_colors;
2522 if (maximum_colors == 0)
2523 maximum_colors=MaxColormapSize;
2524 if (maximum_colors > MaxColormapSize)
2525 maximum_colors=MaxColormapSize;
2526 if ((IsGrayImage(image,&image->exception) != MagickFalse) &&
2527 (image->matte == MagickFalse))
2528 (void) SetGrayscaleImage(image);
2529 if ((image->storage_class == PseudoClass) &&
2530 (image->colors <= maximum_colors))
2531 return(MagickTrue);
cristy0157aea2010-04-24 21:12:18 +00002532 if ((image->columns*image->rows) <= maximum_colors)
2533 return(DirectToColormapImage(image,&image->exception));
cristy3ed852e2009-09-05 21:47:34 +00002534 depth=quantize_info->tree_depth;
2535 if (depth == 0)
2536 {
2537 unsigned long
2538 colors;
2539
2540 /*
2541 Depth of color tree is: Log4(colormap size)+2.
2542 */
2543 colors=maximum_colors;
2544 for (depth=1; colors != 0; depth++)
2545 colors>>=2;
2546 if ((quantize_info->dither != MagickFalse) && (depth > 2))
2547 depth--;
2548 if ((image->matte != MagickFalse) && (depth > 5))
2549 depth--;
2550 }
2551 /*
2552 Initialize color cube.
2553 */
2554 cube_info=GetCubeInfo(quantize_info,depth,maximum_colors);
2555 if (cube_info == (CubeInfo *) NULL)
2556 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
2557 image->filename);
2558 status=ClassifyImageColors(cube_info,image,&image->exception);
2559 if (status != MagickFalse)
2560 {
2561 /*
2562 Reduce the number of colors in the image.
2563 */
2564 ReduceImageColors(image,cube_info);
2565 status=AssignImageColors(image,cube_info);
2566 }
2567 DestroyCubeInfo(cube_info);
2568 return(status);
2569}
2570
2571/*
2572%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2573% %
2574% %
2575% %
2576% Q u a n t i z e I m a g e s %
2577% %
2578% %
2579% %
2580%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2581%
2582% QuantizeImages() analyzes the colors within a set of reference images and
2583% chooses a fixed number of colors to represent the set. The goal of the
2584% algorithm is to minimize the color difference between the input and output
2585% images while minimizing the processing time.
2586%
2587% The format of the QuantizeImages method is:
2588%
2589% MagickBooleanType QuantizeImages(const QuantizeInfo *quantize_info,
2590% Image *images)
2591%
2592% A description of each parameter follows:
2593%
2594% o quantize_info: Specifies a pointer to an QuantizeInfo structure.
2595%
2596% o images: Specifies a pointer to a list of Image structures.
2597%
2598*/
2599MagickExport MagickBooleanType QuantizeImages(const QuantizeInfo *quantize_info,
2600 Image *images)
2601{
2602 CubeInfo
2603 *cube_info;
2604
2605 Image
2606 *image;
2607
2608 MagickBooleanType
2609 proceed,
2610 status;
2611
2612 MagickProgressMonitor
2613 progress_monitor;
2614
2615 register long
2616 i;
2617
2618 unsigned long
2619 depth,
2620 maximum_colors,
2621 number_images;
2622
2623 assert(quantize_info != (const QuantizeInfo *) NULL);
2624 assert(quantize_info->signature == MagickSignature);
2625 assert(images != (Image *) NULL);
2626 assert(images->signature == MagickSignature);
2627 if (images->debug != MagickFalse)
2628 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
2629 if (GetNextImageInList(images) == (Image *) NULL)
2630 {
2631 /*
2632 Handle a single image with QuantizeImage.
2633 */
2634 status=QuantizeImage(quantize_info,images);
2635 return(status);
2636 }
2637 status=MagickFalse;
2638 maximum_colors=quantize_info->number_colors;
2639 if (maximum_colors == 0)
2640 maximum_colors=MaxColormapSize;
2641 if (maximum_colors > MaxColormapSize)
2642 maximum_colors=MaxColormapSize;
2643 depth=quantize_info->tree_depth;
2644 if (depth == 0)
2645 {
2646 unsigned long
2647 colors;
2648
2649 /*
2650 Depth of color tree is: Log4(colormap size)+2.
2651 */
2652 colors=maximum_colors;
2653 for (depth=1; colors != 0; depth++)
2654 colors>>=2;
2655 if (quantize_info->dither != MagickFalse)
2656 depth--;
2657 }
2658 /*
2659 Initialize color cube.
2660 */
2661 cube_info=GetCubeInfo(quantize_info,depth,maximum_colors);
2662 if (cube_info == (CubeInfo *) NULL)
2663 {
2664 (void) ThrowMagickException(&images->exception,GetMagickModule(),
2665 ResourceLimitError,"MemoryAllocationFailed","`%s'",images->filename);
2666 return(MagickFalse);
2667 }
2668 number_images=GetImageListLength(images);
2669 image=images;
2670 for (i=0; image != (Image *) NULL; i++)
2671 {
2672 progress_monitor=SetImageProgressMonitor(image,(MagickProgressMonitor) NULL,
2673 image->client_data);
2674 status=ClassifyImageColors(cube_info,image,&image->exception);
2675 if (status == MagickFalse)
2676 break;
2677 (void) SetImageProgressMonitor(image,progress_monitor,image->client_data);
2678 proceed=SetImageProgress(image,AssignImageTag,i,number_images);
2679 if (proceed == MagickFalse)
2680 break;
2681 image=GetNextImageInList(image);
2682 }
2683 if (status != MagickFalse)
2684 {
2685 /*
2686 Reduce the number of colors in an image sequence.
2687 */
2688 ReduceImageColors(images,cube_info);
2689 image=images;
2690 for (i=0; image != (Image *) NULL; i++)
2691 {
2692 progress_monitor=SetImageProgressMonitor(image,(MagickProgressMonitor)
2693 NULL,image->client_data);
2694 status=AssignImageColors(image,cube_info);
2695 if (status == MagickFalse)
2696 break;
2697 (void) SetImageProgressMonitor(image,progress_monitor,
2698 image->client_data);
2699 proceed=SetImageProgress(image,AssignImageTag,i,number_images);
2700 if (proceed == MagickFalse)
2701 break;
2702 image=GetNextImageInList(image);
2703 }
2704 }
2705 DestroyCubeInfo(cube_info);
2706 return(status);
2707}
2708
2709/*
2710%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2711% %
2712% %
2713% %
2714+ R e d u c e %
2715% %
2716% %
2717% %
2718%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2719%
2720% Reduce() traverses the color cube tree and prunes any node whose
2721% quantization error falls below a particular threshold.
2722%
2723% The format of the Reduce method is:
2724%
2725% Reduce(const Image *image,CubeInfo *cube_info,const NodeInfo *node_info)
2726%
2727% A description of each parameter follows.
2728%
2729% o image: the image.
2730%
2731% o cube_info: A pointer to the Cube structure.
2732%
2733% o node_info: pointer to node in color cube tree that is to be pruned.
2734%
2735*/
2736static void Reduce(const Image *image,CubeInfo *cube_info,
2737 const NodeInfo *node_info)
2738{
2739 register long
2740 i;
2741
2742 unsigned long
2743 number_children;
2744
2745 /*
2746 Traverse any children.
2747 */
2748 number_children=cube_info->associate_alpha == MagickFalse ? 8UL : 16UL;
2749 for (i=0; i < (long) number_children; i++)
2750 if (node_info->child[i] != (NodeInfo *) NULL)
2751 Reduce(image,cube_info,node_info->child[i]);
2752 if (node_info->quantize_error <= cube_info->pruning_threshold)
2753 PruneChild(image,cube_info,node_info);
2754 else
2755 {
2756 /*
2757 Find minimum pruning threshold.
2758 */
2759 if (node_info->number_unique > 0)
2760 cube_info->colors++;
2761 if (node_info->quantize_error < cube_info->next_threshold)
2762 cube_info->next_threshold=node_info->quantize_error;
2763 }
2764}
2765
2766/*
2767%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2768% %
2769% %
2770% %
2771+ R e d u c e I m a g e C o l o r s %
2772% %
2773% %
2774% %
2775%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2776%
2777% ReduceImageColors() repeatedly prunes the tree until the number of nodes
2778% with n2 > 0 is less than or equal to the maximum number of colors allowed
2779% in the output image. On any given iteration over the tree, it selects
2780% those nodes whose E value is minimal for pruning and merges their
2781% color statistics upward. It uses a pruning threshold, Ep, to govern
2782% node selection as follows:
2783%
2784% Ep = 0
2785% while number of nodes with (n2 > 0) > required maximum number of colors
2786% prune all nodes such that E <= Ep
2787% Set Ep to minimum E in remaining nodes
2788%
2789% This has the effect of minimizing any quantization error when merging
2790% two nodes together.
2791%
2792% When a node to be pruned has offspring, the pruning procedure invokes
2793% itself recursively in order to prune the tree from the leaves upward.
2794% n2, Sr, Sg, and Sb in a node being pruned are always added to the
2795% corresponding data in that node's parent. This retains the pruned
2796% node's color characteristics for later averaging.
2797%
2798% For each node, n2 pixels exist for which that node represents the
2799% smallest volume in RGB space containing those pixel's colors. When n2
2800% > 0 the node will uniquely define a color in the output image. At the
2801% beginning of reduction, n2 = 0 for all nodes except a the leaves of
2802% the tree which represent colors present in the input image.
2803%
2804% The other pixel count, n1, indicates the total number of colors
2805% within the cubic volume which the node represents. This includes n1 -
2806% n2 pixels whose colors should be defined by nodes at a lower level in
2807% the tree.
2808%
2809% The format of the ReduceImageColors method is:
2810%
2811% ReduceImageColors(const Image *image,CubeInfo *cube_info)
2812%
2813% A description of each parameter follows.
2814%
2815% o image: the image.
2816%
2817% o cube_info: A pointer to the Cube structure.
2818%
2819*/
2820static void ReduceImageColors(const Image *image,CubeInfo *cube_info)
2821{
2822#define ReduceImageTag "Reduce/Image"
2823
2824 MagickBooleanType
2825 proceed;
2826
2827 MagickOffsetType
2828 offset;
2829
2830 unsigned long
2831 span;
2832
2833 cube_info->next_threshold=0.0;
2834 for (span=cube_info->colors; cube_info->colors > cube_info->maximum_colors; )
2835 {
2836 cube_info->pruning_threshold=cube_info->next_threshold;
2837 cube_info->next_threshold=cube_info->root->quantize_error-1;
2838 cube_info->colors=0;
2839 Reduce(image,cube_info,cube_info->root);
2840 offset=(MagickOffsetType) span-cube_info->colors;
2841 proceed=SetImageProgress(image,ReduceImageTag,offset,span-
2842 cube_info->maximum_colors+1);
2843 if (proceed == MagickFalse)
2844 break;
2845 }
2846}
2847
2848/*
2849%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2850% %
2851% %
2852% %
2853% R e m a p I m a g e %
2854% %
2855% %
2856% %
2857%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2858%
2859% RemapImage() replaces the colors of an image with the closest color from
2860% a reference image.
2861%
2862% The format of the RemapImage method is:
2863%
2864% MagickBooleanType RemapImage(const QuantizeInfo *quantize_info,
2865% Image *image,const Image *remap_image)
2866%
2867% A description of each parameter follows:
2868%
2869% o quantize_info: Specifies a pointer to an QuantizeInfo structure.
2870%
2871% o image: the image.
2872%
2873% o remap_image: the reference image.
2874%
2875*/
2876MagickExport MagickBooleanType RemapImage(const QuantizeInfo *quantize_info,
2877 Image *image,const Image *remap_image)
2878{
2879 CubeInfo
2880 *cube_info;
2881
2882 MagickBooleanType
2883 status;
2884
2885 /*
2886 Initialize color cube.
2887 */
2888 assert(image != (Image *) NULL);
2889 assert(image->signature == MagickSignature);
2890 if (image->debug != MagickFalse)
2891 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2892 assert(remap_image != (Image *) NULL);
2893 assert(remap_image->signature == MagickSignature);
2894 cube_info=GetCubeInfo(quantize_info,MaxTreeDepth,
2895 quantize_info->number_colors);
2896 if (cube_info == (CubeInfo *) NULL)
2897 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
2898 image->filename);
2899 status=ClassifyImageColors(cube_info,remap_image,&image->exception);
2900 if (status != MagickFalse)
2901 {
2902 /*
2903 Classify image colors from the reference image.
2904 */
2905 cube_info->quantize_info->number_colors=cube_info->colors;
2906 status=AssignImageColors(image,cube_info);
2907 }
2908 DestroyCubeInfo(cube_info);
2909 return(status);
2910}
2911
2912/*
2913%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2914% %
2915% %
2916% %
2917% R e m a p I m a g e s %
2918% %
2919% %
2920% %
2921%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2922%
2923% RemapImages() replaces the colors of a sequence of images with the
2924% closest color from a reference image.
2925%
2926% The format of the RemapImage method is:
2927%
2928% MagickBooleanType RemapImages(const QuantizeInfo *quantize_info,
2929% Image *images,Image *remap_image)
2930%
2931% A description of each parameter follows:
2932%
2933% o quantize_info: Specifies a pointer to an QuantizeInfo structure.
2934%
2935% o images: the image sequence.
2936%
2937% o remap_image: the reference image.
2938%
2939*/
2940MagickExport MagickBooleanType RemapImages(const QuantizeInfo *quantize_info,
2941 Image *images,const Image *remap_image)
2942{
2943 CubeInfo
2944 *cube_info;
2945
2946 Image
2947 *image;
2948
2949 MagickBooleanType
2950 status;
2951
2952 assert(images != (Image *) NULL);
2953 assert(images->signature == MagickSignature);
2954 if (images->debug != MagickFalse)
2955 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
2956 image=images;
2957 if (remap_image == (Image *) NULL)
2958 {
2959 /*
2960 Create a global colormap for an image sequence.
2961 */
2962 status=QuantizeImages(quantize_info,images);
2963 return(status);
2964 }
2965 /*
2966 Classify image colors from the reference image.
2967 */
2968 cube_info=GetCubeInfo(quantize_info,MaxTreeDepth,
2969 quantize_info->number_colors);
2970 if (cube_info == (CubeInfo *) NULL)
2971 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
2972 image->filename);
2973 status=ClassifyImageColors(cube_info,remap_image,&image->exception);
2974 if (status != MagickFalse)
2975 {
2976 /*
2977 Classify image colors from the reference image.
2978 */
2979 cube_info->quantize_info->number_colors=cube_info->colors;
2980 image=images;
2981 for ( ; image != (Image *) NULL; image=GetNextImageInList(image))
2982 {
2983 status=AssignImageColors(image,cube_info);
2984 if (status == MagickFalse)
2985 break;
2986 }
2987 }
2988 DestroyCubeInfo(cube_info);
2989 return(status);
2990}
2991
2992/*
2993%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2994% %
2995% %
2996% %
2997% S e t G r a y s c a l e I m a g e %
2998% %
2999% %
3000% %
3001%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3002%
3003% SetGrayscaleImage() converts an image to a PseudoClass grayscale image.
3004%
3005% The format of the SetGrayscaleImage method is:
3006%
3007% MagickBooleanType SetGrayscaleImage(Image *image)
3008%
3009% A description of each parameter follows:
3010%
3011% o image: The image.
3012%
3013*/
3014
3015#if defined(__cplusplus) || defined(c_plusplus)
3016extern "C" {
3017#endif
3018
3019static int IntensityCompare(const void *x,const void *y)
3020{
3021 long
3022 intensity;
3023
3024 PixelPacket
3025 *color_1,
3026 *color_2;
3027
3028 color_1=(PixelPacket *) x;
3029 color_2=(PixelPacket *) y;
3030 intensity=PixelIntensityToQuantum(color_1)-(long)
3031 PixelIntensityToQuantum(color_2);
3032 return(intensity);
3033}
3034
3035#if defined(__cplusplus) || defined(c_plusplus)
3036}
3037#endif
3038
3039static MagickBooleanType SetGrayscaleImage(Image *image)
3040{
cristyc4c8d132010-01-07 01:58:38 +00003041 CacheView
3042 *image_view;
3043
cristy3ed852e2009-09-05 21:47:34 +00003044 ExceptionInfo
3045 *exception;
3046
3047 long
3048 j,
3049 y;
3050
3051 PixelPacket
3052 *colormap;
3053
3054 long
3055 *colormap_index;
3056
3057 register long
3058 i;
3059
3060 MagickBooleanType
3061 status;
3062
cristy3ed852e2009-09-05 21:47:34 +00003063 assert(image != (Image *) NULL);
3064 assert(image->signature == MagickSignature);
3065 if (image->type != GrayscaleType)
3066 (void) TransformImageColorspace(image,GRAYColorspace);
3067 colormap_index=(long *) AcquireQuantumMemory(MaxMap+1,
3068 sizeof(*colormap_index));
3069 if (colormap_index == (long *) NULL)
3070 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
3071 image->filename);
3072 if (image->storage_class != PseudoClass)
3073 {
3074 ExceptionInfo
3075 *exception;
3076
3077 for (i=0; i <= (long) MaxMap; i++)
3078 colormap_index[i]=(-1);
3079 if (AcquireImageColormap(image,MaxMap+1) == MagickFalse)
3080 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
3081 image->filename);
3082 image->colors=0;
3083 status=MagickTrue;
3084 exception=(&image->exception);
3085 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +00003086#if defined(MAGICKCORE_OPENMP_SUPPORT)
3087 #pragma omp parallel for schedule(dynamic,4) shared(status)
cristy3ed852e2009-09-05 21:47:34 +00003088#endif
3089 for (y=0; y < (long) image->rows; y++)
3090 {
3091 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00003092 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00003093
3094 register long
3095 x;
3096
3097 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00003098 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003099
3100 if (status == MagickFalse)
3101 continue;
3102 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
3103 exception);
3104 if (q == (PixelPacket *) NULL)
3105 {
3106 status=MagickFalse;
3107 continue;
3108 }
3109 indexes=GetCacheViewAuthenticIndexQueue(image_view);
3110 for (x=0; x < (long) image->columns; x++)
3111 {
3112 register unsigned long
3113 intensity;
3114
3115 intensity=ScaleQuantumToMap(q->red);
3116 if (colormap_index[intensity] < 0)
3117 {
cristyb5d5f722009-11-04 03:03:49 +00003118#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00003119 #pragma omp critical (MagickCore_SetGrayscaleImage)
3120#endif
3121 if (colormap_index[intensity] < 0)
3122 {
3123 colormap_index[intensity]=(long) image->colors;
3124 image->colormap[image->colors]=(*q);
3125 image->colors++;
3126 }
3127 }
3128 indexes[x]=(IndexPacket) colormap_index[intensity];
3129 q++;
3130 }
3131 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3132 status=MagickFalse;
3133 }
3134 image_view=DestroyCacheView(image_view);
3135 }
3136 for (i=0; i < (long) image->colors; i++)
3137 image->colormap[i].opacity=(unsigned short) i;
3138 qsort((void *) image->colormap,image->colors,sizeof(PixelPacket),
3139 IntensityCompare);
3140 colormap=(PixelPacket *) AcquireQuantumMemory(image->colors,
3141 sizeof(*colormap));
3142 if (colormap == (PixelPacket *) NULL)
3143 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
3144 image->filename);
3145 j=0;
3146 colormap[j]=image->colormap[0];
3147 for (i=0; i < (long) image->colors; i++)
3148 {
3149 if (IsSameColor(image,&colormap[j],&image->colormap[i]) == MagickFalse)
3150 {
3151 j++;
3152 colormap[j]=image->colormap[i];
3153 }
3154 colormap_index[(long) image->colormap[i].opacity]=j;
3155 }
3156 image->colors=(unsigned long) (j+1);
3157 image->colormap=(PixelPacket *) RelinquishMagickMemory(image->colormap);
3158 image->colormap=colormap;
3159 status=MagickTrue;
3160 exception=(&image->exception);
3161 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +00003162#if defined(MAGICKCORE_OPENMP_SUPPORT)
3163 #pragma omp parallel for schedule(dynamic,4) shared(status)
cristy3ed852e2009-09-05 21:47:34 +00003164#endif
3165 for (y=0; y < (long) image->rows; y++)
3166 {
3167 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00003168 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00003169
3170 register long
3171 x;
3172
3173 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00003174 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00003175
3176 if (status == MagickFalse)
3177 continue;
3178 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3179 if (q == (PixelPacket *) NULL)
3180 {
3181 status=MagickFalse;
3182 continue;
3183 }
3184 indexes=GetCacheViewAuthenticIndexQueue(image_view);
3185 for (x=0; x < (long) image->columns; x++)
3186 indexes[x]=(IndexPacket) colormap_index[ScaleQuantumToMap(indexes[x])];
3187 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3188 status=MagickFalse;
3189 }
3190 image_view=DestroyCacheView(image_view);
3191 colormap_index=(long *) RelinquishMagickMemory(colormap_index);
3192 image->type=GrayscaleType;
3193 if (IsMonochromeImage(image,&image->exception) != MagickFalse)
3194 image->type=BilevelType;
3195 return(status);
3196}