public static int GetBitsPerPixel(SceGxmTextureBaseFormat baseFormat) { if (!bitsPerPixelMap.ContainsKey(baseFormat)) { throw new Exception(string.Format("No matching bits per pixel known for {0}", baseFormat)); } return(bitsPerPixelMap[baseFormat]); }
public TextureBundle(BinaryReader reader, SceGxtTextureInfo info) { reader.BaseStream.Seek(info.DataOffset, SeekOrigin.Begin); Width = info.GetWidth(); Height = info.GetHeight(); PaletteIndex = info.PaletteIndex; RawLineSize = (int)(info.DataSize / info.GetHeightRounded()); TextureFormat = info.GetTextureFormat(); RoundedWidth = Width; //info.GetWidthRounded(); RoundedHeight = Height; //info.GetHeightRounded(); if (!PixelDataProviders.PixelFormatMap.ContainsKey(TextureFormat) || !PixelDataProviders.ProviderFunctions.ContainsKey(TextureFormat)) { throw new FormatNotImplementedException(TextureFormat); } PixelFormat = PixelDataProviders.PixelFormatMap[TextureFormat]; PixelData = PixelDataProviders.ProviderFunctions[TextureFormat](reader, info); SceGxmTextureBaseFormat textureBaseFormat = info.GetTextureBaseFormat(); // TODO: is this right? PVRTC/PVRTC2 doesn't need this, but everything else does? if (textureBaseFormat != SceGxmTextureBaseFormat.PVRT2BPP && textureBaseFormat != SceGxmTextureBaseFormat.PVRT4BPP && textureBaseFormat != SceGxmTextureBaseFormat.PVRTII2BPP && textureBaseFormat != SceGxmTextureBaseFormat.PVRTII4BPP) { SceGxmTextureType textureType = info.GetTextureType(); switch (textureType) { case SceGxmTextureType.Linear: // Nothing to be done! break; case SceGxmTextureType.Tiled: // TODO: verify me! PixelData = PostProcessing.UntileTexture(PixelData, info.GetWidthRounded(), info.GetHeightRounded(), PixelFormat); break; case SceGxmTextureType.Swizzled: case SceGxmTextureType.Cube: // TODO: is cube really the same as swizzled? seems that way from CS' *env* files... PixelData = PostProcessing.UnswizzleTexture(PixelData, info.GetWidthRounded(), info.GetHeightRounded(), PixelFormat); break; case (SceGxmTextureType)0xA0000000: // TODO: mehhhhh PixelData = PostProcessing.UnswizzleTexture(PixelData, info.GetWidthRounded(), info.GetHeightRounded(), PixelFormat); break; default: throw new TypeNotImplementedException(textureType); } } }
private static byte[] DecompressDxtBlock(BinaryReader reader, SceGxmTextureBaseFormat format) { byte[] outputData = new byte[(4 * 4) * 4]; byte[] colorData = null, alphaData = null; if (format != SceGxmTextureBaseFormat.UBC1) { alphaData = DecompressDxtAlpha(reader, format); } colorData = DecompressDxtColor(reader, format); for (int i = 0; i < colorData.Length; i += 4) { outputData[i] = colorData[i]; outputData[i + 1] = colorData[i + 1]; outputData[i + 2] = colorData[i + 2]; outputData[i + 3] = (alphaData != null ? alphaData[i + 3] : colorData[i + 3]); } return(outputData); }
private Bitmap CreateBitmap(int infoIdx, int forcePaletteIdx = -1) { SceGxtTextureInfo info = TextureInfos[infoIdx]; ImageBinary imageBinary = new ImageBinary(); imageBinary.Width = info.GetWidth(); imageBinary.Height = info.GetHeight(); imageBinary.InputPixelFormat = PSVita.GetPixelDataFormat(info.GetTextureFormat()); imageBinary.InputEndianness = Endian.LittleEndian; imageBinary.AddInputPixels(PixelData[infoIdx]); // TODO: verify all this crap, GXT conversion wrt image [dimension/format/type] is fragile as all hell SceGxmTextureBaseFormat textureBaseFormat = info.GetTextureBaseFormat(); SceGxmTextureType textureType = info.GetTextureType(); if (textureType == SceGxmTextureType.Linear && textureBaseFormat != SceGxmTextureBaseFormat.UBC1 && textureBaseFormat != SceGxmTextureBaseFormat.UBC2 && textureBaseFormat != SceGxmTextureBaseFormat.UBC3 && textureBaseFormat != SceGxmTextureBaseFormat.PVRT2BPP && textureBaseFormat != SceGxmTextureBaseFormat.PVRT4BPP && textureBaseFormat != SceGxmTextureBaseFormat.PVRTII2BPP && textureBaseFormat != SceGxmTextureBaseFormat.PVRTII4BPP) { imageBinary.PhysicalWidth = (int)(((info.DataSize / imageBinary.Height) * 8) / PSVita.GetBitsPerPixel(textureBaseFormat)); imageBinary.PhysicalHeight = info.GetHeight(); } else { imageBinary.PhysicalWidth = info.GetWidthRounded(); imageBinary.PhysicalHeight = info.GetHeightRounded(); } if (textureBaseFormat != SceGxmTextureBaseFormat.PVRT2BPP && textureBaseFormat != SceGxmTextureBaseFormat.PVRT4BPP) { switch (textureType) { case SceGxmTextureType.Linear: // Nothing to be done! break; case SceGxmTextureType.Tiled: // TODO: verify me! imageBinary.InputPixelFormat |= PixelDataFormat.PixelOrderingTiled3DS; break; case SceGxmTextureType.Swizzled: case SceGxmTextureType.Cube: // TODO: is cube really the same as swizzled? seems that way from CS' *env* files... imageBinary.InputPixelFormat |= PixelDataFormat.PixelOrderingSwizzledVita; break; case (SceGxmTextureType)0xA0000000: // TODO: this is odd and needs investigation, found ex. in Odin Sphere, Puyo Puyo Tetris, ... imageBinary.InputPixelFormat |= PixelDataFormat.PixelOrderingSwizzledVita; break; } } if (textureBaseFormat == SceGxmTextureBaseFormat.P4 || textureBaseFormat == SceGxmTextureBaseFormat.P8) { imageBinary.InputPaletteFormat = PSVita.GetPaletteFormat(info.GetTextureFormat()); if (textureBaseFormat == SceGxmTextureBaseFormat.P4) { foreach (byte[] paletteData in P4Palettes) { imageBinary.AddInputPalette(paletteData); } } else if (textureBaseFormat == SceGxmTextureBaseFormat.P8) { foreach (byte[] paletteData in P8Palettes) { imageBinary.AddInputPalette(paletteData); } } } return(imageBinary.GetBitmap(0, forcePaletteIdx != -1 ? forcePaletteIdx : info.PaletteIndex)); }
private static byte[] DecompressDxtColor(BinaryReader reader, SceGxmTextureBaseFormat format) { byte[] colorOut = new byte[(4 * 4) * 4]; ushort color0 = reader.ReadUInt16(); ushort color1 = reader.ReadUInt16(); uint bits = reader.ReadUInt32(); byte c0r, c0g, c0b, c1r, c1g, c1b; UnpackRgb565(color0, out c0r, out c0g, out c0b); UnpackRgb565(color1, out c1r, out c1g, out c1b); byte[] bitsExt = new byte[16]; for (int i = 0; i < bitsExt.Length; i++) { bitsExt[i] = (byte)((bits >> (i * 2)) & 0x3); } for (int y = 0; y < 4; y++) { for (int x = 0; x < 4; x++) { byte code = bitsExt[(y * 4) + x]; int destOffset = ((y * 4) + x) * 4; // NOTE: Vita DXT1 appears to be RGB-only, thus always opaque colorOut[destOffset + 3] = 0xFF; if (format == SceGxmTextureBaseFormat.UBC1 && color0 <= color1) { switch (code) { case 0x00: colorOut[destOffset + 0] = c0b; colorOut[destOffset + 1] = c0g; colorOut[destOffset + 2] = c0r; break; case 0x01: colorOut[destOffset + 0] = c1b; colorOut[destOffset + 1] = c1g; colorOut[destOffset + 2] = c1r; break; case 0x02: colorOut[destOffset + 0] = (byte)((c0b + c1b) / 2); colorOut[destOffset + 1] = (byte)((c0g + c1g) / 2); colorOut[destOffset + 2] = (byte)((c0r + c1r) / 2); break; case 0x03: colorOut[destOffset + 0] = 0; colorOut[destOffset + 1] = 0; colorOut[destOffset + 2] = 0; break; } } else { switch (code) { case 0x00: colorOut[destOffset + 0] = c0b; colorOut[destOffset + 1] = c0g; colorOut[destOffset + 2] = c0r; break; case 0x01: colorOut[destOffset + 0] = c1b; colorOut[destOffset + 1] = c1g; colorOut[destOffset + 2] = c1r; break; case 0x02: colorOut[destOffset + 0] = (byte)((2 * c0b + c1b) / 3); colorOut[destOffset + 1] = (byte)((2 * c0g + c1g) / 3); colorOut[destOffset + 2] = (byte)((2 * c0r + c1r) / 3); break; case 0x03: colorOut[destOffset + 0] = (byte)((c0b + 2 * c1b) / 3); colorOut[destOffset + 1] = (byte)((c0g + 2 * c1g) / 3); colorOut[destOffset + 2] = (byte)((c0r + 2 * c1r) / 3); break; } } } } return(colorOut); }
private static void DecodeDxtTile(BinaryReader reader, byte[] targetData, int x, int y, int width, int height, SceGxmTextureBaseFormat format) { for (int by = 0; by < 8; by += 4) { for (int bx = 0; bx < 8; bx += 4) { using (BinaryReader decodedReader = new BinaryReader(new MemoryStream(DecompressDxtBlock(reader, format)))) { for (int px = 0; px < 4; px++) { for (int py = 0; py < 4; py++) { int ix = (x + bx + px); int iy = (y + by + py); if (ix >= width || iy >= height) { continue; } int pixelOffset = (int)(((iy * width) + ix) * 4); Buffer.BlockCopy(decodedReader.ReadBytes(4), 0, targetData, pixelOffset, 4); } } } } } }
private static byte[] DecompressDxtAlpha(BinaryReader reader, SceGxmTextureBaseFormat format) { byte[] alphaOut = new byte[(4 * 4) * 4]; if (format == SceGxmTextureBaseFormat.UBC2) { ulong alpha = reader.ReadUInt64(); for (int i = 0; i < alphaOut.Length; i += 4) { alphaOut[i + 3] = (byte)(((alpha & 0xF) << 4) | (alpha & 0xF)); alpha >>= 4; } } else if (format == SceGxmTextureBaseFormat.UBC3) { byte alpha0 = reader.ReadByte(); byte alpha1 = reader.ReadByte(); byte bits_5 = reader.ReadByte(); byte bits_4 = reader.ReadByte(); byte bits_3 = reader.ReadByte(); byte bits_2 = reader.ReadByte(); byte bits_1 = reader.ReadByte(); byte bits_0 = reader.ReadByte(); ulong bits = (ulong)(((ulong)bits_0 << 40) | ((ulong)bits_1 << 32) | ((ulong)bits_2 << 24) | ((ulong)bits_3 << 16) | ((ulong)bits_4 << 8) | (ulong)bits_5); byte[] bitsExt = new byte[16]; for (int i = 0; i < bitsExt.Length; i++) { bitsExt[i] = (byte)((bits >> (i * 3)) & 0x7); } for (int y = 0; y < 4; y++) { for (int x = 0; x < 4; x++) { byte code = bitsExt[(y * 4) + x]; int destOffset = (((y * 4) + x) * 4) + 3; if (alpha0 > alpha1) { switch (code) { case 0x00: alphaOut[destOffset] = alpha0; break; case 0x01: alphaOut[destOffset] = alpha1; break; case 0x02: alphaOut[destOffset] = (byte)((6 * alpha0 + 1 * alpha1) / 7); break; case 0x03: alphaOut[destOffset] = (byte)((5 * alpha0 + 2 * alpha1) / 7); break; case 0x04: alphaOut[destOffset] = (byte)((4 * alpha0 + 3 * alpha1) / 7); break; case 0x05: alphaOut[destOffset] = (byte)((3 * alpha0 + 4 * alpha1) / 7); break; case 0x06: alphaOut[destOffset] = (byte)((2 * alpha0 + 5 * alpha1) / 7); break; case 0x07: alphaOut[destOffset] = (byte)((1 * alpha0 + 6 * alpha1) / 7); break; } } else { switch (code) { case 0x00: alphaOut[destOffset] = alpha0; break; case 0x01: alphaOut[destOffset] = alpha1; break; case 0x02: alphaOut[destOffset] = (byte)((4 * alpha0 + 1 * alpha1) / 5); break; case 0x03: alphaOut[destOffset] = (byte)((3 * alpha0 + 2 * alpha1) / 5); break; case 0x04: alphaOut[destOffset] = (byte)((2 * alpha0 + 3 * alpha1) / 5); break; case 0x05: alphaOut[destOffset] = (byte)((1 * alpha0 + 4 * alpha1) / 5); break; case 0x06: alphaOut[destOffset] = 0x00; break; case 0x07: alphaOut[destOffset] = 0xFF; break; } } } } } return(alphaOut); }