Compare commits
39 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 432446689c | |||
| 1afdc8ffb9 | |||
| 2f418866a4 | |||
| 776719648e | |||
| b7983c96b2 | |||
| cb75b7c244 | |||
| 6ffcd38cbc | |||
| 09b5238b24 | |||
| 7157db6bd0 | |||
| df61cdc5aa | |||
| 50c37e58fc | |||
| 8e8c86b2c1 | |||
| ff83cb20f9 | |||
| 3801bc29f8 | |||
| bcd82900c7 | |||
| e759efacf6 | |||
| d9a9e3962f | |||
| f976af3833 | |||
| 9b21bb837b | |||
| e7d7f5f826 | |||
| e0e57b53f4 | |||
| f135a65c96 | |||
| 846e09054d | |||
| 722d38be5c | |||
| 31f5e3938c | |||
| 03ac61a4d2 | |||
| 9dcd5e56fa | |||
| 018f195000 | |||
| 13ea2941da | |||
| 0c46adc68b | |||
| e7c90af3ed | |||
| 112c1065b0 | |||
| ede16bc468 | |||
| 6ba91c8808 | |||
| ecab13f766 | |||
| 50c2ec73e7 | |||
|
cb896f9cf3
|
|||
| 8c3e723cac | |||
| 7f8fad146a |
@@ -1,19 +1,22 @@
|
|||||||
name: Build
|
name: Build
|
||||||
run-name: Building docker container.
|
run-name: Building with docker container.
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
push:
|
push:
|
||||||
branches: stable
|
tags:
|
||||||
|
- '*'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container:
|
container:
|
||||||
image: git.mixedup.dev/quik/docker-cross-compiler
|
image: git.mixedup.dev/refuel/docker-cross-compiler
|
||||||
env:
|
env:
|
||||||
QUIK_API_KEY: "${{secrets.QUIK_API_KEY}}"
|
REFUEL_API_KEY: "${{secrets.REFUEL_API_KEY}}"
|
||||||
|
NUGET_USER_NAME: "${{vars.NUGET_USER_NAME}}"
|
||||||
|
NUGET_INDEX: "${{vars.NUGET_INDEX}}"
|
||||||
volumes:
|
volumes:
|
||||||
- ${{ gitea.workspace }}:/home/quik/src
|
- ${{ gitea.workspace }}:/home/refuel/src
|
||||||
steps:
|
steps:
|
||||||
- name: Check repository out.
|
- name: Check repository out.
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
|||||||
+3
-3
@@ -1,6 +1,6 @@
|
|||||||
[submodule "stb"]
|
[submodule "stb"]
|
||||||
path = stb
|
path = stb
|
||||||
url = https://github.com/nothings/stb.git
|
url = https://github.com/nothings/stb.git
|
||||||
[submodule "Quik.Common"]
|
[submodule "docker-cross-compiler"]
|
||||||
path = Quik.Common
|
path = docker-cross-compiler
|
||||||
url = https://git.mixedup.dev/QUIK/Quik.Common.git
|
url = https://git.mixedup.dev/ReFuel/docker-cross-compiler
|
||||||
|
|||||||
+47
@@ -0,0 +1,47 @@
|
|||||||
|
This package uses 3rd-party software from the following vendors:
|
||||||
|
|
||||||
|
-----
|
||||||
|
* nothings/stb_image.h
|
||||||
|
* nothigns/stb_image_writer.h
|
||||||
|
|
||||||
|
https://raw.githubusercontent.com/nothings/stb/master/LICENSE
|
||||||
|
|
||||||
|
This software is available under 2 licenses -- choose whichever you prefer.
|
||||||
|
|
||||||
|
ALTERNATIVE A - MIT License
|
||||||
|
Copyright (c) 2017 Sean Barrett
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
of the Software, and to permit persons to whom the Software is furnished to do
|
||||||
|
so, subject to the following conditions:
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
||||||
|
ALTERNATIVE B - Public Domain (www.unlicense.org)
|
||||||
|
This is free and unencumbered software released into the public domain.
|
||||||
|
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
|
||||||
|
software, either in source code form or as a compiled binary, for any purpose,
|
||||||
|
commercial or non-commercial, and by any means.
|
||||||
|
In jurisdictions that recognize copyright laws, the author or authors of this
|
||||||
|
software dedicate any and all copyright interest in the software to the public
|
||||||
|
domain. We make this dedication for the benefit of the public at large and to
|
||||||
|
the detriment of our heirs and successors. We intend this dedication to be an
|
||||||
|
overt act of relinquishment in perpetuity of all present and future rights to
|
||||||
|
this software under copyright law.
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
-----
|
||||||
+4
-3
@@ -1,12 +1,13 @@
|
|||||||
cmake_minimum_required(VERSION 3.0)
|
cmake_minimum_required(VERSION 3.5)
|
||||||
|
|
||||||
project(quik_stbi LANGUAGES C VERSION 1.0)
|
project(rf_stbi LANGUAGES C VERSION 1.0)
|
||||||
|
|
||||||
if (NOT ("${CMAKE_SYSTEM_NAME}" STREQUAL "Darwin"))
|
if (NOT ("${CMAKE_SYSTEM_NAME}" STREQUAL "Darwin"))
|
||||||
add_compile_options(-static-libgcc -static-libstdc++ -fno-exceptions)
|
add_compile_options(-static-libgcc -static-libstdc++ -fno-exceptions)
|
||||||
endif()
|
endif()
|
||||||
|
add_compile_options(-O3)
|
||||||
|
|
||||||
add_library(stbi SHARED "quik_stbi.c")
|
add_library(stbi SHARED "rf_stbi.c")
|
||||||
install(
|
install(
|
||||||
TARGETS stbi
|
TARGETS stbi
|
||||||
RUNTIME DESTINATION .
|
RUNTIME DESTINATION .
|
||||||
|
|||||||
+19
@@ -0,0 +1,19 @@
|
|||||||
|
Copyright (c) 2024 ReFuel.StbImage - H. Utku Maden
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
of the Software, and to permit persons to whom the Software is furnished to do
|
||||||
|
so, subject to the following conditions:
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
||||||
|
Also see 3RDPARTY.md
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace Quik.Stb.Image
|
|
||||||
{
|
|
||||||
[AttributeUsage(System.AttributeTargets.All, Inherited = false, AllowMultiple = true)]
|
|
||||||
internal sealed class NativeTypeNameAttribute : System.Attribute
|
|
||||||
{
|
|
||||||
public NativeTypeNameAttribute(string typename)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-1
Submodule Quik.Common deleted from fdfac9d951
@@ -1,71 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
|
||||||
<Nullable>disable</Nullable>
|
|
||||||
<LangVersion>7.3</LangVersion>
|
|
||||||
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
|
||||||
<RuntimeIdentifiers>linux-arm;linux-arm64;linux-x64;win-x86;win-x64;osx-arm64;osx-x64</RuntimeIdentifiers>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<!-- Nuget Properties. -->
|
|
||||||
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
|
||||||
<PackageId>Quik.StbImage</PackageId>
|
|
||||||
<Version>1.1.3</Version>
|
|
||||||
<Authors>STBI Authors, H. Utku Maden</Authors>
|
|
||||||
<Description>
|
|
||||||
A C# wrapper for the ubiquitous Stb Image library.
|
|
||||||
</Description>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Because the .net build system is garbage of the purest quality, I need
|
|
||||||
to specify each runtime directory by itself or it won't be picked up as a
|
|
||||||
native specific to each platform and won't be included in the deps.json file
|
|
||||||
that determines a lot of load paths for projects.
|
|
||||||
-->
|
|
||||||
<ItemGroup>
|
|
||||||
<Content Include="runtimes/linux-arm/native/*.so">
|
|
||||||
<PackagePath>runtimes/linux-arm/native/</PackagePath>
|
|
||||||
<Pack>true</Pack>
|
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
|
||||||
</Content>
|
|
||||||
<Content Include="runtimes/linux-arm64/native/*.so">
|
|
||||||
<PackagePath>runtimes/linux-arm64/native/</PackagePath>
|
|
||||||
<Pack>true</Pack>
|
|
||||||
<CopyToOutputDirectory>PreserveNewest/</CopyToOutputDirectory>
|
|
||||||
</Content>
|
|
||||||
<Content Include="runtimes/linux-x64/native/*.so">
|
|
||||||
<PackagePath>runtimes/linux-x64/native/</PackagePath>
|
|
||||||
<Pack>true</Pack>
|
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
|
||||||
</Content>
|
|
||||||
<Content Include="runtimes/linux-x86/native/*.so">
|
|
||||||
<PackagePath>runtimes/linux-x86/native/</PackagePath>
|
|
||||||
<Pack>true</Pack>
|
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
|
||||||
</Content>
|
|
||||||
<Content Include="runtimes/win-x64/native/*.dll">
|
|
||||||
<PackagePath>runtimes/win-x64/native/</PackagePath>
|
|
||||||
<Pack>true</Pack>
|
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
|
||||||
</Content>
|
|
||||||
<Content Include="runtimes/win-x86/native/*.dll">
|
|
||||||
<PackagePath>runtimes/win-x86/native/</PackagePath>
|
|
||||||
<Pack>true</Pack>
|
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
|
||||||
</Content>
|
|
||||||
<Content Include="runtimes/osx-x64/native/*.dylib">
|
|
||||||
<PackagePath>runtimes/osx-x64/native/</PackagePath>
|
|
||||||
<Pack>true</Pack>
|
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
|
||||||
</Content>
|
|
||||||
<Content Include="runtimes/osx-arm64/native/*.dylib">
|
|
||||||
<PackagePath>runtimes/osx-arm64/native/</PackagePath>
|
|
||||||
<Pack>true</Pack>
|
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
|
||||||
</Content>
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
|
||||||
# Visual Studio Version 17
|
|
||||||
VisualStudioVersion = 17.5.002.0
|
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Quik.StbImage", "Quik.StbImage.csproj", "{C808B4BC-C3AF-4682-8EDA-EAAC780800C3}"
|
|
||||||
EndProject
|
|
||||||
Global
|
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
|
||||||
Debug|Any CPU = Debug|Any CPU
|
|
||||||
Release|Any CPU = Release|Any CPU
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
|
||||||
{C808B4BC-C3AF-4682-8EDA-EAAC780800C3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{C808B4BC-C3AF-4682-8EDA-EAAC780800C3}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{C808B4BC-C3AF-4682-8EDA-EAAC780800C3}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{C808B4BC-C3AF-4682-8EDA-EAAC780800C3}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
|
||||||
HideSolutionNode = FALSE
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
|
||||||
SolutionGuid = {B04E5FBA-41E6-4B0B-BC31-F996BE353F5A}
|
|
||||||
EndGlobalSection
|
|
||||||
EndGlobal
|
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
ReFuel.StbImage
|
||||||
|
=============
|
||||||
|
ReFuel.StbImage is a binary distribution of the popular stb_image.h single header
|
||||||
|
library. Existing packages were either bad source ports, had odd wrappers or
|
||||||
|
weren't up to date.
|
||||||
|
|
||||||
|
Originally intended for my own purposes, however feel free to view the code at
|
||||||
|
https://git.mixedup.dev/ReFuel/ReFuel.StbImage, and shoot me patch files through
|
||||||
|
email.
|
||||||
@@ -0,0 +1,209 @@
|
|||||||
|
/*
|
||||||
|
* ReFuel.StbImage.Viewer - A simple image viewer demo for ReFuel.StbImage.
|
||||||
|
* ------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* Pass an image file as path (or drag and drop over the executable or window) to view
|
||||||
|
* of the file formats stb_image supports. Otherwise the default embedded image will be
|
||||||
|
* shown.
|
||||||
|
*
|
||||||
|
* The demo uses OpenGL2.1 for brevity sake - I did not feel like writing a shader program
|
||||||
|
* for this demo. It is very easy to port this demo to modern OpenGL if desired. Just
|
||||||
|
* replace the FFP calls with the equivalent programmable pipeline calls (glVertexAttribPointer,
|
||||||
|
* glUseShader and its friends).
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using OpenTK.Graphics.OpenGL;
|
||||||
|
using OpenTK.Mathematics;
|
||||||
|
using OpenTK.Windowing.Common;
|
||||||
|
using OpenTK.Windowing.Desktop;
|
||||||
|
using ReFuel.Stb;
|
||||||
|
|
||||||
|
NativeWindow window = new NativeWindow(new NativeWindowSettings()
|
||||||
|
{
|
||||||
|
Profile = ContextProfile.Any,
|
||||||
|
APIVersion = new Version(2, 1),
|
||||||
|
Title = "ReFuel StbImage Viewer",
|
||||||
|
AutoLoadBindings = true,
|
||||||
|
Flags = ContextFlags.Default,
|
||||||
|
});
|
||||||
|
|
||||||
|
bool quit = false;
|
||||||
|
int texture = 0;
|
||||||
|
int imageWidth = 0, imageHeight = 0;
|
||||||
|
|
||||||
|
// This flag is important for OpenGL users, as texture coordinate systems
|
||||||
|
// are the opposite of what most image formats use. Y is up not down.
|
||||||
|
// Does not matter for DX, for example.
|
||||||
|
StbImage.FlipVerticallyOnLoad = true;
|
||||||
|
|
||||||
|
Vertex[] vertices = new Vertex[]
|
||||||
|
{
|
||||||
|
new Vertex(-1, -1, 0, 0),
|
||||||
|
new Vertex(1, -1, 1, 0),
|
||||||
|
new Vertex(1, 1, 1, 1),
|
||||||
|
new Vertex(-1, -1, 0, 0),
|
||||||
|
new Vertex(1, 1, 1, 1),
|
||||||
|
new Vertex(-1, 1, 0, 1),
|
||||||
|
};
|
||||||
|
|
||||||
|
LoadImage(args.Length > 0 ? args[0] : null);
|
||||||
|
|
||||||
|
GL.ClearColor(Color4.Black);
|
||||||
|
GL.Enable(EnableCap.Texture2D);
|
||||||
|
GL.Enable(EnableCap.Blend);
|
||||||
|
GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha); // conventional blending function.
|
||||||
|
GL.EnableClientState(ArrayCap.VertexArray);
|
||||||
|
GL.EnableClientState(ArrayCap.TextureCoordArray);
|
||||||
|
GL.EnableClientState(ArrayCap.ColorArray);
|
||||||
|
|
||||||
|
window.Closing += (_) => quit = true;
|
||||||
|
window.FramebufferResize += (_) => {
|
||||||
|
ResizeImage();
|
||||||
|
Paint();
|
||||||
|
};
|
||||||
|
window.FileDrop += (args) =>
|
||||||
|
LoadImage(
|
||||||
|
args.FileNames
|
||||||
|
.Select(x => new FileInfo(x))
|
||||||
|
.FirstOrDefault(x => x.Exists)?.FullName);
|
||||||
|
|
||||||
|
window.WindowState = WindowState.Normal;
|
||||||
|
while (!quit)
|
||||||
|
{
|
||||||
|
NativeWindow.ProcessWindowEvents(true);
|
||||||
|
Paint();
|
||||||
|
};
|
||||||
|
|
||||||
|
void Paint()
|
||||||
|
{
|
||||||
|
GL.Clear(ClearBufferMask.ColorBufferBit);
|
||||||
|
GL.Viewport(0, 0, window.FramebufferSize.X, window.FramebufferSize.Y);
|
||||||
|
GL.BindTexture(TextureTarget.Texture2D, texture);
|
||||||
|
|
||||||
|
unsafe
|
||||||
|
{
|
||||||
|
fixed (Vertex* pvert = vertices)
|
||||||
|
{
|
||||||
|
// We have to do it this way because the garbage collector may
|
||||||
|
// move the vertex data at any time. The OpenGL client will
|
||||||
|
// handle streaming the data at the glDrawArrays call, unlike
|
||||||
|
// the modern method.
|
||||||
|
GL.VertexPointer(2, VertexPointerType.Float, 32, (nint)(&pvert->X));
|
||||||
|
GL.TexCoordPointer(2, TexCoordPointerType.Float, 32, (nint)(&pvert->U));
|
||||||
|
GL.ColorPointer(4, ColorPointerType.Float, 32, (nint)(&pvert->Color));
|
||||||
|
GL.DrawArrays(PrimitiveType.Triangles, 0, vertices.Length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.Context.SwapBuffers();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResizeImage()
|
||||||
|
{
|
||||||
|
// Regenerates the vertex positions to maintain the aspect ratio and fill the window.
|
||||||
|
float windowAspect = (float)window.FramebufferSize.X / window.FramebufferSize.Y;
|
||||||
|
float imageAspect = (float)imageWidth / imageHeight;
|
||||||
|
float ratio = imageAspect / windowAspect;
|
||||||
|
|
||||||
|
float widthNDC, heightNDC;
|
||||||
|
if (ratio > 1)
|
||||||
|
{
|
||||||
|
widthNDC = 2.0f;
|
||||||
|
heightNDC = 2.0f / ratio;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
heightNDC = 2.0f;
|
||||||
|
widthNDC = 2.0f * ratio;
|
||||||
|
}
|
||||||
|
|
||||||
|
vertices[0].X = -widthNDC / 2; vertices[0].Y = -heightNDC / 2;
|
||||||
|
vertices[1].X = widthNDC / 2; vertices[1].Y = -heightNDC / 2;
|
||||||
|
vertices[2].X = widthNDC / 2; vertices[2].Y = heightNDC / 2;
|
||||||
|
vertices[3].X = -widthNDC / 2; vertices[3].Y = -heightNDC / 2;
|
||||||
|
vertices[4].X = widthNDC / 2; vertices[4].Y = heightNDC / 2;
|
||||||
|
vertices[5].X = -widthNDC / 2; vertices[5].Y = heightNDC / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LoadImage(string? path)
|
||||||
|
{
|
||||||
|
// Load the given image, or the default if the path is null, or any other error happens.
|
||||||
|
StbImage? image = null;
|
||||||
|
if (path != null && File.Exists(path))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using Stream str = File.OpenRead(path);
|
||||||
|
StbImage.TryLoad(out image, str);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (image == null)
|
||||||
|
{
|
||||||
|
using Stream str = Assembly.GetExecutingAssembly().GetManifestResourceStream("default.png")!;
|
||||||
|
image = StbImage.Load(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Boilerplate code for creating a new OpenGL texture.
|
||||||
|
|
||||||
|
GL.DeleteTexture(texture);
|
||||||
|
texture = GL.GenTexture();
|
||||||
|
GL.BindTexture(TextureTarget.Texture2D, texture);
|
||||||
|
GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, image.Width, image.Height, 0, image.Format switch
|
||||||
|
{
|
||||||
|
StbiImageFormat.Grey => PixelFormat.Red,
|
||||||
|
StbiImageFormat.GreyAlpha => PixelFormat.Rg,
|
||||||
|
StbiImageFormat.Rgb => PixelFormat.Rgb,
|
||||||
|
StbiImageFormat.Rgba => PixelFormat.Rgba,
|
||||||
|
_ => throw new Exception()
|
||||||
|
}, image.IsFloat ? PixelType.Float : PixelType.UnsignedByte, image.ImagePointer);
|
||||||
|
|
||||||
|
// Generate mipmaps for better minification.
|
||||||
|
GL.GenerateMipmap(GenerateMipmapTarget.Texture2D);
|
||||||
|
// Enable them.
|
||||||
|
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.LinearMipmapLinear);
|
||||||
|
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
|
||||||
|
|
||||||
|
// Set texture wrap mode to clamp to border to prevent bleeding on the image edges.
|
||||||
|
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.ClampToBorder);
|
||||||
|
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.ClampToBorder);
|
||||||
|
|
||||||
|
// For R or RA format images, we need to set the swizzle mask.
|
||||||
|
switch (image.Format)
|
||||||
|
{
|
||||||
|
case StbiImageFormat.Grey:
|
||||||
|
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureSwizzleR, (int)TextureSwizzle.Red);
|
||||||
|
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureSwizzleG, (int)TextureSwizzle.Red);
|
||||||
|
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureSwizzleB, (int)TextureSwizzle.Red);
|
||||||
|
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureSwizzleA, (int)TextureSwizzle.One);
|
||||||
|
break;
|
||||||
|
case StbiImageFormat.GreyAlpha:
|
||||||
|
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureSwizzleR, (int)TextureSwizzle.Red);
|
||||||
|
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureSwizzleG, (int)TextureSwizzle.Red);
|
||||||
|
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureSwizzleA, (int)TextureSwizzle.Green);
|
||||||
|
// Yes the last channel is green, we uploaded the texture with the Rg color format.
|
||||||
|
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureSwizzleB, (int)TextureSwizzle.Red);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
imageWidth = image.Width;
|
||||||
|
imageHeight = image.Height;
|
||||||
|
ResizeImage();
|
||||||
|
|
||||||
|
image.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vertex struct for convenience. Padded to 32 bytes for memory alignment.
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = 32)]
|
||||||
|
struct Vertex(float x, float y, float u, float v)
|
||||||
|
{
|
||||||
|
public float X = x, Y = y, U = u, V = v;
|
||||||
|
public Color4 Color = Color4.White;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<!-- <ProjectReference Include="..\ReFuel.StbImage\ReFuel.StbImage.csproj" /> -->
|
||||||
|
<PackageReference Include="ReFuel.StbImage" Version="2.1.0"/>
|
||||||
|
<PackageReference Include="OpenTK" Version="4.9.4" />
|
||||||
|
<EmbeddedResource Include="../rf_stbimage.png" LogicalName="default.png"/>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<Import Project="../etc/ReFuel.StbImage.Common.props"/>
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<!-- Nuget Properties. -->
|
||||||
|
<PackageId>ReFuel.StbImage.redis.linux.arm</PackageId>
|
||||||
|
<Version>1.0.0-rc.1</Version>
|
||||||
|
<Description>
|
||||||
|
StbImage distribution for Linux ARM.
|
||||||
|
</Description>
|
||||||
|
<PackageReleaseNotes>* Initial release.</PackageReleaseNotes>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include="runtimes/linux-arm/native/*.so">
|
||||||
|
<PackagePath>runtimes/linux-arm/native/</PackagePath>
|
||||||
|
<Pack>true</Pack>
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<Import Project="../etc/ReFuel.StbImage.Common.props"/>
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<!-- Nuget Properties. -->
|
||||||
|
<PackageId>ReFuel.StbImage.redis.linux.arm64</PackageId>
|
||||||
|
<Version>1.0.0-rc.1</Version>
|
||||||
|
<Description>
|
||||||
|
StbImage distribution for Linux ARM64.
|
||||||
|
</Description>
|
||||||
|
<PackageReleaseNotes>* Initial release.</PackageReleaseNotes>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include="runtimes/linux-arm64/native/*.so">
|
||||||
|
<PackagePath>runtimes/linux-arm64/native/</PackagePath>
|
||||||
|
<Pack>true</Pack>
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<Import Project="../etc/ReFuel.StbImage.Common.props"/>
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<!-- Nuget Properties. -->
|
||||||
|
<PackageId>ReFuel.StbImage.redis.linux.x64</PackageId>
|
||||||
|
<Version>1.0.0-rc.1</Version>
|
||||||
|
<Description>
|
||||||
|
StbImage distribution for Linux x64.
|
||||||
|
</Description>
|
||||||
|
<PackageReleaseNotes>* Initial release.</PackageReleaseNotes>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include="runtimes/linux-x64/native/*.so">
|
||||||
|
<PackagePath>runtimes/linux-x64/native/</PackagePath>
|
||||||
|
<Pack>true</Pack>
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<Import Project="../etc/ReFuel.StbImage.Common.props"/>
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<!-- Nuget Properties. -->
|
||||||
|
<PackageId>ReFuel.StbImage.redis.osx.arm64</PackageId>
|
||||||
|
<Version>1.0.0-rc.1</Version>
|
||||||
|
<Description>
|
||||||
|
StbImage distribution for OSX ARM64.
|
||||||
|
</Description>
|
||||||
|
<PackageReleaseNotes>* Initial release.</PackageReleaseNotes>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include="runtimes/osx-arm64/native/*.dylib">
|
||||||
|
<PackagePath>runtimes/osx-arm64/native/</PackagePath>
|
||||||
|
<Pack>true</Pack>
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<Import Project="../etc/ReFuel.StbImage.Common.props"/>
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<!-- Nuget Properties. -->
|
||||||
|
<PackageId>ReFuel.StbImage.redis.osx.x64</PackageId>
|
||||||
|
<Version>1.0.0-rc.1</Version>
|
||||||
|
<Description>
|
||||||
|
StbImage distribution for OSX x64.
|
||||||
|
</Description>
|
||||||
|
<PackageReleaseNotes>* Initial release.</PackageReleaseNotes>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include="runtimes/osx-x64/native/*.dylib">
|
||||||
|
<PackagePath>runtimes/osx-x64/native/</PackagePath>
|
||||||
|
<Pack>true</Pack>
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<Import Project="../etc/ReFuel.StbImage.Common.props"/>
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<!-- Nuget Properties. -->
|
||||||
|
<PackageId>ReFuel.StbImage.redis.win.x64</PackageId>
|
||||||
|
<Version>1.0.0-rc.1</Version>
|
||||||
|
<Description>
|
||||||
|
StbImage distribution for Windows x64.
|
||||||
|
</Description>
|
||||||
|
<PackageReleaseNotes>* Initial release.</PackageReleaseNotes>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include="runtimes/win-x64/native/*.dll">
|
||||||
|
<PackagePath>runtimes/win-x64/native/</PackagePath>
|
||||||
|
<Pack>true</Pack>
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<Import Project="../etc/ReFuel.StbImage.Common.props"/>
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<!-- Nuget Properties. -->
|
||||||
|
<PackageId>ReFuel.StbImage.redis.win.x86</PackageId>
|
||||||
|
<Version>1.0.0-rc.1</Version>
|
||||||
|
<Description>
|
||||||
|
StbImage distribution for Windows x86.
|
||||||
|
</Description>
|
||||||
|
<PackageReleaseNotes>* Initial release.</PackageReleaseNotes>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include="runtimes/win-x86/native/*.dll">
|
||||||
|
<PackagePath>runtimes/win-x86/native/</PackagePath>
|
||||||
|
<Pack>true</Pack>
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 17
|
||||||
|
VisualStudioVersion = 17.0.31903.59
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReFuel.StbImage", "ReFuel.StbImage\ReFuel.StbImage.csproj", "{EB001CC8-6821-4579-BD5D-27C56A3C121E}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReFuel.StbImage.Viewer", "ReFuel.StbImage.Viewer\ReFuel.StbImage.Viewer.csproj", "{4CFB8D5B-8CE9-46EE-A34B-0D61693BDE50}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Debug|x64 = Debug|x64
|
||||||
|
Debug|x86 = Debug|x86
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
Release|x64 = Release|x64
|
||||||
|
Release|x86 = Release|x86
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{EB001CC8-6821-4579-BD5D-27C56A3C121E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{EB001CC8-6821-4579-BD5D-27C56A3C121E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{EB001CC8-6821-4579-BD5D-27C56A3C121E}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{EB001CC8-6821-4579-BD5D-27C56A3C121E}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{EB001CC8-6821-4579-BD5D-27C56A3C121E}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{EB001CC8-6821-4579-BD5D-27C56A3C121E}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{EB001CC8-6821-4579-BD5D-27C56A3C121E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{EB001CC8-6821-4579-BD5D-27C56A3C121E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{EB001CC8-6821-4579-BD5D-27C56A3C121E}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{EB001CC8-6821-4579-BD5D-27C56A3C121E}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{EB001CC8-6821-4579-BD5D-27C56A3C121E}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{EB001CC8-6821-4579-BD5D-27C56A3C121E}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{4CFB8D5B-8CE9-46EE-A34B-0D61693BDE50}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{4CFB8D5B-8CE9-46EE-A34B-0D61693BDE50}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{4CFB8D5B-8CE9-46EE-A34B-0D61693BDE50}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{4CFB8D5B-8CE9-46EE-A34B-0D61693BDE50}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{4CFB8D5B-8CE9-46EE-A34B-0D61693BDE50}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{4CFB8D5B-8CE9-46EE-A34B-0D61693BDE50}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{4CFB8D5B-8CE9-46EE-A34B-0D61693BDE50}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{4CFB8D5B-8CE9-46EE-A34B-0D61693BDE50}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{4CFB8D5B-8CE9-46EE-A34B-0D61693BDE50}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{4CFB8D5B-8CE9-46EE-A34B-0D61693BDE50}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{4CFB8D5B-8CE9-46EE-A34B-0D61693BDE50}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{4CFB8D5B-8CE9-46EE-A34B-0D61693BDE50}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<Import Project="../etc/ReFuel.StbImage.Common.props"/>
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<!-- <TargetFrameworks>net6.0;net8.0;net10.0</TargetFrameworks> -->
|
||||||
|
<RuntimeIdentifiers>linux-arm;linux-arm64;linux-x64;win-x86;win-x64;osx-arm64;osx-x64</RuntimeIdentifiers>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<!-- Nuget Properties. -->
|
||||||
|
<PackageId>ReFuel.StbImage</PackageId>
|
||||||
|
<Version>2.1.1-rc.1</Version>
|
||||||
|
<Description>
|
||||||
|
A C# wrapper for the ubiquitous stb_image.h and stb_image_write.h library.
|
||||||
|
</Description>
|
||||||
|
<PackageReleaseNotes># 2.1.0 (ABI BRAKING)
|
||||||
|
* Fixed calling convention of unmanaged function pointers. (Thanks NogginBops!)
|
||||||
|
* Modified StbiStreamWrapper in order to fixed backing delegates of function pointers from being prematurely collected
|
||||||
|
by release mode JIT and the GC. StbiStreamWrapper.Callbacks is now a readonly field. (ABI BREAKING)
|
||||||
|
|
||||||
|
# 2.0.1
|
||||||
|
* Enabled optimizations across the board for native and managed assemblies.
|
||||||
|
|
||||||
|
# 2.0.0
|
||||||
|
* Exposed stbi_image_write.h
|
||||||
|
* Moved native function calls to ReFuel.Stb.Native</PackageReleaseNotes>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="ReFuel.StbImage.redis.linux.arm" Version="1.0.*"/>
|
||||||
|
<PackageReference Include="ReFuel.StbImage.redis.linux.arm64" Version="1.0.*"/>
|
||||||
|
<PackageReference Include="ReFuel.StbImage.redis.linux.x64" Version="1.0.*"/>
|
||||||
|
<PackageReference Include="ReFuel.StbImage.redis.osx.arm64" Version="1.0.*"/>
|
||||||
|
<PackageReference Include="ReFuel.StbImage.redis.osx.x64" Version="1.0.*"/>
|
||||||
|
<PackageReference Include="ReFuel.StbImage.redis.win.x64" Version="1.0.*"/>
|
||||||
|
<PackageReference Include="ReFuel.StbImage.redis.win.x86" Version="1.0.*"/>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,575 @@
|
|||||||
|
using ReFuel.Stb.Native;
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.IO;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace ReFuel.Stb
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A class that encompasses all features of stb_image.h in a safe way.
|
||||||
|
/// </summary>
|
||||||
|
public unsafe class StbImage : IDisposable
|
||||||
|
{
|
||||||
|
private bool isDisposed = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Pointer to the image.
|
||||||
|
/// </summary>
|
||||||
|
public IntPtr ImagePointer { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Width of the image.
|
||||||
|
/// </summary>
|
||||||
|
public int Width { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Height of the image.
|
||||||
|
/// </summary>
|
||||||
|
/// <value></value>
|
||||||
|
public int Height { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Internal image format.
|
||||||
|
/// </summary>
|
||||||
|
public StbiImageFormat Format { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// True if the image is a floating point image.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsFloat { get; }
|
||||||
|
|
||||||
|
private StbImage(IntPtr image, int x, int y, StbiImageFormat format, bool isFloat)
|
||||||
|
{
|
||||||
|
ImagePointer = image;
|
||||||
|
Width = x;
|
||||||
|
Height = y;
|
||||||
|
Format = format;
|
||||||
|
IsFloat = isFloat;
|
||||||
|
}
|
||||||
|
|
||||||
|
~StbImage()
|
||||||
|
{
|
||||||
|
Dispose(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get a safe span to the image pointer.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The pixel type.</typeparam>
|
||||||
|
/// <returns>A span to the image data.</returns>
|
||||||
|
/// <exception cref="Exception">The image uses an unexpected image format.</exception>
|
||||||
|
public ReadOnlySpan<T> AsSpan<T>() where T : unmanaged
|
||||||
|
{
|
||||||
|
int sz = Format switch
|
||||||
|
{
|
||||||
|
StbiImageFormat.Grey => 1,
|
||||||
|
StbiImageFormat.GreyAlpha => 2,
|
||||||
|
StbiImageFormat.Rgb => 3,
|
||||||
|
StbiImageFormat.Rgba => 4,
|
||||||
|
_ => throw new Exception("unknown image format")
|
||||||
|
} * (IsFloat ? sizeof(float) : sizeof(byte));
|
||||||
|
|
||||||
|
return new ReadOnlySpan<T>((T*)ImagePointer, Width * Height * sz / sizeof(T));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Write image to a PNG file.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dest">The destination stream.</param>
|
||||||
|
/// <remarks>
|
||||||
|
/// Incurs a conversion cost if the image format is not a byte format. Affected by non-thread safe global options.
|
||||||
|
/// </remarks>
|
||||||
|
public void WritePng(Stream dest) => WritePng(AsSpan<byte>(), Width, Height, Format, dest, isFloat: IsFloat);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Write image to a BMP file.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dest">The destination stream.</param>
|
||||||
|
/// <remarks>
|
||||||
|
/// Incurs a conversion cost if the image format is not a byte format. Affected by non-thread safe global options.
|
||||||
|
/// </remarks>
|
||||||
|
public void WriteBmp(Stream dest) => WriteBmp(AsSpan<byte>(), Width, Height, Format, dest, isFloat: IsFloat);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Write image to a TGA file.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dest">The destination stream.</param>
|
||||||
|
/// <remarks>
|
||||||
|
/// Incurs a conversion cost if the image format is not a byte format. Ignores alpha channel. Affected by non-thread safe global options.
|
||||||
|
/// </remarks>
|
||||||
|
public void WriteTga(Stream dest) => WriteTga(AsSpan<byte>(), Width, Height, Format, dest, isFloat: IsFloat);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Write image to a PNG file.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dest">The destination stream.</param>
|
||||||
|
/// <remarks>
|
||||||
|
/// Incurs a conversion cost if the image format is not a float format. Affected by non-thread safe global options.
|
||||||
|
/// </remarks>
|
||||||
|
public void WriteHdr(Stream dest) => WriteHdr(AsSpan<byte>(), Width, Height, Format, dest, isFloat: IsFloat);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Write image to a PNG file.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dest">The destination stream.</param>
|
||||||
|
/// <param name="quality">The JPEG quality factor.</param>
|
||||||
|
/// <remarks>
|
||||||
|
/// Incurs a conversion cost if the image format is not a byte format. Ignores alpha channel. Affected by non-thread safe global options.
|
||||||
|
/// </remarks>
|
||||||
|
public void WriteJpg(Stream dest, int quality = 90) => WriteJpg(AsSpan<byte>(), Width, Height, Format, dest, quality: quality, isFloat: IsFloat);
|
||||||
|
|
||||||
|
public void Dispose() => Dispose(true);
|
||||||
|
|
||||||
|
private void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (isDisposed) return;
|
||||||
|
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
Stbi.image_free(ImagePointer.ToPointer());
|
||||||
|
isDisposed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set to flip the y-axis of loaded images on load.
|
||||||
|
/// </summary>
|
||||||
|
public static bool FlipVerticallyOnLoad { set => Stbi.set_flip_vertically_on_load(value ? 1 : 0); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set to flip the y-axis of saved images.
|
||||||
|
/// </summary>
|
||||||
|
public static bool FlipVerticallyOnSave { set => Stbi.flip_vertically_on_write(value ? 1 : 0); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set to unpremultiply images on load.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// According to the stb_image documentation, only iPhone PNG images
|
||||||
|
/// can come with premultiplied alpha.
|
||||||
|
/// </remarks>
|
||||||
|
public static bool UnpremultiplyOnLoad { set => Stbi.set_unpremultiply_on_load(value ? 1 : 0); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Force a filter on PNG filter when saving.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// -1 for auto, 0 through 5 to pick a filter. Higher is more. Not thread safe.
|
||||||
|
/// </remarks>
|
||||||
|
public static int WriteForcePngFilter
|
||||||
|
{
|
||||||
|
get => Stbi.write_force_png_filter;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value < -1 || value > 5)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(value), "The PNG filter must be in the range 0 to 5, or -1 for auto.");
|
||||||
|
}
|
||||||
|
|
||||||
|
Stbi.write_force_png_filter = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Change the PNG compression level on save.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Higher is more. Defaults to 8. Not thread safe.
|
||||||
|
/// </remarks>
|
||||||
|
public static int WritePngCompressionLevel
|
||||||
|
{
|
||||||
|
get => Stbi.write_png_compression_level;
|
||||||
|
set => Stbi.write_png_compression_level = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Enable run length encoding on TGA images on save.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Not thread safe.
|
||||||
|
/// </remarks>
|
||||||
|
public static bool WriteTgaEnableRLE
|
||||||
|
{
|
||||||
|
get => Stbi.write_tga_with_rle != 0;
|
||||||
|
set => Stbi.write_tga_with_rle = value ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Try loading an image, without raising exceptions.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="image">The resulting image.</param>
|
||||||
|
/// <param name="stream">Source stream.</param>
|
||||||
|
/// <param name="format">The desired image format.</param>
|
||||||
|
/// <param name="asFloat">The buffer uses floating point pixels if true.</param>
|
||||||
|
/// <returns>True on success.</returns>
|
||||||
|
public static bool TryLoad([NotNullWhen(true)] out StbImage? image, Stream stream, StbiImageFormat format = StbiImageFormat.Default, bool asFloat = false)
|
||||||
|
{
|
||||||
|
int x, y, iFormat;
|
||||||
|
StbiStreamWrapper wrapper = new StbiStreamWrapper(stream, true);
|
||||||
|
stream.Position = 0;
|
||||||
|
IntPtr imagePtr;
|
||||||
|
fixed (stbi_io_callbacks* cb = &wrapper.Callbacks)
|
||||||
|
{
|
||||||
|
if (asFloat)
|
||||||
|
{
|
||||||
|
imagePtr = (IntPtr)Stbi.loadf_from_callbacks(cb, null, &x, &y, &iFormat, (int)format);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
imagePtr = (IntPtr)Stbi.load_from_callbacks(cb, null, &x, &y, &iFormat, (int)format);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (imagePtr != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
image = new StbImage(imagePtr, x, y, (StbiImageFormat)iFormat, asFloat);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
image = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Try loading an image, without raising exceptions.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="image">The resulting image.</param>
|
||||||
|
/// <param name="span">Source memory span.</param>
|
||||||
|
/// <param name="format">The desired image format.</param>
|
||||||
|
/// <param name="asFloat">The buffer uses floating point pixels if true.</param>
|
||||||
|
/// <returns>True on success.</returns>
|
||||||
|
public static bool TryLoad([NotNullWhen(true)] out StbImage? image, ReadOnlySpan<byte> span, StbiImageFormat format = StbiImageFormat.Default, bool asFloat = false)
|
||||||
|
{
|
||||||
|
IntPtr imagePtr = IntPtr.Zero;
|
||||||
|
int x, y, iFormat;
|
||||||
|
fixed (byte *ptr = MemoryMarshal.AsBytes(span))
|
||||||
|
{
|
||||||
|
if (asFloat)
|
||||||
|
{
|
||||||
|
imagePtr = (IntPtr)Stbi.loadf_from_memory(ptr, span.Length, &x, &y, &iFormat, (int)format);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
imagePtr = (IntPtr)Stbi.load_from_memory(ptr, span.Length, &x, &y, &iFormat, (int)format);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (imagePtr != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
image = new StbImage(imagePtr, x, y, (StbiImageFormat)iFormat, asFloat);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
image = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Load an image.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stream">The stream to load from.</param>
|
||||||
|
/// <param name="format">The desired image format.</param>
|
||||||
|
/// <param name="asFloat">The buffer uses floating point pixels if true.</param>
|
||||||
|
/// <returns>The image object.</returns>
|
||||||
|
public static StbImage Load(Stream stream, StbiImageFormat format = StbiImageFormat.Default, bool asFloat = false)
|
||||||
|
{
|
||||||
|
if (TryLoad(out StbImage? image, stream, format, asFloat))
|
||||||
|
{
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
|
||||||
|
string reason = Marshal.PtrToStringUTF8((IntPtr)Stbi.failure_reason())!;
|
||||||
|
throw new Exception($"Failed to load image: {reason}");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Load an image.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="span">The span of memory to load from.</param>
|
||||||
|
/// <param name="format">The desired image format.</param>
|
||||||
|
/// <param name="asFloat">The buffer uses floating point pixels if true.</param>
|
||||||
|
/// <returns>The image object.</returns>
|
||||||
|
public static StbImage Load(ReadOnlySpan<byte> span, StbiImageFormat format = StbiImageFormat.Default, bool asFloat = false)
|
||||||
|
{
|
||||||
|
if (TryLoad(out StbImage? image, span, format, asFloat))
|
||||||
|
{
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
|
||||||
|
string reason = Marshal.PtrToStringUTF8((IntPtr)Stbi.failure_reason())!;
|
||||||
|
throw new Exception($"Failed to load image: {reason}");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Peek image info from a stream.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stream">The stream to peek into.</param>
|
||||||
|
/// <param name="width">Width of the image.</param>
|
||||||
|
/// <param name="height">Height of the image.</param>
|
||||||
|
/// <param name="format">The image format.</param>
|
||||||
|
/// <returns>True if the stream contained an image.</returns>
|
||||||
|
public static bool PeekInfo(Stream stream, out int width, out int height, out StbiImageFormat format)
|
||||||
|
{
|
||||||
|
int x, y, iFormat;
|
||||||
|
StbiStreamWrapper wrapper = new StbiStreamWrapper(stream, true);
|
||||||
|
int result;
|
||||||
|
stream.Position = 0;
|
||||||
|
fixed (stbi_io_callbacks* cb = &wrapper.Callbacks)
|
||||||
|
{
|
||||||
|
result = Stbi.info_from_callbacks(cb, null, &x, &y, &iFormat);
|
||||||
|
}
|
||||||
|
|
||||||
|
width = x;
|
||||||
|
height = y;
|
||||||
|
format = (StbiImageFormat)iFormat;
|
||||||
|
|
||||||
|
return result != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Peek image info from a span.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="span">The span to peek into.</param>
|
||||||
|
/// <param name="width">Width of the image.</param>
|
||||||
|
/// <param name="height">Height of the image.</param>
|
||||||
|
/// <param name="format">The image format.</param>
|
||||||
|
/// <returns>True if the stream contained an image.</returns>
|
||||||
|
public static bool PeekInfo<T>(ReadOnlySpan<T> span, out int width, out int height, out StbiImageFormat format) where T : unmanaged
|
||||||
|
{
|
||||||
|
fixed (byte* ptr = MemoryMarshal.AsBytes(span))
|
||||||
|
{
|
||||||
|
int x, y, iFormat;
|
||||||
|
int result = Stbi.info_from_memory(ptr, span.Length * sizeof(T), &x, &y, &iFormat);
|
||||||
|
width = x;
|
||||||
|
height = y;
|
||||||
|
format = (StbiImageFormat)iFormat;
|
||||||
|
return result != 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int Components(StbiImageFormat format) => format switch
|
||||||
|
{
|
||||||
|
StbiImageFormat.Grey => 1,
|
||||||
|
StbiImageFormat.GreyAlpha => 2,
|
||||||
|
StbiImageFormat.Rgb => 3,
|
||||||
|
StbiImageFormat.Rgba => 4,
|
||||||
|
_ => throw new ArgumentException("Expected a fully qualified format.")
|
||||||
|
};
|
||||||
|
|
||||||
|
private static byte[] ConvertFloatToByte<T>(ReadOnlySpan<T> source, int width, int height, int components)
|
||||||
|
where T : unmanaged
|
||||||
|
{
|
||||||
|
byte[] conversion = new byte[width * height * components];
|
||||||
|
ReadOnlySpan<float> dataAsFloat = MemoryMarshal.Cast<T, float>(source);
|
||||||
|
|
||||||
|
for (int i = 0; i<conversion.Length; i++)
|
||||||
|
{
|
||||||
|
conversion[i] = (byte) Math.Clamp(MathF.Round(dataAsFloat[i]* 255), 0, 255);
|
||||||
|
}
|
||||||
|
|
||||||
|
return conversion;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static float[] ConvertByteToFloat<T>(ReadOnlySpan<T> source, int width, int height, int components)
|
||||||
|
where T : unmanaged
|
||||||
|
{
|
||||||
|
float[] conversion = new float[width * height * components];
|
||||||
|
ReadOnlySpan<byte> dataAsByte = MemoryMarshal.Cast<T, byte>(source);
|
||||||
|
|
||||||
|
for (int i = 0; i < conversion.Length; i++)
|
||||||
|
{
|
||||||
|
conversion[i] = Math.Clamp(dataAsByte[i]/255f, 0f, 1f);
|
||||||
|
}
|
||||||
|
|
||||||
|
return conversion;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Write any image to a PNG file.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">Any packed byte or float array structure.</typeparam>
|
||||||
|
/// <param name="data">Span of pixel data.</param>
|
||||||
|
/// <param name="width">Width of the pixel data in pixels.</param>
|
||||||
|
/// <param name="height">Height of the pixel data in pixels.</param>
|
||||||
|
/// <param name="format">Color format of the pixel data. Must not be <see cref="StbiImageFormat"/>.</param>
|
||||||
|
/// <param name="destination">The destination stream.</param>
|
||||||
|
/// <param name="isFloat">True if the pixel format in data is a floating point format.</param>
|
||||||
|
/// <remarks>
|
||||||
|
/// This will incur a conversion cost if the pixel format is not a byte format. Affected by global non-thread safe options.
|
||||||
|
/// </remarks>
|
||||||
|
public static void WritePng<T>(ReadOnlySpan<T> data, int width, int height, StbiImageFormat format, Stream destination, bool isFloat = false)
|
||||||
|
where T : unmanaged
|
||||||
|
{
|
||||||
|
int components = Components(format);
|
||||||
|
ReadOnlySpan<byte> source;
|
||||||
|
byte[]? conversion;
|
||||||
|
|
||||||
|
if (isFloat)
|
||||||
|
{
|
||||||
|
conversion = ConvertFloatToByte(data, width, height, components);
|
||||||
|
source = conversion;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
source = MemoryMarshal.AsBytes(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
StbiWriteStreamWrapper wrapper = new StbiWriteStreamWrapper(destination);
|
||||||
|
|
||||||
|
fixed (byte *ptr = source)
|
||||||
|
Stbi.write_png_to_func(wrapper, null, width, height, components, ptr, width * components);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Write any image to a BMP file.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">Any packed byte or float array structure.</typeparam>
|
||||||
|
/// <param name="data">Span of pixel data.</param>
|
||||||
|
/// <param name="width">Width of the pixel data in pixels.</param>
|
||||||
|
/// <param name="height">Height of the pixel data in pixels.</param>
|
||||||
|
/// <param name="format">Color format of the pixel data. Must not be <see cref="StbiImageFormat"/>.</param>
|
||||||
|
/// <param name="destination">The destination stream.</param>
|
||||||
|
/// <param name="isFloat">True if the pixel format in data is a floating point format.</param>
|
||||||
|
/// <remarks>
|
||||||
|
/// This will incur a conversion cost if the pixel format is not a byte format. Ignores the alpha channel. Affected by global non-thread safe options.
|
||||||
|
/// </remarks>
|
||||||
|
public static void WriteBmp<T>(ReadOnlySpan<T> data, int width, int height, StbiImageFormat format, Stream destination, bool isFloat = false)
|
||||||
|
where T : unmanaged
|
||||||
|
{
|
||||||
|
int components = Components(format);
|
||||||
|
ReadOnlySpan<byte> source;
|
||||||
|
byte[]? conversion;
|
||||||
|
|
||||||
|
if (isFloat)
|
||||||
|
{
|
||||||
|
conversion = ConvertFloatToByte(data, width, height, components);
|
||||||
|
source = conversion;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
source = MemoryMarshal.AsBytes(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
StbiWriteStreamWrapper wrapper = new StbiWriteStreamWrapper(destination);
|
||||||
|
|
||||||
|
fixed (byte* ptr = source)
|
||||||
|
Stbi.write_bmp_to_func(wrapper, null, width, height, components, ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Write any image to a TGA file.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">Any packed byte or float array structure.</typeparam>
|
||||||
|
/// <param name="data">Span of pixel data.</param>
|
||||||
|
/// <param name="width">Width of the pixel data in pixels.</param>
|
||||||
|
/// <param name="height">Height of the pixel data in pixels.</param>
|
||||||
|
/// <param name="format">Color format of the pixel data. Must not be <see cref="StbiImageFormat"/>.</param>
|
||||||
|
/// <param name="destination">The destination stream.</param>
|
||||||
|
/// <param name="isFloat">True if the pixel format in data is a floating point format.</param>
|
||||||
|
/// <remarks>
|
||||||
|
/// This will incur a conversion cost if the pixel format is not a byte format. Affected by global non-thread safe options.
|
||||||
|
/// </remarks>
|
||||||
|
public static void WriteTga<T>(ReadOnlySpan<T> data, int width, int height, StbiImageFormat format, Stream destination, bool isFloat = false)
|
||||||
|
where T : unmanaged
|
||||||
|
{
|
||||||
|
int components = Components(format);
|
||||||
|
ReadOnlySpan<byte> source;
|
||||||
|
byte[]? conversion;
|
||||||
|
|
||||||
|
if (isFloat)
|
||||||
|
{
|
||||||
|
conversion = ConvertFloatToByte(data, width, height, components);
|
||||||
|
source = conversion;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
source = MemoryMarshal.AsBytes(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
StbiWriteStreamWrapper wrapper = new StbiWriteStreamWrapper(destination);
|
||||||
|
|
||||||
|
fixed (byte* ptr = source)
|
||||||
|
Stbi.write_tga_to_func(wrapper, null, width, height, components, ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Write any image to a PNG file.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">Any packed byte or float array structure.</typeparam>
|
||||||
|
/// <param name="data">Span of pixel data.</param>
|
||||||
|
/// <param name="width">Width of the pixel data in pixels.</param>
|
||||||
|
/// <param name="height">Height of the pixel data in pixels.</param>
|
||||||
|
/// <param name="format">Color format of the pixel data. Must not be <see cref="StbiImageFormat"/>.</param>
|
||||||
|
/// <param name="destination">The destination stream.</param>
|
||||||
|
/// <param name="isFloat">True if the pixel format in data is a floating point format.</param>
|
||||||
|
/// <remarks>
|
||||||
|
/// This will incur a conversion cost if the pixel format is not a float format. Affected by global non-thread safe options.
|
||||||
|
/// </remarks>
|
||||||
|
public static void WriteHdr<T>(ReadOnlySpan<T> data, int width, int height, StbiImageFormat format, Stream destination, bool isFloat = false)
|
||||||
|
where T : unmanaged
|
||||||
|
{
|
||||||
|
int components = Components(format);
|
||||||
|
ReadOnlySpan<float> source;
|
||||||
|
float[]? conversion;
|
||||||
|
|
||||||
|
if (!isFloat)
|
||||||
|
{
|
||||||
|
conversion = ConvertByteToFloat(data, width, height, components);
|
||||||
|
source = conversion;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
source = MemoryMarshal.Cast<T, float>(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
StbiWriteStreamWrapper wrapper = new StbiWriteStreamWrapper(destination);
|
||||||
|
|
||||||
|
fixed (float* ptr = source)
|
||||||
|
Stbi.write_hdr_to_func(wrapper, null, width, height, components, ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Write any image to a PNG file.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">Any packed byte or float array structure.</typeparam>
|
||||||
|
/// <param name="data">Span of pixel data.</param>
|
||||||
|
/// <param name="width">Width of the pixel data in pixels.</param>
|
||||||
|
/// <param name="height">Height of the pixel data in pixels.</param>
|
||||||
|
/// <param name="format">Color format of the pixel data. Must not be <see cref="StbiImageFormat"/>.</param>
|
||||||
|
/// <param name="destination">The destination stream.</param>
|
||||||
|
/// <param name="quality">The JPEG quality factor.</param>
|
||||||
|
/// <param name="isFloat">True if the pixel format in data is a floating point format.</param>
|
||||||
|
/// <remarks>
|
||||||
|
/// This will incur a conversion cost if the pixel format is not a byte format. Ignores the alpha channel. Affected by global non-thread safe options.
|
||||||
|
/// </remarks>
|
||||||
|
public static void WriteJpg<T>(ReadOnlySpan<T> data, int width, int height, StbiImageFormat format, Stream destination, int quality = 90, bool isFloat = false)
|
||||||
|
where T : unmanaged
|
||||||
|
{
|
||||||
|
int components = Components(format);
|
||||||
|
ReadOnlySpan<byte> source;
|
||||||
|
byte[]? conversion;
|
||||||
|
|
||||||
|
if (isFloat)
|
||||||
|
{
|
||||||
|
conversion = ConvertFloatToByte(data, width, height, components);
|
||||||
|
source = conversion;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
source = MemoryMarshal.AsBytes(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
StbiWriteStreamWrapper wrapper = new StbiWriteStreamWrapper(destination);
|
||||||
|
|
||||||
|
fixed (byte* ptr = source)
|
||||||
|
Stbi.write_jpg_to_func(wrapper, null, width, height, components, ptr, quality);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,14 +1,16 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
namespace Quik.Stb
|
namespace ReFuel.Stb.Native
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Direct access to the native STBI function calls.
|
||||||
|
/// </summary>
|
||||||
public unsafe static partial class Stbi
|
public unsafe static partial class Stbi
|
||||||
{
|
{
|
||||||
private delegate void FailedAssertProc(byte *expression, byte *file, int line, byte *function);
|
private delegate void FailedAssertProc(byte *expression, byte *file, int line, byte *function);
|
||||||
|
private static IntPtr stbiHandle;
|
||||||
|
|
||||||
private static readonly string[] LibraryNames = new string[]
|
private static readonly string[] LibraryNames = new string[]
|
||||||
{
|
{
|
||||||
@@ -30,37 +32,32 @@ namespace Quik.Stb
|
|||||||
{
|
{
|
||||||
NativeLibrary.SetDllImportResolver(Assembly.GetExecutingAssembly(), Resolver);
|
NativeLibrary.SetDllImportResolver(Assembly.GetExecutingAssembly(), Resolver);
|
||||||
|
|
||||||
// quik_stbi_failed_assert_store(Marshal.GetFunctionPointerForDelegate<FailedAssertProc>(FailedAssert));
|
// Dummy call to fail_reason so we have a handle to STBI.
|
||||||
|
failure_reason();
|
||||||
|
|
||||||
|
// Load global address pointers.
|
||||||
|
|
||||||
|
_tga_with_rle_ptr = (int*)NativeLibrary.GetExport(stbiHandle, "stbi_write_tga_with_rle");
|
||||||
|
_png_compression_level_ptr = (int*)NativeLibrary.GetExport(stbiHandle, "stbi_write_png_compression_level");
|
||||||
|
_force_png_filter_ptr = (int*)NativeLibrary.GetExport(stbiHandle, "stbi_write_force_png_filter");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IntPtr Resolver(string libraryName, Assembly assembly, DllImportSearchPath? searchPath)
|
private static IntPtr Resolver(string libraryName, Assembly assembly, DllImportSearchPath? searchPath)
|
||||||
{
|
{
|
||||||
if (libraryName != "stbi")
|
if (libraryName != "stbi")
|
||||||
return IntPtr.Zero;
|
return IntPtr.Zero;
|
||||||
|
else if (stbiHandle != IntPtr.Zero)
|
||||||
|
return stbiHandle;
|
||||||
|
|
||||||
foreach (string name in LibraryNames)
|
foreach (string name in LibraryNames)
|
||||||
{
|
{
|
||||||
if (NativeLibrary.TryLoad(name, assembly, searchPath, out IntPtr handle))
|
if (NativeLibrary.TryLoad(name, assembly, searchPath, out stbiHandle))
|
||||||
{
|
{
|
||||||
return handle;
|
return stbiHandle;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return NativeLibrary.Load(libraryName);
|
return stbiHandle = NativeLibrary.Load(libraryName);
|
||||||
}
|
|
||||||
|
|
||||||
private static void FailedAssert(byte *expression, byte *file, int line, byte *function)
|
|
||||||
{
|
|
||||||
string expr = expression == null ? string.Empty : Marshal.PtrToStringUTF8((IntPtr)expression);
|
|
||||||
string f = file == null ? string.Empty : Marshal.PtrToStringUTF8((IntPtr)file);
|
|
||||||
string func = function == null ? string.Empty : Marshal.PtrToStringUTF8((IntPtr)function);
|
|
||||||
|
|
||||||
Exception ex = new Exception("Assert failed in native stbi code.");
|
|
||||||
ex.Data.Add("Expression", expr);
|
|
||||||
ex.Data.Add("File", f);
|
|
||||||
ex.Data.Add("Line", line);
|
|
||||||
ex.Data.Add("Function", func);
|
|
||||||
throw ex;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,8 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using Quik.Stb.Image;
|
|
||||||
|
|
||||||
namespace Quik.Stb
|
namespace ReFuel.Stb.Native
|
||||||
{
|
{
|
||||||
[NativeTypeName("unsigned int")]
|
|
||||||
public enum StbiEnum : uint
|
public enum StbiEnum : uint
|
||||||
{
|
{
|
||||||
STBI_default = 0,
|
STBI_default = 0,
|
||||||
@@ -16,65 +14,62 @@ namespace Quik.Stb
|
|||||||
|
|
||||||
public partial struct stbi_io_callbacks
|
public partial struct stbi_io_callbacks
|
||||||
{
|
{
|
||||||
[NativeTypeName("int (*)(void *, char *, int)")]
|
|
||||||
public IntPtr read;
|
public IntPtr read;
|
||||||
|
|
||||||
[NativeTypeName("void (*)(void *, int)")]
|
|
||||||
public IntPtr skip;
|
public IntPtr skip;
|
||||||
|
|
||||||
[NativeTypeName("int (*)(void *)")]
|
|
||||||
public IntPtr eof;
|
public IntPtr eof;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static unsafe partial class Stbi
|
public static unsafe partial class Stbi
|
||||||
{
|
{
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_load_from_memory", ExactSpelling = true)]
|
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_load_from_memory", ExactSpelling = true)]
|
||||||
[return: NativeTypeName("stbi_uc *")]
|
|
||||||
public static extern byte* load_from_memory([NativeTypeName("const stbi_uc *")] byte* buffer, int len, int* x, int* y, int* channels_in_file, int desired_channels);
|
public static extern byte* load_from_memory(byte* buffer, int len, int* x, int* y, int* channels_in_file, int desired_channels);
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_load_from_callbacks", ExactSpelling = true)]
|
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_load_from_callbacks", ExactSpelling = true)]
|
||||||
[return: NativeTypeName("stbi_uc *")]
|
|
||||||
public static extern byte* load_from_callbacks([NativeTypeName("const stbi_io_callbacks *")] stbi_io_callbacks* clbk, void* user, int* x, int* y, int* channels_in_file, int desired_channels);
|
public static extern byte* load_from_callbacks(stbi_io_callbacks* clbk, void* user, int* x, int* y, int* channels_in_file, int desired_channels);
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_load", ExactSpelling = true)]
|
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_load", ExactSpelling = true)]
|
||||||
[return: NativeTypeName("stbi_uc *")]
|
|
||||||
public static extern byte* load([NativeTypeName("const char *")] sbyte* filename, int* x, int* y, int* channels_in_file, int desired_channels);
|
public static extern byte* load(sbyte* filename, int* x, int* y, int* channels_in_file, int desired_channels);
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_load_from_file", ExactSpelling = true)]
|
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_load_from_file", ExactSpelling = true)]
|
||||||
[return: NativeTypeName("stbi_uc *")]
|
|
||||||
public static extern byte* load_from_file([NativeTypeName("FILE *")] void* f, int* x, int* y, int* channels_in_file, int desired_channels);
|
public static extern byte* load_from_file(void* f, int* x, int* y, int* channels_in_file, int desired_channels);
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_load_gif_from_memory", ExactSpelling = true)]
|
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_load_gif_from_memory", ExactSpelling = true)]
|
||||||
[return: NativeTypeName("stbi_uc *")]
|
|
||||||
public static extern byte* load_gif_from_memory([NativeTypeName("const stbi_uc *")] byte* buffer, int len, int** delays, int* x, int* y, int* z, int* comp, int req_comp);
|
public static extern byte* load_gif_from_memory(byte* buffer, int len, int** delays, int* x, int* y, int* z, int* comp, int req_comp);
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_load_16_from_memory", ExactSpelling = true)]
|
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_load_16_from_memory", ExactSpelling = true)]
|
||||||
[return: NativeTypeName("stbi_us *")]
|
|
||||||
public static extern ushort* load_16_from_memory([NativeTypeName("const stbi_uc *")] byte* buffer, int len, int* x, int* y, int* channels_in_file, int desired_channels);
|
public static extern ushort* load_16_from_memory(byte* buffer, int len, int* x, int* y, int* channels_in_file, int desired_channels);
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_load_16_from_callbacks", ExactSpelling = true)]
|
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_load_16_from_callbacks", ExactSpelling = true)]
|
||||||
[return: NativeTypeName("stbi_us *")]
|
|
||||||
public static extern ushort* load_16_from_callbacks([NativeTypeName("const stbi_io_callbacks *")] stbi_io_callbacks* clbk, void* user, int* x, int* y, int* channels_in_file, int desired_channels);
|
public static extern ushort* load_16_from_callbacks(stbi_io_callbacks* clbk, void* user, int* x, int* y, int* channels_in_file, int desired_channels);
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_load_16", ExactSpelling = true)]
|
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_load_16", ExactSpelling = true)]
|
||||||
[return: NativeTypeName("stbi_us *")]
|
|
||||||
public static extern ushort* load_16([NativeTypeName("const char *")] sbyte* filename, int* x, int* y, int* channels_in_file, int desired_channels);
|
public static extern ushort* load_16(sbyte* filename, int* x, int* y, int* channels_in_file, int desired_channels);
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_load_from_file_16", ExactSpelling = true)]
|
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_load_from_file_16", ExactSpelling = true)]
|
||||||
[return: NativeTypeName("stbi_us *")]
|
|
||||||
public static extern ushort* load_from_file_16([NativeTypeName("FILE *")] void* f, int* x, int* y, int* channels_in_file, int desired_channels);
|
public static extern ushort* load_from_file_16(void* f, int* x, int* y, int* channels_in_file, int desired_channels);
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_loadf_from_memory", ExactSpelling = true)]
|
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_loadf_from_memory", ExactSpelling = true)]
|
||||||
public static extern float* loadf_from_memory([NativeTypeName("const stbi_uc *")] byte* buffer, int len, int* x, int* y, int* channels_in_file, int desired_channels);
|
public static extern float* loadf_from_memory(byte* buffer, int len, int* x, int* y, int* channels_in_file, int desired_channels);
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_loadf_from_callbacks", ExactSpelling = true)]
|
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_loadf_from_callbacks", ExactSpelling = true)]
|
||||||
public static extern float* loadf_from_callbacks([NativeTypeName("const stbi_io_callbacks *")] stbi_io_callbacks* clbk, void* user, int* x, int* y, int* channels_in_file, int desired_channels);
|
public static extern float* loadf_from_callbacks(stbi_io_callbacks* clbk, void* user, int* x, int* y, int* channels_in_file, int desired_channels);
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_loadf", ExactSpelling = true)]
|
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_loadf", ExactSpelling = true)]
|
||||||
public static extern float* loadf([NativeTypeName("const char *")] sbyte* filename, int* x, int* y, int* channels_in_file, int desired_channels);
|
public static extern float* loadf(sbyte* filename, int* x, int* y, int* channels_in_file, int desired_channels);
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_loadf_from_file", ExactSpelling = true)]
|
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_loadf_from_file", ExactSpelling = true)]
|
||||||
public static extern float* loadf_from_file([NativeTypeName("FILE *")] void* f, int* x, int* y, int* channels_in_file, int desired_channels);
|
public static extern float* loadf_from_file(void* f, int* x, int* y, int* channels_in_file, int desired_channels);
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_hdr_to_ldr_gamma", ExactSpelling = true)]
|
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_hdr_to_ldr_gamma", ExactSpelling = true)]
|
||||||
public static extern void hdr_to_ldr_gamma(float gamma);
|
public static extern void hdr_to_ldr_gamma(float gamma);
|
||||||
@@ -89,47 +84,47 @@ namespace Quik.Stb
|
|||||||
public static extern void ldr_to_hdr_scale(float scale);
|
public static extern void ldr_to_hdr_scale(float scale);
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_is_hdr_from_callbacks", ExactSpelling = true)]
|
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_is_hdr_from_callbacks", ExactSpelling = true)]
|
||||||
public static extern int is_hdr_from_callbacks([NativeTypeName("const stbi_io_callbacks *")] stbi_io_callbacks* clbk, void* user);
|
public static extern int is_hdr_from_callbacks(stbi_io_callbacks* clbk, void* user);
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_is_hdr_from_memory", ExactSpelling = true)]
|
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_is_hdr_from_memory", ExactSpelling = true)]
|
||||||
public static extern int is_hdr_from_memory([NativeTypeName("const stbi_uc *")] byte* buffer, int len);
|
public static extern int is_hdr_from_memory(byte* buffer, int len);
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_is_hdr", ExactSpelling = true)]
|
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_is_hdr", ExactSpelling = true)]
|
||||||
public static extern int is_hdr([NativeTypeName("const char *")] sbyte* filename);
|
public static extern int is_hdr(sbyte* filename);
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_is_hdr_from_file", ExactSpelling = true)]
|
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_is_hdr_from_file", ExactSpelling = true)]
|
||||||
public static extern int is_hdr_from_file([NativeTypeName("FILE *")] void* f);
|
public static extern int is_hdr_from_file(void* f);
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_failure_reason", ExactSpelling = true)]
|
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_failure_reason", ExactSpelling = true)]
|
||||||
[return: NativeTypeName("const char *")]
|
|
||||||
public static extern sbyte* failure_reason();
|
public static extern sbyte* failure_reason();
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_image_free", ExactSpelling = true)]
|
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_image_free", ExactSpelling = true)]
|
||||||
public static extern void image_free(void* retval_from_stbi_load);
|
public static extern void image_free(void* retval_from_stbi_load);
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_info_from_memory", ExactSpelling = true)]
|
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_info_from_memory", ExactSpelling = true)]
|
||||||
public static extern int info_from_memory([NativeTypeName("const stbi_uc *")] byte* buffer, int len, int* x, int* y, int* comp);
|
public static extern int info_from_memory(byte* buffer, int len, int* x, int* y, int* comp);
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_info_from_callbacks", ExactSpelling = true)]
|
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_info_from_callbacks", ExactSpelling = true)]
|
||||||
public static extern int info_from_callbacks([NativeTypeName("const stbi_io_callbacks *")] stbi_io_callbacks* clbk, void* user, int* x, int* y, int* comp);
|
public static extern int info_from_callbacks(stbi_io_callbacks* clbk, void* user, int* x, int* y, int* comp);
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_is_16_bit_from_memory", ExactSpelling = true)]
|
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_is_16_bit_from_memory", ExactSpelling = true)]
|
||||||
public static extern int is_16_bit_from_memory([NativeTypeName("const stbi_uc *")] byte* buffer, int len);
|
public static extern int is_16_bit_from_memory(byte* buffer, int len);
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_is_16_bit_from_callbacks", ExactSpelling = true)]
|
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_is_16_bit_from_callbacks", ExactSpelling = true)]
|
||||||
public static extern int is_16_bit_from_callbacks([NativeTypeName("const stbi_io_callbacks *")] stbi_io_callbacks* clbk, void* user);
|
public static extern int is_16_bit_from_callbacks(stbi_io_callbacks* clbk, void* user);
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_info", ExactSpelling = true)]
|
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_info", ExactSpelling = true)]
|
||||||
public static extern int info([NativeTypeName("const char *")] sbyte* filename, int* x, int* y, int* comp);
|
public static extern int info(sbyte* filename, int* x, int* y, int* comp);
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_info_from_file", ExactSpelling = true)]
|
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_info_from_file", ExactSpelling = true)]
|
||||||
public static extern int info_from_file([NativeTypeName("FILE *")] void* f, int* x, int* y, int* comp);
|
public static extern int info_from_file(void* f, int* x, int* y, int* comp);
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_is_16_bit", ExactSpelling = true)]
|
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_is_16_bit", ExactSpelling = true)]
|
||||||
public static extern int is_16_bit([NativeTypeName("const char *")] sbyte* filename);
|
public static extern int is_16_bit(sbyte* filename);
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_is_16_bit_from_file", ExactSpelling = true)]
|
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_is_16_bit_from_file", ExactSpelling = true)]
|
||||||
public static extern int is_16_bit_from_file([NativeTypeName("FILE *")] void* f);
|
public static extern int is_16_bit_from_file(void* f);
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_set_unpremultiply_on_load", ExactSpelling = true)]
|
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_set_unpremultiply_on_load", ExactSpelling = true)]
|
||||||
public static extern void set_unpremultiply_on_load(int flag_true_if_should_unpremultiply);
|
public static extern void set_unpremultiply_on_load(int flag_true_if_should_unpremultiply);
|
||||||
@@ -150,25 +145,25 @@ namespace Quik.Stb
|
|||||||
public static extern void set_flip_vertically_on_load_thread(int flag_true_if_should_flip);
|
public static extern void set_flip_vertically_on_load_thread(int flag_true_if_should_flip);
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_zlib_decode_malloc_guesssize", ExactSpelling = true)]
|
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_zlib_decode_malloc_guesssize", ExactSpelling = true)]
|
||||||
[return: NativeTypeName("char *")]
|
|
||||||
public static extern sbyte* zlib_decode_malloc_guesssize([NativeTypeName("const char *")] sbyte* buffer, int len, int initial_size, int* outlen);
|
public static extern sbyte* zlib_decode_malloc_guesssize(sbyte* buffer, int len, int initial_size, int* outlen);
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_zlib_decode_malloc_guesssize_headerflag", ExactSpelling = true)]
|
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_zlib_decode_malloc_guesssize_headerflag", ExactSpelling = true)]
|
||||||
[return: NativeTypeName("char *")]
|
|
||||||
public static extern sbyte* zlib_decode_malloc_guesssize_headerflag([NativeTypeName("const char *")] sbyte* buffer, int len, int initial_size, int* outlen, int parse_header);
|
public static extern sbyte* zlib_decode_malloc_guesssize_headerflag(sbyte* buffer, int len, int initial_size, int* outlen, int parse_header);
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_zlib_decode_malloc", ExactSpelling = true)]
|
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_zlib_decode_malloc", ExactSpelling = true)]
|
||||||
[return: NativeTypeName("char *")]
|
|
||||||
public static extern sbyte* zlib_decode_malloc([NativeTypeName("const char *")] sbyte* buffer, int len, int* outlen);
|
public static extern sbyte* zlib_decode_malloc(sbyte* buffer, int len, int* outlen);
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_zlib_decode_buffer", ExactSpelling = true)]
|
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_zlib_decode_buffer", ExactSpelling = true)]
|
||||||
public static extern int zlib_decode_buffer([NativeTypeName("char *")] sbyte* obuffer, int olen, [NativeTypeName("const char *")] sbyte* ibuffer, int ilen);
|
public static extern int zlib_decode_buffer(sbyte* ibuffer, int ilen);
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_zlib_decode_noheader_malloc", ExactSpelling = true)]
|
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_zlib_decode_noheader_malloc", ExactSpelling = true)]
|
||||||
[return: NativeTypeName("char *")]
|
|
||||||
public static extern sbyte* zlib_decode_noheader_malloc([NativeTypeName("const char *")] sbyte* buffer, int len, int* outlen);
|
public static extern sbyte* zlib_decode_noheader_malloc(sbyte* buffer, int len, int* outlen);
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_zlib_decode_noheader_buffer", ExactSpelling = true)]
|
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_zlib_decode_noheader_buffer", ExactSpelling = true)]
|
||||||
public static extern int zlib_decode_noheader_buffer([NativeTypeName("char *")] sbyte* obuffer, int olen, [NativeTypeName("const char *")] sbyte* ibuffer, int ilen);
|
public static extern int zlib_decode_noheader_buffer(sbyte* ibuffer, int ilen);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,10 @@
|
|||||||
namespace Quik.Stb
|
using ReFuel.Stb.Native;
|
||||||
|
|
||||||
|
namespace ReFuel.Stb
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Enumeration of supported STBI image formats.
|
||||||
|
/// </summary>
|
||||||
public enum StbiImageFormat
|
public enum StbiImageFormat
|
||||||
{
|
{
|
||||||
Default = (int)StbiEnum.STBI_default,
|
Default = (int)StbiEnum.STBI_default,
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
using ReFuel.Stb.Native;
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace ReFuel.Stb
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Pointer to STBI stream read function.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="userdata">User provided userdata pointer.</param>
|
||||||
|
/// <param name="buffer">C array to read into.</param>
|
||||||
|
/// <param name="count">Size of the C array in bytes.</param>
|
||||||
|
/// <returns>The number of bytes read from the stream.</returns>
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public unsafe delegate int StbiReadProc(void *userdata, byte* buffer, int count);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Pointer to STBI stream skip function.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="userdata">User provided userdata pointer.</param>
|
||||||
|
/// <param name="count">Number of bytes to skip.</param>
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public unsafe delegate void StbiSkipProc(void *userdata, int count);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Pointer to STBI stream end of file function.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="userdata">User provided userdata pointer.</param>
|
||||||
|
/// <returns>Non-zero value if the end of the stream has been reached.</returns>
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public unsafe delegate int StbiEofProc(void *userdata);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An easy-to-use stream wrapper for use with STBI image load functions.
|
||||||
|
/// </summary>
|
||||||
|
public unsafe class StbiStreamWrapper : IDisposable
|
||||||
|
{
|
||||||
|
public readonly stbi_io_callbacks Callbacks;
|
||||||
|
|
||||||
|
private readonly Stream _stream;
|
||||||
|
private readonly bool _keepOpen;
|
||||||
|
private bool _isDisposed;
|
||||||
|
|
||||||
|
private StbiReadProc _readCb;
|
||||||
|
private StbiSkipProc _skipCb;
|
||||||
|
private StbiEofProc _eofCb;
|
||||||
|
|
||||||
|
public StbiStreamWrapper(Stream stream, bool keepOpen = false)
|
||||||
|
{
|
||||||
|
if (stream == null) throw new ArgumentNullException(nameof(stream));
|
||||||
|
|
||||||
|
_stream = stream;
|
||||||
|
_keepOpen = keepOpen;
|
||||||
|
|
||||||
|
_readCb = ReadCb;
|
||||||
|
_skipCb = SkipCb;
|
||||||
|
_eofCb = EofCb;
|
||||||
|
|
||||||
|
Callbacks = default;
|
||||||
|
Callbacks.read = Marshal.GetFunctionPointerForDelegate<StbiReadProc>(_readCb);
|
||||||
|
Callbacks.skip = Marshal.GetFunctionPointerForDelegate<StbiSkipProc>(_skipCb);
|
||||||
|
Callbacks.eof = Marshal.GetFunctionPointerForDelegate<StbiEofProc>(_eofCb);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int ReadCb(void *userdata, byte* buffer, int count)
|
||||||
|
{
|
||||||
|
Span<byte> bytes = new Span<byte>(buffer, count);
|
||||||
|
return _stream.Read(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SkipCb(void *userdata, int count)
|
||||||
|
{
|
||||||
|
_stream.Seek(count, SeekOrigin.Current);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int EofCb(void *userdata)
|
||||||
|
{
|
||||||
|
if (!_stream.CanRead || _stream.Position == _stream.Length)
|
||||||
|
return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_isDisposed) return;
|
||||||
|
|
||||||
|
if (!_keepOpen) _stream.Dispose();
|
||||||
|
|
||||||
|
_isDisposed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An easy to use stream wrapper for STBI image write functions.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>Keep object alive for the duration of the write operation.</remarks>
|
||||||
|
public class StbiWriteStreamWrapper
|
||||||
|
{
|
||||||
|
private readonly Stream _stream;
|
||||||
|
private readonly StbiWriteProc _cb;
|
||||||
|
|
||||||
|
public IntPtr Callback { get; }
|
||||||
|
|
||||||
|
public StbiWriteStreamWrapper(Stream stream)
|
||||||
|
{
|
||||||
|
_stream = stream;
|
||||||
|
unsafe
|
||||||
|
{
|
||||||
|
_cb = WriteCb;
|
||||||
|
}
|
||||||
|
Callback = Marshal.GetFunctionPointerForDelegate(_cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
private unsafe void WriteCb(void *context, void *data, int size)
|
||||||
|
{
|
||||||
|
_stream.Write(new ReadOnlySpan<byte>((byte*)data, size));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static implicit operator IntPtr(in StbiWriteStreamWrapper wrapper) => wrapper.Callback;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace ReFuel.Stb.Native
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Procedure to STBI image write function.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context">User provided context pointer.</param>
|
||||||
|
/// <param name="data">C Array of data to write.</param>
|
||||||
|
/// <param name="size">Size of the C array in bytes.</param>
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public unsafe delegate void StbiWriteProc(void* context, void* data, int size);
|
||||||
|
|
||||||
|
public unsafe partial class Stbi
|
||||||
|
{
|
||||||
|
private static readonly int* _tga_with_rle_ptr;
|
||||||
|
private static readonly int* _png_compression_level_ptr;
|
||||||
|
private static readonly int* _force_png_filter_ptr;
|
||||||
|
|
||||||
|
public static int write_tga_with_rle
|
||||||
|
{
|
||||||
|
get => *_tga_with_rle_ptr;
|
||||||
|
set => *_tga_with_rle_ptr = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int write_png_compression_level
|
||||||
|
{
|
||||||
|
get => *_png_compression_level_ptr;
|
||||||
|
set => *_png_compression_level_ptr = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int write_force_png_filter
|
||||||
|
{
|
||||||
|
get => *_force_png_filter_ptr;
|
||||||
|
set => *_force_png_filter_ptr = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_write_png_to_func")]
|
||||||
|
public static extern int write_png_to_func(IntPtr func, void* context, int w, int h, int comp, void* data, int stride_in_bytes);
|
||||||
|
|
||||||
|
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_write_bmp_to_func")]
|
||||||
|
public static extern int write_bmp_to_func(IntPtr func, void* context, int w, int h, int comp, void* data);
|
||||||
|
|
||||||
|
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_write_tga_to_func")]
|
||||||
|
public static extern int write_tga_to_func(IntPtr func, void* context, int w, int h, int comp, void* data);
|
||||||
|
|
||||||
|
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_write_hdr_to_func")]
|
||||||
|
public static extern int write_hdr_to_func(IntPtr func, void* context, int w, int h, int comp, void* data);
|
||||||
|
|
||||||
|
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_write_jpg_to_func")]
|
||||||
|
public static extern int write_jpg_to_func(IntPtr func, void* context, int w, int h, int comp, void* data, int quality);
|
||||||
|
|
||||||
|
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_flip_vertically_on_write")]
|
||||||
|
public static extern int flip_vertically_on_write(int value);
|
||||||
|
}
|
||||||
|
}
|
||||||
-144
@@ -1,144 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace Quik.Stb
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// A class that encompasses all features of stb_image.h in a safe way.
|
|
||||||
/// </summary>
|
|
||||||
public unsafe class StbImage : IDisposable
|
|
||||||
{
|
|
||||||
private bool isDisposed = false;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Pointer to the image.
|
|
||||||
/// </summary>
|
|
||||||
public IntPtr ImagePointer { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Width of the image.
|
|
||||||
/// </summary>
|
|
||||||
public int Width { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Height of the image.
|
|
||||||
/// </summary>
|
|
||||||
/// <value></value>
|
|
||||||
public int Height { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Internal image format.
|
|
||||||
/// </summary>
|
|
||||||
public StbiImageFormat Format { get; }
|
|
||||||
public bool IsFloat { get; }
|
|
||||||
|
|
||||||
private StbImage(IntPtr image, int x, int y, StbiImageFormat format, bool isFloat)
|
|
||||||
{
|
|
||||||
ImagePointer = image;
|
|
||||||
Width = x;
|
|
||||||
Height = y;
|
|
||||||
Format = format;
|
|
||||||
IsFloat = isFloat;
|
|
||||||
}
|
|
||||||
|
|
||||||
~StbImage()
|
|
||||||
{
|
|
||||||
Dispose(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose() => Dispose(true);
|
|
||||||
|
|
||||||
private void Dispose(bool disposing)
|
|
||||||
{
|
|
||||||
if (isDisposed) return;
|
|
||||||
|
|
||||||
if (disposing)
|
|
||||||
{
|
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
Stbi.image_free(ImagePointer.ToPointer());
|
|
||||||
isDisposed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Set to flip the y-axis of loaded images on load.
|
|
||||||
/// </summary>
|
|
||||||
public static bool FlipVerticallyOnLoad { set => Stbi.set_flip_vertically_on_load(1); }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Set to unpremultiply images on load.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// According to the stb_image documentation, only iPhone PNG images
|
|
||||||
/// can come with premultiplied alpha.
|
|
||||||
/// </remarks>
|
|
||||||
public static bool UnpremultiplyOnLoad { set => Stbi.set_unpremultiply_on_load(1); }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Try loading an image, without raising exceptions.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="image">The resulting image.</param>
|
|
||||||
/// <param name="stream">Source stream.</param>
|
|
||||||
/// <param name="format">The desired image format.</param>
|
|
||||||
/// <returns>True on success.</returns>
|
|
||||||
public static bool TryLoad(out StbImage image, Stream stream, StbiImageFormat format = StbiImageFormat.Default, bool isFloat = false)
|
|
||||||
{
|
|
||||||
int x, y, iFormat;
|
|
||||||
StbiStreamWrapper wrapper = new StbiStreamWrapper(stream, true);
|
|
||||||
wrapper.CreateCallbacks(out stbi_io_callbacks cb);
|
|
||||||
|
|
||||||
stream.Position = 0;
|
|
||||||
IntPtr imagePtr;
|
|
||||||
if (isFloat)
|
|
||||||
{
|
|
||||||
imagePtr = (IntPtr)Stbi.loadf_from_callbacks(&cb, null, &x, &y, &iFormat, (int)format);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
imagePtr = (IntPtr)Stbi.load_from_callbacks(&cb, null, &x, &y, &iFormat, (int)format);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (imagePtr != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
image = new StbImage(imagePtr, x, y, (StbiImageFormat)iFormat, isFloat);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
image = null;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Load an image.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="stream">The stream to load from.</param>
|
|
||||||
/// <param name="format">The desired image format.</param>
|
|
||||||
/// <returns>The image object.</returns>
|
|
||||||
public static StbImage Load(Stream stream, StbiImageFormat format = StbiImageFormat.Default, bool isFloat = false)
|
|
||||||
{
|
|
||||||
if (TryLoad(out StbImage image, stream, format, isFloat))
|
|
||||||
{
|
|
||||||
return image;
|
|
||||||
}
|
|
||||||
|
|
||||||
string reason = Marshal.PtrToStringUTF8((IntPtr)Stbi.failure_reason());
|
|
||||||
throw new Exception($"Failed to load image: {reason}");
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsLoadable(Stream stream)
|
|
||||||
{
|
|
||||||
int x, y, iFormat;
|
|
||||||
StbiStreamWrapper wrapper = new StbiStreamWrapper(stream, true);
|
|
||||||
wrapper.CreateCallbacks(out stbi_io_callbacks cb);
|
|
||||||
|
|
||||||
stream.Position = 0;
|
|
||||||
int result = Stbi.info_from_callbacks(&cb, null, &x, &y, &iFormat);
|
|
||||||
|
|
||||||
return result != 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace Quik.Stb
|
|
||||||
{
|
|
||||||
public unsafe class StbiStreamWrapper : IDisposable
|
|
||||||
{
|
|
||||||
private Stream _stream;
|
|
||||||
private bool _keepOpen;
|
|
||||||
private bool _isDisposed;
|
|
||||||
|
|
||||||
private delegate int ReadProc(void *userdata, byte* buffer, int count);
|
|
||||||
private delegate void SkipProc(void *userdata, int count);
|
|
||||||
private delegate int Eof(void *userdata);
|
|
||||||
|
|
||||||
public StbiStreamWrapper(Stream stream, bool keepOpen = false)
|
|
||||||
{
|
|
||||||
if (stream == null) throw new ArgumentNullException(nameof(stream));
|
|
||||||
|
|
||||||
_stream = stream;
|
|
||||||
_keepOpen = keepOpen;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void CreateCallbacks(out stbi_io_callbacks cb)
|
|
||||||
{
|
|
||||||
cb = default;
|
|
||||||
cb.read = Marshal.GetFunctionPointerForDelegate<ReadProc>(ReadCb);
|
|
||||||
cb.skip = Marshal.GetFunctionPointerForDelegate<SkipProc>(SkipCb);
|
|
||||||
cb.eof = Marshal.GetFunctionPointerForDelegate<Eof>(EofCb);
|
|
||||||
}
|
|
||||||
|
|
||||||
private int ReadCb(void *userdata, byte* buffer, int count)
|
|
||||||
{
|
|
||||||
Span<byte> bytes = new Span<byte>(buffer, count);
|
|
||||||
return _stream.Read(bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SkipCb(void *userdata, int count)
|
|
||||||
{
|
|
||||||
_stream.Seek(count, SeekOrigin.Current);
|
|
||||||
}
|
|
||||||
|
|
||||||
private int EofCb(void *userdata)
|
|
||||||
{
|
|
||||||
if (!_stream.CanRead || _stream.Position == _stream.Length)
|
|
||||||
return 1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
if (_isDisposed) return;
|
|
||||||
|
|
||||||
if (!_keepOpen) _stream.Dispose();
|
|
||||||
|
|
||||||
_isDisposed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+13
-2
@@ -1,4 +1,15 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
cd $(dirname "$0")
|
cd $(dirname "$0")
|
||||||
./Quik.Common/sh/quik_build_native.sh .
|
# Add the local directory as a .NET package source.
|
||||||
dotnet build
|
dotnet nuget add source -n ReFuel.StbImage bin
|
||||||
|
|
||||||
|
# Build each architecture in its own subfolder.
|
||||||
|
DST=ReFuel.StbImage.redis.linux.arm ./docker-cross-compiler/sh/build_native.sh $PWD linux-arm
|
||||||
|
DST=ReFuel.StbImage.redis.linux.arm64 ./docker-cross-compiler/sh/build_native.sh $PWD linux-arm64
|
||||||
|
DST=ReFuel.StbImage.redis.linux.x64 ./docker-cross-compiler/sh/build_native.sh $PWD linux-x64
|
||||||
|
DST=ReFuel.StbImage.redis.osx.arm64 ./docker-cross-compiler/sh/build_native.sh $PWD osx-arm64
|
||||||
|
DST=ReFuel.StbImage.redis.osx.x64 ./docker-cross-compiler/sh/build_native.sh $PWD osx-x64
|
||||||
|
DST=ReFuel.StbImage.redis.win.x64 ./docker-cross-compiler/sh/build_native.sh $PWD win-x64
|
||||||
|
DST=ReFuel.StbImage.redis.win.x86 ./docker-cross-compiler/sh/build_native.sh $PWD win-x86
|
||||||
|
|
||||||
|
dotnet build -c Release
|
||||||
|
|||||||
+6
-6
@@ -2,15 +2,15 @@ version: "2"
|
|||||||
|
|
||||||
services:
|
services:
|
||||||
build:
|
build:
|
||||||
image: git.mixedup.dev/quik/docker-cross-compiler
|
image: git.mixedup.dev/refuel/docker-cross-compiler
|
||||||
command: "/home/quik/src/build_native.sh"
|
command: "/home/refuel/src/build_native.sh"
|
||||||
volumes:
|
volumes:
|
||||||
- .:/home/quik/src
|
- .:/home/refuel/src
|
||||||
publish:
|
publish:
|
||||||
image: git.mixedup.dev/quik/docker-cross-compiler
|
image: git.mixedup.dev/refuel/docker-cross-compiler
|
||||||
command: "/home/quik/src/publish.sh"
|
command: "/home/refuel/src/publish.sh"
|
||||||
environment:
|
environment:
|
||||||
QUIK_API_KEY: ${QUIK_API_KEY}
|
QUIK_API_KEY: ${QUIK_API_KEY}
|
||||||
volumes:
|
volumes:
|
||||||
- .:/home/quik/src
|
- .:/home/refuel/src
|
||||||
|
|
||||||
Submodule
+1
Submodule docker-cross-compiler added at d0f5760c7a
@@ -0,0 +1,30 @@
|
|||||||
|
<Project>
|
||||||
|
<PropertyGroup>
|
||||||
|
<!--Properties for packages.-->
|
||||||
|
<TargetFrameworks>net6.0;net8.0;net10.0</TargetFrameworks>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<LangVersion>latest</LangVersion>
|
||||||
|
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
||||||
|
<RootNamespace>ReFuel.Stb</RootNamespace>
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
||||||
|
<PackageOutputPath>$(MSBuildThisFileDirectory)../bin</PackageOutputPath>
|
||||||
|
|
||||||
|
<!--Supresses warnings for the nuget packages with only native assemlies.-->
|
||||||
|
<NoWarn>$(NoWarn);NU5128</NoWarn>
|
||||||
|
|
||||||
|
<Authors>STBI Authors, H. Utku Maden</Authors>
|
||||||
|
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||||
|
<PackageLicenseFile>LICENSE.md</PackageLicenseFile>
|
||||||
|
<PackageIcon>images\icon.png</PackageIcon>
|
||||||
|
<PackageProjectUrl>https://refuel.mixedup.dev/docs/ReFuel.StbImage.html</PackageProjectUrl>
|
||||||
|
<RepositoryUrl>https://github.com/ReFuelGameEngine/ReFuel.StbImage</RepositoryUrl>
|
||||||
|
<RepositoryType>git</RepositoryType>
|
||||||
|
<PackageTags>stb; stb_image; stbi; image; load; save; read; write</PackageTags>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include="../*.md" Pack="true" PackagePath="/" />
|
||||||
|
<None Include="../rf_stbimage.png" Pack="true" PackagePath="images\icon.png" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
+18
-5
@@ -1,14 +1,27 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
cd $(dirname "$0")
|
cd $(dirname "$0")
|
||||||
if [ -z "$QUIK_API_KEY" ]
|
|
||||||
|
if [ -z "$REFUEL_API_KEY" ]
|
||||||
then
|
then
|
||||||
echo "Please define QUIK_API_KEY"
|
echo "Please define REFUEL_API_KEY"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$NUGET_USER_NAME" ]
|
||||||
|
then
|
||||||
|
echo "Please define NUGET_USER_NAME"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$NUGET_INDEX" ]
|
||||||
|
then
|
||||||
|
echo "Please define NUGET_INDEX"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
dotnet nuget add source \
|
dotnet nuget add source \
|
||||||
-n QUIK -u themixedupstuff -p "$QUIK_API_KEY" \
|
-n ReFuel -u "$NUGET_USER_NAME" -p "$REFUEL_API_KEY" \
|
||||||
--store-password-in-clear-text \
|
--store-password-in-clear-text \
|
||||||
https://git.mixedup.dev/api/packages/QUIK/nuget/index.json
|
"$NUGET_INDEX"
|
||||||
dotnet nuget push -s QUIK bin/*/*.nupkg
|
dotnet nuget push -s ReFuel bin/*.nupkg
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
#include "quik_stbi.h"
|
|
||||||
|
|
||||||
#define STB_IMAGE_IMPLEMENTATION 1
|
|
||||||
#include "stb/stb_image.h"
|
|
||||||
-13
@@ -1,13 +0,0 @@
|
|||||||
#ifndef _QUIK_STBI_H_
|
|
||||||
#define _QUIK_STBI_H_
|
|
||||||
|
|
||||||
#include "Quik.Common/include/quik_common.h"
|
|
||||||
|
|
||||||
#define STBIDEF QEXTERN
|
|
||||||
|
|
||||||
#define STBI_NO_THREAD_LOCALS 1
|
|
||||||
#define STBI_NO_FAILURE_STRINGS 1
|
|
||||||
#define STBI_THREAD_LOCAL
|
|
||||||
#include "stb/stb_image.h"
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
#include "rf_stbi.h"
|
||||||
|
|
||||||
|
#define STB_IMAGE_IMPLEMENTATION 1
|
||||||
|
#include "stb/stb_image.h"
|
||||||
|
|
||||||
|
#define STB_IMAGE_WRITE_IMPLEMENTATION 1
|
||||||
|
#include "stb/stb_image_write.h"
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
#ifndef _REFUEL_STBI_H_
|
||||||
|
#define _REFUEL_STBI_H_
|
||||||
|
|
||||||
|
#include "docker-cross-compiler/include/rf_common.h"
|
||||||
|
|
||||||
|
#define STBIDEF RFEXTERN
|
||||||
|
#define STBI_NO_THREAD_LOCALS 1
|
||||||
|
#include "stb/stb_image.h"
|
||||||
|
|
||||||
|
#define STBIWDEF RFEXTERN
|
||||||
|
#define STBI_WRITE_NO_STDIO 1
|
||||||
|
#include "stb/stb_image_write.h"
|
||||||
|
|
||||||
|
#endif
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 64 KiB |
+1
-1
Submodule stb updated: 013ac3bedd...f7f20f39fe
@@ -10,18 +10,18 @@
|
|||||||
generate-setslastsystemerror-attribute
|
generate-setslastsystemerror-attribute
|
||||||
unix-types
|
unix-types
|
||||||
--include-directory
|
--include-directory
|
||||||
../lib
|
stb
|
||||||
--include-directory
|
--include-directory
|
||||||
../Quik.StbImage
|
.
|
||||||
--include-directory
|
--include-directory
|
||||||
/usr/lib/llvm-14/lib/clang/14.0.6/include
|
/usr/lib/llvm-14/lib/clang/14.0.6/include
|
||||||
--file
|
--file
|
||||||
../Quik.StbImage.redist/quik_stbi.h
|
rf_stbi.h
|
||||||
../lib/stb/stb_image.h
|
stb/stb_image.h
|
||||||
--methodClassName
|
--methodClassName
|
||||||
Stbi
|
Stbi
|
||||||
--namespace
|
--namespace
|
||||||
Quik.Stb
|
ReFuel.Stb
|
||||||
--output
|
--output
|
||||||
Stbi.cs
|
Stbi.cs
|
||||||
--prefixStrip
|
--prefixStrip
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
-x
|
||||||
|
c
|
||||||
|
-l
|
||||||
|
stbi
|
||||||
|
--config
|
||||||
|
compatible-codegen
|
||||||
|
single-file
|
||||||
|
exclude-fnptr-codegen
|
||||||
|
generate-aggressive-inlining
|
||||||
|
generate-setslastsystemerror-attribute
|
||||||
|
unix-types
|
||||||
|
--include-directory
|
||||||
|
stb
|
||||||
|
--include-directory
|
||||||
|
.
|
||||||
|
--include-directory
|
||||||
|
/usr/lib/llvm-14/lib/clang/14.0.6/include
|
||||||
|
--file
|
||||||
|
rf_stbi.h
|
||||||
|
stb/stb_image_write.h
|
||||||
|
--methodClassName
|
||||||
|
StbiWrite
|
||||||
|
--namespace
|
||||||
|
ReFuel.Stb
|
||||||
|
--output
|
||||||
|
StbiWrite.cs
|
||||||
|
--prefixStrip
|
||||||
|
stbi_write_
|
||||||
|
--with-type
|
||||||
|
FILE=void
|
||||||
Reference in New Issue
Block a user