Refactor core APIs, fix bugs, and improve safety

- Make image result/info structs readonly; improve error handling and memory safety in image library
- Introduce IJobScheduler interface; move job scheduling docs to interface
- Remove "index 0 invalid" convention from slot/sparse maps; fix Count logic
- Add Owner<T> for disposable value types in low-level utilities
- Improve ObjectPool<T> thread safety and logic
- Change List<T>.RemoveAndSwapBack to return bool
- Remove unsafe methods from generated math types; add debug range checks
- Update benchmarks and enable collection checks in tests
- Improve documentation, comments, and error messages
- Bump assembly versions across all projects
This commit is contained in:
2025-12-21 16:08:10 +09:00
parent a1ad0bd2da
commit 1fee890329
38 changed files with 1967 additions and 350 deletions

View File

@@ -1,9 +1,14 @@
namespace Misaki.HighPerformance.Image; namespace Misaki.HighPerformance.Image;
public class AnimatedFrameResult : ImageResult public readonly struct AnimatedFrameResult
{ {
public required ImageResult Image
{
get; init;
}
public int DelayInMs public int DelayInMs
{ {
get; set; get; init;
} }
} }

View File

@@ -15,12 +15,16 @@ internal class AnimatedGifEnumerator : IEnumerator<AnimatedFrameResult>
public AnimatedGifEnumerator(Stream input, ColorComponents colorComponents) public AnimatedGifEnumerator(Stream input, ColorComponents colorComponents)
{ {
if (input == null) if (input == null)
{
throw new ArgumentNullException("input"); throw new ArgumentNullException("input");
}
_context = new StbImage.stbi__context(input); _context = new StbImage.stbi__context(input);
if (StbImage.stbi__gif_test(_context) == 0) if (StbImage.stbi__gif_test(_context) == 0)
{
throw new Exception("Input stream is not GIF file."); throw new Exception("Input stream is not GIF file.");
}
_gif = new StbImage.stbi__gif(); _gif = new StbImage.stbi__gif();
_colorComponents = colorComponents; _colorComponents = colorComponents;
@@ -60,18 +64,25 @@ internal class AnimatedGifEnumerator : IEnumerator<AnimatedFrameResult>
byte two_back; byte two_back;
var result = StbImage.stbi__gif_load_next(_context, _gif, &ccomp, (int)ColorComponents, &two_back); var result = StbImage.stbi__gif_load_next(_context, _gif, &ccomp, (int)ColorComponents, &two_back);
if (result == null) if (result == null)
{
return false; return false;
}
Current ??= new AnimatedFrameResult if (Current.Image.Data == null)
{
Current = new AnimatedFrameResult
{
Image = new ImageResult
{ {
Data = result, Data = result,
Width = (uint)_gif.w, Width = (uint)_gif.w,
Height = (uint)_gif.h, Height = (uint)_gif.h,
SourceComp = (ColorComponents)ccomp, SourceComp = (ColorComponents)ccomp,
Comp = ColorComponents == ColorComponents.Default ? (ColorComponents)ccomp : ColorComponents Comp = ColorComponents == ColorComponents.Default ? (ColorComponents)ccomp : ColorComponents,
},
DelayInMs = _gif.delay
}; };
}
Current.DelayInMs = _gif.delay;
return true; return true;
} }

View File

