mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-08 05:33:36 +00:00
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:
parent
e7b7705875
commit
4cdf648e14
23 changed files with 151 additions and 54 deletions
|
@ -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 |
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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; }
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ public:
|
|||
bool read() override;
|
||||
bool close() override;
|
||||
|
||||
const uint32_t* pixels() override;
|
||||
unique_ptr<Surface> bitmap() override;
|
||||
|
||||
private:
|
||||
void clear();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ public:
|
|||
bool read() override;
|
||||
bool close() override;
|
||||
|
||||
const uint32_t* pixels() override;
|
||||
unique_ptr<Surface> bitmap() override;
|
||||
};
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue