Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
5b174c4
Add NativeEncoding plugin
alexchuber Oct 15, 2025
d201676
Update installations
alexchuber Oct 15, 2025
5c1468f
Add readme
alexchuber Oct 15, 2025
f874d8e
Capitalize and add readme
alexchuber Oct 15, 2025
34db646
Add error handling
alexchuber Oct 20, 2025
dcccbd8
Promisify EncodeImage
alexchuber Oct 20, 2025
e9fd332
Asynchronize EncodeImage
alexchuber Oct 20, 2025
7ae9345
Add NativeEncoding to installation test
alexchuber Oct 20, 2025
7ec843e
Add unit tests
alexchuber Oct 20, 2025
3002bcf
Update readme
alexchuber Oct 20, 2025
c8e9f3c
Remove NativeEncoding from Playground (will handle in separate PR)
alexchuber Oct 20, 2025
be49d00
Update tests
alexchuber Oct 20, 2025
bf18698
Test async operations
alexchuber Oct 20, 2025
7718f35
Add comments
alexchuber Oct 21, 2025
507b939
Copy buffer & tie cancellation to JS env lifetime
alexchuber Oct 21, 2025
ef5c562
Meh, remove cancellation source until better pattern comes up
alexchuber Oct 21, 2025
aa84b88
Remove teardown test (known crash) and block test (not much of a point)
alexchuber Oct 22, 2025
9071bf3
Remove a copy; clean up syntax
alexchuber Oct 22, 2025
1cc8713
Comments
alexchuber Oct 22, 2025
ea0a2b1
Remove unneeded shared_ptr
alexchuber Oct 22, 2025
d3acc69
Debugging notes
alexchuber Oct 24, 2025
baa96e0
Remove const using cast in order to prevent copy
alexchuber Oct 24, 2025
d3a8d9c
Experiment with shared_ptr return
alexchuber Oct 24, 2025
dde1c28
Experiment with unique_ptr return
alexchuber Oct 25, 2025
903738d
Notes
alexchuber Oct 25, 2025
40b4628
PR feedback
alexchuber Oct 25, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Apps/Playground/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ target_link_libraries(Playground
PRIVATE Window
PRIVATE XMLHttpRequest
PRIVATE TestUtils
PRIVATE NativeEncoding
${ADDITIONAL_LIBRARIES}
${BABYLON_NATIVE_PLAYGROUND_EXTENSION_LIBRARIES})

Expand Down
3 changes: 3 additions & 0 deletions Apps/Playground/Win32/App.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include <Babylon/Plugins/NativeOptimizations.h>
#include <Babylon/Plugins/NativeCamera.h>
#include <Babylon/Plugins/NativeInput.h>
#include <Babylon/Plugins/NativeEncoding.h>
#include <Babylon/Plugins/TestUtils.h>
#include <Babylon/Polyfills/Console.h>
#include <Babylon/Polyfills/Window.h>
Expand Down Expand Up @@ -192,6 +193,8 @@ namespace

nativeInput = &Babylon::Plugins::NativeInput::CreateForJavaScript(env);

Babylon::Plugins::NativeEncoding::Initialize(env);

Babylon::Plugins::TestUtils::Initialize(env, hWnd);
});

Expand Down
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ option(BABYLON_NATIVE_PLUGIN_NATIVEOPTIMIZATIONS "Include Babylon Native Plugin
option(BABYLON_NATIVE_PLUGIN_NATIVETRACING "Include Babylon Native Plugin NativeTracing." ON)
option(BABYLON_NATIVE_PLUGIN_NATIVEXR "Include Babylon Native Plugin XR." ON)
option(BABYLON_NATIVE_PLUGIN_TESTUTILS "Include Babylon Native Plugin TestUtils." ON)
option(BABYLON_NATIVE_PLUGIN_NATIVEENCODING "Include Babylon Native Plugin NativeEncoding." ON)

# Polyfills
option(BABYLON_NATIVE_POLYFILL_WINDOW "Include Babylon Native Polyfill Window." ON)
Expand Down
5 changes: 5 additions & 0 deletions Install/Install.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,11 @@ if(TARGET NativeXr)
install_include_for_targets(NativeXr)
endif()

if(TARGET NativeEncoding)
install_targets(NativeEncoding)
install_include_for_targets(NativeEncoding)
endif()

# ----------------
# Polyfills
# ----------------
Expand Down
4 changes: 4 additions & 0 deletions Plugins/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,7 @@ endif()
if(BABYLON_NATIVE_PLUGIN_NATIVEXR AND (ANDROID OR IOS))
add_subdirectory(NativeXr)
endif()

if(BABYLON_NATIVE_PLUGIN_NATIVEENCODING)
add_subdirectory(NativeEncoding)
endif()
18 changes: 18 additions & 0 deletions Plugins/NativeEncoding/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
set(SOURCES
"Include/Babylon/Plugins/NativeEncoding.h"
"Source/NativeEncoding.cpp")

add_library(NativeEncoding ${SOURCES})
warnings_as_errors(NativeEncoding)

target_include_directories(NativeEncoding
PUBLIC "Include")

target_link_libraries(NativeEncoding
PUBLIC napi
PRIVATE GraphicsDevice
PRIVATE GraphicsDeviceContext
PRIVATE JsRuntimeInternal)

set_property(TARGET NativeEncoding PROPERTY FOLDER Plugins)
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${SOURCES})
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#pragma once