@@ -1,15 +1,30 @@
using System.IO; using System.IO;
namespace Misaki.HighPerformance.Image; namespace Misaki.HighPerformance.Image;
public struct ImageInfo public readonly struct ImageInfo
{ {
public int Width; public int Width
public int Height; {
public ColorComponents ColorComponents; get; init;
public int BitsPerChannel; }
public static unsafe ImageInfo? FromStream(Stream stream) public int Height
{
get; init;
}
public ColorComponents ColorComponents
{
get; init;
}
public int BitsPerChannel
{
get; init;
}
public static unsafe ImageInfo FromStream(Stream stream)
{ {
int width, height, comp; int width, height, comp;
var context = new StbImage.stbi__context(stream); var context = new StbImage.stbi__context(stream);
@@ -21,7 +36,9 @@ public struct ImageInfo
StbImage.stbi__rewind(context); StbImage.stbi__rewind(context);
if (infoResult == 0) if (infoResult == 0)
return null; {
return default;
}
return new ImageInfo return new ImageInfo
{ {

View File

@@ -5,7 +5,13 @@ using System.IO;
namespace Misaki.HighPerformance.Image; namespace Misaki.HighPerformance.Image;
public unsafe class ImageResult : IDisposable /// <summary>
/// Represents the result of loading an image, including pixel data and image metadata.
/// </summary>
/// <remarks>The image data is stored as a contiguous block of unmanaged memory and must be released by calling
/// <see cref="Dispose"/> when no longer needed. Be careful that this struct won't stop your double free if you copy it.
/// Ensure to have proper ownership management when using this struct.</remarks>
public unsafe readonly struct ImageResult : IDisposable
{ {
public byte* Data public byte* Data
{ {
@@ -45,10 +51,12 @@ public unsafe class ImageResult : IDisposable
} }
} }
internal static unsafe ImageResult FromResult(byte* result, uint width, uint height, ColorComponents comp, ColorComponents req_comp) internal static ImageResult FromResult(byte* result, uint width, uint height, ColorComponents comp, ColorComponents req_comp)
{ {
if (result == null) if (result == null)
{
throw new InvalidOperationException(StbImage.stbi__g_failure_reason); throw new InvalidOperationException(StbImage.stbi__g_failure_reason);
}
var image = new ImageResult var image = new ImageResult
{ {
@@ -62,13 +70,11 @@ public unsafe class ImageResult : IDisposable
return image; return image;
} }
public static unsafe ImageResult FromStream(Stream stream, public static ImageResult FromStream(Stream stream,
ColorComponents requiredComponents = ColorComponents.Default) ColorComponents requiredComponents = ColorComponents.Default)
{ {
int x, y, comp; int x, y, comp;
var context = new StbImage.stbi__context(stream); var context = new StbImage.stbi__context(stream);
var result = StbImage.stbi__load_and_postprocess_8bit(context, &x, &y, &comp, (int)requiredComponents); var result = StbImage.stbi__load_and_postprocess_8bit(context, &x, &y, &comp, (int)requiredComponents);
return FromResult(result, (uint)x, (uint)y, (ColorComponents)comp, requiredComponents); return FromResult(result, (uint)x, (uint)y, (ColorComponents)comp, requiredComponents);

View File

@@ -1,10 +1,17 @@
using Misaki.HighPerformance.Image.Runtime; using Misaki.HighPerformance.Image.Runtime;
using System; using System;
using System.IO; using System.IO;
namespace Misaki.HighPerformance.Image; namespace Misaki.HighPerformance.Image;
public unsafe class ImageResultFloat : IDisposable /// <summary>
/// Represents a decoded image with pixel data stored as floating-point values, including image dimensions and color
/// component information.
/// </summary>
/// <remarks>The image data is stored as a contiguous block of unmanaged memory and must be released by calling
/// <see cref="Dispose"/> when no longer needed. Be careful that this struct won't stop your double free if you copy it.
/// Ensure to have proper ownership management when using this struct.</remarks>
public unsafe readonly struct ImageResultFloat : IDisposable
{ {
public float* Data public float* Data
{ {
@@ -44,7 +51,7 @@ public unsafe class ImageResultFloat : IDisposable
} }
} }
internal static unsafe ImageResultFloat FromResult(float* result, uint width, uint height, ColorComponents comp, internal static ImageResultFloat FromResult(float* result, uint width, uint height, ColorComponents comp,
ColorComponents req_comp) ColorComponents req_comp)
{ {
if (result == null) if (result == null)
@@ -64,13 +71,11 @@ public unsafe class ImageResultFloat : IDisposable
return image; return image;
} }
public static unsafe ImageResultFloat FromStream(Stream stream, public static ImageResultFloat FromStream(Stream stream,
ColorComponents requiredComponents = ColorComponents.Default) ColorComponents requiredComponents = ColorComponents.Default)
{ {
int x, y, comp; int x, y, comp;
var context = new StbImage.stbi__context(stream); var context = new StbImage.stbi__context(stream);
var result = StbImage.stbi__loadf_main(context, &x, &y, &comp, (int)requiredComponents); var result = StbImage.stbi__loadf_main(context, &x, &y, &comp, (int)requiredComponents);
return FromResult(result, (uint)x, (uint)y, (ColorComponents)comp, requiredComponents); return FromResult(result, (uint)x, (uint)y, (ColorComponents)comp, requiredComponents);
@@ -79,11 +84,9 @@ public unsafe class ImageResultFloat : IDisposable
public static ImageResultFloat FromMemory(byte[] data, public static ImageResultFloat FromMemory(byte[] data,
ColorComponents requiredComponents = ColorComponents.Default) ColorComponents requiredComponents = ColorComponents.Default)
{ {
using (var stream = new MemoryStream(data)) using var stream = new MemoryStream(data);
{
return FromStream(stream, requiredComponents); return FromStream(stream, requiredComponents);
} }
}
public Span<byte> AsSpan() public Span<byte> AsSpan()
{ {

View File

@@ -5,7 +5,7 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<TargetFramework>net10.0</TargetFramework> <TargetFramework>net10.0</TargetFramework>
<Authors>Misaki</Authors> <Authors>Misaki</Authors>
<AssemblyVersion>1.0.0</AssemblyVersion> <AssemblyVersion>1.1.0</AssemblyVersion>
<Version>$(AssemblyVersion)</Version> <Version>$(AssemblyVersion)</Version>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild> <GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<PackageProjectUrl>https://git.personalnas.com/Misaki/Misaki.HighPerformance.git</PackageProjectUrl> <PackageProjectUrl>https://git.personalnas.com/Misaki/Misaki.HighPerformance.git</PackageProjectUrl>

View File

@@ -24,7 +24,9 @@ internal static unsafe class CRuntime
public static void free(void* ptr) public static void free(void* ptr)
{ {
if (ptr == null) if (ptr == null)
{
return; return;
}
NativeMemory.Free(ptr); NativeMemory.Free(ptr);
MemoryStats.Freed(); MemoryStats.Freed();
@@ -54,9 +56,11 @@ internal static unsafe class CRuntime
finally finally
{ {
if (temp != null) if (temp != null)
{
free(temp); free(temp);
} }
} }
}
public static int memcmp(void* a, void* b, long size) public static int memcmp(void* a, void* b, long size)
{ {
@@ -66,7 +70,9 @@ internal static unsafe class CRuntime
for (long i = 0; i < size; ++i) for (long i = 0; i < size; ++i)
{ {
if (*ap != *bp) if (*ap != *bp)
{
result += 1; result += 1;
}
ap++; ap++;
bp++; bp++;
@@ -80,8 +86,10 @@ internal static unsafe class CRuntime
var bptr = (byte*)ptr; var bptr = (byte*)ptr;
var bval = (byte)value; var bval = (byte)value;
for (long i = 0; i < size; ++i) for (long i = 0; i < size; ++i)
{
*bptr++ = bval; *bptr++ = bval;
} }
}
public static void memset(void* ptr, int value, ulong size) public static void memset(void* ptr, int value, ulong size)
{ {
@@ -96,7 +104,9 @@ internal static unsafe class CRuntime
public static void* realloc(void* ptr, long newSize) public static void* realloc(void* ptr, long newSize)
{ {
if (ptr == null) if (ptr == null)
{
return malloc(newSize); return malloc(newSize);
}
var result = NativeMemory.Realloc(ptr, (nuint)newSize); var result = NativeMemory.Realloc(ptr, (nuint)newSize);

View File

@@ -2,26 +2,26 @@ using System.Threading;
namespace Misaki.HighPerformance.Image.Runtime namespace Misaki.HighPerformance.Image.Runtime
{ {
internal static unsafe class MemoryStats internal static class MemoryStats
{ {
private static int _allocations; private static int s_allocations;
public static int Allocations public static int Allocations
{ {
get get
{ {
return _allocations; return s_allocations;
} }
} }
internal static void Allocated() internal static void Allocated()
{ {
Interlocked.Increment(ref _allocations); Interlocked.Increment(ref s_allocations);
} }
internal static void Freed() internal static void Freed()
{ {
Interlocked.Decrement(ref _allocations); Interlocked.Decrement(ref s_allocations);
} }
} }
} }

View File

@@ -34,13 +34,22 @@ namespace Misaki.HighPerformance.Image
var info = new stbi__bmp_data(); var info = new stbi__bmp_data();
info.all_a = 255; info.all_a = 255;
if (stbi__bmp_parse_header(s, &info) == null) if (stbi__bmp_parse_header(s, &info) == null)
{
return null; return null;
}
flip_vertically = (int)s.img_y > 0 ? 1 : 0; flip_vertically = (int)s.img_y > 0 ? 1 : 0;
s.img_y = (uint)CRuntime.abs((int)s.img_y); s.img_y = (uint)CRuntime.abs((int)s.img_y);
if (s.img_y > 1 << 24) if (s.img_y > 1 << 24)
{
return (byte*)(ulong)(stbi__err("too large") != 0 ? 0 : 0); return (byte*)(ulong)(stbi__err("too large") != 0 ? 0 : 0);
}
if (s.img_x > 1 << 24) if (s.img_x > 1 << 24)
{
return (byte*)(ulong)(stbi__err("too large") != 0 ? 0 : 0); return (byte*)(ulong)(stbi__err("too large") != 0 ? 0 : 0);
}
mr = info.mr; mr = info.mr;
mg = info.mg; mg = info.mg;
mb = info.mb; mb = info.mb;
@@ -49,13 +58,17 @@ namespace Misaki.HighPerformance.Image
if (info.hsz == 12) if (info.hsz == 12)
{ {
if (info.bpp < 24) if (info.bpp < 24)
{
psize = (info.offset - info.extra_read - 24) / 3; psize = (info.offset - info.extra_read - 24) / 3;
} }
}
else else
{ {
if (info.bpp < 16) if (info.bpp < 16)
{
psize = (info.offset - info.extra_read - info.hsz) >> 2; psize = (info.offset - info.extra_read - info.hsz) >> 2;
} }
}
if (psize == 0) if (psize == 0)
{ {
@@ -63,26 +76,47 @@ namespace Misaki.HighPerformance.Image
var header_limit = 1024; var header_limit = 1024;
var extra_data_limit = 256 * 4; var extra_data_limit = 256 * 4;
if (bytes_read_so_far <= 0 || bytes_read_so_far > header_limit) if (bytes_read_so_far <= 0 || bytes_read_so_far > header_limit)
{
return (byte*)(ulong)(stbi__err("bad header") != 0 ? 0 : 0); return (byte*)(ulong)(stbi__err("bad header") != 0 ? 0 : 0);
}
if (info.offset < bytes_read_so_far || info.offset - bytes_read_so_far > extra_data_limit) if (info.offset < bytes_read_so_far || info.offset - bytes_read_so_far > extra_data_limit)
{
return (byte*)(ulong)(stbi__err("bad offset") != 0 ? 0 : 0); return (byte*)(ulong)(stbi__err("bad offset") != 0 ? 0 : 0);
}
stbi__skip(s, info.offset - bytes_read_so_far); stbi__skip(s, info.offset - bytes_read_so_far);
} }
if (info.bpp == 24 && ma == 0xff000000) if (info.bpp == 24 && ma == 0xff000000)
{
s.img_n = 3; s.img_n = 3;
}
else else
{
s.img_n = ma != 0 ? 4 : 3; s.img_n = ma != 0 ? 4 : 3;
}
if (req_comp != 0 && req_comp >= 3) if (req_comp != 0 && req_comp >= 3)
{
target = req_comp; target = req_comp;
}
else else
{
target = s.img_n; target = s.img_n;
}
if (stbi__mad3sizes_valid(target, (int)s.img_x, (int)s.img_y, 0) == 0) if (stbi__mad3sizes_valid(target, (int)s.img_x, (int)s.img_y, 0) == 0)
{
return (byte*)(ulong)(stbi__err("too large") != 0 ? 0 : 0); return (byte*)(ulong)(stbi__err("too large") != 0 ? 0 : 0);
}
_out_ = (byte*)stbi__malloc_mad3(target, (int)s.img_x, (int)s.img_y, 0); _out_ = (byte*)stbi__malloc_mad3(target, (int)s.img_x, (int)s.img_y, 0);
if (_out_ == null) if (_out_ == null)
{
return (byte*)(ulong)(stbi__err("outofmem") != 0 ? 0 : 0); return (byte*)(ulong)(stbi__err("outofmem") != 0 ? 0 : 0);
}
if (info.bpp < 16) if (info.bpp < 16)
{ {
var z = 0; var z = 0;
@@ -98,7 +132,10 @@ namespace Misaki.HighPerformance.Image
pal[i][1] = stbi__get8(s); pal[i][1] = stbi__get8(s);
pal[i][0] = stbi__get8(s); pal[i][0] = stbi__get8(s);
if (info.hsz != 12) if (info.hsz != 12)
{
stbi__get8(s); stbi__get8(s);
}
pal[i][3] = 255; pal[i][3] = 255;
} }
@@ -123,6 +160,7 @@ namespace Misaki.HighPerformance.Image
pad = -width & 3; pad = -width & 3;
if (info.bpp == 1) if (info.bpp == 1)
{
for (j = 0; j < (int)s.img_y; ++j) for (j = 0; j < (int)s.img_y; ++j)
{ {
var bit_offset = 7; var bit_offset = 7;
@@ -134,9 +172,15 @@ namespace Misaki.HighPerformance.Image
_out_[z++] = pal[color][1]; _out_[z++] = pal[color][1];
_out_[z++] = pal[color][2]; _out_[z++] = pal[color][2];
if (target == 4) if (target == 4)
{
_out_[z++] = 255; _out_[z++] = 255;
}
if (i + 1 == (int)s.img_x) if (i + 1 == (int)s.img_x)
{
break; break;
}
if (--bit_offset < 0) if (--bit_offset < 0)
{ {
bit_offset = 7; bit_offset = 7;
@@ -146,7 +190,9 @@ namespace Misaki.HighPerformance.Image
stbi__skip(s, pad); stbi__skip(s, pad);
} }
}
else else
{
for (j = 0; j < (int)s.img_y; ++j) for (j = 0; j < (int)s.img_y; ++j)
{ {
for (i = 0; i < (int)s.img_x; i += 2) for (i = 0; i < (int)s.img_x; i += 2)
@@ -163,20 +209,29 @@ namespace Misaki.HighPerformance.Image
_out_[z++] = pal[v][1]; _out_[z++] = pal[v][1];
_out_[z++] = pal[v][2]; _out_[z++] = pal[v][2];
if (target == 4) if (target == 4)
{
_out_[z++] = 255; _out_[z++] = 255;
}
if (i + 1 == (int)s.img_x) if (i + 1 == (int)s.img_x)
{
break; break;
}
v = info.bpp == 8 ? stbi__get8(s) : v2; v = info.bpp == 8 ? stbi__get8(s) : v2;
_out_[z++] = pal[v][0]; _out_[z++] = pal[v][0];
_out_[z++] = pal[v][1]; _out_[z++] = pal[v][1];
_out_[z++] = pal[v][2]; _out_[z++] = pal[v][2];
if (target == 4) if (target == 4)
{
_out_[z++] = 255; _out_[z++] = 255;
} }
}
stbi__skip(s, pad); stbi__skip(s, pad);
} }
} }
}
else else
{ {
var rshift = 0; var rshift = 0;
@@ -191,17 +246,30 @@ namespace Misaki.HighPerformance.Image
var easy = 0; var easy = 0;
stbi__skip(s, info.offset - info.extra_read - info.hsz); stbi__skip(s, info.offset - info.extra_read - info.hsz);
if (info.bpp == 24) if (info.bpp == 24)
{
width = (int)(3 * s.img_x); width = (int)(3 * s.img_x);
}
else if (info.bpp == 16) else if (info.bpp == 16)
{
width = (int)(2 * s.img_x); width = (int)(2 * s.img_x);
}
else else
{
width = 0; width = 0;
}
pad = -width & 3; pad = -width & 3;
if (info.bpp == 24) if (info.bpp == 24)
{
easy = 1; easy = 1;
}
else if (info.bpp == 32) else if (info.bpp == 32)
{
if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000) if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000)
{
easy = 2; easy = 2;
}
}
if (easy == 0) if (easy == 0)
{ {
@@ -240,9 +308,11 @@ namespace Misaki.HighPerformance.Image
a = (byte)(easy == 2 ? stbi__get8(s) : 255); a = (byte)(easy == 2 ? stbi__get8(s) : 255);
all_a |= a; all_a |= a;
if (target == 4) if (target == 4)
{
_out_[z++] = a; _out_[z++] = a;
} }
} }
}
else else
{ {
var bpp = info.bpp; var bpp = info.bpp;
@@ -256,17 +326,23 @@ namespace Misaki.HighPerformance.Image
a = (uint)(ma != 0 ? stbi__shiftsigned(v & ma, ashift, acount) : 255); a = (uint)(ma != 0 ? stbi__shiftsigned(v & ma, ashift, acount) : 255);
all_a |= a; all_a |= a;
if (target == 4) if (target == 4)
{
_out_[z++] = (byte)(a & 255); _out_[z++] = (byte)(a & 255);
} }
} }
}
stbi__skip(s, pad); stbi__skip(s, pad);
} }
} }
if (target == 4 && all_a == 0) if (target == 4 && all_a == 0)
{
for (i = (int)(4 * s.img_x * s.img_y - 1); i >= 0; i -= 4) for (i = (int)(4 * s.img_x * s.img_y - 1); i >= 0; i -= 4)
{
_out_[i] = 255; _out_[i] = 255;
}
}
if (flip_vertically != 0) if (flip_vertically != 0)
{ {
@@ -288,13 +364,18 @@ namespace Misaki.HighPerformance.Image
{ {
_out_ = stbi__convert_format(_out_, target, req_comp, s.img_x, s.img_y); _out_ = stbi__convert_format(_out_, target, req_comp, s.img_x, s.img_y);
if (_out_ == null) if (_out_ == null)
{
return _out_; return _out_;
} }
}
*x = (int)s.img_x; *x = (int)s.img_x;
*y = (int)s.img_y; *y = (int)s.img_y;
if (comp != null) if (comp != null)
{
*comp = s.img_n; *comp = s.img_n;
}
return _out_; return _out_;
} }
@@ -311,16 +392,26 @@ namespace Misaki.HighPerformance.Image
} }
if (x != null) if (x != null)
{
*x = (int)s.img_x; *x = (int)s.img_x;
}
if (y != null) if (y != null)
{
*y = (int)s.img_y; *y = (int)s.img_y;
}
if (comp != null) if (comp != null)
{ {
if (info.bpp == 24 && info.ma == 0xff000000) if (info.bpp == 24 && info.ma == 0xff000000)
{
*comp = 3; *comp = 3;
}
else else
{
*comp = info.ma != 0 ? 4 : 3; *comp = info.ma != 0 ? 4 : 3;
} }
}
return 1; return 1;
} }
@@ -330,9 +421,15 @@ namespace Misaki.HighPerformance.Image
var r = 0; var r = 0;
var sz = 0; var sz = 0;
if (stbi__get8(s) != 66) if (stbi__get8(s) != 66)
{
return 0; return 0;
}
if (stbi__get8(s) != 77) if (stbi__get8(s) != 77)
{
return 0; return 0;
}
stbi__get32le(s); stbi__get32le(s);
stbi__get16le(s); stbi__get16le(s);
stbi__get16le(s); stbi__get16le(s);
@@ -345,7 +442,10 @@ namespace Misaki.HighPerformance.Image
public static int stbi__bmp_set_mask_defaults(stbi__bmp_data* info, int compress) public static int stbi__bmp_set_mask_defaults(stbi__bmp_data* info, int compress)
{ {
if (compress == 3) if (compress == 3)
{
return 1; return 1;
}
if (compress == 0) if (compress == 0)
{ {
if (info->bpp == 16) if (info->bpp == 16)
@@ -377,7 +477,10 @@ namespace Misaki.HighPerformance.Image
{ {
var hsz = 0; var hsz = 0;
if (stbi__get8(s) != 66 || stbi__get8(s) != 77) if (stbi__get8(s) != 66 || stbi__get8(s) != 77)
{
return (byte*)(ulong)(stbi__err("not BMP") != 0 ? 0 : 0); return (byte*)(ulong)(stbi__err("not BMP") != 0 ? 0 : 0);
}
stbi__get32le(s); stbi__get32le(s);
stbi__get16le(s); stbi__get16le(s);
stbi__get16le(s); stbi__get16le(s);
@@ -386,9 +489,15 @@ namespace Misaki.HighPerformance.Image
info->mr = info->mg = info->mb = info->ma = 0; info->mr = info->mg = info->mb = info->ma = 0;
info->extra_read = 14; info->extra_read = 14;
if (info->offset < 0) if (info->offset < 0)
{
return (byte*)(ulong)(stbi__err("bad BMP") != 0 ? 0 : 0); return (byte*)(ulong)(stbi__err("bad BMP") != 0 ? 0 : 0);
}
if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124)
{
return (byte*)(ulong)(stbi__err("unknown BMP") != 0 ? 0 : 0); return (byte*)(ulong)(stbi__err("unknown BMP") != 0 ? 0 : 0);
}
if (hsz == 12) if (hsz == 12)
{ {
s.img_x = (uint)stbi__get16le(s); s.img_x = (uint)stbi__get16le(s);
@@ -401,17 +510,29 @@ namespace Misaki.HighPerformance.Image
} }
if (stbi__get16le(s) != 1) if (stbi__get16le(s) != 1)
{
return (byte*)(ulong)(stbi__err("bad BMP") != 0 ? 0 : 0); return (byte*)(ulong)(stbi__err("bad BMP") != 0 ? 0 : 0);
}
info->bpp = stbi__get16le(s); info->bpp = stbi__get16le(s);
if (hsz != 12) if (hsz != 12)
{ {
var compress = (int)stbi__get32le(s); var compress = (int)stbi__get32le(s);
if (compress == 1 || compress == 2) if (compress == 1 || compress == 2)
{
return (byte*)(ulong)(stbi__err("BMP RLE") != 0 ? 0 : 0); return (byte*)(ulong)(stbi__err("BMP RLE") != 0 ? 0 : 0);
}
if (compress >= 4) if (compress >= 4)
{
return (byte*)(ulong)(stbi__err("BMP JPEG/PNG") != 0 ? 0 : 0); return (byte*)(ulong)(stbi__err("BMP JPEG/PNG") != 0 ? 0 : 0);
}
if (compress == 3 && info->bpp != 16 && info->bpp != 32) if (compress == 3 && info->bpp != 16 && info->bpp != 32)
{
return (byte*)(ulong)(stbi__err("bad BMP") != 0 ? 0 : 0); return (byte*)(ulong)(stbi__err("bad BMP") != 0 ? 0 : 0);
}
stbi__get32le(s); stbi__get32le(s);
stbi__get32le(s); stbi__get32le(s);
stbi__get32le(s); stbi__get32le(s);
@@ -440,8 +561,10 @@ namespace Misaki.HighPerformance.Image
info->mb = stbi__get32le(s); info->mb = stbi__get32le(s);
info->extra_read += 12; info->extra_read += 12;
if (info->mr == info->mg && info->mg == info->mb) if (info->mr == info->mg && info->mg == info->mb)
{
return (byte*)(ulong)(stbi__err("bad BMP") != 0 ? 0 : 0); return (byte*)(ulong)(stbi__err("bad BMP") != 0 ? 0 : 0);
} }
}
else else
{ {
return (byte*)(ulong)(stbi__err("bad BMP") != 0 ? 0 : 0); return (byte*)(ulong)(stbi__err("bad BMP") != 0 ? 0 : 0);
@@ -452,16 +575,24 @@ namespace Misaki.HighPerformance.Image
{ {
var i = 0; var i = 0;
if (hsz != 108 && hsz != 124) if (hsz != 108 && hsz != 124)
{
return (byte*)(ulong)(stbi__err("bad BMP") != 0 ? 0 : 0); return (byte*)(ulong)(stbi__err("bad BMP") != 0 ? 0 : 0);
}
info->mr = stbi__get32le(s); info->mr = stbi__get32le(s);
info->mg = stbi__get32le(s); info->mg = stbi__get32le(s);
info->mb = stbi__get32le(s); info->mb = stbi__get32le(s);
info->ma = stbi__get32le(s); info->ma = stbi__get32le(s);
if (compress != 3) if (compress != 3)
{
stbi__bmp_set_mask_defaults(info, compress); stbi__bmp_set_mask_defaults(info, compress);
}
stbi__get32le(s); stbi__get32le(s);
for (i = 0; i < 12; ++i) for (i = 0; i < 12; ++i)
{
stbi__get32le(s); stbi__get32le(s);
}
if (hsz == 124) if (hsz == 124)
{ {

View File

@@ -100,16 +100,25 @@ namespace Misaki.HighPerformance.Image
public static int stbi__addsizes_valid(int a, int b) public static int stbi__addsizes_valid(int a, int b)
{ {
if (b < 0) if (b < 0)
{
return 0; return 0;
}
return a <= 2147483647 - b ? 1 : 0; return a <= 2147483647 - b ? 1 : 0;
} }
public static int stbi__mul2sizes_valid(int a, int b) public static int stbi__mul2sizes_valid(int a, int b)
{ {
if (a < 0 || b < 0) if (a < 0 || b < 0)
{
return 0; return 0;
}
if (b == 0) if (b == 0)
{
return 1; return 1;
}
return a <= 2147483647 / b ? 1 : 0; return a <= 2147483647 / b ? 1 : 0;
} }
@@ -137,41 +146,65 @@ namespace Misaki.HighPerformance.Image
public static void* stbi__malloc_mad2(int a, int b, int add) public static void* stbi__malloc_mad2(int a, int b, int add)
{ {
if (stbi__mad2sizes_valid(a, b, add) == 0) if (stbi__mad2sizes_valid(a, b, add) == 0)
{
return null; return null;
}
return stbi__malloc((ulong)(a * b + add)); return stbi__malloc((ulong)(a * b + add));
} }
public static void* stbi__malloc_mad3(int a, int b, int c, int add) public static void* stbi__malloc_mad3(int a, int b, int c, int add)
{ {
if (stbi__mad3sizes_valid(a, b, c, add) == 0) if (stbi__mad3sizes_valid(a, b, c, add) == 0)
{
return null; return null;
}
return stbi__malloc((ulong)(a * b * c + add)); return stbi__malloc((ulong)(a * b * c + add));
} }
public static void* stbi__malloc_mad4(int a, int b, int c, int d, int add) public static void* stbi__malloc_mad4(int a, int b, int c, int d, int add)
{ {
if (stbi__mad4sizes_valid(a, b, c, d, add) == 0) if (stbi__mad4sizes_valid(a, b, c, d, add) == 0)
{
return null; return null;
}
return stbi__malloc((ulong)(a * b * c * d + add)); return stbi__malloc((ulong)(a * b * c * d + add));
} }
public static int stbi__addints_valid(int a, int b) public static int stbi__addints_valid(int a, int b)
{ {
if (a >= 0 != b >= 0) if (a >= 0 != b >= 0)
{
return 1; return 1;
}
if (a < 0 && b < 0) if (a < 0 && b < 0)
{
return a >= -2147483647 - 1 - b ? 1 : 0; return a >= -2147483647 - 1 - b ? 1 : 0;
}
return a <= 2147483647 - b ? 1 : 0; return a <= 2147483647 - b ? 1 : 0;
} }
public static int stbi__mul2shorts_valid(int a, int b) public static int stbi__mul2shorts_valid(int a, int b)
{ {
if (b == 0 || b == -1) if (b == 0 || b == -1)
{
return 1; return 1;
}
if (a >= 0 == b >= 0) if (a >= 0 == b >= 0)
{
return a <= 32767 / b ? 1 : 0; return a <= 32767 / b ? 1 : 0;
}
if (b < 0) if (b < 0)
{
return a <= -32768 / b ? 1 : 0; return a <= -32768 / b ? 1 : 0;
}
return a >= -32768 / b ? 1 : 0; return a >= -32768 / b ? 1 : 0;
} }
@@ -182,7 +215,10 @@ namespace Misaki.HighPerformance.Image
var n = 0; var n = 0;
float* output; float* output;
if (data == null) if (data == null)
{
return null; return null;
}
output = (float*)stbi__malloc_mad4(x, y, comp, sizeof(float), 0); output = (float*)stbi__malloc_mad4(x, y, comp, sizeof(float), 0);
if (output == null) if (output == null)
{ {
@@ -191,17 +227,30 @@ namespace Misaki.HighPerformance.Image
} }
if ((comp & 1) != 0) if ((comp & 1) != 0)
{
n = comp; n = comp;
}
else else
{
n = comp - 1; n = comp - 1;
}
for (i = 0; i < x * y; ++i) for (i = 0; i < x * y; ++i)
{
for (k = 0; k < n; ++k) for (k = 0; k < n; ++k)
{
output[i * comp + k] = output[i * comp + k] =
(float)(CRuntime.pow(data[i * comp + k] / 255.0f, stbi__l2h_gamma) * stbi__l2h_scale); (float)(CRuntime.pow(data[i * comp + k] / 255.0f, stbi__l2h_gamma) * stbi__l2h_scale);
}
}
if (n < comp) if (n < comp)
{
for (i = 0; i < x * y; ++i) for (i = 0; i < x * y; ++i)
{
output[i * comp + n] = data[i * comp + n] / 255.0f; output[i * comp + n] = data[i * comp + n] / 255.0f;
}
}
CRuntime.free(data); CRuntime.free(data);
return output; return output;
@@ -215,15 +264,30 @@ namespace Misaki.HighPerformance.Image
ri->channel_order = STBI_ORDER_RGB; ri->channel_order = STBI_ORDER_RGB;
ri->num_channels = 0; ri->num_channels = 0;
if (stbi__png_test(s) != 0) if (stbi__png_test(s) != 0)
{
return stbi__png_load(s, x, y, comp, req_comp, ri); return stbi__png_load(s, x, y, comp, req_comp, ri);
}
if (stbi__bmp_test(s) != 0) if (stbi__bmp_test(s) != 0)
{
return stbi__bmp_load(s, x, y, comp, req_comp, ri); return stbi__bmp_load(s, x, y, comp, req_comp, ri);
}
if (stbi__gif_test(s) != 0) if (stbi__gif_test(s) != 0)
{
return stbi__gif_load(s, x, y, comp, req_comp, ri); return stbi__gif_load(s, x, y, comp, req_comp, ri);
}
if (stbi__psd_test(s) != 0) if (stbi__psd_test(s) != 0)
{
return stbi__psd_load(s, x, y, comp, req_comp, ri, bpc); return stbi__psd_load(s, x, y, comp, req_comp, ri, bpc);
}
if (stbi__jpeg_test(s) != 0) if (stbi__jpeg_test(s) != 0)
{
return stbi__jpeg_load(s, x, y, comp, req_comp, ri); return stbi__jpeg_load(s, x, y, comp, req_comp, ri);
}
if (stbi__hdr_test(s) != 0) if (stbi__hdr_test(s) != 0)
{ {
var hdr = stbi__hdr_load(s, x, y, comp, req_comp, ri); var hdr = stbi__hdr_load(s, x, y, comp, req_comp, ri);
@@ -231,7 +295,10 @@ namespace Misaki.HighPerformance.Image
} }
if (stbi__tga_test(s) != 0) if (stbi__tga_test(s) != 0)
{
return stbi__tga_load(s, x, y, comp, req_comp, ri); return stbi__tga_load(s, x, y, comp, req_comp, ri);
}
return (byte*)(ulong)(stbi__err("unknown image type") != 0 ? 0 : 0); return (byte*)(ulong)(stbi__err("unknown image type") != 0 ? 0 : 0);
} }
@@ -242,9 +309,14 @@ namespace Misaki.HighPerformance.Image
byte* reduced; byte* reduced;
reduced = (byte*)stbi__malloc((ulong)img_len); reduced = (byte*)stbi__malloc((ulong)img_len);
if (reduced == null) if (reduced == null)
{
return (byte*)(ulong)(stbi__err("outofmem") != 0 ? 0 : 0); return (byte*)(ulong)(stbi__err("outofmem") != 0 ? 0 : 0);
}
for (i = 0; i < img_len; ++i) for (i = 0; i < img_len; ++i)
{
reduced[i] = (byte)((orig[i] >> 8) & 0xFF); reduced[i] = (byte)((orig[i] >> 8) & 0xFF);
}
CRuntime.free(orig); CRuntime.free(orig);
return reduced; return reduced;
@@ -257,9 +329,14 @@ namespace Misaki.HighPerformance.Image
ushort* enlarged; ushort* enlarged;
enlarged = (ushort*)stbi__malloc((ulong)(img_len * 2)); enlarged = (ushort*)stbi__malloc((ulong)(img_len * 2));
if (enlarged == null) if (enlarged == null)
{
return (ushort*)(byte*)(ulong)(stbi__err("outofmem") != 0 ? 0 : 0); return (ushort*)(byte*)(ulong)(stbi__err("outofmem") != 0 ? 0 : 0);
}
for (i = 0; i < img_len; ++i) for (i = 0; i < img_len; ++i)
{
enlarged[i] = (ushort)((orig[i] << 8) + orig[i]); enlarged[i] = (ushort)((orig[i] << 8) + orig[i]);
}
CRuntime.free(orig); CRuntime.free(orig);
return enlarged; return enlarged;
@@ -306,7 +383,10 @@ namespace Misaki.HighPerformance.Image
var ri = new stbi__result_info(); var ri = new stbi__result_info();
var result = stbi__load_main(s, x, y, comp, req_comp, &ri, 8); var result = stbi__load_main(s, x, y, comp, req_comp, &ri, 8);
if (result == null) if (result == null)
{
return null; return null;
}
if (ri.bits_per_channel != 8) if (ri.bits_per_channel != 8)
{ {
result = stbi__convert_16_to_8((ushort*)result, *x, *y, req_comp == 0 ? *comp : req_comp); result = stbi__convert_16_to_8((ushort*)result, *x, *y, req_comp == 0 ? *comp : req_comp);
@@ -329,7 +409,10 @@ namespace Misaki.HighPerformance.Image
var ri = new stbi__result_info(); var ri = new stbi__result_info();
var result = stbi__load_main(s, x, y, comp, req_comp, &ri, 16); var result = stbi__load_main(s, x, y, comp, req_comp, &ri, 16);
if (result == null) if (result == null)
{
return null; return null;
}
if (ri.bits_per_channel != 16) if (ri.bits_per_channel != 16)
{ {
result = stbi__convert_8_to_16((byte*)result, *x, *y, req_comp == 0 ? *comp : req_comp); result = stbi__convert_8_to_16((byte*)result, *x, *y, req_comp == 0 ? *comp : req_comp);
@@ -366,13 +449,19 @@ namespace Misaki.HighPerformance.Image
var ri = new stbi__result_info(); var ri = new stbi__result_info();
var hdr_data = stbi__hdr_load(s, x, y, comp, req_comp, &ri); var hdr_data = stbi__hdr_load(s, x, y, comp, req_comp, &ri);
if (hdr_data != null) if (hdr_data != null)
{
stbi__float_postprocess(hdr_data, x, y, comp, req_comp); stbi__float_postprocess(hdr_data, x, y, comp, req_comp);
}
return hdr_data; return hdr_data;
} }
data = stbi__load_and_postprocess_8bit(s, x, y, comp, req_comp); data = stbi__load_and_postprocess_8bit(s, x, y, comp, req_comp);
if (data != null) if (data != null)
{
return stbi__ldr_to_hdr(data, *x, *y, req_comp != 0 ? req_comp : *comp); return stbi__ldr_to_hdr(data, *x, *y, req_comp != 0 ? req_comp : *comp);
}
return (float*)(ulong)(stbi__err("unknown image type") != 0 ? 0 : 0); return (float*)(ulong)(stbi__err("unknown image type") != 0 ? 0 : 0);
} }
@@ -412,7 +501,10 @@ namespace Misaki.HighPerformance.Image
var j = 0; var j = 0;
byte* good; byte* good;
if (req_comp == img_n) if (req_comp == img_n)
{
return data; return data;
}
good = (byte*)stbi__malloc_mad3(req_comp, (int)x, (int)y, 0); good = (byte*)stbi__malloc_mad3(req_comp, (int)x, (int)y, 0);
if (good == null) if (good == null)
{ {
@@ -436,7 +528,9 @@ namespace Misaki.HighPerformance.Image
break; break;
case 1 * 8 + 3: case 1 * 8 + 3:
for (i = (int)(x - 1); i >= 0; --i, src += 1, dest += 3) for (i = (int)(x - 1); i >= 0; --i, src += 1, dest += 3)
{
dest[0] = dest[1] = dest[2] = src[0]; dest[0] = dest[1] = dest[2] = src[0];
}
break; break;
case 1 * 8 + 4: case 1 * 8 + 4:
@@ -449,12 +543,16 @@ namespace Misaki.HighPerformance.Image
break; break;
case 2 * 8 + 1: case 2 * 8 + 1:
for (i = (int)(x - 1); i >= 0; --i, src += 2, dest += 1) for (i = (int)(x - 1); i >= 0; --i, src += 2, dest += 1)
{
dest[0] = src[0]; dest[0] = src[0];
}
break; break;
case 2 * 8 + 3: case 2 * 8 + 3:
for (i = (int)(x - 1); i >= 0; --i, src += 2, dest += 3) for (i = (int)(x - 1); i >= 0; --i, src += 2, dest += 3)
{
dest[0] = dest[1] = dest[2] = src[0]; dest[0] = dest[1] = dest[2] = src[0];
}
break; break;
case 2 * 8 + 4: case 2 * 8 + 4:
@@ -477,7 +575,9 @@ namespace Misaki.HighPerformance.Image
break; break;
case 3 * 8 + 1: case 3 * 8 + 1:
for (i = (int)(x - 1); i >= 0; --i, src += 3, dest += 1) for (i = (int)(x - 1); i >= 0; --i, src += 3, dest += 1)
{
dest[0] = stbi__compute_y(src[0], src[1], src[2]); dest[0] = stbi__compute_y(src[0], src[1], src[2]);
}
break; break;
case 3 * 8 + 2: case 3 * 8 + 2:
@@ -490,7 +590,9 @@ namespace Misaki.HighPerformance.Image
break; break;
case 4 * 8 + 1: case 4 * 8 + 1:
for (i = (int)(x - 1); i >= 0; --i, src += 4, dest += 1) for (i = (int)(x - 1); i >= 0; --i, src += 4, dest += 1)
{
dest[0] = stbi__compute_y(src[0], src[1], src[2]); dest[0] = stbi__compute_y(src[0], src[1], src[2]);
}
break; break;
case 4 * 8 + 2: case 4 * 8 + 2:
@@ -533,7 +635,10 @@ namespace Misaki.HighPerformance.Image
var j = 0; var j = 0;
ushort* good; ushort* good;
if (req_comp == img_n) if (req_comp == img_n)
{
return data; return data;
}
good = (ushort*)stbi__malloc((ulong)(req_comp * x * y * 2)); good = (ushort*)stbi__malloc((ulong)(req_comp * x * y * 2));
if (good == null) if (good == null)
{ {
@@ -557,7 +662,9 @@ namespace Misaki.HighPerformance.Image
break; break;
case 1 * 8 + 3: case 1 * 8 + 3:
for (i = (int)(x - 1); i >= 0; --i, src += 1, dest += 3) for (i = (int)(x - 1); i >= 0; --i, src += 1, dest += 3)
{
dest[0] = dest[1] = dest[2] = src[0]; dest[0] = dest[1] = dest[2] = src[0];
}
break; break;
case 1 * 8 + 4: case 1 * 8 + 4:
@@ -570,12 +677,16 @@ namespace Misaki.HighPerformance.Image
break; break;
case 2 * 8 + 1: case 2 * 8 + 1:
for (i = (int)(x - 1); i >= 0; --i, src += 2, dest += 1) for (i = (int)(x - 1); i >= 0; --i, src += 2, dest += 1)
{
dest[0] = src[0]; dest[0] = src[0];
}
break; break;
case 2 * 8 + 3: case 2 * 8 + 3:
for (i = (int)(x - 1); i >= 0; --i, src += 2, dest += 3) for (i = (int)(x - 1); i >= 0; --i, src += 2, dest += 3)
{
dest[0] = dest[1] = dest[2] = src[0]; dest[0] = dest[1] = dest[2] = src[0];
}
break; break;
case 2 * 8 + 4: case 2 * 8 + 4:
@@ -598,7 +709,9 @@ namespace Misaki.HighPerformance.Image
break; break;
case 3 * 8 + 1: case 3 * 8 + 1:
for (i = (int)(x - 1); i >= 0; --i, src += 3, dest += 1) for (i = (int)(x - 1); i >= 0; --i, src += 3, dest += 1)
{
dest[0] = stbi__compute_y_16(src[0], src[1], src[2]); dest[0] = stbi__compute_y_16(src[0], src[1], src[2]);
}
break; break;
case 3 * 8 + 2: case 3 * 8 + 2:
@@ -611,7 +724,9 @@ namespace Misaki.HighPerformance.Image
break; break;
case 4 * 8 + 1: case 4 * 8 + 1:
for (i = (int)(x - 1); i >= 0; --i, src += 4, dest += 1) for (i = (int)(x - 1); i >= 0; --i, src += 4, dest += 1)
{
dest[0] = stbi__compute_y_16(src[0], src[1], src[2]); dest[0] = stbi__compute_y_16(src[0], src[1], src[2]);
}
break; break;
case 4 * 8 + 2: case 4 * 8 + 2:
@@ -648,10 +763,15 @@ namespace Misaki.HighPerformance.Image
if ((uint)x > 255) if ((uint)x > 255)
{ {
if (x < 0) if (x < 0)
{
return 0; return 0;
}
if (x > 255) if (x > 255)
{
return 255; return 255;
} }
}
return (byte)x; return (byte)x;
} }
@@ -680,7 +800,10 @@ namespace Misaki.HighPerformance.Image
{ {
var n = 0; var n = 0;
if (z == 0) if (z == 0)
{
return -1; return -1;
}
if (z >= 0x10000) if (z >= 0x10000)
{ {
n += 16; n += 16;
@@ -706,7 +829,9 @@ namespace Misaki.HighPerformance.Image
} }
if (z >= 0x00002) if (z >= 0x00002)
{
n += 1; n += 1;
}
return n; return n;
} }
@@ -724,9 +849,14 @@ namespace Misaki.HighPerformance.Image
public static int stbi__shiftsigned(uint v, int shift, int bits) public static int stbi__shiftsigned(uint v, int shift, int bits)
{ {
if (shift < 0) if (shift < 0)
{
v <<= -shift; v <<= -shift;
}
else else
{
v >>= shift; v >>= shift;
}
v >>= 8 - bits; v >>= 8 - bits;
return (int)(v * stbi__shiftsigned_mul_table[bits]) >> stbi__shiftsigned_shift_table[bits]; return (int)(v * stbi__shiftsigned_mul_table[bits]) >> stbi__shiftsigned_shift_table[bits];
} }
@@ -734,28 +864,55 @@ namespace Misaki.HighPerformance.Image
public static int stbi__info_main(stbi__context s, int* x, int* y, int* comp) public static int stbi__info_main(stbi__context s, int* x, int* y, int* comp)
{ {
if (stbi__jpeg_info(s, x, y, comp) != 0) if (stbi__jpeg_info(s, x, y, comp) != 0)
{
return 1; return 1;
}
if (stbi__png_info(s, x, y, comp) != 0) if (stbi__png_info(s, x, y, comp) != 0)
{
return 1; return 1;
}
if (stbi__gif_info(s, x, y, comp) != 0) if (stbi__gif_info(s, x, y, comp) != 0)
{
return 1; return 1;
}
if (stbi__bmp_info(s, x, y, comp) != 0) if (stbi__bmp_info(s, x, y, comp) != 0)
{
return 1; return 1;
}
if (stbi__psd_info(s, x, y, comp) != 0) if (stbi__psd_info(s, x, y, comp) != 0)
{
return 1; return 1;
}
if (stbi__hdr_info(s, x, y, comp) != 0) if (stbi__hdr_info(s, x, y, comp) != 0)
{
return 1; return 1;
}
if (stbi__tga_info(s, x, y, comp) != 0) if (stbi__tga_info(s, x, y, comp) != 0)
{
return 1; return 1;
}
return stbi__err("unknown image type"); return stbi__err("unknown image type");
} }
public static int stbi__is_16_main(stbi__context s) public static int stbi__is_16_main(stbi__context s)
{ {
if (stbi__png_is16(s) != 0) if (stbi__png_is16(s) != 0)
{
return 1; return 1;
}
if (stbi__psd_is16(s) != 0) if (stbi__psd_is16(s) != 0)
{
return 1; return 1;
}
return 0; return 0;
} }

View File

@@ -25,8 +25,10 @@ namespace Misaki.HighPerformance.Image
*x = g.w; *x = g.w;
*y = g.h; *y = g.h;
if (req_comp != 0 && req_comp != 4) if (req_comp != 0 && req_comp != 4)
{
u = stbi__convert_format(u, 4, req_comp, (uint)g.w, (uint)g.h); u = stbi__convert_format(u, 4, req_comp, (uint)g.w, (uint)g.h);
} }
}
else if (g._out_ != null) else if (g._out_ != null)
{ {
CRuntime.free(g._out_); CRuntime.free(g._out_);
@@ -51,7 +53,10 @@ namespace Misaki.HighPerformance.Image
var out_size = 0; var out_size = 0;
var delays_size = 0; var delays_size = 0;
if (delays != null) if (delays != null)
{
*delays = null; *delays = null;
}
do do
{ {
u = stbi__gif_load_next(s, g, comp, req_comp, two_back); u = stbi__gif_load_next(s, g, comp, req_comp, two_back);
@@ -65,14 +70,20 @@ namespace Misaki.HighPerformance.Image
{ {
void* tmp = (byte*)CRuntime.realloc(_out_, (ulong)(layers * stride)); void* tmp = (byte*)CRuntime.realloc(_out_, (ulong)(layers * stride));
if (tmp == null) if (tmp == null)
{
return stbi__load_gif_main_outofmem(g, _out_, delays); return stbi__load_gif_main_outofmem(g, _out_, delays);
}
_out_ = (byte*)tmp; _out_ = (byte*)tmp;
out_size = layers * stride; out_size = layers * stride;
if (delays != null) if (delays != null)
{ {
var new_delays = (int*)CRuntime.realloc(*delays, (ulong)(sizeof(int) * layers)); var new_delays = (int*)CRuntime.realloc(*delays, (ulong)(sizeof(int) * layers));
if (new_delays == null) if (new_delays == null)
{
return stbi__load_gif_main_outofmem(g, _out_, delays); return stbi__load_gif_main_outofmem(g, _out_, delays);
}
*delays = new_delays; *delays = new_delays;
delays_size = layers * sizeof(int); delays_size = layers * sizeof(int);
} }
@@ -81,30 +92,44 @@ namespace Misaki.HighPerformance.Image
{ {
_out_ = (byte*)stbi__malloc((ulong)(layers * stride)); _out_ = (byte*)stbi__malloc((ulong)(layers * stride));
if (_out_ == null) if (_out_ == null)
{
return stbi__load_gif_main_outofmem(g, _out_, delays); return stbi__load_gif_main_outofmem(g, _out_, delays);
}
out_size = layers * stride; out_size = layers * stride;
if (delays != null) if (delays != null)
{ {
*delays = (int*)stbi__malloc((ulong)(layers * sizeof(int))); *delays = (int*)stbi__malloc((ulong)(layers * sizeof(int)));
if (*delays == null) if (*delays == null)
{
return stbi__load_gif_main_outofmem(g, _out_, delays); return stbi__load_gif_main_outofmem(g, _out_, delays);
}
delays_size = layers * sizeof(int); delays_size = layers * sizeof(int);
} }
} }
CRuntime.memcpy(_out_ + (layers - 1) * stride, u, (ulong)stride); CRuntime.memcpy(_out_ + (layers - 1) * stride, u, (ulong)stride);
if (layers >= 2) if (layers >= 2)
{
two_back = _out_ - 2 * stride; two_back = _out_ - 2 * stride;
}
if (delays != null) if (delays != null)
{
(*delays)[layers - 1U] = g.delay; (*delays)[layers - 1U] = g.delay;
} }
}
} while (u != null); } while (u != null);
CRuntime.free(g._out_); CRuntime.free(g._out_);
CRuntime.free(g.history); CRuntime.free(g.history);
CRuntime.free(g.background); CRuntime.free(g.background);
if (req_comp != 0 && req_comp != 4) if (req_comp != 0 && req_comp != 4)
{
_out_ = stbi__convert_format(_out_, 4, req_comp, (uint)(layers * g.w), (uint)g.h); _out_ = stbi__convert_format(_out_, 4, req_comp, (uint)(layers * g.w), (uint)g.h);
}
*z = layers; *z = layers;
return _out_; return _out_;
} }
@@ -121,12 +146,21 @@ namespace Misaki.HighPerformance.Image
{ {
var sz = 0; var sz = 0;
if (stbi__get8(s) != 71 || stbi__get8(s) != 73 || stbi__get8(s) != 70 || stbi__get8(s) != 56) if (stbi__get8(s) != 71 || stbi__get8(s) != 73 || stbi__get8(s) != 70 || stbi__get8(s) != 56)
{
return 0; return 0;
}
sz = stbi__get8(s); sz = stbi__get8(s);
if (sz != 57 && sz != 55) if (sz != 57 && sz != 55)
{
return 0; return 0;
}
if (stbi__get8(s) != 97) if (stbi__get8(s) != 97)
{
return 0; return 0;
}
return 1; return 1;
} }
@@ -146,12 +180,21 @@ namespace Misaki.HighPerformance.Image
{ {
byte version = 0; byte version = 0;
if (stbi__get8(s) != 71 || stbi__get8(s) != 73 || stbi__get8(s) != 70 || stbi__get8(s) != 56) if (stbi__get8(s) != 71 || stbi__get8(s) != 73 || stbi__get8(s) != 70 || stbi__get8(s) != 56)
{
return stbi__err("not GIF"); return stbi__err("not GIF");
}
version = stbi__get8(s); version = stbi__get8(s);
if (version != 55 && version != 57) if (version != 55 && version != 57)
{
return stbi__err("not GIF"); return stbi__err("not GIF");
}
if (stbi__get8(s) != 97) if (stbi__get8(s) != 97)
{
return stbi__err("not GIF"); return stbi__err("not GIF");
}
stbi__g_failure_reason = ""; stbi__g_failure_reason = "";
g.w = stbi__get16le(s); g.w = stbi__get16le(s);
g.h = stbi__get16le(s); g.h = stbi__get16le(s);
@@ -160,15 +203,30 @@ namespace Misaki.HighPerformance.Image
g.ratio = stbi__get8(s); g.ratio = stbi__get8(s);
g.transparent = -1; g.transparent = -1;
if (g.w > 1 << 24) if (g.w > 1 << 24)
{
return stbi__err("too large"); return stbi__err("too large");
}
if (g.h > 1 << 24) if (g.h > 1 << 24)
{
return stbi__err("too large"); return stbi__err("too large");
}
if (comp != null) if (comp != null)
{
*comp = 4; *comp = 4;
}
if (is_info != 0) if (is_info != 0)
{
return 1; return 1;
}
if ((g.flags & 0x80) != 0) if ((g.flags & 0x80) != 0)
{
stbi__gif_parse_colortable(s, g.pal, 2 << (g.flags & 7), -1); stbi__gif_parse_colortable(s, g.pal, 2 << (g.flags & 7), -1);
}
return 1; return 1;
} }
@@ -176,7 +234,10 @@ namespace Misaki.HighPerformance.Image
{ {
var g = new stbi__gif(); var g = new stbi__gif();
if (g == null) if (g == null)
{
return stbi__err("outofmem"); return stbi__err("outofmem");
}
if (stbi__gif_header(s, g, comp, 1) == 0) if (stbi__gif_header(s, g, comp, 1) == 0)
{ {
stbi__rewind(s); stbi__rewind(s);
@@ -184,9 +245,15 @@ namespace Misaki.HighPerformance.Image
} }
if (x != null) if (x != null)
{
*x = g.w; *x = g.w;
}
if (y != null) if (y != null)
{
*y = g.h; *y = g.h;
}
return 1; return 1;
} }
@@ -195,9 +262,15 @@ namespace Misaki.HighPerformance.Image
byte* p; byte* p;
var idx = 0; var idx = 0;
if (g.codes[code].prefix >= 0) if (g.codes[code].prefix >= 0)
{
stbi__out_gif_code(g, (ushort)g.codes[code].prefix); stbi__out_gif_code(g, (ushort)g.codes[code].prefix);
}
if (g.cur_y >= g.max_y) if (g.cur_y >= g.max_y)
{
return; return;
}
idx = g.cur_x + g.cur_y; idx = g.cur_x + g.cur_y;
p = &g._out_[idx]; p = &g._out_[idx];
g.history[idx / 4] = 1; g.history[idx / 4] = 1;
@@ -240,7 +313,10 @@ namespace Misaki.HighPerformance.Image
stbi__gif_lzw* p; stbi__gif_lzw* p;
lzw_cs = stbi__get8(s); lzw_cs = stbi__get8(s);
if (lzw_cs > 12) if (lzw_cs > 12)
{
return null; return null;
}
clear = 1 << lzw_cs; clear = 1 << lzw_cs;
first = 1; first = 1;
codesize = lzw_cs + 1; codesize = lzw_cs + 1;
@@ -258,14 +334,17 @@ namespace Misaki.HighPerformance.Image
oldcode = -1; oldcode = -1;
len = 0; len = 0;
for (; ; ) for (; ; )
{
if (valid_bits < codesize) if (valid_bits < codesize)
{ {
if (len == 0) if (len == 0)
{ {
len = stbi__get8(s); len = stbi__get8(s);
if (len == 0) if (len == 0)
{
return g._out_; return g._out_;
} }
}
--len; --len;
bits |= stbi__get8(s) << valid_bits; bits |= stbi__get8(s) << valid_bits;
@@ -288,20 +367,29 @@ namespace Misaki.HighPerformance.Image
{ {
stbi__skip(s, len); stbi__skip(s, len);
while ((len = stbi__get8(s)) > 0) while ((len = stbi__get8(s)) > 0)
{
stbi__skip(s, len); stbi__skip(s, len);
}
return g._out_; return g._out_;
} }
else if (code <= avail) else if (code <= avail)
{ {
if (first != 0) if (first != 0)
{
return (byte*)(ulong)(stbi__err("no clear code") != 0 ? 0 : 0); return (byte*)(ulong)(stbi__err("no clear code") != 0 ? 0 : 0);
}
if (oldcode >= 0) if (oldcode >= 0)
{ {
fixed (stbi__gif_lzw* p2 = &g.codes[avail++]) fixed (stbi__gif_lzw* p2 = &g.codes[avail++])
{ {
p = p2; p = p2;
if (avail > 8192) if (avail > 8192)
{
return (byte*)(ulong)(stbi__err("too many codes") != 0 ? 0 : 0); return (byte*)(ulong)(stbi__err("too many codes") != 0 ? 0 : 0);
}
p->prefix = (short)oldcode; p->prefix = (short)oldcode;
p->first = g.codes[oldcode].first; p->first = g.codes[oldcode].first;
p->suffix = code == avail ? p->first : g.codes[code].first; p->suffix = code == avail ? p->first : g.codes[code].first;
@@ -327,6 +415,7 @@ namespace Misaki.HighPerformance.Image
} }
} }
} }
}
public static byte* stbi__gif_load_next(stbi__context s, stbi__gif g, int* comp, int req_comp, byte* two_back) public static byte* stbi__gif_load_next(stbi__context s, stbi__gif g, int* comp, int req_comp, byte* two_back)
{ {
@@ -338,15 +427,24 @@ namespace Misaki.HighPerformance.Image
if (g._out_ == null) if (g._out_ == null)
{ {
if (stbi__gif_header(s, g, comp, 0) == 0) if (stbi__gif_header(s, g, comp, 0) == 0)
{
return null; return null;
}
if (stbi__mad3sizes_valid(4, g.w, g.h, 0) == 0) if (stbi__mad3sizes_valid(4, g.w, g.h, 0) == 0)
{
return (byte*)(ulong)(stbi__err("too large") != 0 ? 0 : 0); return (byte*)(ulong)(stbi__err("too large") != 0 ? 0 : 0);
}
pcount = g.w * g.h; pcount = g.w * g.h;
g._out_ = (byte*)stbi__malloc((ulong)(4 * pcount)); g._out_ = (byte*)stbi__malloc((ulong)(4 * pcount));
g.background = (byte*)stbi__malloc((ulong)(4 * pcount)); g.background = (byte*)stbi__malloc((ulong)(4 * pcount));
g.history = (byte*)stbi__malloc((ulong)pcount); g.history = (byte*)stbi__malloc((ulong)pcount);
if (g._out_ == null || g.background == null || g.history == null) if (g._out_ == null || g.background == null || g.history == null)
{
return (byte*)(ulong)(stbi__err("outofmem") != 0 ? 0 : 0); return (byte*)(ulong)(stbi__err("outofmem") != 0 ? 0 : 0);
}
CRuntime.memset(g._out_, 0x00, (ulong)(4 * pcount)); CRuntime.memset(g._out_, 0x00, (ulong)(4 * pcount));
CRuntime.memset(g.background, 0x00, (ulong)(4 * pcount)); CRuntime.memset(g.background, 0x00, (ulong)(4 * pcount));
CRuntime.memset(g.history, 0x00, (ulong)pcount); CRuntime.memset(g.history, 0x00, (ulong)pcount);
@@ -357,19 +455,30 @@ namespace Misaki.HighPerformance.Image
dispose = (g.eflags & 0x1C) >> 2; dispose = (g.eflags & 0x1C) >> 2;
pcount = g.w * g.h; pcount = g.w * g.h;
if (dispose == 3 && two_back == null) if (dispose == 3 && two_back == null)
{
dispose = 2; dispose = 2;
}
if (dispose == 3) if (dispose == 3)
{ {
for (pi = 0; pi < pcount; ++pi) for (pi = 0; pi < pcount; ++pi)
{
if (g.history[pi] != 0) if (g.history[pi] != 0)
{
CRuntime.memcpy(&g._out_[pi * 4], &two_back[pi * 4], (ulong)4); CRuntime.memcpy(&g._out_[pi * 4], &two_back[pi * 4], (ulong)4);
} }
}
}
else if (dispose == 2) else if (dispose == 2)
{ {
for (pi = 0; pi < pcount; ++pi) for (pi = 0; pi < pcount; ++pi)
{
if (g.history[pi] != 0) if (g.history[pi] != 0)
{
CRuntime.memcpy(&g._out_[pi * 4], &g.background[pi * 4], (ulong)4); 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.memcpy(g.background, g._out_, (ulong)(4 * g.w * g.h));
} }
@@ -392,7 +501,10 @@ namespace Misaki.HighPerformance.Image
w = stbi__get16le(s); w = stbi__get16le(s);
h = stbi__get16le(s); h = stbi__get16le(s);
if (x + w > g.w || y + h > g.h) if (x + w > g.w || y + h > g.h)
{
return (byte*)(ulong)(stbi__err("bad Image Descriptor") != 0 ? 0 : 0); return (byte*)(ulong)(stbi__err("bad Image Descriptor") != 0 ? 0 : 0);
}
g.line_size = g.w * 4; g.line_size = g.w * 4;
g.start_x = x * 4; g.start_x = x * 4;
g.start_y = y * g.line_size; g.start_y = y * g.line_size;
@@ -401,7 +513,10 @@ namespace Misaki.HighPerformance.Image
g.cur_x = g.start_x; g.cur_x = g.start_x;
g.cur_y = g.start_y; g.cur_y = g.start_y;
if (w == 0) if (w == 0)
{
g.cur_y = g.max_y; g.cur_y = g.max_y;
}
g.lflags = stbi__get8(s); g.lflags = stbi__get8(s);
if ((g.lflags & 0x40) != 0) if ((g.lflags & 0x40) != 0)
{ {
@@ -431,10 +546,15 @@ namespace Misaki.HighPerformance.Image
o = stbi__process_gif_raster(s, g); o = stbi__process_gif_raster(s, g);
if (o == null) if (o == null)
{
return null; return null;
}
pcount = g.w * g.h; pcount = g.w * g.h;
if (first_frame != 0 && g.bgindex > 0) if (first_frame != 0 && g.bgindex > 0)
{
for (pi = 0; pi < pcount; ++pi) for (pi = 0; pi < pcount; ++pi)
{
if (g.history[pi] == 0) if (g.history[pi] == 0)
{ {
g.pal[g.bgindex][3] = 255; g.pal[g.bgindex][3] = 255;
@@ -443,6 +563,8 @@ namespace Misaki.HighPerformance.Image
CRuntime.memcpy(&g._out_[pi * 4], ptr, (ulong)4); CRuntime.memcpy(&g._out_[pi * 4], ptr, (ulong)4);
} }
} }
}
}
return o; return o;
} }
@@ -459,13 +581,18 @@ namespace Misaki.HighPerformance.Image
g.eflags = stbi__get8(s); g.eflags = stbi__get8(s);
g.delay = 10 * stbi__get16le(s); g.delay = 10 * stbi__get16le(s);
if (g.transparent >= 0) if (g.transparent >= 0)
{
g.pal[g.transparent][3] = 255; g.pal[g.transparent][3] = 255;
}
if ((g.eflags & 0x01) != 0) if ((g.eflags & 0x01) != 0)
{ {
g.transparent = stbi__get8(s); g.transparent = stbi__get8(s);
if (g.transparent >= 0) if (g.transparent >= 0)
{
g.pal[g.transparent][3] = 0; g.pal[g.transparent][3] = 0;
} }
}
else else
{ {
stbi__skip(s, 1); stbi__skip(s, 1);
@@ -480,7 +607,10 @@ namespace Misaki.HighPerformance.Image
} }
while ((len = stbi__get8(s)) != 0) while ((len = stbi__get8(s)) != 0)
{
stbi__skip(s, len); stbi__skip(s, len);
}
break; break;
} }
@@ -498,9 +628,15 @@ namespace Misaki.HighPerformance.Image
CRuntime.free(g.history); CRuntime.free(g.history);
CRuntime.free(g.background); CRuntime.free(g.background);
if (_out_ != null) if (_out_ != null)
{
CRuntime.free(_out_); CRuntime.free(_out_);
}
if (delays != null && *delays != null) if (delays != null && *delays != null)
{
CRuntime.free(*delays); CRuntime.free(*delays);
}
return (byte*)(ulong)(stbi__err("outofmem") != 0 ? 0 : 0); return (byte*)(ulong)(stbi__err("outofmem") != 0 ? 0 : 0);
} }

View File

@@ -44,44 +44,81 @@ namespace Misaki.HighPerformance.Image
sbyte* headerToken; sbyte* headerToken;
headerToken = stbi__hdr_gettoken(s, buffer); headerToken = stbi__hdr_gettoken(s, buffer);
if (CRuntime.strcmp(headerToken, "#?RADIANCE") != 0 && CRuntime.strcmp(headerToken, "#?RGBE") != 0) if (CRuntime.strcmp(headerToken, "#?RADIANCE") != 0 && CRuntime.strcmp(headerToken, "#?RGBE") != 0)
{
return (float*)(ulong)(stbi__err("not HDR") != 0 ? 0 : 0); return (float*)(ulong)(stbi__err("not HDR") != 0 ? 0 : 0);
}
for (; ; ) for (; ; )
{ {
token = stbi__hdr_gettoken(s, buffer); token = stbi__hdr_gettoken(s, buffer);
if (token[0] == 0) if (token[0] == 0)
{
break; break;
}
if (CRuntime.strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) if (CRuntime.strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0)
{
valid = 1; valid = 1;
} }
}
if (valid == 0) if (valid == 0)
{
return (float*)(ulong)(stbi__err("unsupported format") != 0 ? 0 : 0); return (float*)(ulong)(stbi__err("unsupported format") != 0 ? 0 : 0);
}
token = stbi__hdr_gettoken(s, buffer); token = stbi__hdr_gettoken(s, buffer);
if (CRuntime.strncmp(token, "-Y ", 3) != 0) if (CRuntime.strncmp(token, "-Y ", 3) != 0)
{
return (float*)(ulong)(stbi__err("unsupported data layout") != 0 ? 0 : 0); return (float*)(ulong)(stbi__err("unsupported data layout") != 0 ? 0 : 0);
}
token += 3; token += 3;
height = (int)CRuntime.strtol(token, &token, 10); height = (int)CRuntime.strtol(token, &token, 10);
while (*token == 32) while (*token == 32)
{
++token; ++token;
}
if (CRuntime.strncmp(token, "+X ", 3) != 0) if (CRuntime.strncmp(token, "+X ", 3) != 0)
{
return (float*)(ulong)(stbi__err("unsupported data layout") != 0 ? 0 : 0); return (float*)(ulong)(stbi__err("unsupported data layout") != 0 ? 0 : 0);
}
token += 3; token += 3;
width = (int)CRuntime.strtol(token, null, 10); width = (int)CRuntime.strtol(token, null, 10);
if (height > 1 << 24) if (height > 1 << 24)
{
return (float*)(ulong)(stbi__err("too large") != 0 ? 0 : 0); return (float*)(ulong)(stbi__err("too large") != 0 ? 0 : 0);
}
if (width > 1 << 24) if (width > 1 << 24)
{
return (float*)(ulong)(stbi__err("too large") != 0 ? 0 : 0); return (float*)(ulong)(stbi__err("too large") != 0 ? 0 : 0);
}
*x = width; *x = width;
*y = height; *y = height;
if (comp != null) if (comp != null)
{
*comp = 3; *comp = 3;
}
if (req_comp == 0) if (req_comp == 0)
{
req_comp = 3; req_comp = 3;
}
if (stbi__mad4sizes_valid(width, height, req_comp, sizeof(float), 0) == 0) if (stbi__mad4sizes_valid(width, height, req_comp, sizeof(float), 0) == 0)
{
return (float*)(ulong)(stbi__err("too large") != 0 ? 0 : 0); return (float*)(ulong)(stbi__err("too large") != 0 ? 0 : 0);
}
hdr_data = (float*)stbi__malloc_mad4(width, height, req_comp, sizeof(float), 0); hdr_data = (float*)stbi__malloc_mad4(width, height, req_comp, sizeof(float), 0);
if (hdr_data == null) if (hdr_data == null)
{
return (float*)(ulong)(stbi__err("outofmem") != 0 ? 0 : 0); return (float*)(ulong)(stbi__err("outofmem") != 0 ? 0 : 0);
}
if (width < 8 || width >= 32768) if (width < 8 || width >= 32768)
{ {
@@ -176,8 +213,10 @@ namespace Misaki.HighPerformance.Image
} }
for (z = 0; z < count; ++z) for (z = 0; z < count; ++z)
{
scanline[i++ * 4 + k] = value; scanline[i++ * 4 + k] = value;
} }
}
else else
{ {
if (count == 0 || count > nleft) if (count == 0 || count > nleft)
@@ -188,18 +227,24 @@ namespace Misaki.HighPerformance.Image
} }
for (z = 0; z < count; ++z) for (z = 0; z < count; ++z)
{
scanline[i++ * 4 + k] = stbi__get8(s); scanline[i++ * 4 + k] = stbi__get8(s);
} }
} }
} }
}
for (i = 0; i < width; ++i) for (i = 0; i < width; ++i)
{
stbi__hdr_convert(hdr_data + (j * width + i) * req_comp, scanline + i * 4, req_comp); stbi__hdr_convert(hdr_data + (j * width + i) * req_comp, scanline + i * 4, req_comp);
} }
}
if (scanline != null) if (scanline != null)
{
CRuntime.free(scanline); CRuntime.free(scanline);
} }
}
finish: finish:
return hdr_data; return hdr_data;
@@ -212,11 +257,20 @@ namespace Misaki.HighPerformance.Image
var valid = 0; var valid = 0;
var dummy = 0; var dummy = 0;
if (x == null) if (x == null)
{
x = &dummy; x = &dummy;
}
if (y == null) if (y == null)
{
y = &dummy; y = &dummy;
}
if (comp == null) if (comp == null)
{
comp = &dummy; comp = &dummy;
}
if (stbi__hdr_test(s) == 0) if (stbi__hdr_test(s) == 0)
{ {
stbi__rewind(s); stbi__rewind(s);
@@ -227,10 +281,15 @@ namespace Misaki.HighPerformance.Image
{ {
token = stbi__hdr_gettoken(s, buffer); token = stbi__hdr_gettoken(s, buffer);
if (token[0] == 0) if (token[0] == 0)
{
break; break;
}
if (CRuntime.strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) if (CRuntime.strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0)
{
valid = 1; valid = 1;
} }
}
if (valid == 0) if (valid == 0)
{ {
@@ -248,7 +307,10 @@ namespace Misaki.HighPerformance.Image
token += 3; token += 3;
*y = (int)CRuntime.strtol(token, &token, 10); *y = (int)CRuntime.strtol(token, &token, 10);
while (*token == 32) while (*token == 32)
{
++token; ++token;
}
if (CRuntime.strncmp(token, "+X ", 3) != 0) if (CRuntime.strncmp(token, "+X ", 3) != 0)
{ {
stbi__rewind(s); stbi__rewind(s);
@@ -268,7 +330,10 @@ namespace Misaki.HighPerformance.Image
var n = 0; var n = 0;
byte* output; byte* output;
if (data == null) if (data == null)
{
return null; return null;
}
output = (byte*)stbi__malloc_mad3(x, y, comp, 0); output = (byte*)stbi__malloc_mad3(x, y, comp, 0);
if (output == null) if (output == null)
{ {
@@ -277,9 +342,14 @@ namespace Misaki.HighPerformance.Image
} }
if ((comp & 1) != 0) if ((comp & 1) != 0)
{
n = comp; n = comp;
}
else else
{
n = comp - 1; n = comp - 1;
}
for (i = 0; i < x * y; ++i) for (i = 0; i < x * y; ++i)
{ {
for (k = 0; k < n; ++k) for (k = 0; k < n; ++k)
@@ -287,9 +357,15 @@ namespace Misaki.HighPerformance.Image
var z = (float)CRuntime.pow(data[i * comp + k] * stbi__h2l_scale_i, stbi__h2l_gamma_i) * 255 + var z = (float)CRuntime.pow(data[i * comp + k] * stbi__h2l_scale_i, stbi__h2l_gamma_i) * 255 +
0.5f; 0.5f;
if (z < 0) if (z < 0)
{
z = 0; z = 0;
}
if (z > 255) if (z > 255)
{
z = 255; z = 255;
}
output[i * comp + k] = (byte)(int)z; output[i * comp + k] = (byte)(int)z;
} }
@@ -297,9 +373,15 @@ namespace Misaki.HighPerformance.Image
{ {
var z = data[i * comp + k] * 255 + 0.5f; var z = data[i * comp + k] * 255 + 0.5f;
if (z < 0) if (z < 0)
{
z = 0; z = 0;
}
if (z > 255) if (z > 255)
{
z = 255; z = 255;
}
output[i * comp + k] = (byte)(int)z; output[i * comp + k] = (byte)(int)z;
} }
} }
@@ -312,8 +394,13 @@ namespace Misaki.HighPerformance.Image
{ {
var i = 0; var i = 0;
for (i = 0; i < signature.Length; ++i) for (i = 0; i < signature.Length; ++i)
{
if (stbi__get8(s) != signature[i]) if (stbi__get8(s) != signature[i])
{
return 0; return 0;
}
}
stbi__rewind(s); stbi__rewind(s);
return 1; return 1;
} }
@@ -360,10 +447,15 @@ namespace Misaki.HighPerformance.Image
} }
if (req_comp == 2) if (req_comp == 2)
{
output[1] = 1; output[1] = 1;
}
if (req_comp == 4) if (req_comp == 4)
{
output[3] = 1; output[3] = 1;
} }
}
else else
{ {
switch (req_comp) switch (req_comp)
@@ -371,13 +463,19 @@ namespace Misaki.HighPerformance.Image
case 4: case 4:
case 3: case 3:
if (req_comp == 4) if (req_comp == 4)
{
output[3] = 1; output[3] = 1;
}
output[0] = output[1] = output[2] = 0; output[0] = output[1] = output[2] = 0;
break; break;
case 2: case 2:
case 1: case 1:
if (req_comp == 2) if (req_comp == 2)
{
output[1] = 1; output[1] = 1;
}
output[0] = 0; output[0] = 0;
break; break;
} }

File diff suppressed because it is too large Load Diff

View File

@@ -15,7 +15,7 @@ namespace Misaki.HighPerformance.Image
public const int STBI__F_avg_first = 5; public const int STBI__F_avg_first = 5;
public static byte[] first_row_filter = public static byte[] first_row_filter =
{ STBI__F_none, STBI__F_sub, STBI__F_none, STBI__F_avg_first, STBI__F_sub }; [STBI__F_none, STBI__F_sub, STBI__F_none, STBI__F_avg_first, STBI__F_sub];
public static byte[] stbi__check_png_header_png_sig = { 137, 80, 78, 71, 13, 10, 26, 10 }; public static byte[] stbi__check_png_header_png_sig = { 137, 80, 78, 71, 13, 10, 26, 10 };
public static byte[] stbi__depth_scale_table = { 0, 0xff, 0x55, 0, 0x11, 0, 0, 0, 0x01 }; public static byte[] stbi__depth_scale_table = { 0, 0xff, 0x55, 0, 0x11, 0, 0, 0, 0x01 };
@@ -48,7 +48,9 @@ namespace Misaki.HighPerformance.Image
var p = new stbi__png(); var p = new stbi__png();
p.s = s; p.s = s;
if (stbi__png_info_raw(p, null, null, null) == 0) if (stbi__png_info_raw(p, null, null, null) == 0)
{
return 0; return 0;
}
if (p.depth != 16) if (p.depth != 16)
{ {
stbi__rewind(p.s); stbi__rewind(p.s);
@@ -70,8 +72,12 @@ namespace Misaki.HighPerformance.Image
{ {
var i = 0; var i = 0;
for (i = 0; i < 8; ++i) for (i = 0; i < 8; ++i)
{
if (stbi__get8(s) != stbi__check_png_header_png_sig[i]) if (stbi__get8(s) != stbi__check_png_header_png_sig[i])
{
return stbi__err("bad png sig"); return stbi__err("bad png sig");
}
}
return 1; return 1;
} }
@@ -90,12 +96,15 @@ namespace Misaki.HighPerformance.Image
{ {
var i = 0; var i = 0;
if (img_n == 1) if (img_n == 1)
{
for (i = (int)(x - 1); i >= 0; --i) for (i = (int)(x - 1); i >= 0; --i)
{ {
dest[i * 2 + 1] = 255; dest[i * 2 + 1] = 255;
dest[i * 2 + 0] = src[i]; dest[i * 2 + 0] = src[i];
} }
}
else else
{
for (i = (int)(x - 1); i >= 0; --i) for (i = (int)(x - 1); i >= 0; --i)
{ {
dest[i * 4 + 3] = 255; dest[i * 4 + 3] = 255;
@@ -104,6 +113,7 @@ namespace Misaki.HighPerformance.Image
dest[i * 4 + 0] = src[i * 3 + 0]; dest[i * 4 + 0] = src[i * 3 + 0];
} }
} }
}
public static int stbi__create_png_image_raw(stbi__png a, byte* raw, uint raw_len, int out_n, uint x, uint y, public static int stbi__create_png_image_raw(stbi__png a, byte* raw, uint raw_len, int out_n, uint x, uint y,
@@ -123,20 +133,36 @@ namespace Misaki.HighPerformance.Image
var output_bytes = out_n * bytes; var output_bytes = out_n * bytes;
var filter_bytes = img_n * bytes; var filter_bytes = img_n * bytes;
var width = (int)x; var width = (int)x;
a._out_ = (byte*)stbi__malloc_mad3((int)x, (int)y, output_bytes, 0); a._out_ = (byte*)stbi__malloc_mad3((int)x, (int)y, output_bytes, 0);
if (a._out_ == null) if (a._out_ == null)
{
return stbi__err("outofmem"); return stbi__err("outofmem");
}
if (stbi__mad3sizes_valid(img_n, (int)x, depth, 7) == 0) if (stbi__mad3sizes_valid(img_n, (int)x, depth, 7) == 0)
{
return stbi__err("too large"); return stbi__err("too large");
}
img_width_bytes = (uint)((img_n * x * depth + 7) >> 3); img_width_bytes = (uint)((img_n * x * depth + 7) >> 3);
if (stbi__mad2sizes_valid((int)img_width_bytes, (int)y, (int)img_width_bytes) == 0) if (stbi__mad2sizes_valid((int)img_width_bytes, (int)y, (int)img_width_bytes) == 0)
{
return stbi__err("too large"); return stbi__err("too large");
}
img_len = (img_width_bytes + 1) * y; img_len = (img_width_bytes + 1) * y;
if (raw_len < img_len) if (raw_len < img_len)
{
return stbi__err("not enough pixels"); return stbi__err("not enough pixels");
}
filter_buf = (byte*)stbi__malloc_mad2((int)img_width_bytes, 2, 0); filter_buf = (byte*)stbi__malloc_mad2((int)img_width_bytes, 2, 0);
if (filter_buf == null) if (filter_buf == null)
{
return stbi__err("outofmem"); return stbi__err("outofmem");
}
if (depth < 8) if (depth < 8)
{ {
filter_bytes = 1; filter_bytes = 1;
@@ -157,7 +183,10 @@ namespace Misaki.HighPerformance.Image
} }
if (j == 0) if (j == 0)
{
filter = first_row_filter[filter]; filter = first_row_filter[filter];
}
switch (filter) switch (filter)
{ {
case STBI__F_none: case STBI__F_none:
@@ -166,35 +195,49 @@ namespace Misaki.HighPerformance.Image
case STBI__F_sub: case STBI__F_sub:
CRuntime.memcpy(cur, raw, (ulong)filter_bytes); CRuntime.memcpy(cur, raw, (ulong)filter_bytes);
for (k = filter_bytes; k < nk; ++k) for (k = filter_bytes; k < nk; ++k)
{
cur[k] = (byte)((raw[k] + cur[k - filter_bytes]) & 255); cur[k] = (byte)((raw[k] + cur[k - filter_bytes]) & 255);
}
break; break;
case STBI__F_up: case STBI__F_up:
for (k = 0; k < nk; ++k) for (k = 0; k < nk; ++k)
{
cur[k] = (byte)((raw[k] + prior[k]) & 255); cur[k] = (byte)((raw[k] + prior[k]) & 255);
}
break; break;
case STBI__F_avg: case STBI__F_avg:
for (k = 0; k < filter_bytes; ++k) for (k = 0; k < filter_bytes; ++k)
{
cur[k] = (byte)((raw[k] + (prior[k] >> 1)) & 255); cur[k] = (byte)((raw[k] + (prior[k] >> 1)) & 255);
}
for (k = filter_bytes; k < nk; ++k) for (k = filter_bytes; k < nk; ++k)
{
cur[k] = (byte)((raw[k] + ((prior[k] + cur[k - filter_bytes]) >> 1)) & 255); cur[k] = (byte)((raw[k] + ((prior[k] + cur[k - filter_bytes]) >> 1)) & 255);
}
break; break;
case STBI__F_paeth: case STBI__F_paeth:
for (k = 0; k < filter_bytes; ++k) for (k = 0; k < filter_bytes; ++k)
{
cur[k] = (byte)((raw[k] + prior[k]) & 255); cur[k] = (byte)((raw[k] + prior[k]) & 255);
}
for (k = filter_bytes; k < nk; ++k) for (k = filter_bytes; k < nk; ++k)
{
cur[k] = (byte)((raw[k] + stbi__paeth(cur[k - filter_bytes], prior[k], cur[k] = (byte)((raw[k] + stbi__paeth(cur[k - filter_bytes], prior[k],
prior[k - filter_bytes])) & 255); prior[k - filter_bytes])) & 255);
}
break; break;
case STBI__F_avg_first: case STBI__F_avg_first:
CRuntime.memcpy(cur, raw, (ulong)filter_bytes); CRuntime.memcpy(cur, raw, (ulong)filter_bytes);
for (k = filter_bytes; k < nk; ++k) for (k = filter_bytes; k < nk; ++k)
{
cur[k] = (byte)((raw[k] + (cur[k - filter_bytes] >> 1)) & 255); cur[k] = (byte)((raw[k] + (cur[k - filter_bytes] >> 1)) & 255);
}
break; break;
} }
@@ -208,40 +251,61 @@ namespace Misaki.HighPerformance.Image
byte inb = 0; byte inb = 0;
var nsmp = (uint)(x * img_n); var nsmp = (uint)(x * img_n);
if (depth == 4) if (depth == 4)
{
for (i = 0; i < nsmp; ++i) for (i = 0; i < nsmp; ++i)
{ {
if ((i & 1) == 0) if ((i & 1) == 0)
{
inb = *_in_++; inb = *_in_++;
}
*_out_++ = (byte)(scale * (inb >> 4)); *_out_++ = (byte)(scale * (inb >> 4));
inb <<= 4; inb <<= 4;
} }
}
else if (depth == 2) else if (depth == 2)
{
for (i = 0; i < nsmp; ++i) for (i = 0; i < nsmp; ++i)
{ {
if ((i & 3) == 0) if ((i & 3) == 0)
{
inb = *_in_++; inb = *_in_++;
}
*_out_++ = (byte)(scale * (inb >> 6)); *_out_++ = (byte)(scale * (inb >> 6));
inb <<= 2; inb <<= 2;
} }
}
else else
{
for (i = 0; i < nsmp; ++i) for (i = 0; i < nsmp; ++i)
{ {
if ((i & 7) == 0) if ((i & 7) == 0)
{
inb = *_in_++; inb = *_in_++;
}
*_out_++ = (byte)(scale * (inb >> 7)); *_out_++ = (byte)(scale * (inb >> 7));
inb <<= 1; inb <<= 1;
} }
}
if (img_n != out_n) if (img_n != out_n)
{
stbi__create_png_alpha_expand8(dest, dest, x, img_n); stbi__create_png_alpha_expand8(dest, dest, x, img_n);
} }
}
else if (depth == 8) else if (depth == 8)
{ {
if (img_n == out_n) if (img_n == out_n)
{
CRuntime.memcpy(dest, cur, (ulong)(x * img_n)); CRuntime.memcpy(dest, cur, (ulong)(x * img_n));
}
else else
{
stbi__create_png_alpha_expand8(dest, cur, x, img_n); stbi__create_png_alpha_expand8(dest, cur, x, img_n);
} }
}
else if (depth == 16) else if (depth == 16)
{ {
var dest16 = (ushort*)dest; var dest16 = (ushort*)dest;
@@ -249,17 +313,22 @@ namespace Misaki.HighPerformance.Image
if (img_n == out_n) if (img_n == out_n)
{ {
for (i = 0; i < nsmp; ++i, ++dest16, cur += 2) for (i = 0; i < nsmp; ++i, ++dest16, cur += 2)
{
*dest16 = (ushort)((cur[0] << 8) | cur[1]); *dest16 = (ushort)((cur[0] << 8) | cur[1]);
} }
}
else else
{ {
if (img_n == 1) if (img_n == 1)
{
for (i = 0; i < x; ++i, dest16 += 2, cur += 2) for (i = 0; i < x; ++i, dest16 += 2, cur += 2)
{ {
dest16[0] = (ushort)((cur[0] << 8) | cur[1]); dest16[0] = (ushort)((cur[0] << 8) | cur[1]);
dest16[1] = 0xffff; dest16[1] = 0xffff;
} }
}
else else
{
for (i = 0; i < x; ++i, dest16 += 4, cur += 6) for (i = 0; i < x; ++i, dest16 += 4, cur += 6)
{ {
dest16[0] = (ushort)((cur[0] << 8) | cur[1]); dest16[0] = (ushort)((cur[0] << 8) | cur[1]);
@@ -270,10 +339,14 @@ namespace Misaki.HighPerformance.Image
} }
} }
} }
}
CRuntime.free(filter_buf); CRuntime.free(filter_buf);
if (all_ok == 0) if (all_ok == 0)
{
return 0; return 0;
}
return 1; return 1;
} }
@@ -285,17 +358,23 @@ namespace Misaki.HighPerformance.Image
byte* final; byte* final;
var p = 0; var p = 0;
if (interlaced == 0) if (interlaced == 0)
return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a.s.img_x, a.s.img_y, depth,
color);
final = (byte*)stbi__malloc_mad3((int)a.s.img_x, (int)a.s.img_y, out_bytes, 0);
if (final == null)
return stbi__err("outofmem");
for (p = 0; p < 7; ++p)
{ {
return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a.s.img_x, a.s.img_y, depth, color);
}
final = (byte*)stbi__malloc_mad3((int)a.s.img_x, (int)a.s.img_y, out_bytes, 0);
if (final == null)
{
return stbi__err("outofmem");
}
var xorig = stackalloc int[] { 0, 4, 0, 2, 0, 1, 0 }; var xorig = stackalloc int[] { 0, 4, 0, 2, 0, 1, 0 };
var yorig = stackalloc int[] { 0, 0, 4, 0, 2, 0, 1 }; var yorig = stackalloc int[] { 0, 0, 4, 0, 2, 0, 1 };
var xspc = stackalloc int[] { 8, 8, 4, 4, 2, 2, 1 }; var xspc = stackalloc int[] { 8, 8, 4, 4, 2, 2, 1 };
var yspc = stackalloc int[] { 8, 8, 8, 4, 4, 2, 2 }; var yspc = stackalloc int[] { 8, 8, 8, 4, 4, 2, 2 };
for (p = 0; p < 7; ++p)
{
var i = 0; var i = 0;
var j = 0; var j = 0;
var x = 0; var x = 0;
@@ -313,6 +392,7 @@ namespace Misaki.HighPerformance.Image
} }
for (j = 0; j < y; ++j) for (j = 0; j < y; ++j)
{
for (i = 0; i < x; ++i) for (i = 0; i < x; ++i)
{ {
var out_y = j * yspc[p] + yorig[p]; var out_y = j * yspc[p] + yorig[p];
@@ -320,6 +400,7 @@ namespace Misaki.HighPerformance.Image
CRuntime.memcpy(final + out_y * a.s.img_x * out_bytes + out_x * out_bytes, CRuntime.memcpy(final + out_y * a.s.img_x * out_bytes + out_x * out_bytes,
a._out_ + (j * x + i) * out_bytes, (ulong)out_bytes); a._out_ + (j * x + i) * out_bytes, (ulong)out_bytes);
} }
}
CRuntime.free(a._out_); CRuntime.free(a._out_);
image_data += img_len; image_data += img_len;
@@ -338,18 +419,25 @@ namespace Misaki.HighPerformance.Image
var pixel_count = s.img_x * s.img_y; var pixel_count = s.img_x * s.img_y;
var p = z._out_; var p = z._out_;
if (out_n == 2) if (out_n == 2)
{
for (i = 0; i < pixel_count; ++i) for (i = 0; i < pixel_count; ++i)
{ {
p[1] = (byte)(p[0] == tc[0] ? 0 : 255); p[1] = (byte)(p[0] == tc[0] ? 0 : 255);
p += 2; p += 2;
} }
}
else else
{
for (i = 0; i < pixel_count; ++i) for (i = 0; i < pixel_count; ++i)
{ {
if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2])
{
p[3] = 0; p[3] = 0;
}
p += 4; p += 4;
} }
}
return 1; return 1;
} }
@@ -361,18 +449,25 @@ namespace Misaki.HighPerformance.Image
var pixel_count = s.img_x * s.img_y; var pixel_count = s.img_x * s.img_y;
var p = (ushort*)z._out_; var p = (ushort*)z._out_;
if (out_n == 2) if (out_n == 2)
{
for (i = 0; i < pixel_count; ++i) for (i = 0; i < pixel_count; ++i)
{ {
p[1] = (ushort)(p[0] == tc[0] ? 0 : 65535); p[1] = (ushort)(p[0] == tc[0] ? 0 : 65535);
p += 2; p += 2;
} }
}
else else
{
for (i = 0; i < pixel_count; ++i) for (i = 0; i < pixel_count; ++i)
{ {
if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2])
{
p[3] = 0; p[3] = 0;
}
p += 4; p += 4;
} }
}
return 1; return 1;
} }
@@ -386,9 +481,12 @@ namespace Misaki.HighPerformance.Image
var orig = a._out_; var orig = a._out_;
p = (byte*)stbi__malloc_mad2((int)pixel_count, pal_img_n, 0); p = (byte*)stbi__malloc_mad2((int)pixel_count, pal_img_n, 0);
if (p == null) if (p == null)
{
return stbi__err("outofmem"); return stbi__err("outofmem");
}
temp_out = p; temp_out = p;
if (pal_img_n == 3) if (pal_img_n == 3)
{
for (i = 0; i < pixel_count; ++i) for (i = 0; i < pixel_count; ++i)
{ {
var n = orig[i] * 4; var n = orig[i] * 4;
@@ -397,7 +495,9 @@ namespace Misaki.HighPerformance.Image
p[2] = palette[n + 2]; p[2] = palette[n + 2];
p += 3; p += 3;
} }
}
else else
{
for (i = 0; i < pixel_count; ++i) for (i = 0; i < pixel_count; ++i)
{ {
var n = orig[i] * 4; var n = orig[i] * 4;
@@ -407,6 +507,7 @@ namespace Misaki.HighPerformance.Image
p[3] = palette[n + 3]; p[3] = palette[n + 3];
p += 4; p += 4;
} }
}
CRuntime.free(a._out_); CRuntime.free(a._out_);
a._out_ = temp_out; a._out_ = temp_out;
@@ -423,9 +524,7 @@ namespace Misaki.HighPerformance.Image
{ {
for (i = 0; i < pixel_count; ++i) for (i = 0; i < pixel_count; ++i)
{ {
var t = p[0]; (p[2], p[0]) = (p[0], p[2]);
p[0] = p[2];
p[2] = t;
p += 3; p += 3;
} }
} }
@@ -434,6 +533,7 @@ namespace Misaki.HighPerformance.Image
if ((stbi__unpremultiply_on_load_set != 0 if ((stbi__unpremultiply_on_load_set != 0
? stbi__unpremultiply_on_load_local ? stbi__unpremultiply_on_load_local
: stbi__unpremultiply_on_load_global) != 0) : stbi__unpremultiply_on_load_global) != 0)
{
for (i = 0; i < pixel_count; ++i) for (i = 0; i < pixel_count; ++i)
{ {
var a = p[3]; var a = p[3];
@@ -453,16 +553,17 @@ namespace Misaki.HighPerformance.Image
p += 4; p += 4;
} }
}
else else
{
for (i = 0; i < pixel_count; ++i) for (i = 0; i < pixel_count; ++i)
{ {
var t = p[0]; (p[2], p[0]) = (p[0], p[2]);
p[0] = p[2];
p[2] = t;
p += 4; p += 4;
} }
} }
} }
}
public static int stbi__parse_png_file(stbi__png z, int scan, int req_comp) public static int stbi__parse_png_file(stbi__png z, int scan, int req_comp)
{ {
@@ -484,10 +585,17 @@ namespace Misaki.HighPerformance.Image
z.expanded = null; z.expanded = null;
z.idata = null; z.idata = null;
z._out_ = null; z._out_ = null;
if (stbi__check_png_header(s) == 0) if (stbi__check_png_header(s) == 0)
{
return 0; return 0;
}
if (scan == STBI__SCAN_type) if (scan == STBI__SCAN_type)
{
return 1; return 1;
}
for (; ; ) for (; ; )
{ {
var c = stbi__get_chunk_header(s); var c = stbi__get_chunk_header(s);
@@ -502,51 +610,93 @@ namespace Misaki.HighPerformance.Image
var comp = 0; var comp = 0;
var filter = 0; var filter = 0;
if (first == 0) if (first == 0)
{
return stbi__err("multiple IHDR"); return stbi__err("multiple IHDR");
}
first = 0; first = 0;
if (c.length != 13) if (c.length != 13)
{
return stbi__err("bad IHDR len"); return stbi__err("bad IHDR len");
}
s.img_x = stbi__get32be(s); s.img_x = stbi__get32be(s);
s.img_y = stbi__get32be(s); s.img_y = stbi__get32be(s);
if (s.img_y > 1 << 24) if (s.img_y > 1 << 24)
{
return stbi__err("too large"); return stbi__err("too large");
}
if (s.img_x > 1 << 24) if (s.img_x > 1 << 24)
{
return stbi__err("too large"); return stbi__err("too large");
}
z.depth = stbi__get8(s); z.depth = stbi__get8(s);
if (z.depth != 1 && z.depth != 2 && z.depth != 4 && z.depth != 8 && z.depth != 16) if (z.depth != 1 && z.depth != 2 && z.depth != 4 && z.depth != 8 && z.depth != 16)
{
return stbi__err("1/2/4/8/16-bit only"); return stbi__err("1/2/4/8/16-bit only");
}
color = stbi__get8(s); color = stbi__get8(s);
if (color > 6) if (color > 6)
{
return stbi__err("bad ctype"); return stbi__err("bad ctype");
}
if (color == 3 && z.depth == 16) if (color == 3 && z.depth == 16)
{
return stbi__err("bad ctype"); return stbi__err("bad ctype");
}
if (color == 3) if (color == 3)
{
pal_img_n = 3; pal_img_n = 3;
}
else if ((color & 1) != 0) else if ((color & 1) != 0)
{
return stbi__err("bad ctype"); return stbi__err("bad ctype");
}
comp = stbi__get8(s); comp = stbi__get8(s);
if (comp != 0) if (comp != 0)
{
return stbi__err("bad comp method"); return stbi__err("bad comp method");
}
filter = stbi__get8(s); filter = stbi__get8(s);
if (filter != 0) if (filter != 0)
{
return stbi__err("bad filter method"); return stbi__err("bad filter method");
}
interlace = stbi__get8(s); interlace = stbi__get8(s);
if (interlace > 1) if (interlace > 1)
{
return stbi__err("bad interlace method"); return stbi__err("bad interlace method");
}
if (s.img_x == 0 || s.img_y == 0) if (s.img_x == 0 || s.img_y == 0)
{
return stbi__err("0-pixel image"); return stbi__err("0-pixel image");
}
if (pal_img_n == 0) if (pal_img_n == 0)
{ {
s.img_n = ((color & 2) != 0 ? 3 : 1) + ((color & 4) != 0 ? 1 : 0); s.img_n = ((color & 2) != 0 ? 3 : 1) + ((color & 4) != 0 ? 1 : 0);
if ((1 << 30) / s.img_x / s.img_n < s.img_y) if ((1 << 30) / s.img_x / s.img_n < s.img_y)
{
return stbi__err("too large"); return stbi__err("too large");
} }
}
else else
{ {
s.img_n = 1; s.img_n = 1;
if ((1 << 30) / s.img_x / 4 < s.img_y) if ((1 << 30) / s.img_x / 4 < s.img_y)
{
return stbi__err("too large"); return stbi__err("too large");
} }
}
break; break;
} }
@@ -554,12 +704,21 @@ namespace Misaki.HighPerformance.Image
case ((uint)80 << 24) + ((uint)76 << 16) + ((uint)84 << 8) + 69: case ((uint)80 << 24) + ((uint)76 << 16) + ((uint)84 << 8) + 69:
{ {
if (first != 0) if (first != 0)
{
return stbi__err("first not IHDR"); return stbi__err("first not IHDR");
}
if (c.length > 256 * 3) if (c.length > 256 * 3)
{
return stbi__err("invalid PLTE"); return stbi__err("invalid PLTE");
}
pal_len = c.length / 3; pal_len = c.length / 3;
if (pal_len * 3 != c.length) if (pal_len * 3 != c.length)
{
return stbi__err("invalid PLTE"); return stbi__err("invalid PLTE");
}
for (i = 0; i < pal_len; ++i) for (i = 0; i < pal_len; ++i)
{ {
palette[i * 4 + 0] = stbi__get8(s); palette[i * 4 + 0] = stbi__get8(s);
@@ -574,9 +733,15 @@ namespace Misaki.HighPerformance.Image
case ((uint)116 << 24) + ((uint)82 << 16) + ((uint)78 << 8) + 83: case ((uint)116 << 24) + ((uint)82 << 16) + ((uint)78 << 8) + 83:
{ {
if (first != 0) if (first != 0)
{
return stbi__err("first not IHDR"); return stbi__err("first not IHDR");
}
if (z.idata != null) if (z.idata != null)
{
return stbi__err("tRNS after IDAT"); return stbi__err("tRNS after IDAT");
}
if (pal_img_n != 0) if (pal_img_n != 0)
{ {
if (scan == STBI__SCAN_header) if (scan == STBI__SCAN_header)
@@ -586,19 +751,33 @@ namespace Misaki.HighPerformance.Image
} }
if (pal_len == 0) if (pal_len == 0)
{
return stbi__err("tRNS before PLTE"); return stbi__err("tRNS before PLTE");
}
if (c.length > pal_len) if (c.length > pal_len)
{
return stbi__err("bad tRNS len"); return stbi__err("bad tRNS len");
}
pal_img_n = 4; pal_img_n = 4;
for (i = 0; i < c.length; ++i) for (i = 0; i < c.length; ++i)
{
palette[i * 4 + 3] = stbi__get8(s); palette[i * 4 + 3] = stbi__get8(s);
} }
}
else else
{ {
if ((s.img_n & 1) == 0) if ((s.img_n & 1) == 0)
{
return stbi__err("tRNS with alpha"); return stbi__err("tRNS with alpha");
}
if (c.length != (uint)s.img_n * 2) if (c.length != (uint)s.img_n * 2)
{
return stbi__err("bad tRNS len"); return stbi__err("bad tRNS len");
}
has_trans = 1; has_trans = 1;
if (scan == STBI__SCAN_header) if (scan == STBI__SCAN_header)
{ {
@@ -607,12 +786,20 @@ namespace Misaki.HighPerformance.Image
} }
if (z.depth == 16) if (z.depth == 16)
{
for (k = 0; k < s.img_n && k < 3; ++k) for (k = 0; k < s.img_n && k < 3; ++k)
{
tc16[k] = (ushort)stbi__get16be(s); tc16[k] = (ushort)stbi__get16be(s);
}
}
else else
{
for (k = 0; k < s.img_n && k < 3; ++k) for (k = 0; k < s.img_n && k < 3; ++k)
{
tc[k] = (byte)((byte)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z.depth]); tc[k] = (byte)((byte)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z.depth]);
} }
}
}
break; break;
} }
@@ -620,37 +807,63 @@ namespace Misaki.HighPerformance.Image
case ((uint)73 << 24) + ((uint)68 << 16) + ((uint)65 << 8) + 84: case ((uint)73 << 24) + ((uint)68 << 16) + ((uint)65 << 8) + 84:
{ {
if (first != 0) if (first != 0)
{
return stbi__err("first not IHDR"); return stbi__err("first not IHDR");
}
if (pal_img_n != 0 && pal_len == 0) if (pal_img_n != 0 && pal_len == 0)
{
return stbi__err("no PLTE"); return stbi__err("no PLTE");
}
if (scan == STBI__SCAN_header) if (scan == STBI__SCAN_header)
{ {
if (pal_img_n != 0) if (pal_img_n != 0)
{
s.img_n = pal_img_n; s.img_n = pal_img_n;
}
return 1; return 1;
} }
if (c.length > 1u << 30) if (c.length > 1u << 30)
{
return stbi__err("IDAT size limit"); return stbi__err("IDAT size limit");
}
if ((int)(ioff + c.length) < (int)ioff) if ((int)(ioff + c.length) < (int)ioff)
{
return 0; return 0;
}
if (ioff + c.length > idata_limit) if (ioff + c.length > idata_limit)
{ {
var idata_limit_old = idata_limit; var idata_limit_old = idata_limit;
byte* p; byte* p;
if (idata_limit == 0) if (idata_limit == 0)
{
idata_limit = c.length > 4096 ? c.length : 4096; idata_limit = c.length > 4096 ? c.length : 4096;
}
while (ioff + c.length > idata_limit) while (ioff + c.length > idata_limit)
{
idata_limit *= 2; idata_limit *= 2;
}
p = (byte*)CRuntime.realloc(z.idata, (ulong)idata_limit); p = (byte*)CRuntime.realloc(z.idata, (ulong)idata_limit);
if (p == null) if (p == null)
{
return stbi__err("outofmem"); return stbi__err("outofmem");
}
z.idata = p; z.idata = p;
} }
if (stbi__getn(s, z.idata + ioff, (int)c.length) == 0) if (stbi__getn(s, z.idata + ioff, (int)c.length) == 0)
{
return stbi__err("outofdata"); return stbi__err("outofdata");
}
ioff += c.length; ioff += c.length;
break; break;
} }
@@ -660,53 +873,85 @@ namespace Misaki.HighPerformance.Image
uint raw_len = 0; uint raw_len = 0;
uint bpl = 0; uint bpl = 0;
if (first != 0) if (first != 0)
{
return stbi__err("first not IHDR"); return stbi__err("first not IHDR");
}
if (scan != STBI__SCAN_load) if (scan != STBI__SCAN_load)
{
return 1; return 1;
}
if (z.idata == null) if (z.idata == null)
{
return stbi__err("no IDAT"); return stbi__err("no IDAT");
}
bpl = (uint)((s.img_x * z.depth + 7) / 8); bpl = (uint)((s.img_x * z.depth + 7) / 8);
raw_len = (uint)(bpl * s.img_y * s.img_n + s.img_y); raw_len = (uint)(bpl * s.img_y * s.img_n + s.img_y);
z.expanded = (byte*)stbi_zlib_decode_malloc_guesssize_headerflag((sbyte*)z.idata, (int)ioff, z.expanded = (byte*)stbi_zlib_decode_malloc_guesssize_headerflag((sbyte*)z.idata, (int)ioff,
(int)raw_len, (int*)&raw_len, is_iphone == 0 ? 1 : 0); (int)raw_len, (int*)&raw_len, is_iphone == 0 ? 1 : 0);
if (z.expanded == null) if (z.expanded == null)
{
return 0; return 0;
}
CRuntime.free(z.idata); CRuntime.free(z.idata);
z.idata = null; z.idata = null;
if ((req_comp == s.img_n + 1 && req_comp != 3 && pal_img_n == 0) || has_trans != 0) if ((req_comp == s.img_n + 1 && req_comp != 3 && pal_img_n == 0) || has_trans != 0)
{
s.img_out_n = s.img_n + 1; s.img_out_n = s.img_n + 1;
}
else else
{
s.img_out_n = s.img_n; s.img_out_n = s.img_n;
}
if (stbi__create_png_image(z, z.expanded, raw_len, s.img_out_n, z.depth, color, interlace) == 0) if (stbi__create_png_image(z, z.expanded, raw_len, s.img_out_n, z.depth, color, interlace) == 0)
{
return 0; return 0;
}
if (has_trans != 0) if (has_trans != 0)
{ {
if (z.depth == 16) if (z.depth == 16)
{ {
if (stbi__compute_transparency16(z, tc16, s.img_out_n) == 0) if (stbi__compute_transparency16(z, tc16, s.img_out_n) == 0)
{
return 0; return 0;
} }
}
else else
{ {
if (stbi__compute_transparency(z, tc, s.img_out_n) == 0) if (stbi__compute_transparency(z, tc, s.img_out_n) == 0)
{
return 0; return 0;
} }
} }
}
if (is_iphone != 0 && if (is_iphone != 0 &&
(stbi__de_iphone_flag_set != 0 (stbi__de_iphone_flag_set != 0
? stbi__de_iphone_flag_local ? stbi__de_iphone_flag_local
: stbi__de_iphone_flag_global) != 0 && s.img_out_n > 2) : stbi__de_iphone_flag_global) != 0 && s.img_out_n > 2)
{
stbi__de_iphone(z); stbi__de_iphone(z);
}
if (pal_img_n != 0) if (pal_img_n != 0)
{ {
s.img_n = pal_img_n; s.img_n = pal_img_n;
s.img_out_n = pal_img_n; s.img_out_n = pal_img_n;
if (req_comp >= 3) if (req_comp >= 3)
{
s.img_out_n = req_comp; s.img_out_n = req_comp;
}
if (stbi__expand_png_palette(z, palette, (int)pal_len, s.img_out_n) == 0) if (stbi__expand_png_palette(z, palette, (int)pal_len, s.img_out_n) == 0)
{
return 0; return 0;
} }
}
else if (has_trans != 0) else if (has_trans != 0)
{ {
++s.img_n; ++s.img_n;
@@ -720,7 +965,10 @@ namespace Misaki.HighPerformance.Image
default: default:
if (first != 0) if (first != 0)
{
return stbi__err("first not IHDR"); return stbi__err("first not IHDR");
}
if ((c.type & (1 << 29)) == 0) if ((c.type & (1 << 29)) == 0)
{ {
stbi__parse_png_file_invalid_chunk[0] = (char)((c.type >> 24) & 255); stbi__parse_png_file_invalid_chunk[0] = (char)((c.type >> 24) & 255);
@@ -742,33 +990,52 @@ namespace Misaki.HighPerformance.Image
{ {
void* result = null; void* result = null;
if (req_comp < 0 || req_comp > 4) if (req_comp < 0 || req_comp > 4)
{
return (byte*)(ulong)(stbi__err("bad req_comp") != 0 ? 0 : 0); return (byte*)(ulong)(stbi__err("bad req_comp") != 0 ? 0 : 0);
}
if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp) != 0) if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp) != 0)
{ {
if (p.depth <= 8) if (p.depth <= 8)
{
ri->bits_per_channel = 8; ri->bits_per_channel = 8;
}
else if (p.depth == 16) else if (p.depth == 16)
{
ri->bits_per_channel = 16; ri->bits_per_channel = 16;
}
else else
{
return (byte*)(ulong)(stbi__err("bad bits_per_channel") != 0 ? 0 : 0); return (byte*)(ulong)(stbi__err("bad bits_per_channel") != 0 ? 0 : 0);
}
result = p._out_; result = p._out_;
p._out_ = null; p._out_ = null;
if (req_comp != 0 && req_comp != p.s.img_out_n) if (req_comp != 0 && req_comp != p.s.img_out_n)
{ {
if (ri->bits_per_channel == 8) if (ri->bits_per_channel == 8)
{
result = stbi__convert_format((byte*)result, p.s.img_out_n, req_comp, p.s.img_x, p.s.img_y); result = stbi__convert_format((byte*)result, p.s.img_out_n, req_comp, p.s.img_x, p.s.img_y);
}
else else
{
result = stbi__convert_format16((ushort*)result, p.s.img_out_n, req_comp, p.s.img_x, p.s.img_y); result = stbi__convert_format16((ushort*)result, p.s.img_out_n, req_comp, p.s.img_x, p.s.img_y);
}
p.s.img_out_n = req_comp; p.s.img_out_n = req_comp;
if (result == null) if (result == null)
{
return result; return result;
} }
}
*x = (int)p.s.img_x; *x = (int)p.s.img_x;
*y = (int)p.s.img_y; *y = (int)p.s.img_y;
if (n != null) if (n != null)
{
*n = p.s.img_n; *n = p.s.img_n;
} }
}
CRuntime.free(p._out_); CRuntime.free(p._out_);
p._out_ = null; p._out_ = null;
@@ -788,11 +1055,20 @@ namespace Misaki.HighPerformance.Image
} }
if (x != null) if (x != null)
{
*x = (int)p.s.img_x; *x = (int)p.s.img_x;
}
if (y != null) if (y != null)
{
*y = (int)p.s.img_y; *y = (int)p.s.img_y;
}
if (comp != null) if (comp != null)
{
*comp = p.s.img_n; *comp = p.s.img_n;
}
return 1; return 1;
} }

