public static TEX_FORMAT ConvertPICAToGenericFormat(PICASurfaceFormat format) { switch (format) { case PICASurfaceFormat.RGB565: return(TEX_FORMAT.B5G6R5_UNORM); case PICASurfaceFormat.RGB8: return(TEX_FORMAT.R8G8_UNORM); case PICASurfaceFormat.RGBA5551: return(TEX_FORMAT.B5G5R5A1_UNORM); case PICASurfaceFormat.RGBA4: return(TEX_FORMAT.B4G4R4A4_UNORM); case PICASurfaceFormat.LA8: return(TEX_FORMAT.LA8); case PICASurfaceFormat.HiLo8: return(TEX_FORMAT.HIL08); case PICASurfaceFormat.L8: return(TEX_FORMAT.L8); case PICASurfaceFormat.A8: return(TEX_FORMAT.A8_UNORM); case PICASurfaceFormat.LA4: return(TEX_FORMAT.LA4); case PICASurfaceFormat.L4: return(TEX_FORMAT.L4); case PICASurfaceFormat.A4: return(TEX_FORMAT.A4); case PICASurfaceFormat.ETC1: return(TEX_FORMAT.ETC1); case PICASurfaceFormat.ETC1A4: return(TEX_FORMAT.ETC1_A4); default: throw new NotImplementedException("Unsupported format! " + format); } }
public static int CalculateLength(int Width, int Height, PICASurfaceFormat Format) { int Length = (Width * Height * FmtBPP[(int)Format]) / 8; if ((Length & 0x7f) != 0) { Length = (Length & ~0x7f) + 0x80; } return(Length); }
//Much help from encoding thanks to this // https://github.com/Cruel/3dstex/blob/master/src/Encoder.cpp public static byte[] EncodeBlock(byte[] Input, int Width, int Height, PICASurfaceFormat PicaFormat) { int ImageSize = CalculateLength(Width, Height, PicaFormat); if (PicaFormat == PICASurfaceFormat.ETC1 || PicaFormat == PICASurfaceFormat.ETC1A4) { return(new byte[ImageSize]); return(ETC1.ETC1Encode(Input, Width, Height, PicaFormat == PICASurfaceFormat.ETC1A4)); } var mem = new System.IO.MemoryStream(); using (var writer = new FileWriter(mem)) { for (int TY = 0; TY < Height; TY += 8) { for (int TX = 0; TX < Width; TX += 8) { for (int Px = 0; Px < 64; Px++) { int X = SwizzleLUT[Px] & 7; int Y = (SwizzleLUT[Px] - X) >> 3; int IOffs = (TX + X + ((TY + Y) * Width)) * 4; if (PicaFormat == PICASurfaceFormat.RGBA8) { writer.Write(Input[IOffs + 3]); writer.Write(Input[IOffs + 0]); writer.Write(Input[IOffs + 1]); writer.Write(Input[IOffs + 2]); } else if (PicaFormat == PICASurfaceFormat.RGB8) { writer.Write(Input[IOffs + 0]); writer.Write(Input[IOffs + 1]); writer.Write(Input[IOffs + 2]); } else if (PicaFormat == PICASurfaceFormat.A8) { writer.Write(Input[IOffs]); } else if (PicaFormat == PICASurfaceFormat.L8) { writer.Write(ConvertBRG8ToL( new byte[] { Input[IOffs + 0], Input[IOffs + 1], Input[IOffs + 2] })); } else if (PicaFormat == PICASurfaceFormat.LA8) { writer.Write(Input[IOffs + 3]); writer.Write(ConvertBRG8ToL( new byte[] { Input[IOffs + 0], Input[IOffs + 1], Input[IOffs + 2] })); } else if (PicaFormat == PICASurfaceFormat.RGB565) { ushort R = (ushort)(Convert8To5(Input[IOffs + 0])); ushort G = (ushort)(Convert8To6(Input[IOffs + 1]) << 5); ushort B = (ushort)(Convert8To5(Input[IOffs + 2]) << 11); writer.Write((ushort)(R | G | B)); } else if (PicaFormat == PICASurfaceFormat.RGBA4) { ushort R = (ushort)(Convert8To4(Input[IOffs]) << 4); ushort G = (ushort)(Convert8To4(Input[IOffs + 1]) << 8); ushort B = (ushort)(Convert8To4(Input[IOffs + 2]) << 12); ushort A = (ushort)(Convert8To4(Input[IOffs + 3])); writer.Write((ushort)(R | G | B | A)); } else if (PicaFormat == PICASurfaceFormat.RGBA5551) { ushort R = (ushort)(Convert8To5(Input[IOffs + 0]) << 1); ushort G = (ushort)(Convert8To5(Input[IOffs + 1]) << 6); ushort B = (ushort)(Convert8To5(Input[IOffs + 2]) << 11); ushort A = (ushort)(Convert8To1(Input[IOffs + 3])); writer.Write((ushort)(R | G | B | A)); } else if (PicaFormat == PICASurfaceFormat.LA4) { byte A = Input[IOffs + 3]; byte L = ConvertBRG8ToL( new byte[] { Input[IOffs + 0], Input[IOffs + 1], Input[IOffs + 2] }); writer.Write((byte)((A >> 4) | (L & 0xF0))); } else if (PicaFormat == PICASurfaceFormat.L4) { //Todo this has issues byte L1 = ConvertBRG8ToL( new byte[] { Input[IOffs + 0], Input[IOffs + 1], Input[IOffs + 2] }); writer.Write((byte)(L1 >> 4)); } else if (PicaFormat == PICASurfaceFormat.A4) { //Todo this has issues byte A1 = (byte)(Input[IOffs] >> 4); byte A2 = (byte)(Input[IOffs + 3] & 0xF0); writer.Write((byte)(A1 | A2)); } else if (PicaFormat == PICASurfaceFormat.HiLo8) { writer.Write(Input[IOffs]); writer.Write(Input[IOffs + 1]); } } } } } byte[] newOutput = mem.ToArray(); if (newOutput.Length > 0) { return(newOutput); } else { return(new byte[CalculateLength(Width, Height, PicaFormat)]); } }
public static byte[] DecodeBlock(byte[] Input, int Width, int Height, PICASurfaceFormat picaFormat) { if (picaFormat == PICASurfaceFormat.ETC1 || picaFormat == PICASurfaceFormat.ETC1A4) { return(ETC1.ETC1Decompress(Input, Width, Height, picaFormat == PICASurfaceFormat.ETC1A4)); } byte[] Output = new byte[Width * Height * 4]; int Increment = FmtBPP[(int)picaFormat] / 8; if (Increment == 0) { Increment = 1; } int IOffset = 0; for (int TY = 0; TY < Height; TY += 8) { for (int TX = 0; TX < Width; TX += 8) { for (int Px = 0; Px < 64; Px++) { int X = SwizzleLUT[Px] & 7; int Y = (SwizzleLUT[Px] - X) >> 3; int OOffet = (TX + X + ((Height - 1 - (TY + Y)) * Width)) * 4; switch (picaFormat) { case PICASurfaceFormat.RGBA8: Output[OOffet + 0] = Input[IOffset + 3]; Output[OOffet + 1] = Input[IOffset + 2]; Output[OOffet + 2] = Input[IOffset + 1]; Output[OOffet + 3] = Input[IOffset + 0]; break; case PICASurfaceFormat.RGB8: Output[OOffet + 0] = Input[IOffset + 2]; Output[OOffet + 1] = Input[IOffset + 1]; Output[OOffet + 2] = Input[IOffset + 0]; Output[OOffet + 3] = 0xff; break; case PICASurfaceFormat.RGBA5551: DecodeRGBA5551(Output, OOffet, GetUShort(Input, IOffset)); break; case PICASurfaceFormat.RGB565: DecodeRGB565(Output, OOffet, GetUShort(Input, IOffset)); break; case PICASurfaceFormat.RGBA4: DecodeRGBA4(Output, OOffet, GetUShort(Input, IOffset)); break; case PICASurfaceFormat.LA8: Output[OOffet + 0] = Input[IOffset + 1]; Output[OOffet + 1] = Input[IOffset + 1]; Output[OOffet + 2] = Input[IOffset + 1]; Output[OOffet + 3] = Input[IOffset + 0]; break; case PICASurfaceFormat.HiLo8: Output[OOffet + 0] = Input[IOffset + 1]; Output[OOffet + 1] = Input[IOffset + 0]; Output[OOffet + 2] = 0; Output[OOffet + 3] = 0xff; break; case PICASurfaceFormat.L8: Output[OOffet + 0] = Input[IOffset]; Output[OOffet + 1] = Input[IOffset]; Output[OOffet + 2] = Input[IOffset]; Output[OOffet + 3] = 0xff; break; case PICASurfaceFormat.A8: Output[OOffet + 0] = 0xff; Output[OOffet + 1] = 0xff; Output[OOffet + 2] = 0xff; Output[OOffet + 3] = Input[IOffset]; break; case PICASurfaceFormat.LA4: Output[OOffet + 0] = (byte)((Input[IOffset] >> 4) | (Input[IOffset] & 0xf0)); Output[OOffet + 1] = (byte)((Input[IOffset] >> 4) | (Input[IOffset] & 0xf0)); Output[OOffet + 2] = (byte)((Input[IOffset] >> 4) | (Input[IOffset] & 0xf0)); Output[OOffet + 3] = (byte)((Input[IOffset] << 4) | (Input[IOffset] & 0x0f)); break; case PICASurfaceFormat.L4: int L = (Input[IOffset >> 1] >> ((IOffset & 1) << 2)) & 0xf; Output[OOffet + 0] = (byte)((L << 4) | L); Output[OOffet + 1] = (byte)((L << 4) | L); Output[OOffet + 2] = (byte)((L << 4) | L); Output[OOffet + 3] = 0xff; break; case PICASurfaceFormat.A4: int A = (Input[IOffset >> 1] >> ((IOffset & 1) << 2)) & 0xf; Output[OOffet + 0] = 0xff; Output[OOffet + 1] = 0xff; Output[OOffet + 2] = 0xff; Output[OOffet + 3] = (byte)((A << 4) | A); break; } IOffset += Increment; } } } return(Output); }
public static System.Drawing.Bitmap DecodeBlockToBitmap(byte[] Input, int Width, int Height, PICASurfaceFormat picaFormat) { return(BitmapExtension.GetBitmap(DecodeBlock(Input, Width, Height, picaFormat), Width, Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb)); }
//Much help from encoding thanks to this // https://github.com/Cruel/3dstex/blob/master/src/Encoder.cpp public static byte[] EncodeBlock(byte[] Input, int Width, int Height, PICASurfaceFormat PicaFormat) { int ImageSize = CalculateLength(Width, Height, PicaFormat); if (PicaFormat == PICASurfaceFormat.ETC1) { return(SmashForge.RG_ETC1.encodeETC(BitmapExtension.GetBitmap(Input, Width, Height))); } else if (PicaFormat == PICASurfaceFormat.ETC1A4) { return(SmashForge.RG_ETC1.encodeETCa4(BitmapExtension.GetBitmap(Input, Width, Height))); } var mem = new System.IO.MemoryStream(); using (var writer = new FileWriter(mem)) { for (int TY = 0; TY < Height; TY += 8) { for (int TX = 0; TX < Width; TX += 8) { for (int Px = 0; Px < 64; Px++) { int X = SwizzleLUT[Px] & 7; int Y = (SwizzleLUT[Px] - X) >> 3; int IOffs = (TX + X + ((TY + Y) * Width)) * 4; if (PicaFormat == PICASurfaceFormat.RGBA8) { writer.Write(Input[IOffs + 3]); writer.Write(Input[IOffs + 0]); writer.Write(Input[IOffs + 1]); writer.Write(Input[IOffs + 2]); } else if (PicaFormat == PICASurfaceFormat.RGB8) { writer.Write(Input[IOffs + 0]); writer.Write(Input[IOffs + 1]); writer.Write(Input[IOffs + 2]); } else if (PicaFormat == PICASurfaceFormat.A8) { writer.Write(Input[IOffs]); } else if (PicaFormat == PICASurfaceFormat.L8) { writer.Write(ConvertBRG8ToL( new byte[] { Input[IOffs + 0], Input[IOffs + 1], Input[IOffs + 2] })); } else if (PicaFormat == PICASurfaceFormat.LA8) { writer.Write(Input[IOffs + 3]); writer.Write(ConvertBRG8ToL( new byte[] { Input[IOffs + 0], Input[IOffs + 1], Input[IOffs + 2] })); } else if (PicaFormat == PICASurfaceFormat.RGB565) { ushort R = (ushort)(Convert8To5(Input[IOffs + 0])); ushort G = (ushort)(Convert8To6(Input[IOffs + 1]) << 5); ushort B = (ushort)(Convert8To5(Input[IOffs + 2]) << 11); writer.Write((ushort)(R | G | B)); } else if (PicaFormat == PICASurfaceFormat.RGBA4) { ushort R = (ushort)(Convert8To4(Input[IOffs]) << 4); ushort G = (ushort)(Convert8To4(Input[IOffs + 1]) << 8); ushort B = (ushort)(Convert8To4(Input[IOffs + 2]) << 12); ushort A = (ushort)(Convert8To4(Input[IOffs + 3])); writer.Write((ushort)(R | G | B | A)); } else if (PicaFormat == PICASurfaceFormat.RGBA5551) { ushort R = (ushort)(Convert8To5(Input[IOffs + 0]) << 1); ushort G = (ushort)(Convert8To5(Input[IOffs + 1]) << 6); ushort B = (ushort)(Convert8To5(Input[IOffs + 2]) << 11); ushort A = (ushort)(Convert8To1(Input[IOffs + 3])); writer.Write((ushort)(R | G | B | A)); } else if (PicaFormat == PICASurfaceFormat.LA4) { byte A = Input[IOffs + 3]; byte L = ConvertBRG8ToL( new byte[] { Input[IOffs + 0], Input[IOffs + 1], Input[IOffs + 2] }); writer.Write((byte)((A >> 4) | (L & 0xF0))); } else if (PicaFormat == PICASurfaceFormat.L4) { //Skip alpha channel byte L1 = ConvertBRG8ToL( new byte[] { Input[IOffs + 0], Input[IOffs + 1], Input[IOffs + 2] }); byte L2 = ConvertBRG8ToL( new byte[] { Input[IOffs + 4], Input[IOffs + 5], Input[IOffs + 6] }); writer.Write((byte)((L1 >> 4) | (L2 & 0xF0))); Px++; } else if (PicaFormat == PICASurfaceFormat.A4) { byte A1 = (byte)(Input[IOffs + 3] >> 4); byte A2 = (byte)(Input[IOffs + 7] & 0xF0); writer.Write((byte)(A1 | A2)); Px++; } else if (PicaFormat == PICASurfaceFormat.HiLo8) { writer.Write(Input[IOffs]); writer.Write(Input[IOffs + 1]); } } } } } byte[] newOutput = mem.ToArray(); // if (newOutput.Length != ImageSize) // throw new Exception($"Invalid image size! Expected {ImageSize} got {newOutput.Length}"); if (newOutput.Length > 0) { return(newOutput); } else { return(new byte[CalculateLength(Width, Height, PicaFormat)]); } }
public static byte[] DecodeBlock(byte[] Input, int Width, int Height, PICASurfaceFormat picaFormat, SwizzleSettings settings = null) { if (settings == null) { settings = new SwizzleSettings(); } if (picaFormat == PICASurfaceFormat.ETC1 || picaFormat == PICASurfaceFormat.ETC1A4) { if (settings.Orientation == Orientation.Transpose) { return(ETC1.ETC1Decompress(Input, Width, Height, picaFormat == PICASurfaceFormat.ETC1A4)); } else { return(FlipVertical(Width, Height, ETC1.ETC1Decompress(Input, Width, Height, picaFormat == PICASurfaceFormat.ETC1A4))); } } byte[] Output = new byte[Width * Height * 4]; int Increment = FmtBPP[(int)picaFormat] / 8; if (Increment == 0) { Increment = 1; } Console.WriteLine($"Increment {Increment} Input {Input.Length} {Width} {Height} {picaFormat}"); int stride = Width; int IOffset = 0; for (int TY = 0; TY < Height; TY += 8) { for (int TX = 0; TX < Width; TX += 8) { for (int Px = 0; Px < 64; Px++) { int X = Morton7(Px); int Y = Morton7(Px >> 1); int OOffet = ((TY + Y) * stride + TX + X) * 4; if (OOffet + 4 >= Output.Length) { break; } DecodeFormat(Output, Input, OOffet, IOffset, picaFormat); IOffset += Increment; } } } int tile_width = (int)Math.Ceiling(Width / 8.0f); int tile_height = (int)Math.Ceiling(Height / 8.0f); stride = Width; IOffset = 0; for (int TY = 0; TY < tile_width; TY += 8) { for (int TX = 0; TX < tile_height; TX += 8) { for (int x = 0; x < 2; x++) { for (int y = 0; y < 2; y++) { for (int x2 = 0; x2 < 2; x2++) { for (int y2 = 0; y2 < 2; y2++) { for (int x3 = 0; x3 < 2; x3++) { for (int y3 = 0; y3 < 2; y3++) { var pixel_x = (x3 + (x2 * 2) + (x * 4) + (TX * 8)); var pixel_y = (y3 + (y2 * 2) + (y * 4) + (TY * 8)); if (pixel_y >= Height || pixel_y >= Height) { continue; } // same for the x and the input data width if (pixel_x >= Width || pixel_x >= Width) { continue; } } } } } } } for (int Px = 0; Px < 64; Px++) { int X = Morton7(Px); int Y = Morton7(Px >> 1); int OOffet = ((TY + Y) * stride + TX + X) * 4; if (OOffet + 4 >= Output.Length) { break; } DecodeFormat(Output, Input, OOffet, IOffset, picaFormat); IOffset += Increment; } } } if (settings.Orientation == Orientation.Transpose) { return(Output); } else { return(FlipVertical(Width, Height, Output)); } }
public static System.Drawing.Bitmap DecodeBlockToBitmap(byte[] Input, int Width, int Height, PICASurfaceFormat picaFormat) { return(BitmapExtension.CreateBitmap(ImageUtility.ConvertBgraToRgba(DecodeBlock(Input, Width, Height, picaFormat)), Width, Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb)); }
private static void DecodeFormat(byte[] Output, byte[] Input, int OOffet, int IOffset, PICASurfaceFormat picaFormat) { switch (picaFormat) { case PICASurfaceFormat.RGBA8: Output[OOffet + 0] = Input[IOffset + 3]; Output[OOffet + 1] = Input[IOffset + 2]; Output[OOffet + 2] = Input[IOffset + 1]; Output[OOffet + 3] = Input[IOffset + 0]; break; case PICASurfaceFormat.RGB8: Output[OOffet + 0] = Input[IOffset + 2]; Output[OOffet + 1] = Input[IOffset + 1]; Output[OOffet + 2] = Input[IOffset + 0]; Output[OOffet + 3] = 0xff; break; case PICASurfaceFormat.RGBA5551: DecodeRGBA5551(Output, OOffet, GetUShort(Input, IOffset)); break; case PICASurfaceFormat.RGB565: DecodeRGB565(Output, OOffet, GetUShort(Input, IOffset)); break; case PICASurfaceFormat.RGBA4: DecodeRGBA4(Output, OOffet, GetUShort(Input, IOffset)); break; case PICASurfaceFormat.LA8: Output[OOffet + 0] = Input[IOffset + 1]; Output[OOffet + 1] = Input[IOffset + 1]; Output[OOffet + 2] = Input[IOffset + 1]; Output[OOffet + 3] = Input[IOffset + 0]; break; case PICASurfaceFormat.HiLo8: Output[OOffet + 0] = Input[IOffset + 1]; Output[OOffet + 1] = Input[IOffset + 0]; Output[OOffet + 2] = 0; Output[OOffet + 3] = 0xff; break; case PICASurfaceFormat.L8: Output[OOffet + 0] = Input[IOffset]; Output[OOffet + 1] = Input[IOffset]; Output[OOffet + 2] = Input[IOffset]; Output[OOffet + 3] = 0xff; break; case PICASurfaceFormat.A8: Output[OOffet + 0] = 0xff; Output[OOffet + 1] = 0xff; Output[OOffet + 2] = 0xff; Output[OOffet + 3] = Input[IOffset]; break; case PICASurfaceFormat.LA4: byte val = Convert4To8((Input[IOffset] & 0xF0) >> 4); byte a = Convert4To8((Input[IOffset] & 0x0F)); Output[OOffet + 0] = val; Output[OOffet + 1] = val; Output[OOffet + 2] = val; Output[OOffet + 3] = a; break; case PICASurfaceFormat.L4: int L = (Input[IOffset >> 1] >> ((IOffset & 1) << 2)) & 0xf; Output[OOffet + 0] = (byte)((L << 4) | L); Output[OOffet + 1] = (byte)((L << 4) | L); Output[OOffet + 2] = (byte)((L << 4) | L); Output[OOffet + 3] = 0xff; break; case PICASurfaceFormat.A4: int A = (Input[IOffset >> 1] >> ((IOffset & 1) << 2)) & 0xf; Output[OOffet + 0] = 0xff; Output[OOffet + 1] = 0xff; Output[OOffet + 2] = 0xff; Output[OOffet + 3] = (byte)((A << 4) | A); break; } }