1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 % %
4 % %
5 % %
6 % QQQ U U AAA N N TTTTT IIIII ZZZZZ EEEEE %
7 % Q Q U U A A NN N T I ZZ E %
8 % Q Q U U AAAAA N N N T I ZZZ EEEEE %
9 % Q QQ U U A A N NN T I ZZ E %
10 % QQQQ UUU A A N N T IIIII ZZZZZ EEEEE %
11 % %
12 % %
13 % MagickCore Methods to Reduce the Number of Unique Colors in an Image %
14 % %
15 % Software Design %
16 % Cristy %
17 % July 1992 %
18 % %
19 % %
20 % Copyright 1999-2019 ImageMagick Studio LLC, a non-profit organization %
21 % dedicated to making software imaging solutions freely available. %
22 % %
23 % You may not use this file except in compliance with the License. You may %
24 % obtain a copy of the License at %
25 % %
26 % https://imagemagick.org/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 (vertex
65 % nearest the origin in RGB space and the vertex farthest from the origin).
66 %
67 % The tree's root node represents the entire domain, (0,0,0) through
68 % (Cmax,Cmax,Cmax). Each lower level in the tree is generated by
69 % subdividing one node's cube into eight smaller cubes of equal size.
70 % This corresponds to bisecting the parent cube with planes passing
71 % through the midpoints of each edge.
72 %
73 % The basic algorithm operates in three phases: Classification,
74 % Reduction, and Assignment. Classification builds a color description
75 % tree for the image. Reduction collapses the tree until the number it
76 % represents, at most, the number of colors desired in the output image.
77 % Assignment defines the output image's color map and sets each pixel's
78 % color by restorage_class in the reduced tree. Our goal is to minimize
79 % the numerical discrepancies between the original colors and quantized
80 % colors (quantization error).
81 %
82 % Classification begins by initializing a color description tree of
83 % sufficient depth to represent each possible input color in a leaf.
84 % However, it is impractical to generate a fully-formed color description
85 % tree in the storage_class phase for realistic values of Cmax. If
86 % colors components in the input image are quantized to k-bit precision,
87 % so that Cmax= 2k-1, the tree would need k levels below the root node to
88 % allow representing each possible input color in a leaf. This becomes
89 % prohibitive because the tree's total number of nodes is 1 +
90 % sum(i=1, k, 8k).
91 %
92 % A complete tree would require 19,173,961 nodes for k = 8, Cmax = 255.
93 % Therefore, to avoid building a fully populated tree, QUANTIZE: (1)
94 % Initializes data structures for nodes only as they are needed; (2)
95 % Chooses a maximum depth for the tree as a function of the desired
96 % number of colors in the output image (currently log2(colormap size)).
97 %
98 % For each pixel in the input image, storage_class scans downward from
99 % the root of the color description tree. At each level of the tree it
100 % identifies the single node which represents a cube in RGB space
101 % containing the pixel's color. It updates the following data for each
102 % such node:
103 %
104 % n1: Number of pixels whose color is contained in the RGB cube which
105 % this node represents;
106 %
107 % n2: Number of pixels whose color is not represented in a node at
108 % lower depth in the tree; initially, n2 = 0 for all nodes except
109 % leaves of the tree.
110 %
111 % Sr, Sg, Sb: Sums of the red, green, and blue component values for all
112 % pixels not classified at a lower depth. The combination of these sums
113 % and n2 will ultimately characterize the mean color of a set of pixels
114 % represented by this node.
115 %
116 % E: the distance squared in RGB space between each pixel contained
117 % within a node and the nodes' center. This represents the
118 % quantization error for a node.
119 %
120 % Reduction repeatedly prunes the tree until the number of nodes with n2
121 % > 0 is less than or equal to the maximum number of colors allowed in
122 % the output image. On any given iteration over the tree, it selects
123 % those nodes whose E count is minimal for pruning and merges their color
124 % statistics upward. It uses a pruning threshold, Ep, to govern node
125 % selection as follows:
126 %
127 % Ep = 0
128 % while number of nodes with (n2 > 0) > required maximum number of colors
129 % prune all nodes such that E <= Ep
130 % Set Ep to minimum E in remaining nodes
131 %
132 % This has the effect of minimizing any quantization error when merging
133 % two nodes together.
134 %
135 % When a node to be pruned has offspring, the pruning procedure invokes
136 % itself recursively in order to prune the tree from the leaves upward.
137 % n2, Sr, Sg, and Sb in a node being pruned are always added to the
138 % corresponding data in that node's parent. This retains the pruned
139 % node's color characteristics for later averaging.
140 %
141 % For each node, n2 pixels exist for which that node represents the
142 % smallest volume in RGB space containing those pixel's colors. When n2
143 % > 0 the node will uniquely define a color in the output image. At the
144 % beginning of reduction, n2 = 0 for all nodes except a the leaves of
145 % the tree which represent colors present in the input image.
146 %
147 % The other pixel count, n1, indicates the total number of colors within
148 % the cubic volume which the node represents. This includes n1 - n2
149 % pixels whose colors should be defined by nodes at a lower level in the
150 % tree.
151 %
152 % Assignment generates the output image from the pruned tree. The output
153 % image consists of two parts: (1) A color map, which is an array of
154 % color descriptions (RGB triples) for each color present in the output
155 % image; (2) A pixel array, which represents each pixel as an index
156 % into the color map array.
157 %
158 % First, the assignment phase makes one pass over the pruned color
159 % description tree to establish the image's color map. For each node
160 % with n2 > 0, it divides Sr, Sg, and Sb by n2 . This produces the mean
161 % color of all pixels that classify no lower than this node. Each of
162 % these colors becomes an entry in the color map.
163 %
164 % Finally, the assignment phase reclassifies each pixel in the pruned
165 % tree to identify the deepest node containing the pixel's color. The
166 % pixel's value in the pixel array becomes the index of this node's mean
167 % color in the color map.
168 %
169 % This method is based on a similar algorithm written by Paul Raveling.
170 %
171 */
172
173 /*
174 Include declarations.
175 */
176 #include "MagickCore/studio.h"
177 #include "MagickCore/artifact.h"
178 #include "MagickCore/attribute.h"
179 #include "MagickCore/cache-view.h"
180 #include "MagickCore/color.h"
181 #include "MagickCore/color-private.h"
182 #include "MagickCore/colormap.h"
183 #include "MagickCore/colorspace.h"
184 #include "MagickCore/colorspace-private.h"
185 #include "MagickCore/enhance.h"
186 #include "MagickCore/exception.h"
187 #include "MagickCore/exception-private.h"
188 #include "MagickCore/histogram.h"
189 #include "MagickCore/image.h"
190 #include "MagickCore/image-private.h"
191 #include "MagickCore/list.h"
192 #include "MagickCore/memory_.h"
193 #include "MagickCore/memory-private.h"
194 #include "MagickCore/monitor.h"
195 #include "MagickCore/monitor-private.h"
196 #include "MagickCore/option.h"
197 #include "MagickCore/pixel-accessor.h"
198 #include "MagickCore/pixel-private.h"
199 #include "MagickCore/quantize.h"
200 #include "MagickCore/quantum.h"
201 #include "MagickCore/quantum-private.h"
202 #include "MagickCore/resource_.h"
203 #include "MagickCore/string_.h"
204 #include "MagickCore/string-private.h"
205 #include "MagickCore/thread-private.h"
206
207 /*
208 Define declarations.
209 */
210 #if !defined(__APPLE__) && !defined(TARGET_OS_IPHONE)
211 #define CacheShift 2
212 #else
213 #define CacheShift 3
214 #endif
215 #define ErrorQueueLength 16
216 #define MaxNodes 266817
217 #define MaxTreeDepth 8
218 #define NodesInAList 1920
219
220 /*
221 Typdef declarations.
222 */
223 typedef struct _DoublePixelPacket
224 {
225 double
226 red,
227 green,
228 blue,
229 alpha;
230 } DoublePixelPacket;
231
232 typedef struct _NodeInfo
233 {
234 struct _NodeInfo
235 *parent,
236 *child[16];
237
238 MagickSizeType
239 number_unique;
240
241 DoublePixelPacket
242 total_color;
243
244 double
245 quantize_error;
246
247 size_t
248 color_number,
249 id,
250 level;
251 } NodeInfo;
252
253 typedef struct _Nodes
254 {
255 NodeInfo
256 *nodes;
257
258 struct _Nodes
259 *next;
260 } Nodes;
261
262 typedef struct _CubeInfo
263 {
264 NodeInfo
265 *root;
266
267 size_t
268 colors,
269 maximum_colors;
270
271 ssize_t
272 transparent_index;
273
274 MagickSizeType
275 transparent_pixels;
276
277 DoublePixelPacket
278 target;
279
280 double
281 distance,
282 pruning_threshold,
283 next_threshold;
284
285 size_t
286 nodes,
287 free_nodes,
288 color_number;
289
290 NodeInfo
291 *next_node;
292
293 Nodes
294 *node_queue;
295
296 MemoryInfo
297 *memory_info;
298
299 ssize_t
300 *cache;
301
302 DoublePixelPacket
303 error[ErrorQueueLength];
304
305 double
306 weights[ErrorQueueLength];
307
308 QuantizeInfo
309 *quantize_info;
310
311 MagickBooleanType
312 associate_alpha;
313
314 ssize_t
315 x,
316 y;
317
318 size_t
319 depth;
320
321 MagickOffsetType
322 offset;
323
324 MagickSizeType
325 span;
326 } CubeInfo;
327
328 /*
329 Method prototypes.
330 */
331 static CubeInfo
332 *GetCubeInfo(const QuantizeInfo *,const size_t,const size_t);
333
334 static NodeInfo
335 *GetNodeInfo(CubeInfo *,const size_t,const size_t,NodeInfo *);
336
337 static MagickBooleanType
338 AssignImageColors(Image *,CubeInfo *,ExceptionInfo *),
339 ClassifyImageColors(CubeInfo *,const Image *,ExceptionInfo *),
340 DitherImage(Image *,CubeInfo *,ExceptionInfo *),
341 SetGrayscaleImage(Image *,ExceptionInfo *);
342
343 static size_t
344 DefineImageColormap(Image *,CubeInfo *,NodeInfo *);
345
346 static void
347 ClosestColor(const Image *,CubeInfo *,const NodeInfo *),
348 DestroyCubeInfo(CubeInfo *),
349 PruneLevel(CubeInfo *,const NodeInfo *),
350 PruneToCubeDepth(CubeInfo *,const NodeInfo *),
351 ReduceImageColors(const Image *,CubeInfo *);
352
353 /*
354 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
355 % %
356 % %
357 % %
358 % A c q u i r e Q u a n t i z e I n f o %
359 % %
360 % %
361 % %
362 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
363 %
364 % AcquireQuantizeInfo() allocates the QuantizeInfo structure.
365 %
366 % The format of the AcquireQuantizeInfo method is:
367 %
368 % QuantizeInfo *AcquireQuantizeInfo(const ImageInfo *image_info)
369 %
370 % A description of each parameter follows:
371 %
372 % o image_info: the image info.
373 %
374 */
AcquireQuantizeInfo(const ImageInfo * image_info)375 MagickExport QuantizeInfo *AcquireQuantizeInfo(const ImageInfo *image_info)
376 {
377 QuantizeInfo
378 *quantize_info;
379
380 quantize_info=(QuantizeInfo *) AcquireCriticalMemory(sizeof(*quantize_info));
381 GetQuantizeInfo(quantize_info);
382 if (image_info != (ImageInfo *) NULL)
383 {
384 const char
385 *option;
386
387 quantize_info->dither_method=image_info->dither == MagickFalse ?
388 NoDitherMethod : RiemersmaDitherMethod;
389 option=GetImageOption(image_info,"dither");
390 if (option != (const char *) NULL)
391 quantize_info->dither_method=(DitherMethod) ParseCommandOption(
392 MagickDitherOptions,MagickFalse,option);
393 quantize_info->measure_error=image_info->verbose;
394 }
395 return(quantize_info);
396 }
397
398 /*
399 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
400 % %
401 % %
402 % %
403 + A s s i g n I m a g e C o l o r s %
404 % %
405 % %
406 % %
407 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
408 %
409 % AssignImageColors() generates the output image from the pruned tree. The
410 % output image consists of two parts: (1) A color map, which is an array
411 % of color descriptions (RGB triples) for each color present in the
412 % output image; (2) A pixel array, which represents each pixel as an
413 % index into the color map array.
414 %
415 % First, the assignment phase makes one pass over the pruned color
416 % description tree to establish the image's color map. For each node
417 % with n2 > 0, it divides Sr, Sg, and Sb by n2 . This produces the mean
418 % color of all pixels that classify no lower than this node. Each of
419 % these colors becomes an entry in the color map.
420 %
421 % Finally, the assignment phase reclassifies each pixel in the pruned
422 % tree to identify the deepest node containing the pixel's color. The
423 % pixel's value in the pixel array becomes the index of this node's mean
424 % color in the color map.
425 %
426 % The format of the AssignImageColors() method is:
427 %
428 % MagickBooleanType AssignImageColors(Image *image,CubeInfo *cube_info)
429 %
430 % A description of each parameter follows.
431 %
432 % o image: the image.
433 %
434 % o cube_info: A pointer to the Cube structure.
435 %
436 */
437
AssociateAlphaPixel(const Image * image,const CubeInfo * cube_info,const Quantum * pixel,DoublePixelPacket * alpha_pixel)438 static inline void AssociateAlphaPixel(const Image *image,
439 const CubeInfo *cube_info,const Quantum *pixel,DoublePixelPacket *alpha_pixel)
440 {
441 double
442 alpha;
443
444 if ((cube_info->associate_alpha == MagickFalse) ||
445 (GetPixelAlpha(image,pixel) == OpaqueAlpha))
446 {
447 alpha_pixel->red=(double) GetPixelRed(image,pixel);
448 alpha_pixel->green=(double) GetPixelGreen(image,pixel);
449 alpha_pixel->blue=(double) GetPixelBlue(image,pixel);
450 alpha_pixel->alpha=(double) GetPixelAlpha(image,pixel);
451 return;
452 }
453 alpha=(double) (QuantumScale*GetPixelAlpha(image,pixel));
454 alpha_pixel->red=alpha*GetPixelRed(image,pixel);
455 alpha_pixel->green=alpha*GetPixelGreen(image,pixel);
456 alpha_pixel->blue=alpha*GetPixelBlue(image,pixel);
457 alpha_pixel->alpha=(double) GetPixelAlpha(image,pixel);
458 }
459
AssociateAlphaPixelInfo(const CubeInfo * cube_info,const PixelInfo * pixel,DoublePixelPacket * alpha_pixel)460 static inline void AssociateAlphaPixelInfo(const CubeInfo *cube_info,
461 const PixelInfo *pixel,DoublePixelPacket *alpha_pixel)
462 {
463 double
464 alpha;
465
466 if ((cube_info->associate_alpha == MagickFalse) ||
467 (pixel->alpha == OpaqueAlpha))
468 {
469 alpha_pixel->red=(double) pixel->red;
470 alpha_pixel->green=(double) pixel->green;
471 alpha_pixel->blue=(double) pixel->blue;
472 alpha_pixel->alpha=(double) pixel->alpha;
473 return;
474 }
475 alpha=(double) (QuantumScale*pixel->alpha);
476 alpha_pixel->red=alpha*pixel->red;
477 alpha_pixel->green=alpha*pixel->green;
478 alpha_pixel->blue=alpha*pixel->blue;
479 alpha_pixel->alpha=(double) pixel->alpha;
480 }
481
ColorToNodeId(const CubeInfo * cube_info,const DoublePixelPacket * pixel,size_t index)482 static inline size_t ColorToNodeId(const CubeInfo *cube_info,
483 const DoublePixelPacket *pixel,size_t index)
484 {
485 size_t
486 id;
487
488 id=(size_t) (((ScaleQuantumToChar(ClampPixel(pixel->red)) >> index) & 0x01) |
489 ((ScaleQuantumToChar(ClampPixel(pixel->green)) >> index) & 0x01) << 1 |
490 ((ScaleQuantumToChar(ClampPixel(pixel->blue)) >> index) & 0x01) << 2);
491 if (cube_info->associate_alpha != MagickFalse)
492 id|=((ScaleQuantumToChar(ClampPixel(pixel->alpha)) >> index) & 0x1) << 3;
493 return(id);
494 }
495
AssignImageColors(Image * image,CubeInfo * cube_info,ExceptionInfo * exception)496 static MagickBooleanType AssignImageColors(Image *image,CubeInfo *cube_info,
497 ExceptionInfo *exception)
498 {
499 #define AssignImageTag "Assign/Image"
500
501 ColorspaceType
502 colorspace;
503
504 ssize_t
505 y;
506
507 /*
508 Allocate image colormap.
509 */
510 colorspace=image->colorspace;
511 if (cube_info->quantize_info->colorspace != UndefinedColorspace)
512 (void) TransformImageColorspace(image,cube_info->quantize_info->colorspace,
513 exception);
514 if (AcquireImageColormap(image,cube_info->colors,exception) == MagickFalse)
515 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
516 image->filename);
517 image->colors=0;
518 cube_info->transparent_pixels=0;
519 cube_info->transparent_index=(-1);
520 (void) DefineImageColormap(image,cube_info,cube_info->root);
521 /*
522 Create a reduced color image.
523 */
524 if (cube_info->quantize_info->dither_method != NoDitherMethod)
525 (void) DitherImage(image,cube_info,exception);
526 else
527 {
528 CacheView
529 *image_view;
530
531 MagickBooleanType
532 status;
533
534 status=MagickTrue;
535 image_view=AcquireAuthenticCacheView(image,exception);
536 #if defined(MAGICKCORE_OPENMP_SUPPORT)
537 #pragma omp parallel for schedule(static) shared(status) \
538 magick_number_threads(image,image,image->rows,1)
539 #endif
540 for (y=0; y < (ssize_t) image->rows; y++)
541 {
542 CubeInfo
543 cube;
544
545 register Quantum
546 *magick_restrict q;
547
548 register ssize_t
549 x;
550
551 ssize_t
552 count;
553
554 if (status == MagickFalse)
555 continue;
556 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
557 exception);
558 if (q == (Quantum *) NULL)
559 {
560 status=MagickFalse;
561 continue;
562 }
563 cube=(*cube_info);
564 for (x=0; x < (ssize_t) image->columns; x+=count)
565 {
566 DoublePixelPacket
567 pixel;
568
569 register const NodeInfo
570 *node_info;
571
572 register ssize_t
573 i;
574
575 size_t
576 id,
577 index;
578
579 /*
580 Identify the deepest node containing the pixel's color.
581 */
582 for (count=1; (x+count) < (ssize_t) image->columns; count++)
583 {
584 PixelInfo
585 packet;
586
587 GetPixelInfoPixel(image,q+count*GetPixelChannels(image),&packet);
588 if (IsPixelEquivalent(image,q,&packet) == MagickFalse)
589 break;
590 }
591 AssociateAlphaPixel(image,&cube,q,&pixel);
592 node_info=cube.root;
593 for (index=MaxTreeDepth-1; (ssize_t) index > 0; index--)
594 {
595 id=ColorToNodeId(&cube,&pixel,index);
596 if (node_info->child[id] == (NodeInfo *) NULL)
597 break;
598 node_info=node_info->child[id];
599 }
600 /*
601 Find closest color among siblings and their children.
602 */
603 cube.target=pixel;
604 cube.distance=(double) (4.0*(QuantumRange+1.0)*(QuantumRange+1.0)+
605 1.0);
606 ClosestColor(image,&cube,node_info->parent);
607 index=cube.color_number;
608 for (i=0; i < (ssize_t) count; i++)
609 {
610 if (image->storage_class == PseudoClass)
611 SetPixelIndex(image,(Quantum) index,q);
612 if (cube.quantize_info->measure_error == MagickFalse)
613 {
614 SetPixelRed(image,ClampToQuantum(
615 image->colormap[index].red),q);
616 SetPixelGreen(image,ClampToQuantum(
617 image->colormap[index].green),q);
618 SetPixelBlue(image,ClampToQuantum(
619 image->colormap[index].blue),q);
620 if (cube.associate_alpha != MagickFalse)
621 SetPixelAlpha(image,ClampToQuantum(
622 image->colormap[index].alpha),q);
623 }
624 q+=GetPixelChannels(image);
625 }
626 }
627 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
628 status=MagickFalse;
629 if (image->progress_monitor != (MagickProgressMonitor) NULL)
630 {
631 MagickBooleanType
632 proceed;
633
634 proceed=SetImageProgress(image,AssignImageTag,(MagickOffsetType) y,
635 image->rows);
636 if (proceed == MagickFalse)
637 status=MagickFalse;
638 }
639 }
640 image_view=DestroyCacheView(image_view);
641 }
642 if (cube_info->quantize_info->measure_error != MagickFalse)
643 (void) GetImageQuantizeError(image,exception);
644 if ((cube_info->quantize_info->number_colors == 2) &&
645 ((cube_info->quantize_info->colorspace == LinearGRAYColorspace) ||
646 (cube_info->quantize_info->colorspace == GRAYColorspace)))
647 {
648 double
649 intensity;
650
651 /*
652 Monochrome image.
653 */
654 intensity=0.0;
655 if ((image->colors > 1) &&
656 (GetPixelInfoLuma(image->colormap+0) >
657 GetPixelInfoLuma(image->colormap+1)))
658 intensity=(double) QuantumRange;
659 image->colormap[0].red=intensity;
660 image->colormap[0].green=intensity;
661 image->colormap[0].blue=intensity;
662 if (image->colors > 1)
663 {
664 image->colormap[1].red=(double) QuantumRange-intensity;
665 image->colormap[1].green=(double) QuantumRange-intensity;
666 image->colormap[1].blue=(double) QuantumRange-intensity;
667 }
668 }
669 (void) SyncImage(image,exception);
670 if ((cube_info->quantize_info->colorspace != UndefinedColorspace) &&
671 (IssRGBCompatibleColorspace(colorspace) == MagickFalse))
672 (void) TransformImageColorspace(image,colorspace,exception);
673 return(MagickTrue);
674 }
675
676 /*
677 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
678 % %
679 % %
680 % %
681 + C l a s s i f y I m a g e C o l o r s %
682 % %
683 % %
684 % %
685 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
686 %
687 % ClassifyImageColors() begins by initializing a color description tree
688 % of sufficient depth to represent each possible input color in a leaf.
689 % However, it is impractical to generate a fully-formed color
690 % description tree in the storage_class phase for realistic values of
691 % Cmax. If colors components in the input image are quantized to k-bit
692 % precision, so that Cmax= 2k-1, the tree would need k levels below the
693 % root node to allow representing each possible input color in a leaf.
694 % This becomes prohibitive because the tree's total number of nodes is
695 % 1 + sum(i=1,k,8k).
696 %
697 % A complete tree would require 19,173,961 nodes for k = 8, Cmax = 255.
698 % Therefore, to avoid building a fully populated tree, QUANTIZE: (1)
699 % Initializes data structures for nodes only as they are needed; (2)
700 % Chooses a maximum depth for the tree as a function of the desired
701 % number of colors in the output image (currently log2(colormap size)).
702 %
703 % For each pixel in the input image, storage_class scans downward from
704 % the root of the color description tree. At each level of the tree it
705 % identifies the single node which represents a cube in RGB space
706 % containing It updates the following data for each such node:
707 %
708 % n1 : Number of pixels whose color is contained in the RGB cube
709 % which this node represents;
710 %
711 % n2 : Number of pixels whose color is not represented in a node at
712 % lower depth in the tree; initially, n2 = 0 for all nodes except
713 % leaves of the tree.
714 %
715 % Sr, Sg, Sb : Sums of the red, green, and blue component values for
716 % all pixels not classified at a lower depth. The combination of
717 % these sums and n2 will ultimately characterize the mean color of a
718 % set of pixels represented by this node.
719 %
720 % E: the distance squared in RGB space between each pixel contained
721 % within a node and the nodes' center. This represents the quantization
722 % error for a node.
723 %
724 % The format of the ClassifyImageColors() method is:
725 %
726 % MagickBooleanType ClassifyImageColors(CubeInfo *cube_info,
727 % const Image *image,ExceptionInfo *exception)
728 %
729 % A description of each parameter follows.
730 %
731 % o cube_info: A pointer to the Cube structure.
732 %
733 % o image: the image.
734 %
735 */
736
SetAssociatedAlpha(const Image * image,CubeInfo * cube_info)737 static inline void SetAssociatedAlpha(const Image *image,CubeInfo *cube_info)
738 {
739 MagickBooleanType
740 associate_alpha;
741
742 associate_alpha=image->alpha_trait == BlendPixelTrait ? MagickTrue :
743 MagickFalse;
744 if ((cube_info->quantize_info->number_colors == 2) &&
745 ((cube_info->quantize_info->colorspace == LinearGRAYColorspace) ||
746 (cube_info->quantize_info->colorspace == GRAYColorspace)))
747 associate_alpha=MagickFalse;
748 cube_info->associate_alpha=associate_alpha;
749 }
750
ClassifyImageColors(CubeInfo * cube_info,const Image * image,ExceptionInfo * exception)751 static MagickBooleanType ClassifyImageColors(CubeInfo *cube_info,
752 const Image *image,ExceptionInfo *exception)
753 {
754 #define ClassifyImageTag "Classify/Image"
755
756 CacheView
757 *image_view;
758
759 DoublePixelPacket
760 error,
761 mid,
762 midpoint,
763 pixel;
764
765 MagickBooleanType
766 proceed;
767
768 double
769 bisect;
770
771 NodeInfo
772 *node_info;
773
774 size_t
775 count,
776 id,
777 index,
778 level;
779
780 ssize_t
781 y;
782
783 /*
784 Classify the first cube_info->maximum_colors colors to a tree depth of 8.
785 */
786 SetAssociatedAlpha(image,cube_info);
787 if ((cube_info->quantize_info->colorspace != UndefinedColorspace) &&
788 (cube_info->quantize_info->colorspace != CMYKColorspace))
789 (void) TransformImageColorspace((Image *) image,
790 cube_info->quantize_info->colorspace,exception);
791 else
792 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
793 (void) TransformImageColorspace((Image *) image,sRGBColorspace,exception);
794 midpoint.red=(double) QuantumRange/2.0;
795 midpoint.green=(double) QuantumRange/2.0;
796 midpoint.blue=(double) QuantumRange/2.0;
797 midpoint.alpha=(double) QuantumRange/2.0;
798 error.alpha=0.0;
799 image_view=AcquireVirtualCacheView(image,exception);
800 for (y=0; y < (ssize_t) image->rows; y++)
801 {
802 register const Quantum
803 *magick_restrict p;
804
805 register ssize_t
806 x;
807
808 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
809 if (p == (const Quantum *) NULL)
810 break;
811 if (cube_info->nodes > MaxNodes)
812 {
813 /*
814 Prune one level if the color tree is too large.
815 */
816 PruneLevel(cube_info,cube_info->root);
817 cube_info->depth--;
818 }
819 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) count)
820 {
821 /*
822 Start at the root and descend the color cube tree.
823 */
824 for (count=1; (x+(ssize_t) count) < (ssize_t) image->columns; count++)
825 {
826 PixelInfo
827 packet;
828
829 GetPixelInfoPixel(image,p+count*GetPixelChannels(image),&packet);
830 if (IsPixelEquivalent(image,p,&packet) == MagickFalse)
831 break;
832 }
833 AssociateAlphaPixel(image,cube_info,p,&pixel);
834 index=MaxTreeDepth-1;
835 bisect=((double) QuantumRange+1.0)/2.0;
836 mid=midpoint;
837 node_info=cube_info->root;
838 for (level=1; level <= MaxTreeDepth; level++)
839 {
840 double
841 distance;
842
843 bisect*=0.5;
844 id=ColorToNodeId(cube_info,&pixel,index);
845 mid.red+=(id & 1) != 0 ? bisect : -bisect;
846 mid.green+=(id & 2) != 0 ? bisect : -bisect;
847 mid.blue+=(id & 4) != 0 ? bisect : -bisect;
848 mid.alpha+=(id & 8) != 0 ? bisect : -bisect;
849 if (node_info->child[id] == (NodeInfo *) NULL)
850 {
851 /*
852 Set colors of new node to contain pixel.
853 */
854 node_info->child[id]=GetNodeInfo(cube_info,id,level,node_info);
855 if (node_info->child[id] == (NodeInfo *) NULL)
856 {
857 (void) ThrowMagickException(exception,GetMagickModule(),
858 ResourceLimitError,"MemoryAllocationFailed","`%s'",
859 image->filename);
860 continue;
861 }
862 if (level == MaxTreeDepth)
863 cube_info->colors++;
864 }
865 /*
866 Approximate the quantization error represented by this node.
867 */
868 node_info=node_info->child[id];
869 error.red=QuantumScale*(pixel.red-mid.red);
870 error.green=QuantumScale*(pixel.green-mid.green);
871 error.blue=QuantumScale*(pixel.blue-mid.blue);
872 if (cube_info->associate_alpha != MagickFalse)
873 error.alpha=QuantumScale*(pixel.alpha-mid.alpha);
874 distance=(double) (error.red*error.red+error.green*error.green+
875 error.blue*error.blue+error.alpha*error.alpha);
876 if (IsNaN(distance))
877 distance=0.0;
878 node_info->quantize_error+=count*sqrt(distance);
879 cube_info->root->quantize_error+=node_info->quantize_error;
880 index--;
881 }
882 /*
883 Sum RGB for this leaf for later derivation of the mean cube color.
884 */
885 node_info->number_unique+=count;
886 node_info->total_color.red+=count*QuantumScale*ClampPixel(pixel.red);
887 node_info->total_color.green+=count*QuantumScale*ClampPixel(pixel.green);
888 node_info->total_color.blue+=count*QuantumScale*ClampPixel(pixel.blue);
889 if (cube_info->associate_alpha != MagickFalse)
890 node_info->total_color.alpha+=count*QuantumScale*
891 ClampPixel(pixel.alpha);
892 else
893 node_info->total_color.alpha+=count*QuantumScale*
894 ClampPixel((MagickRealType) OpaqueAlpha);
895 p+=count*GetPixelChannels(image);
896 }
897 if (cube_info->colors > cube_info->maximum_colors)
898 {
899 PruneToCubeDepth(cube_info,cube_info->root);
900 break;
901 }
902 proceed=SetImageProgress(image,ClassifyImageTag,(MagickOffsetType) y,
903 image->rows);
904 if (proceed == MagickFalse)
905 break;
906 }
907 for (y++; y < (ssize_t) image->rows; y++)
908 {
909 register const Quantum
910 *magick_restrict p;
911
912 register ssize_t
913 x;
914
915 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
916 if (p == (const Quantum *) NULL)
917 break;
918 if (cube_info->nodes > MaxNodes)
919 {
920 /*
921 Prune one level if the color tree is too large.
922 */
923 PruneLevel(cube_info,cube_info->root);
924 cube_info->depth--;
925 }
926 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) count)
927 {
928 /*
929 Start at the root and descend the color cube tree.
930 */
931 for (count=1; (x+(ssize_t) count) < (ssize_t) image->columns; count++)
932 {
933 PixelInfo
934 packet;
935
936 GetPixelInfoPixel(image,p+count*GetPixelChannels(image),&packet);
937 if (IsPixelEquivalent(image,p,&packet) == MagickFalse)
938 break;
939 }
940 AssociateAlphaPixel(image,cube_info,p,&pixel);
941 index=MaxTreeDepth-1;
942 bisect=((double) QuantumRange+1.0)/2.0;
943 mid=midpoint;
944 node_info=cube_info->root;
945 for (level=1; level <= cube_info->depth; level++)
946 {
947 double
948 distance;
949
950 bisect*=0.5;
951 id=ColorToNodeId(cube_info,&pixel,index);
952 mid.red+=(id & 1) != 0 ? bisect : -bisect;
953 mid.green+=(id & 2) != 0 ? bisect : -bisect;
954 mid.blue+=(id & 4) != 0 ? bisect : -bisect;
955 mid.alpha+=(id & 8) != 0 ? bisect : -bisect;
956 if (node_info->child[id] == (NodeInfo *) NULL)
957 {
958 /*
959 Set colors of new node to contain pixel.
960 */
961 node_info->child[id]=GetNodeInfo(cube_info,id,level,node_info);
962 if (node_info->child[id] == (NodeInfo *) NULL)
963 {
964 (void) ThrowMagickException(exception,GetMagickModule(),
965 ResourceLimitError,"MemoryAllocationFailed","%s",
966 image->filename);
967 continue;
968 }
969 if (level == cube_info->depth)
970 cube_info->colors++;
971 }
972 /*
973 Approximate the quantization error represented by this node.
974 */
975 node_info=node_info->child[id];
976 error.red=QuantumScale*(pixel.red-mid.red);
977 error.green=QuantumScale*(pixel.green-mid.green);
978 error.blue=QuantumScale*(pixel.blue-mid.blue);
979 if (cube_info->associate_alpha != MagickFalse)
980 error.alpha=QuantumScale*(pixel.alpha-mid.alpha);
981 distance=(double) (error.red*error.red+error.green*error.green+
982 error.blue*error.blue+error.alpha*error.alpha);
983 if (IsNaN(distance) != MagickFalse)
984 distance=0.0;
985 node_info->quantize_error+=count*sqrt(distance);
986 cube_info->root->quantize_error+=node_info->quantize_error;
987 index--;
988 }
989 /*
990 Sum RGB for this leaf for later derivation of the mean cube color.
991 */
992 node_info->number_unique+=count;
993 node_info->total_color.red+=count*QuantumScale*ClampPixel(pixel.red);
994 node_info->total_color.green+=count*QuantumScale*ClampPixel(pixel.green);
995 node_info->total_color.blue+=count*QuantumScale*ClampPixel(pixel.blue);
996 if (cube_info->associate_alpha != MagickFalse)
997 node_info->total_color.alpha+=count*QuantumScale*
998 ClampPixel(pixel.alpha);
999 else
1000 node_info->total_color.alpha+=count*QuantumScale*
1001 ClampPixel((MagickRealType) OpaqueAlpha);
1002 p+=count*GetPixelChannels(image);
1003 }
1004 proceed=SetImageProgress(image,ClassifyImageTag,(MagickOffsetType) y,
1005 image->rows);
1006 if (proceed == MagickFalse)
1007 break;
1008 }
1009 image_view=DestroyCacheView(image_view);
1010 if ((cube_info->quantize_info->colorspace != UndefinedColorspace) &&
1011 (cube_info->quantize_info->colorspace != CMYKColorspace))
1012 (void) TransformImageColorspace((Image *) image,sRGBColorspace,exception);
1013 return(y < (ssize_t) image->rows ? MagickFalse : MagickTrue);
1014 }
1015
1016 /*
1017 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1018 % %
1019 % %
1020 % %
1021 % C l o n e Q u a n t i z e I n f o %
1022 % %
1023 % %
1024 % %
1025 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1026 %
1027 % CloneQuantizeInfo() makes a duplicate of the given quantize info structure,
1028 % or if quantize info is NULL, a new one.
1029 %
1030 % The format of the CloneQuantizeInfo method is:
1031 %
1032 % QuantizeInfo *CloneQuantizeInfo(const QuantizeInfo *quantize_info)
1033 %
1034 % A description of each parameter follows:
1035 %
1036 % o clone_info: Method CloneQuantizeInfo returns a duplicate of the given
1037 % quantize info, or if image info is NULL a new one.
1038 %
1039 % o quantize_info: a structure of type info.
1040 %
1041 */
CloneQuantizeInfo(const QuantizeInfo * quantize_info)1042 MagickExport QuantizeInfo *CloneQuantizeInfo(const QuantizeInfo *quantize_info)
1043 {
1044 QuantizeInfo
1045 *clone_info;
1046
1047 clone_info=(QuantizeInfo *) AcquireCriticalMemory(sizeof(*clone_info));
1048 GetQuantizeInfo(clone_info);
1049 if (quantize_info == (QuantizeInfo *) NULL)
1050 return(clone_info);
1051 clone_info->number_colors=quantize_info->number_colors;
1052 clone_info->tree_depth=quantize_info->tree_depth;
1053 clone_info->dither_method=quantize_info->dither_method;
1054 clone_info->colorspace=quantize_info->colorspace;
1055 clone_info->measure_error=quantize_info->measure_error;
1056 return(clone_info);
1057 }
1058
1059 /*
1060 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1061 % %
1062 % %
1063 % %
1064 + C l o s e s t C o l o r %
1065 % %
1066 % %
1067 % %
1068 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1069 %
1070 % ClosestColor() traverses the color cube tree at a particular node and
1071 % determines which colormap entry best represents the input color.
1072 %
1073 % The format of the ClosestColor method is:
1074 %
1075 % void ClosestColor(const Image *image,CubeInfo *cube_info,
1076 % const NodeInfo *node_info)
1077 %
1078 % A description of each parameter follows.
1079 %
1080 % o image: the image.
1081 %
1082 % o cube_info: A pointer to the Cube structure.
1083 %
1084 % o node_info: the address of a structure of type NodeInfo which points to a
1085 % node in the color cube tree that is to be pruned.
1086 %
1087 */
ClosestColor(const Image * image,CubeInfo * cube_info,const NodeInfo * node_info)1088 static void ClosestColor(const Image *image,CubeInfo *cube_info,
1089 const NodeInfo *node_info)
1090 {
1091 register ssize_t
1092 i;
1093
1094 size_t
1095 number_children;
1096
1097 /*
1098 Traverse any children.
1099 */
1100 number_children=cube_info->associate_alpha == MagickFalse ? 8UL : 16UL;
1101 for (i=0; i < (ssize_t) number_children; i++)
1102 if (node_info->child[i] != (NodeInfo *) NULL)
1103 ClosestColor(image,cube_info,node_info->child[i]);
1104 if (node_info->number_unique != 0)
1105 {
1106 double
1107 pixel;
1108
1109 register double
1110 alpha,
1111 beta,
1112 distance;
1113
1114 register DoublePixelPacket
1115 *magick_restrict q;
1116
1117 register PixelInfo
1118 *magick_restrict p;
1119
1120 /*
1121 Determine if this color is "closest".
1122 */
1123 p=image->colormap+node_info->color_number;
1124 q=(&cube_info->target);
1125 alpha=1.0;
1126 beta=1.0;
1127 if (cube_info->associate_alpha != MagickFalse)
1128 {
1129 alpha=(double) (QuantumScale*p->alpha);
1130 beta=(double) (QuantumScale*q->alpha);
1131 }
1132 pixel=alpha*p->red-beta*q->red;
1133 distance=pixel*pixel;
1134 if (distance <= cube_info->distance)
1135 {
1136 pixel=alpha*p->green-beta*q->green;
1137 distance+=pixel*pixel;
1138 if (distance <= cube_info->distance)
1139 {
1140 pixel=alpha*p->blue-beta*q->blue;
1141 distance+=pixel*pixel;
1142 if (distance <= cube_info->distance)
1143 {
1144 if (cube_info->associate_alpha != MagickFalse)
1145 {
1146 pixel=p->alpha-q->alpha;
1147 distance+=pixel*pixel;
1148 }
1149 if (distance <= cube_info->distance)
1150 {
1151 cube_info->distance=distance;
1152 cube_info->color_number=node_info->color_number;
1153 }
1154 }
1155 }
1156 }
1157 }
1158 }
1159
1160 /*
1161 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1162 % %
1163 % %
1164 % %
1165 % C o m p r e s s I m a g e C o l o r m a p %
1166 % %
1167 % %
1168 % %
1169 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1170 %
1171 % CompressImageColormap() compresses an image colormap by removing any
1172 % duplicate or unused color entries.
1173 %
1174 % The format of the CompressImageColormap method is:
1175 %
1176 % MagickBooleanType CompressImageColormap(Image *image,
1177 % ExceptionInfo *exception)
1178 %
1179 % A description of each parameter follows:
1180 %
1181 % o image: the image.
1182 %
1183 % o exception: return any errors or warnings in this structure.
1184 %
1185 */
CompressImageColormap(Image * image,ExceptionInfo * exception)1186 MagickExport MagickBooleanType CompressImageColormap(Image *image,
1187 ExceptionInfo *exception)
1188 {
1189 QuantizeInfo
1190 quantize_info;
1191
1192 assert(image != (Image *) NULL);
1193 assert(image->signature == MagickCoreSignature);
1194 if (image->debug != MagickFalse)
1195 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1196 if (IsPaletteImage(image) == MagickFalse)
1197 return(MagickFalse);
1198 GetQuantizeInfo(&quantize_info);
1199 quantize_info.number_colors=image->colors;
1200 quantize_info.tree_depth=MaxTreeDepth;
1201 return(QuantizeImage(&quantize_info,image,exception));
1202 }
1203
1204 /*
1205 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1206 % %
1207 % %
1208 % %
1209 + D e f i n e I m a g e C o l o r m a p %
1210 % %
1211 % %
1212 % %
1213 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1214 %
1215 % DefineImageColormap() traverses the color cube tree and notes each colormap
1216 % entry. A colormap entry is any node in the color cube tree where the
1217 % of unique colors is not zero. DefineImageColormap() returns the number of
1218 % colors in the image colormap.
1219 %
1220 % The format of the DefineImageColormap method is:
1221 %
1222 % size_t DefineImageColormap(Image *image,CubeInfo *cube_info,
1223 % NodeInfo *node_info)
1224 %
1225 % A description of each parameter follows.
1226 %
1227 % o image: the image.
1228 %
1229 % o cube_info: A pointer to the Cube structure.
1230 %
1231 % o node_info: the address of a structure of type NodeInfo which points to a
1232 % node in the color cube tree that is to be pruned.
1233 %
1234 */
DefineImageColormap(Image * image,CubeInfo * cube_info,NodeInfo * node_info)1235 static size_t DefineImageColormap(Image *image,CubeInfo *cube_info,
1236 NodeInfo *node_info)
1237 {
1238 register ssize_t
1239 i;
1240
1241 size_t
1242 number_children;
1243
1244 /*
1245 Traverse any children.
1246 */
1247 number_children=cube_info->associate_alpha == MagickFalse ? 8UL : 16UL;
1248 for (i=0; i < (ssize_t) number_children; i++)
1249 if (node_info->child[i] != (NodeInfo *) NULL)
1250 (void) DefineImageColormap(image,cube_info,node_info->child[i]);
1251 if (node_info->number_unique != 0)
1252 {
1253 register double
1254 alpha;
1255
1256 register PixelInfo
1257 *magick_restrict q;
1258
1259 /*
1260 Colormap entry is defined by the mean color in this cube.
1261 */
1262 q=image->colormap+image->colors;
1263 alpha=(double) ((MagickOffsetType) node_info->number_unique);
1264 alpha=PerceptibleReciprocal(alpha);
1265 if (cube_info->associate_alpha == MagickFalse)
1266 {
1267 q->red=(double) ClampToQuantum(alpha*QuantumRange*
1268 node_info->total_color.red);
1269 q->green=(double) ClampToQuantum(alpha*QuantumRange*
1270 node_info->total_color.green);
1271 q->blue=(double) ClampToQuantum(alpha*QuantumRange*
1272 node_info->total_color.blue);
1273 q->alpha=(double) OpaqueAlpha;
1274 }
1275 else
1276 {
1277 double
1278 opacity;
1279
1280 opacity=(double) (alpha*QuantumRange*node_info->total_color.alpha);
1281 q->alpha=(double) ClampToQuantum(opacity);
1282 if (q->alpha == OpaqueAlpha)
1283 {
1284 q->red=(double) ClampToQuantum(alpha*QuantumRange*
1285 node_info->total_color.red);
1286 q->green=(double) ClampToQuantum(alpha*QuantumRange*
1287 node_info->total_color.green);
1288 q->blue=(double) ClampToQuantum(alpha*QuantumRange*
1289 node_info->total_color.blue);
1290 }
1291 else
1292 {
1293 double
1294 gamma;
1295
1296 gamma=(double) (QuantumScale*q->alpha);
1297 gamma=PerceptibleReciprocal(gamma);
1298 q->red=(double) ClampToQuantum(alpha*gamma*QuantumRange*
1299 node_info->total_color.red);
1300 q->green=(double) ClampToQuantum(alpha*gamma*QuantumRange*
1301 node_info->total_color.green);
1302 q->blue=(double) ClampToQuantum(alpha*gamma*QuantumRange*
1303 node_info->total_color.blue);
1304 if (node_info->number_unique > cube_info->transparent_pixels)
1305 {
1306 cube_info->transparent_pixels=node_info->number_unique;
1307 cube_info->transparent_index=(ssize_t) image->colors;
1308 }
1309 }
1310 }
1311 node_info->color_number=image->colors++;
1312 }
1313 return(image->colors);
1314 }
1315
1316 /*
1317 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1318 % %
1319 % %
1320 % %
1321 + D e s t r o y C u b e I n f o %
1322 % %
1323 % %
1324 % %
1325 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1326 %
1327 % DestroyCubeInfo() deallocates memory associated with an image.
1328 %
1329 % The format of the DestroyCubeInfo method is:
1330 %
1331 % DestroyCubeInfo(CubeInfo *cube_info)
1332 %
1333 % A description of each parameter follows:
1334 %
1335 % o cube_info: the address of a structure of type CubeInfo.
1336 %
1337 */
DestroyCubeInfo(CubeInfo * cube_info)1338 static void DestroyCubeInfo(CubeInfo *cube_info)
1339 {
1340 register Nodes
1341 *nodes;
1342
1343 /*
1344 Release color cube tree storage.
1345 */
1346 do
1347 {
1348 nodes=cube_info->node_queue->next;
1349 cube_info->node_queue->nodes=(NodeInfo *) RelinquishMagickMemory(
1350 cube_info->node_queue->nodes);
1351 cube_info->node_queue=(Nodes *) RelinquishMagickMemory(
1352 cube_info->node_queue);
1353 cube_info->node_queue=nodes;
1354 } while (cube_info->node_queue != (Nodes *) NULL);
1355 if (cube_info->memory_info != (MemoryInfo *) NULL)
1356 cube_info->memory_info=RelinquishVirtualMemory(cube_info->memory_info);
1357 cube_info->quantize_info=DestroyQuantizeInfo(cube_info->quantize_info);
1358 cube_info=(CubeInfo *) RelinquishMagickMemory(cube_info);
1359 }
1360
1361 /*
1362 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1363 % %
1364 % %
1365 % %
1366 % D e s t r o y Q u a n t i z e I n f o %
1367 % %
1368 % %
1369 % %
1370 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1371 %
1372 % DestroyQuantizeInfo() deallocates memory associated with an QuantizeInfo
1373 % structure.
1374 %
1375 % The format of the DestroyQuantizeInfo method is:
1376 %
1377 % QuantizeInfo *DestroyQuantizeInfo(QuantizeInfo *quantize_info)
1378 %
1379 % A description of each parameter follows:
1380 %
1381 % o quantize_info: Specifies a pointer to an QuantizeInfo structure.
1382 %
1383 */
DestroyQuantizeInfo(QuantizeInfo * quantize_info)1384 MagickExport QuantizeInfo *DestroyQuantizeInfo(QuantizeInfo *quantize_info)
1385 {
1386 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
1387 assert(quantize_info != (QuantizeInfo *) NULL);
1388 assert(quantize_info->signature == MagickCoreSignature);
1389 quantize_info->signature=(~MagickCoreSignature);
1390 quantize_info=(QuantizeInfo *) RelinquishMagickMemory(quantize_info);
1391 return(quantize_info);
1392 }
1393
1394 /*
1395 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1396 % %
1397 % %
1398 % %
1399 + D i t h e r I m a g e %
1400 % %
1401 % %
1402 % %
1403 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1404 %
1405 % DitherImage() distributes the difference between an original image and
1406 % the corresponding color reduced algorithm to neighboring pixels using
1407 % serpentine-scan Floyd-Steinberg error diffusion. DitherImage returns
1408 % MagickTrue if the image is dithered otherwise MagickFalse.
1409 %
1410 % The format of the DitherImage method is:
1411 %
1412 % MagickBooleanType DitherImage(Image *image,CubeInfo *cube_info,
1413 % ExceptionInfo *exception)
1414 %
1415 % A description of each parameter follows.
1416 %
1417 % o image: the image.
1418 %
1419 % o cube_info: A pointer to the Cube structure.
1420 %
1421 % o exception: return any errors or warnings in this structure.
1422 %
1423 */
1424
DestroyPixelThreadSet(DoublePixelPacket ** pixels)1425 static DoublePixelPacket **DestroyPixelThreadSet(DoublePixelPacket **pixels)
1426 {
1427 register ssize_t
1428 i;
1429
1430 assert(pixels != (DoublePixelPacket **) NULL);
1431 for (i=0; i < (ssize_t) GetMagickResourceLimit(ThreadResource); i++)
1432 if (pixels[i] != (DoublePixelPacket *) NULL)
1433 pixels[i]=(DoublePixelPacket *) RelinquishMagickMemory(pixels[i]);
1434 pixels=(DoublePixelPacket **) RelinquishMagickMemory(pixels);
1435 return(pixels);
1436 }
1437
AcquirePixelThreadSet(const size_t count)1438 static DoublePixelPacket **AcquirePixelThreadSet(const size_t count)
1439 {
1440 DoublePixelPacket
1441 **pixels;
1442
1443 register ssize_t
1444 i;
1445
1446 size_t
1447 number_threads;
1448
1449 number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
1450 pixels=(DoublePixelPacket **) AcquireQuantumMemory(number_threads,
1451 sizeof(*pixels));
1452 if (pixels == (DoublePixelPacket **) NULL)
1453 return((DoublePixelPacket **) NULL);
1454 (void) memset(pixels,0,number_threads*sizeof(*pixels));
1455 for (i=0; i < (ssize_t) number_threads; i++)
1456 {
1457 pixels[i]=(DoublePixelPacket *) AcquireQuantumMemory(count,2*
1458 sizeof(**pixels));
1459 if (pixels[i] == (DoublePixelPacket *) NULL)
1460 return(DestroyPixelThreadSet(pixels));
1461 }
1462 return(pixels);
1463 }
1464
CacheOffset(CubeInfo * cube_info,const DoublePixelPacket * pixel)1465 static inline ssize_t CacheOffset(CubeInfo *cube_info,
1466 const DoublePixelPacket *pixel)
1467 {
1468 #define RedShift(pixel) (((pixel) >> CacheShift) << (0*(8-CacheShift)))
1469 #define GreenShift(pixel) (((pixel) >> CacheShift) << (1*(8-CacheShift)))
1470 #define BlueShift(pixel) (((pixel) >> CacheShift) << (2*(8-CacheShift)))
1471 #define AlphaShift(pixel) (((pixel) >> CacheShift) << (3*(8-CacheShift)))
1472
1473 ssize_t
1474 offset;
1475
1476 offset=(ssize_t) (RedShift(ScaleQuantumToChar(ClampPixel(pixel->red))) |
1477 GreenShift(ScaleQuantumToChar(ClampPixel(pixel->green))) |
1478 BlueShift(ScaleQuantumToChar(ClampPixel(pixel->blue))));
1479 if (cube_info->associate_alpha != MagickFalse)
1480 offset|=AlphaShift(ScaleQuantumToChar(ClampPixel(pixel->alpha)));
1481 return(offset);
1482 }
1483
FloydSteinbergDither(Image * image,CubeInfo * cube_info,ExceptionInfo * exception)1484 static MagickBooleanType FloydSteinbergDither(Image *image,CubeInfo *cube_info,
1485 ExceptionInfo *exception)
1486 {
1487 #define DitherImageTag "Dither/Image"
1488
1489 CacheView
1490 *image_view;
1491
1492 const char
1493 *artifact;
1494
1495 double
1496 amount;
1497
1498 DoublePixelPacket
1499 **pixels;
1500
1501 MagickBooleanType
1502 status;
1503
1504 ssize_t
1505 y;
1506
1507 /*
1508 Distribute quantization error using Floyd-Steinberg.
1509 */
1510 pixels=AcquirePixelThreadSet(image->columns);
1511 if (pixels == (DoublePixelPacket **) NULL)
1512 return(MagickFalse);
1513 status=MagickTrue;
1514 amount=1.0;
1515 artifact=GetImageArtifact(image,"dither:diffusion-amount");
1516 if (artifact != (const char *) NULL)
1517 amount=StringToDoubleInterval(artifact,1.0);
1518 image_view=AcquireAuthenticCacheView(image,exception);
1519 for (y=0; y < (ssize_t) image->rows; y++)
1520 {
1521 const int
1522 id = GetOpenMPThreadId();
1523
1524 CubeInfo
1525 cube;
1526
1527 DoublePixelPacket
1528 *current,
1529 *previous;
1530
1531 register Quantum
1532 *magick_restrict q;
1533
1534 register ssize_t
1535 x;
1536
1537 size_t
1538 index;
1539
1540 ssize_t
1541 v;
1542
1543 if (status == MagickFalse)
1544 continue;
1545 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1546 if (q == (Quantum *) NULL)
1547 {
1548 status=MagickFalse;
1549 continue;
1550 }
1551 cube=(*cube_info);
1552 current=pixels[id]+(y & 0x01)*image->columns;
1553 previous=pixels[id]+((y+1) & 0x01)*image->columns;
1554 v=(ssize_t) ((y & 0x01) != 0 ? -1 : 1);
1555 for (x=0; x < (ssize_t) image->columns; x++)
1556 {
1557 DoublePixelPacket
1558 color,
1559 pixel;
1560
1561 register ssize_t
1562 i;
1563
1564 ssize_t
1565 u;
1566
1567 u=(y & 0x01) != 0 ? (ssize_t) image->columns-1-x : x;
1568 AssociateAlphaPixel(image,&cube,q+u*GetPixelChannels(image),&pixel);
1569 if (x > 0)
1570 {
1571 pixel.red+=7.0*amount*current[u-v].red/16;
1572 pixel.green+=7.0*amount*current[u-v].green/16;
1573 pixel.blue+=7.0*amount*current[u-v].blue/16;
1574 if (cube.associate_alpha != MagickFalse)
1575 pixel.alpha+=7.0*amount*current[u-v].alpha/16;
1576 }
1577 if (y > 0)
1578 {
1579 if (x < (ssize_t) (image->columns-1))
1580 {
1581 pixel.red+=previous[u+v].red/16;
1582 pixel.green+=previous[u+v].green/16;
1583 pixel.blue+=previous[u+v].blue/16;
1584 if (cube.associate_alpha != MagickFalse)
1585 pixel.alpha+=previous[u+v].alpha/16;
1586 }
1587 pixel.red+=5.0*amount*previous[u].red/16;
1588 pixel.green+=5.0*amount*previous[u].green/16;
1589 pixel.blue+=5.0*amount*previous[u].blue/16;
1590 if (cube.associate_alpha != MagickFalse)
1591 pixel.alpha+=5.0*amount*previous[u].alpha/16;
1592 if (x > 0)
1593 {
1594 pixel.red+=3.0*amount*previous[u-v].red/16;
1595 pixel.green+=3.0*amount*previous[u-v].green/16;
1596 pixel.blue+=3.0*amount*previous[u-v].blue/16;
1597 if (cube.associate_alpha != MagickFalse)
1598 pixel.alpha+=3.0*amount*previous[u-v].alpha/16;
1599 }
1600 }
1601 pixel.red=(double) ClampPixel(pixel.red);
1602 pixel.green=(double) ClampPixel(pixel.green);
1603 pixel.blue=(double) ClampPixel(pixel.blue);
1604 if (cube.associate_alpha != MagickFalse)
1605 pixel.alpha=(double) ClampPixel(pixel.alpha);
1606 i=CacheOffset(&cube,&pixel);
1607 if (cube.cache[i] < 0)
1608 {
1609 register NodeInfo
1610 *node_info;
1611
1612 register size_t
1613 node_id;
1614
1615 /*
1616 Identify the deepest node containing the pixel's color.
1617 */
1618 node_info=cube.root;
1619 for (index=MaxTreeDepth-1; (ssize_t) index > 0; index--)
1620 {
1621 node_id=ColorToNodeId(&cube,&pixel,index);
1622 if (node_info->child[node_id] == (NodeInfo *) NULL)
1623 break;
1624 node_info=node_info->child[node_id];
1625 }
1626 /*
1627 Find closest color among siblings and their children.
1628 */
1629 cube.target=pixel;
1630 cube.distance=(double) (4.0*(QuantumRange+1.0)*(QuantumRange+1.0)+
1631 1.0);
1632 ClosestColor(image,&cube,node_info->parent);
1633 cube.cache[i]=(ssize_t) cube.color_number;
1634 }
1635 /*
1636 Assign pixel to closest colormap entry.
1637 */
1638 index=(size_t) cube.cache[i];
1639 if (image->storage_class == PseudoClass)
1640 SetPixelIndex(image,(Quantum) index,q+u*GetPixelChannels(image));
1641 if (cube.quantize_info->measure_error == MagickFalse)
1642 {
1643 SetPixelRed(image,ClampToQuantum(image->colormap[index].red),
1644 q+u*GetPixelChannels(image));
1645 SetPixelGreen(image,ClampToQuantum(image->colormap[index].green),
1646 q+u*GetPixelChannels(image));
1647 SetPixelBlue(image,ClampToQuantum(image->colormap[index].blue),
1648 q+u*GetPixelChannels(image));
1649 if (cube.associate_alpha != MagickFalse)
1650 SetPixelAlpha(image,ClampToQuantum(image->colormap[index].alpha),
1651 q+u*GetPixelChannels(image));
1652 }
1653 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1654 status=MagickFalse;
1655 /*
1656 Store the error.
1657 */
1658 AssociateAlphaPixelInfo(&cube,image->colormap+index,&color);
1659 current[u].red=pixel.red-color.red;
1660 current[u].green=pixel.green-color.green;
1661 current[u].blue=pixel.blue-color.blue;
1662 if (cube.associate_alpha != MagickFalse)
1663 current[u].alpha=pixel.alpha-color.alpha;
1664 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1665 {
1666 MagickBooleanType
1667 proceed;
1668
1669 proceed=SetImageProgress(image,DitherImageTag,(MagickOffsetType) y,
1670 image->rows);
1671 if (proceed == MagickFalse)
1672 status=MagickFalse;
1673 }
1674 }
1675 }
1676 image_view=DestroyCacheView(image_view);
1677 pixels=DestroyPixelThreadSet(pixels);
1678 return(MagickTrue);
1679 }
1680
1681 static MagickBooleanType
1682 RiemersmaDither(Image *,CacheView *,CubeInfo *,const unsigned int,
1683 ExceptionInfo *);
1684
Riemersma(Image * image,CacheView * image_view,CubeInfo * cube_info,const size_t level,const unsigned int direction,ExceptionInfo * exception)1685 static void Riemersma(Image *image,CacheView *image_view,CubeInfo *cube_info,
1686 const size_t level,const unsigned int direction,ExceptionInfo *exception)
1687 {
1688 if (level == 1)
1689 switch (direction)
1690 {
1691 case WestGravity:
1692 {
1693 (void) RiemersmaDither(image,image_view,cube_info,EastGravity,
1694 exception);
1695 (void) RiemersmaDither(image,image_view,cube_info,SouthGravity,
1696 exception);
1697 (void) RiemersmaDither(image,image_view,cube_info,WestGravity,
1698 exception);
1699 break;
1700 }
1701 case EastGravity:
1702 {
1703 (void) RiemersmaDither(image,image_view,cube_info,WestGravity,
1704 exception);
1705 (void) RiemersmaDither(image,image_view,cube_info,NorthGravity,
1706 exception);
1707 (void) RiemersmaDither(image,image_view,cube_info,EastGravity,
1708 exception);
1709 break;
1710 }
1711 case NorthGravity:
1712 {
1713 (void) RiemersmaDither(image,image_view,cube_info,SouthGravity,
1714 exception);
1715 (void) RiemersmaDither(image,image_view,cube_info,EastGravity,
1716 exception);
1717 (void) RiemersmaDither(image,image_view,cube_info,NorthGravity,
1718 exception);
1719 break;
1720 }
1721 case SouthGravity:
1722 {
1723 (void) RiemersmaDither(image,image_view,cube_info,NorthGravity,
1724 exception);
1725 (void) RiemersmaDither(image,image_view,cube_info,WestGravity,
1726 exception);
1727 (void) RiemersmaDither(image,image_view,cube_info,SouthGravity,
1728 exception);
1729 break;
1730 }
1731 default:
1732 break;
1733 }
1734 else
1735 switch (direction)
1736 {
1737 case WestGravity:
1738 {
1739 Riemersma(image,image_view,cube_info,level-1,NorthGravity,
1740 exception);
1741 (void) RiemersmaDither(image,image_view,cube_info,EastGravity,
1742 exception);
1743 Riemersma(image,image_view,cube_info,level-1,WestGravity,
1744 exception);
1745 (void) RiemersmaDither(image,image_view,cube_info,SouthGravity,
1746 exception);
1747 Riemersma(image,image_view,cube_info,level-1,WestGravity,
1748 exception);
1749 (void) RiemersmaDither(image,image_view,cube_info,WestGravity,
1750 exception);
1751 Riemersma(image,image_view,cube_info,level-1,SouthGravity,
1752 exception);
1753 break;
1754 }
1755 case EastGravity:
1756 {
1757 Riemersma(image,image_view,cube_info,level-1,SouthGravity,
1758 exception);
1759 (void) RiemersmaDither(image,image_view,cube_info,WestGravity,
1760 exception);
1761 Riemersma(image,image_view,cube_info,level-1,EastGravity,
1762 exception);
1763 (void) RiemersmaDither(image,image_view,cube_info,NorthGravity,
1764 exception);
1765 Riemersma(image,image_view,cube_info,level-1,EastGravity,
1766 exception);
1767 (void) RiemersmaDither(image,image_view,cube_info,EastGravity,
1768 exception);
1769 Riemersma(image,image_view,cube_info,level-1,NorthGravity,
1770 exception);
1771 break;
1772 }
1773 case NorthGravity:
1774 {
1775 Riemersma(image,image_view,cube_info,level-1,WestGravity,
1776 exception);
1777 (void) RiemersmaDither(image,image_view,cube_info,SouthGravity,
1778 exception);
1779 Riemersma(image,image_view,cube_info,level-1,NorthGravity,
1780 exception);
1781 (void) RiemersmaDither(image,image_view,cube_info,EastGravity,
1782 exception);
1783 Riemersma(image,image_view,cube_info,level-1,NorthGravity,
1784 exception);
1785 (void) RiemersmaDither(image,image_view,cube_info,NorthGravity,
1786 exception);
1787 Riemersma(image,image_view,cube_info,level-1,EastGravity,
1788 exception);
1789 break;
1790 }
1791 case SouthGravity:
1792 {
1793 Riemersma(image,image_view,cube_info,level-1,EastGravity,
1794 exception);
1795 (void) RiemersmaDither(image,image_view,cube_info,NorthGravity,
1796 exception);
1797 Riemersma(image,image_view,cube_info,level-1,SouthGravity,
1798 exception);
1799 (void) RiemersmaDither(image,image_view,cube_info,WestGravity,
1800 exception);
1801 Riemersma(image,image_view,cube_info,level-1,SouthGravity,
1802 exception);
1803 (void) RiemersmaDither(image,image_view,cube_info,SouthGravity,
1804 exception);
1805 Riemersma(image,image_view,cube_info,level-1,WestGravity,
1806 exception);
1807 break;
1808 }
1809 default:
1810 break;
1811 }
1812 }
1813
RiemersmaDither(Image * image,CacheView * image_view,CubeInfo * cube_info,const unsigned int direction,ExceptionInfo * exception)1814 static MagickBooleanType RiemersmaDither(Image *image,CacheView *image_view,
1815 CubeInfo *cube_info,const unsigned int direction,ExceptionInfo *exception)
1816 {
1817 #define DitherImageTag "Dither/Image"
1818
1819 DoublePixelPacket
1820 color,
1821 pixel;
1822
1823 MagickBooleanType
1824 proceed;
1825
1826 register CubeInfo
1827 *p;
1828
1829 size_t
1830 index;
1831
1832 p=cube_info;
1833 if ((p->x >= 0) && (p->x < (ssize_t) image->columns) &&
1834 (p->y >= 0) && (p->y < (ssize_t) image->rows))
1835 {
1836 register Quantum
1837 *magick_restrict q;
1838
1839 register ssize_t
1840 i;
1841
1842 /*
1843 Distribute error.
1844 */
1845 q=GetCacheViewAuthenticPixels(image_view,p->x,p->y,1,1,exception);
1846 if (q == (Quantum *) NULL)
1847 return(MagickFalse);
1848 AssociateAlphaPixel(image,cube_info,q,&pixel);
1849 for (i=0; i < ErrorQueueLength; i++)
1850 {
1851 pixel.red+=p->weights[i]*p->error[i].red;
1852 pixel.green+=p->weights[i]*p->error[i].green;
1853 pixel.blue+=p->weights[i]*p->error[i].blue;
1854 if (cube_info->associate_alpha != MagickFalse)
1855 pixel.alpha+=p->weights[i]*p->error[i].alpha;
1856 }
1857 pixel.red=(double) ClampPixel(pixel.red);
1858 pixel.green=(double) ClampPixel(pixel.green);
1859 pixel.blue=(double) ClampPixel(pixel.blue);
1860 if (cube_info->associate_alpha != MagickFalse)
1861 pixel.alpha=(double) ClampPixel(pixel.alpha);
1862 i=CacheOffset(cube_info,&pixel);
1863 if (p->cache[i] < 0)
1864 {
1865 register NodeInfo
1866 *node_info;
1867
1868 register size_t
1869 id;
1870
1871 /*
1872 Identify the deepest node containing the pixel's color.
1873 */
1874 node_info=p->root;
1875 for (index=MaxTreeDepth-1; (ssize_t) index > 0; index--)
1876 {
1877 id=ColorToNodeId(cube_info,&pixel,index);
1878 if (node_info->child[id] == (NodeInfo *) NULL)
1879 break;
1880 node_info=node_info->child[id];
1881 }
1882 /*
1883 Find closest color among siblings and their children.
1884 */
1885 p->target=pixel;
1886 p->distance=(double) (4.0*(QuantumRange+1.0)*((double)
1887 QuantumRange+1.0)+1.0);
1888 ClosestColor(image,p,node_info->parent);
1889 p->cache[i]=(ssize_t) p->color_number;
1890 }
1891 /*
1892 Assign pixel to closest colormap entry.
1893 */
1894 index=(size_t) p->cache[i];
1895 if (image->storage_class == PseudoClass)
1896 SetPixelIndex(image,(Quantum) index,q);
1897 if (cube_info->quantize_info->measure_error == MagickFalse)
1898 {
1899 SetPixelRed(image,ClampToQuantum(image->colormap[index].red),q);
1900 SetPixelGreen(image,ClampToQuantum(image->colormap[index].green),q);
1901 SetPixelBlue(image,ClampToQuantum(image->colormap[index].blue),q);
1902 if (cube_info->associate_alpha != MagickFalse)
1903 SetPixelAlpha(image,ClampToQuantum(image->colormap[index].alpha),q);
1904 }
1905 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1906 return(MagickFalse);
1907 /*
1908 Propagate the error as the last entry of the error queue.
1909 */
1910 (void) memmove(p->error,p->error+1,(ErrorQueueLength-1)*
1911 sizeof(p->error[0]));
1912 AssociateAlphaPixelInfo(cube_info,image->colormap+index,&color);
1913 p->error[ErrorQueueLength-1].red=pixel.red-color.red;
1914 p->error[ErrorQueueLength-1].green=pixel.green-color.green;
1915 p->error[ErrorQueueLength-1].blue=pixel.blue-color.blue;
1916 if (cube_info->associate_alpha != MagickFalse)
1917 p->error[ErrorQueueLength-1].alpha=pixel.alpha-color.alpha;
1918 proceed=SetImageProgress(image,DitherImageTag,p->offset,p->span);
1919 if (proceed == MagickFalse)
1920 return(MagickFalse);
1921 p->offset++;
1922 }
1923 switch (direction)
1924 {
1925 case WestGravity: p->x--; break;
1926 case EastGravity: p->x++; break;
1927 case NorthGravity: p->y--; break;
1928 case SouthGravity: p->y++; break;
1929 }
1930 return(MagickTrue);
1931 }
1932
DitherImage(Image * image,CubeInfo * cube_info,ExceptionInfo * exception)1933 static MagickBooleanType DitherImage(Image *image,CubeInfo *cube_info,
1934 ExceptionInfo *exception)
1935 {
1936 CacheView
1937 *image_view;
1938
1939 MagickBooleanType
1940 status;
1941
1942 register ssize_t
1943 i;
1944
1945 size_t
1946 depth;
1947
1948 if (cube_info->quantize_info->dither_method != RiemersmaDitherMethod)
1949 return(FloydSteinbergDither(image,cube_info,exception));
1950 /*
1951 Distribute quantization error along a Hilbert curve.
1952 */
1953 (void) memset(cube_info->error,0,ErrorQueueLength*
1954 sizeof(*cube_info->error));
1955 cube_info->x=0;
1956 cube_info->y=0;
1957 i=MagickMax((ssize_t) image->columns,(ssize_t) image->rows);
1958 for (depth=1; i != 0; depth++)
1959 i>>=1;
1960 if ((ssize_t) (1L << depth) < MagickMax((ssize_t) image->columns,(ssize_t) image->rows))
1961 depth++;
1962 cube_info->offset=0;
1963 cube_info->span=(MagickSizeType) image->columns*image->rows;
1964 image_view=AcquireAuthenticCacheView(image,exception);
1965 if (depth > 1)
1966 Riemersma(image,image_view,cube_info,depth-1,NorthGravity,exception);
1967 status=RiemersmaDither(image,image_view,cube_info,ForgetGravity,exception);
1968 image_view=DestroyCacheView(image_view);
1969 return(status);
1970 }
1971
1972 /*
1973 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1974 % %
1975 % %
1976 % %
1977 + G e t C u b e I n f o %
1978 % %
1979 % %
1980 % %
1981 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1982 %
1983 % GetCubeInfo() initialize the Cube data structure.
1984 %
1985 % The format of the GetCubeInfo method is:
1986 %
1987 % CubeInfo GetCubeInfo(const QuantizeInfo *quantize_info,
1988 % const size_t depth,const size_t maximum_colors)
1989 %
1990 % A description of each parameter follows.
1991 %
1992 % o quantize_info: Specifies a pointer to an QuantizeInfo structure.
1993 %
1994 % o depth: Normally, this integer value is zero or one. A zero or
1995 % one tells Quantize to choose a optimal tree depth of Log4(number_colors).
1996 % A tree of this depth generally allows the best representation of the
1997 % reference image with the least amount of memory and the fastest
1998 % computational speed. In some cases, such as an image with low color
1999 % dispersion (a few number of colors), a value other than
2000 % Log4(number_colors) is required. To expand the color tree completely,
2001 % use a value of 8.
2002 %
2003 % o maximum_colors: maximum colors.
2004 %
2005 */
GetCubeInfo(const QuantizeInfo * quantize_info,const size_t depth,const size_t maximum_colors)2006 static CubeInfo *GetCubeInfo(const QuantizeInfo *quantize_info,
2007 const size_t depth,const size_t maximum_colors)
2008 {
2009 CubeInfo
2010 *cube_info;
2011
2012 double
2013 sum,
2014 weight;
2015
2016 register ssize_t
2017 i;
2018
2019 size_t
2020 length;
2021
2022 /*
2023 Initialize tree to describe color cube_info.
2024 */
2025 cube_info=(CubeInfo *) AcquireMagickMemory(sizeof(*cube_info));
2026 if (cube_info == (CubeInfo *) NULL)
2027 return((CubeInfo *) NULL);
2028 (void) memset(cube_info,0,sizeof(*cube_info));
2029 cube_info->depth=depth;
2030 if (cube_info->depth > MaxTreeDepth)
2031 cube_info->depth=MaxTreeDepth;
2032 if (cube_info->depth < 2)
2033 cube_info->depth=2;
2034 cube_info->maximum_colors=maximum_colors;
2035 /*
2036 Initialize root node.
2037 */
2038 cube_info->root=GetNodeInfo(cube_info,0,0,(NodeInfo *) NULL);
2039 if (cube_info->root == (NodeInfo *) NULL)
2040 return((CubeInfo *) NULL);
2041 cube_info->root->parent=cube_info->root;
2042 cube_info->quantize_info=CloneQuantizeInfo(quantize_info);
2043 if (cube_info->quantize_info->dither_method == NoDitherMethod)
2044 return(cube_info);
2045 /*
2046 Initialize dither resources.
2047 */
2048 length=(size_t) (1UL << (4*(8-CacheShift)));
2049 cube_info->memory_info=AcquireVirtualMemory(length,sizeof(*cube_info->cache));
2050 if (cube_info->memory_info == (MemoryInfo *) NULL)
2051 return((CubeInfo *) NULL);
2052 cube_info->cache=(ssize_t *) GetVirtualMemoryBlob(cube_info->memory_info);
2053 /*
2054 Initialize color cache.
2055 */
2056 (void) memset(cube_info->cache,(-1),sizeof(*cube_info->cache)*
2057 length);
2058 /*
2059 Distribute weights along a curve of exponential decay.
2060 */
2061 weight=1.0;
2062 for (i=0; i < ErrorQueueLength; i++)
2063 {
2064 cube_info->weights[ErrorQueueLength-i-1]=PerceptibleReciprocal(weight);
2065 weight*=exp(log(((double) QuantumRange+1.0))/(ErrorQueueLength-1.0));
2066 }
2067 /*
2068 Normalize the weighting factors.
2069 */
2070 weight=0.0;
2071 for (i=0; i < ErrorQueueLength; i++)
2072 weight+=cube_info->weights[i];
2073 sum=0.0;
2074 for (i=0; i < ErrorQueueLength; i++)
2075 {
2076 cube_info->weights[i]/=weight;
2077 sum+=cube_info->weights[i];
2078 }
2079 cube_info->weights[0]+=1.0-sum;
2080 return(cube_info);
2081 }
2082
2083 /*
2084 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2085 % %
2086 % %
2087 % %
2088 + G e t N o d e I n f o %
2089 % %
2090 % %
2091 % %
2092 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2093 %
2094 % GetNodeInfo() allocates memory for a new node in the color cube tree and
2095 % presets all fields to zero.
2096 %
2097 % The format of the GetNodeInfo method is:
2098 %
2099 % NodeInfo *GetNodeInfo(CubeInfo *cube_info,const size_t id,
2100 % const size_t level,NodeInfo *parent)
2101 %
2102 % A description of each parameter follows.
2103 %
2104 % o node: The GetNodeInfo method returns a pointer to a queue of nodes.
2105 %
2106 % o id: Specifies the child number of the node.
2107 %
2108 % o level: Specifies the level in the storage_class the node resides.
2109 %
2110 */
GetNodeInfo(CubeInfo * cube_info,const size_t id,const size_t level,NodeInfo * parent)2111 static NodeInfo *GetNodeInfo(CubeInfo *cube_info,const size_t id,
2112 const size_t level,NodeInfo *parent)
2113 {
2114 NodeInfo
2115 *node_info;
2116
2117 if (cube_info->free_nodes == 0)
2118 {
2119 Nodes
2120 *nodes;
2121
2122 /*
2123 Allocate a new queue of nodes.
2124 */
2125 nodes=(Nodes *) AcquireMagickMemory(sizeof(*nodes));
2126 if (nodes == (Nodes *) NULL)
2127 return((NodeInfo *) NULL);
2128 nodes->nodes=(NodeInfo *) AcquireQuantumMemory(NodesInAList,
2129 sizeof(*nodes->nodes));
2130 if (nodes->nodes == (NodeInfo *) NULL)
2131 return((NodeInfo *) NULL);
2132 nodes->next=cube_info->node_queue;
2133 cube_info->node_queue=nodes;
2134 cube_info->next_node=nodes->nodes;
2135 cube_info->free_nodes=NodesInAList;
2136 }
2137 cube_info->nodes++;
2138 cube_info->free_nodes--;
2139 node_info=cube_info->next_node++;
2140 (void) memset(node_info,0,sizeof(*node_info));
2141 node_info->parent=parent;
2142 node_info->id=id;
2143 node_info->level=level;
2144 return(node_info);
2145 }
2146
2147 /*
2148 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2149 % %
2150 % %
2151 % %
2152 % G e t I m a g e Q u a n t i z e E r r o r %
2153 % %
2154 % %
2155 % %
2156 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2157 %
2158 % GetImageQuantizeError() measures the difference between the original
2159 % and quantized images. This difference is the total quantization error.
2160 % The error is computed by summing over all pixels in an image the distance
2161 % squared in RGB space between each reference pixel value and its quantized
2162 % value. These values are computed:
2163 %
2164 % o mean_error_per_pixel: This value is the mean error for any single
2165 % pixel in the image.
2166 %
2167 % o normalized_mean_square_error: This value is the normalized mean
2168 % quantization error for any single pixel in the image. This distance
2169 % measure is normalized to a range between 0 and 1. It is independent
2170 % of the range of red, green, and blue values in the image.
2171 %
2172 % o normalized_maximum_square_error: Thsi value is the normalized
2173 % maximum quantization error for any single pixel in the image. This
2174 % distance measure is normalized to a range between 0 and 1. It is
2175 % independent of the range of red, green, and blue values in your image.
2176 %
2177 % The format of the GetImageQuantizeError method is:
2178 %
2179 % MagickBooleanType GetImageQuantizeError(Image *image,
2180 % ExceptionInfo *exception)
2181 %
2182 % A description of each parameter follows.
2183 %
2184 % o image: the image.
2185 %
2186 % o exception: return any errors or warnings in this structure.
2187 %
2188 */
GetImageQuantizeError(Image * image,ExceptionInfo * exception)2189 MagickExport MagickBooleanType GetImageQuantizeError(Image *image,
2190 ExceptionInfo *exception)
2191 {
2192 CacheView
2193 *image_view;
2194
2195 double
2196 alpha,
2197 area,
2198 beta,
2199 distance,
2200 maximum_error,
2201 mean_error,
2202 mean_error_per_pixel;
2203
2204 size_t
2205 index;
2206
2207 ssize_t
2208 y;
2209
2210 assert(image != (Image *) NULL);
2211 assert(image->signature == MagickCoreSignature);
2212 if (image->debug != MagickFalse)
2213 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2214 image->total_colors=GetNumberColors(image,(FILE *) NULL,exception);
2215 (void) memset(&image->error,0,sizeof(image->error));
2216 if (image->storage_class == DirectClass)
2217 return(MagickTrue);
2218 alpha=1.0;
2219 beta=1.0;
2220 area=3.0*image->columns*image->rows;
2221 maximum_error=0.0;
2222 mean_error_per_pixel=0.0;
2223 mean_error=0.0;
2224 image_view=AcquireVirtualCacheView(image,exception);
2225 for (y=0; y < (ssize_t) image->rows; y++)
2226 {
2227 register const Quantum
2228 *magick_restrict p;
2229
2230 register ssize_t
2231 x;
2232
2233 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
2234 if (p == (const Quantum *) NULL)
2235 break;
2236 for (x=0; x < (ssize_t) image->columns; x++)
2237 {
2238 index=GetPixelIndex(image,p);
2239 if (image->alpha_trait == BlendPixelTrait)
2240 {
2241 alpha=(double) (QuantumScale*GetPixelAlpha(image,p));
2242 beta=(double) (QuantumScale*image->colormap[index].alpha);
2243 }
2244 distance=fabs((double) (alpha*GetPixelRed(image,p)-beta*
2245 image->colormap[index].red));
2246 mean_error_per_pixel+=distance;
2247 mean_error+=distance*distance;
2248 if (distance > maximum_error)
2249 maximum_error=distance;
2250 distance=fabs((double) (alpha*GetPixelGreen(image,p)-beta*
2251 image->colormap[index].green));
2252 mean_error_per_pixel+=distance;
2253 mean_error+=distance*distance;
2254 if (distance > maximum_error)
2255 maximum_error=distance;
2256 distance=fabs((double) (alpha*GetPixelBlue(image,p)-beta*
2257 image->colormap[index].blue));
2258 mean_error_per_pixel+=distance;
2259 mean_error+=distance*distance;
2260 if (distance > maximum_error)
2261 maximum_error=distance;
2262 p+=GetPixelChannels(image);
2263 }
2264 }
2265 image_view=DestroyCacheView(image_view);
2266 image->error.mean_error_per_pixel=(double) mean_error_per_pixel/area;
2267 image->error.normalized_mean_error=(double) QuantumScale*QuantumScale*
2268 mean_error/area;
2269 image->error.normalized_maximum_error=(double) QuantumScale*maximum_error;
2270 return(MagickTrue);
2271 }
2272
2273 /*
2274 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2275 % %
2276 % %
2277 % %
2278 % G e t Q u a n t i z e I n f o %
2279 % %
2280 % %
2281 % %
2282 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2283 %
2284 % GetQuantizeInfo() initializes the QuantizeInfo structure.
2285 %
2286 % The format of the GetQuantizeInfo method is:
2287 %
2288 % GetQuantizeInfo(QuantizeInfo *quantize_info)
2289 %
2290 % A description of each parameter follows:
2291 %
2292 % o quantize_info: Specifies a pointer to a QuantizeInfo structure.
2293 %
2294 */
GetQuantizeInfo(QuantizeInfo * quantize_info)2295 MagickExport void GetQuantizeInfo(QuantizeInfo *quantize_info)
2296 {
2297 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2298 assert(quantize_info != (QuantizeInfo *) NULL);
2299 (void) memset(quantize_info,0,sizeof(*quantize_info));
2300 quantize_info->number_colors=256;
2301 quantize_info->dither_method=RiemersmaDitherMethod;
2302 quantize_info->colorspace=UndefinedColorspace;
2303 quantize_info->measure_error=MagickFalse;
2304 quantize_info->signature=MagickCoreSignature;
2305 }
2306
2307 /*
2308 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2309 % %
2310 % %
2311 % %
2312 % P o s t e r i z e I m a g e %
2313 % %
2314 % %
2315 % %
2316 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2317 %
2318 % PosterizeImage() reduces the image to a limited number of colors for a
2319 % "poster" effect.
2320 %
2321 % The format of the PosterizeImage method is:
2322 %
2323 % MagickBooleanType PosterizeImage(Image *image,const size_t levels,
2324 % const DitherMethod dither_method,ExceptionInfo *exception)
2325 %
2326 % A description of each parameter follows:
2327 %
2328 % o image: Specifies a pointer to an Image structure.
2329 %
2330 % o levels: Number of color levels allowed in each channel. Very low values
2331 % (2, 3, or 4) have the most visible effect.
2332 %
2333 % o dither_method: choose from UndefinedDitherMethod, NoDitherMethod,
2334 % RiemersmaDitherMethod, FloydSteinbergDitherMethod.
2335 %
2336 % o exception: return any errors or warnings in this structure.
2337 %
2338 */
2339
MagickRound(double x)2340 static inline double MagickRound(double x)
2341 {
2342 /*
2343 Round the fraction to nearest integer.
2344 */
2345 if ((x-floor(x)) < (ceil(x)-x))
2346 return(floor(x));
2347 return(ceil(x));
2348 }
2349
PosterizeImage(Image * image,const size_t levels,const DitherMethod dither_method,ExceptionInfo * exception)2350 MagickExport MagickBooleanType PosterizeImage(Image *image,const size_t levels,
2351 const DitherMethod dither_method,ExceptionInfo *exception)
2352 {
2353 #define PosterizeImageTag "Posterize/Image"
2354 #define PosterizePixel(pixel) (Quantum) (QuantumRange*(MagickRound( \
2355 QuantumScale*pixel*(levels-1)))/MagickMax((ssize_t) levels-1,1))
2356
2357 CacheView
2358 *image_view;
2359
2360 MagickBooleanType
2361 status;
2362
2363 MagickOffsetType
2364 progress;
2365
2366 QuantizeInfo
2367 *quantize_info;
2368
2369 register ssize_t
2370 i;
2371
2372 ssize_t
2373 y;
2374
2375 assert(image != (Image *) NULL);
2376 assert(image->signature == MagickCoreSignature);
2377 if (image->debug != MagickFalse)
2378 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2379 assert(exception != (ExceptionInfo *) NULL);
2380 assert(exception->signature == MagickCoreSignature);
2381 if (image->storage_class == PseudoClass)
2382 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2383 #pragma omp parallel for schedule(static) shared(progress,status) \
2384 magick_number_threads(image,image,image->colors,1)
2385 #endif
2386 for (i=0; i < (ssize_t) image->colors; i++)
2387 {
2388 /*
2389 Posterize colormap.
2390 */
2391 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2392 image->colormap[i].red=(double)
2393 PosterizePixel(image->colormap[i].red);
2394 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2395 image->colormap[i].green=(double)
2396 PosterizePixel(image->colormap[i].green);
2397 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2398 image->colormap[i].blue=(double)
2399 PosterizePixel(image->colormap[i].blue);
2400 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
2401 image->colormap[i].alpha=(double)
2402 PosterizePixel(image->colormap[i].alpha);
2403 }
2404 /*
2405 Posterize image.
2406 */
2407 status=MagickTrue;
2408 progress=0;
2409 image_view=AcquireAuthenticCacheView(image,exception);
2410 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2411 #pragma omp parallel for schedule(static) shared(progress,status) \
2412 magick_number_threads(image,image,image->rows,1)
2413 #endif
2414 for (y=0; y < (ssize_t) image->rows; y++)
2415 {
2416 register Quantum
2417 *magick_restrict q;
2418
2419 register ssize_t
2420 x;
2421
2422 if (status == MagickFalse)
2423 continue;
2424 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2425 if (q == (Quantum *) NULL)
2426 {
2427 status=MagickFalse;
2428 continue;
2429 }
2430 for (x=0; x < (ssize_t) image->columns; x++)
2431 {
2432 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2433 SetPixelRed(image,PosterizePixel(GetPixelRed(image,q)),q);
2434 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2435 SetPixelGreen(image,PosterizePixel(GetPixelGreen(image,q)),q);
2436 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2437 SetPixelBlue(image,PosterizePixel(GetPixelBlue(image,q)),q);
2438 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
2439 (image->colorspace == CMYKColorspace))
2440 SetPixelBlack(image,PosterizePixel(GetPixelBlack(image,q)),q);
2441 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
2442 (image->alpha_trait == BlendPixelTrait))
2443 SetPixelAlpha(image,PosterizePixel(GetPixelAlpha(image,q)),q);
2444 q+=GetPixelChannels(image);
2445 }
2446 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2447 status=MagickFalse;
2448 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2449 {
2450 MagickBooleanType
2451 proceed;
2452
2453 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2454 #pragma omp atomic
2455 #endif
2456 progress++;
2457 proceed=SetImageProgress(image,PosterizeImageTag,progress,image->rows);
2458 if (proceed == MagickFalse)
2459 status=MagickFalse;
2460 }
2461 }
2462 image_view=DestroyCacheView(image_view);
2463 quantize_info=AcquireQuantizeInfo((ImageInfo *) NULL);
2464 quantize_info->number_colors=(size_t) MagickMin((ssize_t) levels*levels*
2465 levels,MaxColormapSize+1);
2466 quantize_info->dither_method=dither_method;
2467 quantize_info->tree_depth=MaxTreeDepth;
2468 status=QuantizeImage(quantize_info,image,exception);
2469 quantize_info=DestroyQuantizeInfo(quantize_info);
2470 return(status);
2471 }
2472
2473 /*
2474 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2475 % %
2476 % %
2477 % %
2478 + P r u n e C h i l d %
2479 % %
2480 % %
2481 % %
2482 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2483 %
2484 % PruneChild() deletes the given node and merges its statistics into its
2485 % parent.
2486 %
2487 % The format of the PruneSubtree method is:
2488 %
2489 % PruneChild(CubeInfo *cube_info,const NodeInfo *node_info)
2490 %
2491 % A description of each parameter follows.
2492 %
2493 % o cube_info: A pointer to the Cube structure.
2494 %
2495 % o node_info: pointer to node in color cube tree that is to be pruned.
2496 %
2497 */
PruneChild(CubeInfo * cube_info,const NodeInfo * node_info)2498 static void PruneChild(CubeInfo *cube_info,const NodeInfo *node_info)
2499 {
2500 NodeInfo
2501 *parent;
2502
2503 register ssize_t
2504 i;
2505
2506 size_t
2507 number_children;
2508
2509 /*
2510 Traverse any children.
2511 */
2512 number_children=cube_info->associate_alpha == MagickFalse ? 8UL : 16UL;
2513 for (i=0; i < (ssize_t) number_children; i++)
2514 if (node_info->child[i] != (NodeInfo *) NULL)
2515 PruneChild(cube_info,node_info->child[i]);
2516 /*
2517 Merge color statistics into parent.
2518 */
2519 parent=node_info->parent;
2520 parent->number_unique+=node_info->number_unique;
2521 parent->total_color.red+=node_info->total_color.red;
2522 parent->total_color.green+=node_info->total_color.green;
2523 parent->total_color.blue+=node_info->total_color.blue;
2524 parent->total_color.alpha+=node_info->total_color.alpha;
2525 parent->child[node_info->id]=(NodeInfo *) NULL;
2526 cube_info->nodes--;
2527 }
2528
2529 /*
2530 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2531 % %
2532 % %
2533 % %
2534 + P r u n e L e v e l %
2535 % %
2536 % %
2537 % %
2538 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2539 %
2540 % PruneLevel() deletes all nodes at the bottom level of the color tree merging
2541 % their color statistics into their parent node.
2542 %
2543 % The format of the PruneLevel method is:
2544 %
2545 % PruneLevel(CubeInfo *cube_info,const NodeInfo *node_info)
2546 %
2547 % A description of each parameter follows.
2548 %
2549 % o cube_info: A pointer to the Cube structure.
2550 %
2551 % o node_info: pointer to node in color cube tree that is to be pruned.
2552 %
2553 */
PruneLevel(CubeInfo * cube_info,const NodeInfo * node_info)2554 static void PruneLevel(CubeInfo *cube_info,const NodeInfo *node_info)
2555 {
2556 register ssize_t
2557 i;
2558
2559 size_t
2560 number_children;
2561
2562 /*
2563 Traverse any children.
2564 */
2565 number_children=cube_info->associate_alpha == MagickFalse ? 8UL : 16UL;
2566 for (i=0; i < (ssize_t) number_children; i++)
2567 if (node_info->child[i] != (NodeInfo *) NULL)
2568 PruneLevel(cube_info,node_info->child[i]);
2569 if (node_info->level == cube_info->depth)
2570 PruneChild(cube_info,node_info);
2571 }
2572
2573 /*
2574 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2575 % %
2576 % %
2577 % %
2578 + P r u n e T o C u b e D e p t h %
2579 % %
2580 % %
2581 % %
2582 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2583 %
2584 % PruneToCubeDepth() deletes any nodes at a depth greater than
2585 % cube_info->depth while merging their color statistics into their parent
2586 % node.
2587 %
2588 % The format of the PruneToCubeDepth method is:
2589 %
2590 % PruneToCubeDepth(CubeInfo *cube_info,const NodeInfo *node_info)
2591 %
2592 % A description of each parameter follows.
2593 %
2594 % o cube_info: A pointer to the Cube structure.
2595 %
2596 % o node_info: pointer to node in color cube tree that is to be pruned.
2597 %
2598 */
PruneToCubeDepth(CubeInfo * cube_info,const NodeInfo * node_info)2599 static void PruneToCubeDepth(CubeInfo *cube_info,const NodeInfo *node_info)
2600 {
2601 register ssize_t
2602 i;
2603
2604 size_t
2605 number_children;
2606
2607 /*
2608 Traverse any children.
2609 */
2610 number_children=cube_info->associate_alpha == MagickFalse ? 8UL : 16UL;
2611 for (i=0; i < (ssize_t) number_children; i++)
2612 if (node_info->child[i] != (NodeInfo *) NULL)
2613 PruneToCubeDepth(cube_info,node_info->child[i]);
2614 if (node_info->level > cube_info->depth)
2615 PruneChild(cube_info,node_info);
2616 }
2617
2618 /*
2619 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2620 % %
2621 % %
2622 % %
2623 % Q u a n t i z e I m a g e %
2624 % %
2625 % %
2626 % %
2627 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2628 %
2629 % QuantizeImage() analyzes the colors within a reference image and chooses a
2630 % fixed number of colors to represent the image. The goal of the algorithm
2631 % is to minimize the color difference between the input and output image while
2632 % minimizing the processing time.
2633 %
2634 % The format of the QuantizeImage method is:
2635 %
2636 % MagickBooleanType QuantizeImage(const QuantizeInfo *quantize_info,
2637 % Image *image,ExceptionInfo *exception)
2638 %
2639 % A description of each parameter follows:
2640 %
2641 % o quantize_info: Specifies a pointer to an QuantizeInfo structure.
2642 %
2643 % o image: the image.
2644 %
2645 % o exception: return any errors or warnings in this structure.
2646 %
2647 */
QuantizeImage(const QuantizeInfo * quantize_info,Image * image,ExceptionInfo * exception)2648 MagickExport MagickBooleanType QuantizeImage(const QuantizeInfo *quantize_info,
2649 Image *image,ExceptionInfo *exception)
2650 {
2651 CubeInfo
2652 *cube_info;
2653
2654 MagickBooleanType
2655 status;
2656
2657 size_t
2658 depth,
2659 maximum_colors;
2660
2661 assert(quantize_info != (const QuantizeInfo *) NULL);
2662 assert(quantize_info->signature == MagickCoreSignature);
2663 assert(image != (Image *) NULL);
2664 assert(image->signature == MagickCoreSignature);
2665 if (image->debug != MagickFalse)
2666 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2667 assert(exception != (ExceptionInfo *) NULL);
2668 assert(exception->signature == MagickCoreSignature);
2669 maximum_colors=quantize_info->number_colors;
2670 if (maximum_colors == 0)
2671 maximum_colors=MaxColormapSize;
2672 if (maximum_colors > MaxColormapSize)
2673 maximum_colors=MaxColormapSize;
2674 if (image->alpha_trait != BlendPixelTrait)
2675 {
2676 if (SetImageGray(image,exception) != MagickFalse)
2677 (void) SetGrayscaleImage(image,exception);
2678 }
2679 if ((image->storage_class == PseudoClass) &&
2680 (image->colors <= maximum_colors))
2681 {
2682 if ((quantize_info->colorspace != UndefinedColorspace) &&
2683 (quantize_info->colorspace != CMYKColorspace))
2684 (void) TransformImageColorspace(image,quantize_info->colorspace,
2685 exception);
2686 return(MagickTrue);
2687 }
2688 depth=quantize_info->tree_depth;
2689 if (depth == 0)
2690 {
2691 size_t
2692 colors;
2693
2694 /*
2695 Depth of color tree is: Log4(colormap size)+2.
2696 */
2697 colors=maximum_colors;
2698 for (depth=1; colors != 0; depth++)
2699 colors>>=2;
2700 if ((quantize_info->dither_method != NoDitherMethod) && (depth > 2))
2701 depth--;
2702 if ((image->alpha_trait == BlendPixelTrait) && (depth > 5))
2703 depth--;
2704 if (SetImageGray(image,exception) != MagickFalse)
2705 depth=MaxTreeDepth;
2706 }
2707 /*
2708 Initialize color cube.
2709 */
2710 cube_info=GetCubeInfo(quantize_info,depth,maximum_colors);
2711 if (cube_info == (CubeInfo *) NULL)
2712 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
2713 image->filename);
2714 status=ClassifyImageColors(cube_info,image,exception);
2715 if (status != MagickFalse)
2716 {
2717 /*
2718 Reduce the number of colors in the image if it contains more than the
2719 maximum, otherwise we can disable dithering to improve the performance.
2720 */
2721 if (cube_info->colors > cube_info->maximum_colors)
2722 ReduceImageColors(image,cube_info);
2723 else
2724 cube_info->quantize_info->dither_method=NoDitherMethod;
2725 status=AssignImageColors(image,cube_info,exception);
2726 }
2727 DestroyCubeInfo(cube_info);
2728 return(status);
2729 }
2730
2731 /*
2732 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2733 % %
2734 % %
2735 % %
2736 % Q u a n t i z e I m a g e s %
2737 % %
2738 % %
2739 % %
2740 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2741 %
2742 % QuantizeImages() analyzes the colors within a set of reference images and
2743 % chooses a fixed number of colors to represent the set. The goal of the
2744 % algorithm is to minimize the color difference between the input and output
2745 % images while minimizing the processing time.
2746 %
2747 % The format of the QuantizeImages method is:
2748 %
2749 % MagickBooleanType QuantizeImages(const QuantizeInfo *quantize_info,
2750 % Image *images,ExceptionInfo *exception)
2751 %
2752 % A description of each parameter follows:
2753 %
2754 % o quantize_info: Specifies a pointer to an QuantizeInfo structure.
2755 %
2756 % o images: Specifies a pointer to a list of Image structures.
2757 %
2758 % o exception: return any errors or warnings in this structure.
2759 %
2760 */
QuantizeImages(const QuantizeInfo * quantize_info,Image * images,ExceptionInfo * exception)2761 MagickExport MagickBooleanType QuantizeImages(const QuantizeInfo *quantize_info,
2762 Image *images,ExceptionInfo *exception)
2763 {
2764 CubeInfo
2765 *cube_info;
2766
2767 Image
2768 *image;
2769
2770 MagickBooleanType
2771 proceed,
2772 status;
2773
2774 MagickProgressMonitor
2775 progress_monitor;
2776
2777 register ssize_t
2778 i;
2779
2780 size_t
2781 depth,
2782 maximum_colors,
2783 number_images;
2784
2785 assert(quantize_info != (const QuantizeInfo *) NULL);
2786 assert(quantize_info->signature == MagickCoreSignature);
2787 assert(images != (Image *) NULL);
2788 assert(images->signature == MagickCoreSignature);
2789 if (images->debug != MagickFalse)
2790 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
2791 assert(exception != (ExceptionInfo *) NULL);
2792 assert(exception->signature == MagickCoreSignature);
2793 if (GetNextImageInList(images) == (Image *) NULL)
2794 {
2795 /*
2796 Handle a single image with QuantizeImage.
2797 */
2798 status=QuantizeImage(quantize_info,images,exception);
2799 return(status);
2800 }
2801 status=MagickFalse;
2802 maximum_colors=quantize_info->number_colors;
2803 if (maximum_colors == 0)
2804 maximum_colors=MaxColormapSize;
2805 if (maximum_colors > MaxColormapSize)
2806 maximum_colors=MaxColormapSize;
2807 depth=quantize_info->tree_depth;
2808 if (depth == 0)
2809 {
2810 size_t
2811 colors;
2812
2813 /*
2814 Depth of color tree is: Log4(colormap size)+2.
2815 */
2816 colors=maximum_colors;
2817 for (depth=1; colors != 0; depth++)
2818 colors>>=2;
2819 if (quantize_info->dither_method != NoDitherMethod)
2820 depth--;
2821 }
2822 /*
2823 Initialize color cube.
2824 */
2825 cube_info=GetCubeInfo(quantize_info,depth,maximum_colors);
2826 if (cube_info == (CubeInfo *) NULL)
2827 {
2828 (void) ThrowMagickException(exception,GetMagickModule(),
2829 ResourceLimitError,"MemoryAllocationFailed","`%s'",images->filename);
2830 return(MagickFalse);
2831 }
2832 number_images=GetImageListLength(images);
2833 image=images;
2834 for (i=0; image != (Image *) NULL; i++)
2835 {
2836 progress_monitor=SetImageProgressMonitor(image,(MagickProgressMonitor) NULL,
2837 image->client_data);
2838 status=ClassifyImageColors(cube_info,image,exception);
2839 if (status == MagickFalse)
2840 break;
2841 (void) SetImageProgressMonitor(image,progress_monitor,image->client_data);
2842 proceed=SetImageProgress(image,AssignImageTag,(MagickOffsetType) i,
2843 number_images);
2844 if (proceed == MagickFalse)
2845 break;
2846 image=GetNextImageInList(image);
2847 }
2848 if (status != MagickFalse)
2849 {
2850 /*
2851 Reduce the number of colors in an image sequence.
2852 */
2853 ReduceImageColors(images,cube_info);
2854 image=images;
2855 for (i=0; image != (Image *) NULL; i++)
2856 {
2857 progress_monitor=SetImageProgressMonitor(image,(MagickProgressMonitor)
2858 NULL,image->client_data);
2859 status=AssignImageColors(image,cube_info,exception);
2860 if (status == MagickFalse)
2861 break;
2862 (void) SetImageProgressMonitor(image,progress_monitor,
2863 image->client_data);
2864 proceed=SetImageProgress(image,AssignImageTag,(MagickOffsetType) i,
2865 number_images);
2866 if (proceed == MagickFalse)
2867 break;
2868 image=GetNextImageInList(image);
2869 }
2870 }
2871 DestroyCubeInfo(cube_info);
2872 return(status);
2873 }
2874
2875 /*
2876 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2877 % %
2878 % %
2879 % %
2880 + Q u a n t i z e E r r o r F l a t t e n %
2881 % %
2882 % %
2883 % %
2884 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2885 %
2886 % QuantizeErrorFlatten() traverses the color cube and flattens the quantization
2887 % error into a sorted 1D array. This accelerates the color reduction process.
2888 %
2889 % Contributed by Yoya.
2890 %
2891 % The format of the QuantizeErrorFlatten method is:
2892 %
2893 % size_t QuantizeErrorFlatten(const CubeInfo *cube_info,
2894 % const NodeInfo *node_info,const ssize_t offset,
2895 % double *quantize_error)
2896 %
2897 % A description of each parameter follows.
2898 %
2899 % o cube_info: A pointer to the Cube structure.
2900 %
2901 % o node_info: pointer to node in color cube tree that is current pointer.
2902 %
2903 % o offset: quantize error offset.
2904 %
2905 % o quantize_error: the quantization error vector.
2906 %
2907 */
QuantizeErrorFlatten(const CubeInfo * cube_info,const NodeInfo * node_info,const ssize_t offset,double * quantize_error)2908 static size_t QuantizeErrorFlatten(const CubeInfo *cube_info,
2909 const NodeInfo *node_info,const ssize_t offset,double *quantize_error)
2910 {
2911 register ssize_t
2912 i;
2913
2914 size_t
2915 n,
2916 number_children;
2917
2918 if (offset >= (ssize_t) cube_info->nodes)
2919 return(0);
2920 quantize_error[offset]=node_info->quantize_error;
2921 n=1;
2922 number_children=cube_info->associate_alpha == MagickFalse ? 8UL : 16UL;
2923 for (i=0; i < (ssize_t) number_children ; i++)
2924 if (node_info->child[i] != (NodeInfo *) NULL)
2925 n+=QuantizeErrorFlatten(cube_info,node_info->child[i],offset+n,
2926 quantize_error);
2927 return(n);
2928 }
2929
2930 /*
2931 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2932 % %
2933 % %
2934 % %
2935 + R e d u c e %
2936 % %
2937 % %
2938 % %
2939 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2940 %
2941 % Reduce() traverses the color cube tree and prunes any node whose
2942 % quantization error falls below a particular threshold.
2943 %
2944 % The format of the Reduce method is:
2945 %
2946 % Reduce(CubeInfo *cube_info,const NodeInfo *node_info)
2947 %
2948 % A description of each parameter follows.
2949 %
2950 % o cube_info: A pointer to the Cube structure.
2951 %
2952 % o node_info: pointer to node in color cube tree that is to be pruned.
2953 %
2954 */
Reduce(CubeInfo * cube_info,const NodeInfo * node_info)2955 static void Reduce(CubeInfo *cube_info,const NodeInfo *node_info)
2956 {
2957 register ssize_t
2958 i;
2959
2960 size_t
2961 number_children;
2962
2963 /*
2964 Traverse any children.
2965 */
2966 number_children=cube_info->associate_alpha == MagickFalse ? 8UL : 16UL;
2967 for (i=0; i < (ssize_t) number_children; i++)
2968 if (node_info->child[i] != (NodeInfo *) NULL)
2969 Reduce(cube_info,node_info->child[i]);
2970 if (node_info->quantize_error <= cube_info->pruning_threshold)
2971 PruneChild(cube_info,node_info);
2972 else
2973 {
2974 /*
2975 Find minimum pruning threshold.
2976 */
2977 if (node_info->number_unique > 0)
2978 cube_info->colors++;
2979 if (node_info->quantize_error < cube_info->next_threshold)
2980 cube_info->next_threshold=node_info->quantize_error;
2981 }
2982 }
2983
2984 /*
2985 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2986 % %
2987 % %
2988 % %
2989 + R e d u c e I m a g e C o l o r s %
2990 % %
2991 % %
2992 % %
2993 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2994 %
2995 % ReduceImageColors() repeatedly prunes the tree until the number of nodes
2996 % with n2 > 0 is less than or equal to the maximum number of colors allowed
2997 % in the output image. On any given iteration over the tree, it selects
2998 % those nodes whose E value is minimal for pruning and merges their
2999 % color statistics upward. It uses a pruning threshold, Ep, to govern
3000 % node selection as follows:
3001 %
3002 % Ep = 0
3003 % while number of nodes with (n2 > 0) > required maximum number of colors
3004 % prune all nodes such that E <= Ep
3005 % Set Ep to minimum E in remaining nodes
3006 %
3007 % This has the effect of minimizing any quantization error when merging
3008 % two nodes together.
3009 %
3010 % When a node to be pruned has offspring, the pruning procedure invokes
3011 % itself recursively in order to prune the tree from the leaves upward.
3012 % n2, Sr, Sg, and Sb in a node being pruned are always added to the
3013 % corresponding data in that node's parent. This retains the pruned
3014 % node's color characteristics for later averaging.
3015 %
3016 % For each node, n2 pixels exist for which that node represents the
3017 % smallest volume in RGB space containing those pixel's colors. When n2
3018 % > 0 the node will uniquely define a color in the output image. At the
3019 % beginning of reduction, n2 = 0 for all nodes except a the leaves of
3020 % the tree which represent colors present in the input image.
3021 %
3022 % The other pixel count, n1, indicates the total number of colors
3023 % within the cubic volume which the node represents. This includes n1 -
3024 % n2 pixels whose colors should be defined by nodes at a lower level in
3025 % the tree.
3026 %
3027 % The format of the ReduceImageColors method is:
3028 %
3029 % ReduceImageColors(const Image *image,CubeInfo *cube_info)
3030 %
3031 % A description of each parameter follows.
3032 %
3033 % o image: the image.
3034 %
3035 % o cube_info: A pointer to the Cube structure.
3036 %
3037 */
3038
QuantizeErrorCompare(const void * error_p,const void * error_q)3039 static int QuantizeErrorCompare(const void *error_p,const void *error_q)
3040 {
3041 double
3042 *p,
3043 *q;
3044
3045 p=(double *) error_p;
3046 q=(double *) error_q;
3047 if (*p > *q)
3048 return(1);
3049 if (fabs(*q-*p) <= MagickEpsilon)
3050 return(0);
3051 return(-1);
3052 }
3053
ReduceImageColors(const Image * image,CubeInfo * cube_info)3054 static void ReduceImageColors(const Image *image,CubeInfo *cube_info)
3055 {
3056 #define ReduceImageTag "Reduce/Image"
3057
3058 MagickBooleanType
3059 proceed;
3060
3061 MagickOffsetType
3062 offset;
3063
3064 size_t
3065 span;
3066
3067 cube_info->next_threshold=0.0;
3068 if (cube_info->colors > cube_info->maximum_colors)
3069 {
3070 double
3071 *quantize_error;
3072
3073 /*
3074 Enable rapid reduction of the number of unique colors.
3075 */
3076 quantize_error=(double *) AcquireQuantumMemory(cube_info->nodes,
3077 sizeof(*quantize_error));
3078 if (quantize_error != (double *) NULL)
3079 {
3080 (void) QuantizeErrorFlatten(cube_info,cube_info->root,0,
3081 quantize_error);
3082 qsort(quantize_error,cube_info->nodes,sizeof(double),
3083 QuantizeErrorCompare);
3084 if (cube_info->nodes > (110*(cube_info->maximum_colors+1)/100))
3085 cube_info->next_threshold=quantize_error[cube_info->nodes-110*
3086 (cube_info->maximum_colors+1)/100];
3087 quantize_error=(double *) RelinquishMagickMemory(quantize_error);
3088 }
3089 }
3090 for (span=cube_info->colors; cube_info->colors > cube_info->maximum_colors; )
3091 {
3092 cube_info->pruning_threshold=cube_info->next_threshold;
3093 cube_info->next_threshold=cube_info->root->quantize_error-1;
3094 cube_info->colors=0;
3095 Reduce(cube_info,cube_info->root);
3096 offset=(MagickOffsetType) span-cube_info->colors;
3097 proceed=SetImageProgress(image,ReduceImageTag,offset,span-
3098 cube_info->maximum_colors+1);
3099 if (proceed == MagickFalse)
3100 break;
3101 }
3102 }
3103
3104 /*
3105 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3106 % %
3107 % %
3108 % %
3109 % R e m a p I m a g e %
3110 % %
3111 % %
3112 % %
3113 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3114 %
3115 % RemapImage() replaces the colors of an image with the closest of the colors
3116 % from the reference image.
3117 %
3118 % The format of the RemapImage method is:
3119 %
3120 % MagickBooleanType RemapImage(const QuantizeInfo *quantize_info,
3121 % Image *image,const Image *remap_image,ExceptionInfo *exception)
3122 %
3123 % A description of each parameter follows:
3124 %
3125 % o quantize_info: Specifies a pointer to an QuantizeInfo structure.
3126 %
3127 % o image: the image.
3128 %
3129 % o remap_image: the reference image.
3130 %
3131 % o exception: return any errors or warnings in this structure.
3132 %
3133 */
RemapImage(const QuantizeInfo * quantize_info,Image * image,const Image * remap_image,ExceptionInfo * exception)3134 MagickExport MagickBooleanType RemapImage(const QuantizeInfo *quantize_info,
3135 Image *image,const Image *remap_image,ExceptionInfo *exception)
3136 {
3137 CubeInfo
3138 *cube_info;
3139
3140 MagickBooleanType
3141 status;
3142
3143 /*
3144 Initialize color cube.
3145 */
3146 assert(image != (Image *) NULL);
3147 assert(image->signature == MagickCoreSignature);
3148 if (image->debug != MagickFalse)
3149 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3150 assert(remap_image != (Image *) NULL);
3151 assert(remap_image->signature == MagickCoreSignature);
3152 assert(exception != (ExceptionInfo *) NULL);
3153 assert(exception->signature == MagickCoreSignature);
3154 cube_info=GetCubeInfo(quantize_info,MaxTreeDepth,
3155 quantize_info->number_colors);
3156 if (cube_info == (CubeInfo *) NULL)
3157 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
3158 image->filename);
3159 status=ClassifyImageColors(cube_info,remap_image,exception);
3160 if (status != MagickFalse)
3161 {
3162 /*
3163 Classify image colors from the reference image.
3164 */
3165 cube_info->quantize_info->number_colors=cube_info->colors;
3166 status=AssignImageColors(image,cube_info,exception);
3167 }
3168 DestroyCubeInfo(cube_info);
3169 return(status);
3170 }
3171
3172 /*
3173 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3174 % %
3175 % %
3176 % %
3177 % R e m a p I m a g e s %
3178 % %
3179 % %
3180 % %
3181 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3182 %
3183 % RemapImages() replaces the colors of a sequence of images with the
3184 % closest color from a reference image.
3185 %
3186 % The format of the RemapImage method is:
3187 %
3188 % MagickBooleanType RemapImages(const QuantizeInfo *quantize_info,
3189 % Image *images,Image *remap_image,ExceptionInfo *exception)
3190 %
3191 % A description of each parameter follows:
3192 %
3193 % o quantize_info: Specifies a pointer to an QuantizeInfo structure.
3194 %
3195 % o images: the image sequence.
3196 %
3197 % o remap_image: the reference image.
3198 %
3199 % o exception: return any errors or warnings in this structure.
3200 %
3201 */
RemapImages(const QuantizeInfo * quantize_info,Image * images,const Image * remap_image,ExceptionInfo * exception)3202 MagickExport MagickBooleanType RemapImages(const QuantizeInfo *quantize_info,
3203 Image *images,const Image *remap_image,ExceptionInfo *exception)
3204 {
3205 CubeInfo
3206 *cube_info;
3207
3208 Image
3209 *image;
3210
3211 MagickBooleanType
3212 status;
3213
3214 assert(images != (Image *) NULL);
3215 assert(images->signature == MagickCoreSignature);
3216 if (images->debug != MagickFalse)
3217 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
3218 assert(exception != (ExceptionInfo *) NULL);
3219 assert(exception->signature == MagickCoreSignature);
3220 image=images;
3221 if (remap_image == (Image *) NULL)
3222 {
3223 /*
3224 Create a global colormap for an image sequence.
3225 */
3226 status=QuantizeImages(quantize_info,images,exception);
3227 return(status);
3228 }
3229 /*
3230 Classify image colors from the reference image.
3231 */
3232 cube_info=GetCubeInfo(quantize_info,MaxTreeDepth,
3233 quantize_info->number_colors);
3234 if (cube_info == (CubeInfo *) NULL)
3235 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
3236 image->filename);
3237 status=ClassifyImageColors(cube_info,remap_image,exception);
3238 if (status != MagickFalse)
3239 {
3240 /*
3241 Classify image colors from the reference image.
3242 */
3243 cube_info->quantize_info->number_colors=cube_info->colors;
3244 image=images;
3245 for ( ; image != (Image *) NULL; image=GetNextImageInList(image))
3246 {
3247 status=AssignImageColors(image,cube_info,exception);
3248 if (status == MagickFalse)
3249 break;
3250 }
3251 }
3252 DestroyCubeInfo(cube_info);
3253 return(status);
3254 }
3255
3256 /*
3257 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3258 % %
3259 % %
3260 % %
3261 % S e t G r a y s c a l e I m a g e %
3262 % %
3263 % %
3264 % %
3265 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3266 %
3267 % SetGrayscaleImage() converts an image to a PseudoClass grayscale image.
3268 %
3269 % The format of the SetGrayscaleImage method is:
3270 %
3271 % MagickBooleanType SetGrayscaleImage(Image *image,
3272 % ExceptionInfo *exception)
3273 %
3274 % A description of each parameter follows:
3275 %
3276 % o image: The image.
3277 %
3278 % o exception: return any errors or warnings in this structure.
3279 %
3280 */
3281
3282 #if defined(__cplusplus) || defined(c_plusplus)
3283 extern "C" {
3284 #endif
3285
IntensityCompare(const void * x,const void * y)3286 static int IntensityCompare(const void *x,const void *y)
3287 {
3288 double
3289 intensity;
3290
3291 PixelInfo
3292 *color_1,
3293 *color_2;
3294
3295 color_1=(PixelInfo *) x;
3296 color_2=(PixelInfo *) y;
3297 intensity=GetPixelInfoIntensity((const Image *) NULL,color_1)-
3298 GetPixelInfoIntensity((const Image *) NULL,color_2);
3299 return((int) intensity);
3300 }
3301
3302 #if defined(__cplusplus) || defined(c_plusplus)
3303 }
3304 #endif
3305
SetGrayscaleImage(Image * image,ExceptionInfo * exception)3306 static MagickBooleanType SetGrayscaleImage(Image *image,
3307 ExceptionInfo *exception)
3308 {
3309 CacheView
3310 *image_view;
3311
3312 MagickBooleanType
3313 status;
3314
3315 PixelInfo
3316 *colormap;
3317
3318 register ssize_t
3319 i;
3320
3321 ssize_t
3322 *colormap_index,
3323 j,
3324 y;
3325
3326 assert(image != (Image *) NULL);
3327 assert(image->signature == MagickCoreSignature);
3328 if (image->type != GrayscaleType)
3329 (void) TransformImageColorspace(image,GRAYColorspace,exception);
3330 if (image->storage_class == PseudoClass)
3331 colormap_index=(ssize_t *) AcquireQuantumMemory(image->colors+1,
3332 sizeof(*colormap_index));
3333 else
3334 colormap_index=(ssize_t *) AcquireQuantumMemory(MaxColormapSize+1,
3335 sizeof(*colormap_index));
3336 if (colormap_index == (ssize_t *) NULL)
3337 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
3338 image->filename);
3339 if (image->storage_class != PseudoClass)
3340 {
3341 (void) memset(colormap_index,(-1),MaxColormapSize*
3342 sizeof(*colormap_index));
3343 if (AcquireImageColormap(image,MaxColormapSize,exception) == MagickFalse)
3344 {
3345 colormap_index=(ssize_t *) RelinquishMagickMemory(colormap_index);
3346 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
3347 image->filename);
3348 }
3349 image->colors=0;
3350 status=MagickTrue;
3351 image_view=AcquireAuthenticCacheView(image,exception);
3352 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3353 #pragma omp parallel for schedule(static) shared(status) \
3354 magick_number_threads(image,image,image->rows,1)
3355 #endif
3356 for (y=0; y < (ssize_t) image->rows; y++)
3357 {
3358 register Quantum
3359 *magick_restrict q;
3360
3361 register ssize_t
3362 x;
3363
3364 if (status == MagickFalse)
3365 continue;
3366 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
3367 exception);
3368 if (q == (Quantum *) NULL)
3369 {
3370 status=MagickFalse;
3371 continue;
3372 }
3373 for (x=0; x < (ssize_t) image->columns; x++)
3374 {
3375 register size_t
3376 intensity;
3377
3378 intensity=ScaleQuantumToMap(GetPixelRed(image,q));
3379 if (colormap_index[intensity] < 0)
3380 {
3381 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3382 #pragma omp critical (MagickCore_SetGrayscaleImage)
3383 #endif
3384 if (colormap_index[intensity] < 0)
3385 {
3386 colormap_index[intensity]=(ssize_t) image->colors;
3387 image->colormap[image->colors].red=(double)
3388 GetPixelRed(image,q);
3389 image->colormap[image->colors].green=(double)
3390 GetPixelGreen(image,q);
3391 image->colormap[image->colors].blue=(double)
3392 GetPixelBlue(image,q);
3393 image->colors++;
3394 }
3395 }
3396 SetPixelIndex(image,(Quantum) colormap_index[intensity],q);
3397 q+=GetPixelChannels(image);
3398 }
3399 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3400 status=MagickFalse;
3401 }
3402 image_view=DestroyCacheView(image_view);
3403 }
3404 for (i=0; i < (ssize_t) image->colors; i++)
3405 image->colormap[i].alpha=(double) i;
3406 qsort((void *) image->colormap,image->colors,sizeof(PixelInfo),
3407 IntensityCompare);
3408 colormap=(PixelInfo *) AcquireQuantumMemory(image->colors,sizeof(*colormap));
3409 if (colormap == (PixelInfo *) NULL)
3410 {
3411 colormap_index=(ssize_t *) RelinquishMagickMemory(colormap_index);
3412 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
3413 image->filename);
3414 }
3415 j=0;
3416 colormap[j]=image->colormap[0];
3417 for (i=0; i < (ssize_t) image->colors; i++)
3418 {
3419 if (IsPixelInfoEquivalent(&colormap[j],&image->colormap[i]) == MagickFalse)
3420 {
3421 j++;
3422 colormap[j]=image->colormap[i];
3423 }
3424 colormap_index[(ssize_t) image->colormap[i].alpha]=j;
3425 }
3426 image->colors=(size_t) (j+1);
3427 image->colormap=(PixelInfo *) RelinquishMagickMemory(image->colormap);
3428 image->colormap=colormap;
3429 status=MagickTrue;
3430 image_view=AcquireAuthenticCacheView(image,exception);
3431 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3432 #pragma omp parallel for schedule(static) shared(status) \
3433 magick_number_threads(image,image,image->rows,1)
3434 #endif
3435 for (y=0; y < (ssize_t) image->rows; y++)
3436 {
3437 register Quantum
3438 *magick_restrict q;
3439
3440 register ssize_t
3441 x;
3442
3443 if (status == MagickFalse)
3444 continue;
3445 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3446 if (q == (Quantum *) NULL)
3447 {
3448 status=MagickFalse;
3449 continue;
3450 }
3451 for (x=0; x < (ssize_t) image->columns; x++)
3452 {
3453 SetPixelIndex(image,(Quantum) colormap_index[ScaleQuantumToMap(
3454 GetPixelIndex(image,q))],q);
3455 q+=GetPixelChannels(image);
3456 }
3457 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3458 status=MagickFalse;
3459 }
3460 image_view=DestroyCacheView(image_view);
3461 colormap_index=(ssize_t *) RelinquishMagickMemory(colormap_index);
3462 image->type=GrayscaleType;
3463 if (SetImageMonochrome(image,exception) != MagickFalse)
3464 image->type=BilevelType;
3465 return(status);
3466 }
3467