diff --git a/inc/thorvg.h b/inc/thorvg.h index 69f02862..80d19e40 100644 --- a/inc/thorvg.h +++ b/inc/thorvg.h @@ -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 diff --git a/src/examples/images/test.png b/src/examples/images/test.png index 2b793248..cc16c6c9 100644 Binary files a/src/examples/images/test.png and b/src/examples/images/test.png differ diff --git a/src/lib/gl_engine/tvgGlRenderer.cpp b/src/lib/gl_engine/tvgGlRenderer.cpp index 8ea9539f..c12ede58 100644 --- a/src/lib/gl_engine/tvgGlRenderer.cpp +++ b/src/lib/gl_engine/tvgGlRenderer.cpp @@ -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& 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& clips, TVG_UNUSED RenderUpdateFlag flags) { //TODO: return nullptr; diff --git a/src/lib/gl_engine/tvgGlRenderer.h b/src/lib/gl_engine/tvgGlRenderer.h index 0935284e..81e18c66 100644 --- a/src/lib/gl_engine/tvgGlRenderer.h +++ b/src/lib/gl_engine/tvgGlRenderer.h @@ -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& clips, RenderUpdateFlag flags) override; - RenderData prepare(const Picture& picture, RenderData data, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag flags) override; + RenderData prepare(Surface* image, RenderData data, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag flags) override; bool preRender() override; bool renderShape(RenderData data) override; bool renderImage(RenderData data) override; diff --git a/src/lib/sw_engine/tvgSwCommon.h b/src/lib/sw_engine/tvgSwCommon.h index afacc8bc..e0ffc1fb 100644 --- a/src/lib/sw_engine/tvgSwCommon.h +++ b/src/lib/sw_engine/tvgSwCommon.h @@ -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); diff --git a/src/lib/sw_engine/tvgSwImage.cpp b/src/lib/sw_engine/tvgSwImage.cpp index 8176d8c7..fe22fce0 100644 --- a/src/lib/sw_engine/tvgSwImage.cpp +++ b/src/lib/sw_engine/tvgSwImage.cpp @@ -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; diff --git a/src/lib/sw_engine/tvgSwRaster.cpp b/src/lib/sw_engine/tvgSwRaster.cpp index 4e103991..28b1e593 100644 --- a/src/lib/sw_engine/tvgSwRaster.cpp +++ b/src/lib/sw_engine/tvgSwRaster.cpp @@ -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); -} +} \ No newline at end of file diff --git a/src/lib/sw_engine/tvgSwRenderer.cpp b/src/lib/sw_engine/tvgSwRenderer.cpp index 70c7768c..1c0c3c2c 100644 --- a/src/lib/sw_engine/tvgSwRenderer.cpp +++ b/src/lib/sw_engine/tvgSwRenderer.cpp @@ -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(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(*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& clips, RenderUpdateFlag flags) +RenderData SwRenderer::prepare(Surface* image, RenderData data, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag flags) { //prepare task auto task = static_cast(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(data); if (!task) { task = new SwShapeTask; - if (!task) return nullptr; task->sdata = &sdata; } return prepareCommon(task, transform, opacity, clips, flags); diff --git a/src/lib/sw_engine/tvgSwRenderer.h b/src/lib/sw_engine/tvgSwRenderer.h index 51eed5db..3f883ac4 100644 --- a/src/lib/sw_engine/tvgSwRenderer.h +++ b/src/lib/sw_engine/tvgSwRenderer.h @@ -36,7 +36,7 @@ class SwRenderer : public RenderMethod { public: RenderData prepare(const Shape& shape, RenderData data, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag flags) override; - RenderData prepare(const Picture& picture, RenderData data, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag flags) override; + RenderData prepare(Surface* image, RenderData data, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag flags) override; bool preRender() override; bool renderShape(RenderData data) override; bool renderImage(RenderData data) override; diff --git a/src/lib/tvgLoadModule.h b/src/lib/tvgLoadModule.h index 70b95b76..0c34ecbf 100644 --- a/src/lib/tvgLoadModule.h +++ b/src/lib/tvgLoadModule.h @@ -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() { return nullptr; }; + virtual unique_ptr bitmap() { return nullptr; } + virtual unique_ptr paint() { return nullptr; } }; } diff --git a/src/lib/tvgPicture.cpp b/src/lib/tvgPicture.cpp index 9aecbb14..580cb5b5 100644 --- a/src/lib/tvgPicture.cpp +++ b/src/lib/tvgPicture.cpp @@ -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; } \ No newline at end of file diff --git a/src/lib/tvgPictureImpl.h b/src/lib/tvgPictureImpl.h index 8a56f106..794363fe 100644 --- a/src/lib/tvgPictureImpl.h +++ b/src/lib/tvgPictureImpl.h @@ -60,8 +60,10 @@ struct PictureIterator : Iterator struct Picture::Impl { shared_ptr 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(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(pFlag | flag)); + rdata = renderer.prepare(surface, rdata, &transform, opacity, clips, static_cast(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(malloc(sizeof(Surface))); + *dup->surface = *surface; + } dup->w = w; dup->h = h; dup->resizing = resizing; diff --git a/src/lib/tvgRender.h b/src/lib/tvgRender.h index 671370e5..955816e8 100644 --- a/src/lib/tvgRender.h +++ b/src/lib/tvgRender.h @@ -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& clips, RenderUpdateFlag flags) = 0; - virtual RenderData prepare(const Picture& picture, RenderData data, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag flags) = 0; + virtual RenderData prepare(Surface* image, RenderData data, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag flags) = 0; virtual bool preRender() = 0; virtual bool renderShape(RenderData data) = 0; virtual bool renderImage(RenderData data) = 0; diff --git a/src/loaders/external_jpg/tvgJpgLoader.cpp b/src/loaders/external_jpg/tvgJpgLoader.cpp index 76ccdde3..6f9416b6 100644 --- a/src/loaders/external_jpg/tvgJpgLoader.cpp +++ b/src/loaders/external_jpg/tvgJpgLoader.cpp @@ -149,7 +149,16 @@ bool JpgLoader::close() } -const uint32_t* JpgLoader::pixels() +unique_ptr JpgLoader::bitmap() { - return (const uint32_t*) image; + if (!image) return nullptr; + + auto surface = static_cast(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); } diff --git a/src/loaders/external_jpg/tvgJpgLoader.h b/src/loaders/external_jpg/tvgJpgLoader.h index 1aeaa04c..7d35e57d 100644 --- a/src/loaders/external_jpg/tvgJpgLoader.h +++ b/src/loaders/external_jpg/tvgJpgLoader.h @@ -37,7 +37,7 @@ public: bool read() override; bool close() override; - const uint32_t* pixels() override; + unique_ptr bitmap() override; private: void clear(); diff --git a/src/loaders/external_png/tvgPngLoader.cpp b/src/loaders/external_png/tvgPngLoader.cpp index faafc62a..316f51dd 100644 --- a/src/loaders/external_png/tvgPngLoader.cpp +++ b/src/loaders/external_png/tvgPngLoader.cpp @@ -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(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(buffer); + _premultiply(reinterpret_cast(buffer), image->width, image->height); + return true; } @@ -85,7 +107,16 @@ bool PngLoader::close() return true; } -const uint32_t* PngLoader::pixels() +unique_ptr PngLoader::bitmap() { - return this->content; + if (!content) return nullptr; + + auto surface = static_cast(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); } diff --git a/src/loaders/external_png/tvgPngLoader.h b/src/loaders/external_png/tvgPngLoader.h index ab2c8169..b42537c7 100644 --- a/src/loaders/external_png/tvgPngLoader.h +++ b/src/loaders/external_png/tvgPngLoader.h @@ -36,7 +36,7 @@ public: bool read() override; bool close() override; - const uint32_t* pixels() override; + unique_ptr bitmap() override; private: png_imagep image = nullptr; diff --git a/src/loaders/jpg/tvgJpgLoader.cpp b/src/loaders/jpg/tvgJpgLoader.cpp index 599d2fea..8846613c 100644 --- a/src/loaders/jpg/tvgJpgLoader.cpp +++ b/src/loaders/jpg/tvgJpgLoader.cpp @@ -109,11 +109,20 @@ bool JpgLoader::close() } -const uint32_t* JpgLoader::pixels() +unique_ptr JpgLoader::bitmap() { this->done(); - return (const uint32_t*)image; + if (!image) return nullptr; + + auto surface = static_cast(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); } diff --git a/src/loaders/jpg/tvgJpgLoader.h b/src/loaders/jpg/tvgJpgLoader.h index e7ec3ab4..39732dbc 100644 --- a/src/loaders/jpg/tvgJpgLoader.h +++ b/src/loaders/jpg/tvgJpgLoader.h @@ -44,7 +44,7 @@ public: bool read() override; bool close() override; - const uint32_t* pixels() override; + unique_ptr bitmap() override; void run(unsigned tid) override; }; diff --git a/src/loaders/png/tvgPngLoader.cpp b/src/loaders/png/tvgPngLoader.cpp index e50f34e0..b9388933 100644 --- a/src/loaders/png/tvgPngLoader.cpp +++ b/src/loaders/png/tvgPngLoader.cpp @@ -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 PngLoader::bitmap() { this->done(); - return (const uint32_t*) image; + if (!image) return nullptr; + + auto surface = static_cast(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); } @@ -155,4 +184,6 @@ void PngLoader::run(unsigned tid) auto height = static_cast(h); lodepng_decode(&image, &width, &height, &state, data, size); + + _premultiply((uint32_t*)(image), width, height); } \ No newline at end of file diff --git a/src/loaders/png/tvgPngLoader.h b/src/loaders/png/tvgPngLoader.h index fa1860c4..34dbeed0 100644 --- a/src/loaders/png/tvgPngLoader.h +++ b/src/loaders/png/tvgPngLoader.h @@ -47,7 +47,7 @@ public: bool read() override; bool close() override; - const uint32_t* pixels() override; + unique_ptr bitmap() override; void run(unsigned tid) override; }; diff --git a/src/loaders/raw/tvgRawLoader.cpp b/src/loaders/raw/tvgRawLoader.cpp index a38dc5ef..d7d425b1 100644 --- a/src/loaders/raw/tvgRawLoader.cpp +++ b/src/loaders/raw/tvgRawLoader.cpp @@ -72,7 +72,16 @@ bool RawLoader::close() } -const uint32_t* RawLoader::pixels() +unique_ptr RawLoader::bitmap() { - return this->content; + if (!content) return nullptr; + + auto surface = static_cast(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); } diff --git a/src/loaders/raw/tvgRawLoader.h b/src/loaders/raw/tvgRawLoader.h index f6eed20e..20fa3329 100644 --- a/src/loaders/raw/tvgRawLoader.h +++ b/src/loaders/raw/tvgRawLoader.h @@ -35,7 +35,7 @@ public: bool read() override; bool close() override; - const uint32_t* pixels() override; + unique_ptr bitmap() override; };