Skip to content

Commit

Permalink
Base64 Image support (#1328)
Browse files Browse the repository at this point in the history
fixes #1227
  • Loading branch information
CedricGuillemet committed Dec 5, 2023
1 parent 661a5a2 commit af2d6eb
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 15 deletions.
1 change: 1 addition & 0 deletions Polyfills/Canvas/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ target_link_libraries(Canvas
PRIVATE JsRuntimeInternal
PRIVATE GraphicsDeviceContext
PRIVATE UrlLib
PRIVATE base-n
PRIVATE napi_extensions)

set_property(TARGET Canvas PROPERTY FOLDER Polyfills)
Expand Down
4 changes: 4 additions & 0 deletions Polyfills/Canvas/Source/Canvas.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,10 @@ namespace Babylon::Polyfills

void Canvas::Impl::RemoveMonitoredResource(MonitoredResource* monitoredResource)
{
if (m_monitoredResources.empty())
{
return;
}
auto iter = std::find(m_monitoredResources.begin(), m_monitoredResources.end(), monitoredResource);
if (iter != m_monitoredResources.end())
{
Expand Down
59 changes: 44 additions & 15 deletions Polyfills/Canvas/Source/Image.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "nanovg.h"
#include <cassert>
#include <napi/napi_pointer.h>
#include <basen.hpp>

namespace Babylon::Polyfills::Internal
{
Expand Down Expand Up @@ -97,13 +98,52 @@ namespace Babylon::Polyfills::Internal
}
}

bool NativeCanvasImage::SetBuffer(gsl::span<const std::byte> buffer)
{
m_imageContainer = bimg::imageParse(&Graphics::DeviceContext::GetDefaultAllocator(), buffer.data(), static_cast<uint32_t>(buffer.size_bytes()), bimg::TextureFormat::RGBA8);

if (m_imageContainer == nullptr)
{
return false;
}

m_width = m_imageContainer->m_width;
m_height = m_imageContainer->m_height;

if (!m_onloadHandlerRef.IsEmpty())
{
m_onloadHandlerRef.Call({});
}
return true;
}

void NativeCanvasImage::SetSrc(const Napi::CallbackInfo& info, const Napi::Value& value)
{
auto text{value.As<Napi::String>().Utf8Value()};

// try with base64
static const std::string base64{"base64,"};
const auto pos = text.find(base64);
if (pos != std::string::npos)
{
arcana::make_task(m_runtimeScheduler, *m_cancellationSource, [env{info.Env()}, this, text{std::move(text)}, pos]() {
std::vector<uint8_t> base64Buffer;
bn::decode_b64(text.begin() + pos + base64.length(), text.end(), std::back_inserter(base64Buffer));
gsl::span<const std::byte> buffer = {reinterpret_cast<std::byte*>(base64Buffer.data()), base64Buffer.size()};

if (!SetBuffer(buffer))
{
HandleLoadImageError(Napi::Error::New(env, "Unable to decode image with provided base64 source."));
}
});
return;
}

// try with URL
UrlLib::UrlRequest request{};
request.Open(UrlLib::UrlMethod::Get, text);
request.ResponseType(UrlLib::UrlResponseType::Buffer);
request.SendAsync().then(m_runtimeScheduler, *m_cancellationSource, [env{info.Env()}, this, request{std::move(request)}](arcana::expected<void, std::exception_ptr> result) {
request.SendAsync().then(m_runtimeScheduler, *m_cancellationSource, [env{info.Env()}, this, cancellationSource{m_cancellationSource}, request{std::move(request)}, text](arcana::expected<void, std::exception_ptr> result) {
if (result.has_error())
{
HandleLoadImageError(Napi::Error::New(env, result.error()));
Expand All @@ -115,24 +155,13 @@ namespace Babylon::Polyfills::Internal
auto buffer{request.ResponseBuffer()};
if (buffer.data() == nullptr || buffer.size_bytes() == 0)
{
HandleLoadImageError(Napi::Error::New(env, "Image with provided source returned empty response."));
HandleLoadImageError(Napi::Error::New(env, "Image with provided source returned empty response or invalid base64."));
return;
}

m_imageContainer = bimg::imageParse(&Graphics::DeviceContext::GetDefaultAllocator(), buffer.data(), static_cast<uint32_t>(buffer.size_bytes()), bimg::TextureFormat::RGBA8);

if (m_imageContainer == nullptr)
{
HandleLoadImageError(Napi::Error::New(env, "Unable to decode image with provided src."));
return;
}

m_width = m_imageContainer->m_width;
m_height = m_imageContainer->m_height;

if (!m_onloadHandlerRef.IsEmpty())
if (!SetBuffer(buffer))
{
m_onloadHandlerRef.Call({});
HandleLoadImageError(Napi::Error::New(env, "Unable to decode image with provided source URL."));
}
});
}
Expand Down
1 change: 1 addition & 0 deletions Polyfills/Canvas/Source/Image.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ namespace Babylon::Polyfills::Internal
void SetOnload(const Napi::CallbackInfo&, const Napi::Value&);
void SetOnerror(const Napi::CallbackInfo&, const Napi::Value&);
void HandleLoadImageError(const Napi::Error& error);
bool SetBuffer(gsl::span<const std::byte> buffer);
void Dispose();

uint32_t m_width{1};
Expand Down

0 comments on commit af2d6eb

Please sign in to comment.