View File

@@ -28,32 +28,59 @@ namespace Misaki.HighPerformance.Image
var h = 0; var h = 0;
byte* _out_; byte* _out_;
if (stbi__get32be(s) != 0x38425053) if (stbi__get32be(s) != 0x38425053)
{
return (byte*)(ulong)(stbi__err("not PSD") != 0 ? 0 : 0); return (byte*)(ulong)(stbi__err("not PSD") != 0 ? 0 : 0);
}
if (stbi__get16be(s) != 1) if (stbi__get16be(s) != 1)
{
return (byte*)(ulong)(stbi__err("wrong version") != 0 ? 0 : 0); return (byte*)(ulong)(stbi__err("wrong version") != 0 ? 0 : 0);
}
stbi__skip(s, 6); stbi__skip(s, 6);
channelCount = stbi__get16be(s); channelCount = stbi__get16be(s);
if (channelCount < 0 || channelCount > 16) if (channelCount < 0 || channelCount > 16)
{
return (byte*)(ulong)(stbi__err("wrong channel count") != 0 ? 0 : 0); return (byte*)(ulong)(stbi__err("wrong channel count") != 0 ? 0 : 0);
}
h = (int)stbi__get32be(s); h = (int)stbi__get32be(s);
w = (int)stbi__get32be(s); w = (int)stbi__get32be(s);
if (h > 1 << 24) if (h > 1 << 24)
{
return (byte*)(ulong)(stbi__err("too large") != 0 ? 0 : 0); return (byte*)(ulong)(stbi__err("too large") != 0 ? 0 : 0);
}
if (w > 1 << 24) if (w > 1 << 24)
{
return (byte*)(ulong)(stbi__err("too large") != 0 ? 0 : 0); return (byte*)(ulong)(stbi__err("too large") != 0 ? 0 : 0);
}
bitdepth = stbi__get16be(s); bitdepth = stbi__get16be(s);
if (bitdepth != 8 && bitdepth != 16) if (bitdepth != 8 && bitdepth != 16)
{
return (byte*)(ulong)(stbi__err("unsupported bit depth") != 0 ? 0 : 0); return (byte*)(ulong)(stbi__err("unsupported bit depth") != 0 ? 0 : 0);
}
if (stbi__get16be(s) != 3) if (stbi__get16be(s) != 3)
{
return (byte*)(ulong)(stbi__err("wrong color format") != 0 ? 0 : 0); return (byte*)(ulong)(stbi__err("wrong color format") != 0 ? 0 : 0);
}
stbi__skip(s, (int)stbi__get32be(s)); stbi__skip(s, (int)stbi__get32be(s));
stbi__skip(s, (int)stbi__get32be(s)); stbi__skip(s, (int)stbi__get32be(s));
stbi__skip(s, (int)stbi__get32be(s)); stbi__skip(s, (int)stbi__get32be(s));
compression = stbi__get16be(s); compression = stbi__get16be(s);
if (compression > 1) if (compression > 1)
{
return (byte*)(ulong)(stbi__err("bad compression") != 0 ? 0 : 0); return (byte*)(ulong)(stbi__err("bad compression") != 0 ? 0 : 0);
}
if (stbi__mad3sizes_valid(4, w, h, 0) == 0) if (stbi__mad3sizes_valid(4, w, h, 0) == 0)
{
return (byte*)(ulong)(stbi__err("too large") != 0 ? 0 : 0); return (byte*)(ulong)(stbi__err("too large") != 0 ? 0 : 0);
}
if (compression == 0 && bitdepth == 16 && bpc == 16) if (compression == 0 && bitdepth == 16 && bpc == 16)
{ {
_out_ = (byte*)stbi__malloc_mad3(8, w, h, 0); _out_ = (byte*)stbi__malloc_mad3(8, w, h, 0);
@@ -65,7 +92,10 @@ namespace Misaki.HighPerformance.Image
} }
if (_out_ == null) if (_out_ == null)
{
return (byte*)(ulong)(stbi__err("outofmem") != 0 ? 0 : 0); return (byte*)(ulong)(stbi__err("outofmem") != 0 ? 0 : 0);
}
pixelCount = w * h; pixelCount = w * h;
if (compression != 0) if (compression != 0)
{ {
@@ -77,8 +107,10 @@ namespace Misaki.HighPerformance.Image
if (channel >= channelCount) if (channel >= channelCount)
{ {
for (i = 0; i < pixelCount; i++, p += 4) for (i = 0; i < pixelCount; i++, p += 4)
{
*p = (byte)(channel == 3 ? 255 : 0); *p = (byte)(channel == 3 ? 255 : 0);
} }
}
else else
{ {
if (stbi__psd_decode_rle(s, p, pixelCount) == 0) if (stbi__psd_decode_rle(s, p, pixelCount) == 0)
@@ -92,6 +124,7 @@ namespace Misaki.HighPerformance.Image
else else
{ {
for (channel = 0; channel < 4; channel++) for (channel = 0; channel < 4; channel++)
{
if (channel >= channelCount) if (channel >= channelCount)
{ {
if (bitdepth == 16 && bpc == 16) if (bitdepth == 16 && bpc == 16)
@@ -99,40 +132,56 @@ namespace Misaki.HighPerformance.Image
var q = (ushort*)_out_ + channel; var q = (ushort*)_out_ + channel;
var val = (ushort)(channel == 3 ? 65535 : 0); var val = (ushort)(channel == 3 ? 65535 : 0);
for (i = 0; i < pixelCount; i++, q += 4) for (i = 0; i < pixelCount; i++, q += 4)
{
*q = val; *q = val;
} }
}
else else
{ {
var p = _out_ + channel; var p = _out_ + channel;
var val = (byte)(channel == 3 ? 255 : 0); var val = (byte)(channel == 3 ? 255 : 0);
for (i = 0; i < pixelCount; i++, p += 4) for (i = 0; i < pixelCount; i++, p += 4)
{
*p = val; *p = val;
} }
} }
}
else else
{ {
if (ri->bits_per_channel == 16) if (ri->bits_per_channel == 16)
{ {
var q = (ushort*)_out_ + channel; var q = (ushort*)_out_ + channel;
for (i = 0; i < pixelCount; i++, q += 4) for (i = 0; i < pixelCount; i++, q += 4)
{
*q = (ushort)stbi__get16be(s); *q = (ushort)stbi__get16be(s);
} }
}
else else
{ {
var p = _out_ + channel; var p = _out_ + channel;
if (bitdepth == 16) if (bitdepth == 16)
{
for (i = 0; i < pixelCount; i++, p += 4) for (i = 0; i < pixelCount; i++, p += 4)
{
*p = (byte)(stbi__get16be(s) >> 8); *p = (byte)(stbi__get16be(s) >> 8);
}
}
else else
{
for (i = 0; i < pixelCount; i++, p += 4) for (i = 0; i < pixelCount; i++, p += 4)
{
*p = stbi__get8(s); *p = stbi__get8(s);
} }
} }
} }
}
}
}
if (channelCount >= 4) if (channelCount >= 4)
{ {
if (ri->bits_per_channel == 16) if (ri->bits_per_channel == 16)
{
for (i = 0; i < w * h; ++i) for (i = 0; i < w * h; ++i)
{ {
var pixel = (ushort*)_out_ + 4 * i; var pixel = (ushort*)_out_ + 4 * i;
@@ -146,7 +195,9 @@ namespace Misaki.HighPerformance.Image
pixel[2] = (ushort)(pixel[2] * ra + inv_a); pixel[2] = (ushort)(pixel[2] * ra + inv_a);
} }
} }
}
else else
{
for (i = 0; i < w * h; ++i) for (i = 0; i < w * h; ++i)
{ {
var pixel = _out_ + 4 * i; var pixel = _out_ + 4 * i;
@@ -161,19 +212,30 @@ namespace Misaki.HighPerformance.Image
} }
} }
} }
}
if (req_comp != 0 && req_comp != 4) if (req_comp != 0 && req_comp != 4)
{ {
if (ri->bits_per_channel == 16) if (ri->bits_per_channel == 16)
{
_out_ = (byte*)stbi__convert_format16((ushort*)_out_, 4, req_comp, (uint)w, (uint)h); _out_ = (byte*)stbi__convert_format16((ushort*)_out_, 4, req_comp, (uint)w, (uint)h);
}
else else
{
_out_ = stbi__convert_format(_out_, 4, req_comp, (uint)w, (uint)h); _out_ = stbi__convert_format(_out_, 4, req_comp, (uint)w, (uint)h);
}
if (_out_ == null) if (_out_ == null)
{
return _out_; return _out_;
} }
}
if (comp != null) if (comp != null)
{
*comp = 4; *comp = 4;
}
*y = h; *y = h;
*x = w; *x = w;
return _out_; return _out_;
@@ -185,11 +247,20 @@ namespace Misaki.HighPerformance.Image
var dummy = 0; var dummy = 0;
var depth = 0; var depth = 0;
if (x == null) if (x == null)
{
x = &dummy; x = &dummy;
}
if (y == null) if (y == null)
{
y = &dummy; y = &dummy;
}
if (comp == null) if (comp == null)
{
comp = &dummy; comp = &dummy;
}
if (stbi__get32be(s) != 0x38425053) if (stbi__get32be(s) != 0x38425053)
{ {
stbi__rewind(s); stbi__rewind(s);
@@ -279,7 +350,10 @@ namespace Misaki.HighPerformance.Image
{ {
len++; len++;
if (len > nleft) if (len > nleft)
{
return 0; return 0;
}
count += len; count += len;
while (len != 0) while (len != 0)
{ {
@@ -293,7 +367,10 @@ namespace Misaki.HighPerformance.Image
byte val = 0; byte val = 0;
len = 257 - len; len = 257 - len;
if (len > nleft) if (len > nleft)
{
return 0; return 0;
}
val = stbi__get8(s); val = stbi__get8(s);
count += len; count += len;
while (len != 0) while (len != 0)

View File

@@ -16,34 +16,58 @@ namespace Misaki.HighPerformance.Image
stbi__get8(s); stbi__get8(s);
tga_color_type = stbi__get8(s); tga_color_type = stbi__get8(s);
if (tga_color_type > 1) if (tga_color_type > 1)
{
goto errorEnd; goto errorEnd;
}
sz = stbi__get8(s); sz = stbi__get8(s);
if (tga_color_type == 1) if (tga_color_type == 1)
{ {
if (sz != 1 && sz != 9) if (sz != 1 && sz != 9)
{
goto errorEnd; goto errorEnd;
}
stbi__skip(s, 4); stbi__skip(s, 4);
sz = stbi__get8(s); sz = stbi__get8(s);
if (sz != 8 && sz != 15 && sz != 16 && sz != 24 && sz != 32) if (sz != 8 && sz != 15 && sz != 16 && sz != 24 && sz != 32)
{
goto errorEnd; goto errorEnd;
}
stbi__skip(s, 4); stbi__skip(s, 4);
} }
else else
{ {
if (sz != 2 && sz != 3 && sz != 10 && sz != 11) if (sz != 2 && sz != 3 && sz != 10 && sz != 11)
{
goto errorEnd; goto errorEnd;
}
stbi__skip(s, 9); stbi__skip(s, 9);
} }
if (stbi__get16le(s) < 1) if (stbi__get16le(s) < 1)
{
goto errorEnd; goto errorEnd;
}
if (stbi__get16le(s) < 1) if (stbi__get16le(s) < 1)
{
goto errorEnd; goto errorEnd;
}
sz = stbi__get8(s); sz = stbi__get8(s);
if (tga_color_type == 1 && sz != 8 && sz != 16) if (tga_color_type == 1 && sz != 8 && sz != 16)
{
goto errorEnd; goto errorEnd;
}
if (sz != 8 && sz != 15 && sz != 16 && sz != 24 && sz != 32) if (sz != 8 && sz != 15 && sz != 16 && sz != 24 && sz != 32)
{
goto errorEnd; goto errorEnd;
}
res = 1; res = 1;
errorEnd: errorEnd:
; ;
@@ -82,9 +106,15 @@ namespace Misaki.HighPerformance.Image
var RLE_repeating = 0; var RLE_repeating = 0;
var read_next_pixel = 1; var read_next_pixel = 1;
if (tga_height > 1 << 24) if (tga_height > 1 << 24)
{
return (byte*)(ulong)(stbi__err("too large") != 0 ? 0 : 0); return (byte*)(ulong)(stbi__err("too large") != 0 ? 0 : 0);
}
if (tga_width > 1 << 24) if (tga_width > 1 << 24)
{
return (byte*)(ulong)(stbi__err("too large") != 0 ? 0 : 0); return (byte*)(ulong)(stbi__err("too large") != 0 ? 0 : 0);
}
if (tga_image_type >= 8) if (tga_image_type >= 8)
{ {
tga_image_type -= 8; tga_image_type -= 8;
@@ -93,20 +123,37 @@ namespace Misaki.HighPerformance.Image
tga_inverted = 1 - ((tga_inverted >> 5) & 1); tga_inverted = 1 - ((tga_inverted >> 5) & 1);
if (tga_indexed != 0) if (tga_indexed != 0)
{
tga_comp = stbi__tga_get_comp(tga_palette_bits, 0, &tga_rgb16); tga_comp = stbi__tga_get_comp(tga_palette_bits, 0, &tga_rgb16);
}
else else
{
tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, tga_image_type == 3 ? 1 : 0, &tga_rgb16); tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, tga_image_type == 3 ? 1 : 0, &tga_rgb16);
}
if (tga_comp == 0) if (tga_comp == 0)
{
return (byte*)(ulong)(stbi__err("bad format") != 0 ? 0 : 0); return (byte*)(ulong)(stbi__err("bad format") != 0 ? 0 : 0);
}
*x = tga_width; *x = tga_width;
*y = tga_height; *y = tga_height;
if (comp != null) if (comp != null)
{
*comp = tga_comp; *comp = tga_comp;
}
if (stbi__mad3sizes_valid(tga_width, tga_height, tga_comp, 0) == 0) if (stbi__mad3sizes_valid(tga_width, tga_height, tga_comp, 0) == 0)
{
return (byte*)(ulong)(stbi__err("too large") != 0 ? 0 : 0); return (byte*)(ulong)(stbi__err("too large") != 0 ? 0 : 0);
}
tga_data = (byte*)stbi__malloc_mad3(tga_width, tga_height, tga_comp, 0); tga_data = (byte*)stbi__malloc_mad3(tga_width, tga_height, tga_comp, 0);
if (tga_data == null) if (tga_data == null)
{
return (byte*)(ulong)(stbi__err("outofmem") != 0 ? 0 : 0); return (byte*)(ulong)(stbi__err("outofmem") != 0 ? 0 : 0);
}
stbi__skip(s, tga_offset); stbi__skip(s, tga_offset);
if (tga_indexed == 0 && tga_is_RLE == 0 && tga_rgb16 == 0) if (tga_indexed == 0 && tga_is_RLE == 0 && tga_rgb16 == 0)
{ {
@@ -179,11 +226,16 @@ namespace Misaki.HighPerformance.Image
{ {
var pal_idx = tga_bits_per_pixel == 8 ? stbi__get8(s) : stbi__get16le(s); var pal_idx = tga_bits_per_pixel == 8 ? stbi__get8(s) : stbi__get16le(s);
if (pal_idx >= tga_palette_len) if (pal_idx >= tga_palette_len)
{
pal_idx = 0; pal_idx = 0;
}
pal_idx *= tga_comp; pal_idx *= tga_comp;
for (j = 0; j < tga_comp; ++j) for (j = 0; j < tga_comp; ++j)
{
raw_data[j] = tga_palette[pal_idx + j]; raw_data[j] = tga_palette[pal_idx + j];
} }
}
else if (tga_rgb16 != 0) else if (tga_rgb16 != 0)
{ {
stbi__tga_read_rgb16(s, raw_data); stbi__tga_read_rgb16(s, raw_data);
@@ -191,18 +243,24 @@ namespace Misaki.HighPerformance.Image
else else
{ {
for (j = 0; j < tga_comp; ++j) for (j = 0; j < tga_comp; ++j)
{
raw_data[j] = stbi__get8(s); raw_data[j] = stbi__get8(s);
} }
}
read_next_pixel = 0; read_next_pixel = 0;
} }
for (j = 0; j < tga_comp; ++j) for (j = 0; j < tga_comp; ++j)
{
tga_data[i * tga_comp + j] = raw_data[j]; tga_data[i * tga_comp + j] = raw_data[j];
}
--RLE_count; --RLE_count;
} }
if (tga_inverted != 0) if (tga_inverted != 0)
{
for (j = 0; j * 2 < tga_height; ++j) for (j = 0; j * 2 < tga_height; ++j)
{ {
var index1 = j * tga_width * tga_comp; var index1 = j * tga_width * tga_comp;
@@ -216,10 +274,13 @@ namespace Misaki.HighPerformance.Image
++index2; ++index2;
} }
} }
}
if (tga_palette != null) if (tga_palette != null)
{
CRuntime.free(tga_palette); CRuntime.free(tga_palette);
} }
}
if (tga_comp >= 3 && tga_rgb16 == 0) if (tga_comp >= 3 && tga_rgb16 == 0)
{ {
@@ -234,7 +295,10 @@ namespace Misaki.HighPerformance.Image
} }
if (req_comp != 0 && req_comp != tga_comp) if (req_comp != 0 && req_comp != tga_comp)
{
tga_data = stbi__convert_format(tga_data, tga_comp, req_comp, (uint)tga_width, (uint)tga_height); tga_data = stbi__convert_format(tga_data, tga_comp, req_comp, (uint)tga_width, (uint)tga_height);
}
tga_palette_start = tga_palette_len = tga_palette_bits = tga_x_origin = tga_y_origin = 0; tga_palette_start = tga_palette_len = tga_palette_bits = tga_x_origin = tga_y_origin = 0;
return tga_data; return tga_data;
} }
@@ -328,18 +392,30 @@ namespace Misaki.HighPerformance.Image
} }
if (x != null) if (x != null)
{
*x = tga_w; *x = tga_w;
}
if (y != null) if (y != null)
{
*y = tga_h; *y = tga_h;
}
if (comp != null) if (comp != null)
{
*comp = tga_comp; *comp = tga_comp;
}
return 1; return 1;
} }
public static int stbi__tga_get_comp(int bits_per_pixel, int is_grey, int* is_rgb16) public static int stbi__tga_get_comp(int bits_per_pixel, int is_grey, int* is_rgb16)
{ {
if (is_rgb16 != null) if (is_rgb16 != null)
{
*is_rgb16 = 0; *is_rgb16 = 0;
}
switch (bits_per_pixel) switch (bits_per_pixel)
{ {
case 8: case 8:
@@ -347,9 +423,15 @@ namespace Misaki.HighPerformance.Image
case 16: case 16:
case 15: case 15:
if (bits_per_pixel == 16 && is_grey != 0) if (bits_per_pixel == 16 && is_grey != 0)
{
return STBI_grey_alpha; return STBI_grey_alpha;
}
if (is_rgb16 != null) if (is_rgb16 != null)
{
*is_rgb16 = 1; *is_rgb16 = 1;
}
return STBI_rgb; return STBI_rgb;
case 24: case 24:
case 32: case 32:

View File

@@ -45,13 +45,19 @@ namespace Misaki.HighPerformance.Image
var a = new stbi__zbuf(); var a = new stbi__zbuf();
var p = (sbyte*)stbi__malloc((ulong)initial_size); var p = (sbyte*)stbi__malloc((ulong)initial_size);
if (p == null) if (p == null)
{
return null; return null;
}
a.zbuffer = (byte*)buffer; a.zbuffer = (byte*)buffer;
a.zbuffer_end = (byte*)buffer + len; a.zbuffer_end = (byte*)buffer + len;
if (stbi__do_zlib(&a, p, initial_size, 1, 1) != 0) if (stbi__do_zlib(&a, p, initial_size, 1, 1) != 0)
{ {
if (outlen != null) if (outlen != null)
{
*outlen = (int)(a.zout - a.zout_start); *outlen = (int)(a.zout - a.zout_start);
}
return a.zout_start; return a.zout_start;
} }
@@ -65,13 +71,19 @@ namespace Misaki.HighPerformance.Image
var a = new stbi__zbuf(); var a = new stbi__zbuf();
var p = (sbyte*)stbi__malloc((ulong)initial_size); var p = (sbyte*)stbi__malloc((ulong)initial_size);
if (p == null) if (p == null)
{
return null; return null;
}
a.zbuffer = (byte*)buffer; a.zbuffer = (byte*)buffer;
a.zbuffer_end = (byte*)buffer + len; a.zbuffer_end = (byte*)buffer + len;
if (stbi__do_zlib(&a, p, initial_size, 1, parse_header) != 0) if (stbi__do_zlib(&a, p, initial_size, 1, parse_header) != 0)
{ {
if (outlen != null) if (outlen != null)
{
*outlen = (int)(a.zout - a.zout_start); *outlen = (int)(a.zout - a.zout_start);
}
return a.zout_start; return a.zout_start;
} }
@@ -90,7 +102,10 @@ namespace Misaki.HighPerformance.Image
a.zbuffer = (byte*)ibuffer; a.zbuffer = (byte*)ibuffer;
a.zbuffer_end = (byte*)ibuffer + ilen; a.zbuffer_end = (byte*)ibuffer + ilen;
if (stbi__do_zlib(&a, obuffer, olen, 0, 1) != 0) if (stbi__do_zlib(&a, obuffer, olen, 0, 1) != 0)
{
return (int)(a.zout - a.zout_start); return (int)(a.zout - a.zout_start);
}
return -1; return -1;
} }
@@ -99,13 +114,19 @@ namespace Misaki.HighPerformance.Image
var a = new stbi__zbuf(); var a = new stbi__zbuf();
var p = (sbyte*)stbi__malloc(16384); var p = (sbyte*)stbi__malloc(16384);
if (p == null) if (p == null)
{
return null; return null;
}
a.zbuffer = (byte*)buffer; a.zbuffer = (byte*)buffer;
a.zbuffer_end = (byte*)buffer + len; a.zbuffer_end = (byte*)buffer + len;
if (stbi__do_zlib(&a, p, 16384, 1, 0) != 0) if (stbi__do_zlib(&a, p, 16384, 1, 0) != 0)
{ {
if (outlen != null) if (outlen != null)
{
*outlen = (int)(a.zout - a.zout_start); *outlen = (int)(a.zout - a.zout_start);
}
return a.zout_start; return a.zout_start;
} }
@@ -119,7 +140,10 @@ namespace Misaki.HighPerformance.Image
a.zbuffer = (byte*)ibuffer; a.zbuffer = (byte*)ibuffer;
a.zbuffer_end = (byte*)ibuffer + ilen; a.zbuffer_end = (byte*)ibuffer + ilen;
if (stbi__do_zlib(&a, obuffer, olen, 0, 0) != 0) if (stbi__do_zlib(&a, obuffer, olen, 0, 0) != 0)
{
return (int)(a.zout - a.zout_start); return (int)(a.zout - a.zout_start);
}
return -1; return -1;
} }
@@ -133,12 +157,18 @@ namespace Misaki.HighPerformance.Image
CRuntime.memset(sizes, 0, (ulong)(17 * sizeof(int))); CRuntime.memset(sizes, 0, (ulong)(17 * sizeof(int)));
CRuntime.memset(z->fast, 0, (ulong)(512 * sizeof(ushort))); CRuntime.memset(z->fast, 0, (ulong)(512 * sizeof(ushort)));
for (i = 0; i < num; ++i) for (i = 0; i < num; ++i)
{
++sizes[sizelist[i]]; ++sizes[sizelist[i]];
}
sizes[0] = 0; sizes[0] = 0;
for (i = 1; i < 16; ++i) for (i = 1; i < 16; ++i)
{
if (sizes[i] > 1 << i) if (sizes[i] > 1 << i)
{
return stbi__err("bad sizes"); return stbi__err("bad sizes");
}
}
code = 0; code = 0;
for (i = 1; i < 16; ++i) for (i = 1; i < 16; ++i)
@@ -148,8 +178,13 @@ namespace Misaki.HighPerformance.Image
z->firstsymbol[i] = (ushort)k; z->firstsymbol[i] = (ushort)k;
code = code + sizes[i]; code = code + sizes[i];
if (sizes[i] != 0) if (sizes[i] != 0)
{
if (code - 1 >= 1 << i) if (code - 1 >= 1 << i)
{
return stbi__err("bad codelengths"); return stbi__err("bad codelengths");
}
}
z->maxcode[i] = code << (16 - i); z->maxcode[i] = code << (16 - i);
code <<= 1; code <<= 1;
k += sizes[i]; k += sizes[i];
@@ -211,7 +246,10 @@ namespace Misaki.HighPerformance.Image
{ {
uint k = 0; uint k = 0;
if (z->num_bits < n) if (z->num_bits < n)
{
stbi__fill_bits(z); stbi__fill_bits(z);
}
k = (uint)(z->code_buffer & ((1 << n) - 1)); k = (uint)(z->code_buffer & ((1 << n) - 1));
z->code_buffer >>= n; z->code_buffer >>= n;
z->num_bits -= n; z->num_bits -= n;
@@ -225,16 +263,29 @@ namespace Misaki.HighPerformance.Image
var k = 0; var k = 0;
k = stbi__bit_reverse((int)a->code_buffer, 16); k = stbi__bit_reverse((int)a->code_buffer, 16);
for (s = 9 + 1; ; ++s) for (s = 9 + 1; ; ++s)
{
if (k < z->maxcode[s]) if (k < z->maxcode[s])
{
break; break;
}
}
if (s >= 16) if (s >= 16)
{
return -1; return -1;
}
b = (k >> (16 - s)) - z->firstcode[s] + z->firstsymbol[s]; b = (k >> (16 - s)) - z->firstcode[s] + z->firstsymbol[s];
if (b >= 288) if (b >= 288)
{
return -1; return -1;
}
if (z->size[b] != s) if (z->size[b] != s)
{
return -1; return -1;
}
a->code_buffer >>= s; a->code_buffer >>= s;
a->num_bits -= s; a->num_bits -= s;
return z->value[b]; return z->value[b];
@@ -284,21 +335,33 @@ namespace Misaki.HighPerformance.Image
uint old_limit = 0; uint old_limit = 0;
z->zout = zout; z->zout = zout;
if (z->z_expandable == 0) if (z->z_expandable == 0)
{
return stbi__err("output buffer limit"); return stbi__err("output buffer limit");
}
cur = (uint)(z->zout - z->zout_start); cur = (uint)(z->zout - z->zout_start);
limit = old_limit = (uint)(z->zout_end - z->zout_start); limit = old_limit = (uint)(z->zout_end - z->zout_start);
if (0xffffffff - cur < (uint)n) if (0xffffffff - cur < (uint)n)
{
return stbi__err("outofmem"); return stbi__err("outofmem");
}
while (cur + n > limit) while (cur + n > limit)
{ {
if (limit > 0xffffffff / 2) if (limit > 0xffffffff / 2)
{
return stbi__err("outofmem"); return stbi__err("outofmem");
}
limit *= 2; limit *= 2;
} }
q = (sbyte*)CRuntime.realloc(z->zout_start, (ulong)limit); q = (sbyte*)CRuntime.realloc(z->zout_start, (ulong)limit);
if (q == null) if (q == null)
{
return stbi__err("outofmem"); return stbi__err("outofmem");
}
z->zout_start = q; z->zout_start = q;
z->zout = q + cur; z->zout = q + cur;
z->zout_end = q + limit; z->zout_end = q + limit;
@@ -314,11 +377,17 @@ namespace Misaki.HighPerformance.Image
if (z < 256) if (z < 256)
{ {
if (z < 0) if (z < 0)
{
return stbi__err("bad huffman code"); return stbi__err("bad huffman code");
}
if (zout >= a->zout_end) if (zout >= a->zout_end)
{ {
if (stbi__zexpand(a, zout, 1) == 0) if (stbi__zexpand(a, zout, 1) == 0)
{
return 0; return 0;
}
zout = a->zout; zout = a->zout;
} }
@@ -333,29 +402,49 @@ namespace Misaki.HighPerformance.Image
{ {
a->zout = zout; a->zout = zout;
if (a->hit_zeof_once != 0 && a->num_bits < 16) if (a->hit_zeof_once != 0 && a->num_bits < 16)
{
return stbi__err("unexpected end"); return stbi__err("unexpected end");
}
return 1; return 1;
} }
if (z >= 286) if (z >= 286)
{
return stbi__err("bad huffman code"); return stbi__err("bad huffman code");
}
z -= 257; z -= 257;
len = stbi__zlength_base[z]; len = stbi__zlength_base[z];
if (stbi__zlength_extra[z] != 0) if (stbi__zlength_extra[z] != 0)
{
len += (int)stbi__zreceive(a, stbi__zlength_extra[z]); len += (int)stbi__zreceive(a, stbi__zlength_extra[z]);
}
z = stbi__zhuffman_decode(a, &a->z_distance); z = stbi__zhuffman_decode(a, &a->z_distance);
if (z < 0 || z >= 30) if (z < 0 || z >= 30)
{
return stbi__err("bad huffman code"); return stbi__err("bad huffman code");
}
dist = stbi__zdist_base[z]; dist = stbi__zdist_base[z];
if (stbi__zdist_extra[z] != 0) if (stbi__zdist_extra[z] != 0)
{
dist += (int)stbi__zreceive(a, stbi__zdist_extra[z]); dist += (int)stbi__zreceive(a, stbi__zdist_extra[z]);
}
if (zout - a->zout_start < dist) if (zout - a->zout_start < dist)
{
return stbi__err("bad dist"); return stbi__err("bad dist");
}
if (len > a->zout_end - zout) if (len > a->zout_end - zout)
{ {
if (stbi__zexpand(a, zout, len) == 0) if (stbi__zexpand(a, zout, len) == 0)
{
return 0; return 0;
}
zout = a->zout; zout = a->zout;
} }
@@ -364,14 +453,17 @@ namespace Misaki.HighPerformance.Image
{ {
var v = *p; var v = *p;
if (len != 0) if (len != 0)
{
do do
{ {
*zout++ = (sbyte)v; *zout++ = (sbyte)v;
} while (--len != 0); } while (--len != 0);
} }
}
else else
{ {
if (len != 0) if (len != 0)
{
do do
{ {
*zout++ = (sbyte)*p++; *zout++ = (sbyte)*p++;
@@ -380,6 +472,7 @@ namespace Misaki.HighPerformance.Image
} }
} }
} }
}
public static int stbi__compute_huffman_codes(stbi__zbuf* a) public static int stbi__compute_huffman_codes(stbi__zbuf* a)
{ {
@@ -400,13 +493,19 @@ namespace Misaki.HighPerformance.Image
} }
if (stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19) == 0) if (stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19) == 0)
{
return 0; return 0;
}
n = 0; n = 0;
while (n < ntot) while (n < ntot)
{ {
var c = stbi__zhuffman_decode(a, &z_codelength); var c = stbi__zhuffman_decode(a, &z_codelength);
if (c < 0 || c >= 19) if (c < 0 || c >= 19)
{
return stbi__err("bad codelengths"); return stbi__err("bad codelengths");
}
if (c < 16) if (c < 16)
{ {
lencodes[n++] = (byte)c; lencodes[n++] = (byte)c;
@@ -418,7 +517,10 @@ namespace Misaki.HighPerformance.Image
{ {
c = (int)(stbi__zreceive(a, 2) + 3); c = (int)(stbi__zreceive(a, 2) + 3);
if (n == 0) if (n == 0)
{
return stbi__err("bad codelengths"); return stbi__err("bad codelengths");
}
fill = lencodes[n - 1]; fill = lencodes[n - 1];
} }
else if (c == 17) else if (c == 17)
@@ -435,18 +537,30 @@ namespace Misaki.HighPerformance.Image
} }
if (ntot - n < c) if (ntot - n < c)
{
return stbi__err("bad codelengths"); return stbi__err("bad codelengths");
}
CRuntime.memset(lencodes + n, fill, (ulong)c); CRuntime.memset(lencodes + n, fill, (ulong)c);
n += c; n += c;
} }
} }
if (n != ntot) if (n != ntot)
{
return stbi__err("bad codelengths"); return stbi__err("bad codelengths");
}
if (stbi__zbuild_huffman(&a->z_length, lencodes, hlit) == 0) if (stbi__zbuild_huffman(&a->z_length, lencodes, hlit) == 0)
{
return 0; return 0;
}
if (stbi__zbuild_huffman(&a->z_distance, lencodes + hlit, hdist) == 0) if (stbi__zbuild_huffman(&a->z_distance, lencodes + hlit, hdist) == 0)
{
return 0; return 0;
}
return 1; return 1;
} }
@@ -457,7 +571,10 @@ namespace Misaki.HighPerformance.Image
var nlen = 0; var nlen = 0;
var k = 0; var k = 0;
if ((a->num_bits & 7) != 0) if ((a->num_bits & 7) != 0)
{
stbi__zreceive(a, a->num_bits & 7); stbi__zreceive(a, a->num_bits & 7);
}
k = 0; k = 0;
while (a->num_bits > 0) while (a->num_bits > 0)
{ {
@@ -467,19 +584,35 @@ namespace Misaki.HighPerformance.Image
} }
if (a->num_bits < 0) if (a->num_bits < 0)
{
return stbi__err("zlib corrupt"); return stbi__err("zlib corrupt");
}
while (k < 4) while (k < 4)
{
header[k++] = stbi__zget8(a); header[k++] = stbi__zget8(a);
}
len = header[1] * 256 + header[0]; len = header[1] * 256 + header[0];
nlen = header[3] * 256 + header[2]; nlen = header[3] * 256 + header[2];
if (nlen != (len ^ 0xffff)) if (nlen != (len ^ 0xffff))
{
return stbi__err("zlib corrupt"); return stbi__err("zlib corrupt");
}
if (a->zbuffer + len > a->zbuffer_end) if (a->zbuffer + len > a->zbuffer_end)
{
return stbi__err("read past buffer"); return stbi__err("read past buffer");
}
if (a->zout + len > a->zout_end) if (a->zout + len > a->zout_end)
{
if (stbi__zexpand(a, a->zout, len) == 0) if (stbi__zexpand(a, a->zout, len) == 0)
{
return 0; return 0;
}
}
CRuntime.memcpy(a->zout, a->zbuffer, (ulong)len); CRuntime.memcpy(a->zout, a->zbuffer, (ulong)len);
a->zbuffer += len; a->zbuffer += len;
a->zout += len; a->zout += len;
@@ -492,13 +625,25 @@ namespace Misaki.HighPerformance.Image
var cm = cmf & 15; var cm = cmf & 15;
int flg = stbi__zget8(a); int flg = stbi__zget8(a);
if (stbi__zeof(a) != 0) if (stbi__zeof(a) != 0)
{
return stbi__err("bad zlib header"); return stbi__err("bad zlib header");
}
if ((cmf * 256 + flg) % 31 != 0) if ((cmf * 256 + flg) % 31 != 0)
{
return stbi__err("bad zlib header"); return stbi__err("bad zlib header");
}
if ((flg & 32) != 0) if ((flg & 32) != 0)
{
return stbi__err("no preset dict"); return stbi__err("no preset dict");
}
if (cm != 8) if (cm != 8)
{
return stbi__err("bad compression"); return stbi__err("bad compression");
}
return 1; return 1;
} }
@@ -507,8 +652,13 @@ namespace Misaki.HighPerformance.Image
var final = 0; var final = 0;
var type = 0; var type = 0;
if (parse_header != 0) if (parse_header != 0)
{
if (stbi__parse_zlib_header(a) == 0) if (stbi__parse_zlib_header(a) == 0)
{
return 0; return 0;
}
}
a->num_bits = 0; a->num_bits = 0;
a->code_buffer = 0; a->code_buffer = 0;
a->hit_zeof_once = 0; a->hit_zeof_once = 0;
@@ -519,8 +669,10 @@ namespace Misaki.HighPerformance.Image
if (type == 0) if (type == 0)
{ {
if (stbi__parse_uncompressed_block(a) == 0) if (stbi__parse_uncompressed_block(a) == 0)
{
return 0; return 0;
} }
}
else if (type == 3) else if (type == 3)
{ {
return 0; return 0;
@@ -532,24 +684,32 @@ namespace Misaki.HighPerformance.Image
fixed (byte* b = stbi__zdefault_length) fixed (byte* b = stbi__zdefault_length)
{ {
if (stbi__zbuild_huffman(&a->z_length, b, 288) == 0) if (stbi__zbuild_huffman(&a->z_length, b, 288) == 0)
{
return 0; return 0;
} }
}
fixed (byte* b = stbi__zdefault_distance) fixed (byte* b = stbi__zdefault_distance)
{ {
if (stbi__zbuild_huffman(&a->z_distance, b, 32) == 0) if (stbi__zbuild_huffman(&a->z_distance, b, 32) == 0)
{
return 0; return 0;
} }
} }
}
else else
{ {
if (stbi__compute_huffman_codes(a) == 0) if (stbi__compute_huffman_codes(a) == 0)
{
return 0; return 0;
} }
}
if (stbi__parse_huffman_block(a) == 0) if (stbi__parse_huffman_block(a) == 0)
{
return 0; return 0;
} }
}
} while (final == 0); } while (final == 0);
return 1; return 1;

