renderer: revise the pImpl design with a better efficiency

The following is a redesign that extends the class internally.

The main goal is to preserve the characteristics of the pImpl idiom
for data encapsulation, while simultaneously reducing the memory
allocation overhead typically associated with pImpl.

The stragegy is here:
Rather than alloc the impl memory inside of the thorvg engine,
impl extends the API classes in order to consolidate the memory.

size has been decreased by -4kb with optimization=s

issue: https://github.com/thorvg/thorvg/issues/3214
This commit is contained in:
Hermet Park 2025-04-18 13:40:37 +09:00 committed by Hermet Park
parent 7feb43201b
commit 1a332acd37
18 changed files with 192 additions and 189 deletions

View file

@ -801,8 +801,10 @@ public:
* *
* Besides the APIs inherited from the Fill class, it enables setting and getting the linear gradient bounds. * Besides the APIs inherited from the Fill class, it enables setting and getting the linear gradient bounds.
* The behavior outside the gradient bounds depends on the value specified in the spread API. * The behavior outside the gradient bounds depends on the value specified in the spread API.
*
* @warning This class is not designed for inheritance.
*/ */
class TVG_API LinearGradient final : public Fill class TVG_API LinearGradient : public Fill
{ {
public: public:
/** /**
@ -863,8 +865,9 @@ public:
* *
* @brief A class representing the radial gradient fill of the Shape object. * @brief A class representing the radial gradient fill of the Shape object.
* *
* @warning This class is not designed for inheritance.
*/ */
class TVG_API RadialGradient final : public Fill class TVG_API RadialGradient : public Fill
{ {
public: public:
/** /**
@ -939,8 +942,10 @@ public:
* *
* The stroke of Shape is an optional property in case the Shape needs to be represented with/without the outline borders. * The stroke of Shape is an optional property in case the Shape needs to be represented with/without the outline borders.
* It's efficient since the shape path and the stroking path can be shared with each other. It's also convenient when controlling both in one context. * It's efficient since the shape path and the stroking path can be shared with each other. It's also convenient when controlling both in one context.
*
* @warning This class is not designed for inheritance.
*/ */
class TVG_API Shape final : public Paint class TVG_API Shape : public Paint
{ {
public: public:
/** /**
@ -1319,8 +1324,10 @@ public:
* *
* @note Supported formats are depended on the available TVG loaders. * @note Supported formats are depended on the available TVG loaders.
* @note See Animation class if the picture data is animatable. * @note See Animation class if the picture data is animatable.
*
* @warning This class is not designed for inheritance.
*/ */
class TVG_API Picture final : public Paint class TVG_API Picture : public Paint
{ {
public: public:
/** /**
@ -1450,8 +1457,10 @@ public:
* *
* As a group, the scene can be transformed, made translucent and composited with other target paints, * As a group, the scene can be transformed, made translucent and composited with other target paints,
* its children will be affected by the scene world. * its children will be affected by the scene world.
*
* @warning This class is not designed for inheritance.
*/ */
class TVG_API Scene final : public Paint class TVG_API Scene : public Paint
{ {
public: public:
/** /**
@ -1546,9 +1555,11 @@ public:
* *
* @brief A class to represent text objects in a graphical context, allowing for rendering and manipulation of unicode text. * @brief A class to represent text objects in a graphical context, allowing for rendering and manipulation of unicode text.
* *
* @warning This class is not designed for inheritance.
*
* @since 0.15 * @since 0.15
*/ */
class TVG_API Text final : public Paint class TVG_API Text : public Paint
{ {
public: public:
/** /**

View file

@ -481,7 +481,7 @@ void LottieBuilder::updatePath(LottieGroup* parent, LottieObject** child, float
if (ctx->repeaters.empty()) { if (ctx->repeaters.empty()) {
_draw(parent, path, ctx); _draw(parent, path, ctx);
if (path->pathset(frameNo, SHAPE(ctx->merging)->rs.path, ctx->transform, tween, exps, ctx->modifier)) { if (path->pathset(frameNo, SHAPE(ctx->merging)->rs.path, ctx->transform, tween, exps, ctx->modifier)) {
PAINT(ctx->merging)->update(RenderUpdateFlag::Path); PAINT(ctx->merging)->mark(RenderUpdateFlag::Path);
} }
} else { } else {
auto shape = path->pooling(); auto shape = path->pooling();
@ -695,7 +695,7 @@ void LottieBuilder::updatePolystar(LottieGroup* parent, LottieObject** child, fl
_draw(parent, star, ctx); _draw(parent, star, ctx);
if (star->type == LottiePolyStar::Star) updateStar(star, frameNo, (identity ? nullptr : &matrix), ctx->merging, ctx, tween, exps); if (star->type == LottiePolyStar::Star) updateStar(star, frameNo, (identity ? nullptr : &matrix), ctx->merging, ctx, tween, exps);
else updatePolygon(parent, star, frameNo, (identity ? nullptr : &matrix), ctx->merging, ctx, tween, exps); else updatePolygon(parent, star, frameNo, (identity ? nullptr : &matrix), ctx->merging, ctx, tween, exps);
PAINT(ctx->merging)->update(RenderUpdateFlag::Path); PAINT(ctx->merging)->mark(RenderUpdateFlag::Path);
} else { } else {
auto shape = star->pooling(); auto shape = star->pooling();
shape->reset(); shape->reset();
@ -1037,7 +1037,7 @@ void LottieBuilder::updateText(LottieLayer* layer, float frameNo)
auto group = static_cast<LottieGroup*>(*p); auto group = static_cast<LottieGroup*>(*p);
ARRAY_FOREACH(p, group->children) { ARRAY_FOREACH(p, group->children) {
if (static_cast<LottiePath*>(*p)->pathset(frameNo, SHAPE(shape)->rs.path, nullptr, tween, exps)) { if (static_cast<LottiePath*>(*p)->pathset(frameNo, SHAPE(shape)->rs.path, nullptr, tween, exps)) {
PAINT(shape)->update(RenderUpdateFlag::Path); PAINT(shape)->mark(RenderUpdateFlag::Path);
} }
} }
} }

View file

@ -1469,10 +1469,10 @@ RenderData GlRenderer::prepare(const RenderShape& rshape, RenderData data, const
if (clipper) { if (clipper) {
sdata->updateFlag = (rshape.stroke && (rshape.stroke->width > 0)) ? RenderUpdateFlag::Stroke : RenderUpdateFlag::Path; sdata->updateFlag = (rshape.stroke && (rshape.stroke->width > 0)) ? RenderUpdateFlag::Stroke : RenderUpdateFlag::Path;
} else { } else {
if (alphaF) sdata->updateFlag = static_cast<RenderUpdateFlag>(RenderUpdateFlag::Color | sdata->updateFlag); if (alphaF) sdata->updateFlag = (RenderUpdateFlag::Color | sdata->updateFlag);
if (rshape.fill) sdata->updateFlag = static_cast<RenderUpdateFlag>(RenderUpdateFlag::Gradient | sdata->updateFlag); if (rshape.fill) sdata->updateFlag = (RenderUpdateFlag::Gradient | sdata->updateFlag);
if (alphaS) sdata->updateFlag = static_cast<RenderUpdateFlag>(RenderUpdateFlag::Stroke | sdata->updateFlag); if (alphaS) sdata->updateFlag = (RenderUpdateFlag::Stroke | sdata->updateFlag);
if (rshape.strokeFill()) sdata->updateFlag = static_cast<RenderUpdateFlag>(RenderUpdateFlag::GradientStroke | sdata->updateFlag); if (rshape.strokeFill()) sdata->updateFlag = (RenderUpdateFlag::GradientStroke | sdata->updateFlag);
} }
if (sdata->updateFlag == RenderUpdateFlag::None) return sdata; if (sdata->updateFlag == RenderUpdateFlag::None) return sdata;

View file

@ -67,10 +67,10 @@ static uint32_t _estimateAAMargin(const Fill* fdata)
constexpr float marginScalingFactor = 800.0f; constexpr float marginScalingFactor = 800.0f;
if (fdata->type() == Type::RadialGradient) { if (fdata->type() == Type::RadialGradient) {
auto radius = RADIAL(fdata)->r; auto radius = CONST_RADIAL(fdata)->r;
return tvg::zero(radius) ? 0 : static_cast<uint32_t>(marginScalingFactor / radius); return tvg::zero(radius) ? 0 : static_cast<uint32_t>(marginScalingFactor / radius);
} else { } else {
auto grad = LINEAR(fdata); auto grad = CONST_LINEAR(fdata);
Point p1 {grad->x1, grad->y1}; Point p1 {grad->x1, grad->y1};
Point p2 {grad->x2, grad->y2}; Point p2 {grad->x2, grad->y2};
auto len = length(&p1, &p2); auto len = length(&p1, &p2);
@ -226,7 +226,7 @@ bool _prepareLinear(SwFill* fill, const LinearGradient* linear, const Matrix& pT
fill->linear.dy /= len; fill->linear.dy /= len;
fill->linear.offset = -fill->linear.dx * x1 - fill->linear.dy * y1; fill->linear.offset = -fill->linear.dx * x1 - fill->linear.dy * y1;
auto transform = pTransform * linear->transform(); const auto& transform = pTransform * linear->transform();
Matrix itransform; Matrix itransform;
if (!inverse(&transform, &itransform)) return false; if (!inverse(&transform, &itransform)) return false;
@ -279,7 +279,7 @@ bool _prepareRadial(SwFill* fill, const RadialGradient* radial, const Matrix& pT
if (fill->radial.a > 0) fill->radial.invA = 1.0f / fill->radial.a; if (fill->radial.a > 0) fill->radial.invA = 1.0f / fill->radial.a;
auto transform = pTransform * radial->transform(); const auto& transform = pTransform * radial->transform();
Matrix itransform; Matrix itransform;
if (!inverse(&transform, &itransform)) return false; if (!inverse(&transform, &itransform)) return false;

View file

@ -89,8 +89,6 @@ namespace tvg {
uint16_t THORVG_VERSION_NUMBER(); uint16_t THORVG_VERSION_NUMBER();
#define PIMPL(INST, CLASS) ((CLASS::Impl*)INST->pImpl) //Access to pimpl
#define TVG_DELETE(PAINT) \ #define TVG_DELETE(PAINT) \
if (PAINT->refCnt() == 0) delete(PAINT) if (PAINT->refCnt() == 0) delete(PAINT)

View file

@ -27,16 +27,8 @@
/* Fill Class Implementation */ /* Fill Class Implementation */
/************************************************************************/ /************************************************************************/
Fill::Fill() Fill::Fill() = default;
{ Fill::~Fill() = default;
}
Fill::~Fill()
{
delete(pImpl);
}
Result Fill::colorStops(const ColorStop* colorStops, uint32_t cnt) noexcept Result Fill::colorStops(const ColorStop* colorStops, uint32_t cnt) noexcept
{ {
@ -79,7 +71,9 @@ Matrix& Fill::transform() const noexcept
Fill* Fill::duplicate() const noexcept Fill* Fill::duplicate() const noexcept
{ {
return pImpl->duplicate(); if (type() == Type::LinearGradient) return CONST_LINEAR(this)->duplicate();
else if (type() == Type::RadialGradient) return CONST_RADIAL(this)->duplicate();
return nullptr;
} }
@ -87,11 +81,7 @@ Fill* Fill::duplicate() const noexcept
/* RadialGradient Class Implementation */ /* RadialGradient Class Implementation */
/************************************************************************/ /************************************************************************/
RadialGradient::RadialGradient() = default;
RadialGradient::RadialGradient()
{
Fill::pImpl = new Impl;
}
Result RadialGradient::radial(float cx, float cy, float r, float fx, float fy, float fr) noexcept Result RadialGradient::radial(float cx, float cy, float r, float fx, float fy, float fr) noexcept
@ -102,13 +92,13 @@ Result RadialGradient::radial(float cx, float cy, float r, float fx, float fy, f
Result RadialGradient::radial(float* cx, float* cy, float* r, float* fx, float* fy, float* fr) const noexcept Result RadialGradient::radial(float* cx, float* cy, float* r, float* fx, float* fy, float* fr) const noexcept
{ {
return RADIAL(this)->radial(cx, cy, r, fx, fy, fr); return CONST_RADIAL(this)->radial(cx, cy, r, fx, fy, fr);
} }
RadialGradient* RadialGradient::gen() noexcept RadialGradient* RadialGradient::gen() noexcept
{ {
return new RadialGradient; return new RadialGradientImpl;
} }
@ -122,11 +112,7 @@ Type RadialGradient::type() const noexcept
/* LinearGradient Class Implementation */ /* LinearGradient Class Implementation */
/************************************************************************/ /************************************************************************/
LinearGradient::LinearGradient() = default;
LinearGradient::LinearGradient()
{
Fill::pImpl = new Impl;
}
Result LinearGradient::linear(float x1, float y1, float x2, float y2) noexcept Result LinearGradient::linear(float x1, float y1, float x2, float y2) noexcept
@ -137,13 +123,13 @@ Result LinearGradient::linear(float x1, float y1, float x2, float y2) noexcept
Result LinearGradient::linear(float* x1, float* y1, float* x2, float* y2) const noexcept Result LinearGradient::linear(float* x1, float* y1, float* x2, float* y2) const noexcept
{ {
return LINEAR(this)->linear(x1, y1, x2, y2); return CONST_LINEAR(this)->linear(x1, y1, x2, y2);
} }
LinearGradient* LinearGradient::gen() noexcept LinearGradient* LinearGradient::gen() noexcept
{ {
return new LinearGradient; return new LinearGradientImpl;
} }

View file

@ -26,8 +26,11 @@
#include "tvgCommon.h" #include "tvgCommon.h"
#include "tvgMath.h" #include "tvgMath.h"
#define LINEAR(A) PIMPL(A, LinearGradient) #define LINEAR(A) static_cast<LinearGradientImpl*>(A)
#define RADIAL(A) PIMPL(A, RadialGradient) #define CONST_LINEAR(A) static_cast<const LinearGradientImpl*>(A)
#define RADIAL(A) static_cast<RadialGradientImpl*>(A)
#define CONST_RADIAL(A) static_cast<const RadialGradientImpl*>(A)
struct Fill::Impl struct Fill::Impl
{ {
@ -41,13 +44,13 @@ struct Fill::Impl
tvg::free(colorStops); tvg::free(colorStops);
} }
void copy(Fill::Impl* dup) void copy(const Fill::Impl& dup)
{ {
cnt = dup->cnt; cnt = dup.cnt;
spread = dup->spread; spread = dup.spread;
colorStops = tvg::malloc<ColorStop*>(sizeof(ColorStop) * dup->cnt); colorStops = tvg::malloc<ColorStop*>(sizeof(ColorStop) * dup.cnt);
if (dup->cnt > 0) memcpy(colorStops, dup->colorStops, sizeof(ColorStop) * dup->cnt); if (dup.cnt > 0) memcpy(colorStops, dup.colorStops, sizeof(ColorStop) * dup.cnt);
transform = dup->transform; transform = dup.transform;
} }
Result update(const ColorStop* colorStops, uint32_t cnt) Result update(const ColorStop* colorStops, uint32_t cnt)
@ -72,21 +75,26 @@ struct Fill::Impl
return Result::Success; return Result::Success;
} }
virtual Fill* duplicate() = 0;
}; };
struct RadialGradient::Impl : Fill::Impl struct RadialGradientImpl : RadialGradient
{ {
Fill::Impl impl;
float cx = 0.0f, cy = 0.0f; float cx = 0.0f, cy = 0.0f;
float fx = 0.0f, fy = 0.0f; float fx = 0.0f, fy = 0.0f;
float r = 0.0f, fr = 0.0f; float r = 0.0f, fr = 0.0f;
Fill* duplicate() override RadialGradientImpl()
{
Fill::pImpl = &impl;
}
Fill* duplicate() const
{ {
auto ret = RadialGradient::gen(); auto ret = RadialGradient::gen();
RADIAL(ret)->copy(this); RADIAL(ret)->impl.copy(this->impl);
RADIAL(ret)->cx = cx; RADIAL(ret)->cx = cx;
RADIAL(ret)->cy = cy; RADIAL(ret)->cy = cy;
RADIAL(ret)->r = r; RADIAL(ret)->r = r;
@ -125,17 +133,24 @@ struct RadialGradient::Impl : Fill::Impl
}; };
struct LinearGradient::Impl : Fill::Impl struct LinearGradientImpl : LinearGradient
{ {
Fill::Impl impl;
float x1 = 0.0f; float x1 = 0.0f;
float y1 = 0.0f; float y1 = 0.0f;
float x2 = 0.0f; float x2 = 0.0f;
float y2 = 0.0f; float y2 = 0.0f;
Fill* duplicate() override LinearGradientImpl()
{
Fill::pImpl = &impl;
}
Fill* duplicate() const
{ {
auto ret = LinearGradient::gen(); auto ret = LinearGradient::gen();
LINEAR(ret)->copy(this); LINEAR(ret)->impl.copy(this->impl);
LINEAR(ret)->x1 = x1; LINEAR(ret)->x1 = x1;
LINEAR(ret)->y1 = y1; LINEAR(ret)->y1 = y1;
LINEAR(ret)->x2 = x2; LINEAR(ret)->x2 = x2;

View file

@ -163,7 +163,7 @@ Paint* Paint::Impl::duplicate(Paint* ret)
//duplicate Transform //duplicate Transform
ret->pImpl->tr = tr; ret->pImpl->tr = tr;
ret->pImpl->renderFlag |= RenderUpdateFlag::Transform; ret->pImpl->mark(RenderUpdateFlag::Transform);
ret->pImpl->opacity = opacity; ret->pImpl->opacity = opacity;
@ -247,7 +247,7 @@ RenderData Paint::Impl::update(RenderMethod* renderer, const Matrix& pm, Array<R
/* 2. Clipping */ /* 2. Clipping */
if (this->clipper) { if (this->clipper) {
auto pclip = PAINT(this->clipper); auto pclip = PAINT(this->clipper);
if (pclip->renderFlag) renderFlag |= RenderUpdateFlag::Clip; if (pclip->renderFlag) mark(RenderUpdateFlag::Clip);
pclip->ctxFlag &= ~ContextFlag::FastTrack; //reset pclip->ctxFlag &= ~ContextFlag::FastTrack; //reset
viewport = renderer->viewport(); viewport = renderer->viewport();
/* TODO: Intersect the clipper's clipper, if both are FastTrack. /* TODO: Intersect the clipper's clipper, if both are FastTrack.
@ -263,7 +263,7 @@ RenderData Paint::Impl::update(RenderMethod* renderer, const Matrix& pm, Array<R
} }
/* 3. Main Update */ /* 3. Main Update */
auto newFlag = static_cast<RenderUpdateFlag>(pFlag | renderFlag); auto newFlag = pFlag | renderFlag;
renderFlag = RenderUpdateFlag::None; renderFlag = RenderUpdateFlag::None;
opacity = MULTIPLY(opacity, this->opacity); opacity = MULTIPLY(opacity, this->opacity);
@ -317,15 +317,8 @@ Result Paint::Impl::bounds(Point* pt4, Matrix* pm, bool obb, bool stroking)
/* External Class Implementation */ /* External Class Implementation */
/************************************************************************/ /************************************************************************/
Paint :: Paint() Paint :: Paint() = default;
{ Paint :: ~Paint() = default;
}
Paint :: ~Paint()
{
delete(pImpl);
}
Result Paint::rotate(float degree) noexcept Result Paint::rotate(float degree) noexcept
@ -410,7 +403,7 @@ Result Paint::opacity(uint8_t o) noexcept
if (pImpl->opacity == o) return Result::Success; if (pImpl->opacity == o) return Result::Success;
pImpl->opacity = o; pImpl->opacity = o;
pImpl->renderFlag |= RenderUpdateFlag::Color; pImpl->mark(RenderUpdateFlag::Color);
return Result::Success; return Result::Success;
} }

View file

@ -27,7 +27,8 @@
#include "tvgRender.h" #include "tvgRender.h"
#include "tvgMath.h" #include "tvgMath.h"
#define PAINT(A) PIMPL(A, Paint)
#define PAINT(A) ((Paint::Impl*)A->pImpl)
namespace tvg namespace tvg
{ {
@ -85,6 +86,7 @@ namespace tvg
Impl(Paint* pnt) : paint(pnt) Impl(Paint* pnt) : paint(pnt)
{ {
pnt->pImpl = this;
reset(); reset();
} }
@ -129,7 +131,7 @@ namespace tvg
return refCnt; return refCnt;
} }
void update(RenderUpdateFlag flag) void mark(RenderUpdateFlag flag)
{ {
renderFlag |= flag; renderFlag |= flag;
} }
@ -138,7 +140,7 @@ namespace tvg
{ {
if (&tr.m != &m) tr.m = m; if (&tr.m != &m) tr.m = m;
tr.overriding = true; tr.overriding = true;
renderFlag |= RenderUpdateFlag::Transform; mark(RenderUpdateFlag::Transform);
return true; return true;
} }
@ -236,7 +238,7 @@ namespace tvg
if (tr.overriding) return false; if (tr.overriding) return false;
if (tvg::equal(degree, tr.degree)) return true; if (tvg::equal(degree, tr.degree)) return true;
tr.degree = degree; tr.degree = degree;
renderFlag |= RenderUpdateFlag::Transform; mark(RenderUpdateFlag::Transform);
return true; return true;
} }
@ -246,7 +248,7 @@ namespace tvg
if (tr.overriding) return false; if (tr.overriding) return false;
if (tvg::equal(factor, tr.scale)) return true; if (tvg::equal(factor, tr.scale)) return true;
tr.scale = factor; tr.scale = factor;
renderFlag |= RenderUpdateFlag::Transform; mark(RenderUpdateFlag::Transform);
return true; return true;
} }
@ -257,7 +259,7 @@ namespace tvg
if (tvg::equal(x, tr.m.e13) && tvg::equal(y, tr.m.e23)) return true; if (tvg::equal(x, tr.m.e13) && tvg::equal(y, tr.m.e23)) return true;
tr.m.e13 = x; tr.m.e13 = x;
tr.m.e23 = y; tr.m.e23 = y;
renderFlag |= RenderUpdateFlag::Transform; mark(RenderUpdateFlag::Transform);
return true; return true;
} }
@ -266,7 +268,7 @@ namespace tvg
{ {
if (blendMethod != method) { if (blendMethod != method) {
blendMethod = method; blendMethod = method;
renderFlag |= RenderUpdateFlag::Blend; mark(RenderUpdateFlag::Blend);
} }
} }

View file

@ -23,15 +23,12 @@
#include "tvgPaint.h" #include "tvgPaint.h"
#include "tvgPicture.h" #include "tvgPicture.h"
Picture::Picture() Picture::Picture() = default;
{
pImpl = new Impl(this);
}
Picture* Picture::gen() noexcept Picture* Picture::gen() noexcept
{ {
return new Picture; return new PictureImpl;
} }
@ -74,7 +71,7 @@ Result Picture::size(float w, float h) noexcept
Result Picture::size(float* w, float* h) const noexcept Result Picture::size(float* w, float* h) const noexcept
{ {
return PICTURE(this)->size(w, h); return CONST_PICTURE(this)->size(w, h);
} }

View file

@ -26,7 +26,8 @@
#include "tvgPaint.h" #include "tvgPaint.h"
#include "tvgLoader.h" #include "tvgLoader.h"
#define PICTURE(A) PIMPL(A, Picture) #define PICTURE(A) static_cast<PictureImpl*>(A)
#define CONST_PICTURE(A) static_cast<const PictureImpl*>(A)
struct PictureIterator : Iterator struct PictureIterator : Iterator
{ {
@ -55,8 +56,9 @@ struct PictureIterator : Iterator
}; };
struct Picture::Impl : Paint::Impl struct PictureImpl : Picture
{ {
Paint::Impl impl;
ImageLoader* loader = nullptr; ImageLoader* loader = nullptr;
Paint* vector = nullptr; //vector picture uses Paint* vector = nullptr; //vector picture uses
RenderSurface* bitmap = nullptr; //bitmap picture uses RenderSurface* bitmap = nullptr; //bitmap picture uses
@ -64,11 +66,11 @@ struct Picture::Impl : Paint::Impl
uint8_t compFlag = CompositionFlag::Invalid; uint8_t compFlag = CompositionFlag::Invalid;
bool resizing = false; bool resizing = false;
Impl(Picture* p) : Paint::Impl(p) PictureImpl() : impl(Paint::Impl(this))
{ {
} }
~Impl() ~PictureImpl()
{ {
LoaderMgr::retrieve(loader); LoaderMgr::retrieve(loader);
delete(vector); delete(vector);
@ -76,10 +78,10 @@ struct Picture::Impl : Paint::Impl
RenderData update(RenderMethod* renderer, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, TVG_UNUSED bool clipper) RenderData update(RenderMethod* renderer, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, TVG_UNUSED bool clipper)
{ {
auto flag = static_cast<RenderUpdateFlag>(pFlag | load()); auto flag = (pFlag | load());
if (bitmap) { if (bitmap) {
if (flag == RenderUpdateFlag::None) return rd; if (flag == RenderUpdateFlag::None) return impl.rd;
//Overriding Transformation by the desired image size //Overriding Transformation by the desired image size
auto sx = w / loader->w; auto sx = w / loader->w;
@ -87,7 +89,7 @@ struct Picture::Impl : Paint::Impl
auto scale = sx < sy ? sx : sy; auto scale = sx < sy ? sx : sy;
auto m = transform * Matrix{scale, 0, 0, 0, scale, 0, 0, 0, 1}; auto m = transform * Matrix{scale, 0, 0, 0, scale, 0, 0, 0, 1};
rd = renderer->prepare(bitmap, rd, m, clips, opacity, flag); impl.rd = renderer->prepare(bitmap, impl.rd, m, clips, opacity, flag);
} else if (vector) { } else if (vector) {
if (resizing) { if (resizing) {
loader->resize(vector, w, h); loader->resize(vector, w, h);
@ -96,7 +98,7 @@ struct Picture::Impl : Paint::Impl
queryComposition(opacity); queryComposition(opacity);
return vector->pImpl->update(renderer, transform, clips, opacity, flag, false); return vector->pImpl->update(renderer, transform, clips, opacity, flag, false);
} }
return rd; return impl.rd;
} }
void size(float w, float h) void size(float w, float h)
@ -173,7 +175,7 @@ struct Picture::Impl : Paint::Impl
if (loader) { if (loader) {
dup->loader = loader; dup->loader = loader;
++dup->loader->sharing; ++dup->loader->sharing;
PAINT(picture)->renderFlag |= RenderUpdateFlag::Image; PAINT(picture)->mark(RenderUpdateFlag::Image);
} }
dup->bitmap = bitmap; dup->bitmap = bitmap;
@ -214,7 +216,7 @@ struct Picture::Impl : Paint::Impl
} else { } else {
vector = loader->paint(); vector = loader->paint();
if (vector) { if (vector) {
PAINT(vector)->parent = paint; PAINT(vector)->parent = this;
if (w != loader->w || h != loader->h) { if (w != loader->w || h != loader->h) {
if (!resizing) { if (!resizing) {
w = loader->w; w = loader->w;
@ -244,7 +246,7 @@ struct Picture::Impl : Paint::Impl
//Composition test //Composition test
const Paint* target; const Paint* target;
paint->mask(&target); PAINT(this)->mask(&target);
if (!target || target->pImpl->opacity == 255 || target->pImpl->opacity == 0) return; if (!target || target->pImpl->opacity == 255 || target->pImpl->opacity == 0) return;
compFlag = CompositionFlag::Opacity; compFlag = CompositionFlag::Opacity;
} }
@ -252,9 +254,9 @@ struct Picture::Impl : Paint::Impl
bool render(RenderMethod* renderer) bool render(RenderMethod* renderer)
{ {
bool ret = false; bool ret = false;
renderer->blend(blendMethod); renderer->blend(impl.blendMethod);
if (bitmap) return renderer->renderImage(rd); if (bitmap) return renderer->renderImage(impl.rd);
else if (vector) { else if (vector) {
RenderCompositor* cmp = nullptr; RenderCompositor* cmp = nullptr;
if (compFlag) { if (compFlag) {
@ -269,7 +271,7 @@ struct Picture::Impl : Paint::Impl
RenderRegion bounds(RenderMethod* renderer) RenderRegion bounds(RenderMethod* renderer)
{ {
if (rd) return renderer->region(rd); if (impl.rd) return renderer->region(impl.rd);
if (vector) return vector->pImpl->bounds(renderer); if (vector) return vector->pImpl->bounds(renderer);
return {0, 0, 0, 0}; return {0, 0, 0, 0};
} }

View file

@ -45,6 +45,12 @@ static inline void operator|=(RenderUpdateFlag& a, const RenderUpdateFlag b)
a = RenderUpdateFlag(uint16_t(a) | uint16_t(b)); a = RenderUpdateFlag(uint16_t(a) | uint16_t(b));
} }
static inline RenderUpdateFlag operator|(const RenderUpdateFlag a, const RenderUpdateFlag b)
{
return RenderUpdateFlag(uint16_t(a) | uint16_t(b));
}
struct RenderSurface struct RenderSurface
{ {
union { union {

View file

@ -23,15 +23,12 @@
#include "tvgScene.h" #include "tvgScene.h"
Scene::Scene() Scene::Scene() = default;
{
pImpl = new Impl(this);
}
Scene* Scene::gen() noexcept Scene* Scene::gen() noexcept
{ {
return new Scene; return new SceneImpl;
} }
@ -56,7 +53,7 @@ Result Scene::remove(Paint* paint) noexcept
const list<Paint*>& Scene::paints() const noexcept const list<Paint*>& Scene::paints() const noexcept
{ {
return SCENE(this)->paints; return CONST_SCENE(this)->paints;
} }

View file

@ -28,7 +28,8 @@
#include "tvgMath.h" #include "tvgMath.h"
#include "tvgPaint.h" #include "tvgPaint.h"
#define SCENE(A) PIMPL(A, Scene) #define SCENE(A) static_cast<SceneImpl*>(A)
#define CONST_SCENE(A) static_cast<const SceneImpl*>(A)
struct SceneIterator : Iterator struct SceneIterator : Iterator
{ {
@ -59,22 +60,22 @@ struct SceneIterator : Iterator
} }
}; };
struct Scene::Impl : Paint::Impl struct SceneImpl : Scene
{ {
Paint::Impl impl;
list<Paint*> paints; //children list list<Paint*> paints; //children list
RenderRegion vport = {0, 0, INT32_MAX, INT32_MAX}; RenderRegion vport = {0, 0, INT32_MAX, INT32_MAX};
Array<RenderEffect*>* effects = nullptr; Array<RenderEffect*>* effects = nullptr;
uint8_t compFlag = CompositionFlag::Invalid; uint8_t compFlag = CompositionFlag::Invalid;
uint8_t opacity; //for composition uint8_t opacity; //for composition
Impl(Scene* s) : Paint::Impl(s) SceneImpl() : impl(Paint::Impl(this))
{ {
} }
~Impl() ~SceneImpl()
{ {
resetEffects(); resetEffects();
clearPaints(); clearPaints();
} }
@ -86,8 +87,8 @@ struct Scene::Impl : Paint::Impl
//post effects, masking, blending may require composition //post effects, masking, blending may require composition
if (effects) compFlag |= CompositionFlag::PostProcessing; if (effects) compFlag |= CompositionFlag::PostProcessing;
if (paint->mask(nullptr) != MaskMethod::None) compFlag |= CompositionFlag::Masking; if (PAINT(this)->mask(nullptr) != MaskMethod::None) compFlag |= CompositionFlag::Masking;
if (blendMethod != BlendMethod::Normal) compFlag |= CompositionFlag::Blending; if (impl.blendMethod != BlendMethod::Normal) compFlag |= CompositionFlag::Blending;
//Half translucent requires intermediate composition. //Half translucent requires intermediate composition.
if (opacity == 255) return compFlag; if (opacity == 255) return compFlag;
@ -130,7 +131,7 @@ struct Scene::Impl : Paint::Impl
RenderCompositor* cmp = nullptr; RenderCompositor* cmp = nullptr;
auto ret = true; auto ret = true;
renderer->blend(blendMethod); renderer->blend(impl.blendMethod);
if (compFlag) { if (compFlag) {
cmp = renderer->target(bounds(renderer), renderer->colorSpace(), static_cast<CompositionFlag>(compFlag)); cmp = renderer->target(bounds(renderer), renderer->colorSpace(), static_cast<CompositionFlag>(compFlag));
@ -258,7 +259,7 @@ struct Scene::Impl : Paint::Impl
Result remove(Paint* paint) Result remove(Paint* paint)
{ {
if (PAINT(paint)->parent != this->paint) return Result::InsufficientCondition; if (PAINT(paint)->parent != this) return Result::InsufficientCondition;
PAINT(paint)->unref(); PAINT(paint)->unref();
paints.remove(paint); paints.remove(paint);
return Result::Success; return Result::Success;
@ -273,7 +274,7 @@ struct Scene::Impl : Paint::Impl
target->ref(); target->ref();
//Relocated the paint to the current scene space //Relocated the paint to the current scene space
timpl->renderFlag |= RenderUpdateFlag::Transform; timpl->mark(RenderUpdateFlag::Transform);
if (!at) { if (!at) {
paints.push_back(target); paints.push_back(target);
@ -283,9 +284,9 @@ struct Scene::Impl : Paint::Impl
if (itr == paints.end()) return Result::InvalidArguments; if (itr == paints.end()) return Result::InvalidArguments;
paints.insert(itr, target); paints.insert(itr, target);
} }
timpl->parent = paint; timpl->parent = this;
if (timpl->clipper) PAINT(timpl->clipper)->parent = paint; if (timpl->clipper) PAINT(timpl->clipper)->parent = this;
if (timpl->maskData) PAINT(timpl->maskData->target)->parent = paint; if (timpl->maskData) PAINT(timpl->maskData->target)->parent = this;
return Result::Success; return Result::Success;
} }
@ -298,7 +299,7 @@ struct Scene::Impl : Paint::Impl
{ {
if (effects) { if (effects) {
ARRAY_FOREACH(p, *effects) { ARRAY_FOREACH(p, *effects) {
renderer->dispose(*p); impl.renderer->dispose(*p);
delete(*p); delete(*p);
} }
delete(effects); delete(effects);

View file

@ -24,15 +24,12 @@
#include "tvgShape.h" #include "tvgShape.h"
Shape :: Shape() Shape :: Shape() = default;
{
pImpl = new Impl(this);
}
Shape* Shape::gen() noexcept Shape* Shape::gen() noexcept
{ {
return new Shape; return new ShapeImpl;
} }
@ -51,11 +48,11 @@ Result Shape::reset() noexcept
Result Shape::path(const PathCommand** cmds, uint32_t* cmdsCnt, const Point** pts, uint32_t* ptsCnt) const noexcept Result Shape::path(const PathCommand** cmds, uint32_t* cmdsCnt, const Point** pts, uint32_t* ptsCnt) const noexcept
{ {
if (cmds) *cmds = SHAPE(this)->rs.path.cmds.data; if (cmds) *cmds = CONST_SHAPE(this)->rs.path.cmds.data;
if (cmdsCnt) *cmdsCnt = SHAPE(this)->rs.path.cmds.count; if (cmdsCnt) *cmdsCnt = CONST_SHAPE(this)->rs.path.cmds.count;
if (pts) *pts = SHAPE(this)->rs.path.pts.data; if (pts) *pts = CONST_SHAPE(this)->rs.path.pts.data;
if (ptsCnt) *ptsCnt = SHAPE(this)->rs.path.pts.count; if (ptsCnt) *ptsCnt = CONST_SHAPE(this)->rs.path.pts.count;
return Result::Success; return Result::Success;
} }
@ -124,14 +121,14 @@ Result Shape::fill(Fill* f) noexcept
Result Shape::fill(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) const noexcept Result Shape::fill(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) const noexcept
{ {
SHAPE(this)->rs.fillColor(r, g, b, a); CONST_SHAPE(this)->rs.fillColor(r, g, b, a);
return Result::Success; return Result::Success;
} }
const Fill* Shape::fill() const noexcept const Fill* Shape::fill() const noexcept
{ {
return SHAPE(this)->rs.fill; return CONST_SHAPE(this)->rs.fill;
} }
@ -151,7 +148,7 @@ Result Shape::strokeWidth(float width) noexcept
float Shape::strokeWidth() const noexcept float Shape::strokeWidth() const noexcept
{ {
return SHAPE(this)->rs.strokeWidth(); return CONST_SHAPE(this)->rs.strokeWidth();
} }
@ -164,7 +161,7 @@ Result Shape::strokeFill(uint8_t r, uint8_t g, uint8_t b, uint8_t a) noexcept
Result Shape::strokeFill(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) const noexcept Result Shape::strokeFill(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) const noexcept
{ {
if (!SHAPE(this)->rs.strokeFill(r, g, b, a)) return Result::InsufficientCondition; if (!CONST_SHAPE(this)->rs.strokeFill(r, g, b, a)) return Result::InsufficientCondition;
return Result::Success; return Result::Success;
} }
@ -177,7 +174,7 @@ Result Shape::strokeFill(Fill* f) noexcept
const Fill* Shape::strokeFill() const noexcept const Fill* Shape::strokeFill() const noexcept
{ {
return SHAPE(this)->rs.strokeFill(); return CONST_SHAPE(this)->rs.strokeFill();
} }
@ -189,7 +186,7 @@ Result Shape::strokeDash(const float* dashPattern, uint32_t cnt, float offset) n
uint32_t Shape::strokeDash(const float** dashPattern, float* offset) const noexcept uint32_t Shape::strokeDash(const float** dashPattern, float* offset) const noexcept
{ {
return SHAPE(this)->rs.strokeDash(dashPattern, offset); return CONST_SHAPE(this)->rs.strokeDash(dashPattern, offset);
} }
@ -215,19 +212,19 @@ Result Shape::strokeMiterlimit(float miterlimit) noexcept
StrokeCap Shape::strokeCap() const noexcept StrokeCap Shape::strokeCap() const noexcept
{ {
return SHAPE(this)->rs.strokeCap(); return CONST_SHAPE(this)->rs.strokeCap();
} }
StrokeJoin Shape::strokeJoin() const noexcept StrokeJoin Shape::strokeJoin() const noexcept
{ {
return SHAPE(this)->rs.strokeJoin(); return CONST_SHAPE(this)->rs.strokeJoin();
} }
float Shape::strokeMiterlimit() const noexcept float Shape::strokeMiterlimit() const noexcept
{ {
return SHAPE(this)->rs.strokeMiterlimit(); return CONST_SHAPE(this)->rs.strokeMiterlimit();
} }
@ -247,5 +244,5 @@ Result Shape::fill(FillRule r) noexcept
FillRule Shape::fillRule() const noexcept FillRule Shape::fillRule() const noexcept
{ {
return SHAPE(this)->rs.rule; return CONST_SHAPE(this)->rs.rule;
} }

View file

@ -27,32 +27,34 @@
#include "tvgMath.h" #include "tvgMath.h"
#include "tvgPaint.h" #include "tvgPaint.h"
#define SHAPE(A) PIMPL(A, Shape) #define SHAPE(A) static_cast<ShapeImpl*>(A)
#define CONST_SHAPE(A) static_cast<const ShapeImpl*>(A)
struct Shape::Impl : Paint::Impl struct ShapeImpl : Shape
{ {
Paint::Impl impl;
RenderShape rs; RenderShape rs;
uint8_t compFlag = CompositionFlag::Invalid; uint8_t compFlag = CompositionFlag::Invalid;
uint8_t opacity; //for composition uint8_t opacity; //for composition
Impl(Shape* s) : Paint::Impl(s) ShapeImpl() : impl(Paint::Impl(this))
{ {
} }
bool render(RenderMethod* renderer) bool render(RenderMethod* renderer)
{ {
if (!rd) return false; if (!impl.rd) return false;
RenderCompositor* cmp = nullptr; RenderCompositor* cmp = nullptr;
renderer->blend(blendMethod); renderer->blend(impl.blendMethod);
if (compFlag) { if (compFlag) {
cmp = renderer->target(bounds(renderer), renderer->colorSpace(), static_cast<CompositionFlag>(compFlag)); cmp = renderer->target(bounds(renderer), renderer->colorSpace(), static_cast<CompositionFlag>(compFlag));
renderer->beginComposite(cmp, MaskMethod::None, opacity); renderer->beginComposite(cmp, MaskMethod::None, opacity);
} }
auto ret = renderer->renderShape(rd); auto ret = renderer->renderShape(impl.rd);
if (cmp) renderer->endComposite(cmp); if (cmp) renderer->endComposite(cmp);
return ret; return ret;
} }
@ -75,10 +77,10 @@ struct Shape::Impl : Paint::Impl
//Composition test //Composition test
const Paint* target; const Paint* target;
auto method = paint->mask(&target); auto method = PAINT(this)->mask(&target);
if (!target) return false; if (!target) return false;
if ((target->pImpl->opacity == 255 || target->pImpl->opacity == 0) && target->type() == Type::Shape) { if ((target->pImpl->opacity == 255 || target->pImpl->opacity == 0) && target->type() == tvg::Type::Shape) {
auto shape = static_cast<const Shape*>(target); auto shape = static_cast<const Shape*>(target);
if (!shape->fill()) { if (!shape->fill()) {
uint8_t r, g, b, a; uint8_t r, g, b, a;
@ -97,7 +99,7 @@ struct Shape::Impl : Paint::Impl
RenderData update(RenderMethod* renderer, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper) RenderData update(RenderMethod* renderer, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper)
{ {
if (static_cast<RenderUpdateFlag>(pFlag | renderFlag) == RenderUpdateFlag::None) return rd; if ((pFlag | impl.renderFlag) == RenderUpdateFlag::None) return impl.rd;
if (needComposition(opacity)) { if (needComposition(opacity)) {
/* Overriding opacity value. If this scene is half-translucent, /* Overriding opacity value. If this scene is half-translucent,
@ -106,14 +108,14 @@ struct Shape::Impl : Paint::Impl
opacity = 255; opacity = 255;
} }
rd = renderer->prepare(rs, rd, transform, clips, opacity, static_cast<RenderUpdateFlag>(pFlag | renderFlag), clipper); impl.rd = renderer->prepare(rs, impl.rd, transform, clips, opacity, (pFlag | impl.renderFlag), clipper);
return rd; return impl.rd;
} }
RenderRegion bounds(RenderMethod* renderer) RenderRegion bounds(RenderMethod* renderer)
{ {
if (!rd) return {0, 0, 0, 0}; if (!impl.rd) return {0, 0, 0, 0};
return renderer->region(rd); return renderer->region(impl.rd);
} }
Result bounds(Point* pt4, Matrix& m, bool obb, bool stroking) Result bounds(Point* pt4, Matrix& m, bool obb, bool stroking)
@ -178,7 +180,7 @@ struct Shape::Impl : Paint::Impl
{ {
rs.path.cmds.push(PathCommand::LineTo); rs.path.cmds.push(PathCommand::LineTo);
rs.path.pts.push({x, y}); rs.path.pts.push({x, y});
renderFlag |= RenderUpdateFlag::Path; impl.mark(RenderUpdateFlag::Path);
} }
void cubicTo(float cx1, float cy1, float cx2, float cy2, float x, float y) void cubicTo(float cx1, float cy1, float cx2, float cy2, float x, float y)
@ -188,7 +190,7 @@ struct Shape::Impl : Paint::Impl
rs.path.pts.push({cx2, cy2}); rs.path.pts.push({cx2, cy2});
rs.path.pts.push({x, y}); rs.path.pts.push({x, y});
renderFlag |= RenderUpdateFlag::Path; impl.mark(RenderUpdateFlag::Path);
} }
void close() void close()
@ -196,14 +198,14 @@ struct Shape::Impl : Paint::Impl
//Don't close multiple times. //Don't close multiple times.
if (rs.path.cmds.count > 0 && rs.path.cmds.last() == PathCommand::Close) return; if (rs.path.cmds.count > 0 && rs.path.cmds.last() == PathCommand::Close) return;
rs.path.cmds.push(PathCommand::Close); rs.path.cmds.push(PathCommand::Close);
renderFlag |= RenderUpdateFlag::Path; impl.mark(RenderUpdateFlag::Path);
} }
void strokeWidth(float width) void strokeWidth(float width)
{ {
if (!rs.stroke) rs.stroke = new RenderStroke(); if (!rs.stroke) rs.stroke = new RenderStroke();
rs.stroke->width = width; rs.stroke->width = width;
renderFlag |= RenderUpdateFlag::Stroke; impl.mark(RenderUpdateFlag::Stroke);
} }
void trimpath(const RenderTrimPath& trim) void trimpath(const RenderTrimPath& trim)
@ -216,7 +218,7 @@ struct Shape::Impl : Paint::Impl
if (tvg::equal(rs.stroke->trim.begin, trim.begin) && tvg::equal(rs.stroke->trim.end, trim.end) && rs.stroke->trim.simultaneous == trim.simultaneous) return; if (tvg::equal(rs.stroke->trim.begin, trim.begin) && tvg::equal(rs.stroke->trim.end, trim.end) && rs.stroke->trim.simultaneous == trim.simultaneous) return;
rs.stroke->trim = trim; rs.stroke->trim = trim;
renderFlag |= RenderUpdateFlag::Path; impl.mark(RenderUpdateFlag::Path);
} }
bool trimpath(float* begin, float* end) bool trimpath(float* begin, float* end)
@ -236,14 +238,14 @@ struct Shape::Impl : Paint::Impl
{ {
if (!rs.stroke) rs.stroke = new RenderStroke(); if (!rs.stroke) rs.stroke = new RenderStroke();
rs.stroke->cap = cap; rs.stroke->cap = cap;
renderFlag |= RenderUpdateFlag::Stroke; impl.mark(RenderUpdateFlag::Stroke);
} }
void strokeJoin(StrokeJoin join) void strokeJoin(StrokeJoin join)
{ {
if (!rs.stroke) rs.stroke = new RenderStroke(); if (!rs.stroke) rs.stroke = new RenderStroke();
rs.stroke->join = join; rs.stroke->join = join;
renderFlag |= RenderUpdateFlag::Stroke; impl.mark(RenderUpdateFlag::Stroke);
} }
Result strokeMiterlimit(float miterlimit) Result strokeMiterlimit(float miterlimit)
@ -253,7 +255,7 @@ struct Shape::Impl : Paint::Impl
if (miterlimit < 0.0f) return Result::InvalidArguments; if (miterlimit < 0.0f) return Result::InvalidArguments;
if (!rs.stroke) rs.stroke = new RenderStroke(); if (!rs.stroke) rs.stroke = new RenderStroke();
rs.stroke->miterlimit = miterlimit; rs.stroke->miterlimit = miterlimit;
renderFlag |= RenderUpdateFlag::Stroke; impl.mark(RenderUpdateFlag::Stroke);
return Result::Success; return Result::Success;
} }
@ -264,12 +266,12 @@ struct Shape::Impl : Paint::Impl
if (rs.stroke->fill) { if (rs.stroke->fill) {
delete(rs.stroke->fill); delete(rs.stroke->fill);
rs.stroke->fill = nullptr; rs.stroke->fill = nullptr;
renderFlag |= RenderUpdateFlag::GradientStroke; impl.mark(RenderUpdateFlag::GradientStroke);
} }
rs.stroke->color = {r, g, b, a}; rs.stroke->color = {r, g, b, a};
renderFlag |= RenderUpdateFlag::Stroke; impl.mark(RenderUpdateFlag::Stroke);
} }
Result strokeFill(Fill* f) Result strokeFill(Fill* f)
@ -281,8 +283,7 @@ struct Shape::Impl : Paint::Impl
rs.stroke->fill = f; rs.stroke->fill = f;
rs.stroke->color.a = 0; rs.stroke->color.a = 0;
renderFlag |= RenderUpdateFlag::Stroke; impl.mark(RenderUpdateFlag::Stroke | RenderUpdateFlag::GradientStroke);
renderFlag |= RenderUpdateFlag::GradientStroke;
return Result::Success; return Result::Success;
} }
@ -311,7 +312,7 @@ struct Shape::Impl : Paint::Impl
} }
rs.stroke->dash.count = cnt; rs.stroke->dash.count = cnt;
rs.stroke->dash.offset = offset; rs.stroke->dash.offset = offset;
renderFlag |= RenderUpdateFlag::Stroke; impl.mark(RenderUpdateFlag::Stroke);
return Result::Success; return Result::Success;
} }
@ -326,7 +327,7 @@ struct Shape::Impl : Paint::Impl
{ {
if (!rs.stroke) rs.stroke = new RenderStroke(); if (!rs.stroke) rs.stroke = new RenderStroke();
rs.stroke->strokeFirst = strokeFirst; rs.stroke->strokeFirst = strokeFirst;
renderFlag |= RenderUpdateFlag::Stroke; impl.mark(RenderUpdateFlag::Stroke);
} }
Result fill(Fill* f) Result fill(Fill* f)
@ -335,7 +336,7 @@ struct Shape::Impl : Paint::Impl
if (rs.fill && rs.fill != f) delete(rs.fill); if (rs.fill && rs.fill != f) delete(rs.fill);
rs.fill = f; rs.fill = f;
renderFlag |= RenderUpdateFlag::Gradient; impl.mark(RenderUpdateFlag::Gradient);
return Result::Success; return Result::Success;
} }
@ -345,20 +346,20 @@ struct Shape::Impl : Paint::Impl
if (rs.fill) { if (rs.fill) {
delete(rs.fill); delete(rs.fill);
rs.fill = nullptr; rs.fill = nullptr;
renderFlag |= RenderUpdateFlag::Gradient; impl.mark(RenderUpdateFlag::Gradient);
} }
if (r == rs.color.r && g == rs.color.g && b == rs.color.b && a == rs.color.a) return; if (r == rs.color.r && g == rs.color.g && b == rs.color.b && a == rs.color.a) return;
rs.color = {r, g, b, a}; rs.color = {r, g, b, a};
renderFlag |= RenderUpdateFlag::Color; impl.mark(RenderUpdateFlag::Color);
} }
void resetPath() void resetPath()
{ {
rs.path.cmds.clear(); rs.path.cmds.clear();
rs.path.pts.clear(); rs.path.pts.clear();
renderFlag |= RenderUpdateFlag::Path; impl.mark(RenderUpdateFlag::Path);
} }
Result appendPath(const PathCommand *cmds, uint32_t cmdCnt, const Point* pts, uint32_t ptsCnt) Result appendPath(const PathCommand *cmds, uint32_t cmdCnt, const Point* pts, uint32_t ptsCnt)
@ -367,7 +368,7 @@ struct Shape::Impl : Paint::Impl
grow(cmdCnt, ptsCnt); grow(cmdCnt, ptsCnt);
append(cmds, cmdCnt, pts, ptsCnt); append(cmds, cmdCnt, pts, ptsCnt);
renderFlag |= RenderUpdateFlag::Path; impl.mark(RenderUpdateFlag::Path);
return Result::Success; return Result::Success;
} }
@ -403,7 +404,7 @@ struct Shape::Impl : Paint::Impl
rs.path.pts.count += 13; rs.path.pts.count += 13;
renderFlag |= RenderUpdateFlag::Path; impl.mark(RenderUpdateFlag::Path);
} }
void appendRect(float x, float y, float w, float h, float rx, float ry, bool cw) void appendRect(float x, float y, float w, float h, float rx, float ry, bool cw)
@ -478,7 +479,7 @@ struct Shape::Impl : Paint::Impl
rs.path.cmds.count += 10; rs.path.cmds.count += 10;
rs.path.pts.count += 17; rs.path.pts.count += 17;
} }
renderFlag |= RenderUpdateFlag::Path; impl.mark(RenderUpdateFlag::Path);
} }
Paint* duplicate(Paint* ret) Paint* duplicate(Paint* ret)
@ -491,7 +492,7 @@ struct Shape::Impl : Paint::Impl
delete(dup->rs.fill); delete(dup->rs.fill);
//Default Properties //Default Properties
dup->renderFlag = RenderUpdateFlag::All; dup->impl.mark(RenderUpdateFlag::All);
dup->rs.rule = rs.rule; dup->rs.rule = rs.rule;
dup->rs.color = rs.color; dup->rs.color = rs.color;
@ -517,7 +518,7 @@ struct Shape::Impl : Paint::Impl
void reset() void reset()
{ {
PAINT(paint)->reset(); PAINT(this)->reset();
rs.path.cmds.clear(); rs.path.cmds.clear();
rs.path.pts.clear(); rs.path.pts.clear();

View file

@ -24,10 +24,7 @@
#include "tvgText.h" #include "tvgText.h"
Text::Text() Text::Text() = default;
{
pImpl = new Impl(this);
}
Result Text::text(const char* text) noexcept Result Text::text(const char* text) noexcept
@ -102,7 +99,7 @@ Result Text::fill(Fill* f) noexcept
Text* Text::gen() noexcept Text* Text::gen() noexcept
{ {
return new Text; return new TextImpl;
} }

View file

@ -29,10 +29,12 @@
#include "tvgFill.h" #include "tvgFill.h"
#include "tvgLoader.h" #include "tvgLoader.h"
#define TEXT(A) PIMPL(A, Text) #define TEXT(A) static_cast<TextImpl*>(A)
#define CONST_TEXT(A) static_cast<const TextImpl*>(A)
struct Text::Impl : Paint::Impl struct TextImpl : Text
{ {
Paint::Impl impl;
Shape* shape; //text shape Shape* shape; //text shape
FontLoader* loader = nullptr; FontLoader* loader = nullptr;
FontMetrics metrics; FontMetrics metrics;
@ -41,13 +43,13 @@ struct Text::Impl : Paint::Impl
bool italic = false; bool italic = false;
bool changed = false; bool changed = false;
Impl(Text* p) : Paint::Impl(p), shape(Shape::gen()) TextImpl() : impl(Paint::Impl(this)), shape(Shape::gen())
{ {
PAINT(shape)->parent = p; PAINT(shape)->parent = this;
shape->fill(FillRule::EvenOdd); shape->fill(FillRule::EvenOdd);
} }
~Impl() ~TextImpl()
{ {
tvg::free(utf8); tvg::free(utf8);
LoaderMgr::retrieve(loader); LoaderMgr::retrieve(loader);
@ -95,7 +97,7 @@ struct Text::Impl : Paint::Impl
bool render(RenderMethod* renderer) bool render(RenderMethod* renderer)
{ {
if (!loader) return true; if (!loader) return true;
renderer->blend(blendMethod); renderer->blend(impl.blendMethod);
return PAINT(shape)->render(renderer); return PAINT(shape)->render(renderer);
} }
@ -118,7 +120,7 @@ struct Text::Impl : Paint::Impl
//transform the gradient coordinates based on the final scaled font. //transform the gradient coordinates based on the final scaled font.
auto fill = SHAPE(shape)->rs.fill; auto fill = SHAPE(shape)->rs.fill;
if (fill && SHAPE(shape)->renderFlag & RenderUpdateFlag::Gradient) { if (fill && SHAPE(shape)->impl.renderFlag & RenderUpdateFlag::Gradient) {
if (fill->type() == Type::LinearGradient) { if (fill->type() == Type::LinearGradient) {
LINEAR(fill)->x1 *= scale; LINEAR(fill)->x1 *= scale;
LINEAR(fill)->y1 *= scale; LINEAR(fill)->y1 *= scale;
@ -172,6 +174,4 @@ struct Text::Impl : Paint::Impl
} }
}; };
#endif //_TVG_TEXT_H #endif //_TVG_TEXT_H