git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@9178 75d07b2b-3a1a-0410-a2c5-0572b91ccdca3.0
parent
8af8d87752
commit
3d22756dfc
@ -0,0 +1,304 @@ |
|||||||
|
package com.jme3.scene.plugins.blender.textures; |
||||||
|
|
||||||
|
import java.nio.ByteBuffer; |
||||||
|
import java.util.logging.Logger; |
||||||
|
|
||||||
|
import jme3tools.converters.RGB565; |
||||||
|
|
||||||
|
import com.jme3.math.FastMath; |
||||||
|
import com.jme3.texture.Image; |
||||||
|
import com.jme3.texture.Image.Format; |
||||||
|
import com.jme3.util.BufferUtils; |
||||||
|
|
||||||
|
/** |
||||||
|
* This class decompresses the given image (if necessary) to the RGBA8 format. |
||||||
|
* Currently supported compressed textures are: DXT1, DXT3, DXT5. |
||||||
|
* @author Marcin Roguski (Kaelthas) |
||||||
|
*/ |
||||||
|
/*package*/ class TextureDecompressor { |
||||||
|
private static final Logger LOGGER = Logger.getLogger(TextureDecompressor.class.getName()); |
||||||
|
|
||||||
|
/** |
||||||
|
* This method decompresses the given image. If the given image is already |
||||||
|
* decompressed nothing happens and it is simply returned. |
||||||
|
* |
||||||
|
* @param image |
||||||
|
* the image to decompress |
||||||
|
* @return the decompressed image |
||||||
|
*/ |
||||||
|
public static Image decompress(Image image) {//TODO: support 3D textures
|
||||||
|
byte[] bytes = null; |
||||||
|
TexturePixel[] colors = null; |
||||||
|
ByteBuffer data = image.getData(0); |
||||||
|
data.rewind(); |
||||||
|
Format format = image.getFormat(); |
||||||
|
|
||||||
|
DDSTexelData texelData = new DDSTexelData(data.remaining() / (format.getBitsPerPixel() * 2), image.getWidth(), image.getHeight(), format != Format.DXT1); |
||||||
|
switch (format) {// TODO: DXT1A
|
||||||
|
case DXT1:// BC1
|
||||||
|
bytes = new byte[image.getWidth() * image.getHeight() * 4]; |
||||||
|
colors = new TexturePixel[] { new TexturePixel(), new TexturePixel(), new TexturePixel(), new TexturePixel() }; |
||||||
|
while (data.hasRemaining()) { |
||||||
|
short c0 = data.getShort(); |
||||||
|
short c1 = data.getShort(); |
||||||
|
int col0 = RGB565.RGB565_to_ARGB8(c0); |
||||||
|
int col1 = RGB565.RGB565_to_ARGB8(c1); |
||||||
|
colors[0].fromARGB8(col0); |
||||||
|
colors[1].fromARGB8(col1); |
||||||
|
|
||||||
|
if (col0 > col1) { |
||||||
|
// creating color2 = 2/3color0 + 1/3color1
|
||||||
|
colors[2].fromPixel(colors[0]); |
||||||
|
colors[2].mult(2); |
||||||
|
colors[2].add(colors[1]); |
||||||
|
colors[2].divide(3); |
||||||
|
|
||||||
|
// creating color3 = 1/3color0 + 2/3color1;
|
||||||
|
colors[3].fromPixel(colors[1]); |
||||||
|
colors[3].mult(2); |
||||||
|
colors[3].add(colors[0]); |
||||||
|
colors[3].divide(3); |
||||||
|
} else { |
||||||
|
// creating color2 = 1/2color0 + 1/2color1
|
||||||
|
colors[2].fromPixel(colors[0]); |
||||||
|
colors[2].add(colors[1]); |
||||||
|
colors[2].mult(0.5f); |
||||||
|
|
||||||
|
colors[3].fromARGB8(0); |
||||||
|
} |
||||||
|
int indexes = data.getInt();// 4-byte table with color indexes in decompressed table
|
||||||
|
texelData.add(colors, indexes); |
||||||
|
} |
||||||
|
break; |
||||||
|
case DXT3:// BC2
|
||||||
|
bytes = new byte[image.getWidth() * image.getHeight() * 4]; |
||||||
|
colors = new TexturePixel[] { new TexturePixel(), new TexturePixel(), new TexturePixel(), new TexturePixel() }; |
||||||
|
while (data.hasRemaining()) { |
||||||
|
long alpha = data.getLong(); |
||||||
|
float[] alphas = new float[16]; |
||||||
|
long alphasIndex = 0; |
||||||
|
for (int i = 0; i < 16; ++i) { |
||||||
|
alphasIndex |= i << (i * 4); |
||||||
|
byte a = (byte) (((alpha >> (i * 4)) & 0x0F) << 4); |
||||||
|
alphas[i] = a >= 0 ? a / 255.0f : 1.0f - (~a) / 255.0f; |
||||||
|
} |
||||||
|
|
||||||
|
short c0 = data.getShort(); |
||||||
|
short c1 = data.getShort(); |
||||||
|
int col0 = RGB565.RGB565_to_ARGB8(c0); |
||||||
|
int col1 = RGB565.RGB565_to_ARGB8(c1); |
||||||
|
colors[0].fromARGB8(col0); |
||||||
|
colors[1].fromARGB8(col1); |
||||||
|
|
||||||
|
// creating color2 = 2/3color0 + 1/3color1
|
||||||
|
colors[2].fromPixel(colors[0]); |
||||||
|
colors[2].mult(2); |
||||||
|
colors[2].add(colors[1]); |
||||||
|
colors[2].divide(3); |
||||||
|
|
||||||
|
// creating color3 = 1/3color0 + 2/3color1;
|
||||||
|
colors[3].fromPixel(colors[1]); |
||||||
|
colors[3].mult(2); |
||||||
|
colors[3].add(colors[0]); |
||||||
|
colors[3].divide(3); |
||||||
|
|
||||||
|
int indexes = data.getInt();// 4-byte table with color indexes in decompressed table
|
||||||
|
texelData.add(colors, indexes, alphas, alphasIndex); |
||||||
|
} |
||||||
|
break; |
||||||
|
case DXT5:// BC3
|
||||||
|
bytes = new byte[image.getWidth() * image.getHeight() * 4]; |
||||||
|
colors = new TexturePixel[] { new TexturePixel(), new TexturePixel(), new TexturePixel(), new TexturePixel() }; |
||||||
|
float[] alphas = new float[8]; |
||||||
|
while (data.hasRemaining()) { |
||||||
|
alphas[0] = data.get() * 255.0f; |
||||||
|
alphas[1] = data.get() * 255.0f; |
||||||
|
long alphaIndices = (int) data.get() | ((int) data.get() << 8) | ((int) data.get() << 16) | ((int) data.get() << 24) | ((int) data.get() << 32) | ((int) data.get() << 40); |
||||||
|
if (alphas[0] > alphas[1]) {// 6 interpolated alpha values.
|
||||||
|
alphas[2] = (6 * alphas[0] + alphas[1]) / 7; |
||||||
|
alphas[3] = (5 * alphas[0] + 2 * alphas[1]) / 7; |
||||||
|
alphas[4] = (4 * alphas[0] + 3 * alphas[1]) / 7; |
||||||
|
alphas[5] = (3 * alphas[0] + 4 * alphas[1]) / 7; |
||||||
|
alphas[6] = (2 * alphas[0] + 5 * alphas[1]) / 7; |
||||||
|
alphas[7] = (alphas[0] + 6 * alphas[1]) / 7; |
||||||
|
} else { |
||||||
|
alphas[2] = (4 * alphas[0] + alphas[1]) * 0.2f; |
||||||
|
alphas[3] = (3 * alphas[0] + 2 * alphas[1]) * 0.2f; |
||||||
|
alphas[4] = (2 * alphas[0] + 3 * alphas[1]) * 0.2f; |
||||||
|
alphas[5] = (alphas[0] + 4 * alphas[1]) * 0.2f; |
||||||
|
alphas[6] = 0; |
||||||
|
alphas[7] = 1; |
||||||
|
} |
||||||
|
|
||||||
|
short c0 = data.getShort(); |
||||||
|
short c1 = data.getShort(); |
||||||
|
int col0 = RGB565.RGB565_to_ARGB8(c0); |
||||||
|
int col1 = RGB565.RGB565_to_ARGB8(c1); |
||||||
|
colors[0].fromARGB8(col0); |
||||||
|
colors[1].fromARGB8(col1); |
||||||
|
|
||||||
|
// creating color2 = 2/3color0 + 1/3color1
|
||||||
|
colors[2].fromPixel(colors[0]); |
||||||
|
colors[2].mult(2); |
||||||
|
colors[2].add(colors[1]); |
||||||
|
colors[2].divide(3); |
||||||
|
|
||||||
|
// creating color3 = 1/3color0 + 2/3color1;
|
||||||
|
colors[3].fromPixel(colors[1]); |
||||||
|
colors[3].mult(2); |
||||||
|
colors[3].add(colors[0]); |
||||||
|
colors[3].divide(3); |
||||||
|
|
||||||
|
int indexes = data.getInt();// 4-byte table with color
|
||||||
|
// indexes in decompressed table
|
||||||
|
texelData.add(colors, indexes, alphas, alphaIndices); |
||||||
|
} |
||||||
|
break; |
||||||
|
default: |
||||||
|
LOGGER.fine("Unsupported decompression format."); |
||||||
|
} |
||||||
|
|
||||||
|
if (bytes != null) {// writing the data to the result table
|
||||||
|
byte[] pixelBytes = new byte[4]; |
||||||
|
for (int i = 0; i < image.getWidth(); ++i) { |
||||||
|
for (int j = 0; j < image.getHeight(); ++j) { |
||||||
|
texelData.getRGBA8(i, j, pixelBytes); |
||||||
|
bytes[(j * image.getWidth() + i) * 4] = pixelBytes[0]; |
||||||
|
bytes[(j * image.getWidth() + i) * 4 + 1] = pixelBytes[1]; |
||||||
|
bytes[(j * image.getWidth() + i) * 4 + 2] = pixelBytes[2]; |
||||||
|
bytes[(j * image.getWidth() + i) * 4 + 3] = pixelBytes[3]; |
||||||
|
} |
||||||
|
} |
||||||
|
return new Image(Format.RGBA8, image.getWidth(), image.getHeight(), BufferUtils.createByteBuffer(bytes)); |
||||||
|
} |
||||||
|
return image; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* The data that helps in bytes calculations for the result image. |
||||||
|
* |
||||||
|
* @author Marcin Roguski (Kaelthas) |
||||||
|
*/ |
||||||
|
private static class DDSTexelData { |
||||||
|
/** The colors of the texes. */ |
||||||
|
private TexturePixel[][] colors; |
||||||
|
/** The indexes of the texels. */ |
||||||
|
private long[] indexes; |
||||||
|
/** The alphas of the texels (might be null). */ |
||||||
|
private float[][] alphas; |
||||||
|
/** The indexels of texels alpha values (might be null). */ |
||||||
|
private long[] alphaIndexes; |
||||||
|
/** The counter of texel x column. */ |
||||||
|
private int xCounter; |
||||||
|
/** The counter of texel y row. */ |
||||||
|
private int yCounter; |
||||||
|
/** The width of the image in pixels. */ |
||||||
|
private int pixelWidth; |
||||||
|
/** The height of the image in pixels. */ |
||||||
|
private int pixelHeight; |
||||||
|
/** The total texel count. */ |
||||||
|
private int xTexelCount; |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor. Allocates the required memory. Initializes variables. |
||||||
|
* |
||||||
|
* @param textelsCount |
||||||
|
* the total count of the texels |
||||||
|
* @param pixelWidth |
||||||
|
* the width of the image in pixels |
||||||
|
* @param pixelHeight |
||||||
|
* the height of the image in pixels |
||||||
|
* @param isAlpha |
||||||
|
* indicates if the memory for alpha values should be |
||||||
|
* allocated |
||||||
|
*/ |
||||||
|
public DDSTexelData(int textelsCount, int pixelWidth, int pixelHeight, boolean isAlpha) { |
||||||
|
textelsCount = (pixelWidth * pixelHeight) >> 4; |
||||||
|
this.colors = new TexturePixel[textelsCount][]; |
||||||
|
this.indexes = new long[textelsCount]; |
||||||
|
this.xTexelCount = pixelWidth >> 2; |
||||||
|
this.yCounter = (pixelHeight >> 2) - 1;// xCounter is 0 for now
|
||||||
|
this.pixelHeight = pixelHeight; |
||||||
|
this.pixelWidth = pixelWidth; |
||||||
|
if (isAlpha) { |
||||||
|
this.alphas = new float[textelsCount][]; |
||||||
|
this.alphaIndexes = new long[textelsCount]; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* This method adds a color and indexes for a texel. |
||||||
|
* |
||||||
|
* @param colors |
||||||
|
* the colors of the texel |
||||||
|
* @param indexes |
||||||
|
* the indexes of the texel |
||||||
|
*/ |
||||||
|
public void add(TexturePixel[] colors, int indexes) { |
||||||
|
this.add(colors, indexes, null, 0); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* This method adds a color, color indexes and alha values (with their |
||||||
|
* indexes) for a texel. |
||||||
|
* |
||||||
|
* @param colors |
||||||
|
* the colors of the texel |
||||||
|
* @param indexes |
||||||
|
* the indexes of the texel |
||||||
|
* @param alphas |
||||||
|
* the alpha values |
||||||
|
* @param alphaIndexes |
||||||
|
* the indexes of the given alpha values |
||||||
|
*/ |
||||||
|
public void add(TexturePixel[] colors, int indexes, float[] alphas, long alphaIndexes) { |
||||||
|
int index = yCounter * xTexelCount + xCounter; |
||||||
|
this.colors[index] = colors; |
||||||
|
this.indexes[index] = indexes; |
||||||
|
if (alphas != null) { |
||||||
|
this.alphas[index] = alphas; |
||||||
|
this.alphaIndexes[index] = alphaIndexes; |
||||||
|
} |
||||||
|
++this.xCounter; |
||||||
|
if (this.xCounter >= this.xTexelCount) { |
||||||
|
this.xCounter = 0; |
||||||
|
--this.yCounter; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* This method returns the values of the pixel located on the given |
||||||
|
* coordinates on the result image. |
||||||
|
* |
||||||
|
* @param x |
||||||
|
* the x coordinate of the pixel |
||||||
|
* @param y |
||||||
|
* the y coordinate of the pixel |
||||||
|
* @param result |
||||||
|
* the table where the result is stored |
||||||
|
*/ |
||||||
|
public void getRGBA8(int x, int y, byte[] result) { |
||||||
|
int xTexetlIndex = (x % pixelWidth) / 4; |
||||||
|
int yTexelIndex = (y % pixelHeight) / 4; |
||||||
|
|
||||||
|
int texelIndex = yTexelIndex * xTexelCount + xTexetlIndex; |
||||||
|
TexturePixel[] colors = this.colors[texelIndex]; |
||||||
|
|
||||||
|
// coordinates of the pixel in the selected texel
|
||||||
|
x = x - 4 * xTexetlIndex;// pixels are arranged from left to right
|
||||||
|
y = 3 - y - 4 * yTexelIndex;// pixels are arranged from bottom to top (that is why '3 - ...' is at the start)
|
||||||
|
|
||||||
|
int pixelIndexInTexel = (y * 4 + x) * (int) FastMath.log(colors.length, 2); |
||||||
|
int alphaIndexInTexel = alphas != null ? (y * 4 + x) * (int) FastMath.log(alphas.length, 2) : 0; |
||||||
|
|
||||||
|
// getting the pixel
|
||||||
|
int indexMask = colors.length - 1; |
||||||
|
int colorIndex = (int) ((this.indexes[texelIndex] >> pixelIndexInTexel) & indexMask); |
||||||
|
float alpha = this.alphas != null ? this.alphas[texelIndex][(int) ((this.alphaIndexes[texelIndex] >> alphaIndexInTexel) & 0x07)] : colors[colorIndex].alpha; |
||||||
|
result[0] = (byte) (colors[colorIndex].red * 255.0f); |
||||||
|
result[1] = (byte) (colors[colorIndex].green * 255.0f); |
||||||
|
result[2] = (byte) (colors[colorIndex].blue * 255.0f); |
||||||
|
result[3] = (byte) (alpha * 255.0f); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue