• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

hazendaz / smartsprites / 555

21 May 2026 03:26PM UTC coverage: 87.799%. Remained the same
555

push

github

hazendaz
[tests] Fix tests that were looking at size of original license before spdx change

557 of 670 branches covered (83.13%)

Branch coverage included in aggregate %.

1350 of 1502 relevant lines covered (89.88%)

0.9 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

71.43
/src/main/java/amd/Quantize.java
1
/*
2
 * SPDX-License-Identifier: BSD-3-Clause
3
 * See LICENSE file for details.
4
 *
5
 * Copyright 2021-2026 Hazendaz
6
 * Copyright (C) 2007-2009, Stanisław Osiński.
7
 */
8
package amd;
9

10
/**
11
 * (#)Quantize.java 0.90 9/19/00 Adam Doppelt
12
 * <p>
13
 * An efficient color quantization algorithm, adapted from the C++ implementation quantize.c in
14
 * <a href="http://www.imagemagick.org/">ImageMagick</a>. The pixels for an image are placed into an oct tree. The oct
15
 * tree is reduced in size, and the pixels from the original image are reassigned to the nodes in the reduced tree.
16
 * <p>
17
 * Here is the copyright notice from ImageMagick:
18
 *
19
 * <pre>
20
 * %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
21
 * %  Permission is hereby granted, free of charge, to any person obtaining a    %
22
 * %  copy of this software and associated documentation files ("ImageMagick"),  %
23
 * %  to deal in ImageMagick without restriction, including without limitation   %
24
 * %  the rights to use, copy, modify, merge, publish, distribute, sublicense,   %
25
 * %  and/or sell copies of ImageMagick, and to permit persons to whom the       %
26
 * %  ImageMagick is furnished to do so, subject to the following conditions:    %
27
 * %                                                                             %
28
 * %  The above copyright notice and this permission notice shall be included in %
29
 * %  all copies or substantial portions of ImageMagick.                         %
30
 * %                                                                             %
31
 * %  The software is provided "as is", without warranty of any kind, express or %
32
 * %  implied, including but not limited to the warranties of merchantability,   %
33
 * %  fitness for a particular purpose and noninfringement.  In no event shall   %
34
 * %  E. I. du Pont de Nemours and Company be liable for any claim, damages or   %
35
 * %  other liability, whether in an action of contract, tort or otherwise,      %
36
 * %  arising from, out of or in connection with ImageMagick or the use or other %
37
 * %  dealings in ImageMagick.                                                   %
38
 * %                                                                             %
39
 * %  Except as contained in this notice, the name of the E. I. du Pont de       %
40
 * %  Nemours and Company shall not be used in advertising or otherwise to       %
41
 * %  promote the sale, use or other dealings in ImageMagick without prior       %
42
 * %  written authorization from the E. I. du Pont de Nemours and Company.       %
43
 * %                                                                             %
44
 * %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
45
 * </pre>
46
 *
47
 * @author <a href="http://www.gurge.com/amd/">Adam Doppelt</a>
48
 *
49
 * @version 0.90 19 Sep 2000
50
 */
