sw_engine image: support non-premultiplied alpha images.

Previously, translucent png images are not displayed properly
due to alpha channels premultiplication.

This patch implements that missing part to support it properly
by introducing the Surface data between canvas engine & rasterizer

@Issue: https://github.com/Samsung/thorvg/issues/655
This commit is contained in:
Hermet Park 2021-12-01 14:16:01 +09:00 committed by Hermet Park
parent e7b7705875
commit 4cdf648e14
23 changed files with 151 additions and 54 deletions

View file

@ -1163,6 +1163,8 @@ public:
/**
* @brief Gets the pixels information of the picture.
*
* @note The data must be pre-multiplied by the alpha channels.
*
* @warning Please do not use it, this API is not official one. It could be modified in the next version.
*
* @BETA_API

Binary file not shown.

Before

Width:  |  Height:  |  Size: 451 KiB

After

Width:  |  Height:  |  Size: 450 KiB

View file

@ -184,7 +184,7 @@ bool GlRenderer::dispose(RenderData data)
}
RenderData GlRenderer::prepare(TVG_UNUSED const Picture& picture, TVG_UNUSED RenderData data, TVG_UNUSED const RenderTransform* transform, TVG_UNUSED uint32_t opacity, TVG_UNUSED Array<RenderData>& clips, TVG_UNUSED RenderUpdateFlag flags)
RenderData GlRenderer::prepare(TVG_UNUSED Surface* image, TVG_UNUSED RenderData data, TVG_UNUSED const RenderTransform* transform, TVG_UNUSED uint32_t opacity, TVG_UNUSED Array<RenderData>& clips, TVG_UNUSED RenderUpdateFlag flags)
{
//TODO:
return nullptr;

View file

@ -31,7 +31,7 @@ public:
Surface surface = {nullptr, 0, 0, 0};
RenderData prepare(const Shape& shape, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags) override;
RenderData prepare(const Picture& picture, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags) override;
RenderData prepare(Surface* image, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags) override;
bool preRender() override;
bool renderShape(RenderData data) override;
bool renderImage(RenderData data) override;

View file

@ -321,7 +321,7 @@ SwOutline* strokeExportOutline(SwStroke* stroke, SwMpool* mpool, unsigned tid);
void strokeFree(SwStroke* stroke);
bool imagePrepare(SwImage* image, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid);
bool imageGenRle(SwImage* image, TVG_UNUSED const Picture* pdata, const SwBBox& renderRegion, bool antiAlias);
bool imageGenRle(SwImage* image, const SwBBox& renderRegion, bool antiAlias);
void imageDelOutline(SwImage* image, SwMpool* mpool, uint32_t tid);
void imageReset(SwImage* image);
void imageFree(SwImage* image);

View file

@ -101,7 +101,7 @@ bool imagePrepare(SwImage* image, const Matrix* transform, const SwBBox& clipReg
}
bool imageGenRle(SwImage* image, TVG_UNUSED const Picture* pdata, const SwBBox& renderRegion, bool antiAlias)
bool imageGenRle(SwImage* image, const SwBBox& renderRegion, bool antiAlias)
{
if ((image->rle = rleRender(image->rle, image->outline, renderRegion, antiAlias))) return true;

View file

@ -1817,7 +1817,7 @@ bool rasterClear(SwSurface* surface)
void rasterUnpremultiply(SwSurface* surface)
{
//TODO: Create simd avx and neon version
//OPTIMIZE_ME: +SIMD
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) {
@ -1904,6 +1904,5 @@ bool rasterImage(SwSurface* surface, SwImage* image, const Matrix* transform, co
//TODO: case: _rasterRGBImage()
//TODO: case: _rasterGrayscaleImage()
//TODO: case: _rasterAlphaImage()
return _rasterRGBAImage(surface, image, transform, bbox, opacity);
}

View file

@ -180,7 +180,6 @@ struct SwShapeTask : SwTask
struct SwImageTask : SwTask
{
SwImage image;
const Picture* pdata = nullptr;
void run(unsigned tid) override
{
@ -189,15 +188,12 @@ struct SwImageTask : SwTask
//Invisible shape turned to visible by alpha.
if ((flags & (RenderUpdateFlag::Image | RenderUpdateFlag::Transform | RenderUpdateFlag::Color)) && (opacity > 0)) {
imageReset(&image);
image.data = const_cast<uint32_t*>(pdata->data(&image.w, &image.h));
if (!image.data || image.w == 0 || image.h == 0) goto end;
image.stride = image.w; //same, pixel buffer size.
if (!imagePrepare(&image, transform, clipRegion, bbox, mpool, tid)) goto end;
if (clips.count > 0) {
if (!imageGenRle(&image, pdata, bbox, false)) goto end;
if (!imageGenRle(&image, bbox, false)) goto end;
if (image.rle) {
for (auto clip = clips.data; clip < (clips.data + clips.count); ++clip) {
auto clipper = &static_cast<SwShapeTask*>(*clip)->shape;
@ -613,14 +609,18 @@ void* SwRenderer::prepareCommon(SwTask* task, const RenderTransform* transform,
}
RenderData SwRenderer::prepare(const Picture& pdata, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags)
RenderData SwRenderer::prepare(Surface* image, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags)
{
//prepare task
auto task = static_cast<SwImageTask*>(data);
if (!task) {
task = new SwImageTask;
if (!task) return nullptr;
task->pdata = &pdata;
if (flags & RenderUpdateFlag::Image) {
task->image.data = image->buffer;
task->image.w = image->w;
task->image.h = image->h;
task->image.stride = image->stride;
}
}
return prepareCommon(task, transform, opacity, clips, flags);
}
@ -632,7 +632,6 @@ RenderData SwRenderer::prepare(const Shape& sdata, RenderData data, const Render
auto task = static_cast<SwShapeTask*>(data);
if (!task) {
task = new SwShapeTask;
if (!task) return nullptr;
task->sdata = &sdata;
}
return prepareCommon(task, transform, opacity, clips, flags);

View file

@ -36,7 +36,7 @@ class SwRenderer : public RenderMethod
{
public:
RenderData prepare(const Shape& shape, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags) override;
RenderData prepare(const Picture& picture, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags) override;
RenderData prepare(Surface* image, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags) override;
bool preRender() override;
bool renderShape(RenderData data) override;
bool renderImage(RenderData data) override;

View file

@ -22,7 +22,7 @@
#ifndef _TVG_LOAD_MODULE_H_
#define _TVG_LOAD_MODULE_H_
#include "tvgCommon.h"
#include "tvgRender.h"
namespace tvg
{
@ -40,17 +40,17 @@ public:
virtual ~LoadModule() {}
virtual bool open(const string& path) { return false; };
virtual bool open(const char* data, uint32_t size, bool copy) { return false; };
virtual bool open(const uint32_t* data, uint32_t w, uint32_t h, bool copy) { return false; };
virtual bool open(const string& path) { return false; }
virtual bool open(const char* data, uint32_t size, bool copy) { return false; }
virtual bool open(const uint32_t* data, uint32_t w, uint32_t h, bool copy) { return false; }
//Override this if the vector-format has own resizing policy.
virtual bool resize(Paint* paint, float w, float h) { return false; };
virtual bool resize(Paint* paint, float w, float h) { return false; }
virtual bool read() = 0;
virtual bool close() = 0;
virtual const uint32_t* pixels() { return nullptr; };
virtual unique_ptr<Paint> paint() { return nullptr; };
virtual unique_ptr<Surface> bitmap() { return nullptr; }
virtual unique_ptr<Paint> paint() { return nullptr; }
};
}

View file

@ -116,5 +116,6 @@ const uint32_t* Picture::data(uint32_t* w, uint32_t* h) const noexcept
if (w) *w = 0;
if (h) *h = 0;
}
return pImpl->pixels;
if (pImpl->surface) return pImpl->surface->buffer;
else return nullptr;
}

View file

@ -60,8 +60,10 @@ struct PictureIterator : Iterator
struct Picture::Impl
{
shared_ptr<LoadModule> loader = nullptr;
Paint* paint = nullptr;
uint32_t* pixels = nullptr;
Paint* paint = nullptr; //vector picture uses
Surface* surface = nullptr; //bitmap picture uses
Picture* picture = nullptr;
void* rdata = nullptr; //engine data
float w = 0, h = 0;
@ -74,6 +76,7 @@ struct Picture::Impl
~Impl()
{
if (paint) delete(paint);
free(surface);
}
bool dispose(RenderMethod& renderer)
@ -81,7 +84,7 @@ struct Picture::Impl
bool ret = true;
if (paint) {
ret = paint->pImpl->dispose(renderer);
} else if (pixels) {
} else if (surface) {
ret = renderer.dispose(rdata);
rdata = nullptr;
}
@ -102,10 +105,10 @@ struct Picture::Impl
if (paint) return RenderUpdateFlag::None;
}
}
if (!pixels) {
pixels = const_cast<uint32_t*>(loader->pixels());
free(surface);
if ((surface = loader->bitmap().release())) {
loader->close();
if (pixels) return RenderUpdateFlag::Image;
return RenderUpdateFlag::Image;
}
}
return RenderUpdateFlag::None;
@ -129,9 +132,9 @@ struct Picture::Impl
{
auto flag = reload();
if (pixels) {
if (surface) {
auto transform = resizeTransform(pTransform);
rdata = renderer.prepare(*picture, rdata, &transform, opacity, clips, static_cast<RenderUpdateFlag>(pFlag | flag));
rdata = renderer.prepare(surface, rdata, &transform, opacity, clips, static_cast<RenderUpdateFlag>(pFlag | flag));
} else if (paint) {
if (resizing) {
loader->resize(paint, w, h);
@ -144,7 +147,7 @@ struct Picture::Impl
bool render(RenderMethod &renderer)
{
if (pixels) return renderer.renderImage(rdata);
if (surface) return renderer.renderImage(rdata);
else if (paint) return paint->pImpl->render(renderer);
return false;
}
@ -186,7 +189,7 @@ struct Picture::Impl
Result load(const string& path)
{
if (paint || pixels) return Result::InsufficientCondition;
if (paint || surface) return Result::InsufficientCondition;
if (loader) loader->close();
bool invalid; //Invalid Path
loader = LoaderMgr::loader(path, &invalid);
@ -202,7 +205,7 @@ struct Picture::Impl
Result load(const char* data, uint32_t size, const string& mimeType, bool copy)
{
if (paint || pixels) return Result::InsufficientCondition;
if (paint || surface) return Result::InsufficientCondition;
if (loader) loader->close();
loader = LoaderMgr::loader(data, size, mimeType, copy);
if (!loader) return Result::NonSupport;
@ -214,7 +217,7 @@ struct Picture::Impl
Result load(uint32_t* data, uint32_t w, uint32_t h, bool copy)
{
if (paint || pixels) return Result::InsufficientCondition;
if (paint || surface) return Result::InsufficientCondition;
if (loader) loader->close();
loader = LoaderMgr::loader(data, w, h, copy);
if (!loader) return Result::NonSupport;
@ -234,7 +237,10 @@ struct Picture::Impl
if (paint) dup->paint = paint->duplicate();
dup->loader = loader;
dup->pixels = pixels;
if (surface) {
dup->surface = static_cast<Surface*>(malloc(sizeof(Surface)));
*dup->surface = *surface;
}
dup->w = w;
dup->h = h;
dup->resizing = resizing;

View file

@ -84,12 +84,13 @@ struct RenderTransform
RenderTransform(const RenderTransform* lhs, const RenderTransform* rhs);
};
class RenderMethod
{
public:
virtual ~RenderMethod() {}
virtual RenderData prepare(const Shape& shape, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags) = 0;
virtual RenderData prepare(const Picture& picture, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags) = 0;
virtual RenderData prepare(Surface* image, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags) = 0;
virtual bool preRender() = 0;
virtual bool renderShape(RenderData data) = 0;
virtual bool renderImage(RenderData data) = 0;

View file

@ -149,7 +149,16 @@ bool JpgLoader::close()
}
const uint32_t* JpgLoader::pixels()
unique_ptr<Surface> JpgLoader::bitmap()
{
return (const uint32_t*) image;
if (!image) return nullptr;
auto surface = static_cast<Surface*>(malloc(sizeof(Surface)));
surface->buffer = (uint32_t*)(image);
surface->stride = w;
surface->w = w;
surface->h = h;
surface->cs = SwCanvas::ARGB8888;
return unique_ptr<Surface>(surface);
}

View file

@ -37,7 +37,7 @@ public:
bool read() override;
bool close() override;
const uint32_t* pixels() override;
unique_ptr<Surface> bitmap() override;
private:
void clear();

View file

@ -23,6 +23,25 @@
#include "tvgLoader.h"
#include "tvgPngLoader.h"
static inline uint32_t ALPHA_BLEND(uint32_t c, uint32_t a)
{
return (((((c >> 8) & 0x00ff00ff) * a + 0x00ff00ff) & 0xff00ff00) +
((((c & 0x00ff00ff) * a + 0x00ff00ff) >> 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 = ALPHA_BLEND(*src, (*src >> 24));
}
}
}
PngLoader::PngLoader()
{
image = static_cast<png_imagep>(calloc(1, sizeof(png_image)));
@ -63,6 +82,7 @@ bool PngLoader::open(const char* data, uint32_t size, bool copy)
return true;
}
bool PngLoader::read()
{
png_bytep buffer;
@ -76,6 +96,8 @@ bool PngLoader::read()
if (!png_image_finish_read(image, NULL, buffer, 0, NULL)) return false;
content = reinterpret_cast<uint32_t*>(buffer);
_premultiply(reinterpret_cast<uint32_t*>(buffer), image->width, image->height);
return true;
}
@ -85,7 +107,16 @@ bool PngLoader::close()
return true;
}
const uint32_t* PngLoader::pixels()
unique_ptr<Surface> PngLoader::bitmap()
{
return this->content;
if (!content) return nullptr;
auto surface = static_cast<Surface*>(malloc(sizeof(Surface)));
surface->buffer = (uint32_t*)(content);
surface->stride = w;
surface->w = w;
surface->h = h;
surface->cs = SwCanvas::ARGB8888;
return unique_ptr<Surface>(surface);
}

View file

@ -36,7 +36,7 @@ public:
bool read() override;
bool close() override;
const uint32_t* pixels() override;
unique_ptr<Surface> bitmap() override;
private:
png_imagep image = nullptr;

View file

@ -109,11 +109,20 @@ bool JpgLoader::close()
}
const uint32_t* JpgLoader::pixels()
unique_ptr<Surface> JpgLoader::bitmap()
{
this->done();
return (const uint32_t*)image;
if (!image) return nullptr;
auto surface = static_cast<Surface*>(malloc(sizeof(Surface)));
surface->buffer = (uint32_t*)(image);
surface->stride = w;
surface->w = w;
surface->h = h;
surface->cs = SwCanvas::ARGB8888;
return unique_ptr<Surface>(surface);
}

View file

@ -44,7 +44,7 @@ public:
bool read() override;
bool close() override;
const uint32_t* pixels() override;
unique_ptr<Surface> bitmap() override;
void run(unsigned tid) override;
};

View file

@ -28,6 +28,26 @@
/* Internal Class Implementation */
/************************************************************************/
static inline uint32_t ALPHA_BLEND(uint32_t c, uint32_t a)
{
return (((((c >> 8) & 0x00ff00ff) * a + 0x00ff00ff) & 0xff00ff00) +
((((c & 0x00ff00ff) * a + 0x00ff00ff) >> 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 = ALPHA_BLEND(*src, (*src >> 24));
}
}
}
void PngLoader::clear()
{
lodepng_state_cleanup(&state);
@ -141,11 +161,20 @@ bool PngLoader::close()
}
const uint32_t* PngLoader::pixels()
unique_ptr<Surface> PngLoader::bitmap()
{
this->done();
return (const uint32_t*) image;
if (!image) return nullptr;
auto surface = static_cast<Surface*>(malloc(sizeof(Surface)));
surface->buffer = (uint32_t*)(image);
surface->stride = w;
surface->w = w;
surface->h = h;
surface->cs = SwCanvas::ARGB8888;
return unique_ptr<Surface>(surface);
}
@ -155,4 +184,6 @@ 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

@ -47,7 +47,7 @@ public:
bool read() override;
bool close() override;
const uint32_t* pixels() override;
unique_ptr<Surface> bitmap() override;
void run(unsigned tid) override;
};

View file

@ -72,7 +72,16 @@ bool RawLoader::close()
}
const uint32_t* RawLoader::pixels()
unique_ptr<Surface> RawLoader::bitmap()
{
return this->content;
if (!content) return nullptr;
auto surface = static_cast<Surface*>(malloc(sizeof(Surface)));
surface->buffer = (uint32_t*)(content);
surface->stride = w;
surface->w = w;
surface->h = h;
surface->cs = SwCanvas::ARGB8888;
return unique_ptr<Surface>(surface);
}

View file

@ -35,7 +35,7 @@ public:
bool read() override;
bool close() override;
const uint32_t* pixels() override;
unique_ptr<Surface> bitmap() override;
};