static private void RefineMinMax(ARGB_Rev[] Colors, ref ARGB_Rev Min, ref ARGB_Rev Max) { int MinListCount = 0; int MaxListCount = 0; var MinAccum = default(RGBA32); var MaxAccum = default(RGBA32); foreach (var color in Colors) { int DistanceMin = ARGB_Rev.DistanceRGB(color, Min); int DistanceMax = ARGB_Rev.DistanceRGB(color, Max); if (DistanceMin <= DistanceMax) { MinAccum += color; MinListCount++; } if (DistanceMax <= DistanceMin) { MaxAccum += color; MaxListCount++; } } Min = MinAccum / MinListCount; Max = MaxAccum / MaxListCount; Min.A = 0xFF; Max.A = 0xFF; }
internal void Decode(ref ARGB_Rev[] output) { var color_table = new ARGB_Rev[4]; color_table[0] = ColorUtils.Encode(ColorFormats.RGBA_5650, colors0); color_table[1] = ColorUtils.Encode(ColorFormats.RGBA_5650, colors1); MathUtils.Swap(ref color_table[0].R, ref color_table[0].B); MathUtils.Swap(ref color_table[1].R, ref color_table[1].B); // Color table. if (colors0 > colors1) { color_table[2] = ColorUtils.Mix(color_table[0], color_table[1], 3, 2, 1); color_table[3] = ColorUtils.Mix(color_table[0], color_table[1], 3, 1, 2); } else { color_table[2] = ColorUtils.Mix(color_table[0], color_table[1], 2, 1, 1); color_table[3] = Color.FromArgb(0, 0, 0, 0); } var color_data_transfer = new byte[16]; transferColorData(ref color_data_transfer, extract: true); for (int n = 0; n < 16; n++) { output[n] = color_table[color_data_transfer[n]]; } }
static public Y_CO_CG_A ConvertRGBToCoCg_Y(ARGB_Rev Input) { return(new Y_CO_CG_A() { CO = CLAMP_BYTE(RGB_TO_YCOCG_CO(Input) + 128), CG = CLAMP_BYTE(RGB_TO_YCOCG_CG(Input) + 128), A = Input.A, Y = CLAMP_BYTE(RGB_TO_YCOCG_Y(Input)), }); }
static public ARGB_Rev[] GeneratePallete2(ARGB_Rev C0, ARGB_Rev C1) { return(new ARGB_Rev[] { C0, C1, new ARGB_Rev(0, 0, 0, 0), ColorUtils.Mix(C0, C1, 2, 1, 1), }); }
static public ARGB_Rev[] GeneratePallete1(ARGB_Rev C0, ARGB_Rev C1) { return(new ARGB_Rev[] { C0, C1, ColorUtils.Mix(C0, C1, 3, 2, 1), ColorUtils.Mix(C0, C1, 3, 1, 2), }); }
/// <summary> /// /// </summary> /// <param name="_out"></param> /// <param name="v"></param> static void stb__From16Bit(out ARGB_Rev _out, ushort v) { int rv = (v & 0xf800) >> 11; int gv = (v & 0x07e0) >> 5; int bv = (v & 0x001f) >> 0; _out.R = stb__Expand5[rv]; _out.G = stb__Expand6[gv]; _out.B = stb__Expand5[bv]; _out.A = 0; }
public void TestDecodeBlock() { var Block1Data = new byte[] { 0xD5, 0x5B, 0x5D, 0xB2, 0x49, 0x00, 0xFF, 0xB2, 0xE6, 0xF6, 0xDE, 0x94, 0xFF, 0xFF, 0x02, 0xFE }; var Block = new MemoryStream(Block1Data).ReadStruct <DXT5.AlphaColorBlock>(); var Colors = new ARGB_Rev[16]; Block.Decode(ref Colors); Assert.AreEqual( "#E0D6A973,#E0D6A900,#E0D6A900,#E0D6A900,#E0D6A9BC,#E0D6A95B,#E0D6A95B,#E0D6A95B," + "#E3DAAED5,#E0D6A9D5,#E0D6A9D5,#E0D6A9D5,#E3DAAE8B,#E6DEB4FF,#E6DEB4FF,#E6DEB4FF" , Colors.ToStringArray(",") ); }
public void TestCompressDXT5() { var Colors1 = new ARGB_Rev[16] { "#E0D6A973", "#E0D6A900", "#E0D6A900", "#E0D6A900", "#E0D6A9BC", "#E0D6A95B", "#E0D6A95B", "#E0D6A95B", "#E3DAAED5", "#E0D6A9D5", "#E0D6A9D5", "#E0D6A9D5", "#E3DAAE8B", "#E6DEB4FF", "#E6DEB4FF", "#E6DEB4FF", }; var Colors2 = new ARGB_Rev[16]; var Block = default(DXT5.AlphaColorBlock); //var Color1 = default(ARGB_Rev); //var Color2 = default(ARGB_Rev); CompressDXT.CompressBlockDXT5(Colors1, out Block, CompressDXT.CompressionMode.Normal); Console.WriteLine(StructUtils.StructToBytes(Block).ToHexString().ToUpper()); Block.Decode(ref Colors2); Assert.AreEqual( "#E0D6A973,#E0D6A900,#E0D6A900,#E0D6A900,#E0D6A9BC,#E0D6A95B,#E0D6A95B,#E0D6A95B,#E3DAAED5,#E0D6A9D5,#E0D6A9D5,#E0D6A9D5,#E3DAAE8B,#E6DEB4FF,#E6DEB4FF,#E6DEB4FF", Colors1.ToStringArray(",") ); Assert.AreEqual( "#DED6AC6D,#DED6AC00,#DED6AC00,#DED6AC00,#DED6ACB6,#DED6AC6D,#DED6AC6D,#DED6AC6D,#E0D8AEDA,#DED6ACDA,#DED6ACDA,#DED6ACDA,#E0D8AE91,#E6DEB4FF,#E6DEB4FF,#E6DEB4FF", Colors2.ToStringArray(",") ); //CompressionSimpleDXT5.FindColorPair(Colors1, out Color1, out Color2); //CompressYCoCgDXT5.CompressBlock(Colors1, ref Block); }
static public void FindColorPair(ARGB_Rev[] Colors, out ARGB_Rev Min, out ARGB_Rev Max) { FindBoundingBox(Colors, out Min, out Max); RefineMinMax(Colors, ref Min, ref Max); var Palette1 = GeneratePallete1(Min, Max); var Palette2 = GeneratePallete2(Min, Max); byte[] Indices1 = new byte[16]; byte[] Indices2 = new byte[16]; int Distance1 = ColorToIndexed(Colors, Palette1, Indices1); int Distance2 = ColorToIndexed(Colors, Palette2, Indices2); Console.WriteLine(Min); Console.WriteLine(Max); Console.WriteLine(Distance1); Console.WriteLine(Distance2); }
static public int ColorToIndexed(ARGB_Rev[] Colors, ARGB_Rev[] Palette, byte[] Indices) { int TotalDistance = 0; for (int n = 0; n < 16; n++) { var Color = Colors[n]; int MinDistance = int.MaxValue; int BestIndex = 0; for (int m = 0; m < 4; m++) { var Distance = ARGB_Rev.DistanceRGB(Color, Palette[m]); if (Distance < MinDistance) { BestIndex = m; MinDistance = Distance; } } TotalDistance += MinDistance; Indices[n] = (byte)BestIndex; } return(TotalDistance); }
static public void DrawGlow(Bitmap Bitmap, DistanceEntry[,] _DistanceMap, float GlowDistance, ARGB_Rev GlowColor, Func <float, float> Function = null) { var TransparentColor = (ARGB_Rev)"#00000000"; if (Function == null) { Function = (v) => v; } Bitmap.Shader((color, x, y) => { var Dist = (float)_DistanceMap[x, y].Distance; if (Dist == 0 && color.A == 0xFF) { return(color); } if (Dist > GlowDistance) { return(new ARGB_Rev(0, 0, 0, 0)); } var GenColor = ARGB_Rev.Interpolate(GlowColor, TransparentColor, 1 - Function(Dist / GlowDistance)); return((Dist == 0) ? ARGB_Rev.Mix(color, GenColor) : GenColor); }); }
static private void FindBoundingBox(ARGB_Rev[] Colors, out ARGB_Rev Min, out ARGB_Rev Max) { Min = Color.FromArgb(0xFF, 0xFF, 0xFF, 0x00); Max = Color.FromArgb(0x00, 0x00, 0x00, 0x00); foreach (var color in Colors) { Min.R = Math.Min(color.R, Min.R); Min.G = Math.Min(color.G, Min.G); Min.B = Math.Min(color.B, Min.B); //Min.A = Math.Min(color.A, Min.A); Max.R = Math.Max(color.R, Max.R); Max.G = Math.Max(color.G, Max.G); Max.B = Math.Max(color.B, Max.B); //Max.A = Math.Max(color.A, Max.A); } Min.A = 0xFF; Max.A = 0xFF; Console.WriteLine(Min); Console.WriteLine(Max); }
static public void DrawGlow(Bitmap Bitmap, DistanceEntry[,] _DistanceMap, float GlowDistance, ARGB_Rev GlowColor, float Min, float Max = 1.0f) { DistanceMap.DrawGlow(Bitmap, _DistanceMap, GlowDistance, GlowColor, (f) => { return MathUtils.SmoothStep(Min, Max, f); }); }
/// <summary> /// /// </summary> /// <param name="Bitmap"></param> /// <param name="Stream"></param> /// <param name="mode"></param> public void SaveSwizzled3D(BitmapList BitmapList, Stream Stream, CompressDXT.CompressionMode mode = CompressDXT.CompressionMode.Normal, bool ShowWarnings = false) { int Width = BitmapList.Bitmaps[0].Width, Height = BitmapList.Bitmaps[0].Height, Depth = BitmapList.Bitmaps.Length; if ((Width % 4) != 0 || (Height % 4) != 0) { throw (new InvalidDataException()); } BitmapList.LockUnlockBits(PixelFormat.Format32bppArgb, (BitmapDatas) => { var Bases = new ARGB_Rev *[Depth]; for (int n = 0; n < Depth; n++) { Bases[n] = (ARGB_Rev *)BitmapDatas[n].Scan0.ToPointer(); } int BlockWidth = Width / 4; int BlockHeight = Height / 4; //var BlockCount = BlockWidth * BlockHeight; var ExpectedBlockCount = BlockWidth * BlockHeight * Depth; int RealUsedBlockCount; //RealUsedBlockCount = Swizzling.XGAddress2DTiledExtent(BlockWidth, BlockHeight, BlockSize); RealUsedBlockCount = Swizzling.XGAddress3DTiledExtent(BlockWidth, BlockHeight, Depth, BlockSize); //Console.WriteLine("{0} - {1}", ExpectedBlockCount, UsedBlockCount); var BlockCount = RealUsedBlockCount; var CurrentDecodedColors = new ARGB_Rev[4 * 4]; var Blocks = new TBlock[(uint)BlockCount]; for (int dxt5_n = 0; dxt5_n < BlockCount; dxt5_n++) { int TileX, TileY, TileZ; Swizzling.XGAddress3DTiledXYZ(dxt5_n, BlockWidth, BlockHeight, BlockSize, out TileX, out TileY, out TileZ); int PositionX = TileX * 4; int PositionY = TileY * 4; int PositionZ = TileZ; int n = 0; if ((PositionX + 3 >= Width) || (PositionY + 3 >= Height) || (PositionZ >= Depth)) { if (ShowWarnings) { Console.Error.WriteLine("(Warning! [Write] Position outside ({0}, {1}, {2}) - ({3}x{4}x{5}))", PositionX, PositionY, PositionZ, Width, Height, Depth); } continue; } for (int y = 0; y < 4; y++) { for (int x = 0; x < 4; x++) { CurrentDecodedColors[n] = Bases[TileZ][(PositionY + y) * Width + (PositionX + x)]; n++; } } //for (n = 0; n < 16; n++) CurrentDecodedColors[n] = new ARGB_Rev(0xFF, 0xFF, 0, (byte)(n * 16)); EncodeBlock(ref Blocks[dxt5_n], ref CurrentDecodedColors, mode); } //File.WriteAllBytes(@"C:\temp\font\test.txv", StructUtils.StructArrayToBytes(Blocks)); //Console.WriteLine(Blocks.Length * Marshal.SizeOf(typeof(TBlock))); Stream.WriteStructVector(Blocks); Stream.Flush(); }); }
static private int RGB_TO_YCOCG_CO(ARGB_Rev Color) { return((((Color.R << 1) - (Color.B << 1)) + 2) >> 2); }
static private int RGB_TO_YCOCG_CG(ARGB_Rev Color) { return(((-Color.R + (Color.G << 1) - Color.B) + 2) >> 2); }
/// <summary> /// lerp RGB color /// </summary> /// <param name="_out"></param> /// <param name="p1"></param> /// <param name="p2"></param> static void stb__Lerp13RGB(ref ARGB_Rev _out, ARGB_Rev p1, ARGB_Rev p2) { _out.R = (byte)stb__Lerp13(p1.R, p2.R); _out.G = (byte)stb__Lerp13(p1.G, p2.G); _out.B = (byte)stb__Lerp13(p1.B, p2.B); }
static public void DrawGlow(Bitmap Bitmap, DistanceEntry[,] _DistanceMap, float GlowDistance, ARGB_Rev GlowColor, float Min, float Max = 1.0f) { DistanceMap.DrawGlow(Bitmap, _DistanceMap, GlowDistance, GlowColor, (f) => { return(MathUtils.SmoothStep(Min, Max, f)); }); }
/// <summary> /// /// </summary> /// <param name="File"></param> /// <param name="Width"></param> /// <param name="Height"></param> /// <param name="_Depth"></param> /// <param name="Swizzled"></param> /// <returns></returns> private BitmapList _LoadSwizzled(Stream File, int Width, int Height, int?_Depth, bool Swizzled = true) { if ((Width % 4) != 0 || (Height % 4) != 0) { throw (new InvalidDataException(String.Format("Invalid size {0}x{1} must be multiple of 4", Width, Height))); } int Depth = _Depth ?? 1; bool Is3D = _Depth.HasValue; var BitmapList = new BitmapList(Depth); var BitmapListData = new BitmapData[Depth]; var BitmapListPointers = new ARGB_Rev *[Depth]; for (int n = 0; n < Depth; n++) { BitmapList.Bitmaps[n] = new Bitmap(Width, Height); } for (int n = 0; n < Depth; n++) { var Bitmap = BitmapList.Bitmaps[n]; BitmapListData[n] = Bitmap.LockBits(Bitmap.GetFullRectangle(), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb); BitmapListPointers[n] = (ARGB_Rev *)BitmapListData[n].Scan0.ToPointer(); } int BlockWidth = Width / 4; int BlockHeight = Height / 4; var CurrentDecodedColors = new ARGB_Rev[4 * 4]; var ExpectedBlockCount = BlockWidth * BlockHeight * Depth; int RealUsedBlockCount; if (Is3D) { RealUsedBlockCount = Swizzling.XGAddress3DTiledExtent(Width / 4, Height / 4, Depth, BlockSize); } else { RealUsedBlockCount = Swizzling.XGAddress2DTiledExtent(Width / 4, Height / 4, BlockSize); } //Console.WriteLine("{0} - {1}", ExpectedBlockCount, UsedBlockCount); var BlockCount = RealUsedBlockCount; //var BlockCount = ExpectedBlockCount; if (BlockCount * Marshal.SizeOf(typeof(TBlock)) > File.Length) { Console.Error.WriteLine("File too small"); //throw(new Exception("File too small")); return(new BitmapList(0)); } var Blocks = File.ReadStructVector <TBlock>((uint)BlockCount, -1); //Console.WriteLine(Blocks.Length); for (int BlockN = 0; BlockN < BlockCount; BlockN++) { int TileX, TileY, TileZ; if (Swizzled) { if (Is3D) { Swizzling.XGAddress3DTiledXYZ(BlockN, BlockWidth, BlockHeight, BlockSize, out TileX, out TileY, out TileZ); } else { Swizzling.XGAddress2DTiledXY(BlockN, BlockWidth, BlockSize, out TileX, out TileY); TileZ = 0; } } else { TileX = BlockN % BlockWidth; TileY = BlockN / BlockWidth; TileZ = 0; Console.Error.Write("(Not implemented!)"); } // Skip blocks. if (TileX >= BlockWidth || TileY >= BlockHeight) { continue; } DecodeBlock(ref Blocks[BlockN], ref CurrentDecodedColors); //Console.WriteLine("{0}", CurrentDecodedColors[0]); int PositionX = TileX * 4; int PositionY = TileY * 4; var BlockBitmap = BitmapList.Bitmaps[TileZ]; if ((PositionX + 3 >= BlockBitmap.Width) || (PositionY + 3 >= BlockBitmap.Height)) { Console.Error.WriteLine( "(Warning! [Read] Position outside ({0}, {1}) - ({2}x{3}) ;; ({4}, {5})) - ({6}x{7}) ;; {8}", PositionX, PositionY, Width, Height, TileX, TileY, BlockWidth, BlockHeight, BlockN ); continue; } int n = 0; var BitmapPointer = BitmapListPointers[TileZ]; for (int y = 0; y < 4; y++) { int BaseOffset = (PositionY + y) * Width + (PositionX); for (int x = 0; x < 4; x++) { BitmapPointer[BaseOffset + x] = CurrentDecodedColors[n]; n++; } } } for (int n = 0; n < Depth; n++) { BitmapList.Bitmaps[n].UnlockBits(BitmapListData[n]); } return(BitmapList); }
static public void DrawGlow(Bitmap Bitmap, DistanceEntry[,] _DistanceMap, float GlowDistance, ARGB_Rev GlowColor, Func<float, float> Function = null) { var TransparentColor = (ARGB_Rev)"#00000000"; if (Function == null) Function = (v) => v; Bitmap.Shader((color, x, y) => { var Dist = (float)_DistanceMap[x, y].Distance; if (Dist == 0 && color.A == 0xFF) return color; if (Dist > GlowDistance) return new ARGB_Rev(0, 0, 0, 0); var GenColor = ARGB_Rev.Interpolate(GlowColor, TransparentColor, 1 - Function(Dist / GlowDistance)); return (Dist == 0) ? ARGB_Rev.Mix(color, GenColor) : GenColor; }); }
//static private ARGB_Rev FindNearColorInPalette(IEnumerable<ARGB_Rev> Palette, ARGB_Rev Color) //{ // return Palette.OrderBy(PaletteColor => ARGB_Rev.DistanceRGB(PaletteColor, Color)).First(); //} // //static private ARGB_Rev FindFarColorInPalette(IEnumerable<ARGB_Rev> Palette, ARGB_Rev Color) //{ // return Palette.OrderBy(PaletteColor => ARGB_Rev.DistanceRGB(PaletteColor, Color)).Last(); //} // //static private ARGB_Rev AverageColors(IEnumerable<ARGB_Rev> Palette) //{ // return new ARGB_Rev( // (byte)Palette.Average(Item => Item.A), // (byte)Palette.Average(Item => Item.R), // (byte)Palette.Average(Item => Item.G), // (byte)Palette.Average(Item => Item.B) // ); //} // //private void ReduceColors(List<ARGB_Rev> Palette, int ColorCount) //{ // while (Palette.Count > ColorCount) // { // Palette.Remove(FindNearColorInPalette(Palette, AverageColors(Palette))); // } //} // //private void DoReduceColors(ref ARGB_Rev[] Colors, int ColorCount) //{ // var ReducedColorsList = Colors.ToList(); // ReduceColors(ReducedColorsList, ColorCount); // //Console.WriteLine(String.Join(",", Colors.ToArray())); // //Console.WriteLine(String.Join(",", ReducedColorsList.ToArray())); // for (int n = 0; n < Colors.Length; n++) // { // Colors[n] = FindNearColorInPalette(ReducedColorsList, Colors[n]); // } //} /// <summary> /// /// </summary> /// <param name="Bitmap"></param> /// <param name="File"></param> /// <param name="mode"></param> public void SaveSwizzled2D(Bitmap Bitmap, Stream File, CompressDXT.CompressionMode mode = CompressDXT.CompressionMode.Normal, int ReduceColors = 16, bool ShowWarnings = false) { int Width = Bitmap.Width, Height = Bitmap.Height; if ((Width % 4) != 0 || (Height % 4) != 0) { throw (new InvalidDataException()); } Bitmap.LockBitsUnlock(PixelFormat.Format32bppArgb, (BitmapData) => { var Base = (ARGB_Rev *)BitmapData.Scan0.ToPointer(); int BlockWidth = Width / 4; int BlockHeight = Height / 4; //var BlockCount = BlockWidth * BlockHeight; var ExpectedBlockCount = BlockWidth * BlockHeight; int RealUsedBlockCount; RealUsedBlockCount = Swizzling.XGAddress2DTiledExtent(Width / 4, Height / 4, BlockSize); //Console.WriteLine("{0} - {1}", ExpectedBlockCount, UsedBlockCount); var BlockCount = RealUsedBlockCount; var CurrentDecodedColors = new ARGB_Rev[4 * 4]; var Blocks = new TBlock[(uint)BlockCount]; for (int dxt5_n = 0; dxt5_n < BlockCount; dxt5_n++) { int TileX, TileY; Swizzling.XGAddress2DTiledXY(dxt5_n, BlockWidth, BlockSize, out TileX, out TileY); int PositionX = TileX * 4; int PositionY = TileY * 4; int n = 0; if ((PositionX + 3 >= Width) || (PositionY + 3 >= Height)) { if (ShowWarnings) { Console.Error.WriteLine("(Warning! [Write] Position outside ({0}, {1}) - ({2}x{3}))", PositionX, PositionY, Width, Height); } continue; } for (int y = 0; y < 4; y++) { for (int x = 0; x < 4; x++) { CurrentDecodedColors[n] = Base[(PositionY + y) * Width + (PositionX + x)]; n++; } } //if (ReduceColors != 16) DoReduceColors(ref CurrentDecodedColors, ReduceColors); EncodeBlock(ref Blocks[dxt5_n], ref CurrentDecodedColors, mode); } File.WriteStructVector(Blocks); File.Flush(); }); }