51
public class Quantize {
52

53
    /**
54
     * <pre>
55
     * %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
56
     * %                                                                             %
57
     * %                                                                             %
58
     * %                                                                             %
59
     * %           QQQ   U   U   AAA   N   N  TTTTT  IIIII   ZZZZZ  EEEEE            %
60
     * %          Q   Q  U   U  A   A  NN  N    T      I        ZZ  E                %
61
     * %          Q   Q  U   U  AAAAA  N N N    T      I      ZZZ   EEEEE            %
62
     * %          Q  QQ  U   U  A   A  N  NN    T      I     ZZ     E                %
63
     * %           QQQQ   UUU   A   A  N   N    T    IIIII   ZZZZZ  EEEEE            %
64
     * %                                                                             %
65
     * %                                                                             %
66
     * %              Reduce the Number of Unique Colors in an Image                 %
67
     * %                                                                             %
68
     * %                                                                             %
69
     * %                           Software Design                                   %
70
     * %                             John Cristy                                     %
71
     * %                              July 1992                                      %
72
     * %                                                                             %
73
     * %                                                                             %
74
     * %  Copyright 1998 E. I. du Pont de Nemours and Company                        %
75
     * %                                                                             %
76
     * %  Permission is hereby granted, free of charge, to any person obtaining a    %
77
     * %  copy of this software and associated documentation files ("ImageMagick"),  %
78
     * %  to deal in ImageMagick without restriction, including without limitation   %
79
     * %  the rights to use, copy, modify, merge, publish, distribute, sublicense,   %
80
     * %  and/or sell copies of ImageMagick, and to permit persons to whom the       %
81
     * %  ImageMagick is furnished to do so, subject to the following conditions:    %
82
     * %                                                                             %
83
     * %  The above copyright notice and this permission notice shall be included in %
84
     * %  all copies or substantial portions of ImageMagick.                         %
85
     * %                                                                             %
86
     * %  The software is provided "as is", without warranty of any kind, express or %
87
     * %  implied, including but not limited to the warranties of merchantability,   %
88
     * %  fitness for a particular purpose and noninfringement.  In no event shall   %
89
     * %  E. I. du Pont de Nemours and Company be liable for any claim, damages or   %
90
     * %  other liability, whether in an action of contract, tort or otherwise,      %
91
     * %  arising from, out of or in connection with ImageMagick or the use or other %
92
     * %  dealings in ImageMagick.                                                   %
93
     * %                                                                             %
94
     * %  Except as contained in this notice, the name of the E. I. du Pont de       %
95
     * %  Nemours and Company shall not be used in advertising or otherwise to       %
96
     * %  promote the sale, use or other dealings in ImageMagick without prior       %
97
     * %  written authorization from the E. I. du Pont de Nemours and Company.       %
98
     * %                                                                             %
99
     * %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
100
     * %
101
     * %  Realism in computer graphics typically requires using 24 bits/pixel to
102
     * %  generate an image. Yet many graphic display devices do not contain
103
     * %  the amount of memory necessary to match the spatial and color
104
     * %  resolution of the human eye. The QUANTIZE program takes a 24 bit
105
     * %  image and reduces the number of colors so it can be displayed on
106
     * %  raster device with less bits per pixel. In most instances, the
107
     * %  quantized image closely resembles the original reference image.
108
     * %
109
     * %  A reduction of colors in an image is also desirable for image
110
     * %  transmission and real-time animation.
111
     * %
112
     * %  Function Quantize takes a standard RGB or monochrome images and quantizes
113
     * %  them down to some fixed number of colors.
114
     * %
115
     * %  For purposes of color allocation, an image is a set of n pixels, where
116
     * %  each pixel is a point in RGB space. RGB space is a 3-dimensional
117
     * %  vector space, and each pixel, pi, is defined by an ordered triple of
118
     * %  red, green, and blue coordinates, (ri, gi, bi).
119
     * %
120
     * %  Each primary color component (red, green, or blue) represents an
121
     * %  intensity which varies linearly from 0 to a maximum value, cmax, which
122
     * %  corresponds to full saturation of that color. Color allocation is
123
     * %  defined over a domain consisting of the cube in RGB space with
124
     * %  opposite vertices at (0,0,0) and (cmax,cmax,cmax). QUANTIZE requires
125
     * %  cmax = 255.
126
     * %
127
     * %  The algorithm maps this domain onto a tree in which each node
128
     * %  represents a cube within that domain. In the following discussion
129
     * %  these cubes are defined by the coordinate of two opposite vertices:
130
     * %  The vertex nearest the origin in RGB space and the vertex farthest
131
     * %  from the origin.
132
     * %
133
     * %  The tree's root node represents the the entire domain, (0,0,0) through
134
     * %  (cmax,cmax,cmax). Each lower level in the tree is generated by
135
     * %  subdividing one node's cube into eight smaller cubes of equal size.
136
     * %  This corresponds to bisecting the parent cube with planes passing
137
     * %  through the midpoints of each edge.
138
     * %
139
     * %  The basic algorithm operates in three phases: Classification,
140
     * %  Reduction, and Assignment. Classification builds a color
141
     * %  description tree for the image. Reduction collapses the tree until
142
     * %  the number it represents, at most, the number of colors desired in the
143
     * %  output image. Assignment defines the output image's color map and
144
     * %  sets each pixel's color by reclassification in the reduced tree.
145
     * %  Our goal is to minimize the numerical discrepancies between the original
146
     * %  colors and quantized colors (quantization error).
147
     * %
148
     * %  Classification begins by initializing a color description tree of
149
     * %  sufficient depth to represent each possible input color in a leaf.
150
     * %  However, it is impractical to generate a fully-formed color
151
     * %  description tree in the classification phase for realistic values of
152
     * %  cmax. If colors components in the input image are quantized to k-bit
153
     * %  precision, so that cmax= 2k-1, the tree would need k levels below the
154
     * %  root node to allow representing each possible input color in a leaf.
155
     * %  This becomes prohibitive because the tree's total number of nodes is
156
     * %  1 + sum(i=1,k,8k).
157
     * %
158
     * %  A complete tree would require 19,173,961 nodes for k = 8, cmax = 255.
159
     * %  Therefore, to avoid building a fully populated tree, QUANTIZE: (1)
160
     * %  Initializes data structures for nodes only as they are needed;  (2)
161
     * %  Chooses a maximum depth for the tree as a function of the desired
162
     * %  number of colors in the output image (currently log2(colormap size)).
163
     * %
164
     * %  For each pixel in the input image, classification scans downward from
165
     * %  the root of the color description tree. At each level of the tree it
166
     * %  identifies the single node which represents a cube in RGB space
167
     * %  containing the pixel's color. It updates the following data for each
168
     * %  such node:
169
     * %
170
     * %    n1: Number of pixels whose color is contained in the RGB cube
171
     * %    which this node represents;
172
     * %
173
     * %    n2: Number of pixels whose color is not represented in a node at
174
     * %    lower depth in the tree;  initially,  n2 = 0 for all nodes except
175
     * %    leaves of the tree.
176
     * %
177
     * %    Sr, Sg, Sb: Sums of the red, green, and blue component values for
178
     * %    all pixels not classified at a lower depth. The combination of
179
     * %    these sums and n2  will ultimately characterize the mean color of a
180
     * %    set of pixels represented by this node.
181
     * %
182
     * %    E: The distance squared in RGB space between each pixel contained
183
     * %    within a node and the nodes' center. This represents the quantization
184
     * %    error for a node.
185
     * %
186
     * %  Reduction repeatedly prunes the tree until the number of nodes with
187
     * %  n2 > 0 is less than or equal to the maximum number of colors allowed
188
     * %  in the output image. On any given iteration over the tree, it selects
189
     * %  those nodes whose E count is minimal for pruning and merges their
190
     * %  color statistics upward. It uses a pruning threshold, Ep, to govern
191
     * %  node selection as follows:
192
     * %
193
     * %    Ep = 0
194
     * %    while number of nodes with (n2 > 0) > required maximum number of colors
195
     * %      prune all nodes such that E <= Ep
196
     * %      Set Ep to minimum E in remaining nodes
197
     * %
198
     * %  This has the effect of minimizing any quantization error when merging
199
     * %  two nodes together.
200
     * %
201
     * %  When a node to be pruned has offspring, the pruning procedure invokes
202
     * %  itself recursively in order to prune the tree from the leaves upward.
203
     * %  n2,  Sr, Sg,  and  Sb in a node being pruned are always added to the
204
     * %  corresponding data in that node's parent. This retains the pruned
205
     * %  node's color characteristics for later averaging.
206
     * %
207
     * %  For each node, n2 pixels exist for which that node represents the
208
     * %  smallest volume in RGB space containing those pixel's colors. When n2
209
     * %  > 0 the node will uniquely define a color in the output image. At the
210
     * %  beginning of reduction,  n2 = 0  for all nodes except a the leaves of
211
     * %  the tree which represent colors present in the input image.
212
     * %
213
     * %  The other pixel count, n1, indicates the total number of colors
214
     * %  within the cubic volume which the node represents. This includes n1 -
215
     * %  n2  pixels whose colors should be defined by nodes at a lower level in
216
     * %  the tree.
217
     * %
218
     * %  Assignment generates the output image from the pruned tree. The
219
     * %  output image consists of two parts: (1)  A color map, which is an
220
     * %  array of color descriptions (RGB triples) for each color present in
221
     * %  the output image;  (2)  A pixel array, which represents each pixel as
222
     * %  an index into the color map array.
223
     * %
224
     * %  First, the assignment phase makes one pass over the pruned color
225
     * %  description tree to establish the image's color map. For each node
226
     * %  with n2  > 0, it divides Sr, Sg, and Sb by n2 . This produces the
227
     * %  mean color of all pixels that classify no lower than this node. Each
228
     * %  of these colors becomes an entry in the color map.
229
     * %
230
     * %  Finally,  the assignment phase reclassifies each pixel in the pruned
231
     * %  tree to identify the deepest node containing the pixel's color. The
232
     * %  pixel's value in the pixel array becomes the index of this node's mean
233
     * %  color in the color map.
234
     * %
235
     * %  With the permission of USC Information Sciences Institute, 4676 Admiralty
236
     * %  Way, Marina del Rey, California  90292, this code was adapted from module
237
     * %  ALCOLS written by Paul Raveling.
238
     * %
239
     * %  The names of ISI and USC are not used in advertising or publicity
240
     * %  pertaining to distribution of the software without prior specific
241
     * %  written permission from ISI.
242
     * %
243
     * </pre>
244
     */
245

246
    /** The Constant QUICK. */
247
    static final boolean QUICK = false;
248

249
    /** The Constant MAX_RGB. */
250
    static final int MAX_RGB = 255;
251

252
    /** The Constant MAX_NODES. */
253
    static final int MAX_NODES = 266817;
254

255
    /** The Constant MAX_TREE_DEPTH. */
256
    static final int MAX_TREE_DEPTH = 8;
257

258
    /** The squares. These are precomputed in advance. */
259
    static int[] SQUARES;
260

261
    /** The shift. */
262
    static int[] SHIFT;
263

264
    static {
265
        SQUARES = new int[MAX_RGB + MAX_RGB + 1];
1✔
266
        for (int i = -MAX_RGB; i <= MAX_RGB; i++) {
1✔
267
            SQUARES[i + MAX_RGB] = i * i;
1✔
268
        }
269

270
        SHIFT = new int[MAX_TREE_DEPTH + 1];
1✔
271
        for (int i = 0; i < MAX_TREE_DEPTH + 1; ++i) {
1✔
272
            SHIFT[i] = 1 << (15 - i);
1✔
273
        }
274
    }
1✔
275

276
    /**
277
     * Instantiates a new quantize.
278
     */
279
    private Quantize() {
280
        // Prevent Instantiation
281
    }
282

283
    /**
284
     * Reduce the image to the given number of colors. The pixels are reduced in place.
285
     *
286
     * @param pixels
287
     *            the pixels
288
     * @param maxColors
289
     *            the max colors
290
     *
291
     * @return The new color palette.
292
     */
293
    public static int[] quantizeImage(int[][] pixels, int maxColors) {
294
        Cube cube = new Cube(pixels, maxColors);
1✔
295
        cube.classification();
1✔
296
        cube.reduction();
1✔
297
        cube.assignment();
1✔
298
        return cube.colormap;
1✔
299
    }
300

301
    /**
302
     * The Class Cube.
303
     */
304
    static class Cube {
305

306
        /** The pixels. */
307
        int[][] pixels;
308

309
        /** The max colors. */
310
        int maxColors;
311

312
        /** The colormap. */
313
        int[] colormap;
314

315
        /** The root. */
316
        Node root;
317

318
        /** The depth. */
319
        int depth;
320

321
        // counter for the number of colors in the cube. this gets
322
        /** The colors. */
323
        // recalculated often.
324
        int colors;
325

326
        /** The nodes. */
327
        // counter for the number of nodes in the tree
328
        int nodes;
329

330
        /**
331
         * Instantiates a new cube.
332
         *
333
         * @param pixels
334
         *            the pixels
335
         * @param maxColors
336
         *            the max colors
337
         */
338
        Cube(int[][] pixels, int maxColors) {
1✔
339
            this.pixels = pixels;
1✔
340
            this.maxColors = maxColors;
1✔
341

342
            int i = maxColors;
1✔
343
            // tree_depth = log max_colors
344
            // 4
345
            for (depth = 1; i != 0; depth++) {
1✔
346
                i /= 4;
1✔
347
            }
348
            if (depth > 1) {
1!
349
                --depth;
1✔
350
            }
351
            if (depth > MAX_TREE_DEPTH) {
1!
352
                depth = MAX_TREE_DEPTH;
×
353
            } else if (depth < 2) {
1!
354
                depth = 2;
×
355
            }
356

357
            root = new Node(this);
1✔
358
        }
1✔
359

360
        /**
361
         * Procedure Classification begins by initializing a color description tree of sufficient depth to represent
362
         * each possible input color in a leaf. However, it is impractical to generate a fully-formed color description
363
         * tree in the classification phase for realistic values of cmax. If colors components in the input image are
364
         * quantized to k-bit precision, so that cmax= 2k-1, the tree would need k levels below the root node to allow
365
         * representing each possible input color in a leaf. This becomes prohibitive because the tree's total number of
366
         * nodes is 1 + sum(i=1,k,8k).
367
         * <p>
368
         * A complete tree would require 19,173,961 nodes for k = 8, cmax = 255. Therefore, to avoid building a fully
369
         * populated tree, QUANTIZE: (1) Initializes data structures for nodes only as they are needed; (2) Chooses a
370
         * maximum depth for the tree as a function of the desired number of colors in the output image (currently
371
         * log2(colormap size)).
372
         * <p>
373
         * For each pixel in the input image, classification scans downward from the root of the color description tree.
374
         * At each level of the tree it identifies the single node which represents a cube in RGB space containing It
375
         * updates the following data for each such node:
376
         * <p>
377
         * number_pixels : Number of pixels whose color is contained in the RGB cube which this node represents;
378
         * <p>
379
         * unique : Number of pixels whose color is not represented in a node at lower depth in the tree; initially, n2
380
         * = 0 for all nodes except leaves of the tree.
381
         * <p>
382
         * total_red/green/blue : Sums of the red, green, and blue component values for all pixels not classified at a
383
         * lower depth. The combination of these sums and n2 will ultimately characterize the mean color of a set of
384
         * pixels represented by this node.
385
         */
386
        void classification() {
387
            int[][] pixels = this.pixels;
1✔
388

389
            int width = pixels.length;
1✔
390
            int height = pixels[0].length;
1✔
391

392
            // convert to indexed color
393
            for (int x = width; x-- > 0;) {
1✔
394
                for (int y = height; y-- > 0;) {
1✔
395
                    int pixel = pixels[x][y];
1✔
396
                    int red = pixel >> 16 & 0xFF;
1✔
397
                    int green = pixel >> 8 & 0xFF;
1✔
398
                    int blue = pixel >> 0 & 0xFF;
1✔
399

400
                    // a hard limit on the number of nodes in the tree
401
                    if (nodes > MAX_NODES) {
1!
402
                        System.out.println("pruning");
×
403
                        root.pruneLevel();
×
404
                        --depth;
×
405
                    }
406

407
                    // walk the tree to depth, increasing the
408
                    // number_pixels count for each node
409
                    Node node = root;
1✔
410
                    for (int level = 1; level <= depth; ++level) {
1✔
411
                        int id = (red > node.midRed ? 1 : 0) << 0 | (green > node.midGreen ? 1 : 0) << 1
1✔
412
                                | (blue > node.midBlue ? 1 : 0) << 2;
1✔
413
                        if (node.child[id] == null) {
1✔
414
                            new Node(node, id, level);
1✔
415
                        }
416
                        node = node.child[id];
1✔
417
                        node.numberPixels += SHIFT[level];
1✔
418
                    }
419

420
                    ++node.unique;
1✔
421
                    node.totalRed += red;
1✔
422
                    node.totalGreen += green;
1✔
423
                    node.totalBlue += blue;
1✔
424
                }
1✔
425
            }
426
        }
1✔
427

428
        /**
429
         * Reduction.
430
         * <p>
431
         * reduction repeatedly prunes the tree until the number of nodes with unique > 0 is less than or equal to the
432
         * maximum number of colors allowed in the output image.
433
         * <p>
434
         * When a node to be pruned has offspring, the pruning procedure invokes itself recursively in order to prune
435
         * the tree from the leaves upward. The statistics of the node being pruned are always added to the
436
         * corresponding data in that node's parent. This retains the pruned node's color characteristics for later
437
         * averaging.
438
         */
439
        void reduction() {
440
            int threshold = 1;
1✔
441
            while (colors > maxColors) {
1!
442
                colors = 0;
×
443
                threshold = root.reduce(threshold, Integer.MAX_VALUE);
×
444
            }
445
        }
1✔
446

447
        /**
448
         * The result of a closest color search.
449
         */
450
        static class Search {
1✔
451

452
            /** The distance. */
453
            int distance;
454

455
            /** The color number. */
456
            int colorNumber;
457
        }
458

459
        /**
460
         * Assignment.
461
         * <p>
462
         * Procedure assignment generates the output image from the pruned tree. The output image consists of two parts:
463
         * (1) A color map, which is an array of color descriptions (RGB triples) for each color present in the output
464
         * image; (2) A pixel array, which represents each pixel as an index into the color map array.
465
         * <p>
466
         * First, the assignment phase makes one pass over the pruned color description tree to establish the image's
467
         * color map. For each node with n2 > 0, it divides Sr, Sg, and Sb by n2. This produces the mean color of all
468
         * pixels that classify no lower than this node. Each of these colors becomes an entry in the color map.
469
         * <p>
470
         * Finally, the assignment phase reclassifies each pixel in the pruned tree to identify the deepest node
471
         * containing the pixel's color. The pixel's value in the pixel array becomes the index of this node's mean
472
         * color in the color map.
473
         */
474
        void assignment() {
475
            colormap = new int[colors];
1✔
476

477
            colors = 0;
1✔
478
            root.colormap();
1✔
479

480
            int[][] pixels = this.pixels;
1✔
481

482
            int width = pixels.length;
1✔
483
            int height = pixels[0].length;
1✔
484

485
            Search search = new Search();
1✔
486

487
            // convert to indexed color
488
            for (int x = width; x-- > 0;) {
1✔
489
                for (int y = height; y-- > 0;) {
1✔
490
                    int pixel = pixels[x][y];
1✔
491
                    int red = pixel >> 16 & 0xFF;
1✔
492
                    int green = pixel >> 8 & 0xFF;
1✔
493
                    int blue = pixel >> 0 & 0xFF;
1✔
494

495
                    // walk the tree to find the cube containing that color
496
                    Node node = root;
1✔
497
                    for (;;) {
498
                        int id = (red > node.midRed ? 1 : 0) << 0 | (green > node.midGreen ? 1 : 0) << 1
1✔
499
                                | (blue > node.midBlue ? 1 : 0) << 2;
1✔
500
                        if (node.child[id] == null) {
1✔
501
                            break;
1✔
502
                        }
503
                        node = node.child[id];
1✔
504
                    }
1✔
505

506
                    if (QUICK) {
507
                        // if QUICK is set, just use that
508
                        // node. Strictly speaking, this isn't
509
                        // necessarily best match.
510
                        pixels[x][y] = node.colorNumber;
511
                    } else {
512
                        // Find the closest color.
513
                        search.distance = Integer.MAX_VALUE;
1✔
514
                        node.parent.closestColor(red, green, blue, search);
1✔
515
                        pixels[x][y] = search.colorNumber;
1✔
516
                    }
517
                }
1✔
518
            }
519
        }
1✔
520

521
        /**
522
         * A single Node in the tree.
523
         */
524
        static class Node {
525

526
            /** The cube. */
527
            Cube cube;
528

529
            /** The parent. */
530
            // parent node
531
            Node parent;
532

533
            /** The child. */
534
            // child nodes
535
            Node[] child;
536

537
            /** The nchild. */
538
            int nchild;
539

540
            /** The id. */
541
            // our index within our parent
542
            int id;
543

544
            /** The level. */
545
            // our level within the tree
546
            int level;
547

548
            /** The mid red. */
549
            // our color midpoint
550
            int midRed;
551

552
            /** The mid green. */
553
            int midGreen;
554

555
            /** The mid blue. */
556
            int midBlue;
557

558
            /** The number pixels. */
559
            // the pixel count for this node and all children
560
            int numberPixels;
561

562
            /** The unique. */
563
            // the pixel count for this node
564
            int unique;
565

566
            /** The total red. */
567
            // the sum of all pixels contained in this node
568
            int totalRed;
569

570
            /** The total green. */
571
            int totalGreen;
572

573
            /** The total blue. */
574
            int totalBlue;
575

576
            /** The color number. */
577
            // used to build the colormap
578
            int colorNumber;
579

580
            /**
581
             * Instantiates a new node.
582
             *
583
             * @param cube
584
             *            the cube
585
             */
586
            Node(Cube cube) {
1✔
587
                this.cube = cube;
1✔
588
                this.parent = this;
1✔
589
                this.child = new Node[8];
1✔
590
                this.id = 0;
1✔
591
                this.level = 0;
1✔
592

593
                this.numberPixels = Integer.MAX_VALUE;
1✔
594

595
                this.midRed = (MAX_RGB + 1) >> 1;
1✔
596
                this.midGreen = (MAX_RGB + 1) >> 1;
1✔
597
                this.midBlue = (MAX_RGB + 1) >> 1;
1✔
598
            }
1✔
599

600
            /**
601
             * Instantiates a new node.
602
             *
603
             * @param parent
604
             *            the parent
605
             * @param id
606
             *            the id
607
             * @param level
608
             *            the level
609
             */
610
            Node(Node parent, int id, int level) {
1✔
611
                this.cube = parent.cube;
1✔
612
                this.parent = parent;
1✔
613
                this.child = new Node[8];
1✔
614
                this.id = id;
1✔
615
                this.level = level;
1✔
616

617
                // add to the cube
618
                ++cube.nodes;
1✔
619
                if (level == cube.depth) {
1✔
620
                    ++cube.colors;
1✔
621
                }
622

623
                // add to the parent
624
                ++parent.nchild;
1✔
625
                parent.child[id] = this;
1✔
626

627
                // figure out our midpoint
628
                int bi = 1 << (MAX_TREE_DEPTH - level) >> 1;
1✔
629
                midRed = parent.midRed + ((id & 1) > 0 ? bi : -bi);
1✔
630
                midGreen = parent.midGreen + ((id & 2) > 0 ? bi : -bi);
1✔
631
                midBlue = parent.midBlue + ((id & 4) > 0 ? bi : -bi);
1✔
632
            }
1✔
633

634
            /**
635
             * Remove this child node, and make sure our parent absorbs our pixel statistics.
636
             */
637
            void pruneChild() {
638
                --parent.nchild;
×
639
                parent.unique += unique;
×
640
                parent.totalRed += totalRed;
×
641
                parent.totalGreen += totalGreen;
×
642
                parent.totalBlue += totalBlue;
×
643
                parent.child[id] = null;
×
644
                --cube.nodes;
×
645
                cube = null;
×
646
                parent = null;
×
647
            }
×
648

649
            /**
650
             * Prune the lowest layer of the tree.
651
             */
652
            void pruneLevel() {
653
                if (nchild != 0) {
×
654
                    for (int i = 0; i < 8; i++) {
×
655
                        if (child[i] != null) {
×
656
                            child[i].pruneLevel();
×
657
                        }
658
                    }
659
                }
660
                if (level == cube.depth) {
×
661
                    pruneChild();
×
662
                }
663
            }
×
664

665
            /**
666
             * Remove any nodes that have fewer than threshold pixels. Also, as long as we're walking the tree:
667
             *
668
             * <pre>
669
             *  - figure out the color with the fewest pixels
670
             *  - recalculate the total number of colors in the tree
671
             * </pre>
672
             *
673
             * @param threshold
674
             *            the threshold
675
             * @param nextThreshold
676
             *            the next threshold
677
             *
678
             * @return the int
679
             */
680
            int reduce(int threshold, int nextThreshold) {
681
                if (nchild != 0) {
×
682
                    for (int i = 0; i < 8; i++) {
×
683
                        if (child[i] != null) {
×
684
                            nextThreshold = child[i].reduce(threshold, nextThreshold);
×
685
                        }
686
                    }
687
                }
688
                if (numberPixels <= threshold) {
×
689
                    pruneChild();
×
690
                } else {
691
                    if (unique != 0) {
×
692
                        cube.colors++;
×
693
                    }
694
                    if (numberPixels < nextThreshold) {
×
695
                        nextThreshold = numberPixels;
×
696
                    }
697
                }
698
                return nextThreshold;
×
699
            }
700

701
            /**
702
             * Colormap.
703
             * <p>
704
             * colormap traverses the color cube tree and notes each colormap entry. A colormap entry is any node in the
705
             * color cube tree where the number of unique colors is not zero.
706
             */
707
            void colormap() {
708
                if (nchild != 0) {
1✔
709
                    for (int i = 0; i < 8; i++) {
1✔
710
                        if (child[i] != null) {
1✔
711
                            child[i].colormap();
1✔
712
                        }
713
                    }
714
                }
715
                if (unique != 0) {
1✔
716
                    int r = (totalRed + (unique >> 1)) / unique;
1✔
717
                    int g = (totalGreen + (unique >> 1)) / unique;
1✔
718
                    int b = (totalBlue + (unique >> 1)) / unique;
1✔
719
                    cube.colormap[cube.colors] = 0xFF << 24 | (r & 0xFF) << 16 | (g & 0xFF) << 8 | (b & 0xFF) << 0;
1✔
720
                    colorNumber = cube.colors++;
1✔
721
                }
722
            }
1✔
723

724
            /**
725
             * Closest color.
726
             * <p>
727
             * ClosestColor traverses the color cube tree at a particular node and determines which colormap entry best
728
             * represents the input color.
729
             *
730
             * @param red
731
             *            the red
732
             * @param green
733
             *            the green
734
             * @param blue
735
             *            the blue
736
             * @param search
737
             *            the search
738
             */
739
            void closestColor(int red, int green, int blue, Search search) {
740
                if (nchild != 0) {
1✔
741
                    for (int i = 0; i < 8; i++) {
1✔
742
                        if (child[i] != null) {
1✔
743
                            child[i].closestColor(red, green, blue, search);
1✔
744
                        }
745
                    }
746
                }
747

748
                if (unique != 0) {
1✔
749
                    int color = cube.colormap[colorNumber];
1✔
750
                    int distance = distance(color, red, green, blue);
1✔
751
                    if (distance < search.distance) {
1✔
752
                        search.distance = distance;
1✔
753
                        search.colorNumber = colorNumber;
1✔
754
                    }
755
                }
756
            }
1✔
757

758
            /**
759
             * Figure out the distance between this node and som color.
760
             *
761
             * @param color
762
             *            the color
763
             * @param r
764
             *            the r
765
             * @param g
766
             *            the g
767
             * @param b
768
             *            the b
769
             *
770
             * @return the int
771
             */
772
            static final int distance(int color, int r, int g, int b) {
773
                return SQUARES[(color >> 16 & 0xFF) - r + MAX_RGB] + SQUARES[(color >> 8 & 0xFF) - g + MAX_RGB]
1✔
774
                        + SQUARES[(color >> 0 & 0xFF) - b + MAX_RGB];
775
            }
776

777
            @Override
778
            public String toString() {
779
                StringBuilder buf = new StringBuilder();
×
780
                if (parent == this) {
×
781
                    buf.append("root");
×
782
                } else {
783
                    buf.append("node");
×
784
                }
785
                buf.append(' ');
×
786
                buf.append(level);
×
787
                buf.append(" [");
×
788
                buf.append(midRed);
×
789
                buf.append(',');
×
790
                buf.append(midGreen);
×
791
                buf.append(',');
×
792
                buf.append(midBlue);
×
793
                buf.append(']');
×
794
                return new String(buf);
×
795
            }
796
        }
797
    }
798
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc