Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
11 changes: 11 additions & 0 deletions Library/Dependencies/napi/source/js_native_api_JavaScriptCore.cc
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,17 @@ napi_status napi_create_array_with_length(napi_env env,
return napi_ok;
}


napi_status napi_create_typedarray(napi_env env,
napi_typedarray_type type,
size_t length,
napi_value arraybuffer,
size_t byte_offset,
napi_value* result) {
assert(0);
return napi_ok;
}

napi_status napi_create_string_utf16(napi_env env,
const char16_t* str,
size_t length,
Expand Down
52 changes: 52 additions & 0 deletions Library/Source/NativeEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,9 @@ namespace babylon
InstanceMethod("clearStencil", &NativeEngine::ClearStencil),
InstanceMethod("getRenderWidth", &NativeEngine::GetRenderWidth),
InstanceMethod("getRenderHeight", &NativeEngine::GetRenderHeight),
InstanceMethod("decodeImage", &NativeEngine::DecodeImage),
InstanceMethod("getImageData", &NativeEngine::GetImageData),
InstanceMethod("encodeImage", &NativeEngine::EncodeImage),
});

return Napi::Persistent(func);
Expand Down Expand Up @@ -920,6 +923,55 @@ namespace babylon
return Napi::External<TextureData>::New(info.Env(), new TextureData());
}

Napi::Value NativeEngine::DecodeImage(const Napi::CallbackInfo& info)
{
auto imageData = new ImageData();
const auto buffer = info[0].As<Napi::ArrayBuffer>();

imageData->Image.reset(bimg::imageParse(&m_allocator, buffer.Data(), static_cast<uint32_t>(buffer.ByteLength())));

return Napi::External<ImageData>::New(info.Env(), imageData);
}

Napi::Value NativeEngine::GetImageData(const Napi::CallbackInfo& info)
{
const auto imageData = info[0].As<Napi::External<ImageData>>().Data();
const auto buffer = info[0].As<Napi::ArrayBuffer>();

if (!imageData->Image || !imageData->Image->m_size)
{
return info.Env().Undefined();
}
auto data = Napi::Int8Array::New(info.Env(), imageData->Image->m_size);

const auto ptr = static_cast<uint8_t*>(imageData->Image->m_data);
for (uint32_t i = 0; i < imageData->Image->m_size; i++)
{
data[i] = ptr[i];
}

return data;
}

Napi::Value NativeEngine::EncodeImage(const Napi::CallbackInfo& info)
{
const auto imageData = info[0].As<Napi::External<ImageData>>().Data();
if (!imageData->Image || !imageData->Image->m_size)
{
return info.Env().Undefined();
}

const auto image = imageData->Image.get();
bx::MemoryBlock mb(&m_allocator);
bx::MemoryWriter writer(&mb);
bimg::imageWritePng(&writer, image->m_width, image->m_height, image->m_size/image->m_height, image->m_data, image->m_format, false);

auto data = Napi::Int8Array::New(info.Env(), mb.getSize());
memcpy(data.Data(), static_cast<uint8_t*>(mb.more()), imageData->Image->m_size);

return data;
}

void NativeEngine::LoadTexture(const Napi::CallbackInfo& info)
{
const auto textureData = info[0].As<Napi::External<TextureData>>().Data();
Expand Down
15 changes: 15 additions & 0 deletions Library/Source/NativeEngine.h
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,18 @@ namespace babylon
bgfx::TextureHandle Texture{ bgfx::kInvalidHandle };
};

struct ImageData final

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you replace this struct with just a using to a unique_ptr with a custom deleter?

https://stackoverflow.com/questions/19053351/how-do-i-use-a-custom-deleter-with-a-stdunique-ptr-member

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the idea but I didn't manage to do it properly. I ended up having another struct with an operator for deleter. Merging with the ImageData struct for now. If you have an implementation with a custom deleter, I'd be curious to take a look at it :)

{
~ImageData()
{
if (Image)
{
bimg::imageFree(Image.get());
}
}
std::unique_ptr<bimg::ImageContainer> Image;
};

struct ProgramData final
{
~ProgramData()
Expand Down Expand Up @@ -382,6 +394,9 @@ namespace babylon
void UpdateSize(size_t width, size_t height);
void DispatchAnimationFrameAsync(Napi::FunctionReference callback);

Napi::Value DecodeImage(const Napi::CallbackInfo& info);
Napi::Value GetImageData(const Napi::CallbackInfo& info);
Napi::Value EncodeImage(const Napi::CallbackInfo& info);

ShaderCompiler m_shaderCompiler;

Expand Down
21 changes: 21 additions & 0 deletions TestApp/ValidationTests/decode_encode_image_test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
function DecodeEncodeImage(url)
{
let onLoadFileError = function(request, exception) {
console.log("Failed to retrieve " + url + ".", exception);
};
var onload = function(data, responseURL) {
if (typeof (data) === "string") {
throw new Error("Decode Image from string data not yet implemented.");
}
var image = engine._native.decodeImage(data);
var imageData = engine._native.getImageData(image);
console.log("Image data length is " + imageData.length);
var encoded = engine._native.encodeImage(image);
console.log("Encoded Image data length is " + encoded.length);
}
BABYLON.Tools.LoadFile(url, onload, undefined, undefined, /*useArrayBuffer*/true, onLoadFileError);
}

var engine = new BABYLON.NativeEngine();

DecodeEncodeImage("https://github.com/BabylonJS/Babylon.js/raw/master/tests/validation/ReferenceImages/Billboard.png");