I have the following method, that reduces the palette size in a PNG image.
private BufferedImage setColour(BufferedImage image) {
IndexColorModel cm = new IndexColorModel(
3,
6,
new byte[]{-100, 0, 0, 0, -1, 0}, // r
new byte[]{0, -100, 60, 0, -1, 0}, // g
new byte[]{0, 0, 0, -100, -1, 0}); // b
BufferedImage img = new BufferedImage(
image.getWidth(), image.getHeight(),
BufferedImage.TYPE_BYTE_INDEXED,
cm);
Graphics2D g2 = img.createGraphics();
g2.drawImage(image, 0, 0, null);
g2.dispose();
return img;
}
I understand that each byte array is used for indexing the palette associated with a PNG pixel. What I don't understand, is how to set the valu开发者_开发知识库es in the byte arrays so that I could only slightly reduce the colour number in the PNG image.
The indexed color model essentially lists all colors that can be used in the BufferedImage, with their RGB values.
Each column in
// red, light green, dark green, blue, white, black
new byte[]{-100, 0, 0, 0, -1, 0}, // red part
new byte[]{ 0, -100, 60, 0, -1, 0}, // green part
new byte[]{ 0, 0, 0, -100, -1, 0} // blue part
corresponds to one color - I added them in the comment line.
Only these colors will finally be used in the resulting image, everything else will be approximated by the nearest color, or a pattern from the two nearest colors:
(Image stolen from Rekin's answer.)
If you want to reduce to not only 6 colors, but a larger number, you simply have to list more of them. You could either use a predefined list here, or adapt them from statistics of the original image (e.g. if the image consists mainly of blue sky, include several blue hues here, but not so much of the other colors). Or select between several predefined palettes, looking which gives the smallest difference to the original image.
Instead using IndexedColorModel, you could also use one of the other ColorModel implementations. For example, this would be a 64-color space with 4 transparency levels, equally distributed (I think), using one byte per pixel (two bits per color):
new DirectColorModel(8, 0xC0, 0x30, 0x0C, 0x03);
This one would use only one bit for transparency, instead using three bits for green (which human eyes can see more clearly):
new DirectColorModel(8, 0xC0, 0x38, 0x06, 0x01);
I did not test them together with the PNG encoder, please do this.
精彩评论