/src/imagemagick/MagickCore/quantize.c
Line | Count | Source |
1 | | /* |
2 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
3 | | % % |
4 | | % % |
5 | | % % |
6 | | % QQQ U U AAA N N TTTTT IIIII ZZZZZ EEEEE % |
7 | | % Q Q U U A A NN N T I ZZ E % |
8 | | % Q Q U U AAAAA N N N T I ZZZ EEEEE % |
9 | | % Q QQ U U A A N NN T I ZZ E % |
10 | | % QQQQ UUU A A N N T IIIII ZZZZZ EEEEE % |
11 | | % % |
12 | | % % |
13 | | % MagickCore Methods to Reduce the Number of Unique Colors in an Image % |
14 | | % % |
15 | | % Software Design % |
16 | | % Cristy % |
17 | | % July 1992 % |
18 | | % % |
19 | | % % |
20 | | % Copyright @ 1999 ImageMagick Studio LLC, a non-profit organization % |
21 | | % dedicated to making software imaging solutions freely available. % |
22 | | % % |
23 | | % You may not use this file except in compliance with the License. You may % |
24 | | % obtain a copy of the License at % |
25 | | % % |
26 | | % https://imagemagick.org/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/compare.h" |
186 | | #include "MagickCore/enhance.h" |
187 | | #include "MagickCore/exception.h" |
188 | | #include "MagickCore/exception-private.h" |
189 | | #include "MagickCore/histogram.h" |
190 | | #include "MagickCore/image.h" |
191 | | #include "MagickCore/image-private.h" |
192 | | #include "MagickCore/list.h" |
193 | | #include "MagickCore/memory_.h" |
194 | | #include "MagickCore/memory-private.h" |
195 | | #include "MagickCore/monitor.h" |
196 | | #include "MagickCore/monitor-private.h" |
197 | | #include "MagickCore/option.h" |
198 | | #include "MagickCore/pixel-accessor.h" |
199 | | #include "MagickCore/property.h" |
200 | | #include "MagickCore/quantize.h" |
201 | | #include "MagickCore/quantum.h" |
202 | | #include "MagickCore/quantum-private.h" |
203 | | #include "MagickCore/random_.h" |
204 | | #include "MagickCore/resource_.h" |
205 | | #include "MagickCore/string_.h" |
206 | | #include "MagickCore/string-private.h" |
207 | | #include "MagickCore/thread-private.h" |
208 | | |
209 | | /* |
210 | | Define declarations. |
211 | | */ |
212 | | #if !defined(__APPLE__) && !defined(TARGET_OS_IPHONE) |
213 | 1.32G | #define CacheShift 2 |
214 | | #else |
215 | | #define CacheShift 3 |
216 | | #endif |
217 | 4.38G | #define ErrorQueueLength 16 |
218 | 10.5G | #define ErrorRelativeWeight MagickSafeReciprocal(16) |
219 | 430k | #define MaxQNodes 266817 |
220 | 21.0M | #define MaxTreeDepth 8 |
221 | 8.15k | #define QNodesInAList 1920 |
222 | | |
223 | | /* |
224 | | Typedef declarations. |
225 | | */ |
226 | | typedef struct _DoublePixelPacket |
227 | | { |
228 | | double |
229 | | red, |
230 | | green, |
231 | | blue, |
232 | | alpha; |
233 | | } DoublePixelPacket; |
234 | | |
235 | | typedef struct _QNodeInfo |
236 | | { |
237 | | struct _QNodeInfo |
238 | | *parent, |
239 | | *child[16]; |
240 | | |
241 | | MagickSizeType |
242 | | number_unique; |
243 | | |
244 | | DoublePixelPacket |
245 | | total_color; |
246 | | |
247 | | double |
248 | | quantize_error; |
249 | | |
250 | | size_t |
251 | | color_number, |
252 | | id, |
253 | | level; |
254 | | } QNodeInfo; |
255 | | |
256 | | typedef struct _QNodes |
257 | | { |
258 | | QNodeInfo |
259 | | *nodes; |
260 | | |
261 | | struct _QNodes |
262 | | *next; |
263 | | } QNodes; |
264 | | |
265 | | typedef struct _QCubeInfo |
266 | | { |
267 | | QNodeInfo |
268 | | *root; |
269 | | |
270 | | size_t |
271 | | colors, |
272 | | maximum_colors; |
273 | | |
274 | | ssize_t |
275 | | transparent_index; |
276 | | |
277 | | MagickSizeType |
278 | | transparent_pixels; |
279 | | |
280 | | DoublePixelPacket |
281 | | target; |
282 | | |
283 | | double |
284 | | distance, |
285 | | pruning_threshold, |
286 | | next_threshold; |
287 | | |
288 | | size_t |
289 | | nodes, |
290 | | free_nodes, |
291 | | color_number; |
292 | | |
293 | | QNodeInfo |
294 | | *next_node; |
295 | | |
296 | | QNodes |
297 | | *node_queue; |
298 | | |
299 | | MemoryInfo |
300 | | *memory_info; |
301 | | |
302 | | ssize_t |
303 | | *cache; |
304 | | |
305 | | DoublePixelPacket |
306 | | error[ErrorQueueLength]; |
307 | | |
308 | | double |
309 | | diffusion, |
310 | | weights[ErrorQueueLength]; |
311 | | |
312 | | QuantizeInfo |
313 | | *quantize_info; |
314 | | |
315 | | MagickBooleanType |
316 | | associate_alpha; |
317 | | |
318 | | ssize_t |
319 | | x, |
320 | | y; |
321 | | |
322 | | size_t |
323 | | depth; |
324 | | |
325 | | MagickOffsetType |
326 | | offset; |
327 | | |
328 | | MagickSizeType |
329 | | span; |
330 | | } QCubeInfo; |
331 | | |
332 | | /* |
333 | | Method prototypes. |
334 | | */ |
335 | | static QCubeInfo |
336 | | *GetQCubeInfo(const QuantizeInfo *,const size_t,const size_t); |
337 | | |
338 | | static QNodeInfo |
339 | | *GetQNodeInfo(QCubeInfo *,const size_t,const size_t,QNodeInfo *); |
340 | | |
341 | | static MagickBooleanType |
342 | | AssignImageColors(Image *,QCubeInfo *,ExceptionInfo *), |
343 | | ClassifyImageColors(QCubeInfo *,const Image *,ExceptionInfo *), |
344 | | DitherImage(Image *,QCubeInfo *,ExceptionInfo *), |
345 | | SetGrayscaleImage(Image *,ExceptionInfo *), |
346 | | SetImageColormap(Image *,QCubeInfo *,ExceptionInfo *); |
347 | | |
348 | | static void |
349 | | ClosestColor(const Image *,QCubeInfo *,const QNodeInfo *), |
350 | | DefineImageColormap(Image *,QCubeInfo *,QNodeInfo *), |
351 | | DestroyQCubeInfo(QCubeInfo *), |
352 | | PruneLevel(QCubeInfo *,const QNodeInfo *), |
353 | | PruneToCubeDepth(QCubeInfo *,const QNodeInfo *), |
354 | | ReduceImageColors(const Image *,QCubeInfo *); |
355 | | |
356 | | /* |
357 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
358 | | % % |
359 | | % % |
360 | | % % |
361 | | % A c q u i r e Q u a n t i z e I n f o % |
362 | | % % |
363 | | % % |
364 | | % % |
365 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
366 | | % |
367 | | % AcquireQuantizeInfo() allocates the QuantizeInfo structure. |
368 | | % |
369 | | % The format of the AcquireQuantizeInfo method is: |
370 | | % |
371 | | % QuantizeInfo *AcquireQuantizeInfo(const ImageInfo *image_info) |
372 | | % |
373 | | % A description of each parameter follows: |
374 | | % |
375 | | % o image_info: the image info. |
376 | | % |
377 | | */ |
378 | | MagickExport QuantizeInfo *AcquireQuantizeInfo(const ImageInfo *image_info) |
379 | 4.22k | { |
380 | 4.22k | QuantizeInfo |
381 | 4.22k | *quantize_info; |
382 | | |
383 | 4.22k | quantize_info=(QuantizeInfo *) AcquireCriticalMemory(sizeof(*quantize_info)); |
384 | 4.22k | GetQuantizeInfo(quantize_info); |
385 | 4.22k | if (image_info != (ImageInfo *) NULL) |
386 | 4.22k | { |
387 | 4.22k | const char |
388 | 4.22k | *option; |
389 | | |
390 | 4.22k | quantize_info->dither_method=image_info->dither == MagickFalse ? |
391 | 4.22k | NoDitherMethod : RiemersmaDitherMethod; |
392 | 4.22k | option=GetImageOption(image_info,"dither"); |
393 | 4.22k | if (option != (const char *) NULL) |
394 | 0 | quantize_info->dither_method=(DitherMethod) ParseCommandOption( |
395 | 0 | MagickDitherOptions,MagickFalse,option); |
396 | 4.22k | quantize_info->measure_error=image_info->verbose; |
397 | 4.22k | } |
398 | 4.22k | return(quantize_info); |
399 | 4.22k | } |
400 | | |
401 | | /* |
402 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
403 | | % % |
404 | | % % |
405 | | % % |
406 | | + A s s i g n I m a g e C o l o r s % |
407 | | % % |
408 | | % % |
409 | | % % |
410 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
411 | | % |
412 | | % AssignImageColors() generates the output image from the pruned tree. The |
413 | | % output image consists of two parts: (1) A color map, which is an array |
414 | | % of color descriptions (RGB triples) for each color present in the |
415 | | % output image; (2) A pixel array, which represents each pixel as an |
416 | | % index into the color map array. |
417 | | % |
418 | | % First, the assignment phase makes one pass over the pruned color |
419 | | % description tree to establish the image's color map. For each node |
420 | | % with n2 > 0, it divides Sr, Sg, and Sb by n2 . This produces the mean |
421 | | % color of all pixels that classify no lower than this node. Each of |
422 | | % these colors becomes an entry in the color map. |
423 | | % |
424 | | % Finally, the assignment phase reclassifies each pixel in the pruned |
425 | | % tree to identify the deepest node containing the pixel's color. The |
426 | | % pixel's value in the pixel array becomes the index of this node's mean |
427 | | % color in the color map. |
428 | | % |
429 | | % The format of the AssignImageColors() method is: |
430 | | % |
431 | | % MagickBooleanType AssignImageColors(Image *image,QCubeInfo *cube_info) |
432 | | % |
433 | | % A description of each parameter follows. |
434 | | % |
435 | | % o image: the image. |
436 | | % |
437 | | % o cube_info: A pointer to the Cube structure. |
438 | | % |
439 | | */ |
440 | | |
441 | | static inline void AssociateAlphaPixel(const Image *image, |
442 | | const QCubeInfo *cube_info,const Quantum *pixel, |
443 | | DoublePixelPacket *alpha_pixel) |
444 | 209M | { |
445 | 209M | double |
446 | 209M | alpha; |
447 | | |
448 | 209M | if ((cube_info->associate_alpha == MagickFalse) || |
449 | 40.2M | (GetPixelAlpha(image,pixel) == OpaqueAlpha)) |
450 | 191M | { |
451 | 191M | alpha_pixel->red=(double) GetPixelRed(image,pixel); |
452 | 191M | alpha_pixel->green=(double) GetPixelGreen(image,pixel); |
453 | 191M | alpha_pixel->blue=(double) GetPixelBlue(image,pixel); |
454 | 191M | alpha_pixel->alpha=(double) GetPixelAlpha(image,pixel); |
455 | 191M | return; |
456 | 191M | } |
457 | 18.1M | alpha=QuantumScale*(double) GetPixelAlpha(image,pixel); |
458 | 18.1M | alpha_pixel->red=alpha*(double) GetPixelRed(image,pixel); |
459 | 18.1M | alpha_pixel->green=alpha*(double) GetPixelGreen(image,pixel); |
460 | 18.1M | alpha_pixel->blue=alpha*(double) GetPixelBlue(image,pixel); |
461 | 18.1M | alpha_pixel->alpha=(double) GetPixelAlpha(image,pixel); |
462 | 18.1M | } |
463 | | |
464 | | static inline void AssociateAlphaPixelInfo(const QCubeInfo *cube_info, |
465 | | const PixelInfo *pixel,DoublePixelPacket *alpha_pixel) |
466 | 206M | { |
467 | 206M | double |
468 | 206M | alpha; |
469 | | |
470 | 206M | if ((cube_info->associate_alpha == MagickFalse) || |
471 | 39.9M | (pixel->alpha == (double) OpaqueAlpha)) |
472 | 188M | { |
473 | 188M | alpha_pixel->red=(double) pixel->red; |
474 | 188M | alpha_pixel->green=(double) pixel->green; |
475 | 188M | alpha_pixel->blue=(double) pixel->blue; |
476 | 188M | alpha_pixel->alpha=(double) pixel->alpha; |
477 | 188M | return; |
478 | 188M | } |
479 | 18.0M | alpha=(double) (QuantumScale*pixel->alpha); |
480 | 18.0M | alpha_pixel->red=alpha*pixel->red; |
481 | 18.0M | alpha_pixel->green=alpha*pixel->green; |
482 | 18.0M | alpha_pixel->blue=alpha*pixel->blue; |
483 | 18.0M | alpha_pixel->alpha=(double) pixel->alpha; |
484 | 18.0M | } |
485 | | |
486 | | static inline size_t ColorToQNodeId(const QCubeInfo *cube_info, |
487 | | const DoublePixelPacket *pixel,size_t index) |
488 | 20.3M | { |
489 | 20.3M | size_t |
490 | 20.3M | id; |
491 | | |
492 | 20.3M | id=(size_t) (((ScaleQuantumToChar(ClampPixel(pixel->red)) >> index) & 0x01) | |
493 | 20.3M | ((ScaleQuantumToChar(ClampPixel(pixel->green)) >> index) & 0x01) << 1 | |
494 | 20.3M | ((ScaleQuantumToChar(ClampPixel(pixel->blue)) >> index) & 0x01) << 2); |
495 | 20.3M | if (cube_info->associate_alpha != MagickFalse) |
496 | 1.68M | id|=((((size_t) ScaleQuantumToChar(ClampPixel(pixel->alpha)) >> index) & |
497 | 1.68M | 0x1) << 3); |
498 | 20.3M | return(id); |
499 | 20.3M | } |
500 | | |
501 | | static MagickBooleanType AssignImageColors(Image *image,QCubeInfo *cube_info, |
502 | | ExceptionInfo *exception) |
503 | 3.96k | { |
504 | 3.96k | #define AssignImageTag "Assign/Image" |
505 | | |
506 | 3.96k | ColorspaceType |
507 | 3.96k | colorspace; |
508 | | |
509 | 3.96k | ssize_t |
510 | 3.96k | y; |
511 | | |
512 | | /* |
513 | | Allocate image colormap. |
514 | | */ |
515 | 3.96k | colorspace=image->colorspace; |
516 | 3.96k | if (cube_info->quantize_info->colorspace != UndefinedColorspace) |
517 | 2.16k | (void) TransformImageColorspace(image,cube_info->quantize_info->colorspace, |
518 | 2.16k | exception); |
519 | 3.96k | cube_info->transparent_pixels=0; |
520 | 3.96k | cube_info->transparent_index=(-1); |
521 | 3.96k | if (SetImageColormap(image,cube_info,exception) == MagickFalse) |
522 | 0 | return(MagickFalse); |
523 | | /* |
524 | | Create a reduced color image. |
525 | | */ |
526 | 3.96k | if (cube_info->quantize_info->dither_method != NoDitherMethod) |
527 | 3.79k | (void) DitherImage(image,cube_info,exception); |
528 | 173 | else |
529 | 173 | { |
530 | 173 | CacheView |
531 | 173 | *image_view; |
532 | | |
533 | 173 | MagickBooleanType |
534 | 173 | status; |
535 | | |
536 | 173 | status=MagickTrue; |
537 | 173 | image_view=AcquireAuthenticCacheView(image,exception); |
538 | | #if defined(MAGICKCORE_OPENMP_SUPPORT) |
539 | | #pragma omp parallel for schedule(static) shared(status) \ |
540 | | magick_number_threads(image,image,image->rows,1) |
541 | | #endif |
542 | 12.2k | for (y=0; y < (ssize_t) image->rows; y++) |
543 | 12.0k | { |
544 | 12.0k | QCubeInfo |
545 | 12.0k | cube; |
546 | | |
547 | 12.0k | Quantum |
548 | 12.0k | *magick_restrict q; |
549 | | |
550 | 12.0k | ssize_t |
551 | 12.0k | count, |
552 | 12.0k | x; |
553 | | |
554 | 12.0k | if (status == MagickFalse) |
555 | 0 | continue; |
556 | 12.0k | q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1, |
557 | 12.0k | exception); |
558 | 12.0k | if (q == (Quantum *) NULL) |
559 | 0 | { |
560 | 0 | status=MagickFalse; |
561 | 0 | continue; |
562 | 0 | } |
563 | 12.0k | cube=(*cube_info); |
564 | 47.2k | for (x=0; x < (ssize_t) image->columns; x+=count) |
565 | 35.1k | { |
566 | 35.1k | DoublePixelPacket |
567 | 35.1k | pixel; |
568 | | |
569 | 35.1k | const QNodeInfo |
570 | 35.1k | *node_info; |
571 | | |
572 | 35.1k | ssize_t |
573 | 35.1k | i; |
574 | | |
575 | 35.1k | size_t |
576 | 35.1k | id, |
577 | 35.1k | index; |
578 | | |
579 | | /* |
580 | | Identify the deepest node containing the pixel's color. |
581 | | */ |
582 | 62.5k | for (count=1; (x+count) < (ssize_t) image->columns; count++) |
583 | 50.4k | { |
584 | 50.4k | PixelInfo |
585 | 50.4k | packet; |
586 | | |
587 | 50.4k | GetPixelInfoPixel(image,q+count*(ssize_t) GetPixelChannels(image), |
588 | 50.4k | &packet); |
589 | 50.4k | if (IsPixelEquivalent(image,q,&packet) == MagickFalse) |
590 | 23.0k | break; |
591 | 50.4k | } |
592 | 35.1k | AssociateAlphaPixel(image,&cube,q,&pixel); |
593 | 35.1k | node_info=cube.root; |
594 | 281k | for (index=MaxTreeDepth-1; (ssize_t) index > 0; index--) |
595 | 245k | { |
596 | 245k | id=ColorToQNodeId(&cube,&pixel,index); |
597 | 245k | if (node_info->child[id] == (QNodeInfo *) NULL) |
598 | 0 | break; |
599 | 245k | node_info=node_info->child[id]; |
600 | 245k | } |
601 | | /* |
602 | | Find closest color among siblings and their children. |
603 | | */ |
604 | 35.1k | cube.target=pixel; |
605 | 35.1k | cube.distance=(double) (4.0*((double) QuantumRange+1.0)* |
606 | 35.1k | ((double) QuantumRange+1.0)+1.0); |
607 | 35.1k | ClosestColor(image,&cube,node_info->parent); |
608 | 35.1k | index=cube.color_number; |
609 | 97.6k | for (i=0; i < (ssize_t) count; i++) |
610 | 62.5k | { |
611 | 62.5k | if (image->storage_class == PseudoClass) |
612 | 62.5k | SetPixelIndex(image,(Quantum) index,q); |
613 | 62.5k | if (cube.quantize_info->measure_error == MagickFalse) |
614 | 62.5k | { |
615 | 62.5k | SetPixelRed(image,ClampToQuantum( |
616 | 62.5k | image->colormap[index].red),q); |
617 | 62.5k | SetPixelGreen(image,ClampToQuantum( |
618 | 62.5k | image->colormap[index].green),q); |
619 | 62.5k | SetPixelBlue(image,ClampToQuantum( |
620 | 62.5k | image->colormap[index].blue),q); |
621 | 62.5k | if (cube.associate_alpha != MagickFalse) |
622 | 0 | SetPixelAlpha(image,ClampToQuantum( |
623 | 0 | image->colormap[index].alpha),q); |
624 | 62.5k | } |
625 | 62.5k | q+=(ptrdiff_t) GetPixelChannels(image); |
626 | 62.5k | } |
627 | 35.1k | } |
628 | 12.0k | if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse) |
629 | 0 | status=MagickFalse; |
630 | 12.0k | if (image->progress_monitor != (MagickProgressMonitor) NULL) |
631 | 0 | { |
632 | 0 | MagickBooleanType |
633 | 0 | proceed; |
634 | |
|
635 | 0 | proceed=SetImageProgress(image,AssignImageTag,(MagickOffsetType) y, |
636 | 0 | image->rows); |
637 | 0 | if (proceed == MagickFalse) |
638 | 0 | status=MagickFalse; |
639 | 0 | } |
640 | 12.0k | } |
641 | 173 | image_view=DestroyCacheView(image_view); |
642 | 173 | } |
643 | 3.96k | if (cube_info->quantize_info->measure_error != MagickFalse) |
644 | 0 | (void) GetImageQuantizeError(image,exception); |
645 | 3.96k | if ((cube_info->quantize_info->number_colors == 2) && |
646 | 2.08k | (IsGrayColorspace(cube_info->quantize_info->colorspace))) |
647 | 2.08k | { |
648 | 2.08k | double |
649 | 2.08k | intensity; |
650 | | |
651 | | /* |
652 | | Monochrome image. |
653 | | */ |
654 | 2.08k | intensity=GetPixelInfoLuma(image->colormap+0) < (double) |
655 | 2.08k | QuantumRange/2.0 ? 0.0 : (double) QuantumRange; |
656 | 2.08k | if (image->colors > 1) |
657 | 1.33k | { |
658 | 1.33k | intensity=0.0; |
659 | 1.33k | if (GetPixelInfoLuma(image->colormap+0) > |
660 | 1.33k | GetPixelInfoLuma(image->colormap+1)) |
661 | 0 | intensity=(double) QuantumRange; |
662 | 1.33k | } |
663 | 2.08k | image->colormap[0].red=intensity; |
664 | 2.08k | image->colormap[0].green=intensity; |
665 | 2.08k | image->colormap[0].blue=intensity; |
666 | 2.08k | if (image->colors > 1) |
667 | 1.33k | { |
668 | 1.33k | image->colormap[1].red=(double) QuantumRange-intensity; |
669 | 1.33k | image->colormap[1].green=(double) QuantumRange-intensity; |
670 | 1.33k | image->colormap[1].blue=(double) QuantumRange-intensity; |
671 | 1.33k | } |
672 | 2.08k | } |
673 | 3.96k | (void) SyncImage(image,exception); |
674 | 3.96k | if ((cube_info->quantize_info->colorspace != UndefinedColorspace) && |
675 | 2.16k | (IssRGBCompatibleColorspace(colorspace) == MagickFalse)) |
676 | 0 | (void) TransformImageColorspace(image,colorspace,exception); |
677 | 3.96k | return(MagickTrue); |
678 | 3.96k | } |
679 | | |
680 | | /* |
681 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
682 | | % % |
683 | | % % |
684 | | % % |
685 | | + C l a s s i f y I m a g e C o l o r s % |
686 | | % % |
687 | | % % |
688 | | % % |
689 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
690 | | % |
691 | | % ClassifyImageColors() begins by initializing a color description tree |
692 | | % of sufficient depth to represent each possible input color in a leaf. |
693 | | % However, it is impractical to generate a fully-formed color |
694 | | % description tree in the storage_class phase for realistic values of |
695 | | % Cmax. If colors components in the input image are quantized to k-bit |
696 | | % precision, so that Cmax= 2k-1, the tree would need k levels below the |
697 | | % root node to allow representing each possible input color in a leaf. |
698 | | % This becomes prohibitive because the tree's total number of nodes is |
699 | | % 1 + sum(i=1,k,8k). |
700 | | % |
701 | | % A complete tree would require 19,173,961 nodes for k = 8, Cmax = 255. |
702 | | % Therefore, to avoid building a fully populated tree, QUANTIZE: (1) |
703 | | % Initializes data structures for nodes only as they are needed; (2) |
704 | | % Chooses a maximum depth for the tree as a function of the desired |
705 | | % number of colors in the output image (currently log2(colormap size)). |
706 | | % |
707 | | % For each pixel in the input image, storage_class scans downward from |
708 | | % the root of the color description tree. At each level of the tree it |
709 | | % identifies the single node which represents a cube in RGB space |
710 | | % containing It updates the following data for each such node: |
711 | | % |
712 | | % n1 : Number of pixels whose color is contained in the RGB cube |
713 | | % which this node represents; |
714 | | % |
715 | | % n2 : Number of pixels whose color is not represented in a node at |
716 | | % lower depth in the tree; initially, n2 = 0 for all nodes except |
717 | | % leaves of the tree. |
718 | | % |
719 | | % Sr, Sg, Sb : Sums of the red, green, and blue component values for |
720 | | % all pixels not classified at a lower depth. The combination of |
721 | | % these sums and n2 will ultimately characterize the mean color of a |
722 | | % set of pixels represented by this node. |
723 | | % |
724 | | % E: the distance squared in RGB space between each pixel contained |
725 | | % within a node and the nodes' center. This represents the quantization |
726 | | % error for a node. |
727 | | % |
728 | | % The format of the ClassifyImageColors() method is: |
729 | | % |
730 | | % MagickBooleanType ClassifyImageColors(QCubeInfo *cube_info, |
731 | | % const Image *image,ExceptionInfo *exception) |
732 | | % |
733 | | % A description of each parameter follows. |
734 | | % |
735 | | % o cube_info: A pointer to the Cube structure. |
736 | | % |
737 | | % o image: the image. |
738 | | % |
739 | | */ |
740 | | |
741 | | static inline void SetAssociatedAlpha(const Image *image,QCubeInfo *cube_info) |
742 | 3.96k | { |
743 | 3.96k | MagickBooleanType |
744 | 3.96k | associate_alpha; |
745 | | |
746 | 3.96k | associate_alpha=image->alpha_trait != UndefinedPixelTrait ? MagickTrue : |
747 | 3.96k | MagickFalse; |
748 | 3.96k | if ((cube_info->quantize_info->number_colors == 2) && |
749 | 2.08k | ((cube_info->quantize_info->colorspace == LinearGRAYColorspace) || |
750 | 2.08k | (cube_info->quantize_info->colorspace == GRAYColorspace))) |
751 | 2.08k | associate_alpha=MagickFalse; |
752 | 3.96k | cube_info->associate_alpha=associate_alpha; |
753 | 3.96k | } |
754 | | |
755 | | static MagickBooleanType ClassifyImageColors(QCubeInfo *cube_info, |
756 | | const Image *image,ExceptionInfo *exception) |
757 | 3.96k | { |
758 | 430k | #define ClassifyImageTag "Classify/Image" |
759 | | |
760 | 3.96k | CacheView |
761 | 3.96k | *image_view; |
762 | | |
763 | 3.96k | double |
764 | 3.96k | bisect; |
765 | | |
766 | 3.96k | DoublePixelPacket |
767 | 3.96k | error, |
768 | 3.96k | mid, |
769 | 3.96k | midpoint, |
770 | 3.96k | pixel; |
771 | | |
772 | 3.96k | MagickBooleanType |
773 | 3.96k | proceed; |
774 | | |
775 | 3.96k | QNodeInfo |
776 | 3.96k | *node_info; |
777 | | |
778 | 3.96k | size_t |
779 | 3.96k | id, |
780 | 3.96k | index, |
781 | 3.96k | level; |
782 | | |
783 | 3.96k | ssize_t |
784 | 3.96k | count, |
785 | 3.96k | y; |
786 | | |
787 | | /* |
788 | | Classify the first cube_info->maximum_colors colors to a tree depth of 8. |
789 | | */ |
790 | 3.96k | SetAssociatedAlpha(image,cube_info); |
791 | 3.96k | if (cube_info->quantize_info->colorspace != image->colorspace) |
792 | 1.85k | { |
793 | 1.85k | if ((cube_info->quantize_info->colorspace != UndefinedColorspace) && |
794 | 60 | (cube_info->quantize_info->colorspace != CMYKColorspace)) |
795 | 60 | (void) TransformImageColorspace((Image *) image, |
796 | 60 | cube_info->quantize_info->colorspace,exception); |
797 | 1.79k | else |
798 | 1.79k | if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse) |
799 | 0 | (void) TransformImageColorspace((Image *) image,sRGBColorspace, |
800 | 0 | exception); |
801 | 1.85k | } |
802 | 3.96k | midpoint.red=(double) QuantumRange/2.0; |
803 | 3.96k | midpoint.green=(double) QuantumRange/2.0; |
804 | 3.96k | midpoint.blue=(double) QuantumRange/2.0; |
805 | 3.96k | midpoint.alpha=(double) QuantumRange/2.0; |
806 | 3.96k | error.alpha=0.0; |
807 | 3.96k | image_view=AcquireVirtualCacheView(image,exception); |
808 | 398k | for (y=0; y < (ssize_t) image->rows; y++) |
809 | 394k | { |
810 | 394k | const Quantum |
811 | 394k | *magick_restrict p; |
812 | | |
813 | 394k | ssize_t |
814 | 394k | x; |
815 | | |
816 | 394k | p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception); |
817 | 394k | if (p == (const Quantum *) NULL) |
818 | 0 | break; |
819 | 394k | if (cube_info->nodes > MaxQNodes) |
820 | 0 | { |
821 | | /* |
822 | | Prune one level if the color tree is too large. |
823 | | */ |
824 | 0 | PruneLevel(cube_info,cube_info->root); |
825 | 0 | cube_info->depth--; |
826 | 0 | } |
827 | 2.35M | for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) count) |
828 | 1.96M | { |
829 | | /* |
830 | | Start at the root and descend the color cube tree. |
831 | | */ |
832 | 187M | for (count=1; (x+(ssize_t) count) < (ssize_t) image->columns; count++) |
833 | 187M | { |
834 | 187M | PixelInfo |
835 | 187M | packet; |
836 | | |
837 | 187M | GetPixelInfoPixel(image,p+count*(ssize_t) GetPixelChannels(image), |
838 | 187M | &packet); |
839 | 187M | if (IsPixelEquivalent(image,p,&packet) == MagickFalse) |
840 | 1.56M | break; |
841 | 187M | } |
842 | 1.96M | AssociateAlphaPixel(image,cube_info,p,&pixel); |
843 | 1.96M | index=MaxTreeDepth-1; |
844 | 1.96M | bisect=((double) QuantumRange+1.0)/2.0; |
845 | 1.96M | mid=midpoint; |
846 | 1.96M | node_info=cube_info->root; |
847 | 17.6M | for (level=1; level <= MaxTreeDepth; level++) |
848 | 15.6M | { |
849 | 15.6M | double |
850 | 15.6M | distance; |
851 | | |
852 | 15.6M | bisect*=0.5; |
853 | 15.6M | id=ColorToQNodeId(cube_info,&pixel,index); |
854 | 15.6M | mid.red+=(id & 1) != 0 ? bisect : -bisect; |
855 | 15.6M | mid.green+=(id & 2) != 0 ? bisect : -bisect; |
856 | 15.6M | mid.blue+=(id & 4) != 0 ? bisect : -bisect; |
857 | 15.6M | mid.alpha+=(id & 8) != 0 ? bisect : -bisect; |
858 | 15.6M | if (node_info->child[id] == (QNodeInfo *) NULL) |
859 | 489k | { |
860 | | /* |
861 | | Set colors of new node to contain pixel. |
862 | | */ |
863 | 489k | node_info->child[id]=GetQNodeInfo(cube_info,id,level,node_info); |
864 | 489k | if (node_info->child[id] == (QNodeInfo *) NULL) |
865 | 0 | { |
866 | 0 | (void) ThrowMagickException(exception,GetMagickModule(), |
867 | 0 | ResourceLimitError,"MemoryAllocationFailed","`%s'", |
868 | 0 | image->filename); |
869 | 0 | continue; |
870 | 0 | } |
871 | 489k | if (level == MaxTreeDepth) |
872 | 115k | cube_info->colors++; |
873 | 489k | } |
874 | | /* |
875 | | Approximate the quantization error represented by this node. |
876 | | */ |
877 | 15.6M | node_info=node_info->child[id]; |
878 | 15.6M | error.red=QuantumScale*(pixel.red-mid.red); |
879 | 15.6M | error.green=QuantumScale*(pixel.green-mid.green); |
880 | 15.6M | error.blue=QuantumScale*(pixel.blue-mid.blue); |
881 | 15.6M | if (cube_info->associate_alpha != MagickFalse) |
882 | 1.27M | error.alpha=QuantumScale*(pixel.alpha-mid.alpha); |
883 | 15.6M | distance=(double) (error.red*error.red+error.green*error.green+ |
884 | 15.6M | error.blue*error.blue+error.alpha*error.alpha); |
885 | 15.6M | if (IsNaN(distance) != 0) |
886 | 12.1k | distance=0.0; |
887 | 15.6M | node_info->quantize_error+=count*sqrt(distance); |
888 | 15.6M | cube_info->root->quantize_error+=node_info->quantize_error; |
889 | 15.6M | index--; |
890 | 15.6M | } |
891 | | /* |
892 | | Sum RGB for this leaf for later derivation of the mean cube color. |
893 | | */ |
894 | 1.96M | node_info->number_unique=(size_t) ((ssize_t) node_info->number_unique+ |
895 | 1.96M | count); |
896 | 1.96M | node_info->total_color.red+=count*QuantumScale*(double) |
897 | 1.96M | ClampPixel(pixel.red); |
898 | 1.96M | node_info->total_color.green+=count*QuantumScale*(double) |
899 | 1.96M | ClampPixel(pixel.green); |
900 | 1.96M | node_info->total_color.blue+=count*QuantumScale*(double) |
901 | 1.96M | ClampPixel(pixel.blue); |
902 | 1.96M | if (cube_info->associate_alpha != MagickFalse) |
903 | 159k | node_info->total_color.alpha+=count*QuantumScale*(double) |
904 | 159k | ClampPixel(pixel.alpha); |
905 | 1.80M | else |
906 | 1.80M | node_info->total_color.alpha+=count*QuantumScale*(double) |
907 | 1.80M | ClampPixel((double) OpaqueAlpha); |
908 | 1.96M | p+=(ptrdiff_t) count*(ssize_t) GetPixelChannels(image); |
909 | 1.96M | } |
910 | 394k | if (cube_info->colors > cube_info->maximum_colors) |
911 | 256 | { |
912 | 256 | PruneToCubeDepth(cube_info,cube_info->root); |
913 | 256 | break; |
914 | 256 | } |
915 | 394k | proceed=SetImageProgress(image,ClassifyImageTag,(MagickOffsetType) y, |
916 | 394k | image->rows); |
917 | 394k | if (proceed == MagickFalse) |
918 | 0 | break; |
919 | 394k | } |
920 | 40.2k | for (y++; y < (ssize_t) image->rows; y++) |
921 | 36.2k | { |
922 | 36.2k | const Quantum |
923 | 36.2k | *magick_restrict p; |
924 | | |
925 | 36.2k | ssize_t |
926 | 36.2k | x; |
927 | | |
928 | 36.2k | p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception); |
929 | 36.2k | if (p == (const Quantum *) NULL) |
930 | 0 | break; |
931 | 36.2k | if (cube_info->nodes > MaxQNodes) |
932 | 0 | { |
933 | | /* |
934 | | Prune one level if the color tree is too large. |
935 | | */ |
936 | 0 | PruneLevel(cube_info,cube_info->root); |
937 | 0 | cube_info->depth--; |
938 | 0 | } |
939 | 667k | for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) count) |
940 | 631k | { |
941 | | /* |
942 | | Start at the root and descend the color cube tree. |
943 | | */ |
944 | 19.1M | for (count=1; (x+(ssize_t) count) < (ssize_t) image->columns; count++) |
945 | 19.0M | { |
946 | 19.0M | PixelInfo |
947 | 19.0M | packet; |
948 | | |
949 | 19.0M | GetPixelInfoPixel(image,p+count*(ssize_t) GetPixelChannels(image), |
950 | 19.0M | &packet); |
951 | 19.0M | if (IsPixelEquivalent(image,p,&packet) == MagickFalse) |
952 | 595k | break; |
953 | 19.0M | } |
954 | 631k | AssociateAlphaPixel(image,cube_info,p,&pixel); |
955 | 631k | index=MaxTreeDepth-1; |
956 | 631k | bisect=((double) QuantumRange+1.0)/2.0; |
957 | 631k | mid=midpoint; |
958 | 631k | node_info=cube_info->root; |
959 | 3.78M | for (level=1; level <= cube_info->depth; level++) |
960 | 3.15M | { |
961 | 3.15M | double |
962 | 3.15M | distance; |
963 | | |
964 | 3.15M | bisect*=0.5; |
965 | 3.15M | id=ColorToQNodeId(cube_info,&pixel,index); |
966 | 3.15M | mid.red+=(id & 1) != 0 ? bisect : -bisect; |
967 | 3.15M | mid.green+=(id & 2) != 0 ? bisect : -bisect; |
968 | 3.15M | mid.blue+=(id & 4) != 0 ? bisect : -bisect; |
969 | 3.15M | mid.alpha+=(id & 8) != 0 ? bisect : -bisect; |
970 | 3.15M | if (node_info->child[id] == (QNodeInfo *) NULL) |
971 | 183k | { |
972 | | /* |
973 | | Set colors of new node to contain pixel. |
974 | | */ |
975 | 183k | node_info->child[id]=GetQNodeInfo(cube_info,id,level,node_info); |
976 | 183k | if (node_info->child[id] == (QNodeInfo *) NULL) |
977 | 0 | { |
978 | 0 | (void) ThrowMagickException(exception,GetMagickModule(), |
979 | 0 | ResourceLimitError,"MemoryAllocationFailed","%s", |
980 | 0 | image->filename); |
981 | 0 | continue; |
982 | 0 | } |
983 | 183k | if (level == cube_info->depth) |
984 | 125k | cube_info->colors++; |
985 | 183k | } |
986 | | /* |
987 | | Approximate the quantization error represented by this node. |
988 | | */ |
989 | 3.15M | node_info=node_info->child[id]; |
990 | 3.15M | error.red=QuantumScale*(pixel.red-mid.red); |
991 | 3.15M | error.green=QuantumScale*(pixel.green-mid.green); |
992 | 3.15M | error.blue=QuantumScale*(pixel.blue-mid.blue); |
993 | 3.15M | if (cube_info->associate_alpha != MagickFalse) |
994 | 333k | error.alpha=QuantumScale*(pixel.alpha-mid.alpha); |
995 | 3.15M | distance=(double) (error.red*error.red+error.green*error.green+ |
996 | 3.15M | error.blue*error.blue+error.alpha*error.alpha); |
997 | 3.15M | if (IsNaN(distance) != 0) |
998 | 0 | distance=0.0; |
999 | 3.15M | node_info->quantize_error+=count*sqrt(distance); |
1000 | 3.15M | cube_info->root->quantize_error+=node_info->quantize_error; |
1001 | 3.15M | index--; |
1002 | 3.15M | } |
1003 | | /* |
1004 | | Sum RGB for this leaf for later derivation of the mean cube color. |
1005 | | */ |
1006 | 631k | node_info->number_unique=(size_t) ((ssize_t) node_info->number_unique+ |
1007 | 631k | count); |
1008 | 631k | node_info->total_color.red+=count*QuantumScale*(double) |
1009 | 631k | ClampPixel(pixel.red); |
1010 | 631k | node_info->total_color.green+=count*QuantumScale*(double) |
1011 | 631k | ClampPixel(pixel.green); |
1012 | 631k | node_info->total_color.blue+=count*QuantumScale*(double) |
1013 | 631k | ClampPixel(pixel.blue); |
1014 | 631k | if (cube_info->associate_alpha != MagickFalse) |
1015 | 66.6k | node_info->total_color.alpha+=count*QuantumScale*(double) |
1016 | 66.6k | ClampPixel(pixel.alpha); |
1017 | 564k | else |
1018 | 564k | node_info->total_color.alpha+=count*QuantumScale*(double) |
1019 | 564k | ClampPixel((MagickRealType) OpaqueAlpha); |
1020 | 631k | p+=(ptrdiff_t) count*(ssize_t) GetPixelChannels(image); |
1021 | 631k | } |
1022 | 36.2k | proceed=SetImageProgress(image,ClassifyImageTag,(MagickOffsetType) y, |
1023 | 36.2k | image->rows); |
1024 | 36.2k | if (proceed == MagickFalse) |
1025 | 0 | break; |
1026 | 36.2k | } |
1027 | 3.96k | image_view=DestroyCacheView(image_view); |
1028 | 3.96k | if (cube_info->quantize_info->colorspace != image->colorspace) |
1029 | 1.85k | if ((cube_info->quantize_info->colorspace != UndefinedColorspace) && |
1030 | 60 | (cube_info->quantize_info->colorspace != CMYKColorspace)) |
1031 | 60 | (void) TransformImageColorspace((Image *) image,sRGBColorspace,exception); |
1032 | 3.96k | return(y < (ssize_t) image->rows ? MagickFalse : MagickTrue); |
1033 | 3.96k | } |
1034 | | |
1035 | | /* |
1036 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1037 | | % % |
1038 | | % % |
1039 | | % % |
1040 | | % C l o n e Q u a n t i z e I n f o % |
1041 | | % % |
1042 | | % % |
1043 | | % % |
1044 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1045 | | % |
1046 | | % CloneQuantizeInfo() makes a duplicate of the given quantize info structure, |
1047 | | % or if quantize info is NULL, a new one. |
1048 | | % |
1049 | | % The format of the CloneQuantizeInfo method is: |
1050 | | % |
1051 | | % QuantizeInfo *CloneQuantizeInfo(const QuantizeInfo *quantize_info) |
1052 | | % |
1053 | | % A description of each parameter follows: |
1054 | | % |
1055 | | % o clone_info: Method CloneQuantizeInfo returns a duplicate of the given |
1056 | | % quantize info, or if image info is NULL a new one. |
1057 | | % |
1058 | | % o quantize_info: a structure of type info. |
1059 | | % |
1060 | | */ |
1061 | | MagickExport QuantizeInfo *CloneQuantizeInfo(const QuantizeInfo *quantize_info) |
1062 | 3.96k | { |
1063 | 3.96k | QuantizeInfo |
1064 | 3.96k | *clone_info; |
1065 | | |
1066 | 3.96k | clone_info=(QuantizeInfo *) AcquireCriticalMemory(sizeof(*clone_info)); |
1067 | 3.96k | GetQuantizeInfo(clone_info); |
1068 | 3.96k | if (quantize_info == (QuantizeInfo *) NULL) |
1069 | 0 | return(clone_info); |
1070 | 3.96k | clone_info->number_colors=quantize_info->number_colors; |
1071 | 3.96k | clone_info->tree_depth=quantize_info->tree_depth; |
1072 | 3.96k | clone_info->dither_method=quantize_info->dither_method; |
1073 | 3.96k | clone_info->colorspace=quantize_info->colorspace; |
1074 | 3.96k | clone_info->measure_error=quantize_info->measure_error; |
1075 | 3.96k | return(clone_info); |
1076 | 3.96k | } |
1077 | | |
1078 | | /* |
1079 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1080 | | % % |
1081 | | % % |
1082 | | % % |
1083 | | + C l o s e s t C o l o r % |
1084 | | % % |
1085 | | % % |
1086 | | % % |
1087 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1088 | | % |
1089 | | % ClosestColor() traverses the color cube tree at a particular node and |
1090 | | % determines which colormap entry best represents the input color. |
1091 | | % |
1092 | | % The format of the ClosestColor method is: |
1093 | | % |
1094 | | % void ClosestColor(const Image *image,QCubeInfo *cube_info, |
1095 | | % const QNodeInfo *node_info) |
1096 | | % |
1097 | | % A description of each parameter follows. |
1098 | | % |
1099 | | % o image: the image. |
1100 | | % |
1101 | | % o cube_info: A pointer to the Cube structure. |
1102 | | % |
1103 | | % o node_info: the address of a structure of type QNodeInfo which points to a |
1104 | | % node in the color cube tree that is to be pruned. |
1105 | | % |
1106 | | */ |
1107 | | static void ClosestColor(const Image *image,QCubeInfo *cube_info, |
1108 | | const QNodeInfo *node_info) |
1109 | 3.15M | { |
1110 | 3.15M | size_t |
1111 | 3.15M | number_children; |
1112 | | |
1113 | 3.15M | ssize_t |
1114 | 3.15M | i; |
1115 | | |
1116 | | /* |
1117 | | Traverse any children. |
1118 | | */ |
1119 | 3.15M | number_children=cube_info->associate_alpha == MagickFalse ? 8UL : 16UL; |
1120 | 29.4M | for (i=0; i < (ssize_t) number_children; i++) |
1121 | 26.2M | if (node_info->child[i] != (QNodeInfo *) NULL) |
1122 | 2.84M | ClosestColor(image,cube_info,node_info->child[i]); |
1123 | 3.15M | if (node_info->number_unique != 0) |
1124 | 2.80M | { |
1125 | 2.80M | double |
1126 | 2.80M | alpha, |
1127 | 2.80M | beta, |
1128 | 2.80M | distance, |
1129 | 2.80M | pixel; |
1130 | | |
1131 | 2.80M | DoublePixelPacket |
1132 | 2.80M | *magick_restrict q; |
1133 | | |
1134 | 2.80M | PixelInfo |
1135 | 2.80M | *magick_restrict p; |
1136 | | |
1137 | | /* |
1138 | | Determine if this color is "closest". |
1139 | | */ |
1140 | 2.80M | p=image->colormap+node_info->color_number; |
1141 | 2.80M | q=(&cube_info->target); |
1142 | 2.80M | alpha=1.0; |
1143 | 2.80M | beta=1.0; |
1144 | 2.80M | if (cube_info->associate_alpha != MagickFalse) |
1145 | 81.0k | { |
1146 | 81.0k | alpha=(MagickRealType) (QuantumScale*p->alpha); |
1147 | 81.0k | beta=(MagickRealType) (QuantumScale*q->alpha); |
1148 | 81.0k | } |
1149 | 2.80M | pixel=alpha*p->red-beta*q->red; |
1150 | 2.80M | distance=pixel*pixel; |
1151 | 2.80M | if (distance <= cube_info->distance) |
1152 | 1.70M | { |
1153 | 1.70M | pixel=alpha*p->green-beta*q->green; |
1154 | 1.70M | distance+=pixel*pixel; |
1155 | 1.70M | if (distance <= cube_info->distance) |
1156 | 1.18M | { |
1157 | 1.18M | pixel=alpha*p->blue-beta*q->blue; |
1158 | 1.18M | distance+=pixel*pixel; |
1159 | 1.18M | if (distance <= cube_info->distance) |
1160 | 888k | { |
1161 | 888k | if (cube_info->associate_alpha != MagickFalse) |
1162 | 35.3k | { |
1163 | 35.3k | pixel=p->alpha-q->alpha; |
1164 | 35.3k | distance+=pixel*pixel; |
1165 | 35.3k | } |
1166 | 888k | if (distance <= cube_info->distance) |
1167 | 887k | { |
1168 | 887k | cube_info->distance=distance; |
1169 | 887k | cube_info->color_number=node_info->color_number; |
1170 | 887k | } |
1171 | 888k | } |
1172 | 1.18M | } |
1173 | 1.70M | } |
1174 | 2.80M | } |
1175 | 3.15M | } |
1176 | | |
1177 | | /* |
1178 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1179 | | % % |
1180 | | % % |
1181 | | % % |
1182 | | % C o m p r e s s I m a g e C o l o r m a p % |
1183 | | % % |
1184 | | % % |
1185 | | % % |
1186 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1187 | | % |
1188 | | % CompressImageColormap() compresses an image colormap by removing any |
1189 | | % duplicate or unused color entries. |
1190 | | % |
1191 | | % The format of the CompressImageColormap method is: |
1192 | | % |
1193 | | % MagickBooleanType CompressImageColormap(Image *image, |
1194 | | % ExceptionInfo *exception) |
1195 | | % |
1196 | | % A description of each parameter follows: |
1197 | | % |
1198 | | % o image: the image. |
1199 | | % |
1200 | | % o exception: return any errors or warnings in this structure. |
1201 | | % |
1202 | | */ |
1203 | | MagickExport MagickBooleanType CompressImageColormap(Image *image, |
1204 | | ExceptionInfo *exception) |
1205 | 0 | { |
1206 | 0 | QuantizeInfo |
1207 | 0 | quantize_info; |
1208 | |
|
1209 | 0 | assert(image != (Image *) NULL); |
1210 | 0 | assert(image->signature == MagickCoreSignature); |
1211 | 0 | if (IsEventLogging() != MagickFalse) |
1212 | 0 | (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); |
1213 | 0 | if (IsPaletteImage(image) == MagickFalse) |
1214 | 0 | return(MagickFalse); |
1215 | 0 | GetQuantizeInfo(&quantize_info); |
1216 | 0 | quantize_info.number_colors=image->colors; |
1217 | 0 | quantize_info.tree_depth=MaxTreeDepth; |
1218 | 0 | return(QuantizeImage(&quantize_info,image,exception)); |
1219 | 0 | } |
1220 | | |
1221 | | /* |
1222 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1223 | | % % |
1224 | | % % |
1225 | | % % |
1226 | | + D e f i n e I m a g e C o l o r m a p % |
1227 | | % % |
1228 | | % % |
1229 | | % % |
1230 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1231 | | % |
1232 | | % DefineImageColormap() traverses the color cube tree and notes each colormap |
1233 | | % entry. A colormap entry is any node in the color cube tree where the |
1234 | | % of unique colors is not zero. |
1235 | | % |
1236 | | % The format of the DefineImageColormap method is: |
1237 | | % |
1238 | | % void DefineImageColormap(Image *image,QCubeInfo *cube_info, |
1239 | | % QNodeInfo *node_info) |
1240 | | % |
1241 | | % A description of each parameter follows. |
1242 | | % |
1243 | | % o image: the image. |
1244 | | % |
1245 | | % o cube_info: A pointer to the Cube structure. |
1246 | | % |
1247 | | % o node_info: the address of a structure of type QNodeInfo which points to a |
1248 | | % node in the color cube tree that is to be pruned. |
1249 | | % |
1250 | | */ |
1251 | | static void DefineImageColormap(Image *image,QCubeInfo *cube_info, |
1252 | | QNodeInfo *node_info) |
1253 | 260k | { |
1254 | 260k | size_t |
1255 | 260k | number_children; |
1256 | | |
1257 | 260k | ssize_t |
1258 | 260k | i; |
1259 | | |
1260 | | /* |
1261 | | Traverse any children. |
1262 | | */ |
1263 | 260k | number_children=cube_info->associate_alpha == MagickFalse ? 8UL : 16UL; |
1264 | 2.51M | for (i=0; i < (ssize_t) number_children; i++) |
1265 | 2.25M | if (node_info->child[i] != (QNodeInfo *) NULL) |
1266 | 256k | DefineImageColormap(image,cube_info,node_info->child[i]); |
1267 | 260k | if (node_info->number_unique != 0) |
1268 | 91.5k | { |
1269 | 91.5k | double |
1270 | 91.5k | alpha; |
1271 | | |
1272 | 91.5k | PixelInfo |
1273 | 91.5k | *magick_restrict q; |
1274 | | |
1275 | | /* |
1276 | | Colormap entry is defined by the mean color in this cube. |
1277 | | */ |
1278 | 91.5k | q=image->colormap+image->colors; |
1279 | 91.5k | alpha=(double) ((MagickOffsetType) node_info->number_unique); |
1280 | 91.5k | alpha=MagickSafeReciprocal(alpha); |
1281 | 91.5k | if (cube_info->associate_alpha == MagickFalse) |
1282 | 87.4k | { |
1283 | 87.4k | q->red=(double) ClampToQuantum(alpha*(double) QuantumRange* |
1284 | 87.4k | node_info->total_color.red); |
1285 | 87.4k | q->green=(double) ClampToQuantum(alpha*(double) QuantumRange* |
1286 | 87.4k | node_info->total_color.green); |
1287 | 87.4k | q->blue=(double) ClampToQuantum(alpha*(double) QuantumRange* |
1288 | 87.4k | node_info->total_color.blue); |
1289 | 87.4k | q->alpha=(double) OpaqueAlpha; |
1290 | 87.4k | } |
1291 | 4.17k | else |
1292 | 4.17k | { |
1293 | 4.17k | double |
1294 | 4.17k | opacity; |
1295 | | |
1296 | 4.17k | opacity=(double) (alpha*(double) QuantumRange* |
1297 | 4.17k | node_info->total_color.alpha); |
1298 | 4.17k | q->alpha=(double) ClampToQuantum(opacity); |
1299 | 4.17k | if (q->alpha == (double) OpaqueAlpha) |
1300 | 3.01k | { |
1301 | 3.01k | q->red=(double) ClampToQuantum(alpha*(double) QuantumRange* |
1302 | 3.01k | node_info->total_color.red); |
1303 | 3.01k | q->green=(double) ClampToQuantum(alpha*(double) QuantumRange* |
1304 | 3.01k | node_info->total_color.green); |
1305 | 3.01k | q->blue=(double) ClampToQuantum(alpha*(double) QuantumRange* |
1306 | 3.01k | node_info->total_color.blue); |
1307 | 3.01k | } |
1308 | 1.15k | else |
1309 | 1.15k | { |
1310 | 1.15k | double |
1311 | 1.15k | gamma; |
1312 | | |
1313 | 1.15k | gamma=(double) (QuantumScale*q->alpha); |
1314 | 1.15k | gamma=MagickSafeReciprocal(gamma); |
1315 | 1.15k | q->red=(double) ClampToQuantum(alpha*gamma*(double) QuantumRange* |
1316 | 1.15k | node_info->total_color.red); |
1317 | 1.15k | q->green=(double) ClampToQuantum(alpha*gamma*(double) |
1318 | 1.15k | QuantumRange*node_info->total_color.green); |
1319 | 1.15k | q->blue=(double) ClampToQuantum(alpha*gamma*(double) QuantumRange* |
1320 | 1.15k | node_info->total_color.blue); |
1321 | 1.15k | if (node_info->number_unique > cube_info->transparent_pixels) |
1322 | 357 | { |
1323 | 357 | cube_info->transparent_pixels=node_info->number_unique; |
1324 | 357 | cube_info->transparent_index=(ssize_t) image->colors; |
1325 | 357 | } |
1326 | 1.15k | } |
1327 | 4.17k | } |
1328 | 91.5k | node_info->color_number=image->colors++; |
1329 | 91.5k | } |
1330 | 260k | } |
1331 | | |
1332 | | /* |
1333 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1334 | | % % |
1335 | | % % |
1336 | | % % |
1337 | | + D e s t r o y Q C u b e I n f o % |
1338 | | % % |
1339 | | % % |
1340 | | % % |
1341 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1342 | | % |
1343 | | % DestroyQCubeInfo() deallocates memory associated with an image. |
1344 | | % |
1345 | | % The format of the DestroyQCubeInfo method is: |
1346 | | % |
1347 | | % DestroyQCubeInfo(QCubeInfo *cube_info) |
1348 | | % |
1349 | | % A description of each parameter follows: |
1350 | | % |
1351 | | % o cube_info: the address of a structure of type QCubeInfo. |
1352 | | % |
1353 | | */ |
1354 | | static void DestroyQCubeInfo(QCubeInfo *cube_info) |
1355 | 3.96k | { |
1356 | 3.96k | QNodes |
1357 | 3.96k | *nodes; |
1358 | | |
1359 | | /* |
1360 | | Release color cube tree storage. |
1361 | | */ |
1362 | 3.96k | do |
1363 | 4.07k | { |
1364 | 4.07k | nodes=cube_info->node_queue->next; |
1365 | 4.07k | cube_info->node_queue->nodes=(QNodeInfo *) RelinquishMagickMemory( |
1366 | 4.07k | cube_info->node_queue->nodes); |
1367 | 4.07k | cube_info->node_queue=(QNodes *) RelinquishMagickMemory( |
1368 | 4.07k | cube_info->node_queue); |
1369 | 4.07k | cube_info->node_queue=nodes; |
1370 | 4.07k | } while (cube_info->node_queue != (QNodes *) NULL); |
1371 | 3.96k | if (cube_info->memory_info != (MemoryInfo *) NULL) |
1372 | 3.79k | cube_info->memory_info=RelinquishVirtualMemory(cube_info->memory_info); |
1373 | 3.96k | cube_info->quantize_info=DestroyQuantizeInfo(cube_info->quantize_info); |
1374 | 3.96k | cube_info=(QCubeInfo *) RelinquishMagickMemory(cube_info); |
1375 | 3.96k | } |
1376 | | |
1377 | | /* |
1378 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1379 | | % % |
1380 | | % % |
1381 | | % % |
1382 | | % D e s t r o y Q u a n t i z e I n f o % |
1383 | | % % |
1384 | | % % |
1385 | | % % |
1386 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1387 | | % |
1388 | | % DestroyQuantizeInfo() deallocates memory associated with an QuantizeInfo |
1389 | | % structure. |
1390 | | % |
1391 | | % The format of the DestroyQuantizeInfo method is: |
1392 | | % |
1393 | | % QuantizeInfo *DestroyQuantizeInfo(QuantizeInfo *quantize_info) |
1394 | | % |
1395 | | % A description of each parameter follows: |
1396 | | % |
1397 | | % o quantize_info: Specifies a pointer to an QuantizeInfo structure. |
1398 | | % |
1399 | | */ |
1400 | | MagickExport QuantizeInfo *DestroyQuantizeInfo(QuantizeInfo *quantize_info) |
1401 | 640k | { |
1402 | 640k | assert(quantize_info != (QuantizeInfo *) NULL); |
1403 | 640k | assert(quantize_info->signature == MagickCoreSignature); |
1404 | 640k | if (IsEventLogging() != MagickFalse) |
1405 | 0 | (void) LogMagickEvent(TraceEvent,GetMagickModule(),"..."); |
1406 | 640k | quantize_info->signature=(~MagickCoreSignature); |
1407 | 640k | quantize_info=(QuantizeInfo *) RelinquishMagickMemory(quantize_info); |
1408 | 640k | return(quantize_info); |
1409 | 640k | } |
1410 | | |
1411 | | /* |
1412 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1413 | | % % |
1414 | | % % |
1415 | | % % |
1416 | | + D i t h e r I m a g e % |
1417 | | % % |
1418 | | % % |
1419 | | % % |
1420 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1421 | | % |
1422 | | % DitherImage() distributes the difference between an original image and |
1423 | | % the corresponding color reduced algorithm to neighboring pixels using |
1424 | | % serpentine-scan Floyd-Steinberg error diffusion. DitherImage returns |
1425 | | % MagickTrue if the image is dithered otherwise MagickFalse. |
1426 | | % |
1427 | | % The format of the DitherImage method is: |
1428 | | % |
1429 | | % MagickBooleanType DitherImage(Image *image,QCubeInfo *cube_info, |
1430 | | % ExceptionInfo *exception) |
1431 | | % |
1432 | | % A description of each parameter follows. |
1433 | | % |
1434 | | % o image: the image. |
1435 | | % |
1436 | | % o cube_info: A pointer to the Cube structure. |
1437 | | % |
1438 | | % o exception: return any errors or warnings in this structure. |
1439 | | % |
1440 | | */ |
1441 | | |
1442 | | static DoublePixelPacket **DestroyPixelTLS(DoublePixelPacket **pixels) |
1443 | 0 | { |
1444 | 0 | ssize_t |
1445 | 0 | i; |
1446 | |
|
1447 | 0 | assert(pixels != (DoublePixelPacket **) NULL); |
1448 | 0 | for (i=0; i < (ssize_t) GetMagickResourceLimit(ThreadResource); i++) |
1449 | 0 | if (pixels[i] != (DoublePixelPacket *) NULL) |
1450 | 0 | pixels[i]=(DoublePixelPacket *) RelinquishMagickMemory(pixels[i]); |
1451 | 0 | pixels=(DoublePixelPacket **) RelinquishMagickMemory(pixels); |
1452 | 0 | return(pixels); |
1453 | 0 | } |
1454 | | |
1455 | | static DoublePixelPacket **AcquirePixelTLS(const size_t count) |
1456 | 0 | { |
1457 | 0 | DoublePixelPacket |
1458 | 0 | **pixels; |
1459 | |
|
1460 | 0 | size_t |
1461 | 0 | number_threads; |
1462 | |
|
1463 | 0 | ssize_t |
1464 | 0 | i; |
1465 | |
|
1466 | 0 | number_threads=(size_t) GetMagickResourceLimit(ThreadResource); |
1467 | 0 | pixels=(DoublePixelPacket **) AcquireQuantumMemory(number_threads, |
1468 | 0 | sizeof(*pixels)); |
1469 | 0 | if (pixels == (DoublePixelPacket **) NULL) |
1470 | 0 | return((DoublePixelPacket **) NULL); |
1471 | 0 | (void) memset(pixels,0,number_threads*sizeof(*pixels)); |
1472 | 0 | for (i=0; i < (ssize_t) number_threads; i++) |
1473 | 0 | { |
1474 | 0 | pixels[i]=(DoublePixelPacket *) AcquireQuantumMemory(count,2* |
1475 | 0 | sizeof(**pixels)); |
1476 | 0 | if (pixels[i] == (DoublePixelPacket *) NULL) |
1477 | 0 | return(DestroyPixelTLS(pixels)); |
1478 | 0 | } |
1479 | 0 | return(pixels); |
1480 | 0 | } |
1481 | | |
1482 | | static inline ssize_t CacheOffset(QCubeInfo *cube_info, |
1483 | | const DoublePixelPacket *pixel) |
1484 | 206M | { |
1485 | 206M | #define RedShift(pixel) (((pixel) >> CacheShift) << (0*(8-CacheShift))) |
1486 | 206M | #define GreenShift(pixel) (((pixel) >> CacheShift) << (1*(8-CacheShift))) |
1487 | 206M | #define BlueShift(pixel) (((pixel) >> CacheShift) << (2*(8-CacheShift))) |
1488 | 206M | #define AlphaShift(pixel) (((pixel) >> CacheShift) << (3*(8-CacheShift))) |
1489 | | |
1490 | 206M | ssize_t |
1491 | 206M | offset; |
1492 | | |
1493 | 206M | offset=(ssize_t) (RedShift(ScaleQuantumToChar(ClampPixel(pixel->red))) | |
1494 | 206M | GreenShift(ScaleQuantumToChar(ClampPixel(pixel->green))) | |
1495 | 206M | BlueShift(ScaleQuantumToChar(ClampPixel(pixel->blue)))); |
1496 | 206M | if (cube_info->associate_alpha != MagickFalse) |
1497 | 39.9M | offset|=AlphaShift(ScaleQuantumToChar(ClampPixel(pixel->alpha))); |
1498 | 206M | return(offset); |
1499 | 206M | } |
1500 | | |
1501 | | static MagickBooleanType FloydSteinbergDither(Image *image,QCubeInfo *cube_info, |
1502 | | ExceptionInfo *exception) |
1503 | 0 | { |
1504 | 0 | #define DitherImageTag "Dither/Image" |
1505 | |
|
1506 | 0 | CacheView |
1507 | 0 | *image_view; |
1508 | |
|
1509 | 0 | DoublePixelPacket |
1510 | 0 | **pixels; |
1511 | |
|
1512 | 0 | MagickBooleanType |
1513 | 0 | status; |
1514 | |
|
1515 | 0 | ssize_t |
1516 | 0 | y; |
1517 | | |
1518 | | /* |
1519 | | Distribute quantization error using Floyd-Steinberg. |
1520 | | */ |
1521 | 0 | pixels=AcquirePixelTLS(image->columns); |
1522 | 0 | if (pixels == (DoublePixelPacket **) NULL) |
1523 | 0 | return(MagickFalse); |
1524 | 0 | status=MagickTrue; |
1525 | 0 | image_view=AcquireAuthenticCacheView(image,exception); |
1526 | 0 | for (y=0; y < (ssize_t) image->rows; y++) |
1527 | 0 | { |
1528 | 0 | const int |
1529 | 0 | id = GetOpenMPThreadId(); |
1530 | |
|
1531 | 0 | DoublePixelPacket |
1532 | 0 | *current, |
1533 | 0 | *previous; |
1534 | |
|
1535 | 0 | QCubeInfo |
1536 | 0 | cube; |
1537 | |
|
1538 | 0 | Quantum |
1539 | 0 | *magick_restrict q; |
1540 | |
|
1541 | 0 | size_t |
1542 | 0 | index; |
1543 | |
|
1544 | 0 | ssize_t |
1545 | 0 | x, |
1546 | 0 | v; |
1547 | |
|
1548 | 0 | if (status == MagickFalse) |
1549 | 0 | continue; |
1550 | 0 | q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception); |
1551 | 0 | if (q == (Quantum *) NULL) |
1552 | 0 | { |
1553 | 0 | status=MagickFalse; |
1554 | 0 | continue; |
1555 | 0 | } |
1556 | 0 | cube=(*cube_info); |
1557 | 0 | current=pixels[id]+(y & 0x01)*image->columns; |
1558 | 0 | previous=pixels[id]+((y+1) & 0x01)*image->columns; |
1559 | 0 | v=(ssize_t) ((y & 0x01) != 0 ? -1 : 1); |
1560 | 0 | for (x=0; x < (ssize_t) image->columns; x++) |
1561 | 0 | { |
1562 | 0 | DoublePixelPacket |
1563 | 0 | color, |
1564 | 0 | pixel; |
1565 | |
|
1566 | 0 | ssize_t |
1567 | 0 | i; |
1568 | |
|
1569 | 0 | ssize_t |
1570 | 0 | u; |
1571 | |
|
1572 | 0 | u=(y & 0x01) != 0 ? (ssize_t) image->columns-1-x : x; |
1573 | 0 | AssociateAlphaPixel(image,&cube,q+u*(ssize_t) GetPixelChannels(image), |
1574 | 0 | &pixel); |
1575 | 0 | if (x > 0) |
1576 | 0 | { |
1577 | 0 | pixel.red+=7.0*cube_info->diffusion*current[u-v].red/16; |
1578 | 0 | pixel.green+=7.0*cube_info->diffusion*current[u-v].green/16; |
1579 | 0 | pixel.blue+=7.0*cube_info->diffusion*current[u-v].blue/16; |
1580 | 0 | if (cube.associate_alpha != MagickFalse) |
1581 | 0 | pixel.alpha+=7.0*cube_info->diffusion*current[u-v].alpha/16; |
1582 | 0 | } |
1583 | 0 | if (y > 0) |
1584 | 0 | { |
1585 | 0 | if (x < (ssize_t) (image->columns-1)) |
1586 | 0 | { |
1587 | 0 | pixel.red+=cube_info->diffusion*previous[u+v].red/16; |
1588 | 0 | pixel.green+=cube_info->diffusion*previous[u+v].green/16; |
1589 | 0 | pixel.blue+=cube_info->diffusion*previous[u+v].blue/16; |
1590 | 0 | if (cube.associate_alpha != MagickFalse) |
1591 | 0 | pixel.alpha+=cube_info->diffusion*previous[u+v].alpha/16; |
1592 | 0 | } |
1593 | 0 | pixel.red+=5.0*cube_info->diffusion*previous[u].red/16; |
1594 | 0 | pixel.green+=5.0*cube_info->diffusion*previous[u].green/16; |
1595 | 0 | pixel.blue+=5.0*cube_info->diffusion*previous[u].blue/16; |
1596 | 0 | if (cube.associate_alpha != MagickFalse) |
1597 | 0 | pixel.alpha+=5.0*cube_info->diffusion*previous[u].alpha/16; |
1598 | 0 | if (x > 0) |
1599 | 0 | { |
1600 | 0 | pixel.red+=3.0*cube_info->diffusion*previous[u-v].red/16; |
1601 | 0 | pixel.green+=3.0*cube_info->diffusion*previous[u-v].green/16; |
1602 | 0 | pixel.blue+=3.0*cube_info->diffusion*previous[u-v].blue/16; |
1603 | 0 | if (cube.associate_alpha != MagickFalse) |
1604 | 0 | pixel.alpha+=3.0*cube_info->diffusion*previous[u-v].alpha/16; |
1605 | 0 | } |
1606 | 0 | } |
1607 | 0 | pixel.red=(double) ClampPixel(pixel.red); |
1608 | 0 | pixel.green=(double) ClampPixel(pixel.green); |
1609 | 0 | pixel.blue=(double) ClampPixel(pixel.blue); |
1610 | 0 | if (cube.associate_alpha != MagickFalse) |
1611 | 0 | pixel.alpha=(double) ClampPixel(pixel.alpha); |
1612 | 0 | i=CacheOffset(&cube,&pixel); |
1613 | 0 | if (cube.cache[i] < 0) |
1614 | 0 | { |
1615 | 0 | QNodeInfo |
1616 | 0 | *node_info; |
1617 | |
|
1618 | 0 | size_t |
1619 | 0 | node_id; |
1620 | | |
1621 | | /* |
1622 | | Identify the deepest node containing the pixel's color. |
1623 | | */ |
1624 | 0 | node_info=cube.root; |
1625 | 0 | for (index=MaxTreeDepth-1; (ssize_t) index > 0; index--) |
1626 | 0 | { |
1627 | 0 | node_id=ColorToQNodeId(&cube,&pixel,index); |
1628 | 0 | if (node_info->child[node_id] == (QNodeInfo *) NULL) |
1629 | 0 | break; |
1630 | 0 | node_info=node_info->child[node_id]; |
1631 | 0 | } |
1632 | | /* |
1633 | | Find closest color among siblings and their children. |
1634 | | */ |
1635 | 0 | cube.target=pixel; |
1636 | 0 | cube.distance=(double) (4.0*((double) QuantumRange+1.0)*((double) |
1637 | 0 | QuantumRange+1.0)+1.0); |
1638 | 0 | ClosestColor(image,&cube,node_info->parent); |
1639 | 0 | cube.cache[i]=(ssize_t) cube.color_number; |
1640 | 0 | } |
1641 | | /* |
1642 | | Assign pixel to closest colormap entry. |
1643 | | */ |
1644 | 0 | index=(size_t) cube.cache[i]; |
1645 | 0 | if (image->storage_class == PseudoClass) |
1646 | 0 | SetPixelIndex(image,(Quantum) index,q+u*(ssize_t) |
1647 | 0 | GetPixelChannels(image)); |
1648 | 0 | if (cube.quantize_info->measure_error == MagickFalse) |
1649 | 0 | { |
1650 | 0 | SetPixelRed(image,ClampToQuantum(image->colormap[index].red), |
1651 | 0 | q+u*(ssize_t) GetPixelChannels(image)); |
1652 | 0 | SetPixelGreen(image,ClampToQuantum(image->colormap[index].green), |
1653 | 0 | q+u*(ssize_t) GetPixelChannels(image)); |
1654 | 0 | SetPixelBlue(image,ClampToQuantum(image->colormap[index].blue), |
1655 | 0 | q+u*(ssize_t) GetPixelChannels(image)); |
1656 | 0 | if (cube.associate_alpha != MagickFalse) |
1657 | 0 | SetPixelAlpha(image,ClampToQuantum(image->colormap[index].alpha), |
1658 | 0 | q+u*(ssize_t) GetPixelChannels(image)); |
1659 | 0 | } |
1660 | 0 | if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse) |
1661 | 0 | status=MagickFalse; |
1662 | | /* |
1663 | | Store the error. |
1664 | | */ |
1665 | 0 | AssociateAlphaPixelInfo(&cube,image->colormap+index,&color); |
1666 | 0 | current[u].red=pixel.red-color.red; |
1667 | 0 | current[u].green=pixel.green-color.green; |
1668 | 0 | current[u].blue=pixel.blue-color.blue; |
1669 | 0 | if (cube.associate_alpha != MagickFalse) |
1670 | 0 | current[u].alpha=pixel.alpha-color.alpha; |
1671 | 0 | if (image->progress_monitor != (MagickProgressMonitor) NULL) |
1672 | 0 | { |
1673 | 0 | MagickBooleanType |
1674 | 0 | proceed; |
1675 | |
|
1676 | 0 | proceed=SetImageProgress(image,DitherImageTag,(MagickOffsetType) y, |
1677 | 0 | image->rows); |
1678 | 0 | if (proceed == MagickFalse) |
1679 | 0 | status=MagickFalse; |
1680 | 0 | } |
1681 | 0 | } |
1682 | 0 | } |
1683 | 0 | image_view=DestroyCacheView(image_view); |
1684 | 0 | pixels=DestroyPixelTLS(pixels); |
1685 | 0 | return(MagickTrue); |
1686 | 0 | } |
1687 | | |
1688 | | static MagickBooleanType RiemersmaDither(Image *image,CacheView *image_view, |
1689 | | QCubeInfo *cube_info,const unsigned int direction,ExceptionInfo *exception) |
1690 | 1.66G | { |
1691 | 1.66G | #define DitherImageTag "Dither/Image" |
1692 | | |
1693 | 1.66G | QCubeInfo |
1694 | 1.66G | *p; |
1695 | | |
1696 | 1.66G | DoublePixelPacket |
1697 | 1.66G | color, |
1698 | 1.66G | pixel; |
1699 | | |
1700 | 1.66G | MagickBooleanType |
1701 | 1.66G | proceed; |
1702 | | |
1703 | 1.66G | size_t |
1704 | 1.66G | index; |
1705 | | |
1706 | 1.66G | p=cube_info; |
1707 | 1.66G | if ((p->x >= 0) && (p->x < (ssize_t) image->columns) && |
1708 | 1.00G | (p->y >= 0) && (p->y < (ssize_t) image->rows)) |
1709 | 206M | { |
1710 | 206M | Quantum |
1711 | 206M | *magick_restrict q; |
1712 | | |
1713 | 206M | ssize_t |
1714 | 206M | i; |
1715 | | |
1716 | | /* |
1717 | | Distribute error. |
1718 | | */ |
1719 | 206M | q=GetCacheViewAuthenticPixels(image_view,p->x,p->y,1,1,exception); |
1720 | 206M | if (q == (Quantum *) NULL) |
1721 | 0 | return(MagickFalse); |
1722 | 206M | AssociateAlphaPixel(image,cube_info,q,&pixel); |
1723 | 3.51G | for (i=0; i < ErrorQueueLength; i++) |
1724 | 3.30G | { |
1725 | 3.30G | pixel.red+=ErrorRelativeWeight*cube_info->diffusion*p->weights[i]* |
1726 | 3.30G | p->error[i].red; |
1727 | 3.30G | pixel.green+=ErrorRelativeWeight*cube_info->diffusion*p->weights[i]* |
1728 | 3.30G | p->error[i].green; |
1729 | 3.30G | pixel.blue+=ErrorRelativeWeight*cube_info->diffusion*p->weights[i]* |
1730 | 3.30G | p->error[i].blue; |
1731 | 3.30G | if (cube_info->associate_alpha != MagickFalse) |
1732 | 639M | pixel.alpha+=ErrorRelativeWeight*cube_info->diffusion*p->weights[i]* |
1733 | 639M | p->error[i].alpha; |
1734 | 3.30G | } |
1735 | 206M | pixel.red=(double) ClampPixel(pixel.red); |
1736 | 206M | pixel.green=(double) ClampPixel(pixel.green); |
1737 | 206M | pixel.blue=(double) ClampPixel(pixel.blue); |
1738 | 206M | if (cube_info->associate_alpha != MagickFalse) |
1739 | 39.9M | pixel.alpha=(double) ClampPixel(pixel.alpha); |
1740 | 206M | i=CacheOffset(cube_info,&pixel); |
1741 | 206M | if (p->cache[i] < 0) |
1742 | 278k | { |
1743 | 278k | QNodeInfo |
1744 | 278k | *node_info; |
1745 | | |
1746 | 278k | size_t |
1747 | 278k | id; |
1748 | | |
1749 | | /* |
1750 | | Identify the deepest node containing the pixel's color. |
1751 | | */ |
1752 | 278k | node_info=p->root; |
1753 | 1.27M | for (index=MaxTreeDepth-1; (ssize_t) index > 0; index--) |
1754 | 1.25M | { |
1755 | 1.25M | id=ColorToQNodeId(cube_info,&pixel,index); |
1756 | 1.25M | if (node_info->child[id] == (QNodeInfo *) NULL) |
1757 | 261k | break; |
1758 | 996k | node_info=node_info->child[id]; |
1759 | 996k | } |
1760 | | /* |
1761 | | Find closest color among siblings and their children. |
1762 | | */ |
1763 | 278k | p->target=pixel; |
1764 | 278k | p->distance=(double) (4.0*((double) QuantumRange+1.0)*((double) |
1765 | 278k | QuantumRange+1.0)+1.0); |
1766 | 278k | ClosestColor(image,p,node_info->parent); |
1767 | 278k | p->cache[i]=(ssize_t) p->color_number; |
1768 | 278k | } |
1769 | | /* |
1770 | | Assign pixel to closest colormap entry. |
1771 | | */ |
1772 | 206M | index=(size_t) p->cache[i]; |
1773 | 206M | if (image->storage_class == PseudoClass) |
1774 | 206M | SetPixelIndex(image,(Quantum) index,q); |
1775 | 206M | if (cube_info->quantize_info->measure_error == MagickFalse) |
1776 | 206M | { |
1777 | 206M | SetPixelRed(image,ClampToQuantum(image->colormap[index].red),q); |
1778 | 206M | SetPixelGreen(image,ClampToQuantum(image->colormap[index].green),q); |
1779 | 206M | SetPixelBlue(image,ClampToQuantum(image->colormap[index].blue),q); |
1780 | 206M | if (cube_info->associate_alpha != MagickFalse) |
1781 | 39.9M | SetPixelAlpha(image,ClampToQuantum(image->colormap[index].alpha),q); |
1782 | 206M | } |
1783 | 206M | if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse) |
1784 | 0 | return(MagickFalse); |
1785 | | /* |
1786 | | Propagate the error as the last entry of the error queue. |
1787 | | */ |
1788 | 206M | (void) memmove(p->error,p->error+1,(ErrorQueueLength-1)* |
1789 | 206M | sizeof(p->error[0])); |
1790 | 206M | AssociateAlphaPixelInfo(cube_info,image->colormap+index,&color); |
1791 | 206M | p->error[ErrorQueueLength-1].red=pixel.red-color.red; |
1792 | 206M | p->error[ErrorQueueLength-1].green=pixel.green-color.green; |
1793 | 206M | p->error[ErrorQueueLength-1].blue=pixel.blue-color.blue; |
1794 | 206M | if (cube_info->associate_alpha != MagickFalse) |
1795 | 39.9M | p->error[ErrorQueueLength-1].alpha=pixel.alpha-color.alpha; |
1796 | 206M | proceed=SetImageProgress(image,DitherImageTag,p->offset,p->span); |
1797 | 206M | if (proceed == MagickFalse) |
1798 | 0 | return(MagickFalse); |
1799 | 206M | p->offset++; |
1800 | 206M | } |
1801 | 1.66G | switch (direction) |
1802 | 1.66G | { |
1803 | 414M | case WestGravity: p->x--; break; |
1804 | 415M | case EastGravity: p->x++; break; |
1805 | 415M | case NorthGravity: p->y--; break; |
1806 | 415M | case SouthGravity: p->y++; break; |
1807 | 1.66G | } |
1808 | 1.66G | return(MagickTrue); |
1809 | 1.66G | } |
1810 | | |
1811 | | static MagickBooleanType Riemersma(Image *image,CacheView *image_view, |
1812 | | QCubeInfo *cube_info,const size_t level,const unsigned int direction, |
1813 | | ExceptionInfo *exception) |
1814 | 553M | { |
1815 | 553M | MagickBooleanType |
1816 | 553M | status; |
1817 | | |
1818 | 553M | status=MagickTrue; |
1819 | 553M | if (level == 1) |
1820 | 415M | switch (direction) |
1821 | 415M | { |
1822 | 103M | case WestGravity: |
1823 | 103M | { |
1824 | 103M | status=RiemersmaDither(image,image_view,cube_info,EastGravity, |
1825 | 103M | exception); |
1826 | 103M | if (status != MagickFalse) |
1827 | 103M | status=RiemersmaDither(image,image_view,cube_info,SouthGravity, |
1828 | 103M | exception); |
1829 | 103M | if (status != MagickFalse) |
1830 | 103M | status=RiemersmaDither(image,image_view,cube_info,WestGravity, |
1831 | 103M | exception); |
1832 | 103M | break; |
1833 | 0 | } |
1834 | 103M | case EastGravity: |
1835 | 103M | { |
1836 | 103M | status=RiemersmaDither(image,image_view,cube_info,WestGravity, |
1837 | 103M | exception); |
1838 | 103M | if (status != MagickFalse) |
1839 | 103M | status=RiemersmaDither(image,image_view,cube_info,NorthGravity, |
1840 | 103M | exception); |
1841 | 103M | if (status != MagickFalse) |
1842 | 103M | status=RiemersmaDither(image,image_view,cube_info,EastGravity, |
1843 | 103M | exception); |
1844 | 103M | break; |
1845 | 0 | } |
1846 | 104M | case NorthGravity: |
1847 | 104M | { |
1848 | 104M | status=RiemersmaDither(image,image_view,cube_info,SouthGravity, |
1849 | 104M | exception); |
1850 | 104M | if (status != MagickFalse) |
1851 | 104M | status=RiemersmaDither(image,image_view,cube_info,EastGravity, |
1852 | 104M | exception); |
1853 | 104M | if (status != MagickFalse) |
1854 | 104M | status=RiemersmaDither(image,image_view,cube_info,NorthGravity, |
1855 | 104M | exception); |
1856 | 104M | break; |
1857 | 0 | } |
1858 | 103M | case SouthGravity: |
1859 | 103M | { |
1860 | 103M | status=RiemersmaDither(image,image_view,cube_info,NorthGravity, |
1861 | 103M | exception); |
1862 | 103M | if (status != MagickFalse) |
1863 | 103M | status=RiemersmaDither(image,image_view,cube_info,WestGravity, |
1864 | 103M | exception); |
1865 | 103M | if (status != MagickFalse) |
1866 | 103M | status=RiemersmaDither(image,image_view,cube_info,SouthGravity, |
1867 | 103M | exception); |
1868 | 103M | break; |
1869 | 0 | } |
1870 | 0 | default: |
1871 | 0 | break; |
1872 | 415M | } |
1873 | 138M | else |
1874 | 138M | switch (direction) |
1875 | 138M | { |
1876 | 34.5M | case WestGravity: |
1877 | 34.5M | { |
1878 | 34.5M | status=Riemersma(image,image_view,cube_info,level-1,NorthGravity, |
1879 | 34.5M | exception); |
1880 | 34.5M | if (status != MagickFalse) |
1881 | 34.5M | status=RiemersmaDither(image,image_view,cube_info,EastGravity, |
1882 | 34.5M | exception); |
1883 | 34.5M | if (status != MagickFalse) |
1884 | 34.5M | status=Riemersma(image,image_view,cube_info,level-1,WestGravity, |
1885 | 34.5M | exception); |
1886 | 34.5M | if (status != MagickFalse) |
1887 | 34.5M | status=RiemersmaDither(image,image_view,cube_info,SouthGravity, |
1888 | 34.5M | exception); |
1889 | 34.5M | if (status != MagickFalse) |
1890 | 34.5M | status=Riemersma(image,image_view,cube_info,level-1,WestGravity, |
1891 | 34.5M | exception); |
1892 | 34.5M | if (status != MagickFalse) |
1893 | 34.5M | status=RiemersmaDither(image,image_view,cube_info,WestGravity, |
1894 | 34.5M | exception); |
1895 | 34.5M | if (status != MagickFalse) |
1896 | 34.5M | status=Riemersma(image,image_view,cube_info,level-1,SouthGravity, |
1897 | 34.5M | exception); |
1898 | 34.5M | break; |
1899 | 0 | } |
1900 | 34.5M | case EastGravity: |
1901 | 34.5M | { |
1902 | 34.5M | status=Riemersma(image,image_view,cube_info,level-1,SouthGravity, |
1903 | 34.5M | exception); |
1904 | 34.5M | if (status != MagickFalse) |
1905 | 34.5M | status=RiemersmaDither(image,image_view,cube_info,WestGravity, |
1906 | 34.5M | exception); |
1907 | 34.5M | if (status != MagickFalse) |
1908 | 34.5M | status=Riemersma(image,image_view,cube_info,level-1,EastGravity, |
1909 | 34.5M | exception); |
1910 | 34.5M | if (status != MagickFalse) |
1911 | 34.5M | status=RiemersmaDither(image,image_view,cube_info,NorthGravity, |
1912 | 34.5M | exception); |
1913 | 34.5M | if (status != MagickFalse) |
1914 | 34.5M | status=Riemersma(image,image_view,cube_info,level-1,EastGravity, |
1915 | 34.5M | exception); |
1916 | 34.5M | if (status != MagickFalse) |
1917 | 34.5M | status=RiemersmaDither(image,image_view,cube_info,EastGravity, |
1918 | 34.5M | exception); |
1919 | 34.5M | if (status != MagickFalse) |
1920 | 34.5M | status=Riemersma(image,image_view,cube_info,level-1,NorthGravity, |
1921 | 34.5M | exception); |
1922 | 34.5M | break; |
1923 | 0 | } |
1924 | 34.8M | case NorthGravity: |
1925 | 34.8M | { |
1926 | 34.8M | status=Riemersma(image,image_view,cube_info,level-1,WestGravity, |
1927 | 34.8M | exception); |
1928 | 34.8M | if (status != MagickFalse) |
1929 | 34.8M | status=RiemersmaDither(image,image_view,cube_info,SouthGravity, |
1930 | 34.8M | exception); |
1931 | 34.8M | if (status != MagickFalse) |
1932 | 34.8M | status=Riemersma(image,image_view,cube_info,level-1,NorthGravity, |
1933 | 34.8M | exception); |
1934 | 34.8M | if (status != MagickFalse) |
1935 | 34.8M | status=RiemersmaDither(image,image_view,cube_info,EastGravity, |
1936 | 34.8M | exception); |
1937 | 34.8M | if (status != MagickFalse) |
1938 | 34.8M | status=Riemersma(image,image_view,cube_info,level-1,NorthGravity, |
1939 | 34.8M | exception); |
1940 | 34.8M | if (status != MagickFalse) |
1941 | 34.8M | status=RiemersmaDither(image,image_view,cube_info,NorthGravity, |
1942 | 34.8M | exception); |
1943 | 34.8M | if (status != MagickFalse) |
1944 | 34.8M | status=Riemersma(image,image_view,cube_info,level-1,EastGravity, |
1945 | 34.8M | exception); |
1946 | 34.8M | break; |
1947 | 0 | } |
1948 | 34.3M | case SouthGravity: |
1949 | 34.3M | { |
1950 | 34.3M | status=Riemersma(image,image_view,cube_info,level-1,EastGravity, |
1951 | 34.3M | exception); |
1952 | 34.3M | if (status != MagickFalse) |
1953 | 34.3M | status=RiemersmaDither(image,image_view,cube_info,NorthGravity, |
1954 | 34.3M | exception); |
1955 | 34.3M | if (status != MagickFalse) |
1956 | 34.3M | status=Riemersma(image,image_view,cube_info,level-1,SouthGravity, |
1957 | 34.3M | exception); |
1958 | 34.3M | if (status != MagickFalse) |
1959 | 34.3M | status=RiemersmaDither(image,image_view,cube_info,WestGravity, |
1960 | 34.3M | exception); |
1961 | 34.3M | if (status != MagickFalse) |
1962 | 34.3M | status=Riemersma(image,image_view,cube_info,level-1,SouthGravity, |
1963 | 34.3M | exception); |
1964 | 34.3M | if (status != MagickFalse) |
1965 | 34.3M | status=RiemersmaDither(image,image_view,cube_info,SouthGravity, |
1966 | 34.3M | exception); |
1967 | 34.3M | if (status != MagickFalse) |
1968 | 34.3M | status=Riemersma(image,image_view,cube_info,level-1,WestGravity, |
1969 | 34.3M | exception); |
1970 | 34.3M | break; |
1971 | 0 | } |
1972 | 0 | default: |
1973 | 0 | break; |
1974 | 138M | } |
1975 | 553M | return(status); |
1976 | 553M | } |
1977 | | |
1978 | | static MagickBooleanType DitherImage(Image *image,QCubeInfo *cube_info, |
1979 | | ExceptionInfo *exception) |
1980 | 3.79k | { |
1981 | 3.79k | CacheView |
1982 | 3.79k | *image_view; |
1983 | | |
1984 | 3.79k | const char |
1985 | 3.79k | *artifact; |
1986 | | |
1987 | 3.79k | MagickBooleanType |
1988 | 3.79k | status; |
1989 | | |
1990 | 3.79k | size_t |
1991 | 3.79k | extent, |
1992 | 3.79k | level; |
1993 | | |
1994 | 3.79k | artifact=GetImageArtifact(image,"dither:diffusion-amount"); |
1995 | 3.79k | if (artifact != (const char *) NULL) |
1996 | 0 | cube_info->diffusion=StringToDoubleInterval(artifact,1.0); |
1997 | 3.79k | if (cube_info->quantize_info->dither_method != RiemersmaDitherMethod) |
1998 | 0 | return(FloydSteinbergDither(image,cube_info,exception)); |
1999 | | /* |
2000 | | Distribute quantization error along a Hilbert curve. |
2001 | | */ |
2002 | 3.79k | (void) memset(cube_info->error,0,ErrorQueueLength*sizeof(*cube_info->error)); |
2003 | 3.79k | cube_info->x=0; |
2004 | 3.79k | cube_info->y=0; |
2005 | 3.79k | extent=MagickMax(image->columns,image->rows); |
2006 | 3.79k | level=(size_t) log2((double) extent); |
2007 | 3.79k | if (((size_t) 1UL << level) < extent) |
2008 | 2.42k | level++; |
2009 | 3.79k | cube_info->offset=0; |
2010 | 3.79k | cube_info->span=(MagickSizeType) image->columns*image->rows; |
2011 | 3.79k | image_view=AcquireAuthenticCacheView(image,exception); |
2012 | 3.79k | status=MagickTrue; |
2013 | 3.79k | if (level > 0) |
2014 | 3.47k | status=Riemersma(image,image_view,cube_info,level,NorthGravity,exception); |
2015 | 3.79k | if (status != MagickFalse) |
2016 | 3.79k | status=RiemersmaDither(image,image_view,cube_info,ForgetGravity,exception); |
2017 | 3.79k | image_view=DestroyCacheView(image_view); |
2018 | 3.79k | return(status); |
2019 | 3.79k | } |
2020 | | |
2021 | | /* |
2022 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
2023 | | % % |
2024 | | % % |
2025 | | % % |
2026 | | + G e t Q C u b e I n f o % |
2027 | | % % |
2028 | | % % |
2029 | | % % |
2030 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
2031 | | % |
2032 | | % GetQCubeInfo() initialize the Cube data structure. |
2033 | | % |
2034 | | % The format of the GetQCubeInfo method is: |
2035 | | % |
2036 | | % QCubeInfo GetQCubeInfo(const QuantizeInfo *quantize_info, |
2037 | | % const size_t depth,const size_t maximum_colors) |
2038 | | % |
2039 | | % A description of each parameter follows. |
2040 | | % |
2041 | | % o quantize_info: Specifies a pointer to an QuantizeInfo structure. |
2042 | | % |
2043 | | % o depth: Normally, this integer value is zero or one. A zero or |
2044 | | % one tells Quantize to choose a optimal tree depth of Log4(number_colors). |
2045 | | % A tree of this depth generally allows the best representation of the |
2046 | | % reference image with the least amount of memory and the fastest |
2047 | | % computational speed. In some cases, such as an image with low color |
2048 | | % dispersion (a few number of colors), a value other than |
2049 | | % Log4(number_colors) is required. To expand the color tree completely, |
2050 | | % use a value of 8. |
2051 | | % |
2052 | | % o maximum_colors: maximum colors. |
2053 | | % |
2054 | | */ |
2055 | | static QCubeInfo *GetQCubeInfo(const QuantizeInfo *quantize_info, |
2056 | | const size_t depth,const size_t maximum_colors) |
2057 | 3.96k | { |
2058 | 3.96k | double |
2059 | 3.96k | weight; |
2060 | | |
2061 | 3.96k | QCubeInfo |
2062 | 3.96k | *cube_info; |
2063 | | |
2064 | 3.96k | size_t |
2065 | 3.96k | length; |
2066 | | |
2067 | 3.96k | ssize_t |
2068 | 3.96k | i; |
2069 | | |
2070 | | /* |
2071 | | Initialize tree to describe color cube_info. |
2072 | | */ |
2073 | 3.96k | cube_info=(QCubeInfo *) AcquireMagickMemory(sizeof(*cube_info)); |
2074 | 3.96k | if (cube_info == (QCubeInfo *) NULL) |
2075 | 0 | return((QCubeInfo *) NULL); |
2076 | 3.96k | (void) memset(cube_info,0,sizeof(*cube_info)); |
2077 | 3.96k | cube_info->depth=depth; |
2078 | 3.96k | if (cube_info->depth > MaxTreeDepth) |
2079 | 0 | cube_info->depth=MaxTreeDepth; |
2080 | 3.96k | if (cube_info->depth < 2) |
2081 | 0 | cube_info->depth=2; |
2082 | 3.96k | cube_info->maximum_colors=maximum_colors; |
2083 | | /* |
2084 | | Initialize root node. |
2085 | | */ |
2086 | 3.96k | cube_info->root=GetQNodeInfo(cube_info,0,0,(QNodeInfo *) NULL); |
2087 | 3.96k | if (cube_info->root == (QNodeInfo *) NULL) |
2088 | 0 | return((QCubeInfo *) NULL); |
2089 | 3.96k | cube_info->root->parent=cube_info->root; |
2090 | 3.96k | cube_info->quantize_info=CloneQuantizeInfo(quantize_info); |
2091 | 3.96k | if (cube_info->quantize_info->dither_method == NoDitherMethod) |
2092 | 173 | return(cube_info); |
2093 | | /* |
2094 | | Initialize dither resources. |
2095 | | */ |
2096 | 3.79k | length=(size_t) (1UL << (4*(8-CacheShift))); |
2097 | 3.79k | cube_info->memory_info=AcquireVirtualMemory(length,sizeof(*cube_info->cache)); |
2098 | 3.79k | if (cube_info->memory_info == (MemoryInfo *) NULL) |
2099 | 2 | return((QCubeInfo *) NULL); |
2100 | 3.79k | cube_info->cache=(ssize_t *) GetVirtualMemoryBlob(cube_info->memory_info); |
2101 | | /* |
2102 | | Initialize color cache. |
2103 | | */ |
2104 | 3.79k | (void) memset(cube_info->cache,(-1),sizeof(*cube_info->cache)*length); |
2105 | | /* |
2106 | | Distribute weights along a curve of exponential decay. |
2107 | | */ |
2108 | 3.79k | weight=1.0; |
2109 | 64.4k | for (i=0; i < ErrorQueueLength; i++) |
2110 | 60.6k | { |
2111 | 60.6k | cube_info->weights[i]=MagickSafeReciprocal(weight); |
2112 | 60.6k | weight*=exp(log(1.0/ErrorRelativeWeight)/(ErrorQueueLength-1.0)); |
2113 | 60.6k | } |
2114 | 3.79k | cube_info->diffusion=1.0; |
2115 | 3.79k | return(cube_info); |
2116 | 3.79k | } |
2117 | | |
2118 | | /* |
2119 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
2120 | | % % |
2121 | | % % |
2122 | | % % |
2123 | | + G e t N o d e I n f o % |
2124 | | % % |
2125 | | % % |
2126 | | % % |
2127 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
2128 | | % |
2129 | | % GetQNodeInfo() allocates memory for a new node in the color cube tree and |
2130 | | % presets all fields to zero. |
2131 | | % |
2132 | | % The format of the GetQNodeInfo method is: |
2133 | | % |
2134 | | % QNodeInfo *GetQNodeInfo(QCubeInfo *cube_info,const size_t id, |
2135 | | % const size_t level,QNodeInfo *parent) |
2136 | | % |
2137 | | % A description of each parameter follows. |
2138 | | % |
2139 | | % o node: The GetQNodeInfo method returns a pointer to a queue of nodes. |
2140 | | % |
2141 | | % o id: Specifies the child number of the node. |
2142 | | % |
2143 | | % o level: Specifies the level in the storage_class the node resides. |
2144 | | % |
2145 | | */ |
2146 | | static QNodeInfo *GetQNodeInfo(QCubeInfo *cube_info,const size_t id, |
2147 | | const size_t level,QNodeInfo *parent) |
2148 | 676k | { |
2149 | 676k | QNodeInfo |
2150 | 676k | *node_info; |
2151 | | |
2152 | 676k | if (cube_info->free_nodes == 0) |
2153 | 4.07k | { |
2154 | 4.07k | QNodes |
2155 | 4.07k | *nodes; |
2156 | | |
2157 | | /* |
2158 | | Allocate a new queue of nodes. |
2159 | | */ |
2160 | 4.07k | nodes=(QNodes *) AcquireMagickMemory(sizeof(*nodes)); |
2161 | 4.07k | if (nodes == (QNodes *) NULL) |
2162 | 0 | return((QNodeInfo *) NULL); |
2163 | 4.07k | nodes->nodes=(QNodeInfo *) AcquireQuantumMemory(QNodesInAList, |
2164 | 4.07k | sizeof(*nodes->nodes)); |
2165 | 4.07k | if (nodes->nodes == (QNodeInfo *) NULL) |
2166 | 0 | return((QNodeInfo *) NULL); |
2167 | 4.07k | nodes->next=cube_info->node_queue; |
2168 | 4.07k | cube_info->node_queue=nodes; |
2169 | 4.07k | cube_info->next_node=nodes->nodes; |
2170 | 4.07k | cube_info->free_nodes=QNodesInAList; |
2171 | 4.07k | } |
2172 | 676k | cube_info->nodes++; |
2173 | 676k | cube_info->free_nodes--; |
2174 | 676k | node_info=cube_info->next_node++; |
2175 | 676k | (void) memset(node_info,0,sizeof(*node_info)); |
2176 | 676k | node_info->parent=parent; |
2177 | 676k | node_info->id=id; |
2178 | 676k | node_info->level=level; |
2179 | 676k | return(node_info); |
2180 | 676k | } |
2181 | | |
2182 | | /* |
2183 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
2184 | | % % |
2185 | | % % |
2186 | | % % |
2187 | | % G e t I m a g e Q u a n t i z e E r r o r % |
2188 | | % % |
2189 | | % % |
2190 | | % % |
2191 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
2192 | | % |
2193 | | % GetImageQuantizeError() measures the difference between the original |
2194 | | % and quantized images. This difference is the total quantization error. |
2195 | | % The error is computed by summing over all pixels in an image the distance |
2196 | | % squared in RGB space between each reference pixel value and its quantized |
2197 | | % value. These values are computed: |
2198 | | % |
2199 | | % o mean_error_per_pixel: This value is the mean error for any single |
2200 | | % pixel in the image. |
2201 | | % |
2202 | | % o normalized_mean_square_error: This value is the normalized mean |
2203 | | % quantization error for any single pixel in the image. This distance |
2204 | | % measure is normalized to a range between 0 and 1. It is independent |
2205 | | % of the range of red, green, and blue values in the image. |
2206 | | % |
2207 | | % o normalized_maximum_square_error: This value is the normalized |
2208 | | % maximum quantization error for any single pixel in the image. This |
2209 | | % distance measure is normalized to a range between 0 and 1. It is |
2210 | | % independent of the range of red, green, and blue values in your image. |
2211 | | % |
2212 | | % The format of the GetImageQuantizeError method is: |
2213 | | % |
2214 | | % MagickBooleanType GetImageQuantizeError(Image *image, |
2215 | | % ExceptionInfo *exception) |
2216 | | % |
2217 | | % A description of each parameter follows. |
2218 | | % |
2219 | | % o image: the image. |
2220 | | % |
2221 | | % o exception: return any errors or warnings in this structure. |
2222 | | % |
2223 | | */ |
2224 | | MagickExport MagickBooleanType GetImageQuantizeError(Image *image, |
2225 | | ExceptionInfo *exception) |
2226 | 0 | { |
2227 | 0 | CacheView |
2228 | 0 | *image_view; |
2229 | |
|
2230 | 0 | double |
2231 | 0 | alpha, |
2232 | 0 | area, |
2233 | 0 | beta, |
2234 | 0 | distance, |
2235 | 0 | maximum_error, |
2236 | 0 | mean_error, |
2237 | 0 | mean_error_per_pixel; |
2238 | |
|
2239 | 0 | ssize_t |
2240 | 0 | index, |
2241 | 0 | y; |
2242 | |
|
2243 | 0 | assert(image != (Image *) NULL); |
2244 | 0 | assert(image->signature == MagickCoreSignature); |
2245 | 0 | if (IsEventLogging() != MagickFalse) |
2246 | 0 | (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); |
2247 | 0 | image->total_colors=GetNumberColors(image,(FILE *) NULL,exception); |
2248 | 0 | (void) memset(&image->error,0,sizeof(image->error)); |
2249 | 0 | if (image->storage_class == DirectClass) |
2250 | 0 | return(MagickTrue); |
2251 | 0 | alpha=1.0; |
2252 | 0 | beta=1.0; |
2253 | 0 | area=3.0*image->columns*image->rows; |
2254 | 0 | maximum_error=0.0; |
2255 | 0 | mean_error_per_pixel=0.0; |
2256 | 0 | mean_error=0.0; |
2257 | 0 | image_view=AcquireVirtualCacheView(image,exception); |
2258 | 0 | for (y=0; y < (ssize_t) image->rows; y++) |
2259 | 0 | { |
2260 | 0 | const Quantum |
2261 | 0 | *magick_restrict p; |
2262 | |
|
2263 | 0 | ssize_t |
2264 | 0 | x; |
2265 | |
|
2266 | 0 | p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception); |
2267 | 0 | if (p == (const Quantum *) NULL) |
2268 | 0 | break; |
2269 | 0 | for (x=0; x < (ssize_t) image->columns; x++) |
2270 | 0 | { |
2271 | 0 | index=(ssize_t) GetPixelIndex(image,p); |
2272 | 0 | if (image->alpha_trait != UndefinedPixelTrait) |
2273 | 0 | { |
2274 | 0 | alpha=(double) (QuantumScale*(double) GetPixelAlpha(image,p)); |
2275 | 0 | beta=(double) (QuantumScale*image->colormap[index].alpha); |
2276 | 0 | } |
2277 | 0 | distance=fabs((double) (alpha*(double) GetPixelRed(image,p)-beta* |
2278 | 0 | image->colormap[index].red)); |
2279 | 0 | mean_error_per_pixel+=distance; |
2280 | 0 | mean_error+=distance*distance; |
2281 | 0 | if (distance > maximum_error) |
2282 | 0 | maximum_error=distance; |
2283 | 0 | distance=fabs((double) (alpha*(double) GetPixelGreen(image,p)-beta* |
2284 | 0 | image->colormap[index].green)); |
2285 | 0 | mean_error_per_pixel+=distance; |
2286 | 0 | mean_error+=distance*distance; |
2287 | 0 | if (distance > maximum_error) |
2288 | 0 | maximum_error=distance; |
2289 | 0 | distance=fabs((double) (alpha*(double) GetPixelBlue(image,p)-beta* |
2290 | 0 | image->colormap[index].blue)); |
2291 | 0 | mean_error_per_pixel+=distance; |
2292 | 0 | mean_error+=distance*distance; |
2293 | 0 | if (distance > maximum_error) |
2294 | 0 | maximum_error=distance; |
2295 | 0 | p+=(ptrdiff_t) GetPixelChannels(image); |
2296 | 0 | } |
2297 | 0 | } |
2298 | 0 | image_view=DestroyCacheView(image_view); |
2299 | 0 | image->error.mean_error_per_pixel=(double) mean_error_per_pixel/area; |
2300 | 0 | image->error.normalized_mean_error=(double) QuantumScale*QuantumScale* |
2301 | 0 | mean_error/area; |
2302 | 0 | image->error.normalized_maximum_error=(double) QuantumScale*maximum_error; |
2303 | 0 | return(MagickTrue); |
2304 | 0 | } |
2305 | | |
2306 | | /* |
2307 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
2308 | | % % |
2309 | | % % |
2310 | | % % |
2311 | | % G e t Q u a n t i z e I n f o % |
2312 | | % % |
2313 | | % % |
2314 | | % % |
2315 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
2316 | | % |
2317 | | % GetQuantizeInfo() initializes the QuantizeInfo structure. |
2318 | | % |
2319 | | % The format of the GetQuantizeInfo method is: |
2320 | | % |
2321 | | % GetQuantizeInfo(QuantizeInfo *quantize_info) |
2322 | | % |
2323 | | % A description of each parameter follows: |
2324 | | % |
2325 | | % o quantize_info: Specifies a pointer to a QuantizeInfo structure. |
2326 | | % |
2327 | | */ |
2328 | | MagickExport void GetQuantizeInfo(QuantizeInfo *quantize_info) |
2329 | 640k | { |
2330 | 640k | assert(quantize_info != (QuantizeInfo *) NULL); |
2331 | 640k | if (IsEventLogging() != MagickFalse) |
2332 | 0 | (void) LogMagickEvent(TraceEvent,GetMagickModule(),"..."); |
2333 | 640k | (void) memset(quantize_info,0,sizeof(*quantize_info)); |
2334 | 640k | quantize_info->number_colors=256; |
2335 | 640k | quantize_info->dither_method=RiemersmaDitherMethod; |
2336 | 640k | quantize_info->colorspace=UndefinedColorspace; |
2337 | 640k | quantize_info->measure_error=MagickFalse; |
2338 | 640k | quantize_info->signature=MagickCoreSignature; |
2339 | 640k | } |
2340 | | |
2341 | | /* |
2342 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
2343 | | % % |
2344 | | % % |
2345 | | % % |
2346 | | % K m e a n s I m a g e % |
2347 | | % % |
2348 | | % % |
2349 | | % % |
2350 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
2351 | | % |
2352 | | % KmeansImage() applies k-means color reduction to an image. This is a |
2353 | | % colorspace clustering or segmentation technique. |
2354 | | % |
2355 | | % The format of the KmeansImage method is: |
2356 | | % |
2357 | | % MagickBooleanType KmeansImage(Image *image,const size_t number_colors, |
2358 | | % const size_t max_iterations,const double tolerance, |
2359 | | % ExceptionInfo *exception) |
2360 | | % |
2361 | | % A description of each parameter follows: |
2362 | | % |
2363 | | % o image: the image. |
2364 | | % |
2365 | | % o number_colors: number of colors to use as seeds. |
2366 | | % |
2367 | | % o max_iterations: maximum number of iterations while converging. |
2368 | | % |
2369 | | % o tolerance: the maximum tolerance. |
2370 | | % |
2371 | | % o exception: return any errors or warnings in this structure. |
2372 | | % |
2373 | | */ |
2374 | | |
2375 | | typedef struct _KmeansInfo |
2376 | | { |
2377 | | double |
2378 | | red, |
2379 | | green, |
2380 | | blue, |
2381 | | alpha, |
2382 | | black, |
2383 | | count, |
2384 | | distortion; |
2385 | | } KmeansInfo; |
2386 | | |
2387 | | static KmeansInfo **DestroyKmeansTLS(KmeansInfo **kmeans_info) |
2388 | 0 | { |
2389 | 0 | ssize_t |
2390 | 0 | i; |
2391 | |
|
2392 | 0 | assert(kmeans_info != (KmeansInfo **) NULL); |
2393 | 0 | for (i=0; i < (ssize_t) GetMagickResourceLimit(ThreadResource); i++) |
2394 | 0 | if (kmeans_info[i] != (KmeansInfo *) NULL) |
2395 | 0 | kmeans_info[i]=(KmeansInfo *) RelinquishMagickMemory(kmeans_info[i]); |
2396 | 0 | kmeans_info=(KmeansInfo **) RelinquishMagickMemory(kmeans_info); |
2397 | 0 | return(kmeans_info); |
2398 | 0 | } |
2399 | | |
2400 | | static int DominantColorCompare(const void *x,const void *y) |
2401 | 0 | { |
2402 | 0 | PixelInfo |
2403 | 0 | *pixel_1, |
2404 | 0 | *pixel_2; |
2405 | |
|
2406 | 0 | pixel_1=(PixelInfo *) x; |
2407 | 0 | pixel_2=(PixelInfo *) y; |
2408 | 0 | return((int) pixel_2->count-(int) pixel_1->count); |
2409 | 0 | } |
2410 | | |
2411 | | static KmeansInfo **AcquireKmeansTLS(const size_t number_colors) |
2412 | 0 | { |
2413 | 0 | KmeansInfo |
2414 | 0 | **kmeans_info; |
2415 | |
|
2416 | 0 | size_t |
2417 | 0 | number_threads; |
2418 | |
|
2419 | 0 | ssize_t |
2420 | 0 | i; |
2421 | |
|
2422 | 0 | number_threads=(size_t) GetMagickResourceLimit(ThreadResource); |
2423 | 0 | kmeans_info=(KmeansInfo **) AcquireQuantumMemory(number_threads, |
2424 | 0 | sizeof(*kmeans_info)); |
2425 | 0 | if (kmeans_info == (KmeansInfo **) NULL) |
2426 | 0 | return((KmeansInfo **) NULL); |
2427 | 0 | (void) memset(kmeans_info,0,number_threads*sizeof(*kmeans_info)); |
2428 | 0 | for (i=0; i < (ssize_t) number_threads; i++) |
2429 | 0 | { |
2430 | 0 | kmeans_info[i]=(KmeansInfo *) AcquireQuantumMemory(number_colors, |
2431 | 0 | sizeof(**kmeans_info)); |
2432 | 0 | if (kmeans_info[i] == (KmeansInfo *) NULL) |
2433 | 0 | return(DestroyKmeansTLS(kmeans_info)); |
2434 | 0 | } |
2435 | 0 | return(kmeans_info); |
2436 | 0 | } |
2437 | | |
2438 | | static inline double KmeansMetric(const Image *magick_restrict image, |
2439 | | const Quantum *magick_restrict p,const PixelInfo *magick_restrict q) |
2440 | 0 | { |
2441 | 0 | double |
2442 | 0 | gamma, |
2443 | 0 | metric, |
2444 | 0 | pixel; |
2445 | |
|
2446 | 0 | gamma=1.0; |
2447 | 0 | metric=0.0; |
2448 | 0 | if ((image->alpha_trait != UndefinedPixelTrait) || |
2449 | 0 | (q->alpha_trait != UndefinedPixelTrait)) |
2450 | 0 | { |
2451 | 0 | pixel=(double) GetPixelAlpha(image,p)-(q->alpha_trait != |
2452 | 0 | UndefinedPixelTrait ? q->alpha : (double) OpaqueAlpha); |
2453 | 0 | metric+=pixel*pixel; |
2454 | 0 | if (image->alpha_trait != UndefinedPixelTrait) |
2455 | 0 | gamma*=QuantumScale*(double) GetPixelAlpha(image,p); |
2456 | 0 | if (q->alpha_trait != UndefinedPixelTrait) |
2457 | 0 | gamma*=QuantumScale*q->alpha; |
2458 | 0 | } |
2459 | 0 | if (image->colorspace == CMYKColorspace) |
2460 | 0 | { |
2461 | 0 | pixel=QuantumScale*((double) GetPixelBlack(image,p)-q->black); |
2462 | 0 | metric+=gamma*pixel*pixel; |
2463 | 0 | gamma*=QuantumScale*((double) QuantumRange-(double) |
2464 | 0 | GetPixelBlack(image,p)); |
2465 | 0 | gamma*=QuantumScale*((double) QuantumRange-q->black); |
2466 | 0 | } |
2467 | 0 | metric*=3.0; |
2468 | 0 | pixel=QuantumScale*((double) GetPixelRed(image,p)-q->red); |
2469 | 0 | if (IsHueCompatibleColorspace(image->colorspace) != MagickFalse) |
2470 | 0 | { |
2471 | 0 | if (fabs((double) pixel) > 0.5) |
2472 | 0 | pixel-=0.5; |
2473 | 0 | pixel*=2.0; |
2474 | 0 | } |
2475 | 0 | metric+=gamma*pixel*pixel; |
2476 | 0 | pixel=QuantumScale*((double) GetPixelGreen(image,p)-q->green); |
2477 | 0 | metric+=gamma*pixel*pixel; |
2478 | 0 | pixel=QuantumScale*((double) GetPixelBlue(image,p)-q->blue); |
2479 | 0 | metric+=gamma*pixel*pixel; |
2480 | 0 | return(metric); |
2481 | 0 | } |
2482 | | |
2483 | | MagickExport MagickBooleanType KmeansImage(Image *image, |
2484 | | const size_t number_colors,const size_t max_iterations,const double tolerance, |
2485 | | ExceptionInfo *exception) |
2486 | 0 | { |
2487 | 0 | #define KmeansImageTag "Kmeans/Image" |
2488 | 0 | #define RandomColorComponent(info) \ |
2489 | 0 | ((double) QuantumRange*GetPseudoRandomValue(info)) |
2490 | |
|
2491 | 0 | CacheView |
2492 | 0 | *image_view; |
2493 | |
|
2494 | 0 | char |
2495 | 0 | tuple[MagickPathExtent]; |
2496 | |
|
2497 | 0 | const char |
2498 | 0 | *colors; |
2499 | |
|
2500 | 0 | double |
2501 | 0 | previous_tolerance; |
2502 | |
|
2503 | 0 | Image |
2504 | 0 | *dominant_image; |
2505 | |
|
2506 | 0 | KmeansInfo |
2507 | 0 | **kmeans_pixels; |
2508 | |
|
2509 | 0 | MagickBooleanType |
2510 | 0 | verbose, |
2511 | 0 | status; |
2512 | |
|
2513 | 0 | size_t |
2514 | 0 | number_threads; |
2515 | |
|
2516 | 0 | ssize_t |
2517 | 0 | n; |
2518 | |
|
2519 | 0 | assert(image != (Image *) NULL); |
2520 | 0 | assert(image->signature == MagickCoreSignature); |
2521 | 0 | assert(exception != (ExceptionInfo *) NULL); |
2522 | 0 | assert(exception->signature == MagickCoreSignature); |
2523 | 0 | if (IsEventLogging() != MagickFalse) |
2524 | 0 | (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); |
2525 | 0 | if (max_iterations == 0) |
2526 | 0 | return(MagickFalse); |
2527 | 0 | colors=GetImageArtifact(image,"kmeans:seed-colors"); |
2528 | 0 | if (colors == (const char *) NULL) |
2529 | 0 | { |
2530 | 0 | QCubeInfo |
2531 | 0 | *cube_info; |
2532 | |
|
2533 | 0 | QuantizeInfo |
2534 | 0 | *quantize_info; |
2535 | |
|
2536 | 0 | size_t |
2537 | 0 | depth; |
2538 | | |
2539 | | /* |
2540 | | Seed clusters from color quantization. |
2541 | | */ |
2542 | 0 | quantize_info=AcquireQuantizeInfo((ImageInfo *) NULL); |
2543 | 0 | quantize_info->colorspace=image->colorspace; |
2544 | 0 | quantize_info->number_colors=number_colors; |
2545 | 0 | quantize_info->dither_method=NoDitherMethod; |
2546 | 0 | n=(ssize_t) number_colors; |
2547 | 0 | for (depth=1; n != 0; depth++) |
2548 | 0 | n>>=2; |
2549 | 0 | cube_info=GetQCubeInfo(quantize_info,depth,number_colors); |
2550 | 0 | if (cube_info == (QCubeInfo *) NULL) |
2551 | 0 | { |
2552 | 0 | quantize_info=DestroyQuantizeInfo(quantize_info); |
2553 | 0 | ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", |
2554 | 0 | image->filename); |
2555 | 0 | } |
2556 | 0 | status=ClassifyImageColors(cube_info,image,exception); |
2557 | 0 | if (status != MagickFalse) |
2558 | 0 | { |
2559 | 0 | if (cube_info->colors > cube_info->maximum_colors) |
2560 | 0 | ReduceImageColors(image,cube_info); |
2561 | 0 | status=SetImageColormap(image,cube_info,exception); |
2562 | 0 | } |
2563 | 0 | DestroyQCubeInfo(cube_info); |
2564 | 0 | quantize_info=DestroyQuantizeInfo(quantize_info); |
2565 | 0 | if (status == MagickFalse) |
2566 | 0 | return(status); |
2567 | 0 | } |
2568 | 0 | else |
2569 | 0 | { |
2570 | 0 | char |
2571 | 0 | color[MagickPathExtent]; |
2572 | |
|
2573 | 0 | const char |
2574 | 0 | *p; |
2575 | | |
2576 | | /* |
2577 | | Seed clusters from color list (e.g. red;green;blue). |
2578 | | */ |
2579 | 0 | status=AcquireImageColormap(image,number_colors,exception); |
2580 | 0 | if (status == MagickFalse) |
2581 | 0 | return(status); |
2582 | 0 | for (n=0, p=colors; n < (ssize_t) image->colors; n++) |
2583 | 0 | { |
2584 | 0 | const char |
2585 | 0 | *q; |
2586 | |
|
2587 | 0 | for (q=p; *q != '\0'; q++) |
2588 | 0 | if (*q == ';') |
2589 | 0 | break; |
2590 | 0 | (void) CopyMagickString(color,p,(size_t) MagickMin(q-p+1, |
2591 | 0 | MagickPathExtent)); |
2592 | 0 | (void) QueryColorCompliance(color,AllCompliance,image->colormap+n, |
2593 | 0 | exception); |
2594 | 0 | if (*q == '\0') |
2595 | 0 | { |
2596 | 0 | n++; |
2597 | 0 | break; |
2598 | 0 | } |
2599 | 0 | p=q+1; |
2600 | 0 | } |
2601 | 0 | if (n < (ssize_t) image->colors) |
2602 | 0 | { |
2603 | 0 | RandomInfo |
2604 | 0 | *random_info; |
2605 | | |
2606 | | /* |
2607 | | Seed clusters from random values. |
2608 | | */ |
2609 | 0 | random_info=AcquireRandomInfo(); |
2610 | 0 | for ( ; n < (ssize_t) image->colors; n++) |
2611 | 0 | { |
2612 | 0 | (void) QueryColorCompliance("#000",AllCompliance,image->colormap+n, |
2613 | 0 | exception); |
2614 | 0 | image->colormap[n].red=RandomColorComponent(random_info); |
2615 | 0 | image->colormap[n].green=RandomColorComponent(random_info); |
2616 | 0 | image->colormap[n].blue=RandomColorComponent(random_info); |
2617 | 0 | if (image->alpha_trait != UndefinedPixelTrait) |
2618 | 0 | image->colormap[n].alpha=RandomColorComponent(random_info); |
2619 | 0 | if (image->colorspace == CMYKColorspace) |
2620 | 0 | image->colormap[n].black=RandomColorComponent(random_info); |
2621 | 0 | } |
2622 | 0 | random_info=DestroyRandomInfo(random_info); |
2623 | 0 | } |
2624 | 0 | } |
2625 | | /* |
2626 | | Iterative refinement. |
2627 | | */ |
2628 | 0 | kmeans_pixels=AcquireKmeansTLS(number_colors); |
2629 | 0 | if (kmeans_pixels == (KmeansInfo **) NULL) |
2630 | 0 | ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", |
2631 | 0 | image->filename); |
2632 | 0 | previous_tolerance=0.0; |
2633 | 0 | verbose=IsStringTrue(GetImageArtifact(image,"verbose")); |
2634 | 0 | number_threads=(size_t) GetMagickResourceLimit(ThreadResource); |
2635 | 0 | image_view=AcquireAuthenticCacheView(image,exception); |
2636 | 0 | for (n=0; n < (ssize_t) max_iterations; n++) |
2637 | 0 | { |
2638 | 0 | double |
2639 | 0 | distortion; |
2640 | |
|
2641 | 0 | ssize_t |
2642 | 0 | j, |
2643 | 0 | y; |
2644 | |
|
2645 | 0 | for (j=0; j < (ssize_t) number_threads; j++) |
2646 | 0 | (void) memset(kmeans_pixels[j],0,image->colors*sizeof(*kmeans_pixels[j])); |
2647 | | #if defined(MAGICKCORE_OPENMP_SUPPORT) |
2648 | | #pragma omp parallel for schedule(dynamic) shared(status) \ |
2649 | | magick_number_threads(image,image,image->rows,1) |
2650 | | #endif |
2651 | 0 | for (y=0; y < (ssize_t) image->rows; y++) |
2652 | 0 | { |
2653 | 0 | const int |
2654 | 0 | id = GetOpenMPThreadId(); |
2655 | |
|
2656 | 0 | Quantum |
2657 | 0 | *magick_restrict q; |
2658 | |
|
2659 | 0 | ssize_t |
2660 | 0 | x; |
2661 | |
|
2662 | 0 | if (status == MagickFalse) |
2663 | 0 | continue; |
2664 | 0 | q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception); |
2665 | 0 | if (q == (Quantum *) NULL) |
2666 | 0 | { |
2667 | 0 | status=MagickFalse; |
2668 | 0 | continue; |
2669 | 0 | } |
2670 | 0 | for (x=0; x < (ssize_t) image->columns; x++) |
2671 | 0 | { |
2672 | 0 | double |
2673 | 0 | min_distance; |
2674 | |
|
2675 | 0 | ssize_t |
2676 | 0 | i, |
2677 | 0 | k; |
2678 | | |
2679 | | /* |
2680 | | Assign each pixel whose mean has the least squared color distance. |
2681 | | */ |
2682 | 0 | k=0; |
2683 | 0 | min_distance=KmeansMetric(image,q,image->colormap+0); |
2684 | 0 | for (i=1; i < (ssize_t) image->colors; i++) |
2685 | 0 | { |
2686 | 0 | double |
2687 | 0 | distance; |
2688 | |
|
2689 | 0 | if (min_distance <= MagickEpsilon) |
2690 | 0 | break; |
2691 | 0 | distance=KmeansMetric(image,q,image->colormap+i); |
2692 | 0 | if (distance < min_distance) |
2693 | 0 | { |
2694 | 0 | min_distance=distance; |
2695 | 0 | k=i; |
2696 | 0 | } |
2697 | 0 | } |
2698 | 0 | kmeans_pixels[id][k].red+=QuantumScale*(double) GetPixelRed(image,q); |
2699 | 0 | kmeans_pixels[id][k].green+=QuantumScale*(double) |
2700 | 0 | GetPixelGreen(image,q); |
2701 | 0 | kmeans_pixels[id][k].blue+=QuantumScale*(double) GetPixelBlue(image,q); |
2702 | 0 | if (image->alpha_trait != UndefinedPixelTrait) |
2703 | 0 | kmeans_pixels[id][k].alpha+=QuantumScale*(double) |
2704 | 0 | GetPixelAlpha(image,q); |
2705 | 0 | if (image->colorspace == CMYKColorspace) |
2706 | 0 | kmeans_pixels[id][k].black+=QuantumScale*(double) |
2707 | 0 | GetPixelBlack(image,q); |
2708 | 0 | kmeans_pixels[id][k].count++; |
2709 | 0 | kmeans_pixels[id][k].distortion+=min_distance; |
2710 | 0 | SetPixelIndex(image,(Quantum) k,q); |
2711 | 0 | q+=(ptrdiff_t) GetPixelChannels(image); |
2712 | 0 | } |
2713 | 0 | if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse) |
2714 | 0 | status=MagickFalse; |
2715 | 0 | } |
2716 | 0 | if (status == MagickFalse) |
2717 | 0 | break; |
2718 | | /* |
2719 | | Reduce sums to [0] entry. |
2720 | | */ |
2721 | 0 | for (j=1; j < (ssize_t) number_threads; j++) |
2722 | 0 | { |
2723 | 0 | ssize_t |
2724 | 0 | k; |
2725 | |
|
2726 | 0 | for (k=0; k < (ssize_t) image->colors; k++) |
2727 | 0 | { |
2728 | 0 | kmeans_pixels[0][k].red+=kmeans_pixels[j][k].red; |
2729 | 0 | kmeans_pixels[0][k].green+=kmeans_pixels[j][k].green; |
2730 | 0 | kmeans_pixels[0][k].blue+=kmeans_pixels[j][k].blue; |
2731 | 0 | if (image->alpha_trait != UndefinedPixelTrait) |
2732 | 0 | kmeans_pixels[0][k].alpha+=kmeans_pixels[j][k].alpha; |
2733 | 0 | if (image->colorspace == CMYKColorspace) |
2734 | 0 | kmeans_pixels[0][k].black+=kmeans_pixels[j][k].black; |
2735 | 0 | kmeans_pixels[0][k].count+=kmeans_pixels[j][k].count; |
2736 | 0 | kmeans_pixels[0][k].distortion+=kmeans_pixels[j][k].distortion; |
2737 | 0 | } |
2738 | 0 | } |
2739 | | /* |
2740 | | Calculate the new means (centroids) of the pixels in the new clusters. |
2741 | | */ |
2742 | 0 | distortion=0.0; |
2743 | 0 | for (j=0; j < (ssize_t) image->colors; j++) |
2744 | 0 | { |
2745 | 0 | double |
2746 | 0 | gamma; |
2747 | |
|
2748 | 0 | gamma=MagickSafeReciprocal((double) kmeans_pixels[0][j].count); |
2749 | 0 | image->colormap[j].red=gamma*(double) QuantumRange* |
2750 | 0 | kmeans_pixels[0][j].red; |
2751 | 0 | image->colormap[j].green=gamma*(double) QuantumRange* |
2752 | 0 | kmeans_pixels[0][j].green; |
2753 | 0 | image->colormap[j].blue=gamma*(double) QuantumRange* |
2754 | 0 | kmeans_pixels[0][j].blue; |
2755 | 0 | if (image->alpha_trait != UndefinedPixelTrait) |
2756 | 0 | image->colormap[j].alpha=gamma*(double) QuantumRange* |
2757 | 0 | kmeans_pixels[0][j].alpha; |
2758 | 0 | if (image->colorspace == CMYKColorspace) |
2759 | 0 | image->colormap[j].black=gamma*(double) QuantumRange* |
2760 | 0 | kmeans_pixels[0][j].black; |
2761 | 0 | image->colormap[j].count=(MagickSizeType) kmeans_pixels[0][j].count; |
2762 | 0 | distortion+=kmeans_pixels[0][j].distortion; |
2763 | 0 | } |
2764 | 0 | if (image->debug != MagickFalse) |
2765 | 0 | (void) LogMagickEvent(ImageEvent,GetMagickModule(), |
2766 | 0 | "distortion[%.20g]: %*g %*g\n",(double) n,GetMagickPrecision(), |
2767 | 0 | distortion,GetMagickPrecision(),fabs(distortion-previous_tolerance)); |
2768 | 0 | if (fabs(distortion-previous_tolerance) <= tolerance) |
2769 | 0 | break; |
2770 | 0 | previous_tolerance=distortion; |
2771 | 0 | if (image->progress_monitor != (MagickProgressMonitor) NULL) |
2772 | 0 | { |
2773 | 0 | MagickBooleanType |
2774 | 0 | proceed; |
2775 | |
|
2776 | 0 | proceed=SetImageProgress(image,KmeansImageTag,(MagickOffsetType) n, |
2777 | 0 | max_iterations); |
2778 | 0 | if (proceed == MagickFalse) |
2779 | 0 | status=MagickFalse; |
2780 | 0 | } |
2781 | 0 | } |
2782 | 0 | image_view=DestroyCacheView(image_view); |
2783 | 0 | if (verbose != MagickFalse) |
2784 | 0 | for (n=0; n < (ssize_t) image->colors; n++) |
2785 | 0 | { |
2786 | 0 | GetColorTuple(image->colormap+n,MagickTrue,tuple); |
2787 | 0 | (void) FormatLocaleFile(stderr,"%s %.20g\n",tuple,(double) |
2788 | 0 | image->colormap[n].count); |
2789 | 0 | } |
2790 | 0 | dominant_image=CloneImage(image,0,0,MagickTrue,exception); |
2791 | 0 | if (dominant_image != (Image *) NULL) |
2792 | 0 | { |
2793 | | /* |
2794 | | Note dominant color. |
2795 | | */ |
2796 | 0 | qsort((void *) dominant_image->colormap,dominant_image->colors, |
2797 | 0 | sizeof(*dominant_image->colormap),DominantColorCompare); |
2798 | 0 | GetColorTuple(dominant_image->colormap,MagickTrue,tuple); |
2799 | 0 | dominant_image=DestroyImage(dominant_image); |
2800 | 0 | (void) SetImageProperty(image,"dominant-color",tuple,exception); |
2801 | 0 | } |
2802 | 0 | kmeans_pixels=DestroyKmeansTLS(kmeans_pixels); |
2803 | 0 | if (image->progress_monitor != (MagickProgressMonitor) NULL) |
2804 | 0 | (void) SetImageProgress(image,KmeansImageTag,(MagickOffsetType) |
2805 | 0 | max_iterations-1,max_iterations); |
2806 | 0 | if (status == MagickFalse) |
2807 | 0 | return(status); |
2808 | 0 | return(SyncImage(image,exception)); |
2809 | 0 | } |
2810 | | |
2811 | | /* |
2812 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
2813 | | % % |
2814 | | % % |
2815 | | % % |
2816 | | % P o s t e r i z e I m a g e % |
2817 | | % % |
2818 | | % % |
2819 | | % % |
2820 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
2821 | | % |
2822 | | % PosterizeImage() reduces the image to a limited number of colors for a |
2823 | | % "poster" effect. |
2824 | | % |
2825 | | % The format of the PosterizeImage method is: |
2826 | | % |
2827 | | % MagickBooleanType PosterizeImage(Image *image,const size_t levels, |
2828 | | % const DitherMethod dither_method,ExceptionInfo *exception) |
2829 | | % |
2830 | | % A description of each parameter follows: |
2831 | | % |
2832 | | % o image: Specifies a pointer to an Image structure. |
2833 | | % |
2834 | | % o levels: Number of color levels allowed in each channel. Very low values |
2835 | | % (2, 3, or 4) have the most visible effect. |
2836 | | % |
2837 | | % o dither_method: choose from UndefinedDitherMethod, NoDitherMethod, |
2838 | | % RiemersmaDitherMethod, FloydSteinbergDitherMethod. |
2839 | | % |
2840 | | % o exception: return any errors or warnings in this structure. |
2841 | | % |
2842 | | */ |
2843 | | |
2844 | | static inline double MagickRound(double x) |
2845 | 0 | { |
2846 | | /* |
2847 | | Round the fraction to nearest integer. |
2848 | | */ |
2849 | 0 | if ((x-floor(x)) < (ceil(x)-x)) |
2850 | 0 | return(floor(x)); |
2851 | 0 | return(ceil(x)); |
2852 | 0 | } |
2853 | | |
2854 | | static inline Quantum PosterizePixel(const Quantum pixel,const size_t levels) |
2855 | 0 | { |
2856 | 0 | double posterize_pixel = QuantumRange*MagickRound(QuantumScale*(double) pixel* |
2857 | 0 | ((double) levels-1.0))/MagickMax((double) levels-1.0,1.0); |
2858 | 0 | return(ClampToQuantum((MagickRealType) posterize_pixel)); |
2859 | 0 | } |
2860 | | |
2861 | | MagickExport MagickBooleanType PosterizeImage(Image *image,const size_t levels, |
2862 | | const DitherMethod dither_method,ExceptionInfo *exception) |
2863 | 0 | { |
2864 | 0 | #define PosterizeImageTag "Posterize/Image" |
2865 | |
|
2866 | 0 | CacheView |
2867 | 0 | *image_view; |
2868 | |
|
2869 | 0 | MagickBooleanType |
2870 | 0 | status = MagickTrue; |
2871 | |
|
2872 | 0 | MagickOffsetType |
2873 | 0 | progress; |
2874 | |
|
2875 | 0 | ssize_t |
2876 | 0 | y; |
2877 | |
|
2878 | 0 | assert(image != (Image *) NULL); |
2879 | 0 | assert(image->signature == MagickCoreSignature); |
2880 | 0 | assert(exception != (ExceptionInfo *) NULL); |
2881 | 0 | assert(exception->signature == MagickCoreSignature); |
2882 | 0 | if (IsEventLogging() != MagickFalse) |
2883 | 0 | (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); |
2884 | 0 | if ((dither_method != NoDitherMethod) && (levels > 1) && (levels < 17)) |
2885 | 0 | for (y=0; y < 1; y++) |
2886 | 0 | { |
2887 | 0 | Image |
2888 | 0 | *map_image; |
2889 | |
|
2890 | 0 | size_t |
2891 | 0 | channels = 0, |
2892 | 0 | number_columns; |
2893 | |
|
2894 | 0 | ssize_t |
2895 | 0 | i; |
2896 | |
|
2897 | 0 | for (i=0; i < (ssize_t) GetPixelChannels(image); i++) |
2898 | 0 | { |
2899 | 0 | PixelChannel channel = GetPixelChannelChannel(image,i); |
2900 | 0 | PixelTrait traits = GetPixelChannelTraits(image,channel); |
2901 | 0 | if ((traits & UpdatePixelTrait) != 0) |
2902 | 0 | channels++; |
2903 | 0 | } |
2904 | 0 | number_columns=(size_t) pow((double) levels,(double) channels); |
2905 | 0 | map_image=CloneImage(image,number_columns,1,MagickTrue,exception); |
2906 | 0 | if (map_image == (Image *) NULL) |
2907 | 0 | { |
2908 | 0 | status=MagickFalse; |
2909 | 0 | break; |
2910 | 0 | } |
2911 | 0 | if (SetImageStorageClass(map_image,DirectClass,exception) == MagickFalse) |
2912 | 0 | { |
2913 | 0 | status=MagickFalse; |
2914 | 0 | break; |
2915 | 0 | } |
2916 | 0 | { |
2917 | 0 | CacheView |
2918 | 0 | *map_image_view; |
2919 | |
|
2920 | 0 | MagickRealType |
2921 | 0 | scale = (MagickRealType) QuantumRange/(levels-1.0); |
2922 | |
|
2923 | 0 | Quantum |
2924 | 0 | *magick_restrict q; |
2925 | |
|
2926 | 0 | ssize_t |
2927 | 0 | c, |
2928 | 0 | x; |
2929 | | |
2930 | | /* |
2931 | | Populate the map image. |
2932 | | */ |
2933 | 0 | map_image_view=AcquireAuthenticCacheView (map_image,exception); |
2934 | 0 | q=GetCacheViewAuthenticPixels(map_image_view,0,0,number_columns,1, |
2935 | 0 | exception); |
2936 | 0 | if (q == (const Quantum *) NULL) |
2937 | 0 | { |
2938 | 0 | map_image_view=DestroyCacheView(map_image_view); |
2939 | 0 | status=MagickFalse; |
2940 | 0 | break; |
2941 | 0 | } |
2942 | 0 | for (x=0; x < (ssize_t) number_columns; x++) |
2943 | 0 | { |
2944 | 0 | size_t remainder = (size_t) x; |
2945 | 0 | for (c=0; c < (ssize_t) GetPixelChannels(image); c++) |
2946 | 0 | { |
2947 | 0 | PixelChannel channel = GetPixelChannelChannel(image,c); |
2948 | 0 | PixelTrait traits = GetPixelChannelTraits(image,channel); |
2949 | 0 | if ((traits & UpdatePixelTrait) != 0) |
2950 | 0 | { |
2951 | 0 | size_t value = remainder % levels; |
2952 | 0 | SetPixelChannel(map_image,channel,(const Quantum) (scale*value),q); |
2953 | 0 | remainder=(remainder-value)/levels; |
2954 | 0 | } |
2955 | 0 | } |
2956 | 0 | q+=(ptrdiff_t) GetPixelChannels(map_image); |
2957 | 0 | } |
2958 | 0 | if (SyncCacheViewAuthenticPixels(map_image_view,exception) == MagickFalse) |
2959 | 0 | { |
2960 | 0 | map_image_view=DestroyCacheView(map_image_view); |
2961 | 0 | status=MagickFalse; |
2962 | 0 | break; |
2963 | 0 | } |
2964 | 0 | map_image_view=DestroyCacheView(map_image_view); |
2965 | 0 | } |
2966 | 0 | if (status != MagickFalse) |
2967 | 0 | { |
2968 | | /* |
2969 | | Remap to the map image. |
2970 | | */ |
2971 | 0 | QuantizeInfo *quantize_info = AcquireQuantizeInfo((ImageInfo *) NULL); |
2972 | 0 | quantize_info->dither_method=dither_method; |
2973 | 0 | (void) RemapImage(quantize_info,image,map_image,exception); |
2974 | 0 | quantize_info=DestroyQuantizeInfo(quantize_info); |
2975 | 0 | } |
2976 | 0 | map_image=DestroyImage(map_image); |
2977 | 0 | } |
2978 | 0 | else |
2979 | 0 | { |
2980 | | /* |
2981 | | No dither or too many levels. |
2982 | | */ |
2983 | 0 | if (image->storage_class == PseudoClass) |
2984 | 0 | { |
2985 | 0 | ssize_t |
2986 | 0 | i; |
2987 | |
|
2988 | | #if defined(MAGICKCORE_OPENMP_SUPPORT) |
2989 | | #pragma omp parallel for schedule(static) shared(progress,status) \ |
2990 | | magick_number_threads(image,image,image->colors,1) |
2991 | | #endif |
2992 | 0 | for (i=0; i < (ssize_t) image->colors; i++) |
2993 | 0 | { |
2994 | | /* |
2995 | | Posterize colormap. |
2996 | | */ |
2997 | 0 | if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0) |
2998 | 0 | image->colormap[i].red=(MagickRealType) |
2999 | 0 | PosterizePixel((const Quantum) image->colormap[i].red,levels); |
3000 | 0 | if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0) |
3001 | 0 | image->colormap[i].green=(MagickRealType) |
3002 | 0 | PosterizePixel((const Quantum) image->colormap[i].green,levels); |
3003 | 0 | if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0) |
3004 | 0 | image->colormap[i].blue=(MagickRealType) |
3005 | 0 | PosterizePixel((const Quantum) image->colormap[i].blue,levels); |
3006 | 0 | if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) |
3007 | 0 | image->colormap[i].alpha=(MagickRealType) |
3008 | 0 | PosterizePixel((const Quantum) image->colormap[i].alpha,levels); |
3009 | 0 | } |
3010 | 0 | } |
3011 | | /* |
3012 | | Posterize image. |
3013 | | */ |
3014 | 0 | progress=0; |
3015 | 0 | image_view=AcquireAuthenticCacheView(image,exception); |
3016 | | #if defined(MAGICKCORE_OPENMP_SUPPORT) |
3017 | | #pragma omp parallel for schedule(static) shared(progress,status) \ |
3018 | | magick_number_threads(image,image,image->rows,1) |
3019 | | #endif |
3020 | 0 | for (y=0; y < (ssize_t) image->rows; y++) |
3021 | 0 | { |
3022 | 0 | Quantum |
3023 | 0 | *magick_restrict q; |
3024 | | |
3025 | 0 | ssize_t |
3026 | 0 | x; |
3027 | | |
3028 | 0 | if (status == MagickFalse) |
3029 | 0 | continue; |
3030 | 0 | q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1, |
3031 | 0 | exception); |
3032 | 0 | if (q == (Quantum *) NULL) |
3033 | 0 | { |
3034 | 0 | status=MagickFalse; |
3035 | 0 | continue; |
3036 | 0 | } |
3037 | 0 | for (x=0; x < (ssize_t) image->columns; x++) |
3038 | 0 | { |
3039 | 0 | ssize_t |
3040 | 0 | i; |
3041 | |
|
3042 | 0 | for (i=0; i < (ssize_t) GetPixelChannels(image); i++) |
3043 | 0 | { |
3044 | 0 | PixelChannel channel = GetPixelChannelChannel(image,i); |
3045 | 0 | PixelTrait traits = GetPixelChannelTraits(image,channel); |
3046 | 0 | if ((traits & UpdatePixelTrait) == 0) |
3047 | 0 | continue; |
3048 | 0 | SetPixelChannel(image,channel,PosterizePixel(q[i],levels),q); |
3049 | 0 | } |
3050 | 0 | q+=(ptrdiff_t) GetPixelChannels(image); |
3051 | 0 | } |
3052 | 0 | if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse) |
3053 | 0 | status=MagickFalse; |
3054 | 0 | if (image->progress_monitor != (MagickProgressMonitor) NULL) |
3055 | 0 | { |
3056 | 0 | MagickBooleanType |
3057 | 0 | proceed; |
3058 | |
|
3059 | | #if defined(MAGICKCORE_OPENMP_SUPPORT) |
3060 | | #pragma omp atomic |
3061 | | #endif |
3062 | 0 | progress++; |
3063 | 0 | proceed=SetImageProgress(image,PosterizeImageTag,progress, |
3064 | 0 | image->rows); |
3065 | 0 | if (proceed == MagickFalse) |
3066 | 0 | status=MagickFalse; |
3067 | 0 | } |
3068 | 0 | } |
3069 | 0 | image_view=DestroyCacheView(image_view); |
3070 | 0 | { |
3071 | 0 | QuantizeInfo *quantize_info=AcquireQuantizeInfo((ImageInfo *) NULL); |
3072 | 0 | quantize_info->number_colors=(size_t) MagickMin(levels*levels*levels, |
3073 | 0 | MaxColormapSize); |
3074 | 0 | quantize_info->dither_method=dither_method; |
3075 | 0 | status=QuantizeImage(quantize_info,image,exception); |
3076 | 0 | quantize_info=DestroyQuantizeInfo(quantize_info); |
3077 | 0 | } |
3078 | 0 | } |
3079 | 0 | return(status); |
3080 | 0 | } |
3081 | | |
3082 | | /* |
3083 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
3084 | | % % |
3085 | | % % |
3086 | | % % |
3087 | | + P r u n e C h i l d % |
3088 | | % % |
3089 | | % % |
3090 | | % % |
3091 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
3092 | | % |
3093 | | % PruneChild() deletes the given node and merges its statistics into its |
3094 | | % parent. |
3095 | | % |
3096 | | % The format of the PruneSubtree method is: |
3097 | | % |
3098 | | % PruneChild(QCubeInfo *cube_info,const QNodeInfo *node_info) |
3099 | | % |
3100 | | % A description of each parameter follows. |
3101 | | % |
3102 | | % o cube_info: A pointer to the Cube structure. |
3103 | | % |
3104 | | % o node_info: pointer to node in color cube tree that is to be pruned. |
3105 | | % |
3106 | | */ |
3107 | | static void PruneChild(QCubeInfo *cube_info,const QNodeInfo *node_info) |
3108 | 424k | { |
3109 | 424k | QNodeInfo |
3110 | 424k | *parent; |
3111 | | |
3112 | 424k | size_t |
3113 | 424k | number_children; |
3114 | | |
3115 | 424k | ssize_t |
3116 | 424k | i; |
3117 | | |
3118 | | /* |
3119 | | Traverse any children. |
3120 | | */ |
3121 | 424k | number_children=cube_info->associate_alpha == MagickFalse ? 8UL : 16UL; |
3122 | 3.88M | for (i=0; i < (ssize_t) number_children; i++) |
3123 | 3.46M | if (node_info->child[i] != (QNodeInfo *) NULL) |
3124 | 4.22k | PruneChild(cube_info,node_info->child[i]); |
3125 | 424k | if (cube_info->nodes > cube_info->maximum_colors) |
3126 | 416k | { |
3127 | | /* |
3128 | | Merge color statistics into parent. |
3129 | | */ |
3130 | 416k | parent=node_info->parent; |
3131 | 416k | parent->number_unique+=node_info->number_unique; |
3132 | 416k | parent->total_color.red+=node_info->total_color.red; |
3133 | 416k | parent->total_color.green+=node_info->total_color.green; |
3134 | 416k | parent->total_color.blue+=node_info->total_color.blue; |
3135 | 416k | parent->total_color.alpha+=node_info->total_color.alpha; |
3136 | 416k | parent->child[node_info->id]=(QNodeInfo *) NULL; |
3137 | 416k | cube_info->nodes--; |
3138 | 416k | } |
3139 | 424k | } |
3140 | | |
3141 | | /* |
3142 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
3143 | | % % |
3144 | | % % |
3145 | | % % |
3146 | | + P r u n e L e v e l % |
3147 | | % % |
3148 | | % % |
3149 | | % % |
3150 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
3151 | | % |
3152 | | % PruneLevel() deletes all nodes at the bottom level of the color tree merging |
3153 | | % their color statistics into their parent node. |
3154 | | % |
3155 | | % The format of the PruneLevel method is: |
3156 | | % |
3157 | | % PruneLevel(QCubeInfo *cube_info,const QNodeInfo *node_info) |
3158 | | % |
3159 | | % A description of each parameter follows. |
3160 | | % |
3161 | | % o cube_info: A pointer to the Cube structure. |
3162 | | % |
3163 | | % o node_info: pointer to node in color cube tree that is to be pruned. |
3164 | | % |
3165 | | */ |
3166 | | static void PruneLevel(QCubeInfo *cube_info,const QNodeInfo *node_info) |
3167 | 0 | { |
3168 | 0 | size_t |
3169 | 0 | number_children; |
3170 | |
|
3171 | 0 | ssize_t |
3172 | 0 | i; |
3173 | | |
3174 | | /* |
3175 | | Traverse any children. |
3176 | | */ |
3177 | 0 | number_children=cube_info->associate_alpha == MagickFalse ? 8UL : 16UL; |
3178 | 0 | for (i=0; i < (ssize_t) number_children; i++) |
3179 | 0 | if (node_info->child[i] != (QNodeInfo *) NULL) |
3180 | 0 | PruneLevel(cube_info,node_info->child[i]); |
3181 | 0 | if (node_info->level == cube_info->depth) |
3182 | 0 | PruneChild(cube_info,node_info); |
3183 | 0 | } |
3184 | | |
3185 | | /* |
3186 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
3187 | | % % |
3188 | | % % |
3189 | | % % |
3190 | | + P r u n e T o C u b e D e p t h % |
3191 | | % % |
3192 | | % % |
3193 | | % % |
3194 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
3195 | | % |
3196 | | % PruneToCubeDepth() deletes any nodes at a depth greater than |
3197 | | % cube_info->depth while merging their color statistics into their parent |
3198 | | % node. |
3199 | | % |
3200 | | % The format of the PruneToCubeDepth method is: |
3201 | | % |
3202 | | % PruneToCubeDepth(QCubeInfo *cube_info,const QNodeInfo *node_info) |
3203 | | % |
3204 | | % A description of each parameter follows. |
3205 | | % |
3206 | | % o cube_info: A pointer to the Cube structure. |
3207 | | % |
3208 | | % o node_info: pointer to node in color cube tree that is to be pruned. |
3209 | | % |
3210 | | */ |
3211 | | static void PruneToCubeDepth(QCubeInfo *cube_info,const QNodeInfo *node_info) |
3212 | 304k | { |
3213 | 304k | size_t |
3214 | 304k | number_children; |
3215 | | |
3216 | 304k | ssize_t |
3217 | 304k | i; |
3218 | | |
3219 | | /* |
3220 | | Traverse any children. |
3221 | | */ |
3222 | 304k | number_children=cube_info->associate_alpha == MagickFalse ? 8UL : 16UL; |
3223 | 2.77M | for (i=0; i < (ssize_t) number_children; i++) |
3224 | 2.47M | if (node_info->child[i] != (QNodeInfo *) NULL) |
3225 | 303k | PruneToCubeDepth(cube_info,node_info->child[i]); |
3226 | 304k | if (node_info->level > cube_info->depth) |
3227 | 202k | PruneChild(cube_info,node_info); |
3228 | 304k | } |
3229 | | |
3230 | | /* |
3231 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
3232 | | % % |
3233 | | % % |
3234 | | % % |
3235 | | % Q u a n t i z e I m a g e % |
3236 | | % % |
3237 | | % % |
3238 | | % % |
3239 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
3240 | | % |
3241 | | % QuantizeImage() analyzes the colors within a reference image and chooses a |
3242 | | % fixed number of colors to represent the image. The goal of the algorithm |
3243 | | % is to minimize the color difference between the input and output image while |
3244 | | % minimizing the processing time. |
3245 | | % |
3246 | | % The format of the QuantizeImage method is: |
3247 | | % |
3248 | | % MagickBooleanType QuantizeImage(const QuantizeInfo *quantize_info, |
3249 | | % Image *image,ExceptionInfo *exception) |
3250 | | % |
3251 | | % A description of each parameter follows: |
3252 | | % |
3253 | | % o quantize_info: Specifies a pointer to an QuantizeInfo structure. |
3254 | | % |
3255 | | % o image: the image. |
3256 | | % |
3257 | | % o exception: return any errors or warnings in this structure. |
3258 | | % |
3259 | | */ |
3260 | | MagickExport MagickBooleanType QuantizeImage(const QuantizeInfo *quantize_info, |
3261 | | Image *image,ExceptionInfo *exception) |
3262 | 3.93k | { |
3263 | 3.93k | QCubeInfo |
3264 | 3.93k | *cube_info; |
3265 | | |
3266 | 3.93k | ImageType |
3267 | 3.93k | type; |
3268 | | |
3269 | 3.93k | MagickBooleanType |
3270 | 3.93k | status; |
3271 | | |
3272 | 3.93k | size_t |
3273 | 3.93k | depth, |
3274 | 3.93k | maximum_colors; |
3275 | | |
3276 | 3.93k | assert(quantize_info != (const QuantizeInfo *) NULL); |
3277 | 3.93k | assert(quantize_info->signature == MagickCoreSignature); |
3278 | 3.93k | assert(image != (Image *) NULL); |
3279 | 3.93k | assert(image->signature == MagickCoreSignature); |
3280 | 3.93k | assert(exception != (ExceptionInfo *) NULL); |
3281 | 3.93k | assert(exception->signature == MagickCoreSignature); |
3282 | 3.93k | if (IsEventLogging() != MagickFalse) |
3283 | 0 | (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); |
3284 | 3.93k | maximum_colors=quantize_info->number_colors; |
3285 | 3.93k | if (maximum_colors == 0) |
3286 | 0 | maximum_colors=MaxColormapSize; |
3287 | 3.93k | if (maximum_colors > MaxColormapSize) |
3288 | 0 | maximum_colors=MaxColormapSize; |
3289 | 3.93k | type=IdentifyImageGray(image,exception); |
3290 | 3.93k | if (IsGrayImageType(type) != MagickFalse) |
3291 | 3.02k | (void) SetGrayscaleImage(image,exception); |
3292 | 3.93k | depth=quantize_info->tree_depth; |
3293 | 3.93k | if (depth == 0) |
3294 | 3.93k | { |
3295 | 3.93k | size_t |
3296 | 3.93k | colors; |
3297 | | |
3298 | | /* |
3299 | | Depth of color tree is: Log4(colormap size)+2. |
3300 | | */ |
3301 | 3.93k | colors=maximum_colors; |
3302 | 15.3k | for (depth=1; colors != 0; depth++) |
3303 | 11.3k | colors>>=2; |
3304 | 3.93k | if ((quantize_info->dither_method != NoDitherMethod) && (depth > 2)) |
3305 | 1.68k | depth--; |
3306 | 3.93k | if ((image->alpha_trait != UndefinedPixelTrait) && (depth > 5)) |
3307 | 0 | depth--; |
3308 | 3.93k | if (IsGrayImageType(type) != MagickFalse) |
3309 | 3.02k | depth=MaxTreeDepth; |
3310 | 3.93k | } |
3311 | | /* |
3312 | | Initialize color cube. |
3313 | | */ |
3314 | 3.93k | cube_info=GetQCubeInfo(quantize_info,depth,maximum_colors); |
3315 | 3.93k | if (cube_info == (QCubeInfo *) NULL) |
3316 | 2 | ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", |
3317 | 3.93k | image->filename); |
3318 | 3.93k | status=ClassifyImageColors(cube_info,image,exception); |
3319 | 3.93k | if (status != MagickFalse) |
3320 | 3.93k | { |
3321 | | /* |
3322 | | Reduce the number of colors in the image. |
3323 | | */ |
3324 | 3.93k | if (cube_info->colors > cube_info->maximum_colors) |
3325 | 256 | ReduceImageColors(image,cube_info); |
3326 | 3.93k | status=AssignImageColors(image,cube_info,exception); |
3327 | 3.93k | } |
3328 | 3.93k | DestroyQCubeInfo(cube_info); |
3329 | 3.93k | return(status); |
3330 | 3.93k | } |
3331 | | |
3332 | | /* |
3333 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
3334 | | % % |
3335 | | % % |
3336 | | % % |
3337 | | % Q u a n t i z e I m a g e s % |
3338 | | % % |
3339 | | % % |
3340 | | % % |
3341 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
3342 | | % |
3343 | | % QuantizeImages() analyzes the colors within a set of reference images and |
3344 | | % chooses a fixed number of colors to represent the set. The goal of the |
3345 | | % algorithm is to minimize the color difference between the input and output |
3346 | | % images while minimizing the processing time. |
3347 | | % |
3348 | | % The format of the QuantizeImages method is: |
3349 | | % |
3350 | | % MagickBooleanType QuantizeImages(const QuantizeInfo *quantize_info, |
3351 | | % Image *images,ExceptionInfo *exception) |
3352 | | % |
3353 | | % A description of each parameter follows: |
3354 | | % |
3355 | | % o quantize_info: Specifies a pointer to an QuantizeInfo structure. |
3356 | | % |
3357 | | % o images: Specifies a pointer to a list of Image structures. |
3358 | | % |
3359 | | % o exception: return any errors or warnings in this structure. |
3360 | | % |
3361 | | */ |
3362 | | MagickExport MagickBooleanType QuantizeImages(const QuantizeInfo *quantize_info, |
3363 | | Image *images,ExceptionInfo *exception) |
3364 | 0 | { |
3365 | 0 | Image |
3366 | 0 | *image; |
3367 | |
|
3368 | 0 | MagickBooleanType |
3369 | 0 | proceed, |
3370 | 0 | status; |
3371 | |
|
3372 | 0 | MagickProgressMonitor |
3373 | 0 | progress_monitor; |
3374 | |
|
3375 | 0 | QCubeInfo |
3376 | 0 | *cube_info; |
3377 | |
|
3378 | 0 | size_t |
3379 | 0 | depth, |
3380 | 0 | maximum_colors, |
3381 | 0 | number_images; |
3382 | |
|
3383 | 0 | ssize_t |
3384 | 0 | i; |
3385 | |
|
3386 | 0 | assert(quantize_info != (const QuantizeInfo *) NULL); |
3387 | 0 | assert(quantize_info->signature == MagickCoreSignature); |
3388 | 0 | assert(images != (Image *) NULL); |
3389 | 0 | assert(images->signature == MagickCoreSignature); |
3390 | 0 | assert(exception != (ExceptionInfo *) NULL); |
3391 | 0 | assert(exception->signature == MagickCoreSignature); |
3392 | 0 | if (IsEventLogging() != MagickFalse) |
3393 | 0 | (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename); |
3394 | 0 | if (GetNextImageInList(images) == (Image *) NULL) |
3395 | 0 | { |
3396 | | /* |
3397 | | Handle a single image with QuantizeImage. |
3398 | | */ |
3399 | 0 | status=QuantizeImage(quantize_info,images,exception); |
3400 | 0 | return(status); |
3401 | 0 | } |
3402 | 0 | status=MagickFalse; |
3403 | 0 | maximum_colors=quantize_info->number_colors; |
3404 | 0 | if (maximum_colors == 0) |
3405 | 0 | maximum_colors=MaxColormapSize; |
3406 | 0 | if (maximum_colors > MaxColormapSize) |
3407 | 0 | maximum_colors=MaxColormapSize; |
3408 | 0 | depth=quantize_info->tree_depth; |
3409 | 0 | if (depth == 0) |
3410 | 0 | { |
3411 | 0 | size_t |
3412 | 0 | colors; |
3413 | | |
3414 | | /* |
3415 | | Depth of color tree is: Log4(colormap size)+2. |
3416 | | */ |
3417 | 0 | colors=maximum_colors; |
3418 | 0 | for (depth=1; colors != 0; depth++) |
3419 | 0 | colors>>=2; |
3420 | 0 | if (quantize_info->dither_method != NoDitherMethod) |
3421 | 0 | depth--; |
3422 | 0 | } |
3423 | | /* |
3424 | | Initialize color cube. |
3425 | | */ |
3426 | 0 | cube_info=GetQCubeInfo(quantize_info,depth,maximum_colors); |
3427 | 0 | if (cube_info == (QCubeInfo *) NULL) |
3428 | 0 | { |
3429 | 0 | (void) ThrowMagickException(exception,GetMagickModule(), |
3430 | 0 | ResourceLimitError,"MemoryAllocationFailed","`%s'",images->filename); |
3431 | 0 | return(MagickFalse); |
3432 | 0 | } |
3433 | 0 | number_images=GetImageListLength(images); |
3434 | 0 | image=images; |
3435 | 0 | for (i=0; image != (Image *) NULL; i++) |
3436 | 0 | { |
3437 | 0 | progress_monitor=SetImageProgressMonitor(image,(MagickProgressMonitor) NULL, |
3438 | 0 | image->client_data); |
3439 | 0 | status=ClassifyImageColors(cube_info,image,exception); |
3440 | 0 | if (status == MagickFalse) |
3441 | 0 | break; |
3442 | 0 | (void) SetImageProgressMonitor(image,progress_monitor,image->client_data); |
3443 | 0 | proceed=SetImageProgress(image,AssignImageTag,(MagickOffsetType) i, |
3444 | 0 | number_images); |
3445 | 0 | if (proceed == MagickFalse) |
3446 | 0 | break; |
3447 | 0 | image=GetNextImageInList(image); |
3448 | 0 | } |
3449 | 0 | if (status != MagickFalse) |
3450 | 0 | { |
3451 | | /* |
3452 | | Reduce the number of colors in an image sequence. |
3453 | | */ |
3454 | 0 | ReduceImageColors(images,cube_info); |
3455 | 0 | image=images; |
3456 | 0 | for (i=0; image != (Image *) NULL; i++) |
3457 | 0 | { |
3458 | 0 | progress_monitor=SetImageProgressMonitor(image,(MagickProgressMonitor) |
3459 | 0 | NULL,image->client_data); |
3460 | 0 | status=AssignImageColors(image,cube_info,exception); |
3461 | 0 | if (status == MagickFalse) |
3462 | 0 | break; |
3463 | 0 | (void) SetImageProgressMonitor(image,progress_monitor, |
3464 | 0 | image->client_data); |
3465 | 0 | proceed=SetImageProgress(image,AssignImageTag,(MagickOffsetType) i, |
3466 | 0 | number_images); |
3467 | 0 | if (proceed == MagickFalse) |
3468 | 0 | break; |
3469 | 0 | image=GetNextImageInList(image); |
3470 | 0 | } |
3471 | 0 | } |
3472 | 0 | DestroyQCubeInfo(cube_info); |
3473 | 0 | return(status); |
3474 | 0 | } |
3475 | | |
3476 | | /* |
3477 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
3478 | | % % |
3479 | | % % |
3480 | | % % |
3481 | | + Q u a n t i z e E r r o r F l a t t e n % |
3482 | | % % |
3483 | | % % |
3484 | | % % |
3485 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
3486 | | % |
3487 | | % QuantizeErrorFlatten() traverses the color cube and flattens the quantization |
3488 | | % error into a sorted 1D array. This accelerates the color reduction process. |
3489 | | % |
3490 | | % Contributed by Yoya. |
3491 | | % |
3492 | | % The format of the QuantizeErrorFlatten method is: |
3493 | | % |
3494 | | % size_t QuantizeErrorFlatten(const QCubeInfo *cube_info, |
3495 | | % const QNodeInfo *node_info,const ssize_t offset, |
3496 | | % double *quantize_error) |
3497 | | % |
3498 | | % A description of each parameter follows. |
3499 | | % |
3500 | | % o cube_info: A pointer to the Cube structure. |
3501 | | % |
3502 | | % o node_info: pointer to node in color cube tree that is current pointer. |
3503 | | % |
3504 | | % o offset: quantize error offset. |
3505 | | % |
3506 | | % o quantize_error: the quantization error vector. |
3507 | | % |
3508 | | */ |
3509 | | static size_t QuantizeErrorFlatten(const QCubeInfo *cube_info, |
3510 | | const QNodeInfo *node_info,const ssize_t offset,double *quantize_error) |
3511 | 288k | { |
3512 | 288k | size_t |
3513 | 288k | n, |
3514 | 288k | number_children; |
3515 | | |
3516 | 288k | ssize_t |
3517 | 288k | i; |
3518 | | |
3519 | 288k | if (offset >= (ssize_t) cube_info->nodes) |
3520 | 0 | return(0); |
3521 | 288k | quantize_error[offset]=node_info->quantize_error; |
3522 | 288k | n=1; |
3523 | 288k | number_children=cube_info->associate_alpha == MagickFalse ? 8UL : 16UL; |
3524 | 2.64M | for (i=0; i < (ssize_t) number_children ; i++) |
3525 | 2.35M | if (node_info->child[i] != (QNodeInfo *) NULL) |
3526 | 287k | n+=QuantizeErrorFlatten(cube_info,node_info->child[i],offset+(ssize_t) n, |
3527 | 287k | quantize_error); |
3528 | 288k | return(n); |
3529 | 288k | } |
3530 | | |
3531 | | /* |
3532 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
3533 | | % % |
3534 | | % % |
3535 | | % % |
3536 | | + R e d u c e % |
3537 | | % % |
3538 | | % % |
3539 | | % % |
3540 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
3541 | | % |
3542 | | % Reduce() traverses the color cube tree and prunes any node whose |
3543 | | % quantization error falls below a particular threshold. |
3544 | | % |
3545 | | % The format of the Reduce method is: |
3546 | | % |
3547 | | % Reduce(QCubeInfo *cube_info,const QNodeInfo *node_info) |
3548 | | % |
3549 | | % A description of each parameter follows. |
3550 | | % |
3551 | | % o cube_info: A pointer to the Cube structure. |
3552 | | % |
3553 | | % o node_info: pointer to node in color cube tree that is to be pruned. |
3554 | | % |
3555 | | */ |
3556 | | static void Reduce(QCubeInfo *cube_info,const QNodeInfo *node_info) |
3557 | 393k | { |
3558 | 393k | size_t |
3559 | 393k | number_children; |
3560 | | |
3561 | 393k | ssize_t |
3562 | 393k | i; |
3563 | | |
3564 | | /* |
3565 | | Traverse any children. |
3566 | | */ |
3567 | 393k | number_children=cube_info->associate_alpha == MagickFalse ? 8UL : 16UL; |
3568 | 3.59M | for (i=0; i < (ssize_t) number_children; i++) |
3569 | 3.20M | if (node_info->child[i] != (QNodeInfo *) NULL) |
3570 | 393k | Reduce(cube_info,node_info->child[i]); |
3571 | 393k | if (node_info->quantize_error <= cube_info->pruning_threshold) |
3572 | 217k | PruneChild(cube_info,node_info); |
3573 | 176k | else |
3574 | 176k | { |
3575 | | /* |
3576 | | Find minimum pruning threshold. |
3577 | | */ |
3578 | 176k | if (node_info->number_unique > 0) |
3579 | 151k | cube_info->colors++; |
3580 | 176k | if (node_info->quantize_error < cube_info->next_threshold) |
3581 | 4.48k | cube_info->next_threshold=node_info->quantize_error; |
3582 | 176k | } |
3583 | 393k | } |
3584 | | |
3585 | | /* |
3586 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
3587 | | % % |
3588 | | % % |
3589 | | % % |
3590 | | + R e d u c e I m a g e C o l o r s % |
3591 | | % % |
3592 | | % % |
3593 | | % % |
3594 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
3595 | | % |
3596 | | % ReduceImageColors() repeatedly prunes the tree until the number of nodes |
3597 | | % with n2 > 0 is less than or equal to the maximum number of colors allowed |
3598 | | % in the output image. On any given iteration over the tree, it selects |
3599 | | % those nodes whose E value is minimal for pruning and merges their |
3600 | | % color statistics upward. It uses a pruning threshold, Ep, to govern |
3601 | | % node selection as follows: |
3602 | | % |
3603 | | % Ep = 0 |
3604 | | % while number of nodes with (n2 > 0) > required maximum number of colors |
3605 | | % prune all nodes such that E <= Ep |
3606 | | % Set Ep to minimum E in remaining nodes |
3607 | | % |
3608 | | % This has the effect of minimizing any quantization error when merging |
3609 | | % two nodes together. |
3610 | | % |
3611 | | % When a node to be pruned has offspring, the pruning procedure invokes |
3612 | | % itself recursively in order to prune the tree from the leaves upward. |
3613 | | % n2, Sr, Sg, and Sb in a node being pruned are always added to the |
3614 | | % corresponding data in that node's parent. This retains the pruned |
3615 | | % node's color characteristics for later averaging. |
3616 | | % |
3617 | | % For each node, n2 pixels exist for which that node represents the |
3618 | | % smallest volume in RGB space containing those pixel's colors. When n2 |
3619 | | % > 0 the node will uniquely define a color in the output image. At the |
3620 | | % beginning of reduction, n2 = 0 for all nodes except a the leaves of |
3621 | | % the tree which represent colors present in the input image. |
3622 | | % |
3623 | | % The other pixel count, n1, indicates the total number of colors |
3624 | | % within the cubic volume which the node represents. This includes n1 - |
3625 | | % n2 pixels whose colors should be defined by nodes at a lower level in |
3626 | | % the tree. |
3627 | | % |
3628 | | % The format of the ReduceImageColors method is: |
3629 | | % |
3630 | | % ReduceImageColors(const Image *image,QCubeInfo *cube_info) |
3631 | | % |
3632 | | % A description of each parameter follows. |
3633 | | % |
3634 | | % o image: the image. |
3635 | | % |
3636 | | % o cube_info: A pointer to the Cube structure. |
3637 | | % |
3638 | | */ |
3639 | | |
3640 | | static int QuantizeErrorCompare(const void *error_p,const void *error_q) |
3641 | 2.86M | { |
3642 | 2.86M | double |
3643 | 2.86M | *p, |
3644 | 2.86M | *q; |
3645 | | |
3646 | 2.86M | p=(double *) error_p; |
3647 | 2.86M | q=(double *) error_q; |
3648 | 2.86M | if (*p > *q) |
3649 | 1.47M | return(1); |
3650 | 1.39M | if (fabs(*q-*p) <= MagickEpsilon) |
3651 | 23.9k | return(0); |
3652 | 1.36M | return(-1); |
3653 | 1.39M | } |
3654 | | |
3655 | | static void ReduceImageColors(const Image *image,QCubeInfo *cube_info) |
3656 | 256 | { |
3657 | 643 | #define ReduceImageTag "Reduce/Image" |
3658 | | |
3659 | 256 | MagickBooleanType |
3660 | 256 | proceed; |
3661 | | |
3662 | 256 | MagickOffsetType |
3663 | 256 | offset; |
3664 | | |
3665 | 256 | size_t |
3666 | 256 | span; |
3667 | | |
3668 | 256 | cube_info->next_threshold=0.0; |
3669 | 256 | if (cube_info->colors > cube_info->maximum_colors) |
3670 | 256 | { |
3671 | 256 | double |
3672 | 256 | *quantize_error; |
3673 | | |
3674 | | /* |
3675 | | Enable rapid reduction of the number of unique colors. |
3676 | | */ |
3677 | 256 | quantize_error=(double *) AcquireQuantumMemory(cube_info->nodes, |
3678 | 256 | sizeof(*quantize_error)); |
3679 | 256 | if (quantize_error != (double *) NULL) |
3680 | 256 | { |
3681 | 256 | (void) QuantizeErrorFlatten(cube_info,cube_info->root,0, |
3682 | 256 | quantize_error); |
3683 | 256 | qsort(quantize_error,cube_info->nodes,sizeof(double), |
3684 | 256 | QuantizeErrorCompare); |
3685 | 256 | if (cube_info->nodes > (110*(cube_info->maximum_colors+1)/100)) |
3686 | 220 | cube_info->next_threshold=quantize_error[cube_info->nodes-110* |
3687 | 220 | (cube_info->maximum_colors+1)/100]; |
3688 | 256 | quantize_error=(double *) RelinquishMagickMemory(quantize_error); |
3689 | 256 | } |
3690 | 256 | } |
3691 | 899 | for (span=cube_info->colors; cube_info->colors > cube_info->maximum_colors; ) |
3692 | 643 | { |
3693 | 643 | cube_info->pruning_threshold=cube_info->next_threshold; |
3694 | 643 | cube_info->next_threshold=cube_info->root->quantize_error-1; |
3695 | 643 | cube_info->colors=0; |
3696 | 643 | Reduce(cube_info,cube_info->root); |
3697 | 643 | offset=(MagickOffsetType) span-(MagickOffsetType) cube_info->colors; |
3698 | 643 | proceed=SetImageProgress(image,ReduceImageTag,offset,span- |
3699 | 643 | cube_info->maximum_colors+1); |
3700 | 643 | if (proceed == MagickFalse) |
3701 | 0 | break; |
3702 | 643 | } |
3703 | 256 | } |
3704 | | |
3705 | | /* |
3706 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
3707 | | % % |
3708 | | % % |
3709 | | % % |
3710 | | % R e m a p I m a g e % |
3711 | | % % |
3712 | | % % |
3713 | | % % |
3714 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
3715 | | % |
3716 | | % RemapImage() replaces the colors of an image with the closest of the colors |
3717 | | % from the reference image. |
3718 | | % |
3719 | | % The format of the RemapImage method is: |
3720 | | % |
3721 | | % MagickBooleanType RemapImage(const QuantizeInfo *quantize_info, |
3722 | | % Image *image,const Image *remap_image,ExceptionInfo *exception) |
3723 | | % |
3724 | | % A description of each parameter follows: |
3725 | | % |
3726 | | % o quantize_info: Specifies a pointer to an QuantizeInfo structure. |
3727 | | % |
3728 | | % o image: the image. |
3729 | | % |
3730 | | % o remap_image: the reference image. |
3731 | | % |
3732 | | % o exception: return any errors or warnings in this structure. |
3733 | | % |
3734 | | */ |
3735 | | MagickExport MagickBooleanType RemapImage(const QuantizeInfo *quantize_info, |
3736 | | Image *image,const Image *remap_image,ExceptionInfo *exception) |
3737 | 28 | { |
3738 | 28 | QCubeInfo |
3739 | 28 | *cube_info; |
3740 | | |
3741 | 28 | MagickBooleanType |
3742 | 28 | status; |
3743 | | |
3744 | | /* |
3745 | | Initialize color cube. |
3746 | | */ |
3747 | 28 | assert(image != (Image *) NULL); |
3748 | 28 | assert(image->signature == MagickCoreSignature); |
3749 | 28 | assert(remap_image != (Image *) NULL); |
3750 | 28 | assert(remap_image->signature == MagickCoreSignature); |
3751 | 28 | assert(exception != (ExceptionInfo *) NULL); |
3752 | 28 | assert(exception->signature == MagickCoreSignature); |
3753 | 28 | if (IsEventLogging() != MagickFalse) |
3754 | 0 | (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); |
3755 | 28 | cube_info=GetQCubeInfo(quantize_info,MaxTreeDepth,MaxColormapSize); |
3756 | 28 | if (cube_info == (QCubeInfo *) NULL) |
3757 | 0 | ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", |
3758 | 28 | image->filename); |
3759 | 28 | cube_info->quantize_info->colorspace=remap_image->colorspace; |
3760 | 28 | status=ClassifyImageColors(cube_info,remap_image,exception); |
3761 | 28 | if (status != MagickFalse) |
3762 | 28 | { |
3763 | | /* |
3764 | | Classify image colors from the reference image. |
3765 | | */ |
3766 | 28 | cube_info->quantize_info->number_colors=cube_info->colors; |
3767 | 28 | if (cube_info->colors > cube_info->maximum_colors) |
3768 | 0 | ReduceImageColors(image,cube_info); |
3769 | 28 | status=AssignImageColors(image,cube_info,exception); |
3770 | 28 | } |
3771 | 28 | DestroyQCubeInfo(cube_info); |
3772 | 28 | return(status); |
3773 | 28 | } |
3774 | | |
3775 | | /* |
3776 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
3777 | | % % |
3778 | | % % |
3779 | | % % |
3780 | | % R e m a p I m a g e s % |
3781 | | % % |
3782 | | % % |
3783 | | % % |
3784 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
3785 | | % |
3786 | | % RemapImages() replaces the colors of a sequence of images with the |
3787 | | % closest color from a reference image. |
3788 | | % |
3789 | | % The format of the RemapImage method is: |
3790 | | % |
3791 | | % MagickBooleanType RemapImages(const QuantizeInfo *quantize_info, |
3792 | | % Image *images,Image *remap_image,ExceptionInfo *exception) |
3793 | | % |
3794 | | % A description of each parameter follows: |
3795 | | % |
3796 | | % o quantize_info: Specifies a pointer to an QuantizeInfo structure. |
3797 | | % |
3798 | | % o images: the image sequence. |
3799 | | % |
3800 | | % o remap_image: the reference image. |
3801 | | % |
3802 | | % o exception: return any errors or warnings in this structure. |
3803 | | % |
3804 | | */ |
3805 | | MagickExport MagickBooleanType RemapImages(const QuantizeInfo *quantize_info, |
3806 | | Image *images,const Image *remap_image,ExceptionInfo *exception) |
3807 | 0 | { |
3808 | 0 | Image |
3809 | 0 | *image; |
3810 | |
|
3811 | 0 | MagickBooleanType |
3812 | 0 | status; |
3813 | |
|
3814 | 0 | QCubeInfo |
3815 | 0 | *cube_info; |
3816 | |
|
3817 | 0 | assert(images != (Image *) NULL); |
3818 | 0 | assert(images->signature == MagickCoreSignature); |
3819 | 0 | assert(exception != (ExceptionInfo *) NULL); |
3820 | 0 | assert(exception->signature == MagickCoreSignature); |
3821 | 0 | if (IsEventLogging() != MagickFalse) |
3822 | 0 | (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename); |
3823 | 0 | image=images; |
3824 | 0 | if (remap_image == (Image *) NULL) |
3825 | 0 | { |
3826 | | /* |
3827 | | Create a global colormap for an image sequence. |
3828 | | */ |
3829 | 0 | status=QuantizeImages(quantize_info,images,exception); |
3830 | 0 | return(status); |
3831 | 0 | } |
3832 | | /* |
3833 | | Classify image colors from the reference image. |
3834 | | */ |
3835 | 0 | cube_info=GetQCubeInfo(quantize_info,MaxTreeDepth, |
3836 | 0 | quantize_info->number_colors); |
3837 | 0 | if (cube_info == (QCubeInfo *) NULL) |
3838 | 0 | ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", |
3839 | 0 | image->filename); |
3840 | 0 | status=ClassifyImageColors(cube_info,remap_image,exception); |
3841 | 0 | if (status != MagickFalse) |
3842 | 0 | { |
3843 | | /* |
3844 | | Classify image colors from the reference image. |
3845 | | */ |
3846 | 0 | cube_info->quantize_info->number_colors=cube_info->colors; |
3847 | 0 | image=images; |
3848 | 0 | for ( ; image != (Image *) NULL; image=GetNextImageInList(image)) |
3849 | 0 | { |
3850 | 0 | status=AssignImageColors(image,cube_info,exception); |
3851 | 0 | if (status == MagickFalse) |
3852 | 0 | break; |
3853 | 0 | } |
3854 | 0 | } |
3855 | 0 | DestroyQCubeInfo(cube_info); |
3856 | 0 | return(status); |
3857 | 0 | } |
3858 | | |
3859 | | /* |
3860 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
3861 | | % % |
3862 | | % % |
3863 | | % % |
3864 | | % S e t G r a y s c a l e I m a g e % |
3865 | | % % |
3866 | | % % |
3867 | | % % |
3868 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
3869 | | % |
3870 | | % SetGrayscaleImage() converts an image to a PseudoClass grayscale image. |
3871 | | % |
3872 | | % The format of the SetGrayscaleImage method is: |
3873 | | % |
3874 | | % MagickBooleanType SetGrayscaleImage(Image *image, |
3875 | | % ExceptionInfo *exception) |
3876 | | % |
3877 | | % A description of each parameter follows: |
3878 | | % |
3879 | | % o image: The image. |
3880 | | % |
3881 | | % o exception: return any errors or warnings in this structure. |
3882 | | % |
3883 | | */ |
3884 | | |
3885 | | #if defined(__cplusplus) || defined(c_plusplus) |
3886 | | extern "C" { |
3887 | | #endif |
3888 | | |
3889 | | static int IntensityCompare(const void *x,const void *y) |
3890 | 1.41M | { |
3891 | 1.41M | double |
3892 | 1.41M | intensity; |
3893 | | |
3894 | 1.41M | PixelInfo |
3895 | 1.41M | *color_1, |
3896 | 1.41M | *color_2; |
3897 | | |
3898 | 1.41M | color_1=(PixelInfo *) x; |
3899 | 1.41M | color_2=(PixelInfo *) y; |
3900 | 1.41M | intensity=GetPixelInfoIntensity((const Image *) NULL,color_1)- |
3901 | 1.41M | GetPixelInfoIntensity((const Image *) NULL,color_2); |
3902 | 1.41M | if (intensity < (double) INT_MIN) |
3903 | 8 | intensity=(double) INT_MIN; |
3904 | 1.41M | if (intensity > (double) INT_MAX) |
3905 | 8 | intensity=(double) INT_MAX; |
3906 | 1.41M | return((int) intensity); |
3907 | 1.41M | } |
3908 | | |
3909 | | #if defined(__cplusplus) || defined(c_plusplus) |
3910 | | } |
3911 | | #endif |
3912 | | |
3913 | | static MagickBooleanType SetGrayscaleImage(Image *image, |
3914 | | ExceptionInfo *exception) |
3915 | 3.02k | { |
3916 | 3.02k | CacheView |
3917 | 3.02k | *image_view; |
3918 | | |
3919 | 3.02k | MagickBooleanType |
3920 | 3.02k | status; |
3921 | | |
3922 | 3.02k | PixelInfo |
3923 | 3.02k | *colormap; |
3924 | | |
3925 | 3.02k | size_t |
3926 | 3.02k | extent; |
3927 | | |
3928 | 3.02k | ssize_t |
3929 | 3.02k | *colormap_index, |
3930 | 3.02k | i, |
3931 | 3.02k | j, |
3932 | 3.02k | y; |
3933 | | |
3934 | 3.02k | assert(image != (Image *) NULL); |
3935 | 3.02k | assert(image->signature == MagickCoreSignature); |
3936 | 3.02k | if (image->type != GrayscaleType) |
3937 | 2.79k | (void) TransformImageColorspace(image,GRAYColorspace,exception); |
3938 | 3.02k | extent=MagickMax(image->colors+1,MagickMax(MaxColormapSize,MaxMap+1)); |
3939 | 3.02k | colormap_index=(ssize_t *) AcquireQuantumMemory(extent, |
3940 | 3.02k | sizeof(*colormap_index)); |
3941 | 3.02k | if (colormap_index == (ssize_t *) NULL) |
3942 | 0 | ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", |
3943 | 3.02k | image->filename); |
3944 | 3.02k | if (image->storage_class != PseudoClass) |
3945 | 3.02k | { |
3946 | 3.02k | (void) memset(colormap_index,(-1),extent*sizeof(*colormap_index)); |
3947 | 3.02k | if (AcquireImageColormap(image,MaxColormapSize,exception) == MagickFalse) |
3948 | 0 | { |
3949 | 0 | colormap_index=(ssize_t *) RelinquishMagickMemory(colormap_index); |
3950 | 0 | ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", |
3951 | 0 | image->filename); |
3952 | 0 | } |
3953 | 3.02k | image->colors=0; |
3954 | 3.02k | status=MagickTrue; |
3955 | 3.02k | image_view=AcquireAuthenticCacheView(image,exception); |
3956 | | #if defined(MAGICKCORE_OPENMP_SUPPORT) |
3957 | | #pragma omp parallel for schedule(static) shared(status) \ |
3958 | | magick_number_threads(image,image,image->rows,1) |
3959 | | #endif |
3960 | 358k | for (y=0; y < (ssize_t) image->rows; y++) |
3961 | 355k | { |
3962 | 355k | Quantum |
3963 | 355k | *magick_restrict q; |
3964 | | |
3965 | 355k | ssize_t |
3966 | 355k | x; |
3967 | | |
3968 | 355k | if (status == MagickFalse) |
3969 | 0 | continue; |
3970 | 355k | q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1, |
3971 | 355k | exception); |
3972 | 355k | if (q == (Quantum *) NULL) |
3973 | 0 | { |
3974 | 0 | status=MagickFalse; |
3975 | 0 | continue; |
3976 | 0 | } |
3977 | 183M | for (x=0; x < (ssize_t) image->columns; x++) |
3978 | 183M | { |
3979 | 183M | size_t |
3980 | 183M | intensity; |
3981 | | |
3982 | 183M | intensity=ScaleQuantumToMap(GetPixelRed(image,q)); |
3983 | 183M | if (colormap_index[intensity] < 0) |
3984 | 136k | { |
3985 | | #if defined(MAGICKCORE_OPENMP_SUPPORT) |
3986 | | #pragma omp critical (MagickCore_SetGrayscaleImage) |
3987 | | #endif |
3988 | 136k | if (colormap_index[intensity] < 0) |
3989 | 136k | { |
3990 | 136k | colormap_index[intensity]=(ssize_t) image->colors; |
3991 | 136k | image->colormap[image->colors].red=(double) |
3992 | 136k | GetPixelRed(image,q); |
3993 | 136k | image->colormap[image->colors].green=(double) |
3994 | 136k | GetPixelGreen(image,q); |
3995 | 136k | image->colormap[image->colors].blue=(double) |
3996 | 136k | GetPixelBlue(image,q); |
3997 | 136k | image->colors++; |
3998 | 136k | } |
3999 | 136k | } |
4000 | 183M | SetPixelIndex(image,(Quantum) colormap_index[intensity],q); |
4001 | 183M | q+=(ptrdiff_t) GetPixelChannels(image); |
4002 | 183M | } |
4003 | 355k | if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse) |
4004 | 0 | status=MagickFalse; |
4005 | 355k | } |
4006 | 3.02k | image_view=DestroyCacheView(image_view); |
4007 | 3.02k | } |
4008 | 3.02k | (void) memset(colormap_index,0,extent*sizeof(*colormap_index)); |
4009 | 140k | for (i=0; i < (ssize_t) image->colors; i++) |
4010 | 137k | image->colormap[i].alpha=(double) i; |
4011 | 3.02k | qsort((void *) image->colormap,image->colors,sizeof(PixelInfo), |
4012 | 3.02k | IntensityCompare); |
4013 | 3.02k | colormap=(PixelInfo *) AcquireQuantumMemory(image->colors,sizeof(*colormap)); |
4014 | 3.02k | if (colormap == (PixelInfo *) NULL) |
4015 | 0 | { |
4016 | 0 | colormap_index=(ssize_t *) RelinquishMagickMemory(colormap_index); |
4017 | 0 | ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", |
4018 | 0 | image->filename); |
4019 | 0 | } |
4020 | 3.02k | j=0; |
4021 | 3.02k | colormap[j]=image->colormap[0]; |
4022 | 140k | for (i=0; i < (ssize_t) image->colors; i++) |
4023 | 137k | { |
4024 | 137k | if (IsPixelInfoEquivalent(&colormap[j],&image->colormap[i]) == MagickFalse) |
4025 | 134k | { |
4026 | 134k | j++; |
4027 | 134k | colormap[j]=image->colormap[i]; |
4028 | 134k | } |
4029 | 137k | colormap_index[(ssize_t) image->colormap[i].alpha]=j; |
4030 | 137k | } |
4031 | 3.02k | image->colors=(size_t) (j+1); |
4032 | 3.02k | image->colormap=(PixelInfo *) RelinquishMagickMemory(image->colormap); |
4033 | 3.02k | image->colormap=colormap; |
4034 | 3.02k | status=MagickTrue; |
4035 | 3.02k | image_view=AcquireAuthenticCacheView(image,exception); |
4036 | | #if defined(MAGICKCORE_OPENMP_SUPPORT) |
4037 | | #pragma omp parallel for schedule(static) shared(status) \ |
4038 | | magick_number_threads(image,image,image->rows,1) |
4039 | | #endif |
4040 | 358k | for (y=0; y < (ssize_t) image->rows; y++) |
4041 | 355k | { |
4042 | 355k | Quantum |
4043 | 355k | *magick_restrict q; |
4044 | | |
4045 | 355k | ssize_t |
4046 | 355k | x; |
4047 | | |
4048 | 355k | if (status == MagickFalse) |
4049 | 0 | continue; |
4050 | 355k | q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception); |
4051 | 355k | if (q == (Quantum *) NULL) |
4052 | 0 | { |
4053 | 0 | status=MagickFalse; |
4054 | 0 | continue; |
4055 | 0 | } |
4056 | 183M | for (x=0; x < (ssize_t) image->columns; x++) |
4057 | 183M | { |
4058 | 183M | SetPixelIndex(image,(Quantum) colormap_index[ScaleQuantumToMap( |
4059 | 183M | GetPixelIndex(image,q))],q); |
4060 | 183M | q+=(ptrdiff_t) GetPixelChannels(image); |
4061 | 183M | } |
4062 | 355k | if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse) |
4063 | 0 | status=MagickFalse; |
4064 | 355k | } |
4065 | 3.02k | image_view=DestroyCacheView(image_view); |
4066 | 3.02k | colormap_index=(ssize_t *) RelinquishMagickMemory(colormap_index); |
4067 | 3.02k | image->type=GrayscaleType; |
4068 | 3.02k | if (SetImageMonochrome(image,exception) != MagickFalse) |
4069 | 2.48k | image->type=BilevelType; |
4070 | 3.02k | return(status); |
4071 | 3.02k | } |
4072 | | |
4073 | | /* |
4074 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
4075 | | % % |
4076 | | % % |
4077 | | % % |
4078 | | + S e t I m a g e C o l o r m a p % |
4079 | | % % |
4080 | | % % |
4081 | | % % |
4082 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
4083 | | % |
4084 | | % SetImageColormap() traverses the color cube tree and sets the colormap of |
4085 | | % the image. A colormap entry is any node in the color cube tree where the |
4086 | | % of unique colors is not zero. |
4087 | | % |
4088 | | % The format of the SetImageColormap method is: |
4089 | | % |
4090 | | % MagickBooleanType SetImageColormap(Image *image,QCubeInfo *cube_info, |
4091 | | % ExceptionInfo *node_info) |
4092 | | % |
4093 | | % A description of each parameter follows. |
4094 | | % |
4095 | | % o image: the image. |
4096 | | % |
4097 | | % o cube_info: A pointer to the Cube structure. |
4098 | | % |
4099 | | % o exception: return any errors or warnings in this structure. |
4100 | | % |
4101 | | */ |
4102 | | MagickBooleanType SetImageColormap(Image *image,QCubeInfo *cube_info, |
4103 | | ExceptionInfo *exception) |
4104 | 3.96k | { |
4105 | 3.96k | size_t |
4106 | 3.96k | number_colors; |
4107 | | |
4108 | 3.96k | number_colors=MagickMax(cube_info->maximum_colors,cube_info->colors); |
4109 | 3.96k | if (AcquireImageColormap(image,number_colors,exception) == MagickFalse) |
4110 | 0 | ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", |
4111 | 3.96k | image->filename); |
4112 | 3.96k | image->colors=0; |
4113 | 3.96k | DefineImageColormap(image,cube_info,cube_info->root); |
4114 | 3.96k | if (image->colors != number_colors) |
4115 | 2.55k | { |
4116 | 2.55k | image->colormap=(PixelInfo *) ResizeQuantumMemory(image->colormap, |
4117 | 2.55k | image->colors+1,sizeof(*image->colormap)); |
4118 | 2.55k | if (image->colormap == (PixelInfo *) NULL) |
4119 | 0 | ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", |
4120 | 2.55k | image->filename); |
4121 | 2.55k | } |
4122 | 3.96k | return(MagickTrue); |
4123 | 3.96k | } |