• 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

96.72
/src/main/java/org/carrot2/util/ColorQuantizer.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 org.carrot2.util;
9

10
import java.awt.Color;
11
import java.awt.image.BufferedImage;
12
import java.awt.image.DataBuffer;
13
import java.awt.image.IndexColorModel;
14
import java.awt.image.WritableRaster;
15

16
import amd.Quantize;
17

18
/**
19
 * A simple utility wrapping the {@link Quantize} class to work on {@link BufferedImage}s and handle transparency.
20
 */
21
public class ColorQuantizer {
22

23
    /** Maximum number of colors in an indexed image, leaving one for transparency. */
24
    public static final int MAX_INDEXED_COLORS = 255;
25

26
    /**
27
     * Instantiates a new color quantizer.
28
     */
29
    private ColorQuantizer() {
30
        // Prevent Instantiation
31
    }
32

33
    /**
34
     * Quantizes the image to {@link #MAX_INDEXED_COLORS} with white matte for areas with partial transparency (full
35
     * transparency will be preserved).
36
     *
37
     * @param source
38
     *            the source
39
     *
40
     * @return {@link BufferedImage} with type {@link BufferedImage#TYPE_BYTE_INDEXED} and quantized colors
41
     */
42
    public static BufferedImage quantize(BufferedImage source) {
43
        return quantize(source, Color.WHITE);
1✔
44
    }
45

46
    /**
47
     * Quantizes the image to {@link #MAX_INDEXED_COLORS} with the provided matte {@link Color} for areas with partial
48
     * transparency (full transparency will be preserved).
49
     *
50
     * @param source
51
     *            the source
52
     * @param matteColor
53
     *            the matte color
54
     *
55
     * @return {@link BufferedImage} with type {@link BufferedImage#TYPE_BYTE_INDEXED} and quantized colors
56
     */
57
    public static BufferedImage quantize(BufferedImage source, Color matteColor) {
58
        return quantize(source, matteColor, MAX_INDEXED_COLORS);
1✔
59
    }
60

61
    /**
62
     * Quantizes the image to the provided number of colors with the provided matte {@link Color} for areas with partial
63
     * transparency (full transparency will be preserved).
64
     *
65
     * @param source
66
     *            the source
67
     * @param matteColor
68
     *            the matte color
69
     * @param maxColors
70
     *            the max colors
71
     *
72
     * @return {@link BufferedImage} with type {@link BufferedImage#TYPE_BYTE_INDEXED} and quantized colors
73
     */
74
    public static BufferedImage quantize(BufferedImage source, Color matteColor, int maxColors) {
75
        final int width = source.getWidth();
1✔
76
        final int height = source.getHeight();
1✔
77

78
        // First put the matte color so that we have a sensible result
79
        // for images with full alpha transparencies
80
        final BufferedImage mattedSource = BufferedImageUtils.matte(source, matteColor);
1✔
81

82
        // Get two copies of RGB data (quantization will overwrite one)
83
        final int[][] bitmap = BufferedImageUtils.getRgb(mattedSource);
1✔
84

85
        // Quantize colors and shift palette by one for transparency color
86
        // We'll keep transparency color black for now.
87
        final int[] colors = Quantize.quantizeImage(bitmap, maxColors);
1✔
88
        final int[] colorsWithAlpha = new int[colors.length + 1];
1✔
89
        System.arraycopy(colors, 0, colorsWithAlpha, 1, colors.length);
1✔
90
        colorsWithAlpha[0] = matteColor.getRGB();
1✔
91
        final IndexColorModel colorModel = new IndexColorModel(8, colorsWithAlpha.length, colorsWithAlpha, 0, false, 0,
1✔
92
                DataBuffer.TYPE_BYTE);
93

94
        // Write the results to an indexed image, skipping the fully transparent bits
95
        final BufferedImage quantized = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_INDEXED, colorModel);
1✔
96
        final WritableRaster raster = quantized.getRaster();
1✔
97
        final int[][] rgb = BufferedImageUtils.getRgb(source);
1✔
98
        for (int x = 0; x < width; x++) {
1✔
99
            for (int y = 0; y < height; y++) {
1✔
100
                final int value = (rgb[x][y] & 0xff000000) != 0x00000000 ? bitmap[x][y] + 1 : 0;
1✔
101
                raster.setPixel(x, y, new int[] { value });
1✔
102
            }
103
        }
104

105
        return quantized;
1✔
106
    }
