public static Color[] DecodeETC1(ulong Data, ulong Alpha = ~0ul) { Color[] Result = new Color[4 * 4]; bool diffbit = ((Data >> 33) & 1) == 1; bool flipbit = ((Data >> 32) & 1) == 1; //0: |||, 1: |-| int r1, r2, g1, g2, b1, b2; if (diffbit) //'differential' mode { int r = (int)((Data >> 59) & 0x1F); int g = (int)((Data >> 51) & 0x1F); int b = (int)((Data >> 43) & 0x1F); r1 = (r << 3) | ((r & 0x1C) >> 2); g1 = (g << 3) | ((g & 0x1C) >> 2); b1 = (b << 3) | ((b & 0x1C) >> 2); r += (int)((Data >> 56) & 0x7) << 29 >> 29; g += (int)((Data >> 48) & 0x7) << 29 >> 29; b += (int)((Data >> 40) & 0x7) << 29 >> 29; r2 = (r << 3) | ((r & 0x1C) >> 2); g2 = (g << 3) | ((g & 0x1C) >> 2); b2 = (b << 3) | ((b & 0x1C) >> 2); } else //'individual' mode { r1 = (int)((Data >> 60) & 0xF) * 0x11; g1 = (int)((Data >> 52) & 0xF) * 0x11; b1 = (int)((Data >> 44) & 0xF) * 0x11; r2 = (int)((Data >> 56) & 0xF) * 0x11; g2 = (int)((Data >> 48) & 0xF) * 0x11; b2 = (int)((Data >> 40) & 0xF) * 0x11; } int Table1 = (int)((Data >> 37) & 0x7); int Table2 = (int)((Data >> 34) & 0x7); for (int y3 = 0; y3 < 4; y3++) { for (int x3 = 0; x3 < 4; x3++) { //if (x + j + x3 >= physicalwidth) continue; //if (y + i + y3 >= physicalheight) continue; int val = (int)((Data >> (x3 * 4 + y3)) & 0x1); bool neg = ((Data >> (x3 * 4 + y3 + 16)) & 0x1) == 1; uint c; if ((flipbit && y3 < 2) || (!flipbit && x3 < 2)) { int add = ETC1Modifiers[Table1, val] * (neg ? -1 : 1); c = GFXUtil.ToColorFormat((byte)(((Alpha >> ((x3 * 4 + y3) * 4)) & 0xF) * 0x11), (byte)ColorClamp(r1 + add), (byte)ColorClamp(g1 + add), (byte)ColorClamp(b1 + add), ColorFormat.ARGB8888); } else { int add = ETC1Modifiers[Table2, val] * (neg ? -1 : 1); c = GFXUtil.ToColorFormat((byte)(((Alpha >> ((x3 * 4 + y3) * 4)) & 0xF) * 0x11), (byte)ColorClamp(r2 + add), (byte)ColorClamp(g2 + add), (byte)ColorClamp(b2 + add), ColorFormat.ARGB8888); } Result[y3 * 4 + x3] = Color.FromArgb((int)c); //res[(i + y3) * stride + x + j + x3] = c; } } return(Result); }
public static unsafe Bitmap ToBitmap(byte[] Data, int Offset, int Width, int Height, ImageFormat Format, bool ExactSize = false) { if (Data == null || Data.Length < 1 || Offset < 0 || Offset >= Data.Length || Width < 1 || Height < 1) { return(null); } if (ExactSize && ((Width % 8) != 0 || (Height % 8) != 0)) { return(null); } int physicalwidth = Width; int physicalheight = Height; if (!ExactSize) { Width = 1 << (int)Math.Ceiling(Math.Log(Width, 2)); Height = 1 << (int)Math.Ceiling(Math.Log(Height, 2)); } Bitmap bitm = new Bitmap(physicalwidth, physicalheight); BitmapData d = bitm.LockBits(new Rectangle(0, 0, bitm.Width, bitm.Height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb); uint * res = (uint *)d.Scan0; int offs = Offset;//0; int stride = d.Stride / 4; switch (Format) { case ImageFormat.RGBA8: for (int y = 0; y < Height; y += 8) { for (int x = 0; x < Width; x += 8) { for (int i = 0; i < 64; i++) { int x2 = i % 8; if (x + x2 >= physicalwidth) { continue; } int y2 = i / 8; if (y + y2 >= physicalheight) { continue; } int pos = TileOrder[x2 % 4 + y2 % 4 * 4] + 16 * (x2 / 4) + 32 * (y2 / 4); res[(y + y2) * stride + x + x2] = GFXUtil.ConvertColorFormat( IOUtil.ReadU32LE(Data, offs + pos * 4), ColorFormat.RGBA8888, ColorFormat.ARGB8888); /*GFXUtil.ToArgb( * Data[offs + pos * 4], * Data[offs + pos * 4 + 3], * Data[offs + pos * 4 + 2], * Data[offs + pos * 4 + 1] * );*/ } offs += 64 * 4; } } break; case ImageFormat.RGB8: for (int y = 0; y < Height; y += 8) { for (int x = 0; x < Width; x += 8) { for (int i = 0; i < 64; i++) { int x2 = i % 8; if (x + x2 >= physicalwidth) { continue; } int y2 = i / 8; if (y + y2 >= physicalheight) { continue; } int pos = TileOrder[x2 % 4 + y2 % 4 * 4] + 16 * (x2 / 4) + 32 * (y2 / 4); res[(y + y2) * stride + x + x2] = GFXUtil.ConvertColorFormat( IOUtil.ReadU24LE(Data, offs + pos * 3), ColorFormat.RGB888, ColorFormat.ARGB8888); /*GFXUtil.ToArgb( * Data[offs + pos * 3 + 2], * Data[offs + pos * 3 + 1], * Data[offs + pos * 3 + 0] * );*/ } offs += 64 * 3; } } break; case ImageFormat.RGBA5551: for (int y = 0; y < Height; y += 8) { for (int x = 0; x < Width; x += 8) { for (int i = 0; i < 64; i++) { int x2 = i % 8; if (x + x2 >= physicalwidth) { continue; } int y2 = i / 8; if (y + y2 >= physicalheight) { continue; } int pos = TileOrder[x2 % 4 + y2 % 4 * 4] + 16 * (x2 / 4) + 32 * (y2 / 4); res[(y + y2) * stride + x + x2] = GFXUtil.ConvertColorFormat( IOUtil.ReadU16LE(Data, offs + pos * 2), ColorFormat.ARGB1555, ColorFormat.ARGB8888); //GFXUtil.ARGB1555ToArgb(IOUtil.ReadU16LE(Data, offs + pos * 2)); } offs += 64 * 2; } } break; case ImageFormat.RGB565: for (int y = 0; y < Height; y += 8) { for (int x = 0; x < Width; x += 8) { for (int i = 0; i < 64; i++) { int x2 = i % 8; if (x + x2 >= physicalwidth) { continue; } int y2 = i / 8; if (y + y2 >= physicalheight) { continue; } int pos = TileOrder[x2 % 4 + y2 % 4 * 4] + 16 * (x2 / 4) + 32 * (y2 / 4); res[(y + y2) * stride + x + x2] = GFXUtil.ConvertColorFormat( IOUtil.ReadU16LE(Data, offs + pos * 2), ColorFormat.RGB565, ColorFormat.ARGB8888); //GFXUtil.RGB565ToArgb(IOUtil.ReadU16LE(Data, offs + pos * 2)); } offs += 64 * 2; } } break; case ImageFormat.RGBA4: for (int y = 0; y < Height; y += 8) { for (int x = 0; x < Width; x += 8) { for (int i = 0; i < 64; i++) { int x2 = i % 8; if (x + x2 >= physicalwidth) { continue; } int y2 = i / 8; if (y + y2 >= physicalheight) { continue; } int pos = TileOrder[x2 % 4 + y2 % 4 * 4] + 16 * (x2 / 4) + 32 * (y2 / 4); res[(y + y2) * stride + x + x2] = GFXUtil.ConvertColorFormat( IOUtil.ReadU16LE(Data, offs + pos * 2), ColorFormat.RGBA4444, ColorFormat.ARGB8888); /*GFXUtil.ToArgb( * (byte)((Data[offs + pos * 2] & 0xF) * 0x11), * (byte)((Data[offs + pos * 2 + 1] >> 4) * 0x11), * (byte)((Data[offs + pos * 2 + 1] & 0xF) * 0x11), * (byte)((Data[offs + pos * 2] >> 4) * 0x11) * );*/ } offs += 64 * 2; } } break; case ImageFormat.LA8: for (int y = 0; y < Height; y += 8) { for (int x = 0; x < Width; x += 8) { for (int i = 0; i < 64; i++) { int x2 = i % 8; if (x + x2 >= physicalwidth) { continue; } int y2 = i / 8; if (y + y2 >= physicalheight) { continue; } int pos = TileOrder[x2 % 4 + y2 % 4 * 4] + 16 * (x2 / 4) + 32 * (y2 / 4); res[(y + y2) * stride + x + x2] = GFXUtil.ToColorFormat( Data[offs + pos * 2], Data[offs + pos * 2 + 1], Data[offs + pos * 2 + 1], Data[offs + pos * 2 + 1], ColorFormat.ARGB8888 ); } offs += 64 * 2; } } break; case ImageFormat.HILO8: for (int y = 0; y < Height; y += 8) { for (int x = 0; x < Width; x += 8) { for (int i = 0; i < 64; i++) { int x2 = i % 8; if (x + x2 >= physicalwidth) { continue; } int y2 = i / 8; if (y + y2 >= physicalheight) { continue; } int pos = TileOrder[x2 % 4 + y2 % 4 * 4] + 16 * (x2 / 4) + 32 * (y2 / 4); res[(y + y2) * stride + x + x2] = GFXUtil.ToColorFormat( Data[offs + pos * 2], Data[offs + pos * 2 + 1], Data[offs + pos * 2 + 1], Data[offs + pos * 2 + 1], ColorFormat.ARGB8888 ); } offs += 64 * 2; } } break; case ImageFormat.L8: for (int y = 0; y < Height; y += 8) { for (int x = 0; x < Width; x += 8) { for (int i = 0; i < 64; i++) { int x2 = i % 8; if (x + x2 >= physicalwidth) { continue; } int y2 = i / 8; if (y + y2 >= physicalheight) { continue; } int pos = TileOrder[x2 % 4 + y2 % 4 * 4] + 16 * (x2 / 4) + 32 * (y2 / 4); res[(y + y2) * stride + x + x2] = GFXUtil.ToColorFormat( Data[offs + pos], Data[offs + pos], Data[offs + pos], ColorFormat.ARGB8888 ); } offs += 64; } } break; case ImageFormat.A8: for (int y = 0; y < Height; y += 8) { for (int x = 0; x < Width; x += 8) { for (int i = 0; i < 64; i++) { int x2 = i % 8; if (x + x2 >= physicalwidth) { continue; } int y2 = i / 8; if (y + y2 >= physicalheight) { continue; } int pos = TileOrder[x2 % 4 + y2 % 4 * 4] + 16 * (x2 / 4) + 32 * (y2 / 4); res[(y + y2) * stride + x + x2] = GFXUtil.ToColorFormat( Data[offs + pos], 255, 255, 255, ColorFormat.ARGB8888 ); } offs += 64; } } break; case ImageFormat.LA4: for (int y = 0; y < Height; y += 8) { for (int x = 0; x < Width; x += 8) { for (int i = 0; i < 64; i++) { int x2 = i % 8; if (x + x2 >= physicalwidth) { continue; } int y2 = i / 8; if (y + y2 >= physicalheight) { continue; } int pos = TileOrder[x2 % 4 + y2 % 4 * 4] + 16 * (x2 / 4) + 32 * (y2 / 4); res[(y + y2) * stride + x + x2] = GFXUtil.ToColorFormat( (byte)((Data[offs + pos] & 0xF) * 0x11), (byte)((Data[offs + pos] >> 4) * 0x11), (byte)((Data[offs + pos] >> 4) * 0x11), (byte)((Data[offs + pos] >> 4) * 0x11), ColorFormat.ARGB8888 ); } offs += 64; } } break; case ImageFormat.L4: for (int y = 0; y < Height; y += 8) { for (int x = 0; x < Width; x += 8) { for (int i = 0; i < 64; i++) { int x2 = i % 8; if (x + x2 >= physicalwidth) { continue; } int y2 = i / 8; if (y + y2 >= physicalheight) { continue; } int pos = TileOrder[x2 % 4 + y2 % 4 * 4] + 16 * (x2 / 4) + 32 * (y2 / 4); int shift = (pos & 1) * 4; res[(y + y2) * stride + x + x2] = GFXUtil.ToColorFormat( (byte)(((Data[offs + pos / 2] >> shift) & 0xF) * 0x11), (byte)(((Data[offs + pos / 2] >> shift) & 0xF) * 0x11), (byte)(((Data[offs + pos / 2] >> shift) & 0xF) * 0x11), ColorFormat.ARGB8888 ); } offs += 64 / 2; } } break; case ImageFormat.A4: for (int y = 0; y < Height; y += 8) { for (int x = 0; x < Width; x += 8) { for (int i = 0; i < 64; i++) { int x2 = i % 8; if (x + x2 >= physicalwidth) { continue; } int y2 = i / 8; if (y + y2 >= physicalheight) { continue; } int pos = TileOrder[x2 % 4 + y2 % 4 * 4] + 16 * (x2 / 4) + 32 * (y2 / 4); int shift = (pos & 1) * 4; res[(y + y2) * stride + x + x2] = GFXUtil.ToColorFormat( (byte)(((Data[offs + pos / 2] >> shift) & 0xF) * 0x11), 255, 255, 255, ColorFormat.ARGB8888 ); } offs += 64 / 2; } } break; case ImageFormat.ETC1: //Some reference: http://www.khronos.org/registry/gles/extensions/OES/OES_compressed_ETC1_RGB8_texture.txt case ImageFormat.ETC1A4: { for (int y = 0; y < Height; y += 8) { for (int x = 0; x < Width; x += 8) { for (int i = 0; i < 8; i += 4) { for (int j = 0; j < 8; j += 4) { ulong alpha = 0xFFFFFFFFFFFFFFFF; if (Format == ImageFormat.ETC1A4) { alpha = IOUtil.ReadU64LE(Data, offs); offs += 8; } ulong data = IOUtil.ReadU64LE(Data, offs); bool diffbit = ((data >> 33) & 1) == 1; bool flipbit = ((data >> 32) & 1) == 1; //0: |||, 1: |-| int r1, r2, g1, g2, b1, b2; if (diffbit) //'differential' mode { int r = (int)((data >> 59) & 0x1F); int g = (int)((data >> 51) & 0x1F); int b = (int)((data >> 43) & 0x1F); r1 = (r << 3) | ((r & 0x1C) >> 2); g1 = (g << 3) | ((g & 0x1C) >> 2); b1 = (b << 3) | ((b & 0x1C) >> 2); r += (int)((data >> 56) & 0x7) << 29 >> 29; g += (int)((data >> 48) & 0x7) << 29 >> 29; b += (int)((data >> 40) & 0x7) << 29 >> 29; r2 = (r << 3) | ((r & 0x1C) >> 2); g2 = (g << 3) | ((g & 0x1C) >> 2); b2 = (b << 3) | ((b & 0x1C) >> 2); } else //'individual' mode { r1 = (int)((data >> 60) & 0xF) * 0x11; g1 = (int)((data >> 52) & 0xF) * 0x11; b1 = (int)((data >> 44) & 0xF) * 0x11; r2 = (int)((data >> 56) & 0xF) * 0x11; g2 = (int)((data >> 48) & 0xF) * 0x11; b2 = (int)((data >> 40) & 0xF) * 0x11; } int Table1 = (int)((data >> 37) & 0x7); int Table2 = (int)((data >> 34) & 0x7); for (int y3 = 0; y3 < 4; y3++) { for (int x3 = 0; x3 < 4; x3++) { if (x + j + x3 >= physicalwidth) { continue; } if (y + i + y3 >= physicalheight) { continue; } int val = (int)((data >> (x3 * 4 + y3)) & 0x1); bool neg = ((data >> (x3 * 4 + y3 + 16)) & 0x1) == 1; uint c; if ((flipbit && y3 < 2) || (!flipbit && x3 < 2)) { int add = ETC1Modifiers[Table1, val] * (neg ? -1 : 1); c = GFXUtil.ToColorFormat((byte)(((alpha >> ((x3 * 4 + y3) * 4)) & 0xF) * 0x11), (byte)ColorClamp(r1 + add), (byte)ColorClamp(g1 + add), (byte)ColorClamp(b1 + add), ColorFormat.ARGB8888); } else { int add = ETC1Modifiers[Table2, val] * (neg ? -1 : 1); c = GFXUtil.ToColorFormat((byte)(((alpha >> ((x3 * 4 + y3) * 4)) & 0xF) * 0x11), (byte)ColorClamp(r2 + add), (byte)ColorClamp(g2 + add), (byte)ColorClamp(b2 + add), ColorFormat.ARGB8888); } res[(i + y3) * stride + x + j + x3] = c; } } offs += 8; } } } res += stride * 8; } } break; } bitm.UnlockBits(d); return(bitm); }
public static unsafe byte[] FromBitmap(Bitmap Picture, ImageFormat Format, bool ExactSize = false) { if (ExactSize && ((Picture.Width % 8) != 0 || (Picture.Height % 8) != 0)) { return(null); } int physicalwidth = Picture.Width; int physicalheight = Picture.Height; int ConvWidth = Picture.Width; int ConvHeight = Picture.Height; if (!ExactSize) { ConvWidth = 1 << (int)Math.Ceiling(Math.Log(Picture.Width, 2)); ConvHeight = 1 << (int)Math.Ceiling(Math.Log(Picture.Height, 2)); } BitmapData d = Picture.LockBits(new Rectangle(0, 0, Picture.Width, Picture.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb); uint * res = (uint *)d.Scan0; byte[] result = new byte[ConvWidth * ConvHeight * GetBpp(Format) / 8]; int offs = 0; switch (Format) { case ImageFormat.RGBA8: for (int y = 0; y < ConvHeight; y += 8) { for (int x = 0; x < ConvWidth; x += 8) { for (int i = 0; i < 64; i++) { int x2 = i % 8; if (x + x2 >= physicalwidth) { continue; } int y2 = i / 8; if (y + y2 >= physicalheight) { continue; } int pos = TileOrder[x2 % 4 + y2 % 4 * 4] + 16 * (x2 / 4) + 32 * (y2 / 4); Color c = Color.FromArgb((int)res[(y + y2) * d.Stride / 4 + x + x2]); result[offs + pos * 4 + 0] = c.A; result[offs + pos * 4 + 1] = c.B; result[offs + pos * 4 + 2] = c.G; result[offs + pos * 4 + 3] = c.R; } offs += 64 * 4; } } break; case ImageFormat.RGB8: for (int y = 0; y < ConvHeight; y += 8) { for (int x = 0; x < ConvWidth; x += 8) { for (int i = 0; i < 64; i++) { int x2 = i % 8; if (x + x2 >= physicalwidth) { continue; } int y2 = i / 8; if (y + y2 >= physicalheight) { continue; } int pos = TileOrder[x2 % 4 + y2 % 4 * 4] + 16 * (x2 / 4) + 32 * (y2 / 4); Color c = Color.FromArgb((int)res[(y + y2) * d.Stride / 4 + x + x2]); result[offs + pos * 3 + 0] = c.B; result[offs + pos * 3 + 1] = c.G; result[offs + pos * 3 + 2] = c.R; } offs += 64 * 3; } } break; case ImageFormat.RGB565: for (int y = 0; y < ConvHeight; y += 8) { for (int x = 0; x < ConvWidth; x += 8) { for (int i = 0; i < 64; i++) { int x2 = i % 8; if (x + x2 >= physicalwidth) { continue; } int y2 = i / 8; if (y + y2 >= physicalheight) { continue; } int pos = TileOrder[x2 % 4 + y2 % 4 * 4] + 16 * (x2 / 4) + 32 * (y2 / 4); IOUtil.WriteU16LE(result, offs + pos * 2, (ushort)GFXUtil.ConvertColorFormat(res[(y + y2) * d.Stride / 4 + x + x2], ColorFormat.ARGB8888, ColorFormat.RGB565)); //GFXUtil.ArgbToRGB565(res[(y + y2) * d.Stride / 4 + x + x2])); } offs += 64 * 2; } } break; case ImageFormat.ETC1: case ImageFormat.ETC1A4: for (int y = 0; y < ConvHeight; y += 8) { for (int x = 0; x < ConvWidth; x += 8) { for (int i = 0; i < 8; i += 4) { for (int j = 0; j < 8; j += 4) { if (Format == ImageFormat.ETC1A4) { ulong alpha = 0; int iiii = 0; for (int xx = 0; xx < 4; xx++) { for (int yy = 0; yy < 4; yy++) { uint color; if (x + j + xx >= physicalwidth) { color = 0x00FFFFFF; } else if (y + i + yy >= physicalheight) { color = 0x00FFFFFF; } else { color = res[((y + i + yy) * (d.Stride / 4)) + x + j + xx]; } uint a = color >> 24; a >>= 4; alpha |= (ulong)a << (iiii * 4); iiii++; } } IOUtil.WriteU64LE(result, offs, alpha); offs += 8; } Color[] pixels = new Color[4 * 4]; for (int yy = 0; yy < 4; yy++) { for (int xx = 0; xx < 4; xx++) { if (x + j + xx >= physicalwidth) { pixels[yy * 4 + xx] = Color.Transparent; } else if (y + i + yy >= physicalheight) { pixels[yy * 4 + xx] = Color.Transparent; } else { pixels[yy * 4 + xx] = Color.FromArgb((int)res[((y + i + yy) * (d.Stride / 4)) + x + j + xx]); } } } IOUtil.WriteU64LE(result, offs, ETC1.GenETC1(pixels)); offs += 8; } } } } break; default: throw new NotImplementedException("This format is not implemented yet."); } return(result); }