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);
//Write image info before compressed data
Span<byte> TexInfo = stackalloc byte[6];
BinaryPrimitives.WriteInt16LittleEndian(TexInfo, (short)Width);
BinaryPrimitives.WriteInt16LittleEndian(TexInfo[2..], (short)Height);
TexInfo[4] = (byte)img.Format;
TexInfo[5] = (byte)img.Mipmaps;
using MemoryStream stream = new();
stream.Write(TexInfo);
DeflateStream compressor = new(stream, CompressionLevel.Optimal);
compressor.Write(img.DataSpan());
compressor.Dispose();
SerializedData = stream.ToArray();
Raylib.UnloadImage(img);
}
Bitmap(Texture2D tex, byte[] serializedData)
{
SerializedData = serializedData;
Texture = tex;
}
public Bitmap Copy()
{
Image ImgCopy = Raylib.LoadImageFromTexture(Texture);
Bitmap Copy = new(Raylib.LoadTextureFromImage(ImgCopy), SerializedData.ToArray());
Raylib.UnloadImage(ImgCopy);
return Copy;
}
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.LoadImageFromMemory(".png", File.ReadAllBytes(path)), MaxW, MaxH);
}
public static Bitmap FromPNGMemory(byte[] memory, int MaxW = MaxWidth, int MaxH = MaxHeight) => new(Raylib.LoadImageFromMemory(".png", memory), MaxW, MaxH);
public static Bitmap Deserialize(ReadOnlySpan<byte> span, int MaxW = MaxWidth, int MaxH = MaxHeight, Image? fallbackImage = null)
{
int width = BinaryPrimitives.ReadInt16LittleEndian(span[..2]);
int height = BinaryPrimitives.ReadInt16LittleEndian(span.Slice(2, 2));
PixelFormat format = (PixelFormat)span[4];
int mipmaps = span[5];
bool Invalid = width <= 0 || height <= 0 || width > MaxW || height > MaxH || format != PixelFormat.UncompressedR8G8B8A8;
//If the is somehow invalid, then no use in loading the rest. Either use fallback, or generate error checkerboard.
if (Invalid && fallbackImage != null) return new Bitmap(Raylib.ImageCopy(fallbackImage.Value), MaxW, MaxH);
Image img = Raylib.GenImageChecked(Math.Clamp(width, 8, MaxW), Math.Clamp(height, 8, MaxH), 4, 4, Color.Pink, Color.Black);
if (Invalid) return new Bitmap(img);
byte[] compressed = span[6..].ToArray();
byte[] data = new byte[RaylibExt.TextureLength(width, height, format, mipmaps)];
{
using MemoryStream input = new(compressed);
using DeflateStream decompressor = new(input, CompressionMode.Decompress);
decompressor.ReadExactly(data);
}
unsafe {fixed (void* dataPtr = data) {
Texture2D tex = new() {
Id = Rlgl.LoadTexture(dataPtr, width, height, format, mipmaps),
Width = width,
Height = height,
Format = format,
Mipmaps = mipmaps
};
return new Bitmap(tex, compressed);
}}
}
public byte[] Serialize() => SerializedData;
bool disposed = false;
public void Dispose()
{
if (disposed) return;
Raylib.UnloadTexture(Texture);
GC.SuppressFinalize(this);
disposed = true;
}
~Bitmap() => Dispose();
}
|