Skip to content

Commit

Permalink
(fix): Move stb_image sample to backend.
Browse files Browse the repository at this point in the history
  • Loading branch information
hyblocker committed Nov 3, 2024
1 parent 6c57849 commit 8c1d982
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 146 deletions.
117 changes: 61 additions & 56 deletions Backends/RmlUi_Backend_GLFW_DX11.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,67 @@
#include <GLFW/glfw3native.h>

#ifdef RMLUI_USE_STB_IMAGE_LOADER
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#include <RmlUi/Core/FileInterface.h>
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#include <RmlUi/Core.h>
#include <RmlUi/Core/FileInterface.h>
#endif

/**
Custom render interface example for the DX11/GLFW backend.
Overloads the DX11 render interface to load textures through stb_image.
*/
class RenderInterface_DX11_Win32 : public RenderInterface_DX11 {
public:
RenderInterface_DX11_Win32() {}

#ifdef RMLUI_USE_STB_IMAGE_LOADER
Rml::TextureHandle LoadTexture(Rml::Vector2i& texture_dimensions, const Rml::String& source) override
{
int texture_width = 0, texture_height = 0, num_channels = 0;
size_t image_size_bytes = 0;
uint8_t* texture_data = nullptr;

// Find where on disk the file is
Rml::FileInterface* file_interface = Rml::GetFileInterface();
Rml::FileHandle file_handle = file_interface->Open(source);
if (!file_handle)
{
// Tell RmlUI that the image is invalid
texture_data = nullptr;
return false;
}

// stb_image based loaders
void LoadTexture(const Rml::String& filename, int* pWidth, int* pHeight, uint8_t** pData, size_t* pDataSize);
void FreeTexture(uint8_t* pData);
// Load the file through stb_image
texture_data = stbi_load_from_file((FILE*)file_handle, &texture_width, &texture_height, &num_channels, 0);

// If the file data is correct
if (texture_data != nullptr)
{
// Compute number of elements in texture
image_size_bytes = texture_width * texture_height * num_channels;

// Pre-multiply the data
for (int i = 0; i < image_size_bytes; i += 4)
{
texture_data[i + 0] = (uint8_t)((texture_data[i + 0] * texture_data[i + 3]) / 255);
texture_data[i + 1] = (uint8_t)((texture_data[i + 1] * texture_data[i + 3]) / 255);
texture_data[i + 2] = (uint8_t)((texture_data[i + 2] * texture_data[i + 3]) / 255);
}

texture_dimensions.x = texture_width;
texture_dimensions.y = texture_height;

Rml::TextureHandle handle = GenerateTexture({texture_data, image_size_bytes}, texture_dimensions);

stbi_image_free(texture_data);
return handle;
}
return false;
}
#endif
};

static void SetupCallbacks(GLFWwindow* window);

Expand All @@ -66,7 +119,7 @@ static void LogErrorFromGLFW(int error, const char* description)
*/
struct BackendData {
SystemInterface_GLFW system_interface;
RenderInterface_DX11 render_interface;
RenderInterface_DX11_Win32 render_interface;
GLFWwindow* window = nullptr;
int glfw_active_modifiers = 0;
bool context_dimensions_dirty = true;
Expand Down Expand Up @@ -119,9 +172,6 @@ bool Backend::Initialize(const char* name, int width, int height, bool allow_res
}

#ifdef RMLUI_USE_STB_IMAGE_LOADER
// Assign stb_image loader to render interface
data->render_interface.LoadTextureFromFileRaw = &LoadTexture;
data->render_interface.FreeTextureFromFileRaw = &FreeTexture;
// Disable pre-multiplication because loading images through stb_image will inconsistently return pre-multiplied images (only iPhone PNGs are
// pre-multipled) We handle pre-multiplication manually on decode
stbi_set_unpremultiply_on_load(true);
Expand Down Expand Up @@ -381,49 +431,4 @@ static void CleanupRenderTarget()
data->d3d_resources.pMainRenderTargetView->Release();
data->d3d_resources.pMainRenderTargetView = nullptr;
}
}

