common: optimization pImpl data structures

ThorVG pImpl idiom caused internal data to be scattered
across hierarchical classes. This refactoring consolidates
the data by inheriting pImpl internally, reducing memory
allocation counts and eliminating unnecessary strategy methods.
This commit is contained in:
Hermet Park 2024-12-13 17:31:58 +09:00
parent 01f4d6304a
commit 1806b32971
47 changed files with 895 additions and 1080 deletions

View file

@ -43,12 +43,16 @@
#define _TVG_DECLARE_PRIVATE(A) \ #define _TVG_DECLARE_PRIVATE(A) \
struct Impl; \ struct Impl; \
Impl* pImpl; \
protected: \ protected: \
A(const A&) = delete; \ A(const A&) = delete; \
const A& operator=(const A&) = delete; \ const A& operator=(const A&) = delete; \
A() A()
#define _TVG_DECLARE_PRIVATE_BASE(A) \
_TVG_DECLARE_PRIVATE(A); \
public: \
Impl* pImpl
#define _TVG_DISABLE_CTOR(A) \ #define _TVG_DISABLE_CTOR(A) \
A() = delete; \ A() = delete; \
~A() = delete ~A() = delete
@ -515,7 +519,7 @@ public:
*/ */
uint32_t id = 0; uint32_t id = 0;
_TVG_DECLARE_PRIVATE(Paint); _TVG_DECLARE_PRIVATE_BASE(Paint);
}; };
@ -616,7 +620,7 @@ public:
*/ */
virtual Type type() const noexcept = 0; virtual Type type() const noexcept = 0;
_TVG_DECLARE_PRIVATE(Fill); _TVG_DECLARE_PRIVATE_BASE(Fill);
}; };
@ -633,7 +637,6 @@ public:
class TVG_API Canvas class TVG_API Canvas
{ {
public: public:
Canvas(RenderMethod*);
virtual ~Canvas(); virtual ~Canvas();
/** /**
@ -749,7 +752,7 @@ public:
*/ */
Result sync() noexcept; Result sync() noexcept;
_TVG_DECLARE_PRIVATE(Canvas); _TVG_DECLARE_PRIVATE_BASE(Canvas);
}; };
@ -764,8 +767,6 @@ public:
class TVG_API LinearGradient final : public Fill class TVG_API LinearGradient final : public Fill
{ {
public: public:
~LinearGradient();
/** /**
* @brief Sets the linear gradient bounds. * @brief Sets the linear gradient bounds.
* *
@ -828,8 +829,6 @@ public:
class TVG_API RadialGradient final : public Fill class TVG_API RadialGradient final : public Fill
{ {
public: public:
~RadialGradient();
/** /**
* @brief Sets the radial gradient attributes. * @brief Sets the radial gradient attributes.
* *
@ -906,8 +905,6 @@ public:
class TVG_API Shape final : public Paint class TVG_API Shape final : public Paint
{ {
public: public:
~Shape();
/** /**
* @brief Resets the shape path. * @brief Resets the shape path.
* *
@ -1287,8 +1284,6 @@ public:
class TVG_API Picture final : public Paint class TVG_API Picture final : public Paint
{ {
public: public:
~Picture();
/** /**
* @brief Loads a picture data directly from a file. * @brief Loads a picture data directly from a file.
* *
@ -1420,8 +1415,6 @@ public:
class TVG_API Scene final : public Paint class TVG_API Scene final : public Paint
{ {
public: public:
~Scene();
/** /**
* @brief Inserts a paint object to the scene. * @brief Inserts a paint object to the scene.
* *
@ -1519,8 +1512,6 @@ public:
class TVG_API Text final : public Paint class TVG_API Text final : public Paint
{ {
public: public:
~Text();
/** /**
* @brief Sets the font properties for the text. * @brief Sets the font properties for the text.
* *
@ -1902,7 +1893,7 @@ public:
class TVG_API Animation class TVG_API Animation
{ {
public: public:
~Animation(); virtual ~Animation();
/** /**
* @brief Specifies the current frame in the animation. * @brief Specifies the current frame in the animation.
@ -2012,7 +2003,7 @@ public:
*/ */
static Animation* gen() noexcept; static Animation* gen() noexcept;
_TVG_DECLARE_PRIVATE(Animation); _TVG_DECLARE_PRIVATE_BASE(Animation);
}; };
@ -2115,7 +2106,7 @@ public:
*/ */
static Saver* gen() noexcept; static Saver* gen() noexcept;
_TVG_DECLARE_PRIVATE(Saver); _TVG_DECLARE_PRIVATE_BASE(Saver);
}; };
@ -2171,7 +2162,7 @@ public:
*/ */
static Accessor* gen() noexcept; static Accessor* gen() noexcept;
_TVG_DECLARE_PRIVATE(Accessor); _TVG_DECLARE_PRIVATE_BASE(Accessor);
}; };
/** @}*/ /** @}*/

View file

@ -36,7 +36,6 @@ void JpgLoader::clear()
freeData = false; freeData = false;
} }
/************************************************************************/ /************************************************************************/
/* External Class Implementation */ /* External Class Implementation */
/************************************************************************/ /************************************************************************/

View file

@ -33,7 +33,6 @@ void PngLoader::clear()
image = nullptr; image = nullptr;
} }
/************************************************************************/ /************************************************************************/
/* External Class Implementation */ /* External Class Implementation */
/************************************************************************/ /************************************************************************/

View file