View File

@@ -1,4 +1,4 @@
using Misaki.HighPerformance.Image.Runtime; using Misaki.HighPerformance.Image.Runtime;
using System; using System;
using System.IO; using System.IO;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
@@ -31,7 +31,9 @@ public static unsafe partial class StbImage
public stbi__context(Stream stream) public stbi__context(Stream stream)
{ {
if (stream == null) if (stream == null)
{
throw new ArgumentNullException("stream"); throw new ArgumentNullException("stream");
}
_stream = stream; _stream = stream;
} }
@@ -55,7 +57,9 @@ public static unsafe partial class StbImage
{ {
var b = s.Stream.ReadByte(); var b = s.Stream.ReadByte();
if (b == -1) if (b == -1)
{
return 0; return 0;
}
return (byte)b; return (byte)b;
} }
@@ -79,7 +83,9 @@ public static unsafe partial class StbImage
{ {
if (s._tempBuffer == null || if (s._tempBuffer == null ||
s._tempBuffer.Length < size) s._tempBuffer.Length < size)
{
s._tempBuffer = new byte[size * 2]; s._tempBuffer = new byte[size * 2];
}
var result = s.Stream.Read(s._tempBuffer, 0, size); var result = s.Stream.Read(s._tempBuffer, 0, size);
Marshal.Copy(s._tempBuffer, 0, new IntPtr(buf), result); Marshal.Copy(s._tempBuffer, 0, new IntPtr(buf), result);

View File

@@ -6,6 +6,161 @@ using System.Runtime.CompilerServices;
namespace Misaki.HighPerformance.Jobs; namespace Misaki.HighPerformance.Jobs;
public interface IJobScheduler
{
/// <summary>
/// Gets the number of worker threads managed by the job scheduler.
/// </summary>
int WorkerCount
{
get;
}
/// <summary>
/// Schedules a single job for execution on a specified thread, with an optional dependency on another job.
/// </summary>
/// <typeparam name="T">The type of the job to execute. Must implement <see cref="IJob"/> and be unmanaged.</typeparam>
/// <param name="job">The job instance to be executed. The job data will be copied internally.</param>
/// <param name="threadIndex">The index of the thread that will execute the job. This is used to assign thread-specific data. Use -1 to allow any thread to execute the job.</param>
/// <param name="dependency">A <see cref="JobHandle"/> representing the dependencies that must be completed before this job can begin.
/// Use <see cref="JobHandle.Invalid"/> if there are no dependencies.</param>
/// <returns>A <see cref="JobHandle"/> that can be used to track the completion of the scheduled job.
/// Returns <see cref="JobHandle.Invalid"/> if the job data allocation fails.</returns>
JobHandle Schedule<T>(ref T job, int threadIndex, JobHandle dependency)
where T : unmanaged, IJob;
/// <summary>
/// Schedules a single job for execution on a specified thread without dependency.
/// </summary>
/// <typeparam name="T">The type of the job to execute. Must implement <see cref="IJob"/> and be unmanaged.</typeparam>
/// <param name="job">The job instance to be executed. The job data will be copied internally.</param>
/// <param name="threadIndex">The index of the thread that will execute the job. This is used to assign thread-specific data. Use -1 to allow any thread to execute the job.</param>
/// <returns>A <see cref="JobHandle"/> that can be used to track the completion of the scheduled job.
/// Returns <see cref="JobHandle.Invalid"/> if the job data allocation fails.</returns>
JobHandle Schedule<T>(ref T job, int threadIndex)
where T : unmanaged, IJob;
/// <summary>
/// Schedules a single job for execution on any thread, with an optional dependency on another job.
/// </summary>
/// <typeparam name="T">The type of the job to execute. Must implement <see cref="IJob"/> and be unmanaged.</typeparam>
/// <param name="job">The job instance to be executed. The job data will be copied internally.</param>
/// <param name="threadIndex">The index of the thread that will execute the job. This is used to assign thread-specific data. Use -1 to allow any thread to execute the job.</param>
/// <returns>A <see cref="JobHandle"/> that can be used to track the completion of the scheduled job.
/// Returns <see cref="JobHandle.Invalid"/> if the job data allocation fails.</returns>
JobHandle Schedule<T>(ref T job, JobHandle dependency)
where T : unmanaged, IJob;
/// <summary>
/// Schedules a single job for execution on any thread without dependency.
/// </summary>
/// <typeparam name="T">The type of the job to execute. Must implement <see cref="IJob"/> and be unmanaged.</typeparam>
/// <param name="job">The job instance to be executed. The job data will be copied internally.</param>
/// <param name="threadIndex">The index of the thread that will execute the job. This is used to assign thread-specific data. Use -1 to allow any thread to execute the job.</param>
/// <returns>A <see cref="JobHandle"/> that can be used to track the completion of the scheduled job.
/// Returns <see cref="JobHandle.Invalid"/> if the job data allocation fails.</returns>
JobHandle Schedule<T>(ref T job)
where T : unmanaged, IJob;
/// <summary>
/// Schedules a parallel job for execution, dividing the workload into batches and distributing it across threads.
/// </summary>
/// <typeparam name="T">The type of the job to execute. Must implement <see cref="IJobParallelFor"/> and be unmanaged.</typeparam>
/// <param name="job">The job instance to be executed. The job data will be copied internally.</param>
/// <param name="totalIteration">The total number of iterations to be processed by the job.</param>
/// <param name="batchSize">The number of iterations to include in each batch.</param>
/// <param name="threadIndex">The index of the thread that will execute the job. This is used to assign thread-specific data. Use -1 to allow any thread to execute the job.</param>
/// <param name="dependency">A <see cref="JobHandle"/> representing the dependencies that must be completed before this job can begin.
/// Use <see cref="JobHandle.Invalid"/> if there are no dependencies.</param>
/// <returns>A <see cref="JobHandle"/> that can be used to track the completion of the scheduled job.
/// Returns <see cref="JobHandle.Invalid"/> if the job data allocation fails.</returns>
JobHandle ScheduleParallel<T>(ref T job, int totalIteration, int batchSize, int threadIndex, JobHandle dependency)
where T : unmanaged, IJobParallelFor;
/// <summary>
/// Schedules a parallel job for execution, dividing the workload into batches and distributing it across threads on a specified thread without dependency.
/// </summary>
/// <typeparam name="T">The type of the job to execute. Must implement <see cref="IJobParallelFor"/> and be unmanaged.</typeparam>
/// <param name="job">The job instance to be executed. The job data will be copied internally.</param>
/// <param name="totalIteration">The total number of iterations to be processed by the job.</param>
/// <param name="batchSize">The number of iterations to include in each batch.</param>
/// <param name="threadIndex">The index of the thread that will execute the job. This is used to assign thread-specific data. Use -1 to allow any thread to execute the job.</param>
/// <returns>A <see cref="JobHandle"/> that can be used to track the completion of the scheduled job.
/// Returns <see cref="JobHandle.Invalid"/> if the job data allocation fails.</returns>
public JobHandle ScheduleParallel<T>(ref T job, int totalIteration, int batchSize, int threadIndex)
where T : unmanaged, IJobParallelFor;
/// <summary>
/// Schedules a parallel job for execution, dividing the workload into batches and distributing it across threads on any thread, with an optional dependency on another job..
/// </summary>
/// <typeparam name="T">The type of the job to execute. Must implement <see cref="IJobParallelFor"/> and be unmanaged.</typeparam>
/// <param name="job">The job instance to be executed. The job data will be copied internally.</param>
/// <param name="totalIteration">The total number of iterations to be processed by the job.</param>
/// <param name="batchSize">The number of iterations to include in each batch.</param>
/// <param name="threadIndex">The index of the thread that will execute the job. This is used to assign thread-specific data. Use -1 to allow any thread to execute the job.</param>
/// <returns>A <see cref="JobHandle"/> that can be used to track the completion of the scheduled job.
/// Returns <see cref="JobHandle.Invalid"/> if the job data allocation fails.</returns>
public JobHandle ScheduleParallel<T>(ref T job, int totalIteration, int batchSize, JobHandle dependency)
where T : unmanaged, IJobParallelFor;
/// <summary>
/// Schedules a parallel job for execution, dividing the workload into batches and distributing it across threads on any thread without dependency.
/// </summary>
/// <typeparam name="T">The type of the job to execute. Must implement <see cref="IJobParallelFor"/> and be unmanaged.</typeparam>
/// <param name="job">The job instance to be executed. The job data will be copied internally.</param>
/// <param name="totalIteration">The total number of iterations to be processed by the job.</param>
/// <param name="batchSize">The number of iterations to include in each batch.</param>
/// <param name="threadIndex">The index of the thread that will execute the job. This is used to assign thread-specific data. Use -1 to allow any thread to execute the job.</param>
/// <returns>A <see cref="JobHandle"/> that can be used to track the completion of the scheduled job.
/// Returns <see cref="JobHandle.Invalid"/> if the job data allocation fails.</returns>
public JobHandle ScheduleParallel<T>(ref T job, int totalIteration, int batchSize)
where T : unmanaged, IJobParallelFor;
/// <summary>
/// Combines multiple job dependencies into a single <see cref="JobHandle"/>.
/// </summary>
/// <param name="dependencies">A collection of <see cref="JobHandle"/> instances representing the dependencies to combine.</param>
/// <returns>A <see cref="JobHandle"/> that represents the combined dependencies. The returned handle can be used to ensure
/// that all specified dependencies are completed before proceeding.</returns>
public JobHandle CombineDependencies(params ReadOnlySpan<JobHandle> dependencies);
/// <summary>
/// Retrieves the current status of a job identified by the specified handle.
/// </summary>
/// <param name="handle">The handle representing the job whose status is to be retrieved. The handle must be valid.</param>
/// <returns>The current status of the job as a <see cref="JobState"/> value.
/// Returns <see cref="JobState.Invalid"/> if the handle is invalid or the job does not exist.</returns>
public JobState GetJobStatus(JobHandle handle);
/// <summary>
/// Blocks the calling thread until the specified job is completed.
/// </summary>
/// <param name="handle">The handle of the job to wait for.</param>
public void WaitComplete(JobHandle handle);
/// <summary>
/// Blocks the calling thread until all specified job handles have completed.
/// </summary>
/// <remarks>This method waits for all jobs referenced by the provided handles to complete before
/// returning. The calling thread will be blocked until every job has finished. If any handle is invalid or does not
/// correspond to an active job, it is considered completed. This method is not thread-safe and should not be called
/// concurrently from multiple threads.</remarks>
/// <param name="handles">A collection of job handles to wait for. Each handle represents an asynchronous job whose completion is awaited.
/// The collection must not be empty.</param>
public void WaitAll(params ReadOnlySpan<JobHandle> handles);
/// <summary>
/// Waits until any of the specified job handles has completed and returns the first completed handle.
/// </summary>
/// <remarks>This method blocks the calling thread until at least one of the specified jobs has finished.
/// The returned handle corresponds to the job that completed first among those provided. The order of handles in
/// the span may affect which handle is returned if multiple jobs complete simultaneously.</remarks>
/// <param name="handles">A read-only span containing the job handles to monitor for completion. Each handle represents a job whose
/// completion status will be checked.</param>
/// <returns>The first job handle from the provided collection that has completed.</returns>
public JobHandle WaitAny(params ReadOnlySpan<JobHandle> handles);
}
public unsafe partial class JobScheduler public unsafe partial class JobScheduler
{ {
public static readonly TempJobAllocator* pTempAllocator; public static readonly TempJobAllocator* pTempAllocator;
@@ -37,7 +192,7 @@ public unsafe partial class JobScheduler
/// <summary> /// <summary>
/// Provides a mechanism for scheduling and executing jobs across multiple worker threads. /// Provides a mechanism for scheduling and executing jobs across multiple worker threads.
/// </summary> /// </summary>
public sealed unsafe partial class JobScheduler : IDisposable public sealed unsafe partial class JobScheduler : IJobScheduler, IDisposable
{ {
private const int _SLEEP_THRESHOLD = 100; private const int _SLEEP_THRESHOLD = 100;
@@ -253,16 +408,6 @@ public sealed unsafe partial class JobScheduler : IDisposable
} }
} }
/// <summary>
/// Schedules a single job for execution on a specified thread, with an optional dependency on another job.
/// </summary>
/// <typeparam name="T">The type of the job to execute. Must implement <see cref="IJob"/> and be unmanaged.</typeparam>
/// <param name="job">The job instance to be executed. The job data will be copied internally.</param>
/// <param name="threadIndex">The index of the thread that will execute the job. This is used to assign thread-specific data. Use -1 to allow any thread to execute the job.</param>
/// <param name="dependency">A <see cref="JobHandle"/> representing the dependencies that must be completed before this job can begin.
/// Use <see cref="JobHandle.Invalid"/> if there are no dependencies.</param>
/// <returns>A <see cref="JobHandle"/> that can be used to track the completion of the scheduled job.
/// Returns <see cref="JobHandle.Invalid"/> if the job data allocation fails.</returns>
public JobHandle Schedule<T>(ref T job, int threadIndex, JobHandle dependency) public JobHandle Schedule<T>(ref T job, int threadIndex, JobHandle dependency)
where T : unmanaged, IJob where T : unmanaged, IJob
{ {
@@ -291,54 +436,18 @@ public sealed unsafe partial class JobScheduler : IDisposable
return CreateJobHandle(ref jobInfo, dependency); return CreateJobHandle(ref jobInfo, dependency);
} }
/// <summary>
/// Schedules a single job for execution on a specified thread without dependency.
/// </summary>
/// <typeparam name="T">The type of the job to execute. Must implement <see cref="IJob"/> and be unmanaged.</typeparam>
/// <param name="job">The job instance to be executed. The job data will be copied internally.</param>
/// <param name="threadIndex">The index of the thread that will execute the job. This is used to assign thread-specific data. Use -1 to allow any thread to execute the job.</param>
/// <returns>A <see cref="JobHandle"/> that can be used to track the completion of the scheduled job.
/// Returns <see cref="JobHandle.Invalid"/> if the job data allocation fails.</returns>
public JobHandle Schedule<T>(ref T job, int threadIndex) public JobHandle Schedule<T>(ref T job, int threadIndex)
where T : unmanaged, IJob where T : unmanaged, IJob
=> Schedule(ref job, threadIndex, JobHandle.Invalid); => Schedule(ref job, threadIndex, JobHandle.Invalid);
/// <summary>
/// Schedules a single job for execution on any thread, with an optional dependency on another job.
/// </summary>
/// <typeparam name="T">The type of the job to execute. Must implement <see cref="IJob"/> and be unmanaged.</typeparam>
/// <param name="job">The job instance to be executed. The job data will be copied internally.</param>
/// <param name="threadIndex">The index of the thread that will execute the job. This is used to assign thread-specific data. Use -1 to allow any thread to execute the job.</param>
/// <returns>A <see cref="JobHandle"/> that can be used to track the completion of the scheduled job.
/// Returns <see cref="JobHandle.Invalid"/> if the job data allocation fails.</returns>
public JobHandle Schedule<T>(ref T job, JobHandle dependency) public JobHandle Schedule<T>(ref T job, JobHandle dependency)
where T : unmanaged, IJob where T : unmanaged, IJob
=> Schedule(ref job, -1, dependency); => Schedule(ref job, -1, dependency);
/// <summary>
/// Schedules a single job for execution on any thread without dependency.
/// </summary>
/// <typeparam name="T">The type of the job to execute. Must implement <see cref="IJob"/> and be unmanaged.</typeparam>
/// <param name="job">The job instance to be executed. The job data will be copied internally.</param>
/// <param name="threadIndex">The index of the thread that will execute the job. This is used to assign thread-specific data. Use -1 to allow any thread to execute the job.</param>
/// <returns>A <see cref="JobHandle"/> that can be used to track the completion of the scheduled job.
/// Returns <see cref="JobHandle.Invalid"/> if the job data allocation fails.</returns>
public JobHandle Schedule<T>(ref T job) public JobHandle Schedule<T>(ref T job)
where T : unmanaged, IJob where T : unmanaged, IJob
=> Schedule(ref job, -1, JobHandle.Invalid); => Schedule(ref job, -1, JobHandle.Invalid);
/// <summary>
/// Schedules a parallel job for execution, dividing the workload into batches and distributing it across threads.
/// </summary>
/// <typeparam name="T">The type of the job to execute. Must implement <see cref="IJobParallelFor"/> and be unmanaged.</typeparam>
/// <param name="job">The job instance to be executed. The job data will be copied internally.</param>
/// <param name="totalIteration">The total number of iterations to be processed by the job.</param>
/// <param name="batchSize">The number of iterations to include in each batch.</param>
/// <param name="threadIndex">The index of the thread that will execute the job. This is used to assign thread-specific data. Use -1 to allow any thread to execute the job.</param>
/// <param name="dependency">A <see cref="JobHandle"/> representing the dependencies that must be completed before this job can begin.
/// Use <see cref="JobHandle.Invalid"/> if there are no dependencies.</param>
/// <returns>A <see cref="JobHandle"/> that can be used to track the completion of the scheduled job.
/// Returns <see cref="JobHandle.Invalid"/> if the job data allocation fails.</returns>
public JobHandle ScheduleParallel<T>(ref T job, int totalIteration, int batchSize, int threadIndex, JobHandle dependency) public JobHandle ScheduleParallel<T>(ref T job, int totalIteration, int batchSize, int threadIndex, JobHandle dependency)
where T : unmanaged, IJobParallelFor where T : unmanaged, IJobParallelFor
{ {
@@ -375,54 +484,18 @@ public sealed unsafe partial class JobScheduler : IDisposable
return CreateJobHandle(ref jobInfo, dependency); return CreateJobHandle(ref jobInfo, dependency);
} }
/// <summary>
/// Schedules a parallel job for execution, dividing the workload into batches and distributing it across threads on a specified thread without dependency.
/// </summary>
/// <typeparam name="T">The type of the job to execute. Must implement <see cref="IJobParallelFor"/> and be unmanaged.</typeparam>
/// <param name="job">The job instance to be executed. The job data will be copied internally.</param>
/// <param name="totalIteration">The total number of iterations to be processed by the job.</param>
/// <param name="batchSize">The number of iterations to include in each batch.</param>
/// <param name="threadIndex">The index of the thread that will execute the job. This is used to assign thread-specific data. Use -1 to allow any thread to execute the job.</param>
/// <returns>A <see cref="JobHandle"/> that can be used to track the completion of the scheduled job.
/// Returns <see cref="JobHandle.Invalid"/> if the job data allocation fails.</returns>
public JobHandle ScheduleParallel<T>(ref T job, int totalIteration, int batchSize, int threadIndex) public JobHandle ScheduleParallel<T>(ref T job, int totalIteration, int batchSize, int threadIndex)
where T : unmanaged, IJobParallelFor where T : unmanaged, IJobParallelFor
=> ScheduleParallel(ref job, totalIteration, batchSize, threadIndex, JobHandle.Invalid); => ScheduleParallel(ref job, totalIteration, batchSize, threadIndex, JobHandle.Invalid);
/// <summary>
/// Schedules a parallel job for execution, dividing the workload into batches and distributing it across threads on any thread, with an optional dependency on another job..
/// </summary>
/// <typeparam name="T">The type of the job to execute. Must implement <see cref="IJobParallelFor"/> and be unmanaged.</typeparam>
/// <param name="job">The job instance to be executed. The job data will be copied internally.</param>
/// <param name="totalIteration">The total number of iterations to be processed by the job.</param>
/// <param name="batchSize">The number of iterations to include in each batch.</param>
/// <param name="threadIndex">The index of the thread that will execute the job. This is used to assign thread-specific data. Use -1 to allow any thread to execute the job.</param>
/// <returns>A <see cref="JobHandle"/> that can be used to track the completion of the scheduled job.
/// Returns <see cref="JobHandle.Invalid"/> if the job data allocation fails.</returns>
public JobHandle ScheduleParallel<T>(ref T job, int totalIteration, int batchSize, JobHandle dependency) public JobHandle ScheduleParallel<T>(ref T job, int totalIteration, int batchSize, JobHandle dependency)
where T : unmanaged, IJobParallelFor where T : unmanaged, IJobParallelFor
=> ScheduleParallel(ref job, totalIteration, batchSize, -1, dependency); => ScheduleParallel(ref job, totalIteration, batchSize, -1, dependency);
/// <summary>
/// Schedules a parallel job for execution, dividing the workload into batches and distributing it across threads on any thread without dependency.
/// </summary>
/// <typeparam name="T">The type of the job to execute. Must implement <see cref="IJobParallelFor"/> and be unmanaged.</typeparam>
/// <param name="job">The job instance to be executed. The job data will be copied internally.</param>
/// <param name="totalIteration">The total number of iterations to be processed by the job.</param>
/// <param name="batchSize">The number of iterations to include in each batch.</param>
/// <param name="threadIndex">The index of the thread that will execute the job. This is used to assign thread-specific data. Use -1 to allow any thread to execute the job.</param>
/// <returns>A <see cref="JobHandle"/> that can be used to track the completion of the scheduled job.
/// Returns <see cref="JobHandle.Invalid"/> if the job data allocation fails.</returns>
public JobHandle ScheduleParallel<T>(ref T job, int totalIteration, int batchSize) public JobHandle ScheduleParallel<T>(ref T job, int totalIteration, int batchSize)
where T : unmanaged, IJobParallelFor where T : unmanaged, IJobParallelFor
=> ScheduleParallel(ref job, totalIteration, batchSize, -1, JobHandle.Invalid); => ScheduleParallel(ref job, totalIteration, batchSize, -1, JobHandle.Invalid);
/// <summary>
/// Combines multiple job dependencies into a single <see cref="JobHandle"/>.
/// </summary>
/// <param name="dependencies">A collection of <see cref="JobHandle"/> instances representing the dependencies to combine.</param>
/// <returns>A <see cref="JobHandle"/> that represents the combined dependencies. The returned handle can be used to ensure
/// that all specified dependencies are completed before proceeding.</returns>
public JobHandle CombineDependencies(params ReadOnlySpan<JobHandle> dependencies) public JobHandle CombineDependencies(params ReadOnlySpan<JobHandle> dependencies)
{ {
var jobInfo = new JobInfo var jobInfo = new JobInfo
@@ -439,12 +512,6 @@ public sealed unsafe partial class JobScheduler : IDisposable
return CreateJobHandle(ref jobInfo, dependencies); return CreateJobHandle(ref jobInfo, dependencies);
} }
/// <summary>
/// Retrieves the current status of a job identified by the specified handle.
/// </summary>
/// <param name="handle">The handle representing the job whose status is to be retrieved. The handle must be valid.</param>
/// <returns>The current status of the job as a <see cref="JobState"/> value.
/// Returns <see cref="JobState.Invalid"/> if the handle is invalid or the job does not exist.</returns>
public JobState GetJobStatus(JobHandle handle) public JobState GetJobStatus(JobHandle handle)
{ {
if (!handle.IsValid) if (!handle.IsValid)
@@ -461,10 +528,6 @@ public sealed unsafe partial class JobScheduler : IDisposable
return (JobState)Volatile.Read(ref Unsafe.As<JobState, int>(ref jobInfo.state)); return (JobState)Volatile.Read(ref Unsafe.As<JobState, int>(ref jobInfo.state));
} }
/// <summary>
/// Blocks the calling thread until the specified job is completed.
/// </summary>
/// <param name="handle">The handle of the job to wait for.</param>
public void WaitComplete(JobHandle handle) public void WaitComplete(JobHandle handle)
{ {
if (!handle.IsValid) if (!handle.IsValid)
@@ -484,15 +547,6 @@ public sealed unsafe partial class JobScheduler : IDisposable
} }
} }
/// <summary>
/// Blocks the calling thread until all specified job handles have completed.
/// </summary>
/// <remarks>This method waits for all jobs referenced by the provided handles to complete before
/// returning. The calling thread will be blocked until every job has finished. If any handle is invalid or does not
/// correspond to an active job, it is considered completed. This method is not thread-safe and should not be called
/// concurrently from multiple threads.</remarks>
/// <param name="handles">A collection of job handles to wait for. Each handle represents an asynchronous job whose completion is awaited.
/// The collection must not be empty.</param>
public void WaitAll(params ReadOnlySpan<JobHandle> handles) public void WaitAll(params ReadOnlySpan<JobHandle> handles)
{ {
var sleepThreshold = _SLEEP_THRESHOLD * handles.Length; var sleepThreshold = _SLEEP_THRESHOLD * handles.Length;
@@ -518,15 +572,6 @@ public sealed unsafe partial class JobScheduler : IDisposable
} }
} }
/// <summary>
/// Waits until any of the specified job handles has completed and returns the first completed handle.
/// </summary>
/// <remarks>This method blocks the calling thread until at least one of the specified jobs has finished.
/// The returned handle corresponds to the job that completed first among those provided. The order of handles in
/// the span may affect which handle is returned if multiple jobs complete simultaneously.</remarks>
/// <param name="handles">A read-only span containing the job handles to monitor for completion. Each handle represents a job whose
/// completion status will be checked.</param>
/// <returns>The first job handle from the provided collection that has completed.</returns>
public JobHandle WaitAny(params ReadOnlySpan<JobHandle> handles) public JobHandle WaitAny(params ReadOnlySpan<JobHandle> handles)
{ {
var sleepThreshold = _SLEEP_THRESHOLD * handles.Length; var sleepThreshold = _SLEEP_THRESHOLD * handles.Length;

View File

@@ -6,7 +6,7 @@
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks> <AllowUnsafeBlocks>True</AllowUnsafeBlocks>
<AssemblyVersion>1.2.1</AssemblyVersion> <AssemblyVersion>1.2.2</AssemblyVersion>
<Version>$(AssemblyVersion)</Version> <Version>$(AssemblyVersion)</Version>
<Authors>Misaki</Authors> <Authors>Misaki</Authors>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild> <GeneratePackageOnBuild>True</GeneratePackageOnBuild>

View File

@@ -81,7 +81,7 @@ public unsafe struct UnsafeSlotMap<T> : IUnsafeCollection<T>
private int _count; private int _count;
private int _capacity; private int _capacity;
public readonly int Count => _count - 1; public readonly int Count => _count;
public readonly int Capacity => _capacity; public readonly int Capacity => _capacity;
public readonly bool IsCreated => _data.IsCreated && _generations.IsCreated && _freeSlots.IsCreated && _validBits.IsCreated; public readonly bool IsCreated => _data.IsCreated && _generations.IsCreated && _freeSlots.IsCreated && _validBits.IsCreated;
@@ -126,9 +126,6 @@ public unsafe struct UnsafeSlotMap<T> : IUnsafeCollection<T>
_count = 0; _count = 0;
_capacity = capacity; _capacity = capacity;
// Add a default item for invalid slot
Add(default, out _);
} }
/// <summary> /// <summary>
@@ -212,8 +209,7 @@ public unsafe struct UnsafeSlotMap<T> : IUnsafeCollection<T>
/// <returns>true if the slot at the specified index is valid and its generation matches the specified value; otherwise, false.</returns> /// <returns>true if the slot at the specified index is valid and its generation matches the specified value; otherwise, false.</returns>
public readonly bool Contains(int slotIndex, int generation) public readonly bool Contains(int slotIndex, int generation)
{ {
// 0 is reserved for invalid slot if (slotIndex < 0 || slotIndex >= _capacity)
if (slotIndex <= 0 || slotIndex >= _capacity)
{ {
return false; return false;
} }

View File

@@ -85,7 +85,7 @@ public unsafe struct UnsafeSparseSet<T> : IUnsafeCollection<T>
private int _nextId; // Next available sparse index private int _nextId; // Next available sparse index
private int _capacity; private int _capacity;
public readonly int Count => _count - 1; public readonly int Count => _count;
public readonly int Capacity => _capacity; public readonly int Capacity => _capacity;
public readonly bool IsCreated => _dense.IsCreated && _sparse.IsCreated && _reverse.IsCreated; public readonly bool IsCreated => _dense.IsCreated && _sparse.IsCreated && _reverse.IsCreated;
@@ -133,8 +133,6 @@ public unsafe struct UnsafeSparseSet<T> : IUnsafeCollection<T>
_sparse.AsSpan().Fill(-1); _sparse.AsSpan().Fill(-1);
_generations.Clear(); _generations.Clear();
Add(default, out _); // Make index 0 invalid
} }
/// <summary> /// <summary>
@@ -240,8 +238,7 @@ public unsafe struct UnsafeSparseSet<T> : IUnsafeCollection<T>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly bool Contains(int sparseIndex, int generation) public readonly bool Contains(int sparseIndex, int generation)
{ {
// 0 is reserved as invalid index if (sparseIndex < 0 || sparseIndex >= _sparse.Count)
if (sparseIndex <= 0 || sparseIndex >= _sparse.Count)
{ {
return false; return false;
} }

View File

@@ -6,7 +6,7 @@
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks> <AllowUnsafeBlocks>True</AllowUnsafeBlocks>
<Authors>Misaki</Authors> <Authors>Misaki</Authors>
<AssemblyVersion>1.3.2</AssemblyVersion> <AssemblyVersion>1.3.3</AssemblyVersion>
<Version>$(AssemblyVersion)</Version> <Version>$(AssemblyVersion)</Version>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild> <GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<PackageProjectUrl>https://git.personalnas.com/Misaki/Misaki.HighPerformance.git</PackageProjectUrl> <PackageProjectUrl>https://git.personalnas.com/Misaki/Misaki.HighPerformance.git</PackageProjectUrl>

View File

@@ -67,7 +67,7 @@ public readonly unsafe struct SharedPtr<T> : IEquatable<SharedPtr<T>>
/// Ensures that the pointer is not shared and can be safely transferred or detached. /// Ensures that the pointer is not shared and can be safely transferred or detached.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// UniquePtr<T> is designed to encapsulate a raw pointer, enforcing unique ownership semantics similar to C++'s std::unique_ptr. /// <see cref="UniquePtr{T}"/> is designed to encapsulate a raw pointer, enforcing unique ownership semantics similar to C++'s std::unique_ptr.
/// </remarks> /// </remarks>
/// <typeparam name="T">The unmanaged type of the value to which the pointer refers.</typeparam> /// <typeparam name="T">The unmanaged type of the value to which the pointer refers.</typeparam>
[NonCopyable] [NonCopyable]
@@ -174,3 +174,47 @@ public ref struct Ref<T>
return !(left == right); return !(left == right);
} }
} }
/// <summary>
/// Provides a wrapper for a value type that implements <see cref="IDisposable"/>, ensuring proper disposal of the contained value.
/// </summary>
/// <remarks>The <see cref="Owner{T}"/> class manages the lifetime of the contained value by calling its
/// <see cref="IDisposable.Dispose"/> method when the wrapper is disposed or finalized. After disposal, accessing the value
/// will throw an <see cref="ObjectDisposedException"/>.</remarks>
/// <typeparam name="T">The value type to wrap. Must be a struct that implements <see cref="IDisposable"/>.</typeparam>
public class Owner<T> : IDisposable
where T : struct, IDisposable
{
private T _value;
private bool _disposed;
public Owner(T value)
{
_value = value;
}
~Owner()
{
Dispose();
}
public ref T Get()
{
ObjectDisposedException.ThrowIf(_disposed, this);
return ref _value;
}
public void Dispose()
{
if (_disposed)
{
return;
}
_value.Dispose();
_disposed = true;
GC.SuppressFinalize(this);
}
}

