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.
* 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:
/**
@ -863,8 +865,9 @@ public:
*
* @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:
/**
@ -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.
* 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:
/**
@ -1319,8 +1324,10 @@ public:
*
* @note Supported formats are depended on the available TVG loaders.
* @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:
/**
@ -1450,8 +1457,10 @@ public:
*
* 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.
*
* @warning This class is not designed for inheritance.
*/
class TVG_API Scene final : public Paint
class TVG_API Scene : public Paint
{
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.
*
* @warning This class is not designed for inheritance.
*
* @since 0.15
*/
class TVG_API Text final : public Paint
class TVG_API Text : public Paint
{
public:
/**

View file

@ -481,7 +481,7 @@ void LottieBuilder::updatePath(LottieGroup* parent, LottieObject** child, float
if (ctx->repeaters.empty()) {
_draw(parent, path, ctx);
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 {
auto shape = path->pooling();
@ -695,7 +695,7 @@ void LottieBuilder::updatePolystar(LottieGroup* parent, LottieObject** child, fl
_draw(parent, star, ctx);
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);
PAINT(ctx->merging)->update(RenderUpdateFlag::Path);
PAINT(ctx->merging)->mark(RenderUpdateFlag::Path);
} else {
auto shape = star->pooling();
shape->reset();
@ -1037,7 +1037,7 @@ void LottieBuilder::updateText(LottieLayer* layer, float frameNo)
auto group = static_cast<LottieGroup*>(*p);
ARRAY_FOREACH(p, group->children) {
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) {
sdata->updateFlag = (rshape.stroke && (rshape.stroke->width > 0)) ? RenderUpdateFlag::Stroke : RenderUpdateFlag::Path;
} else {
if (alphaF) sdata->updateFlag = static_cast<RenderUpdateFlag>(RenderUpdateFlag::Color | sdata->updateFlag);
if (rshape.fill) sdata->updateFlag = static_cast<RenderUpdateFlag>(RenderUpdateFlag::Gradient | sdata->updateFlag);
if (alphaS) sdata->updateFlag = static_cast<RenderUpdateFlag>(RenderUpdateFlag::Stroke | sdata->updateFlag);
if (rshape.strokeFill()) sdata->updateFlag = static_cast<RenderUpdateFlag>(RenderUpdateFlag::GradientStroke | sdata->updateFlag);
if (alphaF) sdata->updateFlag = (RenderUpdateFlag::Color | sdata->updateFlag);
if (rshape.fill) sdata->updateFlag = (RenderUpdateFlag::Gradient | sdata->updateFlag);
if (alphaS) sdata->updateFlag = (RenderUpdateFlag::Stroke | sdata->updateFlag);
if (rshape.strokeFill()) sdata->updateFlag = (RenderUpdateFlag::GradientStroke | sdata->updateFlag);
}
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;
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);
} else {
auto grad = LINEAR(fdata);
auto grad = CONST_LINEAR(fdata);
Point p1 {grad->x1, grad->y1};
Point p2 {grad->x2, grad->y2};
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.offset = -fill->linear.dx * x1 - fill->linear.dy * y1;
auto transform = pTransform * linear->transform();
const auto& transform = pTransform * linear->transform();
Matrix itransform;
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;
auto transform = pTransform * radial->transform();
const auto& transform = pTransform * radial->transform();
Matrix itransform;
if (!inverse(&transform, &itransform)) return false;

View file

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

View file

@ -27,16 +27,8 @@
/* Fill Class Implementation */
/************************************************************************/
Fill::Fill()
{
}
Fill::~Fill()
{
delete(pImpl);
}
Fill::Fill() = default;
Fill::~Fill() = default;
Result Fill::colorStops(const ColorStop* colorStops, uint32_t cnt) noexcept
{
@ -79,7 +71,9 @@ Matrix& Fill::transform() 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::RadialGradient()
{
Fill::pImpl = new Impl;
}
RadialGradient::RadialGradient() = default;
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
{
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
{
return new RadialGradient;
return new RadialGradientImpl;
}
@ -122,11 +112,7 @@ Type RadialGradient::type() const noexcept
/* LinearGradient Class Implementation */
/************************************************************************/
LinearGradient::LinearGradient()
{
Fill::pImpl = new Impl;
}
LinearGradient::LinearGradient() = default;
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
{
return LINEAR(this)->linear(x1, y1, x2, y2);
return CONST_LINEAR(this)->linear(x1, y1, x2, y2);
}
LinearGradient* LinearGradient::gen() noexcept
{
return new LinearGradient;
return new LinearGradientImpl;
}

View file

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

View file

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

View file

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

View file

@ -23,15 +23,12 @@
#include "tvgPaint.h"
#include "tvgPicture.h"
Picture::Picture()
{
pImpl = new Impl(this);
}
Picture::Picture() = default;
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
{
return PICTURE(this)->size(w, h);
return CONST_PICTURE(this)->size(w, h);
}

View file

@ -26,7 +26,8 @@
#include "tvgPaint.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
{
@ -55,8 +56,9 @@ struct PictureIterator : Iterator
};
struct Picture::Impl : Paint::Impl
struct PictureImpl : Picture
{
Paint::Impl impl;
ImageLoader* loader = nullptr;
Paint* vector = nullptr; //vector picture uses
RenderSurface* bitmap = nullptr; //bitmap picture uses
@ -64,11 +66,11 @@ struct Picture::Impl : Paint::Impl
uint8_t compFlag = CompositionFlag::Invalid;
bool resizing = false;
Impl(Picture* p) : Paint::Impl(p)
PictureImpl() : impl(Paint::Impl(this))
{
}
~Impl()
~PictureImpl()
{
LoaderMgr::retrieve(loader);
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)
{
auto flag = static_cast<RenderUpdateFlag>(pFlag | load());
auto flag = (pFlag | load());
if (bitmap) {
if (flag == RenderUpdateFlag::None) return rd;
if (flag == RenderUpdateFlag::None) return impl.rd;
//Overriding Transformation by the desired image size
auto sx = w / loader->w;
@ -87,7 +89,7 @@ struct Picture::Impl : Paint::Impl
auto scale = sx < sy ? sx : sy;
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) {
if (resizing) {
loader->resize(vector, w, h);
@ -96,7 +98,7 @@ struct Picture::Impl : Paint::Impl
queryComposition(opacity);
return vector->pImpl->update(renderer, transform, clips, opacity, flag, false);
}
return rd;
return impl.rd;
}
void size(float w, float h)
@ -173,7 +175,7 @@ struct Picture::Impl : Paint::Impl
if (loader) {
dup->loader = loader;
++dup->loader->sharing;
PAINT(picture)->renderFlag |= RenderUpdateFlag::Image;
PAINT(picture)->mark(RenderUpdateFlag::Image);
}
dup->bitmap = bitmap;
@ -214,7 +216,7 @@ struct Picture::Impl : Paint::Impl
} else {
vector = loader->paint();
if (vector) {
PAINT(vector)->parent = paint;
PAINT(vector)->parent = this;
if (w != loader->w || h != loader->h) {
if (!resizing) {
w = loader->w;
@ -244,7 +246,7 @@ struct Picture::Impl : Paint::Impl
//Composition test
const Paint* target;
paint->mask(&target);
PAINT(this)->mask(&target);
if (!target || target->pImpl->opacity == 255 || target->pImpl->opacity == 0) return;
compFlag = CompositionFlag::Opacity;
}
@ -252,9 +254,9 @@ struct Picture::Impl : Paint::Impl
bool render(RenderMethod* renderer)
{
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) {
RenderCompositor* cmp = nullptr;
if (compFlag) {
@ -269,7 +271,7 @@ struct Picture::Impl : Paint::Impl
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);
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));
}
static inline RenderUpdateFlag operator|(const RenderUpdateFlag a, const RenderUpdateFlag b)
{
return RenderUpdateFlag(uint16_t(a) | uint16_t(b));
}
struct RenderSurface
{
union {

View file

@ -23,15 +23,12 @@
#include "tvgScene.h"
Scene::Scene()
{
pImpl = new Impl(this);
}
Scene::Scene() = default;
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
{
return SCENE(this)->paints;
return CONST_SCENE(this)->paints;
}

View file

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

View file

@ -24,15 +24,12 @@
#include "tvgShape.h"
Shape :: Shape()
{
pImpl = new Impl(this);
}
Shape :: Shape() = default;
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
{
if (cmds) *cmds = SHAPE(this)->rs.path.cmds.data;
if (cmdsCnt) *cmdsCnt = SHAPE(this)->rs.path.cmds.count;
if (cmds) *cmds = CONST_SHAPE(this)->rs.path.cmds.data;
if (cmdsCnt) *cmdsCnt = CONST_SHAPE(this)->rs.path.cmds.count;
if (pts) *pts = SHAPE(this)->rs.path.pts.data;
if (ptsCnt) *ptsCnt = SHAPE(this)->rs.path.pts.count;
if (pts) *pts = CONST_SHAPE(this)->rs.path.pts.data;
if (ptsCnt) *ptsCnt = CONST_SHAPE(this)->rs.path.pts.count;
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
{
SHAPE(this)->rs.fillColor(r, g, b, a);
CONST_SHAPE(this)->rs.fillColor(r, g, b, a);
return Result::Success;
}
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
{
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
{
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;
}
@ -177,7 +174,7 @@ Result Shape::strokeFill(Fill* f) 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
{
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
{
return SHAPE(this)->rs.strokeCap();
return CONST_SHAPE(this)->rs.strokeCap();
}
StrokeJoin Shape::strokeJoin() const noexcept
{
return SHAPE(this)->rs.strokeJoin();
return CONST_SHAPE(this)->rs.strokeJoin();
}
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
{
return SHAPE(this)->rs.rule;
return CONST_SHAPE(this)->rs.rule;
}

View file

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

View file

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

View file

@ -29,10 +29,12 @@
#include "tvgFill.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
FontLoader* loader = nullptr;
FontMetrics metrics;
@ -41,13 +43,13 @@ struct Text::Impl : Paint::Impl
bool italic = 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);
}
~Impl()
~TextImpl()
{
tvg::free(utf8);
LoaderMgr::retrieve(loader);
@ -95,7 +97,7 @@ struct Text::Impl : Paint::Impl
bool render(RenderMethod* renderer)
{
if (!loader) return true;
renderer->blend(blendMethod);
renderer->blend(impl.blendMethod);
return PAINT(shape)->render(renderer);
}
@ -118,7 +120,7 @@ struct Text::Impl : Paint::Impl
//transform the gradient coordinates based on the final scaled font.
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) {
LINEAR(fill)->x1 *= scale;
LINEAR(fill)->y1 *= scale;
@ -172,6 +174,4 @@ struct Text::Impl : Paint::Impl
}
};
#endif //_TVG_TEXT_H