Add image processing and memory management features

Added new namespace `Misaki.HighPerformance.Image` for image processing, including classes for animated GIF handling and memory management.
Added `AnimatedFrameResult` class for individual frames in animated images.
Added `AnimatedGifEnumerator` class for enumerating frames in animated GIFs.
Added `ColorComponents` enum for different color formats.
Added `ImageInfo` struct for image dimensions and color components.
Added `CRuntime` class for low-level memory management functions.
Added `MemoryStats` class to track memory allocation statistics.
Added utility functions for creating multi-dimensional arrays.
Added new structures for fixed-size UTF-8 encoded strings.
Added benchmarking classes to test new memory management features.

Changed `StbImage.cs` to include new namespaces and functionality for image data manipulation.
Changed project files to target .NET 9.0 and enable new features.
Changed `Arena.cs` and `DynamicArena.cs` to use `nuint` for size parameters.
Changed `BitSet.cs` to enhance bit manipulation methods.
Changed `Program.cs` to run `FunctionPtrBenchmark` for performance testing.

Removed memory tracking code from `AllocationManager.cs`, including the `_allocated` dictionary and related logic.
Removed `Free` method from `IAllocator.cs` interface.
Removed `UNSAFE_COLLECTION_CHECK` preprocessor directive from the codebase.

Refactored various files to improve organization, moving from `Unsafe` to `LowLevel` namespace.
Refactored `MemoryUtilities` class to include new memory operation methods.
Refactored `UnsafeUtilities.cs` to support new collection structures.
This commit is contained in:
2025-07-12 19:48:42 +09:00
parent d306f183de
commit eeff3313b5
72 changed files with 14444 additions and 471 deletions

View File