#include <napi/env.h>
#include <Babylon/Api.h>

namespace Babylon::Plugins::NativeEncoding
{
void BABYLON_API Initialize(Napi::Env env);
}
28 changes: 28 additions & 0 deletions Plugins/NativeEncoding/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# NativeEncoding

> ⚠️ **This plugin is experimental and subject to change.**

The NativeEncoding plugin provides native image encoding capabilities to Babylon, allowing raw pixel data to be encoded into standard image formats (PNG, JPEG, WebP, etc.).

## Design

Unlike a traditional polyfill which would implement Canvas's `toBlob()` or `toDataURL()` methods, NativeEncoding exists as a plugin because:
1. **No standard Web API exists** for general-purpose image encoding separate from Canvas
2. **Simplicity** - Exposes only what Babylon actually needs: direct pixel-to-bytes encoding
3. **Efficiency** - Avoids extra routing through the Canvas API via intermediate data structures
4. **Modularity** - Image encoding is a separate concern from 2D canvas rendering
5. **Extensibility** - New codecs can be added in the future without bloating other components

An encoding function is exposed on the `_native` global object, similar to NativeOptimizations.

```typescript
interface INative {
EncodeImage: (pixelData: Uint8Array, width: number, height: number, mimeType: string, invertY: boolean) => ArrayBuffer;
}
```

It should be wrapped by higher-level Babylon.js APIs (e.g., DumpTools) for common workflows like asset exports and screenshots.

## Limitations

Currently, **only PNG encoding** is supported.
58 changes: 58 additions & 0 deletions Plugins/NativeEncoding/Source/NativeEncoding.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#include <Babylon/Plugins/NativeEncoding.h>
#include <Babylon/JsRuntime.h>

#include <napi/napi.h>
#include <Babylon/JsRuntime.h>
#include <Babylon/Graphics/DeviceContext.h>
#include <bimg/encode.h>
#include <bx/readerwriter.h>

namespace Babylon::Plugins
{
namespace
{
Napi::ArrayBuffer EncodePNG(Napi::Env env, const uint8_t* pixelData, uint32_t width, uint32_t height, bool invertY)
{
bx::MemoryBlock memoryBlock(&Graphics::DeviceContext::GetDefaultAllocator());
bx::MemoryWriter writer(&memoryBlock);

bimg::imageWritePng(&writer, width, height, width * 4, pixelData, bimg::TextureFormat::RGBA8, invertY);

auto byteLength = memoryBlock.getSize();
auto result = Napi::ArrayBuffer::New(env, byteLength);
std::memcpy(result.Data(), memoryBlock.more(0), byteLength);

return result;
}

Napi::Value EncodeImage(const Napi::CallbackInfo& info)
{
const auto buffer = info[0].As<Napi::Uint8Array>();
const auto width = info[1].As<Napi::Number>().Uint32Value();
const auto height = info[2].As<Napi::Number>().Uint32Value();
const auto mimeType = info[3].As<Napi::String>().Utf8Value();
const auto invertY = info[4].As<Napi::Boolean>().Value();

if (buffer.ByteLength() != width * height * 4)
{
throw Napi::RangeError::New(info.Env(), "Buffer byte length is insufficient for RGBA8 image of provided dimensions.");
}

if (mimeType == "image/png")
{
return EncodePNG(info.Env(), buffer.Data(), width, height, invertY);
}

throw Napi::Error::New(info.Env(), "Unsupported mime type: " + mimeType + ". Only image/png is currently supported.");
}
}
}

namespace Babylon::Plugins::NativeEncoding
{
void BABYLON_API Initialize(Napi::Env env)
{
auto native = JsRuntime::NativeObject::GetFromJavaScript(env);
native.Set("EncodeImage", Napi::Function::New(env, EncodeImage, "EncodeImage"));
}
}