@ -30,7 +30,6 @@
/* Internal Class Implementation */ /* Internal Class Implementation */
/************************************************************************/ /************************************************************************/
void WebpLoader::run(unsigned tid) void WebpLoader::run(unsigned tid)
{ {
//TODO: acquire the current colorspace format & pre-multiplied alpha image. //TODO: acquire the current colorspace format & pre-multiplied alpha image.

View file

@ -20,8 +20,6 @@ namespace tvg
class TVG_API LottieAnimation final : public Animation class TVG_API LottieAnimation final : public Animation
{ {
public: public:
~LottieAnimation();
/** /**
* @brief Override Lottie properties using slot data. * @brief Override Lottie properties using slot data.
* *
@ -87,6 +85,8 @@ public:
* @since 0.15 * @since 0.15
*/ */
static LottieAnimation* gen() noexcept; static LottieAnimation* gen() noexcept;
_TVG_DECLARE_PRIVATE(LottieAnimation);
}; };
} //namespace } //namespace

View file

@ -26,24 +26,16 @@
#include "tvgAnimation.h" #include "tvgAnimation.h"
/************************************************************************/ LottieAnimation::LottieAnimation()
/* Internal Class Implementation */
/************************************************************************/
/************************************************************************/
/* External Class Implementation */
/************************************************************************/
LottieAnimation::~LottieAnimation()
{ {
} }
Result LottieAnimation::override(const char* slot) noexcept Result LottieAnimation::override(const char* slot) noexcept
{ {
if (!pImpl->picture->pImpl->loader) return Result::InsufficientCondition; if (!PICTURE(pImpl->picture)->loader) return Result::InsufficientCondition;
if (static_cast<LottieLoader*>(pImpl->picture->pImpl->loader)->override(slot)) return Result::Success; if (static_cast<LottieLoader*>(PICTURE(pImpl->picture)->loader)->override(slot)) return Result::Success;
return Result::InvalidArguments; return Result::InvalidArguments;
} }
@ -51,7 +43,7 @@ Result LottieAnimation::override(const char* slot) noexcept
Result LottieAnimation::segment(const char* marker) noexcept Result LottieAnimation::segment(const char* marker) noexcept
{ {
auto loader = pImpl->picture->pImpl->loader; auto loader = PICTURE(pImpl->picture)->loader;
if (!loader) return Result::InsufficientCondition; if (!loader) return Result::InsufficientCondition;
if (!marker) { if (!marker) {
@ -68,7 +60,7 @@ Result LottieAnimation::segment(const char* marker) noexcept
uint32_t LottieAnimation::markersCnt() noexcept uint32_t LottieAnimation::markersCnt() noexcept
{ {
auto loader = pImpl->picture->pImpl->loader; auto loader = PICTURE(pImpl->picture)->loader;
if (!loader) return 0; if (!loader) return 0;
return static_cast<LottieLoader*>(loader)->markersCnt(); return static_cast<LottieLoader*>(loader)->markersCnt();
} }
@ -76,7 +68,7 @@ uint32_t LottieAnimation::markersCnt() noexcept
const char* LottieAnimation::marker(uint32_t idx) noexcept const char* LottieAnimation::marker(uint32_t idx) noexcept
{ {
auto loader = pImpl->picture->pImpl->loader; auto loader = PICTURE(pImpl->picture)->loader;
if (!loader) return nullptr; if (!loader) return nullptr;
return static_cast<LottieLoader*>(loader)->markers(idx); return static_cast<LottieLoader*>(loader)->markers(idx);
} }

View file

@ -189,10 +189,10 @@ void LottieBuilder::updateTransform(LottieGroup* parent, LottieObject** child, f
if (!_updateTransform(transform, frameNo, false, matrix, opacity, exps)) return; if (!_updateTransform(transform, frameNo, false, matrix, opacity, exps)) return;
ctx->propagator->transform(ctx->propagator->transform() * matrix); ctx->propagator->transform(ctx->propagator->transform() * matrix);
ctx->propagator->opacity(MULTIPLY(opacity, PP(ctx->propagator)->opacity)); ctx->propagator->opacity(MULTIPLY(opacity, PAINT(ctx->propagator)->opacity));
//FIXME: preserve the stroke width. too workaround, need a better design. //FIXME: preserve the stroke width. too workaround, need a better design.
if (P(ctx->propagator)->rs.strokeWidth() > 0.0f) { if (SHAPE(ctx->propagator)->rs.strokeWidth() > 0.0f) {
auto denominator = sqrtf(matrix.e11 * matrix.e11 + matrix.e12 * matrix.e12); auto denominator = sqrtf(matrix.e11 * matrix.e11 + matrix.e12 * matrix.e12);
if (denominator > 1.0f) ctx->propagator->strokeWidth(ctx->propagator->strokeWidth() / denominator); if (denominator > 1.0f) ctx->propagator->strokeWidth(ctx->propagator->strokeWidth() / denominator);
} }
@ -213,7 +213,7 @@ void LottieBuilder::updateGroup(LottieGroup* parent, LottieObject** child, float
if (group->mergeable()) _draw(parent, nullptr, ctx); if (group->mergeable()) _draw(parent, nullptr, ctx);
Inlist<RenderContext> contexts; Inlist<RenderContext> contexts;
auto propagator = group->mergeable() ? ctx->propagator : static_cast<Shape*>(PP(ctx->propagator)->duplicate(group->pooling())); auto propagator = group->mergeable() ? ctx->propagator : static_cast<Shape*>(PAINT(ctx->propagator)->duplicate(group->pooling()));
contexts.back(new RenderContext(*ctx, propagator, group->mergeable())); contexts.back(new RenderContext(*ctx, propagator, group->mergeable()));
updateChildren(group, frameNo, contexts); updateChildren(group, frameNo, contexts);
@ -243,7 +243,7 @@ static bool _fragmented(LottieGroup* parent, LottieObject** child, Inlist<Render
if (!ctx->reqFragment) return false; if (!ctx->reqFragment) return false;
if (ctx->fragmenting) return true; if (ctx->fragmenting) return true;
contexts.back(new RenderContext(*ctx, static_cast<Shape*>(PP(ctx->propagator)->duplicate(parent->pooling())))); contexts.back(new RenderContext(*ctx, static_cast<Shape*>(PAINT(ctx->propagator)->duplicate(parent->pooling()))));
auto fragment = contexts.tail; auto fragment = contexts.tail;
fragment->begin = child - 1; fragment->begin = child - 1;
ctx->fragmenting = true; ctx->fragmenting = true;
@ -313,7 +313,7 @@ static bool _draw(LottieGroup* parent, LottieShape* shape, RenderContext* ctx)
if (shape) { if (shape) {
ctx->merging = shape->pooling(); ctx->merging = shape->pooling();
PP(ctx->propagator)->duplicate(ctx->merging); PAINT(ctx->propagator)->duplicate(ctx->merging);
} else { } else {
ctx->merging = static_cast<Shape*>(ctx->propagator->duplicate()); ctx->merging = static_cast<Shape*>(ctx->propagator->duplicate());
} }
@ -338,7 +338,7 @@ static void _repeat(LottieGroup* parent, Shape* path, RenderContext* ctx)
for (auto propagator = propagators.begin(); propagator < propagators.end(); ++propagator) { for (auto propagator = propagators.begin(); propagator < propagators.end(); ++propagator) {
auto shape = static_cast<Shape*>((*propagator)->duplicate()); auto shape = static_cast<Shape*>((*propagator)->duplicate());
P(shape)->rs.path = P(path)->rs.path; SHAPE(shape)->rs.path = SHAPE(path)->rs.path;
auto opacity = repeater->interpOpacity ? lerp<uint8_t>(repeater->startOpacity, repeater->endOpacity, static_cast<float>(i + 1) / repeater->cnt) : repeater->startOpacity; auto opacity = repeater->interpOpacity ? lerp<uint8_t>(repeater->startOpacity, repeater->endOpacity, static_cast<float>(i + 1) / repeater->cnt) : repeater->startOpacity;
shape->opacity(opacity); shape->opacity(opacity);
@ -405,7 +405,7 @@ static void _appendRect(Shape* shape, float x, float y, float w, float h, float
} }
} }
if (offsetPath) offsetPath->modifyRect(commands, 5, points, 4, P(shape)->rs.path.cmds, P(shape)->rs.path.pts); if (offsetPath) offsetPath->modifyRect(commands, 5, points, 4, SHAPE(shape)->rs.path.cmds, SHAPE(shape)->rs.path.pts);
else shape->appendPath(commands, 5, points, 4); else shape->appendPath(commands, 5, points, 4);
//round rect //round rect
} else { } else {
@ -458,7 +458,7 @@ static void _appendRect(Shape* shape, float x, float y, float w, float h, float
} }
} }
if (offsetPath) offsetPath->modifyRect(commands, cmdCnt, points, ptsCnt, P(shape)->rs.path.cmds, P(shape)->rs.path.pts); if (offsetPath) offsetPath->modifyRect(commands, cmdCnt, points, ptsCnt, SHAPE(shape)->rs.path.cmds, SHAPE(shape)->rs.path.pts);
else shape->appendPath(commands, cmdCnt, points, ptsCnt); else shape->appendPath(commands, cmdCnt, points, ptsCnt);
} }
} }
@ -557,12 +557,12 @@ void LottieBuilder::updatePath(LottieGroup* parent, LottieObject** child, float
if (!ctx->repeaters.empty()) { if (!ctx->repeaters.empty()) {
auto shape = path->pooling(); auto shape = path->pooling();
shape->reset(); shape->reset();
path->pathset(frameNo, P(shape)->rs.path.cmds, P(shape)->rs.path.pts, ctx->transform, ctx->roundness, ctx->offsetPath, exps); path->pathset(frameNo, SHAPE(shape)->rs.path.cmds, SHAPE(shape)->rs.path.pts, ctx->transform, ctx->roundness, ctx->offsetPath, exps);
_repeat(parent, shape, ctx); _repeat(parent, shape, ctx);
} else { } else {
_draw(parent, path, ctx); _draw(parent, path, ctx);
if (path->pathset(frameNo, P(ctx->merging)->rs.path.cmds, P(ctx->merging)->rs.path.pts, ctx->transform, ctx->roundness, ctx->offsetPath, exps)) { if (path->pathset(frameNo, SHAPE(ctx->merging)->rs.path.cmds, SHAPE(ctx->merging)->rs.path.pts, ctx->transform, ctx->roundness, ctx->offsetPath, exps)) {
P(ctx->merging)->update(RenderUpdateFlag::Path); PAINT(ctx->merging)->update(RenderUpdateFlag::Path);
} }
} }
} }
@ -615,11 +615,11 @@ static void _updateStar(TVG_UNUSED LottieGroup* parent, LottiePolyStar* star, Ma
} }
if (tvg::zero(innerRoundness) && tvg::zero(outerRoundness)) { if (tvg::zero(innerRoundness) && tvg::zero(outerRoundness)) {
P(shape)->rs.path.pts.reserve(numPoints + 2); SHAPE(shape)->rs.path.pts.reserve(numPoints + 2);
P(shape)->rs.path.cmds.reserve(numPoints + 3); SHAPE(shape)->rs.path.cmds.reserve(numPoints + 3);
} else { } else {
P(shape)->rs.path.pts.reserve(numPoints * 3 + 2); SHAPE(shape)->rs.path.pts.reserve(numPoints * 3 + 2);
P(shape)->rs.path.cmds.reserve(numPoints + 3); SHAPE(shape)->rs.path.cmds.reserve(numPoints + 3);
hasRoundness = true; hasRoundness = true;
} }
@ -687,13 +687,13 @@ static void _updateStar(TVG_UNUSED LottieGroup* parent, LottiePolyStar* star, Ma
if (roundedCorner) { if (roundedCorner) {
if (offsetPath) { if (offsetPath) {
auto intermediate = Shape::gen(); auto intermediate = Shape::gen();
roundness->modifyPolystar(P(shape)->rs.path.cmds, P(shape)->rs.path.pts, P(intermediate)->rs.path.cmds, P(intermediate)->rs.path.pts, outerRoundness, hasRoundness); roundness->modifyPolystar(SHAPE(shape)->rs.path.cmds, SHAPE(shape)->rs.path.pts, SHAPE(intermediate)->rs.path.cmds, SHAPE(intermediate)->rs.path.pts, outerRoundness, hasRoundness);
offsetPath->modifyPolystar(P(intermediate)->rs.path.cmds, P(intermediate)->rs.path.pts, P(merging)->rs.path.cmds, P(merging)->rs.path.pts); offsetPath->modifyPolystar(SHAPE(intermediate)->rs.path.cmds, SHAPE(intermediate)->rs.path.pts, SHAPE(merging)->rs.path.cmds, SHAPE(merging)->rs.path.pts);
delete(intermediate); delete(intermediate);
} else { } else {
roundness->modifyPolystar(P(shape)->rs.path.cmds, P(shape)->rs.path.pts, P(merging)->rs.path.cmds, P(merging)->rs.path.pts, outerRoundness, hasRoundness); roundness->modifyPolystar(SHAPE(shape)->rs.path.cmds, SHAPE(shape)->rs.path.pts, SHAPE(merging)->rs.path.cmds, SHAPE(merging)->rs.path.pts, outerRoundness, hasRoundness);
} }
} else if (offsetPath) offsetPath->modifyPolystar(P(shape)->rs.path.cmds, P(shape)->rs.path.pts, P(merging)->rs.path.cmds, P(merging)->rs.path.pts); } else if (offsetPath) offsetPath->modifyPolystar(SHAPE(shape)->rs.path.cmds, SHAPE(shape)->rs.path.pts, SHAPE(merging)->rs.path.cmds, SHAPE(merging)->rs.path.pts);
} }
@ -722,11 +722,11 @@ static void _updatePolygon(LottieGroup* parent, LottiePolyStar* star, Matrix* tr
} else { } else {
shape = merging; shape = merging;
if (hasRoundness) { if (hasRoundness) {
P(shape)->rs.path.pts.reserve(ptsCnt * 3 + 2); SHAPE(shape)->rs.path.pts.reserve(ptsCnt * 3 + 2);
P(shape)->rs.path.cmds.reserve(ptsCnt + 3); SHAPE(shape)->rs.path.cmds.reserve(ptsCnt + 3);
} else { } else {
P(shape)->rs.path.pts.reserve(ptsCnt + 2); SHAPE(shape)->rs.path.pts.reserve(ptsCnt + 2);
P(shape)->rs.path.cmds.reserve(ptsCnt + 3); SHAPE(shape)->rs.path.cmds.reserve(ptsCnt + 3);
} }
} }
@ -774,13 +774,13 @@ static void _updatePolygon(LottieGroup* parent, LottiePolyStar* star, Matrix* tr
if (roundedCorner) { if (roundedCorner) {
if (offsetPath) { if (offsetPath) {
auto intermediate = Shape::gen(); auto intermediate = Shape::gen();
roundness->modifyPolystar(P(shape)->rs.path.cmds, P(shape)->rs.path.pts, P(intermediate)->rs.path.cmds, P(intermediate)->rs.path.pts, 0.0f, false); roundness->modifyPolystar(SHAPE(shape)->rs.path.cmds, SHAPE(shape)->rs.path.pts, SHAPE(intermediate)->rs.path.cmds, SHAPE(intermediate)->rs.path.pts, 0.0f, false);
offsetPath->modifyPolystar(P(intermediate)->rs.path.cmds, P(intermediate)->rs.path.pts, P(merging)->rs.path.cmds, P(merging)->rs.path.pts); offsetPath->modifyPolystar(SHAPE(intermediate)->rs.path.cmds, SHAPE(intermediate)->rs.path.pts, SHAPE(merging)->rs.path.cmds, SHAPE(merging)->rs.path.pts);
delete(intermediate); delete(intermediate);
} else { } else {
roundness->modifyPolystar(P(shape)->rs.path.cmds, P(shape)->rs.path.pts, P(merging)->rs.path.cmds, P(merging)->rs.path.pts, 0.0f, false); roundness->modifyPolystar(SHAPE(shape)->rs.path.cmds, SHAPE(shape)->rs.path.pts, SHAPE(merging)->rs.path.cmds, SHAPE(merging)->rs.path.pts, 0.0f, false);
} }
} else if (offsetPath) offsetPath->modifyPolystar(P(shape)->rs.path.cmds, P(shape)->rs.path.pts, P(merging)->rs.path.cmds, P(merging)->rs.path.pts); } else if (offsetPath) offsetPath->modifyPolystar(SHAPE(shape)->rs.path.cmds, SHAPE(shape)->rs.path.pts, SHAPE(merging)->rs.path.cmds, SHAPE(merging)->rs.path.pts);
} }
@ -809,7 +809,7 @@ void LottieBuilder::updatePolystar(LottieGroup* parent, LottieObject** child, fl
_draw(parent, star, ctx); _draw(parent, star, ctx);
if (star->type == LottiePolyStar::Star) _updateStar(parent, star, identity ? nullptr : &matrix, ctx->roundness, ctx->offsetPath, frameNo, ctx->merging, exps); if (star->type == LottiePolyStar::Star) _updateStar(parent, star, identity ? nullptr : &matrix, ctx->roundness, ctx->offsetPath, frameNo, ctx->merging, exps);
else _updatePolygon(parent, star, identity ? nullptr : &matrix, ctx->roundness, ctx->offsetPath, frameNo, ctx->merging, exps); else _updatePolygon(parent, star, identity ? nullptr : &matrix, ctx->roundness, ctx->offsetPath, frameNo, ctx->merging, exps);
P(ctx->merging)->update(RenderUpdateFlag::Path); PAINT(ctx->merging)->update(RenderUpdateFlag::Path);
} }
} }
@ -861,15 +861,15 @@ void LottieBuilder::updateTrimpath(TVG_UNUSED LottieGroup* parent, LottieObject*
float begin, end; float begin, end;
trimpath->segment(frameNo, begin, end, exps); trimpath->segment(frameNo, begin, end, exps);
if (P(ctx->propagator)->rs.stroke) { if (SHAPE(ctx->propagator)->rs.stroke) {
auto pbegin = P(ctx->propagator)->rs.stroke->trim.begin; auto pbegin = SHAPE(ctx->propagator)->rs.stroke->trim.begin;
auto pend = P(ctx->propagator)->rs.stroke->trim.end; auto pend = SHAPE(ctx->propagator)->rs.stroke->trim.end;
auto length = fabsf(pend - pbegin); auto length = fabsf(pend - pbegin);
begin = (length * begin) + pbegin; begin = (length * begin) + pbegin;
end = (length * end) + pbegin; end = (length * end) + pbegin;
} }
P(ctx->propagator)->strokeTrim(begin, end, trimpath->type == LottieTrimpath::Type::Simultaneous); ctx->propagator->strokeTrim(begin, end, trimpath->type == LottieTrimpath::Type::Simultaneous);
ctx->merging = nullptr; ctx->merging = nullptr;
} }
@ -1083,8 +1083,8 @@ void LottieBuilder::updateText(LottieLayer* layer, float frameNo)
for (auto g = glyph->children.begin(); g < glyph->children.end(); ++g) { for (auto g = glyph->children.begin(); g < glyph->children.end(); ++g) {
auto group = static_cast<LottieGroup*>(*g); auto group = static_cast<LottieGroup*>(*g);
for (auto p = group->children.begin(); p < group->children.end(); ++p) { for (auto p = group->children.begin(); p < group->children.end(); ++p) {
if (static_cast<LottiePath*>(*p)->pathset(frameNo, P(shape)->rs.path.cmds, P(shape)->rs.path.pts, nullptr, nullptr, nullptr)) { if (static_cast<LottiePath*>(*p)->pathset(frameNo, SHAPE(shape)->rs.path.cmds, SHAPE(shape)->rs.path.pts, nullptr, nullptr, nullptr)) {
P(shape)->update(RenderUpdateFlag::Path); PAINT(shape)->update(RenderUpdateFlag::Path);
} }
} }
} }
@ -1237,11 +1237,11 @@ void LottieBuilder::updateMaskings(LottieLayer* layer, float frameNo)
//Apply Masking Expansion (Offset) //Apply Masking Expansion (Offset)
if (expand == 0.0f) { if (expand == 0.0f) {
pMask->pathset(frameNo, P(pShape)->rs.path.cmds, P(pShape)->rs.path.pts, nullptr, nullptr, nullptr, exps); pMask->pathset(frameNo, SHAPE(pShape)->rs.path.cmds, SHAPE(pShape)->rs.path.pts, nullptr, nullptr, nullptr, exps);
} else { } else {
//TODO: Once path direction support is implemented, ensure that the direction is ignored here //TODO: Once path direction support is implemented, ensure that the direction is ignored here
auto offset = LottieOffsetModifier(pMask->expand(frameNo)); auto offset = LottieOffsetModifier(pMask->expand(frameNo));
pMask->pathset(frameNo, P(pShape)->rs.path.cmds, P(pShape)->rs.path.pts, nullptr, nullptr, &offset, exps); pMask->pathset(frameNo, SHAPE(pShape)->rs.path.cmds, SHAPE(pShape)->rs.path.pts, nullptr, nullptr, &offset, exps);
} }
auto compMethod = (pMethod == MaskMethod::Subtract || pMethod == MaskMethod::InvAlpha) ? MaskMethod::InvAlpha : MaskMethod::Alpha; auto compMethod = (pMethod == MaskMethod::Subtract || pMethod == MaskMethod::InvAlpha) ? MaskMethod::InvAlpha : MaskMethod::Alpha;
@ -1269,14 +1269,14 @@ void LottieBuilder::updateMaskings(LottieLayer* layer, float frameNo)
//Append the mask shape //Append the mask shape
if (pMethod == method && (method == MaskMethod::Subtract || method == MaskMethod::Difference)) { if (pMethod == method && (method == MaskMethod::Subtract || method == MaskMethod::Difference)) {
mask->pathset(frameNo, P(pShape)->rs.path.cmds, P(pShape)->rs.path.pts, nullptr, nullptr, nullptr, exps); mask->pathset(frameNo, SHAPE(pShape)->rs.path.cmds, SHAPE(pShape)->rs.path.pts, nullptr, nullptr, nullptr, exps);
//Chain composition //Chain composition
} else { } else {
auto shape = layer->pooling(); auto shape = layer->pooling();
shape->reset(); shape->reset();
shape->fill(255, 255, 255, mask->opacity(frameNo)); shape->fill(255, 255, 255, mask->opacity(frameNo));
shape->transform(layer->cache.matrix); shape->transform(layer->cache.matrix);
mask->pathset(frameNo, P(shape)->rs.path.cmds, P(shape)->rs.path.pts, nullptr, nullptr, nullptr, exps); mask->pathset(frameNo, SHAPE(shape)->rs.path.cmds, SHAPE(shape)->rs.path.pts, nullptr, nullptr, nullptr, exps);
pShape->mask(shape, method); pShape->mask(shape, method);
pShape = shape; pShape = shape;
pMethod = method; pMethod = method;

View file

@ -62,7 +62,7 @@ struct RenderContext
RenderContext(Shape* propagator) RenderContext(Shape* propagator)
{ {
P(propagator)->reset(); SHAPE(propagator)->reset();
propagator->ref(); propagator->ref();
this->propagator = propagator; this->propagator = propagator;
} }

View file

@ -29,7 +29,6 @@
/* Internal Class Implementation */ /* Internal Class Implementation */
/************************************************************************/ /************************************************************************/
void PngLoader::run(unsigned tid) void PngLoader::run(unsigned tid)
{ {
auto width = static_cast<unsigned>(w); auto width = static_cast<unsigned>(w);

View file

@ -25,14 +25,6 @@
#include "tvgLoader.h" #include "tvgLoader.h"
#include "tvgRawLoader.h" #include "tvgRawLoader.h"
/************************************************************************/
/* Internal Class Implementation */
/************************************************************************/
/************************************************************************/
/* External Class Implementation */
/************************************************************************/
RawLoader::RawLoader() : ImageLoader(FileType::Raw) RawLoader::RawLoader() : ImageLoader(FileType::Raw)
{ {

View file

@ -527,8 +527,8 @@ bool svgPathToShape(const char* svgPath, Shape* shape)
bool closed = false; bool closed = false;
char* path = (char*)svgPath; char* path = (char*)svgPath;
auto& pts = P(shape)->rs.path.pts; auto& pts = SHAPE(shape)->rs.path.pts;
auto& cmds = P(shape)->rs.path.cmds; auto& cmds = SHAPE(shape)->rs.path.cmds;
auto lastCmds = cmds.count; auto lastCmds = cmds.count;
while ((path[0] != '\0')) { while ((path[0] != '\0')) {

View file

@ -153,7 +153,7 @@ static RadialGradient* _applyRadialGradientProperty(SvgStyleGradient* g, const B
else finalTransform = m; else finalTransform = m;
} }
P(fillGrad)->radial(g->radial->cx, g->radial->cy, g->radial->r, g->radial->fx, g->radial->fy, g->radial->fr); fillGrad->radial(g->radial->cx, g->radial->cy, g->radial->r, g->radial->fx, g->radial->fy, g->radial->fr);
fillGrad->spread(g->spread); fillGrad->spread(g->spread);
//Update the stops //Update the stops
@ -216,7 +216,7 @@ static Matrix _compositionTransform(Paint* paint, const SvgNode* node, const Svg
} }
if (!compNode->node.clip.userSpace) { if (!compNode->node.clip.userSpace) {
float x, y, w, h; float x, y, w, h;
P(paint)->bounds(&x, &y, &w, &h, false, false); PAINT(paint)->bounds(&x, &y, &w, &h, false, false);
m *= {w, 0, x, 0, h, y, 0, 0, 1}; m *= {w, 0, x, 0, h, y, 0, 0, 1};
} }
return m; return m;

View file

@ -479,8 +479,8 @@ bool TtfReader::convert(Shape* shape, TtfGlyphMetrics& gmetrics, const Point& of
if (!this->points(outline, flags, pts, ptsCnt, offset + kerning)) return false; if (!this->points(outline, flags, pts, ptsCnt, offset + kerning)) return false;
//generate tvg paths. //generate tvg paths.
auto& pathCmds = P(shape)->rs.path.cmds; auto& pathCmds = SHAPE(shape)->rs.path.cmds;
auto& pathPts = P(shape)->rs.path.pts; auto& pathPts = SHAPE(shape)->rs.path.pts;
pathCmds.reserve(ptsCnt); pathCmds.reserve(ptsCnt);
pathPts.reserve(ptsCnt); pathPts.reserve(ptsCnt);

View file

@ -35,13 +35,9 @@ bool GlGeometry::tesselate(const RenderShape& rshape, RenderUpdateFlag flag)
fillVertex.clear(); fillVertex.clear();
fillIndex.clear(); fillIndex.clear();
BWTessellator bwTess{&fillVertex, &fillIndex}; BWTessellator bwTess{&fillVertex, &fillIndex};
bwTess.tessellate(&rshape, mMatrix); bwTess.tessellate(&rshape, mMatrix);
mFillRule = rshape.rule; mFillRule = rshape.rule;
mBounds = bwTess.bounds(); mBounds = bwTess.bounds();
} }
@ -51,7 +47,6 @@ bool GlGeometry::tesselate(const RenderShape& rshape, RenderUpdateFlag flag)
Stroker stroke{&strokeVertex, &strokeIndex, mMatrix}; Stroker stroke{&strokeVertex, &strokeIndex, mMatrix};
stroke.stroke(&rshape); stroke.stroke(&rshape);
mBounds = stroke.bounds(); mBounds = stroke.bounds();
} }
@ -123,10 +118,7 @@ void GlGeometry::disableVertex(uint32_t location)
bool GlGeometry::draw(GlRenderTask* task, GlStageBuffer* gpuBuffer, RenderUpdateFlag flag) bool GlGeometry::draw(GlRenderTask* task, GlStageBuffer* gpuBuffer, RenderUpdateFlag flag)
{ {
if (flag == RenderUpdateFlag::None) return false;
if (flag == RenderUpdateFlag::None) {
return false;
}
Array<float>* vertexBuffer = nullptr; Array<float>* vertexBuffer = nullptr;
Array<uint32_t>* indexBuffer = nullptr; Array<uint32_t>* indexBuffer = nullptr;
@ -220,10 +212,7 @@ RenderRegion GlGeometry::getBounds() const
static_cast<int32_t>(ceil(right - floor(left))), static_cast<int32_t>(ceil(right - floor(left))),
static_cast<int32_t>(ceil(bottom - floor(top))), static_cast<int32_t>(ceil(bottom - floor(top))),
}; };
if (bounds.w < 0 || bounds.h < 0) { if (bounds.w < 0 || bounds.h < 0) return mBounds;
return mBounds; else return bounds;
} else {
return bounds;
}
} }
} }

View file

