From 479cea74cc0163571152203c0a31bb0b925b8473 Mon Sep 17 00:00:00 2001 From: Michal Maciola Date: Tue, 14 Sep 2021 09:29:14 +0200 Subject: [PATCH] common: Unmultiplicated colorspace This patch introduces _STRAIGHT colorspaces (ABGR8888_STRAIGHT and ARGB8888_STRAIGHT) whose colors are un-alpha-premultiplied. Unmultiplicated colors are especially needed for wasm thorvg loader and svg2png / tvg2png. Only C version now. @issue: #791 --- inc/thorvg.h | 6 ++++-- src/bin/svg2png/svg2png.cpp | 2 +- src/lib/sw_engine/tvgSwCommon.h | 1 + src/lib/sw_engine/tvgSwRaster.cpp | 29 +++++++++++++++++++++++++++-- src/lib/sw_engine/tvgSwRenderer.cpp | 5 +++++ src/wasm/thorvgwasm.cpp | 2 +- 6 files changed, 39 insertions(+), 6 deletions(-) diff --git a/inc/thorvg.h b/inc/thorvg.h index 1b2afdf3..e5dcc668 100644 --- a/inc/thorvg.h +++ b/inc/thorvg.h @@ -1310,8 +1310,10 @@ public: */ enum Colorspace { - ABGR8888 = 0, ///< The channels are joined in the order: alpha, blue, green, red. - ARGB8888 ///< The channels are joined in the order: alpha, red, green, blue. + ABGR8888 = 0, ///< The channels are joined in the order: alpha, blue, green, red. Colors are alpha-premultiplied. + ARGB8888, ///< The channels are joined in the order: alpha, red, green, blue. Colors are alpha-premultiplied. + ABGR8888_STRAIGHT, ///< @BETA_API The channels are joined in the order: alpha, blue, green, red. Colors are un-alpha-premultiplied. + ARGB8888_STRAIGHT, ///< @BETA_API The channels are joined in the order: alpha, red, green, blue. Colors are un-alpha-premultiplied. }; /** diff --git a/src/bin/svg2png/svg2png.cpp b/src/bin/svg2png/svg2png.cpp index 6428c332..c6469189 100644 --- a/src/bin/svg2png/svg2png.cpp +++ b/src/bin/svg2png/svg2png.cpp @@ -90,7 +90,7 @@ public: return 1; } - if (canvas->target(buffer, w, w, h, tvg::SwCanvas::ARGB8888) != tvg::Result::Success) { + if (canvas->target(buffer, w, w, h, tvg::SwCanvas::ARGB8888_STRAIGHT) != tvg::Result::Success) { cout << "Error: Canvas target failure" << endl; return 1; } diff --git a/src/lib/sw_engine/tvgSwCommon.h b/src/lib/sw_engine/tvgSwCommon.h index 4f6968d0..85b9052c 100644 --- a/src/lib/sw_engine/tvgSwCommon.h +++ b/src/lib/sw_engine/tvgSwCommon.h @@ -358,5 +358,6 @@ bool rasterStroke(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint bool rasterGradientStroke(SwSurface* surface, SwShape* shape, unsigned id); bool rasterClear(SwSurface* surface); void rasterRGBA32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len); +void rasterUnpremultiply(SwSurface* surface); #endif /* _TVG_SW_COMMON_H_ */ diff --git a/src/lib/sw_engine/tvgSwRaster.cpp b/src/lib/sw_engine/tvgSwRaster.cpp index 35d0ba4d..e4a0092f 100644 --- a/src/lib/sw_engine/tvgSwRaster.cpp +++ b/src/lib/sw_engine/tvgSwRaster.cpp @@ -1399,10 +1399,10 @@ void rasterRGBA32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len) bool rasterCompositor(SwSurface* surface) { - if (surface->cs == SwCanvas::ABGR8888) { + if (surface->cs == SwCanvas::ABGR8888 || surface->cs == SwCanvas::ABGR8888_STRAIGHT) { surface->blender.alpha = _colorAlpha; surface->blender.join = _abgrJoin; - } else if (surface->cs == SwCanvas::ARGB8888) { + } else if (surface->cs == SwCanvas::ARGB8888 || surface->cs == SwCanvas::ARGB8888_STRAIGHT) { surface->blender.alpha = _colorAlpha; surface->blender.join = _argbJoin; } else { @@ -1515,6 +1515,31 @@ bool rasterClear(SwSurface* surface) } +void rasterUnpremultiply(SwSurface* surface) +{ + //TODO: Create simd avx and neon version + for (uint32_t y = 0; y < surface->h; y++) { + auto buffer = surface->buffer + surface->stride * y; + for (uint32_t x = 0; x < surface->w; ++x) { + uint8_t a = buffer[x] >> 24; + if (a == 255) { + continue; + } else if (a == 0) { + buffer[x] = 0x00ffffff; + } else { + uint16_t r = ((buffer[x] >> 8) & 0xff00) / a; + uint16_t g = ((buffer[x]) & 0xff00) / a; + uint16_t b = ((buffer[x] << 8) & 0xff00) / a; + if (r > 0xff) r = 0xff; + if (g > 0xff) g = 0xff; + if (b > 0xff) b = 0xff; + buffer[x] = (a << 24) | (r << 16) | (g << 8) | (b); + } + } + } +} + + bool rasterImage(SwSurface* surface, SwImage* image, const Matrix* transform, const SwBBox& bbox, uint32_t opacity) { Matrix invTransform; diff --git a/src/lib/sw_engine/tvgSwRenderer.cpp b/src/lib/sw_engine/tvgSwRenderer.cpp index 81cb80aa..8b3ec2c1 100644 --- a/src/lib/sw_engine/tvgSwRenderer.cpp +++ b/src/lib/sw_engine/tvgSwRenderer.cpp @@ -318,6 +318,11 @@ void SwRenderer::clearCompositors() bool SwRenderer::postRender() { + //Unmultiply alpha if needed + if (surface->cs == SwCanvas::ABGR8888_STRAIGHT || surface->cs == SwCanvas::ARGB8888_STRAIGHT) { + rasterUnpremultiply(surface); + } + tasks.clear(); clearCompositors(); return true; diff --git a/src/wasm/thorvgwasm.cpp b/src/wasm/thorvgwasm.cpp index ba76449f..ca615ab7 100644 --- a/src/wasm/thorvgwasm.cpp +++ b/src/wasm/thorvgwasm.cpp @@ -224,7 +224,7 @@ private: mWidth = width; mHeight = height; mBuffer = make_unique(mWidth * mHeight * 4); - mSwCanvas->target((uint32_t *)mBuffer.get(), mWidth, mWidth, mHeight, SwCanvas::ABGR8888); + mSwCanvas->target((uint32_t *)mBuffer.get(), mWidth, mWidth, mHeight, SwCanvas::ABGR8888_STRAIGHT); if (mPicture) mPicture->size(width, height); }