@@ -0,0 +1,545 @@
// Generated by Sichem at 12/24/2021 8:28:15 PM
using System.Runtime.InteropServices;
using Misaki.HighPerformance.Image.Runtime;
using Misaki.HighPerformance.Image;
namespace Misaki.HighPerformance.Image
{
unsafe partial class StbImage
{
public static int stbi__gif_test(stbi__context s)
{
var r = stbi__gif_test_raw(s);
stbi__rewind(s);
return r;
}
public static void* stbi__gif_load(stbi__context s, int* x, int* y, int* comp, int req_comp,
stbi__result_info* ri)
{
byte* u = null;
var g = new stbi__gif();
u = stbi__gif_load_next(s, g, comp, req_comp, null);
if (u != null)
{
*x = g.w;
*y = g.h;
if (req_comp != 0 && req_comp != 4)
u = stbi__convert_format(u, 4, req_comp, (uint)g.w, (uint)g.h);
}
else if (g._out_ != null)
{
CRuntime.free(g._out_);
}
CRuntime.free(g.history);
CRuntime.free(g.background);
return u;
}
public static void* stbi__load_gif_main(stbi__context s, int** delays, int* x, int* y, int* z, int* comp,
int req_comp)
{
if (stbi__gif_test(s) != 0)
{
var layers = 0;
byte* u = null;
byte* _out_ = null;
byte* two_back = null;
var g = new stbi__gif();
var stride = 0;
var out_size = 0;
var delays_size = 0;
if (delays != null)
*delays = null;
do
{
u = stbi__gif_load_next(s, g, comp, req_comp, two_back);
if (u != null)
{
*x = g.w;
*y = g.h;
++layers;
stride = g.w * g.h * 4;
if (_out_ != null)
{
void* tmp = (byte*)CRuntime.realloc(_out_, (ulong)(layers * stride));
if (tmp == null)
return stbi__load_gif_main_outofmem(g, _out_, delays);
_out_ = (byte*)tmp;
out_size = layers * stride;
if (delays != null)
{
var new_delays = (int*)CRuntime.realloc(*delays, (ulong)(sizeof(int) * layers));
if (new_delays == null)
return stbi__load_gif_main_outofmem(g, _out_, delays);
*delays = new_delays;
delays_size = layers * sizeof(int);
}
}
else
{
_out_ = (byte*)stbi__malloc((ulong)(layers * stride));
if (_out_ == null)
return stbi__load_gif_main_outofmem(g, _out_, delays);
out_size = layers * stride;
if (delays != null)
{
*delays = (int*)stbi__malloc((ulong)(layers * sizeof(int)));
if (*delays == null)
return stbi__load_gif_main_outofmem(g, _out_, delays);
delays_size = layers * sizeof(int);
}
}
CRuntime.memcpy(_out_ + (layers - 1) * stride, u, (ulong)stride);
if (layers >= 2)
two_back = _out_ - 2 * stride;
if (delays != null)
(*delays)[layers - 1U] = g.delay;
}
} while (u != null);
CRuntime.free(g._out_);
CRuntime.free(g.history);
CRuntime.free(g.background);
if (req_comp != 0 && req_comp != 4)
_out_ = stbi__convert_format(_out_, 4, req_comp, (uint)(layers * g.w), (uint)g.h);
*z = layers;
return _out_;
}
return (byte*)(ulong)(stbi__err("not GIF") != 0 ? 0 : 0);
}
public static int stbi__gif_info(stbi__context s, int* x, int* y, int* comp)
{
return stbi__gif_info_raw(s, x, y, comp);
}
public static int stbi__gif_test_raw(stbi__context s)
{
var sz = 0;
if (stbi__get8(s) != 71 || stbi__get8(s) != 73 || stbi__get8(s) != 70 || stbi__get8(s) != 56)
return 0;
sz = stbi__get8(s);
if (sz != 57 && sz != 55)
return 0;
if (stbi__get8(s) != 97)
return 0;
return 1;
}
public static void stbi__gif_parse_colortable(stbi__context s, byte[][] pal, int num_entries, int transp)
{
var i = 0;
for (i = 0; i < num_entries; ++i)
{
pal[i][2] = stbi__get8(s);
pal[i][1] = stbi__get8(s);
pal[i][0] = stbi__get8(s);
pal[i][3] = (byte)(transp == i ? 0 : 255);
}
}
public static int stbi__gif_header(stbi__context s, stbi__gif g, int* comp, int is_info)
{
byte version = 0;
if (stbi__get8(s) != 71 || stbi__get8(s) != 73 || stbi__get8(s) != 70 || stbi__get8(s) != 56)
return stbi__err("not GIF");
version = stbi__get8(s);
if (version != 55 && version != 57)
return stbi__err("not GIF");
if (stbi__get8(s) != 97)
return stbi__err("not GIF");
stbi__g_failure_reason = "";
g.w = stbi__get16le(s);
g.h = stbi__get16le(s);
g.flags = stbi__get8(s);
g.bgindex = stbi__get8(s);
g.ratio = stbi__get8(s);
g.transparent = -1;
if (g.w > 1 << 24)
return stbi__err("too large");
if (g.h > 1 << 24)
return stbi__err("too large");
if (comp != null)
*comp = 4;
if (is_info != 0)
return 1;
if ((g.flags & 0x80) != 0)
stbi__gif_parse_colortable(s, g.pal, 2 << (g.flags & 7), -1);
return 1;
}
public static int stbi__gif_info_raw(stbi__context s, int* x, int* y, int* comp)
{
var g = new stbi__gif();
if (g == null)
return stbi__err("outofmem");
if (stbi__gif_header(s, g, comp, 1) == 0)
{
stbi__rewind(s);
return 0;
}
if (x != null)
*x = g.w;
if (y != null)
*y = g.h;
return 1;
}
public static void stbi__out_gif_code(stbi__gif g, ushort code)
{
byte* p;
var idx = 0;
if (g.codes[code].prefix >= 0)
stbi__out_gif_code(g, (ushort)g.codes[code].prefix);
if (g.cur_y >= g.max_y)
return;
idx = g.cur_x + g.cur_y;
p = &g._out_[idx];
g.history[idx / 4] = 1;
var c = g.color_table[g.codes[code].suffix];
if (c[3] > 128)
{
p[0] = c[2];
p[1] = c[1];
p[2] = c[0];
p[3] = c[3];
}
g.cur_x += 4;
if (g.cur_x >= g.max_x)
{
g.cur_x = g.start_x;
g.cur_y += g.step;
while (g.cur_y >= g.max_y && g.parse > 0)
{
g.step = (1 << g.parse) * g.line_size;
g.cur_y = g.start_y + (g.step >> 1);
--g.parse;
}
}
}
public static byte* stbi__process_gif_raster(stbi__context s, stbi__gif g)
{
byte lzw_cs = 0;
var len = 0;
var init_code = 0;
uint first = 0;
var codesize = 0;
var codemask = 0;
var avail = 0;
var oldcode = 0;
var bits = 0;
var valid_bits = 0;
var clear = 0;
stbi__gif_lzw* p;
lzw_cs = stbi__get8(s);
if (lzw_cs > 12)
return null;
clear = 1 << lzw_cs;
first = 1;
codesize = lzw_cs + 1;
codemask = (1 << codesize) - 1;
bits = 0;
valid_bits = 0;
for (init_code = 0; init_code < clear; init_code++)
{
g.codes[init_code].prefix = -1;
g.codes[init_code].first = (byte)init_code;
g.codes[init_code].suffix = (byte)init_code;
}
avail = clear + 2;
oldcode = -1;
len = 0;
for (; ; )
if (valid_bits < codesize)
{
if (len == 0)
{
len = stbi__get8(s);
if (len == 0)
return g._out_;
}
--len;
bits |= stbi__get8(s) << valid_bits;
valid_bits += 8;
}
else
{
var code = bits & codemask;
bits >>= codesize;
valid_bits -= codesize;
if (code == clear)
{
codesize = lzw_cs + 1;
codemask = (1 << codesize) - 1;
avail = clear + 2;
oldcode = -1;
first = 0;
}
else if (code == clear + 1)
{
stbi__skip(s, len);
while ((len = stbi__get8(s)) > 0)
stbi__skip(s, len);
return g._out_;
}
else if (code <= avail)
{
if (first != 0)
return (byte*)(ulong)(stbi__err("no clear code") != 0 ? 0 : 0);
if (oldcode >= 0)
{
fixed (stbi__gif_lzw *p2 = &g.codes[avail++])
{
p = p2;
if (avail > 8192)
return (byte*)(ulong)(stbi__err("too many codes") != 0 ? 0 : 0);
p->prefix = (short)oldcode;
p->first = g.codes[oldcode].first;
p->suffix = code == avail ? p->first : g.codes[code].first;
}
}
else if (code == avail)
{
return (byte*)(ulong)(stbi__err("illegal code in raster") != 0 ? 0 : 0);
}
stbi__out_gif_code(g, (ushort)code);
if ((avail & codemask) == 0 && avail <= 0x0FFF)
{
codesize++;
codemask = (1 << codesize) - 1;
}
oldcode = code;
}
else
{
return (byte*)(ulong)(stbi__err("illegal code in raster") != 0 ? 0 : 0);
}
}
}
public static byte* stbi__gif_load_next(stbi__context s, stbi__gif g, int* comp, int req_comp, byte* two_back)
{
var dispose = 0;
var first_frame = 0;
var pi = 0;
var pcount = 0;
first_frame = 0;
if (g._out_ == null)
{
if (stbi__gif_header(s, g, comp, 0) == 0)
return null;
if (stbi__mad3sizes_valid(4, g.w, g.h, 0) == 0)
return (byte*)(ulong)(stbi__err("too large") != 0 ? 0 : 0);
pcount = g.w * g.h;
g._out_ = (byte*)stbi__malloc((ulong)(4 * pcount));
g.background = (byte*)stbi__malloc((ulong)(4 * pcount));
g.history = (byte*)stbi__malloc((ulong)pcount);
if (g._out_ == null || g.background == null || g.history == null)
return (byte*)(ulong)(stbi__err("outofmem") != 0 ? 0 : 0);
CRuntime.memset(g._out_, 0x00, (ulong)(4 * pcount));
CRuntime.memset(g.background, 0x00, (ulong)(4 * pcount));
CRuntime.memset(g.history, 0x00, (ulong)pcount);
first_frame = 1;
}
else
{
dispose = (g.eflags & 0x1C) >> 2;
pcount = g.w * g.h;
if (dispose == 3 && two_back == null)
dispose = 2;
if (dispose == 3)
{
for (pi = 0; pi < pcount; ++pi)
if (g.history[pi] != 0)
CRuntime.memcpy(&g._out_[pi * 4], &two_back[pi * 4], (ulong)4);
}
else if (dispose == 2)
{
for (pi = 0; pi < pcount; ++pi)
if (g.history[pi] != 0)
CRuntime.memcpy(&g._out_[pi * 4], &g.background[pi * 4], (ulong)4);
}
CRuntime.memcpy(g.background, g._out_, (ulong)(4 * g.w * g.h));
}
CRuntime.memset(g.history, 0x00, (ulong)(g.w * g.h));
for (; ; )
{
int tag = stbi__get8(s);
switch (tag)
{
case 0x2C:
{
var x = 0;
var y = 0;
var w = 0;
var h = 0;
byte* o;
x = stbi__get16le(s);
y = stbi__get16le(s);
w = stbi__get16le(s);
h = stbi__get16le(s);
if (x + w > g.w || y + h > g.h)
return (byte*)(ulong)(stbi__err("bad Image Descriptor") != 0 ? 0 : 0);
g.line_size = g.w * 4;
g.start_x = x * 4;
g.start_y = y * g.line_size;
g.max_x = g.start_x + w * 4;
g.max_y = g.start_y + h * g.line_size;
g.cur_x = g.start_x;
g.cur_y = g.start_y;
if (w == 0)
g.cur_y = g.max_y;
g.lflags = stbi__get8(s);
if ((g.lflags & 0x40) != 0)
{
g.step = 8 * g.line_size;
g.parse = 3;
}
else
{
g.step = g.line_size;
g.parse = 0;
}
if ((g.lflags & 0x80) != 0)
{
stbi__gif_parse_colortable(s, g.lpal, 2 << (g.lflags & 7),
(g.eflags & 0x01) != 0 ? g.transparent : -1);
g.color_table = g.lpal;
}
else if ((g.flags & 0x80) != 0)
{
g.color_table = g.pal;
}
else
{
return (byte*)(ulong)(stbi__err("missing color table") != 0 ? 0 : 0);
}
o = stbi__process_gif_raster(s, g);
if (o == null)
return null;
pcount = g.w * g.h;
if (first_frame != 0 && g.bgindex > 0)
for (pi = 0; pi < pcount; ++pi)
if (g.history[pi] == 0)
{
g.pal[g.bgindex][3] = 255;
fixed (byte* ptr = &g.pal[g.bgindex][0])
{
CRuntime.memcpy(&g._out_[pi * 4], ptr, (ulong)4);
}
}
return o;
}
case 0x21:
{
var len = 0;
int ext = stbi__get8(s);
if (ext == 0xF9)
{
len = stbi__get8(s);
if (len == 4)
{
g.eflags = stbi__get8(s);
g.delay = 10 * stbi__get16le(s);
if (g.transparent >= 0)
g.pal[g.transparent][3] = 255;
if ((g.eflags & 0x01) != 0)
{
g.transparent = stbi__get8(s);
if (g.transparent >= 0)
g.pal[g.transparent][3] = 0;
}
else
{
stbi__skip(s, 1);
g.transparent = -1;
}
}
else
{
stbi__skip(s, len);
break;
}
}
while ((len = stbi__get8(s)) != 0)
stbi__skip(s, len);
break;
}
case 0x3B:
return null;
default:
return (byte*)(ulong)(stbi__err("unknown code") != 0 ? 0 : 0);
}
}
}
public static void* stbi__load_gif_main_outofmem(stbi__gif g, byte* _out_, int** delays)
{
CRuntime.free(g._out_);
CRuntime.free(g.history);
CRuntime.free(g.background);
if (_out_ != null)
CRuntime.free(_out_);
if (delays != null && *delays != null)
CRuntime.free(*delays);
return (byte*)(ulong)(stbi__err("outofmem") != 0 ? 0 : 0);
}
public class stbi__gif
{
public byte* _out_;
public byte* background;
public int bgindex;
public stbi__gif_lzw[] codes = new stbi__gif_lzw[8192];
public byte[][] color_table;
public int cur_x;
public int cur_y;
public int delay;
public int eflags;
public int flags;
public int h;
public byte* history;
public int lflags;
public int line_size;
public byte[][] lpal = Utility.CreateArray<byte>(256, 4);
public int max_x;
public int max_y;
public byte[][] pal = Utility.CreateArray<byte>(256, 4);
public int parse;
public int ratio;
public int start_x;
public int start_y;
public int step;
public int transparent;
public int w;
}
[StructLayout(LayoutKind.Sequential)]
public struct stbi__gif_lzw
{
public short prefix;
public byte first;
public byte suffix;
}
}
}