@ -26,7 +26,7 @@
#include <string.h> #include <string.h>
/************************************************************************/ /************************************************************************/
/* Internal Class Implementation */ /* GlGpuBuffer Implementation */
/************************************************************************/ /************************************************************************/
static GLint _getGpuBufferAlign() static GLint _getGpuBufferAlign()
@ -41,6 +41,25 @@ static GLint _getGpuBufferAlign()
return offset; return offset;
} }
void GlGpuBuffer::updateBufferData(Target target, uint32_t size, const void* data)
{
GL_CHECK(glBufferData(static_cast<uint32_t>(target), size, data, GL_STATIC_DRAW));
}
void GlGpuBuffer::bind(Target target)
{
GL_CHECK(glBindBuffer(static_cast<uint32_t>(target), mGlBufferId));
}
void GlGpuBuffer::unbind(Target target)
{
GL_CHECK(glBindBuffer(static_cast<uint32_t>(target), 0));
}
GlGpuBuffer::GlGpuBuffer() GlGpuBuffer::GlGpuBuffer()
{ {
GL_CHECK(glGenBuffers(1, &mGlBufferId)); GL_CHECK(glGenBuffers(1, &mGlBufferId));
@ -56,27 +75,16 @@ GlGpuBuffer::~GlGpuBuffer()
} }
} }
/************************************************************************/
void GlGpuBuffer::updateBufferData(Target target, uint32_t size, const void* data) /* GlStageBuffer Implementation */
{ /************************************************************************/
GL_CHECK(glBufferData(static_cast<uint32_t>(target), size, data, GL_STATIC_DRAW));
}
void GlGpuBuffer::bind(Target target)
{
GL_CHECK(glBindBuffer(static_cast<uint32_t>(target), mGlBufferId));
}
void GlGpuBuffer::unbind(Target target)
{
GL_CHECK(glBindBuffer(static_cast<uint32_t>(target), 0));
}
GlStageBuffer::GlStageBuffer() : mVao(0), mGpuBuffer(), mGpuIndexBuffer() GlStageBuffer::GlStageBuffer() : mVao(0), mGpuBuffer(), mGpuIndexBuffer()
{ {
GL_CHECK(glGenVertexArrays(1, &mVao)); GL_CHECK(glGenVertexArrays(1, &mVao));
} }
GlStageBuffer::~GlStageBuffer() GlStageBuffer::~GlStageBuffer()
{ {
if (mVao) { if (mVao) {
@ -85,6 +93,7 @@ GlStageBuffer::~GlStageBuffer()
} }
} }
uint32_t GlStageBuffer::push(void *data, uint32_t size, bool alignGpuOffset) uint32_t GlStageBuffer::push(void *data, uint32_t size, bool alignGpuOffset)
{ {
if (alignGpuOffset) alignOffset(size); if (alignGpuOffset) alignOffset(size);
@ -102,6 +111,7 @@ uint32_t GlStageBuffer::push(void *data, uint32_t size, bool alignGpuOffset)
return offset; return offset;
} }
uint32_t GlStageBuffer::pushIndex(void *data, uint32_t size) uint32_t GlStageBuffer::pushIndex(void *data, uint32_t size)
{ {
uint32_t offset = mIndexBuffer.count; uint32_t offset = mIndexBuffer.count;
@ -117,6 +127,7 @@ uint32_t GlStageBuffer::pushIndex(void *data, uint32_t size)
return offset; return offset;
} }
bool GlStageBuffer::flushToGPU() bool GlStageBuffer::flushToGPU()
{ {
if (mStageBuffer.empty() || mIndexBuffer.empty()) { if (mStageBuffer.empty() || mIndexBuffer.empty()) {
@ -140,6 +151,7 @@ bool GlStageBuffer::flushToGPU()
return true; return true;
} }
void GlStageBuffer::bind() void GlStageBuffer::bind()
{ {
glBindVertexArray(mVao); glBindVertexArray(mVao);
@ -148,6 +160,7 @@ void GlStageBuffer::bind()
mGpuIndexBuffer.bind(GlGpuBuffer::Target::ELEMENT_ARRAY_BUFFER); mGpuIndexBuffer.bind(GlGpuBuffer::Target::ELEMENT_ARRAY_BUFFER);
} }
void GlStageBuffer::unbind() void GlStageBuffer::unbind()
{ {
glBindVertexArray(0); glBindVertexArray(0);
@ -156,11 +169,13 @@ void GlStageBuffer::unbind()
mGpuIndexBuffer.unbind(GlGpuBuffer::Target::ELEMENT_ARRAY_BUFFER); mGpuIndexBuffer.unbind(GlGpuBuffer::Target::ELEMENT_ARRAY_BUFFER);
} }
GLuint GlStageBuffer::getBufferId() GLuint GlStageBuffer::getBufferId()
{ {
return mGpuBuffer.getBufferId(); return mGpuBuffer.getBufferId();
} }
void GlStageBuffer::alignOffset(uint32_t size) void GlStageBuffer::alignOffset(uint32_t size)
{ {

View file

@ -40,8 +40,8 @@ public:
void updateBufferData(Target target, uint32_t size, const void* data); void updateBufferData(Target target, uint32_t size, const void* data);
void bind(Target target); void bind(Target target);
void unbind(Target target); void unbind(Target target);
uint32_t getBufferId() { return mGlBufferId; } uint32_t getBufferId() { return mGlBufferId; }
private: private:
uint32_t mGlBufferId = 0; uint32_t mGlBufferId = 0;
@ -53,19 +53,15 @@ public:
~GlStageBuffer(); ~GlStageBuffer();
uint32_t push(void* data, uint32_t size, bool alignGpuOffset = false); uint32_t push(void* data, uint32_t size, bool alignGpuOffset = false);
uint32_t pushIndex(void* data, uint32_t size); uint32_t pushIndex(void* data, uint32_t size);
bool flushToGPU(); bool flushToGPU();
void bind(); void bind();
void unbind(); void unbind();
GLuint getBufferId(); GLuint getBufferId();
private: private:
void alignOffset(uint32_t size); void alignOffset(uint32_t size);
private:
GLuint mVao = 0; GLuint mVao = 0;
GlGpuBuffer mGpuBuffer = {}; GlGpuBuffer mGpuBuffer = {};
GlGpuBuffer mGpuIndexBuffer = {}; GlGpuBuffer mGpuIndexBuffer = {};

View file

@ -29,6 +29,43 @@
uint32_t GlProgram::mCurrentProgram = 0; uint32_t GlProgram::mCurrentProgram = 0;
void GlProgram::linkProgram(std::shared_ptr<GlShader> shader)
{
GLint linked;
// Create the program object
uint32_t progObj = glCreateProgram();
assert(progObj);
glAttachShader(progObj, shader->getVertexShader());
glAttachShader(progObj, shader->getFragmentShader());
// Link the program
glLinkProgram(progObj);
// Check the link status
glGetProgramiv(progObj, GL_LINK_STATUS, &linked);
if (!linked)
{
GLint infoLen = 0;
glGetProgramiv(progObj, GL_INFO_LOG_LENGTH, &infoLen);
if (infoLen > 0)
{
auto infoLog = static_cast<char*>(malloc(sizeof(char) * infoLen));
glGetProgramInfoLog(progObj, infoLen, NULL, infoLog);
TVGERR("GL_ENGINE", "Error linking shader: %s", infoLog);
free(infoLog);
}
glDeleteProgram(progObj);
progObj = 0;
assert(0);
}
mProgramObj = progObj;
}
/************************************************************************/ /************************************************************************/
/* External Class Implementation */ /* External Class Implementation */
/************************************************************************/ /************************************************************************/
@ -149,40 +186,3 @@ void GlProgram::setUniform4x4Value(int32_t location, int count, const float* val
{ {
GL_CHECK(glUniformMatrix4fv(location, count, GL_FALSE, &values[0])); GL_CHECK(glUniformMatrix4fv(location, count, GL_FALSE, &values[0]));
} }
void GlProgram::linkProgram(std::shared_ptr<GlShader> shader)
{
GLint linked;
// Create the program object
uint32_t progObj = glCreateProgram();
assert(progObj);
glAttachShader(progObj, shader->getVertexShader());
glAttachShader(progObj, shader->getFragmentShader());
// Link the program
glLinkProgram(progObj);
// Check the link status
glGetProgramiv(progObj, GL_LINK_STATUS, &linked);
if (!linked)
{
GLint infoLen = 0;
glGetProgramiv(progObj, GL_INFO_LOG_LENGTH, &infoLen);
if (infoLen > 0)
{
auto infoLog = static_cast<char*>(malloc(sizeof(char) * infoLen));
glGetProgramInfoLog(progObj, infoLen, NULL, infoLog);
TVGERR("GL_ENGINE", "Error linking shader: %s", infoLog);
free(infoLog);
}
glDeleteProgram(progObj);
progObj = 0;
assert(0);
}
mProgramObj = progObj;
}

View file

@ -24,9 +24,6 @@
#include "tvgGlProgram.h" #include "tvgGlProgram.h"
#include "tvgGlRenderPass.h" #include "tvgGlRenderPass.h"
/************************************************************************/
/* External Class Implementation */
/************************************************************************/
GlRenderTask::GlRenderTask(GlProgram* program, GlRenderTask* other): mProgram(program) GlRenderTask::GlRenderTask(GlProgram* program, GlRenderTask* other): mProgram(program)
{ {

View file

@ -803,12 +803,10 @@ void GlRenderer::endRenderPass(RenderCompositor* cmp)
} }
/************************************************************************/ /************************************************************************/
/* External Class Implementation */ /* External Class Implementation */
/************************************************************************/ /************************************************************************/
bool GlRenderer::clear() bool GlRenderer::clear()
{ {
clearDisposes(); clearDisposes();

View file

@ -23,35 +23,9 @@
#include "tvgGlShader.h" #include "tvgGlShader.h"
/************************************************************************/ /************************************************************************/
/* External Class Implementation */ /* Internal Class Implementation */
/************************************************************************/ /************************************************************************/
shared_ptr<GlShader> GlShader::gen(const char* vertSrc, const char* fragSrc)
{
shared_ptr<GlShader> shader = make_shared<GlShader>();
shader->createShader(vertSrc, fragSrc);
return shader;
}
GlShader::~GlShader()
{
glDeleteShader(mVtShader);
glDeleteShader(mFrShader);
}
uint32_t GlShader::getVertexShader()
{
return mVtShader;
}
uint32_t GlShader::getFragmentShader()
{
return mFrShader;
}
void GlShader::createShader(const char* vertSrc, const char* fragSrc) void GlShader::createShader(const char* vertSrc, const char* fragSrc)
{ {
mVtShader = compileShader(GL_VERTEX_SHADER, const_cast<char*>(vertSrc)); mVtShader = compileShader(GL_VERTEX_SHADER, const_cast<char*>(vertSrc));
@ -110,3 +84,31 @@ uint32_t GlShader::compileShader(uint32_t type, char* shaderSrc)
return shader; return shader;
} }
/************************************************************************/
/* External Class Implementation */
/************************************************************************/
shared_ptr<GlShader> GlShader::gen(const char* vertSrc, const char* fragSrc)
{
shared_ptr<GlShader> shader = make_shared<GlShader>();
shader->createShader(vertSrc, fragSrc);
return shader;
}
GlShader::~GlShader()
{
glDeleteShader(mVtShader);
glDeleteShader(mFrShader);
}
uint32_t GlShader::getVertexShader()
{
return mVtShader;
}
uint32_t GlShader::getFragmentShader()
{
return mFrShader;
}

View file

@ -61,7 +61,7 @@ private:
Polygon *makePoly(Vertex* v, int32_t winding); Polygon *makePoly(Vertex* v, int32_t winding);
void emitPoly(MonotonePolygon* poly); void emitPoly(MonotonePolygon* poly);
void emitTriangle(Vertex* p1, Vertex* p2, Vertex* p3); void emitTriangle(Vertex* p1, Vertex* p2, Vertex* p3);
private:
FillRule fillRule = FillRule::Winding; FillRule fillRule = FillRule::Winding;
std::unique_ptr<ObjectHeap> pHeap; std::unique_ptr<ObjectHeap> pHeap;
Array<VertexList*> outlines; Array<VertexList*> outlines;
@ -134,7 +134,6 @@ private:
void lineTo(const Point& pt); void lineTo(const Point& pt);
void cubicTo(const Point& pt1, const Point& pt2, const Point& pt3); void cubicTo(const Point& pt1, const Point& pt2, const Point& pt3);
private:
Array<PathCommand>* mCmds; Array<PathCommand>* mCmds;
Array<Point>* mPts; Array<Point>* mPts;
uint32_t mDashCount; uint32_t mDashCount;

View file

@ -66,15 +66,17 @@ static void _calculateCoefficients(const SwFill* fill, uint32_t x, uint32_t y, f
static uint32_t _estimateAAMargin(const Fill* fdata) 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 = P(static_cast<const RadialGradient*>(fdata))->r; auto radius = 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 {
auto grad = P(static_cast<const LinearGradient*>(fdata)); auto grad = 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);
return tvg::zero(len) ? 0 : static_cast<uint32_t>(marginScalingFactor / len); return tvg::zero(len) ? 0 : static_cast<uint32_t>(marginScalingFactor / len);
}
} }
@ -129,7 +131,6 @@ static bool _updateColorTable(SwFill* fill, const Fill* fdata, const SwSurface*
if (!fill->ctable) { if (!fill->ctable) {
fill->ctable = static_cast<uint32_t*>(malloc(GRADIENT_STOP_SIZE * sizeof(uint32_t))); fill->ctable = static_cast<uint32_t*>(malloc(GRADIENT_STOP_SIZE * sizeof(uint32_t)));
if (!fill->ctable) return false;
} }
const Fill::ColorStop* colors; const Fill::ColorStop* colors;
@ -145,7 +146,6 @@ static bool _updateColorTable(SwFill* fill, const Fill* fdata, const SwSurface*
auto g = pColors->g; auto g = pColors->g;
auto b = pColors->b; auto b = pColors->b;
auto rgba = surface->join(r, g, b, a); auto rgba = surface->join(r, g, b, a);
auto inc = 1.0f / static_cast<float>(GRADIENT_STOP_SIZE); auto inc = 1.0f / static_cast<float>(GRADIENT_STOP_SIZE);
auto pos = 1.5f * inc; auto pos = 1.5f * inc;
uint32_t i = 0; uint32_t i = 0;
@ -173,6 +173,7 @@ static bool _updateColorTable(SwFill* fill, const Fill* fdata, const SwSurface*
auto next = curr + 1; auto next = curr + 1;
auto delta = 1.0f / (next->offset - curr->offset); auto delta = 1.0f / (next->offset - curr->offset);
auto a2 = MULTIPLY(next->a, opacity); auto a2 = MULTIPLY(next->a, opacity);
if (!fill->translucent && a2 < 255) fill->translucent = true; if (!fill->translucent && a2 < 255) fill->translucent = true;
auto rgba2 = surface->join(next->r, next->g, next->b, a2); auto rgba2 = surface->join(next->r, next->g, next->b, a2);
@ -181,10 +182,8 @@ static bool _updateColorTable(SwFill* fill, const Fill* fdata, const SwSurface*
auto t = (pos - curr->offset) * delta; auto t = (pos - curr->offset) * delta;
auto dist = static_cast<int32_t>(255 * t); auto dist = static_cast<int32_t>(255 * t);
auto dist2 = 255 - dist; auto dist2 = 255 - dist;
auto color = INTERPOLATE(rgba, rgba2, dist2); auto color = INTERPOLATE(rgba, rgba2, dist2);
fill->ctable[i] = ALPHA_BLEND((color | 0xff000000), (color >> 24)); fill->ctable[i] = ALPHA_BLEND((color | 0xff000000), (color >> 24));
++i; ++i;
pos += inc; pos += inc;
} }
@ -195,8 +194,9 @@ static bool _updateColorTable(SwFill* fill, const Fill* fdata, const SwSurface*
} }
rgba = ALPHA_BLEND((rgba | 0xff000000), a); rgba = ALPHA_BLEND((rgba | 0xff000000), a);
for (; i < GRADIENT_STOP_SIZE; ++i) for (; i < GRADIENT_STOP_SIZE; ++i) {
fill->ctable[i] = rgba; fill->ctable[i] = rgba;
}
//For repeat fill spread apply anti-aliasing between the last and first colors, //For repeat fill spread apply anti-aliasing between the last and first colors,
//othewise make sure the last color stop is represented at the end of the table. //othewise make sure the last color stop is represented at the end of the table.
@ -210,7 +210,7 @@ static bool _updateColorTable(SwFill* fill, const Fill* fdata, const SwSurface*
bool _prepareLinear(SwFill* fill, const LinearGradient* linear, const Matrix& pTransform) bool _prepareLinear(SwFill* fill, const LinearGradient* linear, const Matrix& pTransform)
{ {
float x1, x2, y1, y2; float x1, x2, y1, y2;
if (linear->linear(&x1, &y1, &x2, &y2) != Result::Success) return false; linear->linear(&x1, &y1, &x2, &y2);
fill->linear.dx = x2 - x1; fill->linear.dx = x2 - x1;
fill->linear.dy = y2 - y1; fill->linear.dy = y2 - y1;
@ -244,12 +244,8 @@ bool _prepareLinear(SwFill* fill, const LinearGradient* linear, const Matrix& pT
bool _prepareRadial(SwFill* fill, const RadialGradient* radial, const Matrix& pTransform) bool _prepareRadial(SwFill* fill, const RadialGradient* radial, const Matrix& pTransform)
{ {
auto cx = P(radial)->cx; float cx, cy, r, fx, fy, fr;
auto cy = P(radial)->cy; radial->radial(&cx, &cy, &r, &fx, &fy, &fr);
auto r = P(radial)->r;
auto fx = P(radial)->fx;
auto fy = P(radial)->fy;
auto fr = P(radial)->fr;
if (tvg::zero(r)) { if (tvg::zero(r)) {
fill->solid = true; fill->solid = true;

View file

@ -35,6 +35,7 @@
/************************************************************************/ /************************************************************************/
/* Internal Class Implementation */ /* Internal Class Implementation */
/************************************************************************/ /************************************************************************/
constexpr auto DOWN_SCALE_TOLERANCE = 0.5f; constexpr auto DOWN_SCALE_TOLERANCE = 0.5f;
struct FillLinear struct FillLinear

View file

@ -23,13 +23,6 @@
#include "tvgFrameModule.h" #include "tvgFrameModule.h"
#include "tvgAnimation.h" #include "tvgAnimation.h"
/************************************************************************/
/* Internal Class Implementation */
/************************************************************************/
/************************************************************************/
/* External Class Implementation */
/************************************************************************/
Animation::~Animation() Animation::~Animation()
{ {
@ -44,7 +37,7 @@ Animation::Animation() : pImpl(new Impl)
Result Animation::frame(float no) noexcept Result Animation::frame(float no) noexcept
{ {
auto loader = pImpl->picture->pImpl->loader; auto loader = PICTURE(pImpl->picture)->loader;
if (!loader) return Result::InsufficientCondition; if (!loader) return Result::InsufficientCondition;
if (!loader->animatable()) return Result::NonSupport; if (!loader->animatable()) return Result::NonSupport;
@ -62,7 +55,7 @@ Picture* Animation::picture() const noexcept
float Animation::curFrame() const noexcept float Animation::curFrame() const noexcept
{ {
auto loader = pImpl->picture->pImpl->loader; auto loader = PICTURE(pImpl->picture)->loader;
if (!loader) return 0; if (!loader) return 0;
if (!loader->animatable()) return 0; if (!loader->animatable()) return 0;
@ -73,7 +66,7 @@ float Animation::curFrame() const noexcept
float Animation::totalFrame() const noexcept float Animation::totalFrame() const noexcept
{ {
auto loader = pImpl->picture->pImpl->loader; auto loader = PICTURE(pImpl->picture)->loader;
if (!loader) return 0; if (!loader) return 0;
if (!loader->animatable()) return 0; if (!loader->animatable()) return 0;
@ -84,7 +77,7 @@ float Animation::totalFrame() const noexcept
float Animation::duration() const noexcept float Animation::duration() const noexcept
{ {
auto loader = pImpl->picture->pImpl->loader; auto loader = PICTURE(pImpl->picture)->loader;
if (!loader) return 0; if (!loader) return 0;
if (!loader->animatable()) return 0; if (!loader->animatable()) return 0;
@ -97,7 +90,7 @@ Result Animation::segment(float begin, float end) noexcept
{ {
if (begin < 0.0 || end > 1.0 || begin > end) return Result::InvalidArguments; if (begin < 0.0 || end > 1.0 || begin > end) return Result::InvalidArguments;
auto loader = pImpl->picture->pImpl->loader; auto loader = PICTURE(pImpl->picture)->loader;
if (!loader) return Result::InsufficientCondition; if (!loader) return Result::InsufficientCondition;
if (!loader->animatable()) return Result::NonSupport; if (!loader->animatable()) return Result::NonSupport;
@ -109,7 +102,7 @@ Result Animation::segment(float begin, float end) noexcept
Result Animation::segment(float *begin, float *end) noexcept Result Animation::segment(float *begin, float *end) noexcept
{ {
auto loader = pImpl->picture->pImpl->loader; auto loader = PICTURE(pImpl->picture)->loader;
if (!loader) return Result::InsufficientCondition; if (!loader) return Result::InsufficientCondition;
if (!loader->animatable()) return Result::NonSupport; if (!loader->animatable()) return Result::NonSupport;
if (!begin && !end) return Result::InvalidArguments; if (!begin && !end) return Result::InvalidArguments;

View file

@ -22,11 +22,7 @@
#include "tvgCanvas.h" #include "tvgCanvas.h"
/************************************************************************/ Canvas::Canvas():pImpl(new Impl)
/* External Class Implementation */
/************************************************************************/
Canvas::Canvas(RenderMethod *pRenderer):pImpl(new Impl(pRenderer))
{ {
} }

View file

@ -34,10 +34,9 @@ struct Canvas::Impl
RenderRegion vport = {0, 0, INT32_MAX, INT32_MAX}; RenderRegion vport = {0, 0, INT32_MAX, INT32_MAX};
Status status = Status::Synced; Status status = Status::Synced;
Impl(RenderMethod* pRenderer) : scene(Scene::gen()), renderer(pRenderer) Impl() : scene(Scene::gen())
{ {
scene->ref(); scene->ref();
renderer->ref();
} }
~Impl() ~Impl()
@ -77,8 +76,8 @@ struct Canvas::Impl
auto m = Matrix{1, 0, 0, 0, 1, 0, 0, 0, 1}; auto m = Matrix{1, 0, 0, 0, 1, 0, 0, 0, 1};
if (paint) P(paint)->update(renderer, m, clips, 255, flag); if (paint) PAINT(paint)->update(renderer, m, clips, 255, flag);
else PP(scene)->update(renderer, m, clips, 255, flag); else PAINT(scene)->update(renderer, m, clips, 255, flag);
status = Status::Updating; status = Status::Updating;
return Result::Success; return Result::Success;
@ -96,7 +95,7 @@ struct Canvas::Impl
if (!renderer->preRender()) return Result::InsufficientCondition; if (!renderer->preRender()) return Result::InsufficientCondition;
if (!PP(scene)->render(renderer) || !renderer->postRender()) return Result::InsufficientCondition; if (!PAINT(scene)->render(renderer) || !renderer->postRender()) return Result::InsufficientCondition;
status = Status::Drawing; status = Status::Drawing;

View file

@ -75,10 +75,7 @@ using Size = Point;
uint16_t THORVG_VERSION_NUMBER(); uint16_t THORVG_VERSION_NUMBER();
#define PIMPL(INST, CLASS) ((CLASS::Impl*)INST->pImpl) //Access to pimpl
#define P(A) ((A)->pImpl) //Access to pimpl.
#define PP(A) (((Paint*)(A))->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

@ -22,60 +22,12 @@
#include "tvgFill.h" #include "tvgFill.h"
/************************************************************************/
/* Internal Class Implementation */
/************************************************************************/
Fill* RadialGradient::Impl::duplicate()
{
auto ret = RadialGradient::gen();
if (!ret) return nullptr;
ret->pImpl->cx = cx;
ret->pImpl->cy = cy;
ret->pImpl->r = r;
ret->pImpl->fx = fx;
ret->pImpl->fy = fy;
ret->pImpl->fr = fr;
return ret;
}
Result RadialGradient::Impl::radial(float cx, float cy, float r, float fx, float fy, float fr)
{
if (r < 0 || fr < 0) return Result::InvalidArguments;
this->cx = cx;
this->cy = cy;
this->r = r;
this->fx = fx;
this->fy = fy;
this->fr = fr;
return Result::Success;
};
Fill* LinearGradient::Impl::duplicate()
{
auto ret = LinearGradient::gen();
if (!ret) return nullptr;
ret->pImpl->x1 = x1;
ret->pImpl->y1 = y1;
ret->pImpl->x2 = x2;
ret->pImpl->y2 = y2;
return ret;
};
/************************************************************************/ /************************************************************************/
/* External Class Implementation */ /* Fill Class Implementation */
/************************************************************************/ /************************************************************************/
Fill::Fill():pImpl(new Impl()) Fill::Fill()
{ {
} }
@ -88,32 +40,13 @@ Fill::~Fill()
Result Fill::colorStops(const ColorStop* colorStops, uint32_t cnt) noexcept Result Fill::colorStops(const ColorStop* colorStops, uint32_t cnt) noexcept
{ {
if ((!colorStops && cnt > 0) || (colorStops && cnt == 0)) return Result::InvalidArguments; return pImpl->update(colorStops, cnt);
if (cnt == 0) {
if (pImpl->colorStops) {
free(pImpl->colorStops);
pImpl->colorStops = nullptr;
pImpl->cnt = 0;
}
return Result::Success;
}
if (pImpl->cnt != cnt) {
pImpl->colorStops = static_cast<ColorStop*>(realloc(pImpl->colorStops, cnt * sizeof(ColorStop)));
}
pImpl->cnt = cnt;
memcpy(pImpl->colorStops, colorStops, cnt * sizeof(ColorStop));
return Result::Success;
} }
uint32_t Fill::colorStops(const ColorStop** colorStops) const noexcept uint32_t Fill::colorStops(const ColorStop** colorStops) const noexcept
{ {
if (colorStops) *colorStops = pImpl->colorStops; if (colorStops) *colorStops = pImpl->colorStops;
return pImpl->cnt; return pImpl->cnt;
} }
@ -121,7 +54,6 @@ uint32_t Fill::colorStops(const ColorStop** colorStops) const noexcept
Result Fill::spread(FillSpread s) noexcept Result Fill::spread(FillSpread s) noexcept
{ {
pImpl->spread = s; pImpl->spread = s;
return Result::Success; return Result::Success;
} }
@ -151,34 +83,26 @@ Fill* Fill::duplicate() const noexcept
} }
RadialGradient::RadialGradient():pImpl(new Impl()) /************************************************************************/
{ /* RadialGradient Class Implementation */
Fill::pImpl->method(new FillDup<RadialGradient::Impl>(pImpl)); /************************************************************************/
}
RadialGradient::~RadialGradient() RadialGradient::RadialGradient()
{ {
delete(pImpl); 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
{ {
return pImpl->radial(cx, cy, r, fx, fy, fr); return RADIAL(this)->radial(cx, cy, r, fx, fy, fr);
} }
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
{ {
if (cx) *cx = pImpl->cx; return RADIAL(this)->radial(cx, cy, r, fx, fy, fr);
if (cy) *cy = pImpl->cy;
if (r) *r = pImpl->r;
if (fx) *fx = pImpl->fx;
if (fy) *fy = pImpl->fy;
if (fr) *fr = pImpl->fr;
return Result::Success;
} }
@ -194,37 +118,26 @@ Type RadialGradient::type() const noexcept
} }
LinearGradient::LinearGradient():pImpl(new Impl()) /************************************************************************/
{ /* LinearGradient Class Implementation */
Fill::pImpl->method(new FillDup<LinearGradient::Impl>(pImpl)); /************************************************************************/
}
LinearGradient::~LinearGradient() LinearGradient::LinearGradient()
{ {
delete(pImpl); 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
{ {
pImpl->x1 = x1; return LINEAR(this)->linear(x1, y1, x2, y2);
pImpl->y1 = y1;
pImpl->x2 = x2;
pImpl->y2 = y2;
return Result::Success;
} }
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
{ {
if (x1) *x1 = pImpl->x1; return LINEAR(this)->linear(x1, y1, x2, y2);
if (x2) *x2 = pImpl->x2;
if (y1) *y1 = pImpl->y1;
if (y2) *y2 = pImpl->y2;
return Result::Success;
} }

View file

@ -27,80 +27,143 @@
#include <cstring> #include <cstring>
#include "tvgCommon.h" #include "tvgCommon.h"
template<typename T> #define LINEAR(A) PIMPL(A, LinearGradient)
struct DuplicateMethod #define RADIAL(A) PIMPL(A, RadialGradient)
{
virtual ~DuplicateMethod() {}
virtual T* duplicate() = 0;
};
template<class T>
struct FillDup : DuplicateMethod<Fill>
{
T* inst = nullptr;
FillDup(T* _inst) : inst(_inst) {}
~FillDup() {}
Fill* duplicate() override
{
return inst->duplicate();
}
};
struct Fill::Impl struct Fill::Impl
{ {
ColorStop* colorStops = nullptr; ColorStop* colorStops = nullptr;
Matrix transform = {1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f}; Matrix transform = {1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f};
uint32_t cnt = 0; uint16_t cnt = 0;
DuplicateMethod<Fill>* dup = nullptr; FillSpread spread = FillSpread::Pad;
FillSpread spread;
~Impl() virtual ~Impl()
{ {
delete(dup);
free(colorStops); free(colorStops);
} }
void method(DuplicateMethod<Fill>* dup) void copy(Fill::Impl* dup)
{ {
this->dup = dup; cnt = dup->cnt;
spread = dup->spread;
colorStops = static_cast<ColorStop*>(malloc(sizeof(ColorStop) * dup->cnt));
memcpy(colorStops, dup->colorStops, sizeof(ColorStop) * dup->cnt);
transform = dup->transform;
} }
Fill* duplicate() Result update(const ColorStop* colorStops, uint32_t cnt)
{ {
auto ret = dup->duplicate(); if ((!colorStops && cnt > 0) || (colorStops && cnt == 0)) return Result::InvalidArguments;
ret->pImpl->cnt = cnt; if (cnt == 0) {
ret->pImpl->spread = spread; if (this->colorStops) {
ret->pImpl->colorStops = static_cast<ColorStop*>(malloc(sizeof(ColorStop) * cnt)); free(this->colorStops);
memcpy(ret->pImpl->colorStops, colorStops, sizeof(ColorStop) * cnt); this->colorStops = nullptr;
ret->pImpl->transform = transform; this->cnt = 0;
return ret;
} }
return Result::Success;
}
if (cnt != this->cnt) {
this->colorStops = static_cast<ColorStop*>(realloc(this->colorStops, cnt * sizeof(ColorStop)));
}
this->cnt = cnt;
memcpy(this->colorStops, colorStops, cnt * sizeof(ColorStop));
return Result::Success;
}
virtual Fill* duplicate() = 0;
}; };
struct RadialGradient::Impl struct RadialGradient::Impl : Fill::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(); Fill* duplicate() override
Result radial(float cx, float cy, float r, float fx, float fy, float fr); {
auto ret = RadialGradient::gen();
RADIAL(ret)->copy(this);
RADIAL(ret)->cx = cx;
RADIAL(ret)->cy = cy;
RADIAL(ret)->r = r;
RADIAL(ret)->fx = fx;
RADIAL(ret)->fy = fy;
RADIAL(ret)->fr = fr;
return ret;
}
Result radial(float cx, float cy, float r, float fx, float fy, float fr)
{
if (r < 0 || fr < 0) return Result::InvalidArguments;
this->cx = cx;
this->cy = cy;
this->r = r;
this->fx = fx;
this->fy = fy;
this->fr = fr;
return Result::Success;
}
Result radial(float* cx, float* cy, float* r, float* fx, float* fy, float* fr) const
{
if (cx) *cx = this->cx;
if (cy) *cy = this->cy;
if (r) *r = this->r;
if (fx) *fx = this->fx;
if (fy) *fy = this->fy;
if (fr) *fr = this->fr;
return Result::Success;
}
}; };
struct LinearGradient::Impl struct LinearGradient::Impl : Fill::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(); Fill* duplicate() override
{
auto ret = LinearGradient::gen();
LINEAR(ret)->copy(this);
LINEAR(ret)->x1 = x1;
LINEAR(ret)->y1 = y1;
LINEAR(ret)->x2 = x2;
LINEAR(ret)->y2 = y2;
return ret;
}
Result linear(float x1, float y1, float x2, float y2) noexcept
{
this->x1 = x1;
this->y1 = y1;
this->x2 = x2;
this->y2 = y2;
return Result::Success;
}
Result linear(float* x1, float* y1, float* x2, float* y2) const noexcept
{
if (x1) *x1 = this->x1;
if (x2) *x2 = this->x2;
if (y1) *y1 = this->y1;
if (y2) *y2 = this->y2;
return Result::Success;
}
}; };

View file

@ -24,38 +24,20 @@
#ifdef THORVG_GL_RASTER_SUPPORT #ifdef THORVG_GL_RASTER_SUPPORT
#include "tvgGlRenderer.h" #include "tvgGlRenderer.h"
#else
class GlRenderer : public RenderMethod
{
//Non Supported. Dummy Class */
};
#endif #endif
/************************************************************************/ GlCanvas::GlCanvas()
/* Internal Class Implementation */
/************************************************************************/
struct GlCanvas::Impl
{ {
};
/************************************************************************/
/* External Class Implementation */
/************************************************************************/
#ifdef THORVG_GL_RASTER_SUPPORT #ifdef THORVG_GL_RASTER_SUPPORT
GlCanvas::GlCanvas() : Canvas(GlRenderer::gen()), pImpl(nullptr) pImpl->renderer = GlRenderer::gen();
#else pImpl->renderer->ref();
GlCanvas::GlCanvas() : Canvas(nullptr), pImpl(nullptr)
#endif #endif
{
} }
GlCanvas::~GlCanvas() GlCanvas::~GlCanvas()
{ {
delete(pImpl); //TODO:
} }
@ -64,20 +46,20 @@ Result GlCanvas::target(int32_t id, uint32_t w, uint32_t h, ColorSpace cs) noexc
#ifdef THORVG_GL_RASTER_SUPPORT #ifdef THORVG_GL_RASTER_SUPPORT
if (cs != ColorSpace::ABGR8888S) return Result::NonSupport; if (cs != ColorSpace::ABGR8888S) return Result::NonSupport;
if (Canvas::pImpl->status != Status::Damaged && Canvas::pImpl->status != Status::Synced) { if (pImpl->status != Status::Damaged && pImpl->status != Status::Synced) {
return Result::InsufficientCondition; return Result::InsufficientCondition;
} }
//We know renderer type, avoid dynamic_cast for performance. //We know renderer type, avoid dynamic_cast for performance.
auto renderer = static_cast<GlRenderer*>(Canvas::pImpl->renderer); auto renderer = static_cast<GlRenderer*>(pImpl->renderer);
if (!renderer) return Result::MemoryCorruption; if (!renderer) return Result::MemoryCorruption;
if (!renderer->target(id, w, h)) return Result::Unknown; if (!renderer->target(id, w, h)) return Result::Unknown;
Canvas::pImpl->vport = {0, 0, (int32_t)w, (int32_t)h}; pImpl->vport = {0, 0, (int32_t)w, (int32_t)h};
renderer->viewport(Canvas::pImpl->vport); renderer->viewport(pImpl->vport);
//Paints must be updated again with this new target. //Paints must be updated again with this new target.
Canvas::pImpl->status = Status::Damaged; pImpl->status = Status::Damaged;
return Result::Success; return Result::Success;
#endif #endif

View file

@ -34,7 +34,7 @@ public:
//Utility Method: Iterator Accessor //Utility Method: Iterator Accessor
static Iterator* iterator(const Paint* paint) static Iterator* iterator(const Paint* paint)
{ {
return paint->pImpl->iterator(); return PAINT(paint)->iterator();
} }
}; };

View file

@ -33,10 +33,10 @@
#define PAINT_METHOD(ret, METHOD) \ #define PAINT_METHOD(ret, METHOD) \
switch (paint->type()) { \ switch (paint->type()) { \
case Type::Shape: ret = P((Shape*)paint)->METHOD; break; \ case Type::Shape: ret = SHAPE(paint)->METHOD; break; \
case Type::Scene: ret = P((Scene*)paint)->METHOD; break; \ case Type::Scene: ret = SCENE(paint)->METHOD; break; \
case Type::Picture: ret = P((Picture*)paint)->METHOD; break; \ case Type::Picture: ret = PICTURE(paint)->METHOD; break; \
case Type::Text: ret = P((Text*)paint)->METHOD; break; \ case Type::Text: ret = TEXT(paint)->METHOD; break; \
default: ret = {}; \ default: ret = {}; \
} }
@ -170,40 +170,6 @@ Paint* Paint::Impl::duplicate(Paint* ret)
} }
bool Paint::Impl::rotate(float degree)
{
if (tr.overriding) return false;
if (tvg::equal(degree, tr.degree)) return true;
tr.degree = degree;
renderFlag |= RenderUpdateFlag::Transform;
return true;
}
bool Paint::Impl::scale(float factor)
{
if (tr.overriding) return false;
if (tvg::equal(factor, tr.scale)) return true;
tr.scale = factor;
renderFlag |= RenderUpdateFlag::Transform;
return true;
}
bool Paint::Impl::translate(float x, float y)
{
if (tr.overriding) return false;
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;
return true;
}
bool Paint::Impl::render(RenderMethod* renderer) bool Paint::Impl::render(RenderMethod* renderer)
{ {
if (opacity == 0) return true; if (opacity == 0) return true;
@ -214,7 +180,7 @@ bool Paint::Impl::render(RenderMethod* renderer)
RenderRegion region; RenderRegion region;
PAINT_METHOD(region, bounds(renderer)); PAINT_METHOD(region, bounds(renderer));
if (MASK_REGION_MERGING(maskData->method)) region.add(P(maskData->target)->bounds(renderer)); if (MASK_REGION_MERGING(maskData->method)) region.add(PAINT(maskData->target)->bounds(renderer));
if (region.w == 0 || region.h == 0) return true; if (region.w == 0 || region.h == 0) return true;
cmp = renderer->target(region, MASK_TO_COLORSPACE(renderer, maskData->method), CompositionFlag::Masking); cmp = renderer->target(region, MASK_TO_COLORSPACE(renderer, maskData->method), CompositionFlag::Masking);
if (renderer->beginComposite(cmp, MaskMethod::None, 255)) { if (renderer->beginComposite(cmp, MaskMethod::None, 255)) {
@ -251,7 +217,7 @@ RenderData Paint::Impl::update(RenderMethod* renderer, const Matrix& pm, Array<R
if (maskData) { if (maskData) {
auto target = maskData->target; auto target = maskData->target;
auto method = maskData->method; auto method = maskData->method;
P(target)->ctxFlag &= ~ContextFlag::FastTrack; //reset PAINT(target)->ctxFlag &= ~ContextFlag::FastTrack; //reset
/* If the transformation has no rotational factors and the Alpha(InvAlpha) Masking involves a simple rectangle, /* If the transformation has no rotational factors and the Alpha(InvAlpha) Masking involves a simple rectangle,
we can optimize by using the viewport instead of the regular Alphaing sequence for improved performance. */ we can optimize by using the viewport instead of the regular Alphaing sequence for improved performance. */
@ -260,31 +226,31 @@ RenderData Paint::Impl::update(RenderMethod* renderer, const Matrix& pm, Array<R
uint8_t a; uint8_t a;
shape->fillColor(nullptr, nullptr, nullptr, &a); shape->fillColor(nullptr, nullptr, nullptr, &a);
//no gradient fill & no maskings of the masking target. //no gradient fill & no maskings of the masking target.
if (!shape->fill() && !(PP(shape)->maskData)) { if (!shape->fill() && !(PAINT(shape)->maskData)) {
if ((method == MaskMethod::Alpha && a == 255 && PP(shape)->opacity == 255) || (method == MaskMethod::InvAlpha && (a == 0 || PP(shape)->opacity == 0))) { if ((method == MaskMethod::Alpha && a == 255 && PAINT(shape)->opacity == 255) || (method == MaskMethod::InvAlpha && (a == 0 || PAINT(shape)->opacity == 0))) {
viewport = renderer->viewport(); viewport = renderer->viewport();
if ((compFastTrack = _compFastTrack(renderer, target, pm, viewport)) == Result::Success) { if ((compFastTrack = _compFastTrack(renderer, target, pm, viewport)) == Result::Success) {
P(target)->ctxFlag |= ContextFlag::FastTrack; PAINT(target)->ctxFlag |= ContextFlag::FastTrack;
} }
} }
} }
} }
if (compFastTrack == Result::InsufficientCondition) { if (compFastTrack == Result::InsufficientCondition) {
trd = P(target)->update(renderer, pm, clips, 255, pFlag, false); trd = PAINT(target)->update(renderer, pm, clips, 255, pFlag, false);
} }
} }
/* 2. Clipping */ /* 2. Clipping */
if (this->clipper) { if (this->clipper) {
P(this->clipper)->ctxFlag &= ~ContextFlag::FastTrack; //reset PAINT(this->clipper)->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.
Update the subsequent clipper first and check its ctxFlag. */ Update the subsequent clipper first and check its ctxFlag. */
if (!P(this->clipper)->clipper && (compFastTrack = _compFastTrack(renderer, this->clipper, pm, viewport)) == Result::Success) { if (!PAINT(this->clipper)->clipper && (compFastTrack = _compFastTrack(renderer, this->clipper, pm, viewport)) == Result::Success) {
P(this->clipper)->ctxFlag |= ContextFlag::FastTrack; PAINT(this->clipper)->ctxFlag |= ContextFlag::FastTrack;
} }
if (compFastTrack == Result::InsufficientCondition) { if (compFastTrack == Result::InsufficientCondition) {
trd = P(this->clipper)->update(renderer, pm, clips, 255, pFlag, true); trd = PAINT(this->clipper)->update(renderer, pm, clips, 255, pFlag, true);
clips.push(trd); clips.push(trd);
} }
} }
@ -354,37 +320,11 @@ bool Paint::Impl::bounds(float* x, float* y, float* w, float* h, bool transforme
} }
void Paint::Impl::reset()
{
if (clipper) {
clipper->unref();
clipper = nullptr;
}
if (maskData) {
maskData->target->unref();
free(maskData);
maskData = nullptr;
}
tvg::identity(&tr.m);
tr.degree = 0.0f;
tr.scale = 1.0f;
tr.overriding = false;
blendMethod = BlendMethod::Normal;
renderFlag = RenderUpdateFlag::None;
ctxFlag = ContextFlag::Default;
opacity = 255;
paint->id = 0;
}
/************************************************************************/ /************************************************************************/
/* External Class Implementation */ /* External Class Implementation */
/************************************************************************/ /************************************************************************/
Paint :: Paint() : pImpl(new Impl(this)) Paint :: Paint()
{ {
} }
@ -464,13 +404,7 @@ Result Paint::mask(Paint* target, MaskMethod method) noexcept
MaskMethod Paint::mask(const Paint** target) const noexcept MaskMethod Paint::mask(const Paint** target) const noexcept
{ {
if (pImpl->maskData) { return pImpl->mask(target);
if (target) *target = pImpl->maskData->target;
return pImpl->maskData->method;
} else {
if (target) *target = nullptr;
return MaskMethod::None;
}
} }
@ -495,37 +429,20 @@ Result Paint::blend(BlendMethod method) noexcept
{ {
//TODO: Remove later //TODO: Remove later
if (method == BlendMethod::Hue || method == BlendMethod::Saturation || method == BlendMethod::Color || method == BlendMethod::Luminosity || method == BlendMethod::HardMix) return Result::NonSupport; if (method == BlendMethod::Hue || method == BlendMethod::Saturation || method == BlendMethod::Color || method == BlendMethod::Luminosity || method == BlendMethod::HardMix) return Result::NonSupport;
pImpl->blend(method);
if (pImpl->blendMethod != method) {
pImpl->blendMethod = method;
pImpl->renderFlag |= RenderUpdateFlag::Blend;
}
return Result::Success; return Result::Success;
} }
uint8_t Paint::ref() noexcept uint8_t Paint::ref() noexcept
{ {
if (pImpl->refCnt == UINT8_MAX) TVGERR("RENDERER", "Reference Count Overflow!"); return pImpl->ref();
else ++pImpl->refCnt;
return pImpl->refCnt;
} }
uint8_t Paint::unref(bool free) noexcept uint8_t Paint::unref(bool free) noexcept
{ {
if (pImpl->refCnt > 0) --pImpl->refCnt; return pImpl->unref(free);
else TVGERR("RENDERER", "Corrupted Reference Count!");
if (free && pImpl->refCnt == 0) {
//TODO: use the global dismiss function?
delete(this);
return 0;
}
return pImpl->refCnt;
} }

View file

@ -26,6 +26,8 @@
#include "tvgRender.h" #include "tvgRender.h"
#include "tvgMath.h" #include "tvgMath.h"
#define PAINT(A) PIMPL(A, Paint)
namespace tvg namespace tvg
{ {
enum ContextFlag : uint8_t {Default = 0, FastTrack = 1}; enum ContextFlag : uint8_t {Default = 0, FastTrack = 1};
@ -83,7 +85,7 @@ namespace tvg
reset(); reset();
} }
~Impl() virtual ~Impl()
{ {
if (maskData) { if (maskData) {
maskData->target->unref(); maskData->target->unref();
@ -93,6 +95,32 @@ namespace tvg
if (renderer && (renderer->unref() == 0)) delete(renderer); if (renderer && (renderer->unref() == 0)) delete(renderer);
} }
uint8_t ref()
{
if (refCnt == UINT8_MAX) TVGERR("RENDERER", "Reference Count Overflow!");
else ++refCnt;
return refCnt;
}
uint8_t unref(bool free)
{
if (refCnt > 0) --refCnt;
else TVGERR("RENDERER", "Corrupted Reference Count!");
if (free && refCnt == 0) {
//TODO: use the global dismiss function?
delete(this);
return 0;
}
return refCnt;
}
void update(RenderUpdateFlag flag)
{
renderFlag |= flag;
}
bool transform(const Matrix& m) bool transform(const Matrix& m)
{ {
if (&tr.m != &m) tr.m = m; if (&tr.m != &m) tr.m = m;
@ -143,16 +171,87 @@ namespace tvg
return true; return true;
} }
MaskMethod mask(const Paint** target) const
{
if (maskData) {
if (target) *target = maskData->target;
return maskData->method;
} else {
if (target) *target = nullptr;
return MaskMethod::None;
}
}
void reset()
{
if (clipper) {
clipper->unref();
clipper = nullptr;
}
if (maskData) {
maskData->target->unref();
free(maskData);
maskData = nullptr;
}
tvg::identity(&tr.m);
tr.degree = 0.0f;
tr.scale = 1.0f;
tr.overriding = false;
blendMethod = BlendMethod::Normal;
renderFlag = RenderUpdateFlag::None;
ctxFlag = ContextFlag::Default;
opacity = 255;
paint->id = 0;
}
bool rotate(float degree)
{
if (tr.overriding) return false;
if (tvg::equal(degree, tr.degree)) return true;
tr.degree = degree;
renderFlag |= RenderUpdateFlag::Transform;
return true;
}
bool scale(float factor)
{
if (tr.overriding) return false;
if (tvg::equal(factor, tr.scale)) return true;
tr.scale = factor;
renderFlag |= RenderUpdateFlag::Transform;
return true;
}
bool translate(float x, float y)
{
if (tr.overriding) return false;
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;
return true;
}
void blend(BlendMethod method)
{
if (blendMethod != method) {
blendMethod = method;
renderFlag |= RenderUpdateFlag::Blend;
}
}
RenderRegion bounds(RenderMethod* renderer) const; RenderRegion bounds(RenderMethod* renderer) const;
Iterator* iterator(); Iterator* iterator();
bool rotate(float degree);
bool scale(float factor);
bool translate(float x, float y);
bool bounds(float* x, float* y, float* w, float* h, bool transformed, bool stroking, bool origin = false); bool bounds(float* x, float* y, float* w, float* h, bool transformed, bool stroking, bool origin = false);
RenderData update(RenderMethod* renderer, const Matrix& pm, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper = false); RenderData update(RenderMethod* renderer, const Matrix& pm, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper = false);
bool render(RenderMethod* renderer); bool render(RenderMethod* renderer);
Paint* duplicate(Paint* ret = nullptr); Paint* duplicate(Paint* ret = nullptr);
void reset();
}; };
} }

View file

@ -23,124 +23,9 @@
#include "tvgPaint.h" #include "tvgPaint.h"
#include "tvgPicture.h" #include "tvgPicture.h"
/************************************************************************/ Picture::Picture()
/* Internal Class Implementation */
/************************************************************************/
RenderUpdateFlag Picture::Impl::load()
{ {
if (loader) { pImpl = new Impl(this);
if (paint) {
loader->sync();
} else {
paint = loader->paint();
if (paint) {
if (w != loader->w || h != loader->h) {
if (!resizing) {
w = loader->w;
h = loader->h;
}
loader->resize(paint, w, h);
resizing = false;
}
return RenderUpdateFlag::None;
}
}
if (!surface) {
if ((surface = loader->bitmap())) {
return RenderUpdateFlag::Image;
}
}
}
return RenderUpdateFlag::None;
}
void Picture::Impl::queryComposition(uint8_t opacity)
{
cFlag = CompositionFlag::Invalid;
//In this case, paint(scene) would try composition itself.
if (opacity < 255) return;
//Composition test
const Paint* target;
picture->mask(&target);
if (!target || target->pImpl->opacity == 255 || target->pImpl->opacity == 0) return;
cFlag = CompositionFlag::Opacity;
}
bool Picture::Impl::render(RenderMethod* renderer)
{
bool ret = false;
renderer->blend(PP(picture)->blendMethod);
if (surface) return renderer->renderImage(rd);
else if (paint) {
RenderCompositor* cmp = nullptr;
if (cFlag) {
cmp = renderer->target(bounds(renderer), renderer->colorSpace(), static_cast<CompositionFlag>(cFlag));
renderer->beginComposite(cmp, MaskMethod::None, 255);
}
ret = paint->pImpl->render(renderer);
if (cmp) renderer->endComposite(cmp);
}
return ret;
}
bool Picture::Impl::size(float w, float h)
{
this->w = w;
this->h = h;
resizing = true;
return true;
}
RenderRegion Picture::Impl::bounds(RenderMethod* renderer)
{
if (rd) return renderer->region(rd);
if (paint) return paint->pImpl->bounds(renderer);
return {0, 0, 0, 0};
}
Result Picture::Impl::load(ImageLoader* loader)
{
//Same resource has been loaded.
if (this->loader == loader) {
this->loader->sharing--; //make it sure the reference counting.
return Result::Success;
} else if (this->loader) {
LoaderMgr::retrieve(this->loader);
}
this->loader = loader;
if (!loader->read()) return Result::Unknown;
this->w = loader->w;
this->h = loader->h;
return Result::Success;
}
/************************************************************************/
/* External Class Implementation */
/************************************************************************/
Picture::Picture() : pImpl(new Impl(this))
{
}
Picture::~Picture()
{
delete(pImpl);
} }
@ -160,8 +45,7 @@ Result Picture::load(const char* filename) noexcept
{ {
#ifdef THORVG_FILE_IO_SUPPORT #ifdef THORVG_FILE_IO_SUPPORT
if (!filename) return Result::InvalidArguments; if (!filename) return Result::InvalidArguments;
return PICTURE(this)->load(filename);
return pImpl->load(filename);
#else #else
TVGLOG("RENDERER", "FILE IO is disabled!"); TVGLOG("RENDERER", "FILE IO is disabled!");
return Result::NonSupport; return Result::NonSupport;
@ -171,33 +55,26 @@ Result Picture::load(const char* filename) noexcept
Result Picture::load(const char* data, uint32_t size, const char* mimeType, const char* rpath, bool copy) noexcept Result Picture::load(const char* data, uint32_t size, const char* mimeType, const char* rpath, bool copy) noexcept
{ {
if (!data || size <= 0) return Result::InvalidArguments; return PICTURE(this)->load(data, size, mimeType, rpath, copy);
return pImpl->load(data, size, mimeType, rpath, copy);
} }
Result Picture::load(uint32_t* data, uint32_t w, uint32_t h, ColorSpace cs, bool copy) noexcept Result Picture::load(uint32_t* data, uint32_t w, uint32_t h, ColorSpace cs, bool copy) noexcept
{ {
if (!data || w <= 0 || h <= 0 || cs == ColorSpace::Unknown) return Result::InvalidArguments; return PICTURE(this)->load(data, w, h, cs, copy);
return pImpl->load(data, w, h, cs, copy);
} }
Result Picture::size(float w, float h) noexcept Result Picture::size(float w, float h) noexcept
{ {
if (pImpl->size(w, h)) return Result::Success; PICTURE(this)->size(w, h);
return Result::InsufficientCondition; return Result::Success;
} }
Result Picture::size(float* w, float* h) const noexcept Result Picture::size(float* w, float* h) const noexcept
{ {
if (!pImpl->loader) return Result::InsufficientCondition; return PICTURE(this)->size(w, h);
if (w) *w = pImpl->w;
if (h) *h = pImpl->h;
return Result::Success;
} }

View file

@ -23,10 +23,10 @@
#ifndef _TVG_PICTURE_H_ #ifndef _TVG_PICTURE_H_
#define _TVG_PICTURE_H_ #define _TVG_PICTURE_H_
#include <string>
#include "tvgPaint.h" #include "tvgPaint.h"
#include "tvgLoader.h" #include "tvgLoader.h"
#define PICTURE(A) PIMPL(A, Picture)
struct PictureIterator : Iterator struct PictureIterator : Iterator
{ {
@ -55,44 +55,32 @@ struct PictureIterator : Iterator
}; };
struct Picture::Impl struct Picture::Impl : Paint::Impl
{ {
ImageLoader* loader = nullptr; ImageLoader* loader = nullptr;
Paint* vector = nullptr; //vector picture uses
Paint* paint = nullptr; //vector picture uses RenderSurface* bitmap = nullptr; //bitmap picture uses
RenderSurface* surface = nullptr; //bitmap picture uses RenderData rd = nullptr;
RenderData rd = nullptr; //engine data
float w = 0, h = 0; float w = 0, h = 0;
Picture* picture = nullptr; uint8_t compFlag = CompositionFlag::Invalid;
uint8_t cFlag = CompositionFlag::Invalid;
bool resizing = false; bool resizing = false;
void queryComposition(uint8_t opacity); Impl(Picture* p) : Paint::Impl(p)
bool render(RenderMethod* renderer);
bool size(float w, float h);
RenderRegion bounds(RenderMethod* renderer);
Result load(ImageLoader* ploader);
Impl(Picture* p) : picture(p)
{ {
} }
~Impl() ~Impl()
{ {
LoaderMgr::retrieve(loader); LoaderMgr::retrieve(loader);
if (surface) { if (bitmap && renderer) renderer->dispose(rd);
if (auto renderer = PP(picture)->renderer) { delete(vector);
renderer->dispose(rd);
}
}
delete(paint);
} }
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 = static_cast<RenderUpdateFlag>(pFlag | load());
if (surface) { if (bitmap) {
if (flag == RenderUpdateFlag::None) return rd; if (flag == RenderUpdateFlag::None) return rd;
//Overriding Transformation by the desired image size //Overriding Transformation by the desired image size
@ -101,18 +89,34 @@ struct Picture::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(surface, rd, m, clips, opacity, flag); rd = renderer->prepare(bitmap, rd, m, clips, opacity, flag);
} else if (paint) { } else if (vector) {
if (resizing) { if (resizing) {
loader->resize(paint, w, h); loader->resize(vector, w, h);
resizing = false; resizing = false;
} }
queryComposition(opacity); queryComposition(opacity);
rd = paint->pImpl->update(renderer, transform, clips, opacity, flag, false); rd = vector->pImpl->update(renderer, transform, clips, opacity, flag, false);
} }
return rd; return rd;
} }
void size(float w, float h)
{
this->w = w;
this->h = h;
resizing = true;
}
Result size(float* w, float* h) const
{
if (!loader) return Result::InsufficientCondition;
if (w) *w = this->w;
if (h) *h = this->h;
return Result::Success;
}
bool bounds(float* x, float* y, float* w, float* h, bool stroking) bool bounds(float* x, float* y, float* w, float* h, bool stroking)
{ {
if (x) *x = 0; if (x) *x = 0;
@ -124,7 +128,7 @@ struct Picture::Impl
Result load(const char* filename) Result load(const char* filename)
{ {
if (paint || surface) return Result::InsufficientCondition; if (vector || bitmap) return Result::InsufficientCondition;
bool invalid; //Invalid Path bool invalid; //Invalid Path
auto loader = static_cast<ImageLoader*>(LoaderMgr::loader(filename, &invalid)); auto loader = static_cast<ImageLoader*>(LoaderMgr::loader(filename, &invalid));
@ -137,7 +141,8 @@ struct Picture::Impl
Result load(const char* data, uint32_t size, const char* mimeType, const char* rpath, bool copy) Result load(const char* data, uint32_t size, const char* mimeType, const char* rpath, bool copy)
{ {
if (paint || surface) return Result::InsufficientCondition; if (!data || size <= 0) return Result::InvalidArguments;
if (vector || bitmap) return Result::InsufficientCondition;
auto loader = static_cast<ImageLoader*>(LoaderMgr::loader(data, size, mimeType, rpath, copy)); auto loader = static_cast<ImageLoader*>(LoaderMgr::loader(data, size, mimeType, rpath, copy));
if (!loader) return Result::NonSupport; if (!loader) return Result::NonSupport;
return load(loader); return load(loader);
@ -145,7 +150,8 @@ struct Picture::Impl
Result load(uint32_t* data, uint32_t w, uint32_t h, ColorSpace cs, bool copy) Result load(uint32_t* data, uint32_t w, uint32_t h, ColorSpace cs, bool copy)
{ {
if (paint || surface) return Result::InsufficientCondition; if (!data || w <= 0 || h <= 0 || cs == ColorSpace::Unknown) return Result::InvalidArguments;
if (vector || bitmap) return Result::InsufficientCondition;
auto loader = static_cast<ImageLoader*>(LoaderMgr::loader(data, w, h, cs, copy)); auto loader = static_cast<ImageLoader*>(LoaderMgr::loader(data, w, h, cs, copy));
if (!loader) return Result::FailedAllocation; if (!loader) return Result::FailedAllocation;
@ -160,17 +166,17 @@ struct Picture::Impl
load(); load();
auto picture = Picture::gen(); auto picture = Picture::gen();
auto dup = picture->pImpl; auto dup = PICTURE(picture);
if (paint) dup->paint = paint->duplicate(); if (vector) dup->vector = vector->duplicate();
if (loader) { if (loader) {
dup->loader = loader; dup->loader = loader;
++dup->loader->sharing; ++dup->loader->sharing;
PP(picture)->renderFlag |= RenderUpdateFlag::Image; PAINT(picture)->renderFlag |= RenderUpdateFlag::Image;
} }
dup->surface = surface; dup->bitmap = bitmap;
dup->w = w; dup->w = w;
dup->h = h; dup->h = h;
dup->resizing = resizing; dup->resizing = resizing;
@ -181,7 +187,7 @@ struct Picture::Impl
Iterator* iterator() Iterator* iterator()
{ {
load(); load();
return new PictureIterator(paint); return new PictureIterator(vector);
} }
uint32_t* data(uint32_t* w, uint32_t* h) uint32_t* data(uint32_t* w, uint32_t* h)
@ -196,11 +202,96 @@ struct Picture::Impl
if (w) *w = 0; if (w) *w = 0;
if (h) *h = 0; if (h) *h = 0;
} }
if (surface) return surface->buf32; if (bitmap) return bitmap->buf32;
else return nullptr; else return nullptr;
} }
RenderUpdateFlag load(); RenderUpdateFlag load()
{
if (loader) {
if (vector) {
loader->sync();
} else {
vector = loader->paint();
if (vector) {
if (w != loader->w || h != loader->h) {
if (!resizing) {
w = loader->w;
h = loader->h;
}
loader->resize(vector, w, h);
resizing = false;
}
return RenderUpdateFlag::None;
}
}
if (!bitmap) {
if ((bitmap = loader->bitmap())) {
return RenderUpdateFlag::Image;
}
}
}
return RenderUpdateFlag::None;
}
void queryComposition(uint8_t opacity)
{
compFlag = CompositionFlag::Invalid;
//In this case, paint(scene) would try composition itself.
if (opacity < 255) return;
//Composition test
const Paint* target;
paint->mask(&target);
if (!target || target->pImpl->opacity == 255 || target->pImpl->opacity == 0) return;
compFlag = CompositionFlag::Opacity;
}
bool render(RenderMethod* renderer)
{
bool ret = false;
renderer->blend(blendMethod);
if (bitmap) return renderer->renderImage(rd);
else if (vector) {
RenderCompositor* cmp = nullptr;
if (compFlag) {
cmp = renderer->target(bounds(renderer), renderer->colorSpace(), static_cast<CompositionFlag>(compFlag));
renderer->beginComposite(cmp, MaskMethod::None, 255);
}
ret = vector->pImpl->render(renderer);
if (cmp) renderer->endComposite(cmp);
}
return ret;
}
RenderRegion bounds(RenderMethod* renderer)
{
if (rd) return renderer->region(rd);
if (vector) return vector->pImpl->bounds(renderer);
return {0, 0, 0, 0};
}
Result load(ImageLoader* loader)
{
//Same resource has been loaded.
if (this->loader == loader) {
this->loader->sharing--; //make it sure the reference counting.
return Result::Success;
} else if (this->loader) {
LoaderMgr::retrieve(this->loader);
}
this->loader = loader;
if (!loader->read()) return Result::Unknown;
this->w = loader->w;
this->h = loader->h;
return Result::Success;
}
}; };
#endif //_TVG_PICTURE_H_ #endif //_TVG_PICTURE_H_

View file

@ -23,14 +23,6 @@
#include "tvgMath.h" #include "tvgMath.h"
#include "tvgRender.h" #include "tvgRender.h"
/************************************************************************/
/* Internal Class Implementation */
/************************************************************************/
/************************************************************************/
/* External Class Implementation */
/************************************************************************/
uint32_t RenderMethod::ref() uint32_t RenderMethod::ref()
{ {

View file

@ -20,38 +20,12 @@
* SOFTWARE. * SOFTWARE.
*/ */
#include <cstdarg>
#include "tvgScene.h" #include "tvgScene.h"
/************************************************************************/
/* Internal Class Implementation */
/************************************************************************/
Result Scene::Impl::resetEffects() Scene::Scene()
{ {
if (effects) { pImpl = new Impl(this);
for (auto e = effects->begin(); e < effects->end(); ++e) {
delete(*e);
}
delete(effects);
effects = nullptr;
}
return Result::Success;
}
/************************************************************************/
/* External Class Implementation */
/************************************************************************/
Scene::Scene() : pImpl(new Impl(this))
{
}
Scene::~Scene()
{
delete(pImpl);
} }
@ -69,56 +43,27 @@ Type Scene::type() const noexcept
Result Scene::push(Paint* target, Paint* at) noexcept Result Scene::push(Paint* target, Paint* at) noexcept
{ {
if (!target) return Result::InvalidArguments; return SCENE(this)->insert(target, at);
target->ref();
return pImpl->insert(target, at);
} }
Result Scene::remove(Paint* paint) noexcept Result Scene::remove(Paint* paint) noexcept
{ {
if (paint) return pImpl->remove(paint); if (paint) return SCENE(this)->remove(paint);
else return pImpl->clearPaints(); else return SCENE(this)->clearPaints();
} }
const list<Paint*>& Scene::paints() const noexcept const list<Paint*>& Scene::paints() const noexcept
{ {
return pImpl->paints; return SCENE(this)->paints;
} }
Result Scene::push(SceneEffect effect, ...) noexcept Result Scene::push(SceneEffect effect, ...) noexcept
{ {
if (effect == SceneEffect::ClearAll) return pImpl->resetEffects();
if (!pImpl->effects) pImpl->effects = new Array<RenderEffect*>;
va_list args; va_list args;
va_start(args, effect); va_start(args, effect);
RenderEffect* re = nullptr; return SCENE(this)->push(effect, args);
switch (effect) {
case SceneEffect::GaussianBlur: {
re = RenderEffectGaussianBlur::gen(args);
break;
}
case SceneEffect::DropShadow: {
re = RenderEffectDropShadow::gen(args);
break;
}
case SceneEffect::Fill: {
re = RenderEffectFill::gen(args);
break;
}
default: break;
}
if (!re) return Result::InvalidArguments;
pImpl->effects->push(re);
return Result::Success;
} }

View file

@ -24,9 +24,12 @@
#define _TVG_SCENE_H_ #define _TVG_SCENE_H_
#include <algorithm> #include <algorithm>
#include <cstdarg>
#include "tvgMath.h" #include "tvgMath.h"
#include "tvgPaint.h" #include "tvgPaint.h"
#define SCENE(A) PIMPL(A, Scene)
struct SceneIterator : Iterator struct SceneIterator : Iterator
{ {
list<Paint*>* paints; list<Paint*>* paints;
@ -56,17 +59,16 @@ struct SceneIterator : Iterator
} }
}; };
struct Scene::Impl struct Scene::Impl : Paint::Impl
{ {
list<Paint*> paints; list<Paint*> paints; //children list
RenderData rd = nullptr; RenderData rd = nullptr;
Scene* scene = nullptr;
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) : scene(s) Impl(Scene* s) : Paint::Impl(s)
{ {
} }
@ -76,9 +78,7 @@ struct Scene::Impl
clearPaints(); clearPaints();
if (auto renderer = PP(scene)->renderer) { if (renderer) renderer->dispose(rd);
renderer->dispose(rd);
}
} }
uint8_t needComposition(uint8_t opacity) uint8_t needComposition(uint8_t opacity)
@ -89,8 +89,8 @@ struct Scene::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 (scene->mask(nullptr) != MaskMethod::None) compFlag |= CompositionFlag::Masking; if (paint->mask(nullptr) != MaskMethod::None) compFlag |= CompositionFlag::Masking;
if (PP(scene)->blendMethod != BlendMethod::Normal) compFlag |= CompositionFlag::Blending; if (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;
@ -127,7 +127,7 @@ struct Scene::Impl
RenderCompositor* cmp = nullptr; RenderCompositor* cmp = nullptr;
auto ret = true; auto ret = true;
renderer->blend(PP(scene)->blendMethod); renderer->blend(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));
@ -206,7 +206,7 @@ struct Scene::Impl
auto w = 0.0f; auto w = 0.0f;
auto h = 0.0f; auto h = 0.0f;
if (!P(paint)->bounds(&x, &y, &w, &h, true, stroking)) continue; if (!PAINT(paint)->bounds(&x, &y, &w, &h, true, stroking)) continue;
//Merge regions //Merge regions
if (x < x1) x1 = x; if (x < x1) x1 = x;
@ -228,7 +228,7 @@ struct Scene::Impl
if (ret) TVGERR("RENDERER", "TODO: duplicate()"); if (ret) TVGERR("RENDERER", "TODO: duplicate()");
auto scene = Scene::gen(); auto scene = Scene::gen();
auto dup = scene->pImpl; auto dup = SCENE(scene);
for (auto paint : paints) { for (auto paint : paints) {
auto cdup = paint->duplicate(); auto cdup = paint->duplicate();
@ -265,14 +265,17 @@ struct Scene::Impl
for (auto p : paints) { for (auto p : paints) {
if (p == paint) return; if (p == paint) return;
} }
TVGERR("RENDERER", "The paint(%p) is not existed from the scene(%p)", paint, scene); TVGERR("RENDERER", "The paint(%p) is not existed from the scene(%p)", paint, this->paint);
#endif #endif
} }
Result insert(Paint* target, Paint* at) Result insert(Paint* target, Paint* at)
{ {
if (!target) return Result::InvalidArguments;
target->ref();
//Relocated the paint to the current scene space //Relocated the paint to the current scene space
P(target)->renderFlag |= RenderUpdateFlag::Transform; PAINT(target)->renderFlag |= RenderUpdateFlag::Transform;
if (at == nullptr) { if (at == nullptr) {
paints.push_back(target); paints.push_back(target);
@ -290,7 +293,48 @@ struct Scene::Impl
return new SceneIterator(&paints); return new SceneIterator(&paints);
} }
Result resetEffects(); Result resetEffects()
{
if (effects) {
for (auto e = effects->begin(); e < effects->end(); ++e) {
delete(*e);
}
delete(effects);
effects = nullptr;
}
return Result::Success;
}
Result push(SceneEffect effect, va_list& args)
{
if (effect == SceneEffect::ClearAll) return resetEffects();
if (!this->effects) this->effects = new Array<RenderEffect*>;
RenderEffect* re = nullptr;
switch (effect) {
case SceneEffect::GaussianBlur: {
re = RenderEffectGaussianBlur::gen(args);
break;
}
case SceneEffect::DropShadow: {
re = RenderEffectDropShadow::gen(args);
break;
}
case SceneEffect::Fill: {
re = RenderEffectFill::gen(args);
break;
}
default: break;
}
if (!re) return Result::InvalidArguments;
this->effects->push(re);
return Result::Success;
}
}; };
#endif //_TVG_SCENE_H_ #endif //_TVG_SCENE_H_

View file

@ -23,23 +23,10 @@
#include "tvgMath.h" #include "tvgMath.h"
#include "tvgShape.h" #include "tvgShape.h"
/************************************************************************/
/* Internal Class Implementation */
/************************************************************************/
Shape :: Shape()
/************************************************************************/
/* External Class Implementation */
/************************************************************************/
Shape :: Shape() : pImpl(new Impl(this))
{ {
} pImpl = new Impl(this);
Shape :: ~Shape()
{
delete(pImpl);
} }
@ -57,301 +44,210 @@ Type Shape::type() const noexcept
Result Shape::reset() noexcept Result Shape::reset() noexcept
{ {
pImpl->rs.path.cmds.clear(); SHAPE(this)->resetPath();
pImpl->rs.path.pts.clear();
pImpl->rFlag |= RenderUpdateFlag::Path;
return Result::Success; return Result::Success;
} }
uint32_t Shape::pathCommands(const PathCommand** cmds) const noexcept uint32_t Shape::pathCommands(const PathCommand** cmds) const noexcept
{ {
if (cmds) *cmds = pImpl->rs.path.cmds.data; if (cmds) *cmds = SHAPE(this)->rs.path.cmds.data;
return pImpl->rs.path.cmds.count; return SHAPE(this)->rs.path.cmds.count;
} }
uint32_t Shape::pathCoords(const Point** pts) const noexcept uint32_t Shape::pathCoords(const Point** pts) const noexcept
{ {
if (pts) *pts = pImpl->rs.path.pts.data; if (pts) *pts = SHAPE(this)->rs.path.pts.data;
return pImpl->rs.path.pts.count; return SHAPE(this)->rs.path.pts.count;
} }
Result Shape::appendPath(const PathCommand *cmds, uint32_t cmdCnt, const Point* pts, uint32_t ptsCnt) noexcept Result Shape::appendPath(const PathCommand *cmds, uint32_t cmdCnt, const Point* pts, uint32_t ptsCnt) noexcept
{ {
if (cmdCnt == 0 || ptsCnt == 0 || !cmds || !pts) return Result::InvalidArguments; return SHAPE(this)->appendPath(cmds, cmdCnt, pts, ptsCnt);
pImpl->grow(cmdCnt, ptsCnt);
pImpl->append(cmds, cmdCnt, pts, ptsCnt);
pImpl->rFlag |= RenderUpdateFlag::Path;
return Result::Success;
} }
Result Shape::moveTo(float x, float y) noexcept Result Shape::moveTo(float x, float y) noexcept
{ {
pImpl->moveTo(x, y); SHAPE(this)->moveTo(x, y);
return Result::Success; return Result::Success;
} }
Result Shape::lineTo(float x, float y) noexcept Result Shape::lineTo(float x, float y) noexcept
{ {
pImpl->lineTo(x, y); SHAPE(this)->lineTo(x, y);
pImpl->rFlag |= RenderUpdateFlag::Path;
return Result::Success; return Result::Success;
} }
Result Shape::cubicTo(float cx1, float cy1, float cx2, float cy2, float x, float y) noexcept Result Shape::cubicTo(float cx1, float cy1, float cx2, float cy2, float x, float y) noexcept
{ {
pImpl->cubicTo(cx1, cy1, cx2, cy2, x, y); SHAPE(this)->cubicTo(cx1, cy1, cx2, cy2, x, y);
pImpl->rFlag |= RenderUpdateFlag::Path;
return Result::Success; return Result::Success;
} }
Result Shape::close() noexcept Result Shape::close() noexcept
{ {
pImpl->close(); SHAPE(this)->close();
pImpl->rFlag |= RenderUpdateFlag::Path;
return Result::Success; return Result::Success;
} }
Result Shape::appendCircle(float cx, float cy, float rx, float ry) noexcept Result Shape::appendCircle(float cx, float cy, float rx, float ry) noexcept
{ {
auto rxKappa = rx * PATH_KAPPA; SHAPE(this)->appendCircle(cx, cy, rx, ry);
auto ryKappa = ry * PATH_KAPPA;
pImpl->grow(6, 13);
pImpl->moveTo(cx + rx, cy);
pImpl->cubicTo(cx + rx, cy + ryKappa, cx + rxKappa, cy + ry, cx, cy + ry);
pImpl->cubicTo(cx - rxKappa, cy + ry, cx - rx, cy + ryKappa, cx - rx, cy);
pImpl->cubicTo(cx - rx, cy - ryKappa, cx - rxKappa, cy - ry, cx, cy - ry);
pImpl->cubicTo(cx + rxKappa, cy - ry, cx + rx, cy - ryKappa, cx + rx, cy);
pImpl->close();
pImpl->rFlag |= RenderUpdateFlag::Path;
return Result::Success; return Result::Success;
} }
Result Shape::appendRect(float x, float y, float w, float h, float rx, float ry) noexcept Result Shape::appendRect(float x, float y, float w, float h, float rx, float ry) noexcept
{ {
auto halfW = w * 0.5f; SHAPE(this)->appendRect(x, y, w, h, rx, ry);
auto halfH = h * 0.5f;
//clamping cornerRadius by minimum size
if (rx > halfW) rx = halfW;
if (ry > halfH) ry = halfH;
//rectangle
if (rx == 0 && ry == 0) {
pImpl->grow(5, 4);
pImpl->moveTo(x, y);
pImpl->lineTo(x + w, y);
pImpl->lineTo(x + w, y + h);
pImpl->lineTo(x, y + h);
pImpl->close();
//rounded rectangle or circle
} else {
auto hrx = rx * PATH_KAPPA;
auto hry = ry * PATH_KAPPA;
pImpl->grow(10, 17);
pImpl->moveTo(x + rx, y);
pImpl->lineTo(x + w - rx, y);
pImpl->cubicTo(x + w - rx + hrx, y, x + w, y + ry - hry, x + w, y + ry);
pImpl->lineTo(x + w, y + h - ry);
pImpl->cubicTo(x + w, y + h - ry + hry, x + w - rx + hrx, y + h, x + w - rx, y + h);
pImpl->lineTo(x + rx, y + h);
pImpl->cubicTo(x + rx - hrx, y + h, x, y + h - ry + hry, x, y + h - ry);
pImpl->lineTo(x, y + ry);
pImpl->cubicTo(x, y + ry - hry, x + rx - hrx, y, x + rx, y);
pImpl->close();
}
pImpl->rFlag |= RenderUpdateFlag::Path;
return Result::Success; return Result::Success;
} }
Result Shape::fill(uint8_t r, uint8_t g, uint8_t b, uint8_t a) noexcept Result Shape::fill(uint8_t r, uint8_t g, uint8_t b, uint8_t a) noexcept
{ {
if (pImpl->rs.fill) { SHAPE(this)->fill(r, g, b, a);
delete(pImpl->rs.fill);
pImpl->rs.fill = nullptr;
pImpl->rFlag |= RenderUpdateFlag::Gradient;
}
if (r == pImpl->rs.color.r && g == pImpl->rs.color.g && b == pImpl->rs.color.b && a == pImpl->rs.color.a) return Result::Success;
pImpl->rs.color = {r, g, b, a};
pImpl->rFlag |= RenderUpdateFlag::Color;
return Result::Success; return Result::Success;
} }
Result Shape::fill(Fill* f) noexcept Result Shape::fill(Fill* f) noexcept
{ {
if (!f) return Result::InvalidArguments; return SHAPE(this)->fill(f);
if (pImpl->rs.fill && pImpl->rs.fill != f) delete(pImpl->rs.fill);
pImpl->rs.fill = f;
pImpl->rFlag |= RenderUpdateFlag::Gradient;
return Result::Success;
} }
Result Shape::fillColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) const noexcept Result Shape::fillColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) const noexcept
{ {
pImpl->rs.fillColor(r, g, b, a); 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 pImpl->rs.fill; return SHAPE(this)->rs.fill;
} }
Result Shape::order(bool strokeFirst) noexcept Result Shape::order(bool strokeFirst) noexcept
{ {
pImpl->strokeFirst(strokeFirst); SHAPE(this)->strokeFirst(strokeFirst);
return Result::Success; return Result::Success;
} }
Result Shape::strokeWidth(float width) noexcept Result Shape::strokeWidth(float width) noexcept
{ {
pImpl->strokeWidth(width); SHAPE(this)->strokeWidth(width);
return Result::Success; return Result::Success;
} }
float Shape::strokeWidth() const noexcept float Shape::strokeWidth() const noexcept
{ {
return pImpl->rs.strokeWidth(); return SHAPE(this)->rs.strokeWidth();
} }
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) noexcept
{ {
pImpl->strokeFill(r, g, b, a); SHAPE(this)->strokeFill(r, g, b, a);
return Result::Success; return Result::Success;
} }
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 (!pImpl->rs.strokeFill(r, g, b, a)) return Result::InsufficientCondition; if (!SHAPE(this)->rs.strokeFill(r, g, b, a)) return Result::InsufficientCondition;
return Result::Success; return Result::Success;
} }
Result Shape::strokeFill(Fill* f) noexcept Result Shape::strokeFill(Fill* f) noexcept
{ {
return pImpl->strokeFill(f); return SHAPE(this)->strokeFill(f);
} }
const Fill* Shape::strokeFill() const noexcept const Fill* Shape::strokeFill() const noexcept
{ {
return pImpl->rs.strokeFill(); return SHAPE(this)->rs.strokeFill();
} }
Result Shape::strokeDash(const float* dashPattern, uint32_t cnt, float offset) noexcept Result Shape::strokeDash(const float* dashPattern, uint32_t cnt, float offset) noexcept
{ {
return pImpl->strokeDash(dashPattern, cnt, offset); return SHAPE(this)->strokeDash(dashPattern, cnt, offset);
} }
uint32_t Shape::strokeDash(const float** dashPattern, float* offset) const noexcept uint32_t Shape::strokeDash(const float** dashPattern, float* offset) const noexcept
{ {
return pImpl->rs.strokeDash(dashPattern, offset); return SHAPE(this)->rs.strokeDash(dashPattern, offset);
} }
Result Shape::strokeCap(StrokeCap cap) noexcept Result Shape::strokeCap(StrokeCap cap) noexcept
{ {
pImpl->strokeCap(cap); SHAPE(this)->strokeCap(cap);
return Result::Success; return Result::Success;
} }
Result Shape::strokeJoin(StrokeJoin join) noexcept Result Shape::strokeJoin(StrokeJoin join) noexcept
{ {
pImpl->strokeJoin(join); SHAPE(this)->strokeJoin(join);
return Result::Success; return Result::Success;
} }
Result Shape::strokeMiterlimit(float miterlimit) noexcept Result Shape::strokeMiterlimit(float miterlimit) noexcept
{ {
// https://www.w3.org/TR/SVG2/painting.html#LineJoin return SHAPE(this)->strokeMiterlimit(miterlimit);
// - A negative value for stroke-miterlimit must be treated as an illegal value.
if (miterlimit < 0.0f) return Result::InvalidArguments;
// TODO Find out a reasonable max value.
pImpl->strokeMiterlimit(miterlimit);
return Result::Success;
} }
StrokeCap Shape::strokeCap() const noexcept StrokeCap Shape::strokeCap() const noexcept
{ {
return pImpl->rs.strokeCap(); return SHAPE(this)->rs.strokeCap();
} }
StrokeJoin Shape::strokeJoin() const noexcept StrokeJoin Shape::strokeJoin() const noexcept
{ {
return pImpl->rs.strokeJoin(); return SHAPE(this)->rs.strokeJoin();
} }
float Shape::strokeMiterlimit() const noexcept float Shape::strokeMiterlimit() const noexcept
{ {
return pImpl->rs.strokeMiterlimit(); return SHAPE(this)->rs.strokeMiterlimit();
} }
Result Shape::strokeTrim(float begin, float end, bool simultaneous) noexcept Result Shape::strokeTrim(float begin, float end, bool simultaneous) noexcept
{ {
pImpl->strokeTrim(begin, end, simultaneous); SHAPE(this)->strokeTrim(begin, end, simultaneous);
return Result::Success; return Result::Success;
} }
Result Shape::fill(FillRule r) noexcept Result Shape::fill(FillRule r) noexcept
{ {
pImpl->rs.rule = r; SHAPE(this)->rs.rule = r;
return Result::Success; return Result::Success;
} }
FillRule Shape::fillRule() const noexcept FillRule Shape::fillRule() const noexcept
{ {
return pImpl->rs.rule; return SHAPE(this)->rs.rule;
} }

View file

@ -27,25 +27,22 @@
#include "tvgMath.h" #include "tvgMath.h"
#include "tvgPaint.h" #include "tvgPaint.h"
#define SHAPE(A) PIMPL(A, Shape)
struct Shape::Impl struct Shape::Impl : Paint::Impl
{ {
RenderShape rs; //shape data RenderShape rs;
RenderData rd = nullptr; //engine data RenderData rd = nullptr;
Shape* shape; uint8_t compFlag = CompositionFlag::Invalid;
uint8_t rFlag = RenderUpdateFlag::None;
uint8_t cFlag = CompositionFlag::Invalid;
uint8_t opacity; //for composition uint8_t opacity; //for composition
Impl(Shape* s) : shape(s) Impl(Shape* s) : Paint::Impl(s)
{ {
} }
~Impl() ~Impl()
{ {
if (auto renderer = PP(shape)->renderer) { if (renderer) renderer->dispose(rd);
renderer->dispose(rd);
}
} }
bool render(RenderMethod* renderer) bool render(RenderMethod* renderer)
@ -54,10 +51,10 @@ struct Shape::Impl
RenderCompositor* cmp = nullptr; RenderCompositor* cmp = nullptr;
renderer->blend(PP(shape)->blendMethod); renderer->blend(blendMethod);
if (cFlag) { if (compFlag) {
cmp = renderer->target(bounds(renderer), renderer->colorSpace(), static_cast<CompositionFlag>(cFlag)); cmp = renderer->target(bounds(renderer), renderer->colorSpace(), static_cast<CompositionFlag>(compFlag));
renderer->beginComposite(cmp, MaskMethod::None, opacity); renderer->beginComposite(cmp, MaskMethod::None, opacity);
} }
@ -68,7 +65,7 @@ struct Shape::Impl
bool needComposition(uint8_t opacity) bool needComposition(uint8_t opacity)
{ {
cFlag = CompositionFlag::Invalid; compFlag = CompositionFlag::Invalid;
if (opacity == 0) return false; if (opacity == 0) return false;
@ -78,13 +75,13 @@ struct Shape::Impl
//translucent fill & stroke //translucent fill & stroke
if (opacity < 255) { if (opacity < 255) {
cFlag = CompositionFlag::Opacity; compFlag = CompositionFlag::Opacity;
return true; return true;
} }
//Composition test //Composition test
const Paint* target; const Paint* target;
auto method = shape->mask(&target); auto method = paint->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() == Type::Shape) {
@ -100,13 +97,13 @@ struct Shape::Impl
} }
} }
cFlag = CompositionFlag::Masking; compFlag = CompositionFlag::Masking;
return true; return true;
} }
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 | rFlag) == RenderUpdateFlag::None) return rd; if (static_cast<RenderUpdateFlag>(pFlag | renderFlag) == RenderUpdateFlag::None) return 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,
@ -115,8 +112,7 @@ struct Shape::Impl
opacity = 255; opacity = 255;
} }
rd = renderer->prepare(rs, rd, transform, clips, opacity, static_cast<RenderUpdateFlag>(pFlag | rFlag), clipper); rd = renderer->prepare(rs, rd, transform, clips, opacity, static_cast<RenderUpdateFlag>(pFlag | renderFlag), clipper);
rFlag = RenderUpdateFlag::None;
return rd; return rd;
} }
@ -191,6 +187,7 @@ struct Shape::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;
} }
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)
@ -199,21 +196,23 @@ struct Shape::Impl
rs.path.pts.push({cx1, cy1}); rs.path.pts.push({cx1, cy1});
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;
} }
void close() void close()
{ {
//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;
} }
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;
rFlag |= RenderUpdateFlag::Stroke; renderFlag |= RenderUpdateFlag::Stroke;
} }
void strokeTrim(float begin, float end, bool simultaneous) void strokeTrim(float begin, float end, bool simultaneous)
@ -223,13 +222,12 @@ struct Shape::Impl
rs.stroke = new RenderStroke(); rs.stroke = new RenderStroke();
} }
if (tvg::equal(rs.stroke->trim.begin, begin) && tvg::equal(rs.stroke->trim.end, end) && if (tvg::equal(rs.stroke->trim.begin, begin) && tvg::equal(rs.stroke->trim.end, end) && rs.stroke->trim.simultaneous == simultaneous) return;
rs.stroke->trim.simultaneous == simultaneous) return;
rs.stroke->trim.begin = begin; rs.stroke->trim.begin = begin;
rs.stroke->trim.end = end; rs.stroke->trim.end = end;
rs.stroke->trim.simultaneous = simultaneous; rs.stroke->trim.simultaneous = simultaneous;
rFlag |= RenderUpdateFlag::Stroke; renderFlag |= RenderUpdateFlag::Stroke;
} }
bool strokeTrim(float* begin, float* end) bool strokeTrim(float* begin, float* end)
@ -249,21 +247,26 @@ struct Shape::Impl
{ {
if (!rs.stroke) rs.stroke = new RenderStroke(); if (!rs.stroke) rs.stroke = new RenderStroke();
rs.stroke->cap = cap; rs.stroke->cap = cap;
rFlag |= RenderUpdateFlag::Stroke; renderFlag |= 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;
rFlag |= RenderUpdateFlag::Stroke; renderFlag |= RenderUpdateFlag::Stroke;
} }
void strokeMiterlimit(float miterlimit) Result strokeMiterlimit(float miterlimit)
{ {
// https://www.w3.org/TR/SVG2/painting.html#LineJoin
// - A negative value for stroke-miterlimit must be treated as an illegal value.
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;
rFlag |= RenderUpdateFlag::Stroke; renderFlag |= RenderUpdateFlag::Stroke;
return Result::Success;
} }
void strokeFill(uint8_t r, uint8_t g, uint8_t b, uint8_t a) void strokeFill(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
@ -272,12 +275,12 @@ struct Shape::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;
rFlag |= RenderUpdateFlag::GradientStroke; renderFlag |= RenderUpdateFlag::GradientStroke;
} }
rs.stroke->color = {r, g, b, a}; rs.stroke->color = {r, g, b, a};
rFlag |= RenderUpdateFlag::Stroke; renderFlag |= RenderUpdateFlag::Stroke;
} }
Result strokeFill(Fill* f) Result strokeFill(Fill* f)
@ -289,8 +292,8 @@ struct Shape::Impl
rs.stroke->fill = f; rs.stroke->fill = f;
rs.stroke->color.a = 0; rs.stroke->color.a = 0;
rFlag |= RenderUpdateFlag::Stroke; renderFlag |= RenderUpdateFlag::Stroke;
rFlag |= RenderUpdateFlag::GradientStroke; renderFlag |= RenderUpdateFlag::GradientStroke;
return Result::Success; return Result::Success;
} }
@ -325,7 +328,7 @@ struct Shape::Impl
} }
rs.stroke->dashCnt = cnt; rs.stroke->dashCnt = cnt;
rs.stroke->dashOffset = offset; rs.stroke->dashOffset = offset;
rFlag |= RenderUpdateFlag::Stroke; renderFlag |= RenderUpdateFlag::Stroke;
return Result::Success; return Result::Success;
} }
@ -340,12 +343,99 @@ struct Shape::Impl
{ {
if (!rs.stroke) rs.stroke = new RenderStroke(); if (!rs.stroke) rs.stroke = new RenderStroke();
rs.stroke->strokeFirst = strokeFirst; rs.stroke->strokeFirst = strokeFirst;
rFlag |= RenderUpdateFlag::Stroke; renderFlag |= RenderUpdateFlag::Stroke;
} }
void update(RenderUpdateFlag flag) Result fill(Fill* f)
{ {
rFlag |= flag; if (!f) return Result::InvalidArguments;
if (rs.fill && rs.fill != f) delete(rs.fill);
rs.fill = f;
renderFlag |= RenderUpdateFlag::Gradient;
return Result::Success;
}
void fill(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
{
if (rs.fill) {
delete(rs.fill);
rs.fill = nullptr;
renderFlag |= 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;
}
void resetPath()
{
rs.path.cmds.clear();
rs.path.pts.clear();
renderFlag |= RenderUpdateFlag::Path;
}
Result appendPath(const PathCommand *cmds, uint32_t cmdCnt, const Point* pts, uint32_t ptsCnt)
{
if (cmdCnt == 0 || ptsCnt == 0 || !cmds || !pts) return Result::InvalidArguments;
grow(cmdCnt, ptsCnt);
append(cmds, cmdCnt, pts, ptsCnt);
renderFlag |= RenderUpdateFlag::Path;
return Result::Success;
}
void appendCircle(float cx, float cy, float rx, float ry)
{
auto rxKappa = rx * PATH_KAPPA;
auto ryKappa = ry * PATH_KAPPA;
grow(6, 13);
moveTo(cx + rx, cy);
cubicTo(cx + rx, cy + ryKappa, cx + rxKappa, cy + ry, cx, cy + ry);
cubicTo(cx - rxKappa, cy + ry, cx - rx, cy + ryKappa, cx - rx, cy);
cubicTo(cx - rx, cy - ryKappa, cx - rxKappa, cy - ry, cx, cy - ry);
cubicTo(cx + rxKappa, cy - ry, cx + rx, cy - ryKappa, cx + rx, cy);
close();
}
void appendRect(float x, float y, float w, float h, float rx, float ry)
{
auto halfW = w * 0.5f;
auto halfH = h * 0.5f;
//clamping cornerRadius by minimum size
if (rx > halfW) rx = halfW;
if (ry > halfH) ry = halfH;
//rectangle
if (rx == 0 && ry == 0) {
grow(5, 4);
moveTo(x, y);
lineTo(x + w, y);
lineTo(x + w, y + h);
lineTo(x, y + h);
close();
//rounded rectangle or circle
} else {
auto hrx = rx * PATH_KAPPA;
auto hry = ry * PATH_KAPPA;
grow(10, 17);
moveTo(x + rx, y);
lineTo(x + w - rx, y);
cubicTo(x + w - rx + hrx, y, x + w, y + ry - hry, x + w, y + ry);
lineTo(x + w, y + h - ry);
cubicTo(x + w, y + h - ry + hry, x + w - rx + hrx, y + h, x + w - rx, y + h);
lineTo(x + rx, y + h);
cubicTo(x + rx - hrx, y + h, x, y + h - ry + hry, x, y + h - ry);
lineTo(x, y + ry);
cubicTo(x, y + ry - hry, x + rx - hrx, y, x + rx, y);
close();
}
} }
Paint* duplicate(Paint* ret) Paint* duplicate(Paint* ret)
@ -354,11 +444,11 @@ struct Shape::Impl
if (shape) shape->reset(); if (shape) shape->reset();
else shape = Shape::gen(); else shape = Shape::gen();
auto dup = shape->pImpl; auto dup = SHAPE(shape);
delete(dup->rs.fill); delete(dup->rs.fill);
//Default Properties //Default Properties
dup->rFlag = RenderUpdateFlag::All; dup->renderFlag = RenderUpdateFlag::All;
dup->rs.rule = rs.rule; dup->rs.rule = rs.rule;
dup->rs.color = rs.color; dup->rs.color = rs.color;
@ -384,7 +474,7 @@ struct Shape::Impl
void reset() void reset()
{ {
PP(shape)->reset(); PAINT(paint)->reset();
rs.path.cmds.clear(); rs.path.cmds.clear();
rs.path.pts.clear(); rs.path.pts.clear();

View file

@ -25,38 +25,21 @@
#ifdef THORVG_SW_RASTER_SUPPORT #ifdef THORVG_SW_RASTER_SUPPORT
#include "tvgSwRenderer.h" #include "tvgSwRenderer.h"
#else
class SwRenderer : public RenderMethod
{
//Non Supported. Dummy Class */
};
#endif #endif
/************************************************************************/
/* Internal Class Implementation */
/************************************************************************/
struct SwCanvas::Impl SwCanvas::SwCanvas()
{ {
};
/************************************************************************/
/* External Class Implementation */
/************************************************************************/
#ifdef THORVG_SW_RASTER_SUPPORT #ifdef THORVG_SW_RASTER_SUPPORT
SwCanvas::SwCanvas() : Canvas(SwRenderer::gen()), pImpl(nullptr) pImpl->renderer = SwRenderer::gen();
#else pImpl->renderer->ref();
SwCanvas::SwCanvas() : Canvas(nullptr), pImpl(nullptr)
#endif #endif
{
} }
SwCanvas::~SwCanvas() SwCanvas::~SwCanvas()
{ {
delete(pImpl); //TODO:
} }
@ -64,11 +47,11 @@ Result SwCanvas::mempool(MempoolPolicy policy) noexcept
{ {
#ifdef THORVG_SW_RASTER_SUPPORT #ifdef THORVG_SW_RASTER_SUPPORT
//We know renderer type, avoid dynamic_cast for performance. //We know renderer type, avoid dynamic_cast for performance.
auto renderer = static_cast<SwRenderer*>(Canvas::pImpl->renderer); auto renderer = static_cast<SwRenderer*>(pImpl->renderer);
if (!renderer) return Result::MemoryCorruption; if (!renderer) return Result::MemoryCorruption;
//It can't change the policy during the running. //It can't change the policy during the running.
if (!Canvas::pImpl->scene->paints().empty()) return Result::InsufficientCondition; if (!pImpl->scene->paints().empty()) return Result::InsufficientCondition;
if (policy == MempoolPolicy::Individual) renderer->mempool(false); if (policy == MempoolPolicy::Individual) renderer->mempool(false);
else renderer->mempool(true); else renderer->mempool(true);
@ -85,23 +68,23 @@ Result SwCanvas::target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t
if (cs == ColorSpace::Unknown) return Result::InvalidArguments; if (cs == ColorSpace::Unknown) return Result::InvalidArguments;
if (cs == ColorSpace::Grayscale8) return Result::NonSupport; if (cs == ColorSpace::Grayscale8) return Result::NonSupport;
if (Canvas::pImpl->status != Status::Damaged && Canvas::pImpl->status != Status::Synced) { if (pImpl->status != Status::Damaged && pImpl->status != Status::Synced) {
return Result::InsufficientCondition; return Result::InsufficientCondition;
} }
//We know renderer type, avoid dynamic_cast for performance. //We know renderer type, avoid dynamic_cast for performance.
auto renderer = static_cast<SwRenderer*>(Canvas::pImpl->renderer); auto renderer = static_cast<SwRenderer*>(pImpl->renderer);
if (!renderer) return Result::MemoryCorruption; if (!renderer) return Result::MemoryCorruption;
if (!renderer->target(buffer, stride, w, h, static_cast<ColorSpace>(cs))) return Result::InvalidArguments; if (!renderer->target(buffer, stride, w, h, static_cast<ColorSpace>(cs))) return Result::InvalidArguments;
Canvas::pImpl->vport = {0, 0, (int32_t)w, (int32_t)h}; pImpl->vport = {0, 0, (int32_t)w, (int32_t)h};
renderer->viewport(Canvas::pImpl->vport); renderer->viewport(pImpl->vport);
//FIXME: The value must be associated with an individual canvas instance. //FIXME: The value must be associated with an individual canvas instance.
ImageLoader::cs = static_cast<ColorSpace>(cs); ImageLoader::cs = static_cast<ColorSpace>(cs);
//Paints must be updated again with this new target. //Paints must be updated again with this new target.
Canvas::pImpl->status = Status::Damaged; pImpl->status = Status::Damaged;
return Result::Success; return Result::Success;
#endif #endif

View file

@ -24,37 +24,21 @@
#include "tvgText.h" #include "tvgText.h"
/************************************************************************/ Text::Text()
/* Internal Class Implementation */
/************************************************************************/
/************************************************************************/
/* External Class Implementation */
/************************************************************************/
Text::Text() : pImpl(new Impl(this))
{ {
} pImpl = new Impl(this);
Text::~Text()
{
delete(pImpl);
} }
Result Text::text(const char* text) noexcept Result Text::text(const char* text) noexcept
{ {
return pImpl->text(text); return TEXT(this)->text(text);
} }
Result Text::font(const char* name, float size, const char* style) noexcept Result Text::font(const char* name, float size, const char* style) noexcept
{ {
return pImpl->font(name, size, style); return TEXT(this)->font(name, size, style);
} }
@ -103,13 +87,13 @@ Result Text::unload(const char* filename) noexcept
Result Text::fill(uint8_t r, uint8_t g, uint8_t b) noexcept Result Text::fill(uint8_t r, uint8_t g, uint8_t b) noexcept
{ {
return pImpl->shape->fill(r, g, b); return TEXT(this)->shape->fill(r, g, b);
} }
Result Text::fill(Fill* f) noexcept Result Text::fill(Fill* f) noexcept
{ {
return pImpl->shape->fill(f); return TEXT(this)->shape->fill(f);
} }

View file

@ -28,17 +28,18 @@
#include "tvgFill.h" #include "tvgFill.h"
#include "tvgLoader.h" #include "tvgLoader.h"
struct Text::Impl #define TEXT(A) PIMPL(A, Text)
struct Text::Impl : Paint::Impl
{ {
Shape* shape; //text shape
FontLoader* loader = nullptr; FontLoader* loader = nullptr;
Text* paint;
Shape* shape;
char* utf8 = nullptr; char* utf8 = nullptr;
float fontSize; float fontSize;
bool italic = false; bool italic = false;
bool changed = false; bool changed = false;
Impl(Text* p) : paint(p), shape(Shape::gen()) Impl(Text* p) : Paint::Impl(p), shape(Shape::gen())
{ {
} }
@ -84,14 +85,14 @@ struct Text::Impl
RenderRegion bounds(RenderMethod* renderer) RenderRegion bounds(RenderMethod* renderer)
{ {
return P(shape)->bounds(renderer); return SHAPE(shape)->bounds(renderer);
} }
bool render(RenderMethod* renderer) bool render(RenderMethod* renderer)
{ {
if (!loader) return true; if (!loader) return true;
renderer->blend(PP(paint)->blendMethod); renderer->blend(blendMethod);
return PP(shape)->render(renderer); return PAINT(shape)->render(renderer);
} }
bool load() bool load()
@ -112,30 +113,31 @@ struct Text::Impl
if (!load()) return nullptr; if (!load()) return nullptr;
//transform the gradient coordinates based on the final scaled font. //transform the gradient coordinates based on the final scaled font.
auto fill = P(shape)->rs.fill; auto fill = SHAPE(shape)->rs.fill;
if (fill && P(shape)->rFlag & RenderUpdateFlag::Gradient) { if (fill && SHAPE(shape)->renderFlag & RenderUpdateFlag::Gradient) {
auto scale = 1.0f / loader->scale; auto scale = 1.0f / loader->scale;
if (fill->type() == Type::LinearGradient) { if (fill->type() == Type::LinearGradient) {
P(static_cast<LinearGradient*>(fill))->x1 *= scale; LINEAR(fill)->x1 *= scale;
P(static_cast<LinearGradient*>(fill))->y1 *= scale; LINEAR(fill)->y1 *= scale;
P(static_cast<LinearGradient*>(fill))->x2 *= scale; LINEAR(fill)->x2 *= scale;
P(static_cast<LinearGradient*>(fill))->y2 *= scale; LINEAR(fill)->y2 *= scale;
} else { } else {
P(static_cast<RadialGradient*>(fill))->cx *= scale; RADIAL(fill)->cx *= scale;
P(static_cast<RadialGradient*>(fill))->cy *= scale; RADIAL(fill)->cy *= scale;
P(static_cast<RadialGradient*>(fill))->r *= scale; RADIAL(fill)->r *= scale;
P(static_cast<RadialGradient*>(fill))->fx *= scale; RADIAL(fill)->fx *= scale;
P(static_cast<RadialGradient*>(fill))->fy *= scale; RADIAL(fill)->fy *= scale;
P(static_cast<RadialGradient*>(fill))->fr *= scale; RADIAL(fill)->fr *= scale;
} }
} }
return PP(shape)->update(renderer, transform, clips, opacity, pFlag, false);
return PAINT(shape)->update(renderer, transform, clips, opacity, pFlag, false);
} }
bool bounds(float* x, float* y, float* w, float* h, TVG_UNUSED bool stroking) bool bounds(float* x, float* y, float* w, float* h, TVG_UNUSED bool stroking)
{ {
if (!load()) return false; if (!load()) return false;
PP(shape)->bounds(x, y, w, h, true, true, false); PAINT(shape)->bounds(x, y, w, h, true, true, false);
return true; return true;
} }
@ -146,8 +148,8 @@ struct Text::Impl
load(); load();
auto text = Text::gen(); auto text = Text::gen();
auto dup = text->pImpl; auto dup = TEXT(text);
P(shape)->duplicate(dup->shape); SHAPE(shape)->duplicate(dup->shape);
if (loader) { if (loader) {
dup->loader = loader; dup->loader = loader;

View file

@ -26,32 +26,20 @@
#include "tvgWgRenderer.h" #include "tvgWgRenderer.h"
#endif #endif
/************************************************************************/
/* Internal Class Implementation */
/************************************************************************/
struct WgCanvas::Impl WgCanvas::WgCanvas()
{ {
};
/************************************************************************/
/* External Class Implementation */
/************************************************************************/
#ifdef THORVG_WG_RASTER_SUPPORT #ifdef THORVG_WG_RASTER_SUPPORT
WgCanvas::WgCanvas() : Canvas(WgRenderer::gen()), pImpl(nullptr) pImpl->renderer = WgRenderer::gen();
#else pImpl->renderer->ref();
WgCanvas::WgCanvas() : Canvas(nullptr), pImpl(nullptr)
#endif #endif
{
} }
WgCanvas::~WgCanvas() WgCanvas::~WgCanvas()
{ {
#ifdef THORVG_WG_RASTER_SUPPORT #ifdef THORVG_WG_RASTER_SUPPORT
auto renderer = static_cast<WgRenderer*>(Canvas::pImpl->renderer); auto renderer = static_cast<WgRenderer*>(pImpl->renderer);
renderer->target(nullptr, nullptr, nullptr, 0, 0); renderer->target(nullptr, nullptr, nullptr, 0, 0);
#endif #endif
} }
@ -62,20 +50,20 @@ Result WgCanvas::target(void* device, void* instance, void* target, uint32_t w,
#ifdef THORVG_WG_RASTER_SUPPORT #ifdef THORVG_WG_RASTER_SUPPORT
if (cs != ColorSpace::ABGR8888S) return Result::NonSupport; if (cs != ColorSpace::ABGR8888S) return Result::NonSupport;
if (Canvas::pImpl->status != Status::Damaged && Canvas::pImpl->status != Status::Synced) { if (pImpl->status != Status::Damaged && pImpl->status != Status::Synced) {
return Result::InsufficientCondition; return Result::InsufficientCondition;
} }
//We know renderer type, avoid dynamic_cast for performance. //We know renderer type, avoid dynamic_cast for performance.
auto renderer = static_cast<WgRenderer*>(Canvas::pImpl->renderer); auto renderer = static_cast<WgRenderer*>(pImpl->renderer);
if (!renderer) return Result::MemoryCorruption; if (!renderer) return Result::MemoryCorruption;
if (!renderer->target((WGPUDevice)device, (WGPUInstance)instance, target, w, h, type)) return Result::Unknown; if (!renderer->target((WGPUDevice)device, (WGPUInstance)instance, target, w, h, type)) return Result::Unknown;
Canvas::pImpl->vport = {0, 0, (int32_t)w, (int32_t)h}; pImpl->vport = {0, 0, (int32_t)w, (int32_t)h};
renderer->viewport(Canvas::pImpl->vport); renderer->viewport(pImpl->vport);
//Paints must be updated again with this new target. //Paints must be updated again with this new target.
Canvas::pImpl->status = Status::Damaged; pImpl->status = Status::Damaged;
return Result::Success; return Result::Success;
#endif #endif

View file

@ -39,7 +39,7 @@ public:
WGPUBindGroupLayout layoutBuffer1Un{}; WGPUBindGroupLayout layoutBuffer1Un{};
WGPUBindGroupLayout layoutBuffer2Un{}; WGPUBindGroupLayout layoutBuffer2Un{};
WGPUBindGroupLayout layoutBuffer3Un{}; WGPUBindGroupLayout layoutBuffer3Un{};
public:
WGPUBindGroup createBindGroupTexSampled(WGPUSampler sampler, WGPUTextureView texView); WGPUBindGroup createBindGroupTexSampled(WGPUSampler sampler, WGPUTextureView texView);
WGPUBindGroup createBindGroupTexSampledBuff1Un(WGPUSampler sampler, WGPUTextureView texView, WGPUBuffer buff); WGPUBindGroup createBindGroupTexSampledBuff1Un(WGPUSampler sampler, WGPUTextureView texView, WGPUBuffer buff);
WGPUBindGroup createBindGroupTexSampledBuff2Un(WGPUSampler sampler, WGPUTextureView texView, WGPUBuffer buff0, WGPUBuffer buff1); WGPUBindGroup createBindGroupTexSampledBuff2Un(WGPUSampler sampler, WGPUTextureView texView, WGPUBuffer buff0, WGPUBuffer buff1);
@ -52,7 +52,7 @@ public:
WGPUBindGroup createBindGroupBuffer3Un(WGPUBuffer buff0, WGPUBuffer buff1, WGPUBuffer buff2); WGPUBindGroup createBindGroupBuffer3Un(WGPUBuffer buff0, WGPUBuffer buff1, WGPUBuffer buff2);
void releaseBindGroup(WGPUBindGroup& bindGroup); void releaseBindGroup(WGPUBindGroup& bindGroup);
void releaseBindGroupLayout(WGPUBindGroupLayout& bindGroupLayout); void releaseBindGroupLayout(WGPUBindGroupLayout& bindGroupLayout);
public:
void initialize(WGPUDevice device); void initialize(WGPUDevice device);
void release(); void release();
}; };

View file

@ -62,6 +62,29 @@ private:
// viewport utilities // viewport utilities
RenderRegion shrinkRenderRegion(RenderRegion& rect); RenderRegion shrinkRenderRegion(RenderRegion& rect);
// shapes
void drawShape(WgContext& context, WgRenderDataShape* renderData);
void blendShape(WgContext& context, WgRenderDataShape* renderData, BlendMethod blendMethod);
void clipShape(WgContext& context, WgRenderDataShape* renderData);
// strokes
void drawStrokes(WgContext& context, WgRenderDataShape* renderData);
void blendStrokes(WgContext& context, WgRenderDataShape* renderData, BlendMethod blendMethod);
void clipStrokes(WgContext& context, WgRenderDataShape* renderData);
// images
void drawImage(WgContext& context, WgRenderDataPicture* renderData);
void blendImage(WgContext& context, WgRenderDataPicture* renderData, BlendMethod blendMethod);
void clipImage(WgContext& context, WgRenderDataPicture* renderData);
// scenes
void drawScene(WgContext& context, WgRenderStorage* scene, WgCompose* compose);
void blendScene(WgContext& context, WgRenderStorage* src, WgCompose* compose);
void renderClipPath(WgContext& context, WgRenderDataPaint* paint);
void clearClipPath(WgContext& context, WgRenderDataPaint* paint);
public: public:
void initialize(WgContext& context, uint32_t width, uint32_t height); void initialize(WgContext& context, uint32_t width, uint32_t height);
void initPools(WgContext& context); void initPools(WgContext& context);
@ -81,28 +104,6 @@ public:
// blit render storage to texture view (f.e. screen buffer) // blit render storage to texture view (f.e. screen buffer)
void blit(WgContext& context, WGPUCommandEncoder encoder, WgRenderStorage* src, WGPUTextureView dstView); void blit(WgContext& context, WGPUCommandEncoder encoder, WgRenderStorage* src, WGPUTextureView dstView);
private:
// shapes
void drawShape(WgContext& context, WgRenderDataShape* renderData);
void blendShape(WgContext& context, WgRenderDataShape* renderData, BlendMethod blendMethod);
void clipShape(WgContext& context, WgRenderDataShape* renderData);
// strokes
void drawStrokes(WgContext& context, WgRenderDataShape* renderData);
void blendStrokes(WgContext& context, WgRenderDataShape* renderData, BlendMethod blendMethod);
void clipStrokes(WgContext& context, WgRenderDataShape* renderData);
// images
void drawImage(WgContext& context, WgRenderDataPicture* renderData);
void blendImage(WgContext& context, WgRenderDataPicture* renderData, BlendMethod blendMethod);
void clipImage(WgContext& context, WgRenderDataPicture* renderData);
// scenes
void drawScene(WgContext& context, WgRenderStorage* scene, WgCompose* compose);
void blendScene(WgContext& context, WgRenderStorage* src, WgCompose* compose);
private:
void renderClipPath(WgContext& context, WgRenderDataPaint* paint);
void clearClipPath(WgContext& context, WgRenderDataPaint* paint);
}; };
#endif // _TVG_WG_COMPOSITOR_H_ #endif // _TVG_WG_COMPOSITOR_H_

View file

@ -46,7 +46,7 @@ private:
WGPUShaderModule shader_scene_compose{}; WGPUShaderModule shader_scene_compose{};
// shader blit // shader blit
WGPUShaderModule shader_blit{}; WGPUShaderModule shader_blit{};
private:
// layouts helpers // layouts helpers
WGPUPipelineLayout layout_stencil{}; WGPUPipelineLayout layout_stencil{};
WGPUPipelineLayout layout_depth{}; WGPUPipelineLayout layout_depth{};
@ -93,7 +93,6 @@ public:
WGPURenderPipeline blit{}; WGPURenderPipeline blit{};
private: private:
void releaseGraphicHandles(WgContext& context); void releaseGraphicHandles(WgContext& context);
private:
WGPUShaderModule createShaderModule(WGPUDevice device, const char* label, const char* code); WGPUShaderModule createShaderModule(WGPUDevice device, const char* label, const char* code);
WGPUPipelineLayout createPipelineLayout(WGPUDevice device, const WGPUBindGroupLayout* bindGroupLayouts, const uint32_t bindGroupLayoutsCount); WGPUPipelineLayout createPipelineLayout(WGPUDevice device, const WGPUBindGroupLayout* bindGroupLayouts, const uint32_t bindGroupLayoutsCount);
WGPURenderPipeline createRenderPipeline( WGPURenderPipeline createRenderPipeline(