From e9b12aa9f76b417fe1cca63da6bf6e76f4289ebb Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Fri, 28 Apr 2023 16:43:18 +0900 Subject: [PATCH] engine/loaders: Migrate alpha premultiplying to the engine side. It's not efficient to handle alpha premultiplying in every loader. The backend engine should be responsible for it. Now, we can remove duplicate code. --- src/lib/gl_engine/tvgGlRenderer.h | 2 +- src/lib/sw_engine/tvgSwCommon.h | 3 ++- src/lib/sw_engine/tvgSwRaster.cpp | 23 ++++++++++++++++++- src/lib/sw_engine/tvgSwRasterC.h | 2 +- src/lib/sw_engine/tvgSwRenderer.cpp | 7 +++++- src/lib/tvgPictureImpl.h | 11 ++++----- src/lib/tvgRender.h | 8 ++++--- src/loaders/external_jpg/tvgJpgLoader.cpp | 5 ++++- src/loaders/external_png/tvgPngLoader.cpp | 25 ++++----------------- src/loaders/jpg/tvgJpgLoader.cpp | 5 ++++- src/loaders/png/tvgPngLoader.cpp | 27 ++++------------------- src/loaders/raw/tvgRawLoader.cpp | 5 ++++- 12 files changed, 61 insertions(+), 62 deletions(-) diff --git a/src/lib/gl_engine/tvgGlRenderer.h b/src/lib/gl_engine/tvgGlRenderer.h index a3f0b561..dbbf347d 100644 --- a/src/lib/gl_engine/tvgGlRenderer.h +++ b/src/lib/gl_engine/tvgGlRenderer.h @@ -28,7 +28,7 @@ class GlRenderer : public RenderMethod { public: - Surface surface = {nullptr, 0, 0, 0}; + Surface surface = {nullptr, 0, 0, 0, ColorSpace::Unsupported, true}; RenderData prepare(const RenderShape& rshape, RenderData data, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag flags, bool clipper) override; RenderData prepare(const Array& scene, RenderData data, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag flags) override; diff --git a/src/lib/sw_engine/tvgSwCommon.h b/src/lib/sw_engine/tvgSwCommon.h index 49cf9deb..04b56ffd 100644 --- a/src/lib/sw_engine/tvgSwCommon.h +++ b/src/lib/sw_engine/tvgSwCommon.h @@ -358,7 +358,8 @@ 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); +void rasterUnpremultiply(Surface* surface); +void rasterPremultiply(Surface* surface); bool rasterConvertCS(Surface* surface, ColorSpace to); #endif /* _TVG_SW_COMMON_H_ */ diff --git a/src/lib/sw_engine/tvgSwRaster.cpp b/src/lib/sw_engine/tvgSwRaster.cpp index a4bf8c4d..610d89eb 100644 --- a/src/lib/sw_engine/tvgSwRaster.cpp +++ b/src/lib/sw_engine/tvgSwRaster.cpp @@ -1481,8 +1481,10 @@ bool rasterClear(SwSurface* surface) } -void rasterUnpremultiply(SwSurface* surface) +void rasterUnpremultiply(Surface* surface) { + TVGLOG("SW_ENGINE", "Unpremultiply [Size: %d x %d]", surface->w, surface->h); + //OPTIMIZE_ME: +SIMD for (uint32_t y = 0; y < surface->h; y++) { auto buffer = surface->buffer + surface->stride * y; @@ -1503,6 +1505,25 @@ void rasterUnpremultiply(SwSurface* surface) } } } + surface->premultiplied = false; +} + + +void rasterPremultiply(Surface* surface) +{ + TVGLOG("SW_ENGINE", "Premultiply [Size: %d x %d]", surface->w, surface->h); + + //OPTIMIZE_ME: +SIMD + auto buffer = surface->buffer; + for (uint32_t y = 0; y < surface->h; ++y, buffer += surface->stride) { + auto dst = buffer; + for (uint32_t x = 0; x < surface->w; ++x, ++dst) { + auto c = *dst; + auto a = (c >> 24); + *dst = (c & 0xff000000) + ((((c >> 8) & 0xff) * a) & 0xff00) + ((((c & 0x00ff00ff) * a) >> 8) & 0x00ff00ff); + } + } + surface->premultiplied = true; } diff --git a/src/lib/sw_engine/tvgSwRasterC.h b/src/lib/sw_engine/tvgSwRasterC.h index d45d68c6..fb19b1ee 100644 --- a/src/lib/sw_engine/tvgSwRasterC.h +++ b/src/lib/sw_engine/tvgSwRasterC.h @@ -65,7 +65,7 @@ static bool inline cRasterTranslucentRect(SwSurface* surface, const SwBBox& regi static bool inline cRasterABGRtoARGB(Surface* surface) { - TVGLOG("SW_ENGINE", "Convert ColorSpace ABGR - ARGB"); + TVGLOG("SW_ENGINE", "Convert ColorSpace ABGR - ARGB [Size: %d x %d]", surface->w, surface->h); auto buffer = surface->buffer; for (uint32_t y = 0; y < surface->h; ++y, buffer += surface->stride) { diff --git a/src/lib/sw_engine/tvgSwRenderer.cpp b/src/lib/sw_engine/tvgSwRenderer.cpp index a7bb0b13..74e5965e 100644 --- a/src/lib/sw_engine/tvgSwRenderer.cpp +++ b/src/lib/sw_engine/tvgSwRenderer.cpp @@ -278,7 +278,10 @@ struct SwImageTask : SwTask auto clipRegion = bbox; //Convert colorspace if it's not aligned. - if (surface->cs != source->cs) rasterConvertCS(source, surface->cs); + if (source->owner) { + if (source->cs != surface->cs) rasterConvertCS(source, surface->cs); + if (!source->premultiplied) rasterPremultiply(source); + } image.data = source->buffer; image.w = source->w; @@ -427,6 +430,8 @@ bool SwRenderer::target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t surface->w = w; surface->h = h; surface->cs = cs; + surface->premultiplied = true; + surface->owner = true; vport.x = vport.y = 0; vport.w = surface->w; diff --git a/src/lib/tvgPictureImpl.h b/src/lib/tvgPictureImpl.h index a186633f..b4d28e70 100644 --- a/src/lib/tvgPictureImpl.h +++ b/src/lib/tvgPictureImpl.h @@ -72,7 +72,7 @@ struct Picture::Impl ~Impl() { if (paint) delete(paint); - free(surface); + delete(surface); } bool dispose(RenderMethod& renderer) @@ -275,13 +275,10 @@ struct Picture::Impl dup->loader = loader; if (surface) { - dup->surface = static_cast(malloc(sizeof(Surface))); + dup->surface = new Surface; *dup->surface = *surface; - //TODO: It needs a better design... - //Backend engines might try to align the colorspace. - //Since it shares the bitmap, duplications should not touch the data. - //Only the owner could manage it. - dup->surface->cs = ColorSpace::Unsupported; + //TODO: A dupilcation is not a proxy... it needs copy of the pixel data? + dup->surface->owner = false; } dup->w = w; dup->h = h; diff --git a/src/lib/tvgRender.h b/src/lib/tvgRender.h index a162d116..8f9e7ce0 100644 --- a/src/lib/tvgRender.h +++ b/src/lib/tvgRender.h @@ -46,11 +46,13 @@ enum ColorSpace struct Surface { - //TODO: Union for multiple types uint32_t* buffer; - uint32_t stride; - uint32_t w, h; + uint32_t stride; + uint32_t w, h; ColorSpace cs; + + bool premultiplied; //Alpha-premultiplied + bool owner; //Only owner could modify the buffer }; struct Compositor diff --git a/src/loaders/external_jpg/tvgJpgLoader.cpp b/src/loaders/external_jpg/tvgJpgLoader.cpp index f5e1073c..1c47b4f5 100644 --- a/src/loaders/external_jpg/tvgJpgLoader.cpp +++ b/src/loaders/external_jpg/tvgJpgLoader.cpp @@ -158,12 +158,15 @@ unique_ptr JpgLoader::bitmap() { if (!image) return nullptr; - auto surface = static_cast(malloc(sizeof(Surface))); + //TODO: It's better to keep this surface instance in the loader side + auto surface = new Surface; surface->buffer = (uint32_t*)(image); surface->stride = w; surface->w = w; surface->h = h; surface->cs = cs; + surface->premultiplied = true; + surface->owner = true; return unique_ptr(surface); } diff --git a/src/loaders/external_png/tvgPngLoader.cpp b/src/loaders/external_png/tvgPngLoader.cpp index dacd60d7..1dc4905e 100644 --- a/src/loaders/external_png/tvgPngLoader.cpp +++ b/src/loaders/external_png/tvgPngLoader.cpp @@ -27,24 +27,6 @@ /* Internal Class Implementation */ /************************************************************************/ -static inline uint32_t PREMULTIPLY(uint32_t c) -{ - auto a = (c >> 24); - return (c & 0xff000000) + ((((c >> 8) & 0xff) * a) & 0xff00) + ((((c & 0x00ff00ff) * a) >> 8) & 0x00ff00ff); -} - - -static void _premultiply(uint32_t* data, uint32_t w, uint32_t h) -{ - auto buffer = data; - for (uint32_t y = 0; y < h; ++y, buffer += w) { - auto src = buffer; - for (uint32_t x = 0; x < w; ++x, ++src) { - *src = PREMULTIPLY(*src); - } - } -} - /************************************************************************/ /* External Class Implementation */ @@ -109,8 +91,6 @@ bool PngLoader::read() } content = reinterpret_cast(buffer); - _premultiply(reinterpret_cast(buffer), image->width, image->height); - return true; } @@ -124,12 +104,15 @@ unique_ptr PngLoader::bitmap() { if (!content) return nullptr; - auto surface = static_cast(malloc(sizeof(Surface))); + //TODO: It's better to keep this surface instance in the loader side + auto surface = new Surface; surface->buffer = content; surface->stride = w; surface->w = w; surface->h = h; surface->cs = cs; + surface->owner = true; + surface->premultiplied = false; return unique_ptr(surface); } diff --git a/src/loaders/jpg/tvgJpgLoader.cpp b/src/loaders/jpg/tvgJpgLoader.cpp index f0bd3cb0..15f4cc9b 100644 --- a/src/loaders/jpg/tvgJpgLoader.cpp +++ b/src/loaders/jpg/tvgJpgLoader.cpp @@ -118,12 +118,15 @@ unique_ptr JpgLoader::bitmap() if (!image) return nullptr; - auto surface = static_cast(malloc(sizeof(Surface))); + //TODO: It's better to keep this surface instance in the loader side + auto surface = new Surface; surface->buffer = reinterpret_cast(image); surface->stride = static_cast(w); surface->w = static_cast(w); surface->h = static_cast(h); surface->cs = cs; + surface->premultiplied = true; + surface->owner = true; return unique_ptr(surface); } diff --git a/src/loaders/png/tvgPngLoader.cpp b/src/loaders/png/tvgPngLoader.cpp index 40fb28da..b4364ec8 100644 --- a/src/loaders/png/tvgPngLoader.cpp +++ b/src/loaders/png/tvgPngLoader.cpp @@ -29,26 +29,6 @@ /* Internal Class Implementation */ /************************************************************************/ - -static inline uint32_t PREMULTIPLY(uint32_t c) -{ - auto a = (c >> 24); - return (c & 0xff000000) + ((((c >> 8) & 0xff) * a) & 0xff00) + ((((c & 0x00ff00ff) * a) >> 8) & 0x00ff00ff); -} - - -static void _premultiply(uint32_t* data, uint32_t w, uint32_t h) -{ - auto buffer = data; - for (uint32_t y = 0; y < h; ++y, buffer += w) { - auto src = buffer; - for (uint32_t x = 0; x < w; ++x, ++src) { - *src = PREMULTIPLY(*src); - } - } -} - - void PngLoader::clear() { lodepng_state_cleanup(&state); @@ -174,12 +154,15 @@ unique_ptr PngLoader::bitmap() { this->done(); - auto surface = static_cast(malloc(sizeof(Surface))); + //TODO: It's better to keep this surface instance in the loader side + auto surface = new Surface; surface->buffer = reinterpret_cast(image); surface->stride = static_cast(w); surface->w = static_cast(w); surface->h = static_cast(h); surface->cs = cs; + surface->premultiplied = false; + surface->owner = true; return unique_ptr(surface); } @@ -195,6 +178,4 @@ void PngLoader::run(unsigned tid) auto height = static_cast(h); lodepng_decode(&image, &width, &height, &state, data, size); - - _premultiply((uint32_t*)(image), width, height); } diff --git a/src/loaders/raw/tvgRawLoader.cpp b/src/loaders/raw/tvgRawLoader.cpp index 1130639c..d81de06c 100644 --- a/src/loaders/raw/tvgRawLoader.cpp +++ b/src/loaders/raw/tvgRawLoader.cpp @@ -80,12 +80,15 @@ unique_ptr RawLoader::bitmap() { if (!content) return nullptr; - auto surface = static_cast(malloc(sizeof(Surface))); + //TODO: It's better to keep this surface instance in the loader side + auto surface = new Surface; surface->buffer = content; surface->stride = static_cast(w); surface->w = static_cast(w); surface->h = static_cast(h); surface->cs = cs; + surface->premultiplied = true; + surface->owner = true; return unique_ptr(surface); }