From c91b8f1c3c0a00895c5e70880ac684f027e97aab Mon Sep 17 00:00:00 2001 From: Sarah Duck Date: Wed, 11 Jun 2025 20:47:24 -0700 Subject: Updated Libraries, and Improved Bitmap serialization. --- Bitmap.cs | 105 +++++++++++++++++++++++++------------------------------------- 1 file changed, 42 insertions(+), 63 deletions(-) (limited to 'Bitmap.cs') diff --git a/Bitmap.cs b/Bitmap.cs index 4cd44d7..fc6fa40 100644 --- a/Bitmap.cs +++ b/Bitmap.cs @@ -1,14 +1,13 @@ using System.Buffers.Binary; using System.IO.Compression; -using System.Numerics; using Raylib_cs; namespace OnekoOnline; class Bitmap : IDisposable { - public readonly int Width; - public readonly int Height; + public int Width => Texture.Width; + public int Height => Texture.Height; public readonly Texture2D Texture; readonly byte[] SerializedData; @@ -16,63 +15,50 @@ class Bitmap : IDisposable const int MaxWidth = 256; const int MaxHeight = 256; - public Bitmap(Image img, int MaxW = 256, int MaxH = 256) + public Bitmap(Image img, int MaxW = MaxWidth, int MaxH = MaxHeight) { - Width = img.Width; - Height = img.Height; //Crop image if too big - if (Width > MaxW || Height > MaxH) Raylib.ImageCrop(ref img, new Rectangle(0f, 0f, MaxW, MaxH)); + if (img.Width > MaxW || img.Height > MaxH) { + Raylib.ImageCrop(ref img, new Rectangle(0f, 0f, MaxW, MaxH)); + } + Raylib.ImageFormat(ref img, PixelFormat.UncompressedR8G8B8A8); Texture = Raylib.LoadTextureFromImage(img); //Get Data for Serialization - Color[] colors = new Color[Width*Height]; - int i = 0; - for (int x = 0; x < Width; x++) { - for (int y = 0; y < Height; y++) { - colors[i] = Raylib.GetImageColor(img, x, y); - i++; - } + int imgSize = Raylib.GetPixelDataSize(img.Width, img.Height, PixelFormat.UncompressedR8G8B8A8); + Span imgData; + unsafe { + imgData = new(img.Data, imgSize); } + using MemoryStream stream = new(); - byte[] data = new byte[(colors.Length*4) + (sizeof(short)*2)]; - BinaryPrimitives.WriteInt16LittleEndian(data.AsSpan(0, sizeof(short)), (short)Width); - BinaryPrimitives.WriteInt16LittleEndian(data.AsSpan(sizeof(short), sizeof(short)), (short)Height); - int position = sizeof(short)*2; - for (int c = 0; c < colors.Length; c++) { - data[position] = colors[c].R; - data[position+1] = colors[c].G; - data[position+2] = colors[c].B; - data[position+3] = colors[c].A; - position += 4; - } + //Write width and height before compressed data + Span WidthHeight = stackalloc byte[4]; + BinaryPrimitives.WriteInt16LittleEndian(WidthHeight, (short)Width); + BinaryPrimitives.WriteInt16LittleEndian(WidthHeight[2..], (short)Height); + stream.Write(WidthHeight); - using MemoryStream stream = new(); using DeflateStream compressor = new(stream, CompressionLevel.Optimal); - compressor.Write(data); + compressor.Write(imgData); compressor.Close(); SerializedData = stream.ToArray(); Raylib.UnloadImage(img); } - Bitmap(Image img, byte[] serializedData) + Bitmap(Texture2D tex, byte[] serializedData) { - Width = img.Width; - Height = img.Height; SerializedData = serializedData; - - Texture = Raylib.LoadTextureFromImage(img); - Raylib.UnloadImage(img); + Texture = tex; } public static Bitmap FromFile(string path, int MaxW = MaxWidth, int MaxH = MaxHeight) { if (!File.Exists(path) || new FileInfo(path).Length > 40000 || !path.Contains(".png")) - return new Bitmap(Raylib.GenImageChecked(32, 32, 4, 4, Color.Black, Color.Pink)); + return new Bitmap(Raylib.GenImageChecked(32, 32, 4, 4, Color.Black, Color.Pink)); - byte[] memory = File.ReadAllBytes(path); - return FromPNGMemory(memory, MaxW, MaxH); + return new Bitmap(Raylib.LoadImage(path), MaxW, MaxH); } public static Bitmap FromPNGMemory(byte[] memory, int MaxW = MaxWidth, int MaxH = MaxHeight) @@ -82,40 +68,33 @@ class Bitmap : IDisposable public static Bitmap Deserialize(ReadOnlySpan span, int MaxW = MaxWidth, int MaxH = MaxHeight, byte[]? fallbackImage = null) { - byte[] compressed = span.ToArray(); - byte[] data; + int width = BinaryPrimitives.ReadInt16LittleEndian(span[..2]); + int height = BinaryPrimitives.ReadInt16LittleEndian(span.Slice(2, 2)); + + bool Invalid = width <=0 || height <= 0 || width > MaxW || height > MaxH; + width = Math.Clamp(width, 1, MaxW); + height = Math.Clamp(height, 1, MaxH); + + //If the width and height are somehow wrong, then no use in loading the rest. + if (Invalid && fallbackImage != null) return FromPNGMemory(fallbackImage, MaxW, MaxH); + Image img = Raylib.GenImageChecked(width, height, 4, 4, Color.Pink, Color.Black); + Raylib.ImageFormat(ref img, PixelFormat.UncompressedR8G8B8A8); + if (Invalid) return new Bitmap(img); + + byte[] compressed = span[4..].ToArray(); + byte[] data = new byte[Raylib.GetPixelDataSize(width, height, PixelFormat.UncompressedR8G8B8A8)]; { using MemoryStream input = new(compressed); - using MemoryStream output = new(); using DeflateStream decompressor = new(input, CompressionMode.Decompress); - decompressor.CopyTo(output); - data = output.ToArray(); + decompressor.ReadExactly(data); } - int width = BinaryPrimitives.ReadInt16LittleEndian(data.AsSpan(0, sizeof(short))); - int height = BinaryPrimitives.ReadInt16LittleEndian(data.AsSpan(sizeof(short), sizeof(short))); + Texture2D tex = Raylib.LoadTextureFromImage(img); + Raylib.UpdateTexture(tex, data); - bool TooBig = width > MaxW || height > MaxH; - if (TooBig) { - width = Math.Clamp(width, 0, MaxW); - height = Math.Clamp(height, 0, MaxH); - if (fallbackImage != null) return FromPNGMemory(fallbackImage, MaxW, MaxH); - } - - Image img = Raylib.GenImageChecked(width, height, 4, 4, Color.Pink, Color.Black); - - if (TooBig) return new Bitmap(img); - - int i = sizeof(short)*2; - for (int x = 0; x < width; x++) { - for (int y = 0; y < height; y++) { - Color color = new(data[i], data[i+1], data[i+2], data[i+3]); - Raylib.ImageDrawPixel(ref img, x, y, color); - i += 4; - } - } + Raylib.UnloadImage(img); - return new Bitmap(img, compressed); + return new Bitmap(tex, compressed); } public byte[] Serialize() -- cgit