107

108
    /**
109
     * Reduces a direct color buffered image to an indexed color one without quality loss. To make sure no quality loss
110
     * will occur, check the results of the {@link #getColorReductionInfo(BufferedImage)} method call.
111
     *
112
     * @param source
113
     *            the source
114
     *
115
     * @return the buffered image
116
     *
117
     * @throws IllegalArgumentException
118
     *             if the application of this method would result in image quality loss
119
     */
120
    public static BufferedImage reduce(BufferedImage source) {
121
        final int width = source.getWidth();
1✔
122
        final int height = source.getHeight();
1✔
123

124
        if (BufferedImageUtils.hasPartialTransparency(source)) {
1!
125
            throw new IllegalArgumentException("The source image cannot contain translucent areas");
×
126
        }
127

128
        final int[] colorsWithAlpha = BufferedImageUtils.getDistinctColors(source, 1);
1✔
129
        if (colorsWithAlpha.length - 1 > MAX_INDEXED_COLORS) {
1✔
130
            throw new IllegalArgumentException(
1✔
131
                    "The source image cannot contain more than " + MAX_INDEXED_COLORS + " colors");
132
        }
133

134
        final IndexColorModel colorModel = new IndexColorModel(8, colorsWithAlpha.length, colorsWithAlpha, 0, false, 0,
1✔
135
                DataBuffer.TYPE_BYTE);
136

137
        // Write the results to an indexed image, skipping the fully transparent bits
138
        final BufferedImage quantized = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_INDEXED, colorModel);
1✔
139
        final int[][] rgb = BufferedImageUtils.getRgb(source);
1✔
140

141
        for (int x = 0; x < width; x++) {
1✔
142
            for (int y = 0; y < height; y++) {
1✔
143
                if ((rgb[x][y] & 0xff000000) != 0x00000000) {
1✔
144
                    quantized.setRGB(x, y, source.getRGB(x, y));
1✔
145
                }
146
            }
147
        }
148

149
        return quantized;
1✔
150
    }
151

152
    /**
153
     * Returns a {@link ColorReductionInfo} for the provided image.
154
     *
155
     * @param source
156
     *            the source
157
     *
158
     * @return the color reduction info
159
     */
160
    public static ColorReductionInfo getColorReductionInfo(BufferedImage source) {
161
        return new ColorReductionInfo(BufferedImageUtils.hasPartialTransparency(source),
1✔
162
                BufferedImageUtils.countDistinctColors(source));
1✔
163
    }
164

165
    /**
166
     * Indicates how many distinct colors an image has, whether it has partial transparency (alpha channel).
167
     */
168
    public static class ColorReductionInfo {
169

170
        /** Number of distinct colors in the image. */
171
        public int distinctColors;
172

173
        /** True if the image has partially transparent areas (alpha channel). */
174
        public boolean hasPartialTransparency;
175

176
        /**
177
         * Instantiates a new color reduction info.
178
         *
179
         * @param hasPartialTransparency
180
         *            the has partial transparency
181
         * @param distinctColors
182
         *            the distinct colors
183
         */
184
        public ColorReductionInfo(boolean hasPartialTransparency, int distinctColors) {
1✔
185
            this.hasPartialTransparency = hasPartialTransparency;
1✔
186
            this.distinctColors = distinctColors;
1✔
187
        }
1✔
188

189
        /**
190
         * Returns true if the image can be saved in a 8-bit indexed color format with 1-bit transparency without
191
         * quality loss.
192
         *
193
         * @return true, if successful
194
         */
195
        public boolean canReduceWithoutQualityLoss() {
196
            return !hasPartialTransparency && distinctColors <= MAX_INDEXED_COLORS;
1✔
197
        }
198
    }
199
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc