/******************************************************************************* * Copyright 2012 Geoscience Australia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package au.gov.ga.earthsci.worldwind.common.util; import java.awt.Dimension; import java.awt.image.BufferedImage; import java.nio.ByteBuffer; import java.nio.ByteOrder; /** * Created on Jun 12, 2009 @ 11:39:16 AM. * * @author joel-cohen */ public class DDSUncompressor { private static final int DDPF_FOURCC = 0x0004; private static final int DDSCAPS_TEXTURE = 0x1000; protected static class Color { private int r, g, b; public Color() { this.r = this.g = this.b = 0; } public Color(int r, int g, int b) { this.r = r; this.g = g; this.b = b; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } final Color color = (Color) o; if (b != color.b) { return false; } if (g != color.g) { return false; } //noinspection RedundantIfStatement if (r != color.r) { return false; } return true; } @Override public int hashCode() { int result; result = r; result = 29 * result + g; result = 29 * result + b; return result; } } protected static Dimension readHeaderDxt3(ByteBuffer buffer) { buffer.rewind(); byte[] magic = new byte[4]; buffer.get(magic); assert new String(magic).equals("DDS "); int version = buffer.getInt(); assert version == 124; /*int flags =*/buffer.getInt(); int height = buffer.getInt(); int width = buffer.getInt(); /*int pixels =*/buffer.getInt(); // ??? /*int depth =*/buffer.getInt(); /*int mipmaps =*/buffer.getInt(); buffer.position(buffer.position() + 44); // 11 unused double-words /*int pixelFormatSize =*/buffer.getInt(); // ??? int fourCC = buffer.getInt(); assert fourCC == DDPF_FOURCC; byte[] format = new byte[4]; buffer.get(format); assert new String(format).equals("DXT3"); /*int bpp =*/buffer.getInt(); // bits per pixel for RGB (non-compressed) formats buffer.getInt(); // rgb bit masks for RGB formats buffer.getInt(); // rgb bit masks for RGB formats buffer.getInt(); // rgb bit masks for RGB formats buffer.getInt(); // alpha mask for RGB formats int unknown = buffer.getInt(); assert unknown == DDSCAPS_TEXTURE; /*int ddsCaps =*/buffer.getInt(); // ??? buffer.position(buffer.position() + 12); return new Dimension(width, height); } public static BufferedImage readDxt3(ByteBuffer buffer) { buffer.order(ByteOrder.LITTLE_ENDIAN); Dimension dimension = readHeaderDxt3(buffer); return readDxt3Buffer(buffer, dimension.width, dimension.height); } public static BufferedImage readDxt3Buffer(ByteBuffer buffer, int width, int height) { buffer.order(ByteOrder.LITTLE_ENDIAN); int[] pixels = new int[16]; int[] alphas = new int[16]; BufferedImage result = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB_PRE); int numTilesWide = width / 4; int numTilesHigh = height / 4; for (int i = 0; i < numTilesHigh; i++) { for (int j = 0; j < numTilesWide; j++) { // Read the alpha table. long alphaData = buffer.getLong(); for (int k = alphas.length - 1; k >= 0; k--) { alphas[k] = (int) (alphaData >>> (k * 4)) & 0xF; // Alphas are just 4 bits per pixel alphas[k] <<= 4; } short minColor = buffer.getShort(); short maxColor = buffer.getShort(); Color[] lookupTable = expandLookupTable(minColor, maxColor); int colorData = buffer.getInt(); for (int k = pixels.length - 1; k >= 0; k--) { int colorCode = (colorData >>> k * 2) & 0x03; pixels[k] = (alphas[k] << 24) | getPixel888(multiplyAlpha(lookupTable[colorCode], alphas[k])); } result.setRGB(j * 4, i * 4, 4, 4, pixels, 0, 4); } } return result; } private static Color multiplyAlpha(Color color, int alpha) { Color result = new Color(); double alphaF = alpha / 256.0; result.r = (int) (color.r * alphaF); result.g = (int) (color.g * alphaF); result.b = (int) (color.b * alphaF); return result; } protected static Color getColor565(int pixel) { Color color = new Color(); color.r = (int) (((long) pixel) & 0xf800) >>> 8; color.g = (int) (((long) pixel) & 0x07e0) >>> 3; color.b = (int) (((long) pixel) & 0x001f) << 3; return color; } private static Color[] expandLookupTable(short minColor, short maxColor) { Color[] result = new Color[] { getColor565(minColor), getColor565(maxColor), new Color(), new Color() }; result[2].r = (2 * result[0].r + result[1].r + 1) / 3; result[2].g = (2 * result[0].g + result[1].g + 1) / 3; result[2].b = (2 * result[0].b + result[1].b + 1) / 3; result[3].r = (result[0].r + 2 * result[1].r + 1) / 3; result[3].g = (result[0].g + 2 * result[1].g + 1) / 3; result[3].b = (result[0].b + 2 * result[1].b + 1) / 3; return result; } protected static int getPixel888(Color color) { int r = color.r; int g = color.g; int b = color.b; return r << 16 | g << 8 | b; } }