Skip to content

Commit

Permalink
add RGBA_to_BGRA function
Browse files Browse the repository at this point in the history
  • Loading branch information
beru committed Oct 19, 2024
1 parent d69a9a9 commit a30bd9b
Showing 1 changed file with 68 additions and 63 deletions.
131 changes: 68 additions & 63 deletions modules/svg/image_loader_svg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,64 +89,8 @@ Ref<Image> ImageLoaderSVG::load_mem_svg(const uint8_t *p_svg, int p_size, float
return img;
}

Error ImageLoaderSVG::create_image_from_utf8_buffer(Ref<Image> p_image, const uint8_t *p_buffer, int p_buffer_size, float p_scale, bool p_upsample) {
ERR_FAIL_COND_V_MSG(Math::is_zero_approx(p_scale), ERR_INVALID_PARAMETER, "ImageLoaderSVG: Can't load SVG with a scale of 0.");

std::unique_ptr<tvg::Picture> picture = tvg::Picture::gen();

tvg::Result result = picture->load((const char *)p_buffer, p_buffer_size, "svg", true);
if (result != tvg::Result::Success) {
return ERR_INVALID_DATA;
}
float fw, fh;
picture->size(&fw, &fh);

uint32_t width = MAX(1, round(fw * p_scale));
uint32_t height = MAX(1, round(fh * p_scale));

const uint32_t max_dimension = 16384;
if (width > max_dimension || height > max_dimension) {
WARN_PRINT(vformat(
String::utf8("ImageLoaderSVG: Target canvas dimensions %d×%d (with scale %.2f) exceed the max supported dimensions %d×%d. The target canvas will be scaled down."),
width, height, p_scale, max_dimension, max_dimension));
width = MIN(width, max_dimension);
height = MIN(height, max_dimension);
}

picture->size(width, height);

std::unique_ptr<tvg::SwCanvas> sw_canvas = tvg::SwCanvas::gen();
// Note: memalloc here, be sure to memfree before any return.
uint32_t *buffer = (uint32_t *)memalloc(sizeof(uint32_t) * width * height);

tvg::Result res = sw_canvas->target(buffer, width, width, height, tvg::SwCanvas::ARGB8888S);
if (res != tvg::Result::Success) {
memfree(buffer);
ERR_FAIL_V_MSG(FAILED, "ImageLoaderSVG: Couldn't set target on ThorVG canvas.");
}

res = sw_canvas->push(std::move(picture));
if (res != tvg::Result::Success) {
memfree(buffer);
ERR_FAIL_V_MSG(FAILED, "ImageLoaderSVG: Couldn't insert ThorVG picture on canvas.");
}

res = sw_canvas->draw();
if (res != tvg::Result::Success) {
memfree(buffer);
ERR_FAIL_V_MSG(FAILED, "ImageLoaderSVG: Couldn't draw ThorVG pictures on canvas.");
}

res = sw_canvas->sync();
if (res != tvg::Result::Success) {
memfree(buffer);
ERR_FAIL_V_MSG(FAILED, "ImageLoaderSVG: Couldn't sync ThorVG canvas.");
}

Vector<uint8_t> image;
image.resize(width * height * sizeof(uint32_t));

// RGBA to BGRA
static
void RGBA_to_BGRA(uint32_t width, uint32_t height, Vector<uint8_t> &image, uint32_t *buffer) {
const uint32_t wh = width * height;
uint32_t i;
#if defined(__AVX512BW__)
Expand Down Expand Up @@ -181,10 +125,11 @@ Error ImageLoaderSVG::create_image_from_utf8_buffer(Ref<Image> p_image, const ui
}
i = wh / 8 * 8;
}
#elif defined(__AVX__)
// _mm_shuffle_epi8 is available from SSSE3 but VC++ doesn't define __SSSE3__ when "/arch:AVX" is specified so __AVX__ is used instead.
// On the other hand, GCC supports fine-grained CFLAGS options such as -mssse3.
// Recent x86-64 processors mostly support AVX/AVX2, so outdated processors aren't convered here.
#elif defined(__AVX__) || defined(__SSSE3__)
// _mm_shuffle_epi8 is available from SSSE3
// Recent x86-64 processors support AVX/AVX2 and legacy SSE instructions are covered within AVX instructions.
// VC++ doesn't define __SSSE3__ when "/arch:AVX" is specified so __AVX__ is used for VC++.
// GCC supports fine-grained CFLAGS options such as -mssse3.
{
uint8_t *dst = image.ptrw();
const __m128i mask = _mm_setr_epi8(2, 1, 0, 3, 6, 5, 4, 7, 10, 9, 8, 11, 14, 13, 12, 15);
Expand All @@ -198,7 +143,7 @@ Error ImageLoaderSVG::create_image_from_utf8_buffer(Ref<Image> p_image, const ui
}
#elif defined(__ARM_NEON) || defined(__ARM_NEON__)
// vqtbl1q_u8 is available from ARMv8.
// ARMv7 NEON doesn't support vqtbl1q_u8 but I suppose no one runs the engine on ARVv7 processors.
// ARMv7 NEON doesn't support vqtbl1q_u8.
{
uint8_t *dst = image.ptrw();
const uint8x16_t mask = { 2, 1, 0, 3, 6, 5, 4, 7, 10, 9, 8, 11, 14, 13, 12, 15 };
Expand All @@ -221,6 +166,66 @@ Error ImageLoaderSVG::create_image_from_utf8_buffer(Ref<Image> p_image, const ui
image.write[offset + 2] = n & 0xff;
image.write[offset + 3] = (n >> 24) & 0xff;
}
}

Error ImageLoaderSVG::create_image_from_utf8_buffer(Ref<Image> p_image, const uint8_t *p_buffer, int p_buffer_size, float p_scale, bool p_upsample) {
ERR_FAIL_COND_V_MSG(Math::is_zero_approx(p_scale), ERR_INVALID_PARAMETER, "ImageLoaderSVG: Can't load SVG with a scale of 0.");

std::unique_ptr<tvg::Picture> picture = tvg::Picture::gen();

tvg::Result result = picture->load((const char *)p_buffer, p_buffer_size, "svg", true);
if (result != tvg::Result::Success) {
return ERR_INVALID_DATA;
}
float fw, fh;
picture->size(&fw, &fh);

uint32_t width = MAX(1, round(fw * p_scale));
uint32_t height = MAX(1, round(fh * p_scale));

const uint32_t max_dimension = 16384;
if (width > max_dimension || height > max_dimension) {
WARN_PRINT(vformat(
String::utf8("ImageLoaderSVG: Target canvas dimensions %d×%d (with scale %.2f) exceed the max supported dimensions %d×%d. The target canvas will be scaled down."),
width, height, p_scale, max_dimension, max_dimension));
width = MIN(width, max_dimension);
height = MIN(height, max_dimension);
}

picture->size(width, height);

std::unique_ptr<tvg::SwCanvas> sw_canvas = tvg::SwCanvas::gen();
// Note: memalloc here, be sure to memfree before any return.
uint32_t *buffer = (uint32_t *)memalloc(sizeof(uint32_t) * width * height);

tvg::Result res = sw_canvas->target(buffer, width, width, height, tvg::SwCanvas::ARGB8888S);
if (res != tvg::Result::Success) {
memfree(buffer);
ERR_FAIL_V_MSG(FAILED, "ImageLoaderSVG: Couldn't set target on ThorVG canvas.");
}

res = sw_canvas->push(std::move(picture));
if (res != tvg::Result::Success) {
memfree(buffer);
ERR_FAIL_V_MSG(FAILED, "ImageLoaderSVG: Couldn't insert ThorVG picture on canvas.");
}

res = sw_canvas->draw();
if (res != tvg::Result::Success) {
memfree(buffer);
ERR_FAIL_V_MSG(FAILED, "ImageLoaderSVG: Couldn't draw ThorVG pictures on canvas.");
}

res = sw_canvas->sync();
if (res != tvg::Result::Success) {
memfree(buffer);
ERR_FAIL_V_MSG(FAILED, "ImageLoaderSVG: Couldn't sync ThorVG canvas.");
}

Vector<uint8_t> image;
image.resize(width * height * sizeof(uint32_t));

RGBA_to_BGRA(width, height, image, buffer);

res = sw_canvas->clear(true);
memfree(buffer);
Expand Down

0 comments on commit a30bd9b

Please sign in to comment.