private static PalEntry[] unswizzle8bpp(PalEntry[] pixels, int w, int h) { PalEntry[] unswizzled = new PalEntry[pixels.Length]; for (int y = 0; y < h; ++y) { for (int x = 0; x < w; ++x) { int block_location = (y & (~0xf)) * w + (x & (~0xf)) * 2; int swap_selector = (((y + 2) >> 2) & 0x1) * 4; int posY = (((y & (~3)) >> 1) + (y & 1)) & 0x7; int column_location = posY * w * 2 + ((x + swap_selector) & 0x7) * 4; int byte_num = ((y >> 1) & 1) + ((x >> 2) & 2); // 0,1,2,3 int idx = block_location + column_location + byte_num; if (idx < pixels.Length) { unswizzled[(y * w) + x] = pixels[idx]; } } } return(unswizzled); }
public WriteableBitmap Decode(int offset, int chunkStartOffset) { // Dark Alliance encodes pointers as offsets from the entry in the texture entry table. // Return to arms (more sensibly) encodes pointers as offsets from the current chunk loaded from the disc. int deltaOffset = EngineVersion.DarkAlliance == _engineVersion ? offset : chunkStartOffset; int pixelWidth = DataUtil.getLEUShort(fileData, offset); int pixelHeight = DataUtil.getLEUShort(fileData, offset + 2); int header10 = DataUtil.getLEInt(fileData, offset + 0x10); int compressedDataLen = DataUtil.getLEInt(fileData, offset + 0x14); int compressedDataOffset = header10 + deltaOffset; if (compressedDataOffset <= 0 || compressedDataOffset >= fileData.Length) { return(null); } int palOffset = DataUtil.getLEInt(fileData, compressedDataOffset) + deltaOffset; PalEntry[] palette = PalEntry.readPalette(fileData, palOffset, 16, 16); palette = PalEntry.unswizzlePalette(palette); HuffVal[] huffVals = decodeHuff(palOffset + 0xc00); int p = compressedDataOffset + 4; int width = (pixelWidth + 0x0f) & ~0x0f; int height = (pixelHeight + 0x0f) & ~0x0f; WriteableBitmap image = new WriteableBitmap( width, height, 96, 96, PixelFormats.Bgr32, null); image.Lock(); while (fileData[p] != 0xFF) { int x0 = fileData[p]; int y0 = fileData[p + 1]; int x1 = fileData[p + 2]; int y1 = fileData[p + 3]; p += 4; for (int yblock = y0; yblock <= y1; ++yblock) { for (int xblock = x0; xblock <= x1; ++xblock) { int blockDataStart = DataUtil.getLEInt(fileData, p) + deltaOffset; decodeBlock(xblock, yblock, blockDataStart, palOffset + 0x400, image, palette, huffVals); p += 4; } } } // Specify the area of the bitmap that changed. image.AddDirtyRect(new Int32Rect(0, 0, width, height)); // Release the back buffer and make it available for display. image.Unlock(); return(image); }
private static PalEntry[] applyPalette(PalEntry[] palette, byte[] bytes) { var pixels = new PalEntry[bytes.Length]; for (var i = 0; i < bytes.Length; ++i) { pixels[i] = palette[bytes[i] & 0xFF]; } return(pixels); }
private static PalEntry[] readPixels32(PalEntry[] pixels, byte[] fileData, PalEntry[] palette, int startOffset, int startx, int starty, int rrw, int rrh, int dbw, int dbh) { if (palette.Length == 256) { int numDestBytes = dbh * dbw * 4; int widthBytes = dbw * 4; if (pixels == null) { pixels = new PalEntry[numDestBytes]; } int idx = startOffset; for (int y = 0; y < rrh && (y + starty) < dbh; ++y) { for (int x = 0; x < rrw; ++x) { int destIdx = (y + starty) * widthBytes + (x + startx) * 4; pixels[destIdx++] = palette[fileData[idx++] & 0xFF]; pixels[destIdx++] = palette[fileData[idx++] & 0xFF]; pixels[destIdx++] = palette[fileData[idx++] & 0xFF]; pixels[destIdx] = palette[fileData[idx++] & 0xFF]; } } return(pixels); } else { int numDestBytes = rrh * dbw; if (pixels == null) { pixels = new PalEntry[numDestBytes]; } int idx = startOffset; bool lowbit = false; for (int y = 0; y < rrh; ++y) { for (int x = 0; x < rrw; ++x) { int destIdx = (y + starty) * dbw + x + startx; if (!lowbit) { pixels[destIdx] = palette[fileData[idx] >> 4 & 0x0F]; } else { pixels[destIdx] = palette[fileData[idx++] & 0x0F]; } lowbit = !lowbit; } } return(pixels); } }
public static PalEntry[] readPalette(byte[] fileData, int startOffset, int palw, int palh) { int numEntries = palw * palh; PalEntry[] palette = new PalEntry[numEntries]; for (int i = 0; i < numEntries; ++i) { PalEntry pe = new PalEntry(); pe.r = fileData[startOffset + i * 4]; pe.g = fileData[startOffset + i * 4 + 1]; pe.b = fileData[startOffset + i * 4 + 2]; pe.a = fileData[startOffset + i * 4 + 3]; palette[i] = pe; } return palette; }
public static PalEntry[] unswizzlePalette(PalEntry[] palette) { if (palette.Length == 256) { PalEntry[] unswizzled = new PalEntry[palette.Length]; int j = 0; for (int i = 0; i < 256; i += 32, j += 32) { copy(unswizzled, i, palette, j, 8); copy(unswizzled, i + 16, palette, j + 8, 8); copy(unswizzled, i + 8, palette, j + 16, 8); copy(unswizzled, i + 24, palette, j + 24, 8); } return unswizzled; } else { return palette; } }
public static PalEntry[] readPalette(byte[] fileData, int startOffset, int palw, int palh) { int numEntries = palw * palh; PalEntry[] palette = new PalEntry[numEntries]; for (int i = 0; i < numEntries; ++i) { PalEntry pe = new PalEntry(); pe.r = fileData[startOffset + i * 4]; pe.g = fileData[startOffset + i * 4 + 1]; pe.b = fileData[startOffset + i * 4 + 2]; pe.a = fileData[startOffset + i * 4 + 3]; palette[i] = pe; } return(palette); }
private static PalEntry[] readPixels32(byte[] fileData, int startOffset, int w, int h) { int numPixels = w * h; PalEntry[] pixels = new PalEntry[numPixels]; int destIdx = 0; int endOffset = startOffset + numPixels * 4; for (int idx = startOffset; idx < endOffset;) { PalEntry pe = new PalEntry(); pe.r = fileData[idx++]; pe.g = fileData[idx++]; pe.b = fileData[idx++]; pe.a = fileData[idx++]; pixels[destIdx++] = pe; } return(pixels); }
public static PalEntry[] unswizzlePalette(PalEntry[] palette) { if (palette.Length == 256) { PalEntry[] unswizzled = new PalEntry[palette.Length]; int j = 0; for (int i = 0; i < 256; i += 32, j += 32) { copy(unswizzled, i, palette, j, 8); copy(unswizzled, i + 16, palette, j + 8, 8); copy(unswizzled, i + 8, palette, j + 16, 8); copy(unswizzled, i + 24, palette, j + 24, 8); } return(unswizzled); } else { return(palette); } }
private static PalEntry[] readPixels32(byte[] fileData, int startOffset, int w, int h) { var numPixels = w * h; var pixels = new PalEntry[numPixels]; var destIdx = 0; var endOffset = startOffset + numPixels * 4; for (var idx = startOffset; idx < endOffset;) { var pe = new PalEntry { r = fileData[idx++], g = fileData[idx++], b = fileData[idx++], a = fileData[idx++] }; pixels[destIdx++] = pe; } return(pixels); }
private void decodeBlock(int xblock, int yblock, int blockDataStart, int table0Start, WriteableBitmap image, PalEntry[] palette, HuffVal[] huffVals) { int tableOffset = table0Start + 0x800; int table1Len = DataUtil.getLEInt(fileData, tableOffset) * 2; int table1Start = tableOffset + 4; int table2Start = table1Start + table1Len; int table3Start = table2Start + 0x48; int[] pix8s = new int[16 * 16]; int curpix8 = 0; int startBit = 0; int prevPixel = 0; for (int y = 0; y < 16; ++y) { for (int x = 0; x < 16; ++x) { int startWordIdx = startBit / 16; int word1 = DataUtil.getLEUShort(fileData, blockDataStart + startWordIdx * 2); int word2 = DataUtil.getLEUShort(fileData, blockDataStart + startWordIdx * 2 + 2); // if startBit is 0, word == word1 // if startBit is 1, word is 15 bits of word1 and 1 bit of word2 int word = ((word1 << 16 | word2) >> (16 - (startBit & 0x0f))) & 0xFFFF; int byte1 = (word >> 8) & 0xff; HuffVal hv = huffVals[byte1]; int pixCmd; if (hv.numBits != 0) { pixCmd = hv.val; startBit += hv.numBits; } else { // Must be more than an 8 bit code int bit = 9; int a = word >> (16 - bit); int v = DataUtil.getLEInt(fileData, table3Start + bit * 4); while (v < a) { ++bit; if (bit > 16) { throw new Exception("A decoding error occured"); } a = word >> (16 - bit); v = DataUtil.getLEInt(fileData, table3Start + bit * 4); } startBit += bit; int val = DataUtil.getLEInt(fileData, table2Start + bit * 4); int table1Index = a + val; pixCmd = DataUtil.getLEShort(fileData, table1Start + table1Index * 2); } int pix8 = 0; if (pixCmd < 0x100) { pix8 = pixCmd; } else if (pixCmd < 0x105) { int backjump = backJumpTable[pixCmd - 0x100]; if ((curpix8 + backjump) >= 0) { pix8 = pix8s[curpix8 + backjump]; } else { throw new Exception("Something went wrong"); } } else { int table0Index = (pixCmd - 0x105) + prevPixel * 8; pix8 = fileData[table0Start + table0Index] & 0xFF; } pix8s[curpix8++] = pix8; prevPixel = pix8 & 0xFF; PalEntry pixel = palette[pix8 & 0xFF]; var pBackBuffer = image.BackBuffer; int xpos = xblock * 16 + x; int ypos = yblock * 16 + y; var p = pBackBuffer + ypos * image.BackBufferStride + xpos * 4; unsafe { *((int *)p) = pixel.argb(); } } } }
private static PalEntry[] unswizzle8bpp(PalEntry[] pixels, int w, int h) { PalEntry[] unswizzled = new PalEntry[pixels.Length]; for (int y = 0; y < h; ++y) { for (int x = 0; x < w; ++x) { int block_location = (y & (~0xf)) * w + (x & (~0xf)) * 2; int swap_selector = (((y + 2) >> 2) & 0x1) * 4; int posY = (((y & (~3)) >> 1) + (y & 1)) & 0x7; int column_location = posY * w * 2 + ((x + swap_selector) & 0x7) * 4; int byte_num = ((y >> 1) & 1) + ((x >> 2) & 2); // 0,1,2,3 int idx = block_location + column_location + byte_num; if (idx < pixels.Length) { unswizzled[(y * w) + x] = pixels[idx]; } } } return unswizzled; }
private static PalEntry[] readPixels32(PalEntry[] pixels, byte[] fileData, PalEntry[] palette, int startOffset, int startx, int starty, int rrw, int rrh, int dbw, int dbh) { if (palette.Length == 256) { int numDestBytes = dbh * dbw * 4; int widthBytes = dbw * 4; if (pixels == null) { pixels = new PalEntry[numDestBytes]; } int idx = startOffset; for (int y = 0; y < rrh && (y+starty) < dbh; ++y) { for (int x = 0; x < rrw; ++x) { int destIdx = (y+starty) * widthBytes + (x + startx) * 4; pixels[destIdx++] = palette[fileData[idx++] & 0xFF]; pixels[destIdx++] = palette[fileData[idx++] & 0xFF]; pixels[destIdx++] = palette[fileData[idx++] & 0xFF]; pixels[destIdx] = palette[fileData[idx++] & 0xFF]; } } return pixels; } else { int numDestBytes = rrh * dbw; if (pixels == null) { pixels = new PalEntry[numDestBytes]; } int idx = startOffset; bool lowbit = false; for (int y = 0; y < rrh; ++y) { for (int x = 0; x < rrw; ++x) { int destIdx = (y + starty) * dbw + x + startx; if (!lowbit) { pixels[destIdx] = palette[fileData[idx] >> 4 & 0x0F]; } else { pixels[destIdx] = palette[fileData[idx++] & 0x0F]; } lowbit = !lowbit; } } return pixels; } }
private static void copy(PalEntry[] unswizzled, int i, PalEntry[] swizzled, int j, int num) { for (int x = 0; x < num; ++x) { unswizzled[i + x] = swizzled[j + x]; } }
public static WriteableBitmap Decode(byte[] data, int startOffset) { var gsMem = new GSMemory(); var length = DataUtil.getLEShort(data, startOffset + 6) * 16; int finalw = BitConverter.ToInt16(data, startOffset); int finalh = BitConverter.ToInt16(data, startOffset + 2); var offsetToGIF = DataUtil.getLEInt(data, startOffset + 16); var sourcew = finalw; var sourceh = finalh; PalEntry[] pixels = null; byte[] bytes = null; var curIdx = offsetToGIF + startOffset; var endIndex = curIdx + length; var gifTag = new GIFTag(); gifTag.parse(data, curIdx); // This is basically heuristics. Writing a full GIF parser is complex and as the texture files are written by a tool, // we can safely make some assumptions about their structure. if (gifTag.nloop == 4) { int palw = DataUtil.getLEShort(data, curIdx + 0x30); int palh = DataUtil.getLEShort(data, curIdx + 0x34); curIdx += gifTag.Length; var gifTag2 = new GIFTag(); gifTag2.parse(data, curIdx); // 8 bit palletised var palette = PalEntry.readPalette(data, curIdx + GIFTag.Size, palw, palh); palette = PalEntry.unswizzlePalette(palette); curIdx += gifTag2.Length; var destWBytes = (finalw + 0x0f) & ~0x0f; var destHBytes = (finalh + 0x0f) & ~0x0f; var dpsm = PSMCT32; var dbw = 0; var dbp = 0; var rrw = 0; var rrh = 0; var startx = 0; var starty = 0; while (curIdx < endIndex - GIFTag.Size) { var gifTag3 = new GIFTag(); gifTag3.parse(data, curIdx); while (!gifTag3.IsImage) { var trxregOffset = findADEntry(data, curIdx + GIFTag.Size, gifTag3.nloop, TRXREG); if (trxregOffset != 0) { rrw = DataUtil.getLEShort(data, trxregOffset); rrh = DataUtil.getLEShort(data, trxregOffset + 4); } var trxposOffset = findADEntry(data, curIdx + GIFTag.Size, gifTag3.nloop, TRXPOS); if (trxposOffset != 0) { startx = DataUtil.getLEShort(data, trxposOffset + 0x04) & 0x07FF; starty = DataUtil.getLEShort(data, trxposOffset + 0x06) & 0x07FF; } var bitbltOffset = findADEntry(data, curIdx + GIFTag.Size, gifTag3.nloop, BITBLTBUF); if (bitbltOffset != 0) { //int sbw = fileData[bitbltOffset + 0x02] & 0x3F; dbp = data[bitbltOffset + 0x04] & 0x3FFF; dbw = data[bitbltOffset + 0x06] & 0x3F; dpsm = data[bitbltOffset + 0x07] & 0x3F; } curIdx += gifTag3.Length; if (curIdx + GIFTag.Size >= endIndex) { break; } gifTag3.parse(data, curIdx); } curIdx += GIFTag.Size; // image gif tag var bytesToTransfer = gifTag3.nloop * 16; if (palette.Length == 16) { // source is PSMT4. Dest can be PSMT4 or PSMCT32 if (dpsm == PSMCT32) { var imageData = data; var imageDataIdx = curIdx; // check for multiple IMAGE entries. var nextTagInd = bytesToTransfer + curIdx; if (nextTagInd < endIndex - GIFTag.Size) { var imageTag2 = new GIFTag(); imageTag2.parse(data, nextTagInd); if (imageTag2.flg == 2) { // IMAGE var bytesToTransfer2 = imageTag2.nloop * 16; imageDataIdx = 0; imageData = new byte[bytesToTransfer + bytesToTransfer2]; var j = curIdx; for (var i = 0; i < bytesToTransfer; ++i) { imageData[i] = data[j]; } j = nextTagInd + GIFTag.Size; for (var i = bytesToTransfer; i < bytesToTransfer + bytesToTransfer2; ++i) { imageData[i] = data[j]; } bytesToTransfer += imageTag2.Length; } } gsMem.writeTexPSMCT32(dbp, dbw, startx, starty, rrw, rrh, imageData, imageDataIdx); destWBytes = (finalw + 0x3f) & ~0x3f; bytes = gsMem.readTexPSMT4(dbp, destWBytes / 0x40, startx, starty, destWBytes, destHBytes); bytes = expand4bit(bytes); } else { // dest and source are the same and so image isn't swizzled bytes = transferPSMT4(bytes, data, curIdx, startx, starty, rrw, rrh, destWBytes, destHBytes); } } else { // source is PSMT8. Dest is always PSMCT32. gsMem.writeTexPSMCT32(dbp, dbw, startx, starty, rrw, rrh, data, curIdx); } curIdx += bytesToTransfer; } if (palette.Length == 256) { destWBytes = (finalw + 0x3f) & ~0x3f; dbw = destWBytes / 0x40; bytes = gsMem.readTexPSMT8(dbp, dbw, 0, 0, destWBytes, finalh); } // THIS IS A HACK if (palette.Length == 1024) { destWBytes = (finalw + 0x3f) & ~0x3f; dbw = destWBytes / 0x40; bytes = gsMem.readTexPSMT8(dbp, dbw, 0, 0, destWBytes, finalh); } pixels = applyPalette(palette, bytes); sourcew = destWBytes; sourceh = destHBytes; } else if (gifTag.nloop == 3) { var gifTag2 = new GIFTag(); gifTag2.parse(data, startOffset + 0xC0); if (gifTag2.flg == 2) { // image mode pixels = readPixels32(data, startOffset + 0xD0, finalw, finalh); } } WriteableBitmap image = null; if (finalw != 0) { image = new WriteableBitmap( finalw, finalh, 96, 96, PixelFormats.Bgra32, null); var imageBytes = new int[finalw * finalh]; var stride = 4 * finalw; var pixOffset = 0; if (pixels != null) { for (var y = 0; y < sourceh && y < finalh; ++y) { for (var x = 0; x < sourcew && x < finalw; ++x) { var pixel = pixels[y * sourcew + x]; if (pixel != null) { if (x < finalw && y < finalh) { imageBytes[pixOffset++] = pixel.argb(); } } } } } image.WritePixels(new Int32Rect(0, 0, finalw, finalh), imageBytes, stride, 0); } return(image); }
public static WriteableBitmap Decode(byte[] data, int startOffset, int length) { int endIndex = startOffset + length; int finalw = BitConverter.ToInt16(data, startOffset); int finalh = BitConverter.ToInt16(data, startOffset + 2); int sourcew = finalw; int sourceh = finalh; PalEntry[] pixels = null; int curIdx = 0x80 + startOffset; GIFTag gifTag = new GIFTag(); gifTag.parse(data, curIdx); // This is basically heuristics. Writing a full GIF parser is complex and as the texture files are written by a tool, // we can safely make some assumptions about their structure. if (gifTag.nloop == 4) { int palw = DataUtil.getLEShort(data, curIdx + 0x30); int palh = DataUtil.getLEShort(data, curIdx + 0x34); curIdx += 0x50; GIFTag gifTag2 = new GIFTag(); gifTag2.parse(data, curIdx); // 8 bit palletised PalEntry[] palette = PalEntry.readPalette(data, curIdx + 0x10, palw, palh); palette = PalEntry.unswizzlePalette(palette); int palLen = palw * palh * 4; curIdx += (palLen + 0x10); GIFTag gifTag50 = new GIFTag(); gifTag50.parse(data, curIdx); curIdx += 0x20; int dbw = (sourcew / 2 + 0x07) & ~0x07; int dbh = (sourceh / 2 + 0x07) & ~0x07; // The following should be a loop, there are repeating sections while (curIdx < endIndex - 0x10) { GIFTag gifTag3 = new GIFTag(); gifTag3.parse(data, curIdx); int dimOffset = 0x10; int thisRrw = DataUtil.getLEShort(data, curIdx + dimOffset); int thisRrh = DataUtil.getLEShort(data, curIdx + dimOffset + 4); int startx = DataUtil.getLEShort(data, curIdx + dimOffset + 20); int starty = DataUtil.getLEShort(data, curIdx + dimOffset + 22); curIdx += gifTag.nloop * 0x10 + 0x10; pixels = readPixels32(pixels, data, palette, curIdx, startx, starty, thisRrw, thisRrh, dbw, dbh); curIdx += thisRrw * thisRrh * 4; } if (palLen != 64) { pixels = unswizzle8bpp(pixels, dbw * 2, dbh * 2); sourcew = dbw * 2; sourceh = dbh * 2; } else { sourcew = dbw; sourceh = dbh; } } else if (gifTag.nloop == 3) { GIFTag gifTag2 = new GIFTag(); gifTag2.parse(data, startOffset + 0xC0); if (gifTag2.flg == 2) { // image mode pixels = readPixels32(data, startOffset + 0xD0, finalw, finalh); } } WriteableBitmap image = null; if (finalw != 0 && pixels != null) { image = new WriteableBitmap( finalw, finalh, 96, 96, PixelFormats.Bgr32, null); image.Lock(); unsafe { IntPtr pBackBuffer = image.BackBuffer; for (int y = 0; y < sourceh; ++y) { for (int x = 0; x < sourcew; ++x) { PalEntry pixel = pixels[y * sourcew + x]; if (pixel != null) { if (x < finalw && y < finalh) { var p = pBackBuffer + y * image.BackBufferStride + x * 4; *((int *)p) = pixel.argb(); } } } } } // Specify the area of the bitmap that changed. image.AddDirtyRect(new Int32Rect(0, 0, finalw, finalh)); // Release the back buffer and make it available for display. image.Unlock(); } return(image); }
private void decodeBlock(int xblock, int yblock, int blockDataStart, int table0Start, WriteableBitmap image, PalEntry[] palette, HuffVal[] huffVals) { int tableOffset = table0Start + 0x800; int table1Len = DataUtil.getLEInt(fileData, tableOffset) * 2; int table1Start = tableOffset + 4; int table2Start = table1Start + table1Len; int table3Start = table2Start + 0x48; int[] pix8s = new int[16*16]; int curpix8=0; int startBit=0; int prevPixel=0; for (int y=0; y<16; ++y){ for (int x=0; x < 16; ++x){ int startWordIdx = startBit / 16; int word1 = DataUtil.getLEUShort(fileData, blockDataStart + startWordIdx * 2); int word2 = DataUtil.getLEUShort(fileData, blockDataStart + startWordIdx * 2 + 2); // if startBit is 0, word == word1 // if startBit is 1, word is 15 bits of word1 and 1 bit of word2 int word = ((word1 << 16 | word2) >> (16 - (startBit & 0x0f))) & 0xFFFF; int byte1 = (word >> 8) & 0xff; HuffVal hv = huffVals[byte1]; int pixCmd; if (hv.numBits != 0){ pixCmd = hv.val; startBit += hv.numBits; } else { // Must be more than an 8 bit code int bit=9; int a = word >> (16-bit); int v = DataUtil.getLEInt(fileData, table3Start + bit*4); while (v < a){ ++bit; if (bit > 16){ throw new Exception("A decoding error occured"); } a = word >> (16-bit); v = DataUtil.getLEInt(fileData, table3Start + bit*4); } startBit += bit; int val = DataUtil.getLEInt(fileData, table2Start + bit*4); int table1Index = a + val; pixCmd = DataUtil.getLEShort(fileData, table1Start + table1Index*2); } int pix8 = 0; if (pixCmd < 0x100){ pix8 = pixCmd; } else if (pixCmd < 0x105){ int backjump = backJumpTable[pixCmd - 0x100]; if ((curpix8 + backjump) >= 0){ pix8 = pix8s[curpix8 + backjump]; } else { throw new Exception("Something went wrong"); } } else { int table0Index = (pixCmd - 0x105) + prevPixel * 8; pix8 = fileData[table0Start + table0Index] & 0xFF; } pix8s[curpix8++] = pix8; prevPixel = pix8 & 0xFF; PalEntry pixel = palette[pix8 & 0xFF]; var pBackBuffer = image.BackBuffer; int xpos = xblock * 16 + x; int ypos = yblock * 16 + y; var p = pBackBuffer + ypos * image.BackBufferStride + xpos * 4; unsafe { *((int*)p) = pixel.argb(); } } } }
private static PalEntry[] readPixels32(byte[] fileData, int startOffset, int w, int h) { int numPixels = w * h; PalEntry[] pixels = new PalEntry[numPixels]; int destIdx = 0; int endOffset = startOffset + numPixels * 4; for (int idx = startOffset; idx < endOffset; ) { PalEntry pe = new PalEntry(); pe.r = fileData[idx++]; pe.g = fileData[idx++]; pe.b = fileData[idx++]; pe.a = fileData[idx++]; pixels[destIdx++] = pe; } return pixels; }