From 3d4cdde7642fdf98966988117c22ccf87ee10d94 Mon Sep 17 00:00:00 2001 From: Cedric Guillemet Date: Thu, 24 Oct 2019 17:32:35 +0200 Subject: [PATCH 1/5] Image decoding/encoding --- Library/Source/NativeEngine.cpp | 57 +++++++++++++++++++++++++++++++++ Library/Source/NativeEngine.h | 15 +++++++++ TestApp/Scripts/experience.js | 22 +++++++++++++ 3 files changed, 94 insertions(+) diff --git a/Library/Source/NativeEngine.cpp b/Library/Source/NativeEngine.cpp index 49c8f1a343..0bb240fbee 100644 --- a/Library/Source/NativeEngine.cpp +++ b/Library/Source/NativeEngine.cpp @@ -356,6 +356,9 @@ namespace babylon InstanceMethod("clear", &NativeEngine::Clear), InstanceMethod("getRenderWidth", &NativeEngine::GetRenderWidth), InstanceMethod("getRenderHeight", &NativeEngine::GetRenderHeight), + InstanceMethod("decodeImage", &NativeEngine::DecodeImage), + InstanceMethod("getImageData", &NativeEngine::GetImageData), + InstanceMethod("encodeImage", &NativeEngine::EncodeImage), }); return Napi::Persistent(func); @@ -917,6 +920,60 @@ namespace babylon return Napi::External::New(info.Env(), new TextureData()); } + Napi::Value NativeEngine::DecodeImage(const Napi::CallbackInfo& info) + { + auto imageData = new ImageData(); + const auto buffer = info[0].As(); + + imageData->Image.reset(bimg::imageParse(&m_allocator, buffer.Data(), static_cast(buffer.ByteLength()))); + + return Napi::External::New(info.Env(), imageData); + } + + Napi::Value NativeEngine::GetImageData(const Napi::CallbackInfo& info) + { + const auto imageData = info[0].As>().Data(); + const auto buffer = info[0].As(); + + if (!imageData->Image || !imageData->Image->m_size) + { + return {}; + } + auto data = Napi::Int8Array::New(info.Env(), imageData->Image->m_size); + + const auto ptr = static_cast(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>().Data(); + if (!imageData->Image || !imageData->Image->m_size) + { + return {}; + } + + 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()); + + const auto ptr = static_cast(mb.more()); + for (uint32_t i = 0; i < imageData->Image->m_size; i++) + { + data[i] = ptr[i]; + } + + return data; + } + void NativeEngine::LoadTexture(const Napi::CallbackInfo& info) { const auto textureData = info[0].As>().Data(); diff --git a/Library/Source/NativeEngine.h b/Library/Source/NativeEngine.h index 01fc26f9b9..8cfec9d16e 100644 --- a/Library/Source/NativeEngine.h +++ b/Library/Source/NativeEngine.h @@ -231,6 +231,18 @@ namespace babylon bgfx::TextureHandle Texture{ bgfx::kInvalidHandle }; }; + struct ImageData final + { + ~ImageData() + { + if (Image) + { + bimg::imageFree(Image.get()); + } + } + std::unique_ptr Image; + }; + struct ProgramData final { ~ProgramData() @@ -360,6 +372,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; diff --git a/TestApp/Scripts/experience.js b/TestApp/Scripts/experience.js index 31fa295b55..118a694879 100644 --- a/TestApp/Scripts/experience.js +++ b/TestApp/Scripts/experience.js @@ -46,9 +46,29 @@ function CreateInputHandling(scene) { }); } +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(); var scene = new BABYLON.Scene(engine); +DecodeEncodeImage("https://github.com/BabylonJS/Babylon.js/raw/master/tests/validation/ReferenceImages/Billboard.png"); + CreateBoxAsync().then(function () { //CreateSpheresAsync().then(function () { //BABYLON.SceneLoader.AppendAsync("https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/master/2.0/Box/glTF/Box.gltf").then(function () { @@ -68,6 +88,8 @@ CreateBoxAsync().then(function () { //BABYLON.SceneLoader.AppendAsync("https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/master/2.0/CesiumMan/glTF/CesiumMan.gltf").then(function () { BABYLON.Tools.Log("Loaded"); + + scene.createDefaultCamera(true); scene.activeCamera.alpha += Math.PI; From 6c1df49f30296a279f88d31a7d8ba5857d8f0e5d Mon Sep 17 00:00:00 2001 From: Cedric Guillemet Date: Thu, 24 Oct 2019 17:39:15 +0200 Subject: [PATCH 2/5] removed empty lines --- TestApp/Scripts/experience.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/TestApp/Scripts/experience.js b/TestApp/Scripts/experience.js index 118a694879..9c0a3e1940 100644 --- a/TestApp/Scripts/experience.js +++ b/TestApp/Scripts/experience.js @@ -88,8 +88,6 @@ CreateBoxAsync().then(function () { //BABYLON.SceneLoader.AppendAsync("https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/master/2.0/CesiumMan/glTF/CesiumMan.gltf").then(function () { BABYLON.Tools.Log("Loaded"); - - scene.createDefaultCamera(true); scene.activeCamera.alpha += Math.PI; From 76025eb0134af7e86001d05d30dea6214caa7597 Mon Sep 17 00:00:00 2001 From: Cedric Guillemet Date: Fri, 25 Oct 2019 09:36:11 +0200 Subject: [PATCH 3/5] missing JSC function for typed array --- .../napi/source/js_native_api_JavaScriptCore.cc | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Library/Dependencies/napi/source/js_native_api_JavaScriptCore.cc b/Library/Dependencies/napi/source/js_native_api_JavaScriptCore.cc index fe8e2621b8..e4a8d44950 100644 --- a/Library/Dependencies/napi/source/js_native_api_JavaScriptCore.cc +++ b/Library/Dependencies/napi/source/js_native_api_JavaScriptCore.cc @@ -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, From 0615b3bebaa161b408e7d2648c5d19c41647494a Mon Sep 17 00:00:00 2001 From: Cedric Guillemet Date: Fri, 25 Oct 2019 18:06:32 +0200 Subject: [PATCH 4/5] moved test script for image codec into its own file --- TestApp/Scripts/experience.js | 20 ------------------ .../decode_encode_image_test.js | 21 +++++++++++++++++++ 2 files changed, 21 insertions(+), 20 deletions(-) create mode 100644 TestApp/ValidationTests/decode_encode_image_test.js diff --git a/TestApp/Scripts/experience.js b/TestApp/Scripts/experience.js index 9c0a3e1940..31fa295b55 100644 --- a/TestApp/Scripts/experience.js +++ b/TestApp/Scripts/experience.js @@ -46,29 +46,9 @@ function CreateInputHandling(scene) { }); } -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(); var scene = new BABYLON.Scene(engine); -DecodeEncodeImage("https://github.com/BabylonJS/Babylon.js/raw/master/tests/validation/ReferenceImages/Billboard.png"); - CreateBoxAsync().then(function () { //CreateSpheresAsync().then(function () { //BABYLON.SceneLoader.AppendAsync("https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/master/2.0/Box/glTF/Box.gltf").then(function () { diff --git a/TestApp/ValidationTests/decode_encode_image_test.js b/TestApp/ValidationTests/decode_encode_image_test.js new file mode 100644 index 0000000000..18c23233ff --- /dev/null +++ b/TestApp/ValidationTests/decode_encode_image_test.js @@ -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"); From f2e2c7fcc959e43d8557aac649f84f2d32fb56eb Mon Sep 17 00:00:00 2001 From: Cedric Guillemet Date: Mon, 28 Oct 2019 11:36:43 +0100 Subject: [PATCH 5/5] PR feeedback --- Library/Source/NativeEngine.cpp | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/Library/Source/NativeEngine.cpp b/Library/Source/NativeEngine.cpp index 43397820eb..aef101fd43 100644 --- a/Library/Source/NativeEngine.cpp +++ b/Library/Source/NativeEngine.cpp @@ -940,7 +940,7 @@ namespace babylon if (!imageData->Image || !imageData->Image->m_size) { - return {}; + return info.Env().Undefined(); } auto data = Napi::Int8Array::New(info.Env(), imageData->Image->m_size); @@ -958,7 +958,7 @@ namespace babylon const auto imageData = info[0].As>().Data(); if (!imageData->Image || !imageData->Image->m_size) { - return {}; + return info.Env().Undefined(); } const auto image = imageData->Image.get(); @@ -967,12 +967,7 @@ namespace babylon 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()); - - const auto ptr = static_cast(mb.more()); - for (uint32_t i = 0; i < imageData->Image->m_size; i++) - { - data[i] = ptr[i]; - } + memcpy(data.Data(), static_cast(mb.more()), imageData->Image->m_size); return data; }