+ * Copyright (c) 2015 Kenji Sasaki + * Released under the MIT license. + * https://github.com/npedotnet/DDSReader/blob/master/LICENSE + *
+ * English document + * https://github.com/npedotnet/DDSReader/blob/master/README.md + *
+ * Japanese document
+ * http://3dtech.jp/wiki/index.php?DDSReader
+ */
+
+package com.twelvemonkeys.imageio.plugins.dds;
+
+import javax.imageio.IIOException;
+import javax.imageio.stream.ImageInputStream;
+import java.io.IOException;
+
+final class DDSReader {
+
+ public static final Order order = new Order(16, 8, 0, 24);
+
+ private final DDSHeader header;
+
+
+ public DDSReader(DDSHeader header) {
+ this.header = header;
+ }
+
+ public int[] read(ImageInputStream imageInput, int imageIndex) throws IOException {
+
+ // type
+ DDSType type = getType();
+
+ // offset buffer to index mipmap image
+ byte[] buffer = null;
+ for (int i = 0; i <= imageIndex; i++) {
+ int len = getLength(type, i);
+ buffer = new byte[len];
+ imageInput.readFully(buffer);
+ }
+
+ int width = header.getWidth(imageIndex);
+ int height = header.getHeight(imageIndex);
+
+ switch (type) {
+ case DXT1:
+ return decodeDXT1(width, height, buffer);
+ case DXT2:
+ return decodeDXT2(width, height, buffer);
+ case DXT3:
+ return decodeDXT3(width, height, buffer);
+ case DXT4:
+ return decodeDXT4(width, height, buffer);
+ case DXT5:
+ return decodeDXT5(width, height, buffer);
+ case A1R5G5B5:
+ return readA1R5G5B5(width, height, buffer);
+ case X1R5G5B5:
+ return readX1R5G5B5(width, height, buffer);
+ case A4R4G4B4:
+ return readA4R4G4B4(width, height, buffer);
+ case X4R4G4B4:
+ return readX4R4G4B4(width, height, buffer);
+ case R5G6B5:
+ return readR5G6B5(width, height, buffer);
+ case R8G8B8:
+ return readR8G8B8(width, height, buffer);
+ case A8B8G8R8:
+ return readA8B8G8R8(width, height, buffer);
+ case X8B8G8R8:
+ return readX8B8G8R8(width, height, buffer);
+ case A8R8G8B8:
+ return readA8R8G8B8(width, height, buffer);
+ case X8R8G8B8:
+ return readX8R8G8B8(width, height, buffer);
+ default:
+ throw new IIOException("Unsupported type: " + type);
+ }
+ }
+
+ private DDSType getType() throws IIOException {
+ int flags = header.getPixelFormatFlags();
+
+ if ((flags & 0x04) != 0) {
+ // DXT
+ int type = header.getFourCC();
+ return DDSType.parse(type);
+ } else if ((flags & 0x40) != 0) {
+ // RGB
+ int bitCount = header.getBitCount();
+ int redMask = header.getRedMask();
+ int greenMask = header.getGreenMask();
+ int blueMask = header.getBlueMask();
+ int alphaMask = ((flags & 0x01) != 0) ? header.getAlphaMask() : 0; // 0x01 alpha
+ if (bitCount == 16) {
+ if (redMask == A1R5G5B5_MASKS[0] && greenMask == A1R5G5B5_MASKS[1] && blueMask == A1R5G5B5_MASKS[2] && alphaMask == A1R5G5B5_MASKS[3]) {
+ // A1R5G5B5
+ return DDSType.A1R5G5B5;
+ } else if (redMask == X1R5G5B5_MASKS[0] && greenMask == X1R5G5B5_MASKS[1] && blueMask == X1R5G5B5_MASKS[2] && alphaMask == X1R5G5B5_MASKS[3]) {
+ // X1R5G5B5
+ return DDSType.X1R5G5B5;
+ } else if (redMask == A4R4G4B4_MASKS[0] && greenMask == A4R4G4B4_MASKS[1] && blueMask == A4R4G4B4_MASKS[2] && alphaMask == A4R4G4B4_MASKS[3]) {
+ // A4R4G4B4
+ return DDSType.A4R4G4B4;
+ } else if (redMask == X4R4G4B4_MASKS[0] && greenMask == X4R4G4B4_MASKS[1] && blueMask == X4R4G4B4_MASKS[2] && alphaMask == X4R4G4B4_MASKS[3]) {
+ // X4R4G4B4
+ return DDSType.X4R4G4B4;
+ } else if (redMask == R5G6B5_MASKS[0] && greenMask == R5G6B5_MASKS[1] && blueMask == R5G6B5_MASKS[2] && alphaMask == R5G6B5_MASKS[3]) {
+ // R5G6B5
+ return DDSType.R5G6B5;
+ } else {
+ throw new IIOException("Unsupported 16bit RGB image.");
+ }
+ } else if (bitCount == 24) {
+ if (redMask == R8G8B8_MASKS[0] && greenMask == R8G8B8_MASKS[1] && blueMask == R8G8B8_MASKS[2] && alphaMask == R8G8B8_MASKS[3]) {
+ // R8G8B8
+ return DDSType.R8G8B8;
+ } else {
+ throw new IIOException("Unsupported 24bit RGB image.");
+ }
+ } else if (bitCount == 32) {
+ if (redMask == A8B8G8R8_MASKS[0] && greenMask == A8B8G8R8_MASKS[1] && blueMask == A8B8G8R8_MASKS[2] && alphaMask == A8B8G8R8_MASKS[3]) {
+ // A8B8G8R8
+ return DDSType.A8B8G8R8;
+ } else if (redMask == X8B8G8R8_MASKS[0] && greenMask == X8B8G8R8_MASKS[1] && blueMask == X8B8G8R8_MASKS[2] && alphaMask == X8B8G8R8_MASKS[3]) {
+ // X8B8G8R8
+ return DDSType.X8B8G8R8;
+ } else if (redMask == A8R8G8B8_MASKS[0] && greenMask == A8R8G8B8_MASKS[1] && blueMask == A8R8G8B8_MASKS[2] && alphaMask == A8R8G8B8_MASKS[3]) {
+ // A8R8G8B8
+ return DDSType.A8R8G8B8;
+ } else if (redMask == X8R8G8B8_MASKS[0] && greenMask == X8R8G8B8_MASKS[1] && blueMask == X8R8G8B8_MASKS[2] && alphaMask == X8R8G8B8_MASKS[3]) {
+ // X8R8G8B8
+ return DDSType.X8R8G8B8;
+ } else {
+ throw new IIOException("Unsupported 32bit RGB image.");
+ }
+ } else {
+ throw new IIOException("Unsupported bit count: " + bitCount);
+ }
+ } else {
+ throw new IIOException("Unsupported YUV or LUMINANCE image.");
+ }
+ }
+
+ private int getLength(DDSType type, int imageIndex) throws IIOException {
+ int width = header.getWidth(imageIndex);
+ int height = header.getHeight(imageIndex);
+
+ switch (type) {
+ case DXT1:
+ return 8 * ((width + 3) / 4) * ((height + 3) / 4);
+ case DXT2:
+ case DXT3:
+ case DXT4:
+ case DXT5:
+ return 16 * ((width + 3) / 4) * ((height + 3) / 4);
+ case A1R5G5B5:
+ case X1R5G5B5:
+ case A4R4G4B4:
+ case X4R4G4B4:
+ case R5G6B5:
+ case R8G8B8:
+ case A8B8G8R8:
+ case X8B8G8R8:
+ case A8R8G8B8:
+ case X8R8G8B8:
+ return (type.value() & 0xFF) * width * height;
+ default:
+ throw new IIOException("Unknown type: " + Integer.toHexString(type.value()));
+ }
+ }
+
+ private static int[] decodeDXT1(int width, int height, byte[] buffer) {
+ int[] pixels = new int[width * height];
+ int index = 0;
+ int w = (width + 3) / 4;
+ int h = (height + 3) / 4;
+ for (int i = 0; i < h; i++) {
+ for (int j = 0; j < w; j++) {
+ int c0 = (buffer[index] & 0xFF) | (buffer[index + 1] & 0xFF) << 8;
+ index += 2;
+ int c1 = (buffer[index] & 0xFF) | (buffer[index + 1] & 0xFF) << 8;
+ index += 2;
+ for (int k = 0; k < 4; k++) {
+ if (4 * i + k >= height) break;
+ int t0 = (buffer[index] & 0x03);
+ int t1 = (buffer[index] & 0x0C) >> 2;
+ int t2 = (buffer[index] & 0x30) >> 4;
+ int t3 = (buffer[index++] & 0xC0) >> 6;
+ pixels[4 * width * i + 4 * j + width * k + 0] = getDXTColor(c0, c1, 0xFF, t0);
+ if (4 * j + 1 >= width) continue;
+ pixels[4 * width * i + 4 * j + width * k + 1] = getDXTColor(c0, c1, 0xFF, t1);
+ if (4 * j + 2 >= width) continue;
+ pixels[4 * width * i + 4 * j + width * k + 2] = getDXTColor(c0, c1, 0xFF, t2);
+ if (4 * j + 3 >= width) continue;
+ pixels[4 * width * i + 4 * j + width * k + 3] = getDXTColor(c0, c1, 0xFF, t3);
+ }
+ }
+ }
+ return pixels;
+ }
+
+ private static int[] decodeDXT2(int width, int height, byte[] buffer) {
+ return decodeDXT3(width, height, buffer);
+ }
+
+ private static int[] decodeDXT3(int width, int height, byte[] buffer) {
+ int index = 0;
+ int w = (width + 3) / 4;
+ int h = (height + 3) / 4;
+ int[] pixels = new int[width * height];
+ int[] alphaTable = new int[16];
+ for (int i = 0; i < h; i++) {
+ for (int j = 0; j < w; j++) {
+ // create alpha table(4bit to 8bit)
+ for (int k = 0; k < 4; k++) {
+ int a0 = (buffer[index++] & 0xFF);
+ int a1 = (buffer[index++] & 0xFF);
+ // 4bit alpha to 8bit alpha
+ alphaTable[4 * k + 0] = 17 * ((a0 & 0xF0) >> 4);
+ alphaTable[4 * k + 1] = 17 * (a0 & 0x0F);
+ alphaTable[4 * k + 2] = 17 * ((a1 & 0xF0) >> 4);
+ alphaTable[4 * k + 3] = 17 * (a1 & 0x0F);
+ }
+ int c0 = (buffer[index] & 0xFF) | (buffer[index + 1] & 0xFF) << 8;
+ index += 2;
+ int c1 = (buffer[index] & 0xFF) | (buffer[index + 1] & 0xFF) << 8;
+ index += 2;
+ for (int k = 0; k < 4; k++) {
+ if (4 * i + k >= height) break;
+ int t0 = (buffer[index] & 0x03);
+ int t1 = (buffer[index] & 0x0C) >> 2;
+ int t2 = (buffer[index] & 0x30) >> 4;
+ int t3 = (buffer[index++] & 0xC0) >> 6;
+ pixels[4 * width * i + 4 * j + width * k + 0] = getDXTColor(c0, c1, alphaTable[4 * k + 0], t0);
+ if (4 * j + 1 >= width) continue;
+ pixels[4 * width * i + 4 * j + width * k + 1] = getDXTColor(c0, c1, alphaTable[4 * k + 1], t1);
+ if (4 * j + 2 >= width) continue;
+ pixels[4 * width * i + 4 * j + width * k + 2] = getDXTColor(c0, c1, alphaTable[4 * k + 2], t2);
+ if (4 * j + 3 >= width) continue;
+ pixels[4 * width * i + 4 * j + width * k + 3] = getDXTColor(c0, c1, alphaTable[4 * k + 3], t3);
+ }
+ }
+ }
+ return pixels;
+ }
+
+ private static int[] decodeDXT4(int width, int height, byte[] buffer) {
+ return decodeDXT5(width, height, buffer);
+ }
+
+ private static int[] decodeDXT5(int width, int height, byte[] buffer) {
+ int index = 0;
+ int w = (width + 3) / 4;
+ int h = (height + 3) / 4;
+ int[] pixels = new int[width * height];
+ int[] alphaTable = new int[16];
+ for (int i = 0; i < h; i++) {
+ for (int j = 0; j < w; j++) {
+ // create alpha table
+ int a0 = (buffer[index++] & 0xFF);
+ int a1 = (buffer[index++] & 0xFF);
+ int b0 = (buffer[index] & 0xFF) | (buffer[index + 1] & 0xFF) << 8 | (buffer[index + 2] & 0xFF) << 16;
+ index += 3;
+ int b1 = (buffer[index] & 0xFF) | (buffer[index + 1] & 0xFF) << 8 | (buffer[index + 2] & 0xFF) << 16;
+ index += 3;
+ alphaTable[0] = b0 & 0x07;
+ alphaTable[1] = (b0 >> 3) & 0x07;
+ alphaTable[2] = (b0 >> 6) & 0x07;
+ alphaTable[3] = (b0 >> 9) & 0x07;
+ alphaTable[4] = (b0 >> 12) & 0x07;
+ alphaTable[5] = (b0 >> 15) & 0x07;
+ alphaTable[6] = (b0 >> 18) & 0x07;
+ alphaTable[7] = (b0 >> 21) & 0x07;
+ alphaTable[8] = b1 & 0x07;
+ alphaTable[9] = (b1 >> 3) & 0x07;
+ alphaTable[10] = (b1 >> 6) & 0x07;
+ alphaTable[11] = (b1 >> 9) & 0x07;
+ alphaTable[12] = (b1 >> 12) & 0x07;
+ alphaTable[13] = (b1 >> 15) & 0x07;
+ alphaTable[14] = (b1 >> 18) & 0x07;
+ alphaTable[15] = (b1 >> 21) & 0x07;
+ int c0 = (buffer[index] & 0xFF) | (buffer[index + 1] & 0xFF) << 8;
+ index += 2;
+ int c1 = (buffer[index] & 0xFF) | (buffer[index + 1] & 0xFF) << 8;
+ index += 2;
+ for (int k = 0; k < 4; k++) {
+ if (4 * i + k >= height) break;
+ int t0 = (buffer[index] & 0x03);
+ int t1 = (buffer[index] & 0x0C) >> 2;
+ int t2 = (buffer[index] & 0x30) >> 4;
+ int t3 = (buffer[index++] & 0xC0) >> 6;
+ pixels[4 * width * i + 4 * j + width * k + 0] = getDXTColor(c0, c1, getDXT5Alpha(a0, a1, alphaTable[4 * k + 0]), t0);
+ if (4 * j + 1 >= width) continue;
+ pixels[4 * width * i + 4 * j + width * k + 1] = getDXTColor(c0, c1, getDXT5Alpha(a0, a1, alphaTable[4 * k + 1]), t1);
+ if (4 * j + 2 >= width) continue;
+ pixels[4 * width * i + 4 * j + width * k + 2] = getDXTColor(c0, c1, getDXT5Alpha(a0, a1, alphaTable[4 * k + 2]), t2);
+ if (4 * j + 3 >= width) continue;
+ pixels[4 * width * i + 4 * j + width * k + 3] = getDXTColor(c0, c1, getDXT5Alpha(a0, a1, alphaTable[4 * k + 3]), t3);
+ }
+ }
+ }
+ return pixels;
+ }
+
+ private static int[] readA1R5G5B5(int width, int height, byte[] buffer) {
+ int index = 0;
+ int[] pixels = new int[width * height];
+ for (int i = 0; i < height * width; i++) {
+ int rgba = (buffer[index] & 0xFF) | (buffer[index + 1] & 0xFF) << 8;
+ index += 2;
+ int r = BIT5[(rgba & A1R5G5B5_MASKS[0]) >> 10];
+ int g = BIT5[(rgba & A1R5G5B5_MASKS[1]) >> 5];
+ int b = BIT5[(rgba & A1R5G5B5_MASKS[2])];
+ int a = 255 * ((rgba & A1R5G5B5_MASKS[3]) >> 15);
+ pixels[i] = (a << order.alphaShift) | (r << order.redShift) | (g << order.greenShift) | (b << order.blueShift);
+ }
+ return pixels;
+ }
+
+ private static int[] readX1R5G5B5(int width, int height, byte[] buffer) {
+ int index = 0;
+ int[] pixels = new int[width * height];
+ for (int i = 0; i < height * width; i++) {
+ int rgba = (buffer[index] & 0xFF) | (buffer[index + 1] & 0xFF) << 8;
+ index += 2;
+ int r = BIT5[(rgba & X1R5G5B5_MASKS[0]) >> 10];
+ int g = BIT5[(rgba & X1R5G5B5_MASKS[1]) >> 5];
+ int b = BIT5[(rgba & X1R5G5B5_MASKS[2])];
+ int a = 255;
+ pixels[i] = (a << order.alphaShift) | (r << order.redShift) | (g << order.greenShift) | (b << order.blueShift);
+ }
+ return pixels;
+ }
+
+ private static int[] readA4R4G4B4(int width, int height, byte[] buffer) {
+ int index = 0;
+ int[] pixels = new int[width * height];
+ for (int i = 0; i < height * width; i++) {
+ int rgba = (buffer[index] & 0xFF) | (buffer[index + 1] & 0xFF) << 8;
+ index += 2;
+ int r = 17 * ((rgba & A4R4G4B4_MASKS[0]) >> 8);
+ int g = 17 * ((rgba & A4R4G4B4_MASKS[1]) >> 4);
+ int b = 17 * ((rgba & A4R4G4B4_MASKS[2]));
+ int a = 17 * ((rgba & A4R4G4B4_MASKS[3]) >> 12);
+ pixels[i] = (a << order.alphaShift) | (r << order.redShift) | (g << order.greenShift) | (b << order.blueShift);
+ }
+ return pixels;
+ }
+
+ private static int[] readX4R4G4B4(int width, int height, byte[] buffer) {
+ int index = 0;
+ int[] pixels = new int[width * height];
+ for (int i = 0; i < height * width; i++) {
+ int rgba = (buffer[index] & 0xFF) | (buffer[index + 1] & 0xFF) << 8;
+ index += 2;
+ int r = 17 * ((rgba & A4R4G4B4_MASKS[0]) >> 8);
+ int g = 17 * ((rgba & A4R4G4B4_MASKS[1]) >> 4);
+ int b = 17 * ((rgba & A4R4G4B4_MASKS[2]));
+ int a = 255;
+ pixels[i] = (a << order.alphaShift) | (r << order.redShift) | (g << order.greenShift) | (b << order.blueShift);
+ }
+ return pixels;
+ }
+
+ private static int[] readR5G6B5(int width, int height, byte[] buffer) {
+ int index = 0;
+ int[] pixels = new int[width * height];
+ for (int i = 0; i < height * width; i++) {
+ int rgba = (buffer[index] & 0xFF) | (buffer[index + 1] & 0xFF) << 8;
+ index += 2;
+ int r = BIT5[((rgba & R5G6B5_MASKS[0]) >> 11)];
+ int g = BIT6[((rgba & R5G6B5_MASKS[1]) >> 5)];
+ int b = BIT5[((rgba & R5G6B5_MASKS[2]))];
+ int a = 255;
+ pixels[i] = (a << order.alphaShift) | (r << order.redShift) | (g << order.greenShift) | (b << order.blueShift);
+ }
+ return pixels;
+ }
+
+ private static int[] readR8G8B8(int width, int height, byte[] buffer) {
+ int index = 0;
+ int[] pixels = new int[width * height];
+ for (int i = 0; i < height * width; i++) {
+ int b = buffer[index++] & 0xFF;
+ int g = buffer[index++] & 0xFF;
+ int r = buffer[index++] & 0xFF;
+ int a = 255;
+ pixels[i] = (a << order.alphaShift) | (r << order.redShift) | (g << order.greenShift) | (b << order.blueShift);
+ }
+ return pixels;
+ }
+
+ private static int[] readA8B8G8R8(int width, int height, byte[] buffer) {
+ int index = 0;
+ int[] pixels = new int[width * height];
+ for (int i = 0; i < height * width; i++) {
+ int r = buffer[index++] & 0xFF;
+ int g = buffer[index++] & 0xFF;
+ int b = buffer[index++] & 0xFF;
+ int a = buffer[index++] & 0xFF;
+ pixels[i] = (a << order.alphaShift) | (r << order.redShift) | (g << order.greenShift) | (b << order.blueShift);
+ }
+ return pixels;
+ }
+
+ private static int[] readX8B8G8R8(int width, int height, byte[] buffer) {
+ int index = 0;
+ int[] pixels = new int[width * height];
+ for (int i = 0; i < height * width; i++) {
+ int r = buffer[index++] & 0xFF;
+ int g = buffer[index++] & 0xFF;
+ int b = buffer[index++] & 0xFF;
+ int a = 255;
+ index++;
+ pixels[i] = (a << order.alphaShift) | (r << order.redShift) | (g << order.greenShift) | (b << order.blueShift);
+ }
+ return pixels;
+ }
+
+ private static int[] readA8R8G8B8(int width, int height, byte[] buffer) {
+ int index = 0;
+ int[] pixels = new int[width * height];
+ for (int i = 0; i < height * width; i++) {
+ int b = buffer[index++] & 0xFF;
+ int g = buffer[index++] & 0xFF;
+ int r = buffer[index++] & 0xFF;
+ int a = buffer[index++] & 0xFF;
+ pixels[i] = (a << order.alphaShift) | (r << order.redShift) | (g << order.greenShift) | (b << order.blueShift);
+ }
+ return pixels;
+ }
+
+ private static int[] readX8R8G8B8(int width, int height, byte[] buffer) {
+ int index = 0;
+ int[] pixels = new int[width * height];
+ for (int i = 0; i < height * width; i++) {
+ int b = buffer[index++] & 0xFF;
+ int g = buffer[index++] & 0xFF;
+ int r = buffer[index++] & 0xFF;
+ int a = 255;
+ index++;
+ pixels[i] = (a << order.alphaShift) | (r << order.redShift) | (g << order.greenShift) | (b << order.blueShift);
+ }
+ return pixels;
+ }
+
+ private static int getDXTColor(int c0, int c1, int a, int t) {
+ switch (t) {
+ case 0:
+ return getDXTColor1(c0, a);
+ case 1:
+ return getDXTColor1(c1, a);
+ case 2:
+ return (c0 > c1) ? getDXTColor2_1(c0, c1, a) : getDXTColor1_1(c0, c1, a);
+ case 3:
+ return (c0 > c1) ? getDXTColor2_1(c1, c0, a) : 0;
+ }
+ return 0;
+ }
+
+ private static int getDXTColor2_1(int c0, int c1, int a) {
+ // 2*c0/3 + c1/3
+ int r = (2 * BIT5[(c0 & 0xFC00) >> 11] + BIT5[(c1 & 0xFC00) >> 11]) / 3;
+ int g = (2 * BIT6[(c0 & 0x07E0) >> 5] + BIT6[(c1 & 0x07E0) >> 5]) / 3;
+ int b = (2 * BIT5[c0 & 0x001F] + BIT5[c1 & 0x001F]) / 3;
+ return (a << order.alphaShift) | (r << order.redShift) | (g << order.greenShift) | (b << order.blueShift);
+ }
+
+ private static int getDXTColor1_1(int c0, int c1, int a) {
+ // (c0+c1) / 2
+ int r = (BIT5[(c0 & 0xFC00) >> 11] + BIT5[(c1 & 0xFC00) >> 11]) / 2;
+ int g = (BIT6[(c0 & 0x07E0) >> 5] + BIT6[(c1 & 0x07E0) >> 5]) / 2;
+ int b = (BIT5[c0 & 0x001F] + BIT5[c1 & 0x001F]) / 2;
+ return (a << order.alphaShift) | (r << order.redShift) | (g << order.greenShift) | (b << order.blueShift);
+ }
+
+ private static int getDXTColor1(int c, int a) {
+ int r = BIT5[(c & 0xFC00) >> 11];
+ int g = BIT6[(c & 0x07E0) >> 5];
+ int b = BIT5[(c & 0x001F)];
+ return (a << order.alphaShift) | (r << order.redShift) | (g << order.greenShift) | (b << order.blueShift);
+ }
+
+ private static int getDXT5Alpha(int a0, int a1, int t) {
+ if (a0 > a1) switch (t) {
+ case 0:
+ return a0;
+ case 1:
+ return a1;
+ case 2:
+ return (6 * a0 + a1) / 7;
+ case 3:
+ return (5 * a0 + 2 * a1) / 7;
+ case 4:
+ return (4 * a0 + 3 * a1) / 7;
+ case 5:
+ return (3 * a0 + 4 * a1) / 7;
+ case 6:
+ return (2 * a0 + 5 * a1) / 7;
+ case 7:
+ return (a0 + 6 * a1) / 7;
+ }
+ else switch (t) {
+ case 0:
+ return a0;
+ case 1:
+ return a1;
+ case 2:
+ return (4 * a0 + a1) / 5;
+ case 3:
+ return (3 * a0 + 2 * a1) / 5;
+ case 4:
+ return (2 * a0 + 3 * a1) / 5;
+ case 5:
+ return (a0 + 4 * a1) / 5;
+ case 6:
+ return 0;
+ case 7:
+ return 255;
+ }
+ return 0;
+ }
+
+ // RGBA Masks
+ private static final int[] A1R5G5B5_MASKS = {0x7C00, 0x03E0, 0x001F, 0x8000};
+ private static final int[] X1R5G5B5_MASKS = {0x7C00, 0x03E0, 0x001F, 0x0000};
+ private static final int[] A4R4G4B4_MASKS = {0x0F00, 0x00F0, 0x000F, 0xF000};
+ private static final int[] X4R4G4B4_MASKS = {0x0F00, 0x00F0, 0x000F, 0x0000};
+ private static final int[] R5G6B5_MASKS = {0xF800, 0x07E0, 0x001F, 0x0000};
+ private static final int[] R8G8B8_MASKS = {0xFF0000, 0x00FF00, 0x0000FF, 0x000000};
+ private static final int[] A8B8G8R8_MASKS = {0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000};
+ private static final int[] X8B8G8R8_MASKS = {0x000000FF, 0x0000FF00, 0x00FF0000, 0x00000000};
+ private static final int[] A8R8G8B8_MASKS = {0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000};
+ private static final int[] X8R8G8B8_MASKS = {0x00FF0000, 0x0000FF00, 0x000000FF, 0x00000000};
+
+ // BIT4 = 17 * index;
+ private static final int[] BIT5 = {0, 8, 16, 25, 33, 41, 49, 58, 66, 74, 82, 90, 99, 107, 115, 123, 132, 140, 148, 156, 165, 173, 181, 189, 197, 206, 214, 222, 230, 239, 247, 255};
+ private static final int[] BIT6 = {0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 45, 49, 53, 57, 61, 65, 69, 73, 77, 81, 85, 89, 93, 97, 101, 105, 109, 113, 117, 121, 125, 130, 134, 138, 142, 146, 150, 154, 158, 162, 166, 170, 174, 178, 182, 186, 190, 194, 198, 202, 206, 210, 215, 219, 223, 227, 231, 235, 239, 243, 247, 251, 255};
+
+ private static final class Order {
+ Order(int redShift, int greenShift, int blueShift, int alphaShift) {
+ this.redShift = redShift;
+ this.greenShift = greenShift;
+ this.blueShift = blueShift;
+ this.alphaShift = alphaShift;
+ }
+
+ public int redShift;
+ public int greenShift;
+ public int blueShift;
+ public int alphaShift;
+ }
+
+}
diff --git a/imageio/imageio-dds/src/main/java/com/twelvemonkeys/imageio/plugins/dds/DDSType.java b/imageio/imageio-dds/src/main/java/com/twelvemonkeys/imageio/plugins/dds/DDSType.java
new file mode 100644
index 00000000..9b16445d
--- /dev/null
+++ b/imageio/imageio-dds/src/main/java/com/twelvemonkeys/imageio/plugins/dds/DDSType.java
@@ -0,0 +1,41 @@
+package com.twelvemonkeys.imageio.plugins.dds;
+
+import javax.imageio.IIOException;
+
+enum DDSType {
+
+ DXT1(0x31545844),
+ DXT2(0x32545844),
+ DXT3(0x33545844),
+ DXT4(0x34545844),
+ DXT5(0x35545844),
+ A1R5G5B5((1 << 16) | 2),
+ X1R5G5B5((2 << 16) | 2),
+ A4R4G4B4((3 << 16) | 2),
+ X4R4G4B4((4 << 16) | 2),
+ R5G6B5((5 << 16) | 2),
+ R8G8B8((1 << 16) | 3),
+ A8B8G8R8((1 << 16) | 4),
+ X8B8G8R8((2 << 16) | 4),
+ A8R8G8B8((3 << 16) | 4),
+ X8R8G8B8((4 << 16) | 4);
+
+ private final int value;
+
+ DDSType(int value) {
+ this.value = value;
+ }
+
+ public int value() {
+ return value;
+ }
+
+ public static DDSType parse(int type) throws IIOException {
+ for (DDSType t : DDSType.values()) {
+ if (type == t.value()) {
+ return t;
+ }
+ }
+ throw new IIOException("Unknown type: " + Integer.toHexString(type));
+ }
+}
diff --git a/imageio/imageio-dds/src/main/resources/META-INF/services/javax.imageio.spi.ImageReaderSpi b/imageio/imageio-dds/src/main/resources/META-INF/services/javax.imageio.spi.ImageReaderSpi
new file mode 100755
index 00000000..8be73657
--- /dev/null
+++ b/imageio/imageio-dds/src/main/resources/META-INF/services/javax.imageio.spi.ImageReaderSpi
@@ -0,0 +1 @@
+com.twelvemonkeys.imageio.plugins.dds.DDSImageReaderSpi
diff --git a/imageio/imageio-dds/src/test/java/DDSImageTeaderTest.java b/imageio/imageio-dds/src/test/java/DDSImageTeaderTest.java
new file mode 100644
index 00000000..06a08734
--- /dev/null
+++ b/imageio/imageio-dds/src/test/java/DDSImageTeaderTest.java
@@ -0,0 +1,141 @@
+import com.twelvemonkeys.imageio.plugins.dds.DDSImageReader;
+import com.twelvemonkeys.imageio.plugins.dds.DDSImageReaderSpi;
+import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import javax.imageio.spi.ImageReaderSpi;
+import java.awt.Dimension;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+public class DDSImageTeaderTest extends ImageReaderAbstractTest