View File

@@ -6,7 +6,7 @@ namespace Misaki.HighPerformance.Mathematics.CodeGen.Generators
{ {
internal abstract class GeneratorBase internal abstract class GeneratorBase
{ {
protected const string INLINE_METHOD_ATTRIBUTE = "[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining | global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveOptimization)]"; protected const string INLINE_METHOD_ATTRIBUTE = "[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]";
protected static readonly string[] s_matrixComponents = new[] { "c0", "c1", "c2", "c3" }; protected static readonly string[] s_matrixComponents = new[] { "c0", "c1", "c2", "c3" };
protected static readonly string[] s_vectorComponents = new[] { "x", "y", "z", "w" }; protected static readonly string[] s_vectorComponents = new[] { "x", "y", "z", "w" };
@@ -19,10 +19,9 @@ namespace Misaki.HighPerformance.Mathematics.CodeGen.Generators
this.typeInfo = typeInfo; this.typeInfo = typeInfo;
sourceBuilder.Clear(); sourceBuilder.Clear();
var message = Validation(); if (!Validation(out var message))
if (message != null)
{ {
return message; return message ?? string.Empty;
} }
Initialize(); Initialize();
@@ -51,9 +50,10 @@ namespace Misaki.HighPerformance.Mathematics.CodeGen.Generators
#endregion"); #endregion");
} }
protected virtual string? Validation() protected virtual bool Validation(out string? message)
{ {
return null; message = null;
return true;
} }
protected virtual void Initialize() protected virtual void Initialize()

View File

@@ -1,7 +1,6 @@
using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection.Metadata;
using System.Text; using System.Text;
namespace Misaki.HighPerformance.Mathematics.CodeGen.Generators namespace Misaki.HighPerformance.Mathematics.CodeGen.Generators
@@ -16,14 +15,16 @@ namespace Misaki.HighPerformance.Mathematics.CodeGen.Generators
.Replace("{c}", s_matrixComponents[componentIndex]); .Replace("{c}", s_matrixComponents[componentIndex]);
} }
protected override string? Validation() protected override bool Validation(out string? message)
{ {
if (typeInfo.ElementTypeSymbol == null) if (typeInfo.ElementTypeSymbol == null)
{ {
return "You must specify 'elementType' in NumericTypeAttribute for matrix types."; message = "You must specify 'elementType' in NumericTypeAttribute for matrix types.";
return false;
} }
return null; message = null;
return true;
} }
protected override void GenerateBody() protected override void GenerateBody()
@@ -36,7 +37,6 @@ namespace Misaki.HighPerformance.Mathematics.CodeGen.Generators
} }
GenerateConstructors(); GenerateConstructors();
GenerateUnsafeMethod();
GenerateOverrideMethod(); GenerateOverrideMethod();
if (typeInfo.Arithmetic) if (typeInfo.Arithmetic)
@@ -67,14 +67,19 @@ namespace Misaki.HighPerformance.Mathematics.CodeGen.Generators
{INLINE_METHOD_ATTRIBUTE} {INLINE_METHOD_ATTRIBUTE}
get get
{{ {{
#if ENABLE_COLLECTION_CHECKS RangeCheck(index);
return ref (({typeInfo.ComponentTypeFullName}*)global::System.Runtime.CompilerServices.Unsafe.AsPointer(ref this))[index];
}}
}}");
sourceBuilder.AppendLine($@"
[global::System.Diagnostics.Conditional(""ENABLE_COLLECTION_CHECKS"")]
private void RangeCheck(int index)
{{
if (index < 0 || index >= {typeInfo.Column}) if (index < 0 || index >= {typeInfo.Column})
{{ {{
throw new global::System.ArgumentOutOfRangeException(nameof(index), $""Index {{index}} is out of range of '{typeInfo.TypeName}'""); throw new global::System.ArgumentOutOfRangeException(nameof(index), $""Index {{index}} is out of range of '{typeInfo.TypeName}'"");
}} }}
#endif
return ref (({typeInfo.ComponentTypeFullName}*)global::System.Runtime.CompilerServices.Unsafe.AsPointer(ref this))[index];
}}
}}"); }}");
} }
@@ -218,22 +223,6 @@ namespace Misaki.HighPerformance.Mathematics.CodeGen.Generators
} }
} }
private void GenerateUnsafeMethod()
{
sourceBuilder.AppendLine($@"
{INLINE_METHOD_ATTRIBUTE}
public unsafe {typeInfo.ComponentTypeFullName}* AsPointer()
{{
return ({typeInfo.ComponentTypeFullName}*)global::System.Runtime.CompilerServices.Unsafe.AsPointer(ref this);
}}
{INLINE_METHOD_ATTRIBUTE}
public unsafe global::System.Span<{typeInfo.ComponentTypeFullName}> AsSpan()
{{
return new global::System.Span<{typeInfo.ComponentTypeFullName}>(AsPointer(), {typeInfo.Column});
}}");
}
private void GenerateOverrideMethod() private void GenerateOverrideMethod()
{ {
var components = s_matrixComponents.Take(typeInfo.Column).ToArray(); var components = s_matrixComponents.Take(typeInfo.Column).ToArray();
@@ -760,7 +749,7 @@ namespace Misaki.HighPerformance.Mathematics.CodeGen.Generators
var columnSizeBytes = lhsRows * typeInfo.ComponentSize; var columnSizeBytes = lhsRows * typeInfo.ComponentSize;
var vectorBits = columnSizeBytes > 16 ? 256 : 128; var vectorBits = columnSizeBytes > 16 ? 256 : 128;
bool isFloatingPoint = typeInfo.ElementTypeSymbol!.SpecialType == SpecialType.System_Single|| var isFloatingPoint = typeInfo.ElementTypeSymbol!.SpecialType == SpecialType.System_Single ||
typeInfo.ElementTypeSymbol!.SpecialType == SpecialType.System_Double; typeInfo.ElementTypeSymbol!.SpecialType == SpecialType.System_Double;
sourceBuilder.Append($@" sourceBuilder.Append($@"
@@ -768,7 +757,7 @@ namespace Misaki.HighPerformance.Mathematics.CodeGen.Generators
public static {resultVectorType} mul({lhsType} m, {rhsVectorType} v) public static {resultVectorType} mul({lhsType} m, {rhsVectorType} v)
{{"); {{");
for (int i = 0; i < lhsCols; i++) for (var i = 0; i < lhsCols; i++)
{ {
var component = s_vectorComponents[i]; var component = s_vectorComponents[i];
sourceBuilder.Append($@" sourceBuilder.Append($@"
@@ -779,7 +768,7 @@ namespace Misaki.HighPerformance.Mathematics.CodeGen.Generators
var sum = global::System.Runtime.Intrinsics.Vector{vectorBits}.Multiply(m.c0.AsVector{vectorBits}(), vx);"); var sum = global::System.Runtime.Intrinsics.Vector{vectorBits}.Multiply(m.c0.AsVector{vectorBits}(), vx);");
for (int i = 1; i < lhsCols; i++) for (var i = 1; i < lhsCols; i++)
{ {
var component = s_vectorComponents[i]; var component = s_vectorComponents[i];
var col = s_matrixComponents[i]; var col = s_matrixComponents[i];

View File

@@ -52,7 +52,6 @@ namespace Misaki.HighPerformance.Mathematics.CodeGen.Generators
} }
GenerateConstructors(); GenerateConstructors();
GenerateUnsafeMethod();
GenerateOverrideMethod(); GenerateOverrideMethod();
GenerateConvertionMethod(); GenerateConvertionMethod();
@@ -95,14 +94,19 @@ namespace Misaki.HighPerformance.Mathematics.CodeGen.Generators
{INLINE_METHOD_ATTRIBUTE} {INLINE_METHOD_ATTRIBUTE}
get get
{{ {{
#if ENABLE_COLLECTION_CHECKS RangeCheck(index);
return ref (({typeInfo.ComponentTypeFullName}*)global::System.Runtime.CompilerServices.Unsafe.AsPointer(ref this))[index];
}}
}}");
sourceBuilder.AppendLine($@"
[global::System.Diagnostics.Conditional(""ENABLE_COLLECTION_CHECKS"")]
private void RangeCheck(int index)
{{
if (index < 0 || index >= {typeInfo.Row}) if (index < 0 || index >= {typeInfo.Row})
{{ {{
throw new global::System.ArgumentOutOfRangeException(nameof(index), $""Index {{index}} is out of range of '{typeInfo.TypeName}'""); throw new global::System.ArgumentOutOfRangeException(nameof(index), $""Index {{index}} is out of range of '{typeInfo.TypeName}'"");
}} }}
#endif
return ref (({typeInfo.ComponentTypeFullName}*)global::System.Runtime.CompilerServices.Unsafe.AsPointer(ref this))[index];
}}
}}"); }}");
} }
@@ -295,28 +299,10 @@ namespace Misaki.HighPerformance.Mathematics.CodeGen.Generators
EndRegion(); EndRegion();
} }
private void GenerateUnsafeMethod()
{
var componentType = typeInfo.ComponentTypeSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
StartRegion("Unsafe Methods");
sourceBuilder.AppendLine($@"
{INLINE_METHOD_ATTRIBUTE}
public unsafe {componentType}* AsPointer()
{{
return ({componentType}*)global::System.Runtime.CompilerServices.Unsafe.AsPointer(ref this);
}}
{INLINE_METHOD_ATTRIBUTE}
public unsafe global::System.Span<{componentType}> AsSpan()
{{
return new global::System.Span<{componentType}>(AsPointer(), {typeInfo.Row});
}}");
}
private void GenerateOverrideMethod() private void GenerateOverrideMethod()
{ {
StartRegion("Override Methods");
var typeName = typeInfo.TypeSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); var typeName = typeInfo.TypeSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
var componentType = typeInfo.ComponentTypeSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); var componentType = typeInfo.ComponentTypeSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
var components = s_vectorComponents.Take(typeInfo.Row).ToArray(); var components = s_vectorComponents.Take(typeInfo.Row).ToArray();
@@ -715,6 +701,7 @@ namespace Misaki.HighPerformance.Mathematics.CodeGen.Generators
var modifier = canSet ? "public" : "public readonly"; var modifier = canSet ? "public" : "public readonly";
sourceBuilder.Append($@" sourceBuilder.Append($@"
[global::System.Text.Json.Serialization.JsonIgnore]
{modifier} {targetStruct} {property} {modifier} {targetStruct} {property}
{{ {{
{INLINE_METHOD_ATTRIBUTE} {INLINE_METHOD_ATTRIBUTE}

View File

@@ -6,7 +6,7 @@
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks> <AllowUnsafeBlocks>True</AllowUnsafeBlocks>
<Authors>Misaki</Authors> <Authors>Misaki</Authors>
<AssemblyVersion>1.3.0</AssemblyVersion> <AssemblyVersion>1.3.1</AssemblyVersion>
<Version>$(AssemblyVersion)</Version> <Version>$(AssemblyVersion)</Version>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild> <GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<PackageProjectUrl>https://git.personalnas.com/Misaki/Misaki.HighPerformance.git</PackageProjectUrl> <PackageProjectUrl>https://git.personalnas.com/Misaki/Misaki.HighPerformance.git</PackageProjectUrl>

View File

@@ -2,58 +2,18 @@
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
using Misaki.HighPerformance.Mathematics; using Misaki.HighPerformance.Mathematics;
using Misaki.HighPerformance.Test.Jobs;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.Intrinsics;
namespace Misaki.HighPerformance.Test.Benchmark; namespace Misaki.HighPerformance.Test.Benchmark;
public unsafe class MathematicsBenchmark public class MathematicsBenchmark
{ {
public struct f2
{
public float x;
public float y;
public f2(float x, float y)
{
//this = Asf2(Vector128.Create(x, y, 0, 0));
this.x = x;
this.y = y;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector128<float> AsVector128Unsafe(f2 value)
{
return Vector128.Create(value.x, value.y, 0, 0);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static f2 Asf2(Vector128<float> value)
{
//f2 result;
//result.x = value.GetElement(0);
//result.y = value.GetElement(1);
//return result;
ref byte address = ref Unsafe.As<Vector128<float>, byte>(ref value);
return Unsafe.ReadUnaligned<f2>(ref address);
}
public static f2 operator +(f2 lhs, f2 rhs)
{
//return Asf2(AsVector128Unsafe(lhs) + AsVector128Unsafe(rhs));
return new f2(lhs.x + rhs.x, lhs.y + rhs.y);
}
}
#if VECTOR_BENCHMARK #if VECTOR_BENCHMARK
private Vector2 _v2a = new Vector2(1, 2); private Vector2 _v2a = new Vector2(1, 2);
private Vector2 _v2b = new Vector2(3, 4); private Vector2 _v2b = new Vector2(3, 4);
private f2 _f2a = new f2(1, 2); private float2 _f2a = new float2(1, 2);
private f2 _f2b = new f2(3, 4); private float2 _f2b = new float2(3, 4);
[Benchmark] [Benchmark]
public Vector2 VectorAdd() public Vector2 VectorAdd()
@@ -69,9 +29,9 @@ public unsafe class MathematicsBenchmark
} }
[Benchmark] [Benchmark]
public f2 f2Add() public float2 float2Add()
{ {
var v = new f2(0, 0); var v = new float2(0, 0);
for (var i = 0; i < 10; i++) for (var i = 0; i < 10; i++)
{ {

View File

@@ -41,7 +41,7 @@ public class ParallelNoiseBenchmark
height = _HEIGHT height = _HEIGHT
}; };
var handle = _jobScheduler.ScheduleParallel(ref job, _LENGTH, 64, -1); var handle = _jobScheduler.ScheduleParallel(ref job, _LENGTH, 64, -1, JobHandle.Invalid);
_jobScheduler.WaitComplete(handle); _jobScheduler.WaitComplete(handle);
} }

View File

@@ -9,7 +9,9 @@
<AllowUnsafeBlocks>True</AllowUnsafeBlocks> <AllowUnsafeBlocks>True</AllowUnsafeBlocks>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DefineConstants>$(DefineConstants);ENABLE_COLLECTION_CHECKS</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'" />

View File

@@ -1,28 +1,6 @@
//var threadCount = 8; using Misaki.HighPerformance.Image;
//var map = new ConcurrentSlotMap<int>();
//var barrier = new Barrier(threadCount); //BenchmarkDotNet.Running.BenchmarkRunner.Run<Misaki.HighPerformance.Test.Benchmark.ParallelNoiseBenchmark>();
//Parallel.For(0, threadCount, threadIndex =>
//{
// barrier.SignalAndWait();
// for (var i = 0; i < 1000; i++)
// {
// var id = map.Add(i + threadIndex * 1000, out var gen);
// if (i % 100 == 0)
// {
// map.Remove(id, gen);
// }
// }
//});
//Console.WriteLine($"Count should be {threadCount * 990}, actual: {map.Count}");
//using Misaki.HighPerformance.LowLevel;
using System.Runtime.Intrinsics;
BenchmarkDotNet.Running.BenchmarkRunner.Run<Misaki.HighPerformance.Test.Benchmark.MathematicsBenchmark>();
//using Misaki.HighPerformance.Collections; //using Misaki.HighPerformance.Collections;
//using Misaki.HighPerformance.LowLevel.Buffer; //using Misaki.HighPerformance.LowLevel.Buffer;
@@ -59,3 +37,12 @@ BenchmarkDotNet.Running.BenchmarkRunner.Run<Misaki.HighPerformance.Test.Benchmar
// 8, 7, 6, 5, // 8, 7, 6, 5,
// 4, 3, 2, 1); // 4, 3, 2, 1);
//Console.WriteLine(Matrix4x4.Multiply(ma, mb)); //Console.WriteLine(Matrix4x4.Multiply(ma, mb));
const string _IMAGE_PATH = "C:/Users/Misaki/Downloads/Im/119683453_p2.jpg";
using var stream = File.OpenRead(_IMAGE_PATH);
var imageInfo = ImageInfo.FromStream(stream);
using var image = ImageResult.FromStream(stream);
Console.WriteLine($"{imageInfo.Width}x{imageInfo.Height} {imageInfo.ColorComponents}");

View File

@@ -1,12 +1,13 @@
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
namespace Misaki.HighPerformance.Buffer namespace Misaki.HighPerformance.Buffer
{ {
public class ObjectPool<T> : IDisposable where T : class public class ObjectPool<T> : IDisposable
where T : class
{ {
private readonly Func<T> _factory; private readonly Func<T> _factory;
private readonly ConcurrentQueue<T> _objects = new(); private readonly ConcurrentQueue<T> _pool = new();
private bool _disposed; private bool _disposed;
@@ -18,7 +19,6 @@ namespace Misaki.HighPerformance.Buffer
public uint MaxSize public uint MaxSize
{ {
get; get;
private set;
} }
public ObjectPool(Func<T> factory, uint initialSize = uint.MinValue, uint maxSize = uint.MaxValue) public ObjectPool(Func<T> factory, uint initialSize = uint.MinValue, uint maxSize = uint.MaxValue)
@@ -32,7 +32,7 @@ namespace Misaki.HighPerformance.Buffer
{ {
for (var i = 0; i < initialSize; i++) for (var i = 0; i < initialSize; i++)
{ {
_objects.Enqueue(_factory()); _pool.Enqueue(_factory());
} }
} }
} }
@@ -45,21 +45,24 @@ namespace Misaki.HighPerformance.Buffer
public T Rent() public T Rent()
{ {
ObjectDisposedException.ThrowIf(_disposed, this); ObjectDisposedException.ThrowIf(_disposed, this);
if (_objects.TryDequeue(out var obj)) if (_pool.TryDequeue(out var obj))
{ {
return obj; return obj;
} }
return _factory(); var newInstance = _factory();
_pool.Enqueue(newInstance);
return newInstance;
} }
public bool TryRent([MaybeNullWhen(false)] out T obj) public bool TryRent([MaybeNullWhen(false)] out T obj)
{ {
ObjectDisposedException.ThrowIf(_disposed, this); ObjectDisposedException.ThrowIf(_disposed, this);
if (!_objects.IsEmpty) if (_pool.TryDequeue(out obj))
{ {
return _objects.TryDequeue(out obj); return true;
} }
obj = null; obj = null;
@@ -70,15 +73,15 @@ namespace Misaki.HighPerformance.Buffer
{ {
ObjectDisposedException.ThrowIf(_disposed, this); ObjectDisposedException.ThrowIf(_disposed, this);
if (_objects.Count < MaxSize) if (_pool.Count < MaxSize)
{ {
_objects.Enqueue(obj); _pool.Enqueue(obj);
} }
} }
public void Reset() public void Reset()
{ {
foreach (var obj in _objects) foreach (var obj in _pool)
{ {
if (obj is IDisposable disposable) if (obj is IDisposable disposable)
{ {
@@ -86,7 +89,7 @@ namespace Misaki.HighPerformance.Buffer
} }
} }
_objects.Clear(); _pool.Clear();
GC.Collect(); GC.Collect();
} }

View File

@@ -59,7 +59,7 @@ public class ConcurrentSlotMap<T> : IEnumerable<T>
// For lock-free resizing // For lock-free resizing
private int _isResizing; private int _isResizing;
public int Count => Volatile.Read(ref _count) - 1; public int Count => Volatile.Read(ref _count);
public int Capacity => Volatile.Read(ref _capacity); public int Capacity => Volatile.Read(ref _capacity);
public IEnumerator<T> GetEnumerator() => new Enumerator(this); public IEnumerator<T> GetEnumerator() => new Enumerator(this);
@@ -75,8 +75,6 @@ public class ConcurrentSlotMap<T> : IEnumerable<T>
_data = new SlotEntry[initialCapacity]; _data = new SlotEntry[initialCapacity];
_freeSlots = new(); _freeSlots = new();
Add(default!, out _);
} }
[MethodImpl(MethodImplOptions.NoInlining)] [MethodImpl(MethodImplOptions.NoInlining)]
@@ -211,7 +209,7 @@ public class ConcurrentSlotMap<T> : IEnumerable<T>
public bool Contains(int slotIndex, int generation) public bool Contains(int slotIndex, int generation)
{ {
if (slotIndex <= 0 || slotIndex >= Volatile.Read(ref _capacity)) if (slotIndex < 0 || slotIndex >= Volatile.Read(ref _capacity))
{ {
return false; return false;
} }

View File

@@ -63,8 +63,6 @@ public class SlotMap<T> : IEnumerable<T>
_generations = new int[initialCapacity]; _generations = new int[initialCapacity];
_isOccupiedBits = new BitArray(initialCapacity); _isOccupiedBits = new BitArray(initialCapacity);
_freeSlots = new(initialCapacity); _freeSlots = new(initialCapacity);
Add(default!, out _);
} }
private void Resize() private void Resize()
@@ -108,7 +106,7 @@ public class SlotMap<T> : IEnumerable<T>
public bool Contains(int slotIndex, int generation) public bool Contains(int slotIndex, int generation)
{ {
if (slotIndex <= 0 || slotIndex >= Volatile.Read(ref _capacity)) if (slotIndex < 0 || slotIndex >= Volatile.Read(ref _capacity))
{ {
return false; return false;
} }

View File

@@ -55,7 +55,7 @@ public class SparseSet<T> : IEnumerable<T>
private int _nextId; // Next available sparse index private int _nextId; // Next available sparse index
private int _capacity; private int _capacity;
public int Count => _count - 1; public int Count => _count;
public int Capacity => _capacity; public int Capacity => _capacity;
public Enumerator GetEnumerator() => new(this); public Enumerator GetEnumerator() => new(this);
@@ -83,8 +83,6 @@ public class SparseSet<T> : IEnumerable<T>
_count = 0; _count = 0;
_nextId = 0; _nextId = 0;
_capacity = capacity; _capacity = capacity;
Add(default!, out _); // Reserve index 0
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -178,7 +176,7 @@ public class SparseSet<T> : IEnumerable<T>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Contains(int sparseIndex, int generation) public bool Contains(int sparseIndex, int generation)
{ {
if (sparseIndex <= 0 || sparseIndex >= _sparse.Length) if (sparseIndex < 0 || sparseIndex >= _sparse.Length)
{ {
return false; return false;
} }

View File

@@ -6,7 +6,7 @@
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks> <AllowUnsafeBlocks>True</AllowUnsafeBlocks>
<Authors>Misaki</Authors> <Authors>Misaki</Authors>
<AssemblyVersion>1.0.2</AssemblyVersion> <AssemblyVersion>1.0.3</AssemblyVersion>
<Version>$(AssemblyVersion)</Version> <Version>$(AssemblyVersion)</Version>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild> <GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<PackageProjectUrl>https://git.personalnas.com/Misaki/Misaki.HighPerformance.git</PackageProjectUrl> <PackageProjectUrl>https://git.personalnas.com/Misaki/Misaki.HighPerformance.git</PackageProjectUrl>

View File

@@ -25,14 +25,14 @@ public static class CollectionUtility
/// <typeparam name="T">The type of elements in the list.</typeparam> /// <typeparam name="T">The type of elements in the list.</typeparam>
/// <param name="list">The list from which to remove the element. Cannot be null.</param> /// <param name="list">The list from which to remove the element. Cannot be null.</param>
/// <param name="index">The zero-based index of the element to remove. Must be within the bounds of the list.</param> /// <param name="index">The zero-based index of the element to remove. Must be within the bounds of the list.</param>
/// <returns>The modified list after the element has been removed.</returns>
/// <exception cref="ArgumentOutOfRangeException">Thrown if index is less than 0 or greater than or equal to the number of elements in the list.</exception> /// <exception cref="ArgumentOutOfRangeException">Thrown if index is less than 0 or greater than or equal to the number of elements in the list.</exception>
public static List<T> RemoveAndSwapBack<T>(this List<T> list, int index) /// <returns>True if the element was successfully removed; otherwise, false.</returns>
public static bool RemoveAndSwapBack<T>(this List<T> list, int index)
{ {
var lastIndex = list.Count - 1; var lastIndex = list.Count - 1;
if (index < 0 || index > lastIndex) if (index < 0 || index > lastIndex)
{ {
throw new ArgumentOutOfRangeException(nameof(index)); return false;
} }
if (index != lastIndex) if (index != lastIndex)
@@ -41,6 +41,6 @@ public static class CollectionUtility
} }
list.RemoveAt(lastIndex); list.RemoveAt(lastIndex);
return list; return true;
} }
} }