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.
This commit is contained in:
Hermet Park 2023-04-28 16:43:18 +09:00
parent 9fb66973f0
commit e9b12aa9f7
12 changed files with 61 additions and 62 deletions

View file

@ -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<RenderData>& clips, RenderUpdateFlag flags, bool clipper) override;
RenderData prepare(const Array<RenderData>& scene, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags) override;

View file

@ -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_ */

View file

@ -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;
}

View file

@ -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) {

View file

@ -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;

View file

@ -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<Surface*>(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;

View file

@ -46,11 +46,13 @@ enum ColorSpace
struct Surface
{
//TODO: Union for multiple types
uint32_t* buffer;
uint32_t stride;
uint32_t w, h;
ColorSpace cs;
bool premultiplied; //Alpha-premultiplied
bool owner; //Only owner could modify the buffer
};
struct Compositor

View file

@ -158,12 +158,15 @@ unique_ptr<Surface> JpgLoader::bitmap()
{
if (!image) return nullptr;
auto surface = static_cast<Surface*>(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>(surface);
}

View file

@ -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<uint32_t*>(buffer);
_premultiply(reinterpret_cast<uint32_t*>(buffer), image->width, image->height);
return true;
}
@ -124,12 +104,15 @@ unique_ptr<Surface> PngLoader::bitmap()
{
if (!content) return nullptr;
auto surface = static_cast<Surface*>(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>(surface);
}

View file

@ -118,12 +118,15 @@ unique_ptr<Surface> JpgLoader::bitmap()
if (!image) return nullptr;
auto surface = static_cast<Surface*>(malloc(sizeof(Surface)));
//TODO: It's better to keep this surface instance in the loader side
auto surface = new Surface;
surface->buffer = reinterpret_cast<uint32_t*>(image);
surface->stride = static_cast<uint32_t>(w);
surface->w = static_cast<uint32_t>(w);
surface->h = static_cast<uint32_t>(h);
surface->cs = cs;
surface->premultiplied = true;
surface->owner = true;
return unique_ptr<Surface>(surface);
}

View file

@ -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<Surface> PngLoader::bitmap()
{
this->done();
auto surface = static_cast<Surface*>(malloc(sizeof(Surface)));
//TODO: It's better to keep this surface instance in the loader side
auto surface = new Surface;
surface->buffer = reinterpret_cast<uint32_t*>(image);
surface->stride = static_cast<uint32_t>(w);
surface->w = static_cast<uint32_t>(w);
surface->h = static_cast<uint32_t>(h);
surface->cs = cs;
surface->premultiplied = false;
surface->owner = true;
return unique_ptr<Surface>(surface);
}
@ -195,6 +178,4 @@ void PngLoader::run(unsigned tid)
auto height = static_cast<unsigned>(h);
lodepng_decode(&image, &width, &height, &state, data, size);
_premultiply((uint32_t*)(image), width, height);
}

View file

@ -80,12 +80,15 @@ unique_ptr<Surface> RawLoader::bitmap()
{
if (!content) return nullptr;
auto surface = static_cast<Surface*>(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<uint32_t>(w);
surface->w = static_cast<uint32_t>(w);
surface->h = static_cast<uint32_t>(h);
surface->cs = cs;
surface->premultiplied = true;
surface->owner = true;
return unique_ptr<Surface>(surface);
}