blob: 897c9eb1ffc6a7c9afc2446a49efc10f1f650b45 [file] [log] [blame]
/*
* Copyright (c) 2009-2010 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.jme3.texture.plugins;
import com.jme3.asset.AssetInfo;
import com.jme3.asset.AssetLoader;
import com.jme3.asset.TextureKey;
import com.jme3.texture.Image;
import com.jme3.texture.Image.Format;
import com.jme3.texture.Texture.Type;
import com.jme3.util.BufferUtils;
import com.jme3.util.LittleEndien;
import java.io.DataInput;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
*
* <code>DDSLoader</code> is an image loader that reads in a DirectX DDS file.
* Supports DXT1, DXT3, DXT5, RGB, RGBA, Grayscale, Alpha pixel formats.
* 2D images, mipmapped 2D images, and cubemaps.
*
* @author Gareth Jenkins-Jones
* @author Kirill Vainer
* @version $Id: DDSLoader.java,v 2.0 2008/8/15
*/
public class DDSLoader implements AssetLoader {
private static final Logger logger = Logger.getLogger(DDSLoader.class.getName());
private static final boolean forceRGBA = false;
private static final int DDSD_MANDATORY = 0x1007;
private static final int DDSD_MANDATORY_DX10 = 0x6;
private static final int DDSD_MIPMAPCOUNT = 0x20000;
private static final int DDSD_LINEARSIZE = 0x80000;
private static final int DDSD_DEPTH = 0x800000;
private static final int DDPF_ALPHAPIXELS = 0x1;
private static final int DDPF_FOURCC = 0x4;
private static final int DDPF_RGB = 0x40;
// used by compressonator to mark grayscale images, red channel mask is used for data and bitcount is 8
private static final int DDPF_GRAYSCALE = 0x20000;
// used by compressonator to mark alpha images, alpha channel mask is used for data and bitcount is 8
private static final int DDPF_ALPHA = 0x2;
// used by NVTextureTools to mark normal images.
private static final int DDPF_NORMAL = 0x80000000;
private static final int SWIZZLE_xGxR = 0x78477852;
private static final int DDSCAPS_COMPLEX = 0x8;
private static final int DDSCAPS_TEXTURE = 0x1000;
private static final int DDSCAPS_MIPMAP = 0x400000;
private static final int DDSCAPS2_CUBEMAP = 0x200;
private static final int DDSCAPS2_VOLUME = 0x200000;
private static final int PF_DXT1 = 0x31545844;
private static final int PF_DXT3 = 0x33545844;
private static final int PF_DXT5 = 0x35545844;
private static final int PF_ATI1 = 0x31495441;
private static final int PF_ATI2 = 0x32495441; // 0x41544932;
private static final int PF_DX10 = 0x30315844; // a DX10 format
private static final int DX10DIM_BUFFER = 0x1,
DX10DIM_TEXTURE1D = 0x2,
DX10DIM_TEXTURE2D = 0x3,
DX10DIM_TEXTURE3D = 0x4;
private static final int DX10MISC_GENERATE_MIPS = 0x1,
DX10MISC_TEXTURECUBE = 0x4;
private static final double LOG2 = Math.log(2);
private int width;
private int height;
private int depth;
private int flags;
private int pitchOrSize;
private int mipMapCount;
private int caps1;
private int caps2;
private boolean directx10;
private boolean compressed;
private boolean texture3D;
private boolean grayscaleOrAlpha;
private boolean normal;
private Format pixelFormat;
private int bpp;
private int[] sizes;
private int redMask, greenMask, blueMask, alphaMask;
private DataInput in;
public DDSLoader() {
}
public Object load(AssetInfo info) throws IOException {
if (!(info.getKey() instanceof TextureKey)) {
throw new IllegalArgumentException("Texture assets must be loaded using a TextureKey");
}
InputStream stream = null;
try {
stream = info.openStream();
in = new LittleEndien(stream);
loadHeader();
if (texture3D) {
((TextureKey) info.getKey()).setTextureTypeHint(Type.ThreeDimensional);
} else if (depth > 1) {
((TextureKey) info.getKey()).setTextureTypeHint(Type.CubeMap);
}
ArrayList<ByteBuffer> data = readData(((TextureKey) info.getKey()).isFlipY());
return new Image(pixelFormat, width, height, depth, data, sizes);
} finally {
if (stream != null){
stream.close();
}
}
}
public Image load(InputStream stream) throws IOException {
in = new LittleEndien(stream);
loadHeader();
ArrayList<ByteBuffer> data = readData(false);
return new Image(pixelFormat, width, height, depth, data, sizes);
}
private void loadDX10Header() throws IOException {
int dxgiFormat = in.readInt();
if (dxgiFormat != 83) {
throw new IOException("Only DXGI_FORMAT_BC5_UNORM "
+ "is supported for DirectX10 DDS! Got: " + dxgiFormat);
}
pixelFormat = Format.LATC;
bpp = 8;
compressed = true;
int resDim = in.readInt();
if (resDim == DX10DIM_TEXTURE3D) {
texture3D = true;
}
int miscFlag = in.readInt();
int arraySize = in.readInt();
if (is(miscFlag, DX10MISC_TEXTURECUBE)) {
// mark texture as cube
if (arraySize != 6) {
throw new IOException("Cubemaps should consist of 6 images!");
}
}
in.skipBytes(4); // skip reserved value
}
/**
* Reads the header (first 128 bytes) of a DDS File
*/
private void loadHeader() throws IOException {
if (in.readInt() != 0x20534444 || in.readInt() != 124) {
throw new IOException("Not a DDS file");
}
flags = in.readInt();
if (!is(flags, DDSD_MANDATORY) && !is(flags, DDSD_MANDATORY_DX10)) {
throw new IOException("Mandatory flags missing");
}
height = in.readInt();
width = in.readInt();
pitchOrSize = in.readInt();
depth = in.readInt();
mipMapCount = in.readInt();
in.skipBytes(44);
pixelFormat = null;
directx10 = false;
readPixelFormat();
caps1 = in.readInt();
caps2 = in.readInt();
in.skipBytes(12);
texture3D = false;
if (!directx10) {
if (!is(caps1, DDSCAPS_TEXTURE)) {
throw new IOException("File is not a texture");
}
if (depth <= 0) {
depth = 1;
}
if (is(caps2, DDSCAPS2_CUBEMAP)) {
depth = 6; // somewhat of a hack, force loading 6 textures if a cubemap
}
if (is(caps2, DDSCAPS2_VOLUME)) {
texture3D = true;
}
}
int expectedMipmaps = 1 + (int) Math.ceil(Math.log(Math.max(height, width)) / LOG2);
if (is(caps1, DDSCAPS_MIPMAP)) {
if (!is(flags, DDSD_MIPMAPCOUNT)) {
mipMapCount = expectedMipmaps;
} else if (mipMapCount != expectedMipmaps) {
// changed to warning- images often do not have the required amount,
// or specify that they have mipmaps but include only the top level..
logger.log(Level.WARNING, "Got {0} mipmaps, expected {1}",
new Object[]{mipMapCount, expectedMipmaps});
}
} else {
mipMapCount = 1;
}
if (directx10) {
loadDX10Header();
}
loadSizes();
}
/**
* Reads the PixelFormat structure in a DDS file
*/
private void readPixelFormat() throws IOException {
int pfSize = in.readInt();
if (pfSize != 32) {
throw new IOException("Pixel format size is " + pfSize + ", not 32");
}
int pfFlags = in.readInt();
normal = is(pfFlags, DDPF_NORMAL);
if (is(pfFlags, DDPF_FOURCC)) {
compressed = true;
int fourcc = in.readInt();
int swizzle = in.readInt();
in.skipBytes(16);
switch (fourcc) {
case PF_DXT1:
bpp = 4;
if (is(pfFlags, DDPF_ALPHAPIXELS)) {
pixelFormat = Image.Format.DXT1A;
} else {
pixelFormat = Image.Format.DXT1;
}
break;
case PF_DXT3:
bpp = 8;
pixelFormat = Image.Format.DXT3;
break;
case PF_DXT5:
bpp = 8;
pixelFormat = Image.Format.DXT5;
if (swizzle == SWIZZLE_xGxR) {
normal = true;
}
break;
case PF_ATI1:
bpp = 4;
pixelFormat = Image.Format.LTC;
break;
case PF_ATI2:
bpp = 8;
pixelFormat = Image.Format.LATC;
break;
case PF_DX10:
compressed = false;
directx10 = true;
// exit here, the rest of the structure is not valid
// the real format will be available in the DX10 header
return;
default:
throw new IOException("Unknown fourcc: " + string(fourcc) + ", " + Integer.toHexString(fourcc));
}
int size = ((width + 3) / 4) * ((height + 3) / 4) * bpp * 2;
if (is(flags, DDSD_LINEARSIZE)) {
if (pitchOrSize == 0) {
logger.warning("Must use linear size with fourcc");
pitchOrSize = size;
} else if (pitchOrSize != size) {
logger.log(Level.WARNING, "Expected size = {0}, real = {1}",
new Object[]{size, pitchOrSize});
}
} else {
pitchOrSize = size;
}
} else {
compressed = false;
// skip fourCC
in.readInt();
bpp = in.readInt();
redMask = in.readInt();
greenMask = in.readInt();
blueMask = in.readInt();
alphaMask = in.readInt();
if (is(pfFlags, DDPF_RGB)) {
if (is(pfFlags, DDPF_ALPHAPIXELS)) {
pixelFormat = Format.RGBA8;
} else {
pixelFormat = Format.RGB8;
}
} else if (is(pfFlags, DDPF_GRAYSCALE) && is(pfFlags, DDPF_ALPHAPIXELS)) {
switch (bpp) {
case 16:
pixelFormat = Format.Luminance8Alpha8;
break;
case 32:
pixelFormat = Format.Luminance16Alpha16;
break;
default:
throw new IOException("Unsupported GrayscaleAlpha BPP: " + bpp);
}
grayscaleOrAlpha = true;
} else if (is(pfFlags, DDPF_GRAYSCALE)) {
switch (bpp) {
case 8:
pixelFormat = Format.Luminance8;
break;
case 16:
pixelFormat = Format.Luminance16;
break;
default:
throw new IOException("Unsupported Grayscale BPP: " + bpp);
}
grayscaleOrAlpha = true;
} else if (is(pfFlags, DDPF_ALPHA)) {
switch (bpp) {
case 8:
pixelFormat = Format.Alpha8;
break;
case 16:
pixelFormat = Format.Alpha16;
break;
default:
throw new IOException("Unsupported Alpha BPP: " + bpp);
}
grayscaleOrAlpha = true;
} else {
throw new IOException("Unknown PixelFormat in DDS file");
}
int size = (bpp / 8 * width);
if (is(flags, DDSD_LINEARSIZE)) {
if (pitchOrSize == 0) {
logger.warning("Linear size said to contain valid value but does not");
pitchOrSize = size;
} else if (pitchOrSize != size) {
logger.log(Level.WARNING, "Expected size = {0}, real = {1}",
new Object[]{size, pitchOrSize});
}
} else {
pitchOrSize = size;
}
}
}
/**
* Computes the sizes of each mipmap level in bytes, and stores it in sizes_[].
*/
private void loadSizes() {
int mipWidth = width;
int mipHeight = height;
sizes = new int[mipMapCount];
int outBpp = pixelFormat.getBitsPerPixel();
for (int i = 0; i < mipMapCount; i++) {
int size;
if (compressed) {
size = ((mipWidth + 3) / 4) * ((mipHeight + 3) / 4) * outBpp * 2;
} else {
size = mipWidth * mipHeight * outBpp / 8;
}
sizes[i] = ((size + 3) / 4) * 4;
mipWidth = Math.max(mipWidth / 2, 1);
mipHeight = Math.max(mipHeight / 2, 1);
}
}
/**
* Flips the given image data on the Y axis.
* @param data Data array containing image data (without mipmaps)
* @param scanlineSize Size of a single scanline = width * bytesPerPixel
* @param height Height of the image in pixels
* @return The new data flipped by the Y axis
*/
public byte[] flipData(byte[] data, int scanlineSize, int height) {
byte[] newData = new byte[data.length];
for (int y = 0; y < height; y++) {
System.arraycopy(data, y * scanlineSize,
newData, (height - y - 1) * scanlineSize,
scanlineSize);
}
return newData;
}
/**
* Reads a grayscale image with mipmaps from the InputStream
* @param flip Flip the loaded image by Y axis
* @param totalSize Total size of the image in bytes including the mipmaps
* @return A ByteBuffer containing the grayscale image data with mips.
* @throws java.io.IOException If an error occured while reading from InputStream
*/
public ByteBuffer readGrayscale2D(boolean flip, int totalSize) throws IOException {
ByteBuffer buffer = BufferUtils.createByteBuffer(totalSize);
if (bpp == 8) {
logger.finest("Source image format: R8");
}
assert bpp == pixelFormat.getBitsPerPixel();
int mipWidth = width;
int mipHeight = height;
for (int mip = 0; mip < mipMapCount; mip++) {
byte[] data = new byte[sizes[mip]];
in.readFully(data);
if (flip) {
data = flipData(data, mipWidth * bpp / 8, mipHeight);
}
buffer.put(data);
mipWidth = Math.max(mipWidth / 2, 1);
mipHeight = Math.max(mipHeight / 2, 1);
}
return buffer;
}
/**
* Reads an uncompressed RGB or RGBA image.
*
* @param flip Flip the image on the Y axis
* @param totalSize Size of the image in bytes including mipmaps
* @return ByteBuffer containing image data with mipmaps in the format specified by pixelFormat_
* @throws java.io.IOException If an error occured while reading from InputStream
*/
public ByteBuffer readRGB2D(boolean flip, int totalSize) throws IOException {
int redCount = count(redMask),
blueCount = count(blueMask),
greenCount = count(greenMask),
alphaCount = count(alphaMask);
if (redMask == 0x00FF0000 && greenMask == 0x0000FF00 && blueMask == 0x000000FF) {
if (alphaMask == 0xFF000000 && bpp == 32) {
logger.finest("Data source format: BGRA8");
} else if (bpp == 24) {
logger.finest("Data source format: BGR8");
}
}
int sourcebytesPP = bpp / 8;
int targetBytesPP = pixelFormat.getBitsPerPixel() / 8;
ByteBuffer dataBuffer = BufferUtils.createByteBuffer(totalSize);
int mipWidth = width;
int mipHeight = height;
int offset = 0;
byte[] b = new byte[sourcebytesPP];
for (int mip = 0; mip < mipMapCount; mip++) {
for (int y = 0; y < mipHeight; y++) {
for (int x = 0; x < mipWidth; x++) {
in.readFully(b);
int i = byte2int(b);
byte red = (byte) (((i & redMask) >> redCount));
byte green = (byte) (((i & greenMask) >> greenCount));
byte blue = (byte) (((i & blueMask) >> blueCount));
byte alpha = (byte) (((i & alphaMask) >> alphaCount));
if (flip) {
dataBuffer.position(offset + ((mipHeight - y - 1) * mipWidth + x) * targetBytesPP);
}
//else
// dataBuffer.position(offset + (y * width + x) * targetBytesPP);
if (alphaMask == 0) {
dataBuffer.put(red).put(green).put(blue);
} else {
dataBuffer.put(red).put(green).put(blue).put(alpha);
}
}
}
offset += mipWidth * mipHeight * targetBytesPP;
mipWidth = Math.max(mipWidth / 2, 1);
mipHeight = Math.max(mipHeight / 2, 1);
}
return dataBuffer;
}
/**
* Reads a DXT compressed image from the InputStream
*
* @param totalSize Total size of the image in bytes, including mipmaps
* @return ByteBuffer containing compressed DXT image in the format specified by pixelFormat_
* @throws java.io.IOException If an error occured while reading from InputStream
*/
public ByteBuffer readDXT2D(boolean flip, int totalSize) throws IOException {
logger.finest("Source image format: DXT");
ByteBuffer buffer = BufferUtils.createByteBuffer(totalSize);
int mipWidth = width;
int mipHeight = height;
for (int mip = 0; mip < mipMapCount; mip++) {
if (flip) {
byte[] data = new byte[sizes[mip]];
in.readFully(data);
ByteBuffer wrapped = ByteBuffer.wrap(data);
wrapped.rewind();
ByteBuffer flipped = DXTFlipper.flipDXT(wrapped, mipWidth, mipHeight, pixelFormat);
buffer.put(flipped);
} else {
byte[] data = new byte[sizes[mip]];
in.readFully(data);
buffer.put(data);
}
mipWidth = Math.max(mipWidth / 2, 1);
mipHeight = Math.max(mipHeight / 2, 1);
}
buffer.rewind();
return buffer;
}
/**
* Reads a grayscale image with mipmaps from the InputStream
* @param flip Flip the loaded image by Y axis
* @param totalSize Total size of the image in bytes including the mipmaps
* @return A ByteBuffer containing the grayscale image data with mips.
* @throws java.io.IOException If an error occured while reading from InputStream
*/
public ByteBuffer readGrayscale3D(boolean flip, int totalSize) throws IOException {
ByteBuffer buffer = BufferUtils.createByteBuffer(totalSize * depth);
if (bpp == 8) {
logger.finest("Source image format: R8");
}
assert bpp == pixelFormat.getBitsPerPixel();
for (int i = 0; i < depth; i++) {
int mipWidth = width;
int mipHeight = height;
for (int mip = 0; mip < mipMapCount; mip++) {
byte[] data = new byte[sizes[mip]];
in.readFully(data);
if (flip) {
data = flipData(data, mipWidth * bpp / 8, mipHeight);
}
buffer.put(data);
mipWidth = Math.max(mipWidth / 2, 1);
mipHeight = Math.max(mipHeight / 2, 1);
}
}
buffer.rewind();
return buffer;
}
/**
* Reads an uncompressed RGB or RGBA image.
*
* @param flip Flip the image on the Y axis
* @param totalSize Size of the image in bytes including mipmaps
* @return ByteBuffer containing image data with mipmaps in the format specified by pixelFormat_
* @throws java.io.IOException If an error occured while reading from InputStream
*/
public ByteBuffer readRGB3D(boolean flip, int totalSize) throws IOException {
int redCount = count(redMask),
blueCount = count(blueMask),
greenCount = count(greenMask),
alphaCount = count(alphaMask);
if (redMask == 0x00FF0000 && greenMask == 0x0000FF00 && blueMask == 0x000000FF) {
if (alphaMask == 0xFF000000 && bpp == 32) {
logger.finest("Data source format: BGRA8");
} else if (bpp == 24) {
logger.finest("Data source format: BGR8");
}
}
int sourcebytesPP = bpp / 8;
int targetBytesPP = pixelFormat.getBitsPerPixel() / 8;
ByteBuffer dataBuffer = BufferUtils.createByteBuffer(totalSize * depth);
for (int k = 0; k < depth; k++) {
// ByteBuffer dataBuffer = BufferUtils.createByteBuffer(totalSize);
int mipWidth = width;
int mipHeight = height;
int offset = k * totalSize;
byte[] b = new byte[sourcebytesPP];
for (int mip = 0; mip < mipMapCount; mip++) {
for (int y = 0; y < mipHeight; y++) {
for (int x = 0; x < mipWidth; x++) {
in.readFully(b);
int i = byte2int(b);
byte red = (byte) (((i & redMask) >> redCount));
byte green = (byte) (((i & greenMask) >> greenCount));
byte blue = (byte) (((i & blueMask) >> blueCount));
byte alpha = (byte) (((i & alphaMask) >> alphaCount));
if (flip) {
dataBuffer.position(offset + ((mipHeight - y - 1) * mipWidth + x) * targetBytesPP);
}
//else
// dataBuffer.position(offset + (y * width + x) * targetBytesPP);
if (alphaMask == 0) {
dataBuffer.put(red).put(green).put(blue);
} else {
dataBuffer.put(red).put(green).put(blue).put(alpha);
}
}
}
offset += (mipWidth * mipHeight * targetBytesPP);
mipWidth = Math.max(mipWidth / 2, 1);
mipHeight = Math.max(mipHeight / 2, 1);
}
}
dataBuffer.rewind();
return dataBuffer;
}
/**
* Reads a DXT compressed image from the InputStream
*
* @param totalSize Total size of the image in bytes, including mipmaps
* @return ByteBuffer containing compressed DXT image in the format specified by pixelFormat_
* @throws java.io.IOException If an error occured while reading from InputStream
*/
public ByteBuffer readDXT3D(boolean flip, int totalSize) throws IOException {
logger.finest("Source image format: DXT");
ByteBuffer bufferAll = BufferUtils.createByteBuffer(totalSize * depth);
for (int i = 0; i < depth; i++) {
ByteBuffer buffer = BufferUtils.createByteBuffer(totalSize);
int mipWidth = width;
int mipHeight = height;
for (int mip = 0; mip < mipMapCount; mip++) {
if (flip) {
byte[] data = new byte[sizes[mip]];
in.readFully(data);
ByteBuffer wrapped = ByteBuffer.wrap(data);
wrapped.rewind();
ByteBuffer flipped = DXTFlipper.flipDXT(wrapped, mipWidth, mipHeight, pixelFormat);
flipped.rewind();
buffer.put(flipped);
} else {
byte[] data = new byte[sizes[mip]];
in.readFully(data);
buffer.put(data);
}
mipWidth = Math.max(mipWidth / 2, 1);
mipHeight = Math.max(mipHeight / 2, 1);
}
buffer.rewind();
bufferAll.put(buffer);
}
return bufferAll;
}
/**
* Reads the image data from the InputStream in the required format.
* If the file contains a cubemap image, it is loaded as 6 ByteBuffers
* (potentially containing mipmaps if they were specified), otherwise
* a single ByteBuffer is returned for a 2D image.
*
* @param flip Flip the image data or not.
* For cubemaps, each of the cubemap faces is flipped individually.
* If the image is DXT compressed, no flipping is done.
* @return An ArrayList containing a single ByteBuffer for a 2D image, or 6 ByteBuffers for a cubemap.
* The cubemap ByteBuffer order is PositiveX, NegativeX, PositiveY, NegativeY, PositiveZ, NegativeZ.
*
* @throws java.io.IOException If an error occured while reading from the stream.
*/
public ArrayList<ByteBuffer> readData(boolean flip) throws IOException {
int totalSize = 0;
for (int i = 0; i < sizes.length; i++) {
totalSize += sizes[i];
}
ArrayList<ByteBuffer> allMaps = new ArrayList<ByteBuffer>();
if (depth > 1 && !texture3D) {
for (int i = 0; i < depth; i++) {
if (compressed) {
allMaps.add(readDXT2D(flip, totalSize));
} else if (grayscaleOrAlpha) {
allMaps.add(readGrayscale2D(flip, totalSize));
} else {
allMaps.add(readRGB2D(flip, totalSize));
}
}
} else if (texture3D) {
if (compressed) {
allMaps.add(readDXT3D(flip, totalSize));
} else if (grayscaleOrAlpha) {
allMaps.add(readGrayscale3D(flip, totalSize));
} else {
allMaps.add(readRGB3D(flip, totalSize));
}
} else {
if (compressed) {
allMaps.add(readDXT2D(flip, totalSize));
} else if (grayscaleOrAlpha) {
allMaps.add(readGrayscale2D(flip, totalSize));
} else {
allMaps.add(readRGB2D(flip, totalSize));
}
}
return allMaps;
}
/**
* Checks if flags contains the specified mask
*/
private static boolean is(int flags, int mask) {
return (flags & mask) == mask;
}
/**
* Counts the amount of bits needed to shift till bitmask n is at zero
* @param n Bitmask to test
*/
private static int count(int n) {
if (n == 0) {
return 0;
}
int i = 0;
while ((n & 0x1) == 0) {
n = n >> 1;
i++;
if (i > 32) {
throw new RuntimeException(Integer.toHexString(n));
}
}
return i;
}
/**
* Converts a 1 to 4 sized byte array to an integer
*/
private static int byte2int(byte[] b) {
if (b.length == 1) {
return b[0] & 0xFF;
} else if (b.length == 2) {
return (b[0] & 0xFF) | ((b[1] & 0xFF) << 8);
} else if (b.length == 3) {
return (b[0] & 0xFF) | ((b[1] & 0xFF) << 8) | ((b[2] & 0xFF) << 16);
} else if (b.length == 4) {
return (b[0] & 0xFF) | ((b[1] & 0xFF) << 8) | ((b[2] & 0xFF) << 16) | ((b[3] & 0xFF) << 24);
} else {
return 0;
}
}
/**
* Converts a int representing a FourCC into a String
*/
private static String string(int value) {
StringBuilder buf = new StringBuilder();
buf.append((char) (value & 0xFF));
buf.append((char) ((value & 0xFF00) >> 8));
buf.append((char) ((value & 0xFF0000) >> 16));
buf.append((char) ((value & 0xFF00000) >> 24));
return buf.toString();
}
}