1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
|
using System.Buffers.Binary;
using System.IO.Compression;
using Raylib_cs;
namespace OnekoOnline;
class Bitmap : IDisposable
{
public int Width => Texture.Width;
public int Height => Texture.Height;
public readonly Texture2D Texture;
readonly byte[] SerializedData;
const int MaxWidth = 256;
const int MaxHeight = 256;
public Bitmap(Image img, int MaxW = MaxWidth, int MaxH = MaxHeight)
{
//Crop image if too big
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
int imgSize = Raylib.GetPixelDataSize(img.Width, img.Height, PixelFormat.UncompressedR8G8B8A8);
Span<byte> imgData;
unsafe {
imgData = new(img.Data, imgSize);
}
using MemoryStream stream = new();
//Write width and height before compressed data
Span<byte> WidthHeight = stackalloc byte[4];
BinaryPrimitives.WriteInt16LittleEndian(WidthHeight, (short)Width);
BinaryPrimitives.WriteInt16LittleEndian(WidthHeight[2..], (short)Height);
stream.Write(WidthHeight);
using DeflateStream compressor = new(stream, CompressionLevel.Optimal);
compressor.Write(imgData);
compressor.Close();
SerializedData = stream.ToArray();
Raylib.UnloadImage(img);
}
Bitmap(Texture2D tex, byte[] serializedData)
{
SerializedData = serializedData;
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.LoadImage(path), MaxW, MaxH);
}
public static Bitmap FromPNGMemory(byte[] memory, int MaxW = MaxWidth, int MaxH = MaxHeight)
{
return new Bitmap(Raylib.LoadImageFromMemory(".png", memory), MaxW, MaxH);
}
public static Bitmap Deserialize(ReadOnlySpan<byte> span, int MaxW = MaxWidth, int MaxH = MaxHeight, byte[]? fallbackImage = null)
{
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 DeflateStream decompressor = new(input, CompressionMode.Decompress);
decompressor.ReadExactly(data);
}
Texture2D tex = Raylib.LoadTextureFromImage(img);
Raylib.UpdateTexture(tex, data);
Raylib.UnloadImage(img);
return new Bitmap(tex, compressed);
}
public byte[] Serialize()
{
return SerializedData;
}
bool disposed = false;
public void Dispose()
{
if (disposed) return;
Raylib.UnloadTexture(Texture);
GC.SuppressFinalize(this);
disposed = true;
}
~Bitmap() => Dispose();
}
|