#ifdef RMLUI_USE_STB_IMAGE_LOADER
void LoadTexture(const Rml::String& filename, int* pWidth, int* pHeight, uint8_t** pData, size_t* pDataSize)
{
// Find where on disk the file is
Rml::FileInterface* file_interface = Rml::GetFileInterface();
Rml::FileHandle file_handle = file_interface->Open(filename);
if (!file_handle)
{
// Tell the backend that the data is invalid (*pData = 0), and return
*pData = nullptr;
return;
}

// Load the file through stb_image
int texture_width, texture_height, num_channels;
*pData = stbi_load_from_file((FILE*)file_handle, &texture_width, &texture_height, &num_channels, 0);

// If the file data is correct
if (*pData != nullptr)
{
// Assign image parameters
*pWidth = texture_width;
*pHeight = texture_height;

// Compute number of elements in texture
*pDataSize = texture_width * texture_height * num_channels;

// Pre-multiply the data
uint8_t* dataView = *pData;
for (int i = 0; i < *pDataSize; i += 4)
{
dataView[i + 0] = (uint8_t)((dataView[i + 0] * dataView[i + 3]) / 255);
dataView[i + 1] = (uint8_t)((dataView[i + 1] * dataView[i + 3]) / 255);
dataView[i + 2] = (uint8_t)((dataView[i + 2] * dataView[i + 3]) / 255);
}
}
}

void FreeTexture(uint8_t* pData)
{
// Free the allocated memory once the texture has been uploaded to the GPU
stbi_image_free(pData);
}
#endif
}
116 changes: 60 additions & 56 deletions Backends/RmlUi_Backend_Win32_DX11.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,66 @@
#include <RmlUi/Core/Profiling.h>

#ifdef RMLUI_USE_STB_IMAGE_LOADER
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#include <RmlUi/Core/FileInterface.h>
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#include <RmlUi/Core/FileInterface.h>
#endif

/**
Custom render interface example for the DX11/Win32 backend.
Overloads the DX11 render interface to load textures through stb_image.
*/
class RenderInterface_DX11_Win32 : public RenderInterface_DX11 {
public:
RenderInterface_DX11_Win32() {}

#ifdef RMLUI_USE_STB_IMAGE_LOADER
Rml::TextureHandle LoadTexture(Rml::Vector2i& texture_dimensions, const Rml::String& source) override
{
int texture_width = 0, texture_height = 0, num_channels = 0;
size_t image_size_bytes = 0;
uint8_t* texture_data = nullptr;

// Find where on disk the file is
Rml::FileInterface* file_interface = Rml::GetFileInterface();
Rml::FileHandle file_handle = file_interface->Open(source);
if (!file_handle)
{
// Tell RmlUI that the image is invalid
texture_data = nullptr;
return false;
}

// Load the file through stb_image
texture_data = stbi_load_from_file((FILE*)file_handle, &texture_width, &texture_height, &num_channels, 0);

// stb_image based loaders
void LoadTexture(const Rml::String& filename, int* pWidth, int* pHeight, uint8_t** pData, size_t* pDataSize);
void FreeTexture(uint8_t* pData);
// If the file data is correct
if (texture_data != nullptr)
{
// Compute number of elements in texture
image_size_bytes = texture_width * texture_height * num_channels;

// Pre-multiply the data
for (int i = 0; i < image_size_bytes; i += 4)
{
texture_data[i + 0] = (uint8_t)((texture_data[i + 0] * texture_data[i + 3]) / 255);
texture_data[i + 1] = (uint8_t)((texture_data[i + 1] * texture_data[i + 3]) / 255);
texture_data[i + 2] = (uint8_t)((texture_data[i + 2] * texture_data[i + 3]) / 255);
}

texture_dimensions.x = texture_width;
texture_dimensions.y = texture_height;

Rml::TextureHandle handle = GenerateTexture({texture_data, image_size_bytes}, texture_dimensions);

stbi_image_free(texture_data);
return handle;
}
return false;
}
#endif
};

/**
High DPI support using Windows Per Monitor V2 DPI awareness.
Expand Down Expand Up @@ -118,7 +170,7 @@ Lifetime governed by the calls to Backend::Initialize() and Backend::Shutdown().
*/
struct BackendData {
SystemInterface_Win32 system_interface;
RenderInterface_DX11 render_interface;
RenderInterface_DX11_Win32 render_interface;
TextInputMethodEditor_Win32 text_input_method_editor;

HINSTANCE instance_handle = nullptr;
Expand Down Expand Up @@ -168,9 +220,6 @@ bool Backend::Initialize(const char* window_name, int width, int height, bool al
}

#ifdef RMLUI_USE_STB_IMAGE_LOADER
// Assign stb_image loader to render interface
data->render_interface.LoadTextureFromFileRaw = &LoadTexture;
data->render_interface.FreeTextureFromFileRaw = &FreeTexture;
// Disable pre-multiplication because loading images through stb_image will inconsistently return pre-multiplied images (only iPhone PNGs are pre-multipled)
// We handle pre-multiplication manually on decode
stbi_set_unpremultiply_on_load(true);
Expand Down Expand Up @@ -524,49 +573,4 @@ static void CleanupRenderTarget()
data->d3d_resources.pMainRenderTargetView->Release();
data->d3d_resources.pMainRenderTargetView = nullptr;
}
}

#ifdef RMLUI_USE_STB_IMAGE_LOADER
void LoadTexture(const Rml::String& filename, int* pWidth, int* pHeight, uint8_t** pData, size_t* pDataSize)
{
// Find where on disk the file is
Rml::FileInterface* file_interface = Rml::GetFileInterface();
Rml::FileHandle file_handle = file_interface->Open(filename);
if (!file_handle)
{
// Tell the backend that the data is invalid (*pData = 0), and return
*pData = nullptr;
return;
}

// Load the file through stb_image
int texture_width, texture_height, num_channels;
*pData = stbi_load_from_file((FILE*)file_handle, &texture_width, &texture_height, &num_channels, 0);

// If the file data is correct
if (*pData != nullptr)
{
// Assign image parameters
*pWidth = texture_width;
*pHeight = texture_height;

// Compute number of elements in texture
*pDataSize = texture_width * texture_height * num_channels;

// Pre-multiply the data
uint8_t* dataView = *pData;
for (int i = 0; i < *pDataSize; i += 4)
{
dataView[i + 0] = (uint8_t)((dataView[i + 0] * dataView[i + 3]) / 255);
dataView[i + 1] = (uint8_t)((dataView[i + 1] * dataView[i + 3]) / 255);
dataView[i + 2] = (uint8_t)((dataView[i + 2] * dataView[i + 3]) / 255);
}
}
}

void FreeTexture(uint8_t* pData)
{
// Free the allocated memory once the texture has been uploaded to the GPU
stbi_image_free(pData);
}
#endif
}
22 changes: 0 additions & 22 deletions Backends/RmlUi_Renderer_DX11.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1461,28 +1461,6 @@ struct TGAHeader {

Rml::TextureHandle RenderInterface_DX11::LoadTexture(Rml::Vector2i& texture_dimensions, const Rml::String& source)
{
// Use the user provided image loading function if it's provided, else fallback to the included TGA one
if (LoadTextureFromFileRaw != nullptr && FreeTextureFromFileRaw != nullptr)
{
int texture_width = 0, texture_height = 0;
size_t image_size_bytes = 0;
uint8_t* texture_data = nullptr;
LoadTextureFromFileRaw(source, &texture_width, &texture_height, &texture_data, &image_size_bytes);

if (texture_data != nullptr)
{
texture_dimensions.x = texture_width;
texture_dimensions.y = texture_height;

Rml::TextureHandle handle = GenerateTexture({texture_data, image_size_bytes}, texture_dimensions);

FreeTextureFromFileRaw(texture_data);
return handle;
}

// Image must be invalid if the file failed to load. Fallback to the default loader.
}

Rml::FileInterface* file_interface = Rml::GetFileInterface();
Rml::FileHandle file_handle = file_interface->Open(source);
if (!file_handle)
Expand Down
12 changes: 0 additions & 12 deletions Backends/RmlUi_Renderer_DX11.h
Original file line number Diff line number Diff line change
Expand Up @@ -130,18 +130,6 @@ class RenderInterface_DX11 : public Rml::RenderInterface {
// Can be passed to RenderGeometry() to not touch the cbuffers
static constexpr Rml::TextureHandle TexturePostprocessNoBinding = Rml::TextureHandle(-3);

public:
/// Called by the renderer when it wants to load a texture from disk.
/// @param[in] pFilename A Rml string containing the file name.
/// @param[out] pWidth The width of the texture read from disk.
/// @param[out] pHeight The height of the texture read from disk.
/// @param[out] pData A pointer to an RGBA byte array of texture data, which will then be used to generate a texture. Set to NULL to indicate that the file failed to load for whatever reason.
/// @param[out] pDataSize A pointer to a size_t storing how many bytes is in pData.
pfnLoadTextureRaw LoadTextureFromFileRaw = nullptr;
/// Called by the renderer when it wants to free a texture from disk. Always called after a successful load from LoadTextureFromFileRaw.
/// @param[in] pData A pointer to an RGBA byte array of texture data to free.
pfnFreeTextureRaw FreeTextureFromFileRaw = nullptr;

private:
// Changes blend state if necessary
void SetBlendState(ID3D11BlendState* blend_state);
Expand Down

0 comments on commit 8c1d982

Please sign in to comment.