mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-07 21:23:32 +00:00
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:
parent
01f4d6304a
commit
1806b32971
47 changed files with 895 additions and 1080 deletions
33
inc/thorvg.h
33
inc/thorvg.h
|
@ -43,12 +43,16 @@
|
|||
|
||||
#define _TVG_DECLARE_PRIVATE(A) \
|
||||
struct Impl; \
|
||||
Impl* pImpl; \
|
||||
protected: \
|
||||
A(const A&) = delete; \
|
||||
const A& operator=(const A&) = delete; \
|
||||
A()
|
||||
|
||||
#define _TVG_DECLARE_PRIVATE_BASE(A) \
|
||||
_TVG_DECLARE_PRIVATE(A); \
|
||||
public: \
|
||||
Impl* pImpl
|
||||
|
||||
#define _TVG_DISABLE_CTOR(A) \
|
||||
A() = delete; \
|
||||
~A() = delete
|
||||
|
@ -515,7 +519,7 @@ public:
|
|||
*/
|
||||
uint32_t id = 0;
|
||||
|
||||
_TVG_DECLARE_PRIVATE(Paint);
|
||||
_TVG_DECLARE_PRIVATE_BASE(Paint);
|
||||
};
|
||||
|
||||
|
||||
|
@ -616,7 +620,7 @@ public:
|
|||
*/
|
||||
virtual Type type() const noexcept = 0;
|
||||
|
||||
_TVG_DECLARE_PRIVATE(Fill);
|
||||
_TVG_DECLARE_PRIVATE_BASE(Fill);
|
||||
};
|
||||
|
||||
|
||||
|
@ -633,7 +637,6 @@ public:
|
|||
class TVG_API Canvas
|
||||
{
|
||||
public:
|
||||
Canvas(RenderMethod*);
|
||||
virtual ~Canvas();
|
||||
|
||||
/**
|
||||
|
@ -749,7 +752,7 @@ public:
|
|||
*/
|
||||
Result sync() noexcept;
|
||||
|
||||
_TVG_DECLARE_PRIVATE(Canvas);
|
||||
_TVG_DECLARE_PRIVATE_BASE(Canvas);
|
||||
};
|
||||
|
||||
|
||||
|
@ -764,8 +767,6 @@ public:
|
|||
class TVG_API LinearGradient final : public Fill
|
||||
{
|
||||
public:
|
||||
~LinearGradient();
|
||||
|
||||
/**
|
||||
* @brief Sets the linear gradient bounds.
|
||||
*
|
||||
|
@ -828,8 +829,6 @@ public:
|
|||
class TVG_API RadialGradient final : public Fill
|
||||
{
|
||||
public:
|
||||
~RadialGradient();
|
||||
|
||||
/**
|
||||
* @brief Sets the radial gradient attributes.
|
||||
*
|
||||
|
@ -906,8 +905,6 @@ public:
|
|||
class TVG_API Shape final : public Paint
|
||||
{
|
||||
public:
|
||||
~Shape();
|
||||
|
||||
/**
|
||||
* @brief Resets the shape path.
|
||||
*
|
||||
|
@ -1287,8 +1284,6 @@ public:
|
|||
class TVG_API Picture final : public Paint
|
||||
{
|
||||
public:
|
||||
~Picture();
|
||||
|
||||
/**
|
||||
* @brief Loads a picture data directly from a file.
|
||||
*
|
||||
|
@ -1420,8 +1415,6 @@ public:
|
|||
class TVG_API Scene final : public Paint
|
||||
{
|
||||
public:
|
||||
~Scene();
|
||||
|
||||
/**
|
||||
* @brief Inserts a paint object to the scene.
|
||||
*
|
||||
|
@ -1519,8 +1512,6 @@ public:
|
|||
class TVG_API Text final : public Paint
|
||||
{
|
||||
public:
|
||||
~Text();
|
||||
|
||||
/**
|
||||
* @brief Sets the font properties for the text.
|
||||
*
|
||||
|
@ -1902,7 +1893,7 @@ public:
|
|||
class TVG_API Animation
|
||||
{
|
||||
public:
|
||||
~Animation();
|
||||
virtual ~Animation();
|
||||
|
||||
/**
|
||||
* @brief Specifies the current frame in the animation.
|
||||
|
@ -2012,7 +2003,7 @@ public:
|
|||
*/
|
||||
static Animation* gen() noexcept;
|
||||
|
||||
_TVG_DECLARE_PRIVATE(Animation);
|
||||
_TVG_DECLARE_PRIVATE_BASE(Animation);
|
||||
};
|
||||
|
||||
|
||||
|
@ -2115,7 +2106,7 @@ public:
|
|||
*/
|
||||
static Saver* gen() noexcept;
|
||||
|
||||
_TVG_DECLARE_PRIVATE(Saver);
|
||||
_TVG_DECLARE_PRIVATE_BASE(Saver);
|
||||
};
|
||||
|
||||
|
||||
|
@ -2171,7 +2162,7 @@ public:
|
|||
*/
|
||||
static Accessor* gen() noexcept;
|
||||
|
||||
_TVG_DECLARE_PRIVATE(Accessor);
|
||||
_TVG_DECLARE_PRIVATE_BASE(Accessor);
|
||||
};
|
||||
|
||||
/** @}*/
|
||||
|
|
|
@ -36,7 +36,6 @@ void JpgLoader::clear()
|
|||
freeData = false;
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
|
|
@ -33,7 +33,6 @@ void PngLoader::clear()
|
|||
image = nullptr;
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
|
|
@ -30,7 +30,6 @@
|
|||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
|
||||
void WebpLoader::run(unsigned tid)
|
||||
{
|
||||
//TODO: acquire the current colorspace format & pre-multiplied alpha image.
|
||||
|
|
|
@ -20,8 +20,6 @@ namespace tvg
|
|||
class TVG_API LottieAnimation final : public Animation
|
||||
{
|
||||
public:
|
||||
~LottieAnimation();
|
||||
|
||||
/**
|
||||
* @brief Override Lottie properties using slot data.
|
||||
*
|
||||
|
@ -87,6 +85,8 @@ public:
|
|||
* @since 0.15
|
||||
*/
|
||||
static LottieAnimation* gen() noexcept;
|
||||
|
||||
_TVG_DECLARE_PRIVATE(LottieAnimation);
|
||||
};
|
||||
|
||||
} //namespace
|
||||
|
|
|
@ -26,24 +26,16 @@
|
|||
#include "tvgAnimation.h"
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
LottieAnimation::~LottieAnimation()
|
||||
LottieAnimation::LottieAnimation()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -51,7 +43,7 @@ Result LottieAnimation::override(const char* slot) 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 (!marker) {
|
||||
|
@ -68,7 +60,7 @@ Result LottieAnimation::segment(const char* marker) noexcept
|
|||
|
||||
uint32_t LottieAnimation::markersCnt() noexcept
|
||||
{
|
||||
auto loader = pImpl->picture->pImpl->loader;
|
||||
auto loader = PICTURE(pImpl->picture)->loader;
|
||||
if (!loader) return 0;
|
||||
return static_cast<LottieLoader*>(loader)->markersCnt();
|
||||
}
|
||||
|
@ -76,7 +68,7 @@ uint32_t LottieAnimation::markersCnt() 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;
|
||||
return static_cast<LottieLoader*>(loader)->markers(idx);
|
||||
}
|
||||
|
|
|
@ -189,10 +189,10 @@ void LottieBuilder::updateTransform(LottieGroup* parent, LottieObject** child, f
|
|||
if (!_updateTransform(transform, frameNo, false, matrix, opacity, exps)) return;
|
||||
|
||||
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.
|
||||
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);
|
||||
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);
|
||||
|
||||
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()));
|
||||
|
||||
updateChildren(group, frameNo, contexts);
|
||||
|
@ -243,7 +243,7 @@ static bool _fragmented(LottieGroup* parent, LottieObject** child, Inlist<Render
|
|||
if (!ctx->reqFragment) return false;
|
||||
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;
|
||||
fragment->begin = child - 1;
|
||||
ctx->fragmenting = true;
|
||||
|
@ -313,7 +313,7 @@ static bool _draw(LottieGroup* parent, LottieShape* shape, RenderContext* ctx)
|
|||
|
||||
if (shape) {
|
||||
ctx->merging = shape->pooling();
|
||||
PP(ctx->propagator)->duplicate(ctx->merging);
|
||||
PAINT(ctx->propagator)->duplicate(ctx->merging);
|
||||
} else {
|
||||
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) {
|
||||
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;
|
||||
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);
|
||||
//round rect
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
|
@ -557,12 +557,12 @@ void LottieBuilder::updatePath(LottieGroup* parent, LottieObject** child, float
|
|||
if (!ctx->repeaters.empty()) {
|
||||
auto shape = path->pooling();
|
||||
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);
|
||||
} else {
|
||||
_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)) {
|
||||
P(ctx->merging)->update(RenderUpdateFlag::Path);
|
||||
if (path->pathset(frameNo, SHAPE(ctx->merging)->rs.path.cmds, SHAPE(ctx->merging)->rs.path.pts, ctx->transform, ctx->roundness, ctx->offsetPath, exps)) {
|
||||
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)) {
|
||||
P(shape)->rs.path.pts.reserve(numPoints + 2);
|
||||
P(shape)->rs.path.cmds.reserve(numPoints + 3);
|
||||
SHAPE(shape)->rs.path.pts.reserve(numPoints + 2);
|
||||
SHAPE(shape)->rs.path.cmds.reserve(numPoints + 3);
|
||||
} else {
|
||||
P(shape)->rs.path.pts.reserve(numPoints * 3 + 2);
|
||||
P(shape)->rs.path.cmds.reserve(numPoints + 3);
|
||||
SHAPE(shape)->rs.path.pts.reserve(numPoints * 3 + 2);
|
||||
SHAPE(shape)->rs.path.cmds.reserve(numPoints + 3);
|
||||
hasRoundness = true;
|
||||
}
|
||||
|
||||
|
@ -687,13 +687,13 @@ static void _updateStar(TVG_UNUSED LottieGroup* parent, LottiePolyStar* star, Ma
|
|||
if (roundedCorner) {
|
||||
if (offsetPath) {
|
||||
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);
|
||||
offsetPath->modifyPolystar(P(intermediate)->rs.path.cmds, P(intermediate)->rs.path.pts, P(merging)->rs.path.cmds, P(merging)->rs.path.pts);
|
||||
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(SHAPE(intermediate)->rs.path.cmds, SHAPE(intermediate)->rs.path.pts, SHAPE(merging)->rs.path.cmds, SHAPE(merging)->rs.path.pts);
|
||||
delete(intermediate);
|
||||
} 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 {
|
||||
shape = merging;
|
||||
if (hasRoundness) {
|
||||
P(shape)->rs.path.pts.reserve(ptsCnt * 3 + 2);
|
||||
P(shape)->rs.path.cmds.reserve(ptsCnt + 3);
|
||||
SHAPE(shape)->rs.path.pts.reserve(ptsCnt * 3 + 2);
|
||||
SHAPE(shape)->rs.path.cmds.reserve(ptsCnt + 3);
|
||||
} else {
|
||||
P(shape)->rs.path.pts.reserve(ptsCnt + 2);
|
||||
P(shape)->rs.path.cmds.reserve(ptsCnt + 3);
|
||||
SHAPE(shape)->rs.path.pts.reserve(ptsCnt + 2);
|
||||
SHAPE(shape)->rs.path.cmds.reserve(ptsCnt + 3);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -774,13 +774,13 @@ static void _updatePolygon(LottieGroup* parent, LottiePolyStar* star, Matrix* tr
|
|||
if (roundedCorner) {
|
||||
if (offsetPath) {
|
||||
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);
|
||||
offsetPath->modifyPolystar(P(intermediate)->rs.path.cmds, P(intermediate)->rs.path.pts, P(merging)->rs.path.cmds, P(merging)->rs.path.pts);
|
||||
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(SHAPE(intermediate)->rs.path.cmds, SHAPE(intermediate)->rs.path.pts, SHAPE(merging)->rs.path.cmds, SHAPE(merging)->rs.path.pts);
|
||||
delete(intermediate);
|
||||
} 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);
|
||||
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);
|
||||
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;
|
||||
trimpath->segment(frameNo, begin, end, exps);
|
||||
|
||||
if (P(ctx->propagator)->rs.stroke) {
|
||||
auto pbegin = P(ctx->propagator)->rs.stroke->trim.begin;
|
||||
auto pend = P(ctx->propagator)->rs.stroke->trim.end;
|
||||
if (SHAPE(ctx->propagator)->rs.stroke) {
|
||||
auto pbegin = SHAPE(ctx->propagator)->rs.stroke->trim.begin;
|
||||
auto pend = SHAPE(ctx->propagator)->rs.stroke->trim.end;
|
||||
auto length = fabsf(pend - pbegin);
|
||||
begin = (length * begin) + 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;
|
||||
}
|
||||
|
||||
|
@ -1083,8 +1083,8 @@ void LottieBuilder::updateText(LottieLayer* layer, float frameNo)
|
|||
for (auto g = glyph->children.begin(); g < glyph->children.end(); ++g) {
|
||||
auto group = static_cast<LottieGroup*>(*g);
|
||||
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)) {
|
||||
P(shape)->update(RenderUpdateFlag::Path);
|
||||
if (static_cast<LottiePath*>(*p)->pathset(frameNo, SHAPE(shape)->rs.path.cmds, SHAPE(shape)->rs.path.pts, nullptr, nullptr, nullptr)) {
|
||||
PAINT(shape)->update(RenderUpdateFlag::Path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1237,11 +1237,11 @@ void LottieBuilder::updateMaskings(LottieLayer* layer, float frameNo)
|
|||
|
||||
//Apply Masking Expansion (Offset)
|
||||
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 {
|
||||
//TODO: Once path direction support is implemented, ensure that the direction is ignored here
|
||||
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;
|
||||
|
@ -1269,14 +1269,14 @@ void LottieBuilder::updateMaskings(LottieLayer* layer, float frameNo)
|
|||
|
||||
//Append the mask shape
|
||||
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
|
||||
} else {
|
||||
auto shape = layer->pooling();
|
||||
shape->reset();
|
||||
shape->fill(255, 255, 255, mask->opacity(frameNo));
|
||||
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 = shape;
|
||||
pMethod = method;
|
||||
|
|
|
@ -62,7 +62,7 @@ struct RenderContext
|
|||
|
||||
RenderContext(Shape* propagator)
|
||||
{
|
||||
P(propagator)->reset();
|
||||
SHAPE(propagator)->reset();
|
||||
propagator->ref();
|
||||
this->propagator = propagator;
|
||||
}
|
||||
|
|
|
@ -29,7 +29,6 @@
|
|||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
|
||||
void PngLoader::run(unsigned tid)
|
||||
{
|
||||
auto width = static_cast<unsigned>(w);
|
||||
|
|
|
@ -25,14 +25,6 @@
|
|||
#include "tvgLoader.h"
|
||||
#include "tvgRawLoader.h"
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
RawLoader::RawLoader() : ImageLoader(FileType::Raw)
|
||||
{
|
||||
|
|
|
@ -527,8 +527,8 @@ bool svgPathToShape(const char* svgPath, Shape* shape)
|
|||
bool closed = false;
|
||||
char* path = (char*)svgPath;
|
||||
|
||||
auto& pts = P(shape)->rs.path.pts;
|
||||
auto& cmds = P(shape)->rs.path.cmds;
|
||||
auto& pts = SHAPE(shape)->rs.path.pts;
|
||||
auto& cmds = SHAPE(shape)->rs.path.cmds;
|
||||
auto lastCmds = cmds.count;
|
||||
|
||||
while ((path[0] != '\0')) {
|
||||
|
|
|
@ -153,7 +153,7 @@ static RadialGradient* _applyRadialGradientProperty(SvgStyleGradient* g, const B
|
|||
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);
|
||||
|
||||
//Update the stops
|
||||
|
@ -216,7 +216,7 @@ static Matrix _compositionTransform(Paint* paint, const SvgNode* node, const Svg
|
|||
}
|
||||
if (!compNode->node.clip.userSpace) {
|
||||
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};
|
||||
}
|
||||
return m;
|
||||
|
|
|
@ -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;
|
||||
|
||||
//generate tvg paths.
|
||||
auto& pathCmds = P(shape)->rs.path.cmds;
|
||||
auto& pathPts = P(shape)->rs.path.pts;
|
||||
auto& pathCmds = SHAPE(shape)->rs.path.cmds;
|
||||
auto& pathPts = SHAPE(shape)->rs.path.pts;
|
||||
pathCmds.reserve(ptsCnt);
|
||||
pathPts.reserve(ptsCnt);
|
||||
|
||||
|
|
|
@ -35,13 +35,9 @@ bool GlGeometry::tesselate(const RenderShape& rshape, RenderUpdateFlag flag)
|
|||
fillVertex.clear();
|
||||
fillIndex.clear();
|
||||
|
||||
|
||||
BWTessellator bwTess{&fillVertex, &fillIndex};
|
||||
|
||||
bwTess.tessellate(&rshape, mMatrix);
|
||||
|
||||
mFillRule = rshape.rule;
|
||||
|
||||
mBounds = bwTess.bounds();
|
||||
}
|
||||
|
||||
|
@ -51,7 +47,6 @@ bool GlGeometry::tesselate(const RenderShape& rshape, RenderUpdateFlag flag)
|
|||
|
||||
Stroker stroke{&strokeVertex, &strokeIndex, mMatrix};
|
||||
stroke.stroke(&rshape);
|
||||
|
||||
mBounds = stroke.bounds();
|
||||
}
|
||||
|
||||
|
@ -123,10 +118,7 @@ void GlGeometry::disableVertex(uint32_t location)
|
|||
|
||||
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<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(bottom - floor(top))),
|
||||
};
|
||||
if (bounds.w < 0 || bounds.h < 0) {
|
||||
return mBounds;
|
||||
} else {
|
||||
return bounds;
|
||||
}
|
||||
if (bounds.w < 0 || bounds.h < 0) return mBounds;
|
||||
else return bounds;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
#include <string.h>
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/* GlGpuBuffer Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
static GLint _getGpuBufferAlign()
|
||||
|
@ -41,6 +41,25 @@ static GLint _getGpuBufferAlign()
|
|||
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()
|
||||
{
|
||||
GL_CHECK(glGenBuffers(1, &mGlBufferId));
|
||||
|
@ -56,27 +75,16 @@ GlGpuBuffer::~GlGpuBuffer()
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
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));
|
||||
}
|
||||
/************************************************************************/
|
||||
/* GlStageBuffer Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
GlStageBuffer::GlStageBuffer() : mVao(0), mGpuBuffer(), mGpuIndexBuffer()
|
||||
{
|
||||
GL_CHECK(glGenVertexArrays(1, &mVao));
|
||||
}
|
||||
|
||||
|
||||
GlStageBuffer::~GlStageBuffer()
|
||||
{
|
||||
if (mVao) {
|
||||
|
@ -85,6 +93,7 @@ GlStageBuffer::~GlStageBuffer()
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
uint32_t GlStageBuffer::push(void *data, uint32_t size, bool alignGpuOffset)
|
||||
{
|
||||
if (alignGpuOffset) alignOffset(size);
|
||||
|
@ -102,6 +111,7 @@ uint32_t GlStageBuffer::push(void *data, uint32_t size, bool alignGpuOffset)
|
|||
return offset;
|
||||
}
|
||||
|
||||
|
||||
uint32_t GlStageBuffer::pushIndex(void *data, uint32_t size)
|
||||
{
|
||||
uint32_t offset = mIndexBuffer.count;
|
||||
|
@ -117,6 +127,7 @@ uint32_t GlStageBuffer::pushIndex(void *data, uint32_t size)
|
|||
return offset;
|
||||
}
|
||||
|
||||
|
||||
bool GlStageBuffer::flushToGPU()
|
||||
{
|
||||
if (mStageBuffer.empty() || mIndexBuffer.empty()) {
|
||||
|
@ -140,6 +151,7 @@ bool GlStageBuffer::flushToGPU()
|
|||
return true;
|
||||
}
|
||||
|
||||
|
||||
void GlStageBuffer::bind()
|
||||
{
|
||||
glBindVertexArray(mVao);
|
||||
|
@ -148,6 +160,7 @@ void GlStageBuffer::bind()
|
|||
mGpuIndexBuffer.bind(GlGpuBuffer::Target::ELEMENT_ARRAY_BUFFER);
|
||||
}
|
||||
|
||||
|
||||
void GlStageBuffer::unbind()
|
||||
{
|
||||
glBindVertexArray(0);
|
||||
|
@ -156,11 +169,13 @@ void GlStageBuffer::unbind()
|
|||
mGpuIndexBuffer.unbind(GlGpuBuffer::Target::ELEMENT_ARRAY_BUFFER);
|
||||
}
|
||||
|
||||
|
||||
GLuint GlStageBuffer::getBufferId()
|
||||
{
|
||||
return mGpuBuffer.getBufferId();
|
||||
}
|
||||
|
||||
|
||||
void GlStageBuffer::alignOffset(uint32_t size)
|
||||
{
|
||||
|
||||
|
|
|
@ -40,8 +40,8 @@ public:
|
|||
void updateBufferData(Target target, uint32_t size, const void* data);
|
||||
void bind(Target target);
|
||||
void unbind(Target target);
|
||||
|
||||
uint32_t getBufferId() { return mGlBufferId; }
|
||||
|
||||
private:
|
||||
uint32_t mGlBufferId = 0;
|
||||
|
||||
|
@ -53,19 +53,15 @@ public:
|
|||
~GlStageBuffer();
|
||||
|
||||
uint32_t push(void* data, uint32_t size, bool alignGpuOffset = false);
|
||||
|
||||
uint32_t pushIndex(void* data, uint32_t size);
|
||||
|
||||
bool flushToGPU();
|
||||
|
||||
void bind();
|
||||
|
||||
void unbind();
|
||||
|
||||
GLuint getBufferId();
|
||||
|
||||
private:
|
||||
void alignOffset(uint32_t size);
|
||||
private:
|
||||
|
||||
GLuint mVao = 0;
|
||||
GlGpuBuffer mGpuBuffer = {};
|
||||
GlGpuBuffer mGpuIndexBuffer = {};
|
||||
|
|
|
@ -29,6 +29,43 @@
|
|||
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 */
|
||||
/************************************************************************/
|
||||
|
@ -149,40 +186,3 @@ void GlProgram::setUniform4x4Value(int32_t location, int count, const float* val
|
|||
{
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -24,9 +24,6 @@
|
|||
#include "tvgGlProgram.h"
|
||||
#include "tvgGlRenderPass.h"
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
GlRenderTask::GlRenderTask(GlProgram* program, GlRenderTask* other): mProgram(program)
|
||||
{
|
||||
|
|
|
@ -803,12 +803,10 @@ void GlRenderer::endRenderPass(RenderCompositor* cmp)
|
|||
}
|
||||
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
|
||||
bool GlRenderer::clear()
|
||||
{
|
||||
clearDisposes();
|
||||
|
|
|
@ -23,35 +23,9 @@
|
|||
#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)
|
||||
{
|
||||
mVtShader = compileShader(GL_VERTEX_SHADER, const_cast<char*>(vertSrc));
|
||||
|
@ -110,3 +84,31 @@ uint32_t GlShader::compileShader(uint32_t type, char* shaderSrc)
|
|||
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;
|
||||
}
|
|
@ -61,7 +61,7 @@ private:
|
|||
Polygon *makePoly(Vertex* v, int32_t winding);
|
||||
void emitPoly(MonotonePolygon* poly);
|
||||
void emitTriangle(Vertex* p1, Vertex* p2, Vertex* p3);
|
||||
private:
|
||||
|
||||
FillRule fillRule = FillRule::Winding;
|
||||
std::unique_ptr<ObjectHeap> pHeap;
|
||||
Array<VertexList*> outlines;
|
||||
|
@ -134,7 +134,6 @@ private:
|
|||
void lineTo(const Point& pt);
|
||||
void cubicTo(const Point& pt1, const Point& pt2, const Point& pt3);
|
||||
|
||||
private:
|
||||
Array<PathCommand>* mCmds;
|
||||
Array<Point>* mPts;
|
||||
uint32_t mDashCount;
|
||||
|
|
|
@ -66,15 +66,17 @@ static void _calculateCoefficients(const SwFill* fill, uint32_t x, uint32_t y, f
|
|||
static uint32_t _estimateAAMargin(const Fill* fdata)
|
||||
{
|
||||
constexpr float marginScalingFactor = 800.0f;
|
||||
|
||||
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);
|
||||
}
|
||||
auto grad = P(static_cast<const LinearGradient*>(fdata));
|
||||
} else {
|
||||
auto grad = LINEAR(fdata);
|
||||
Point p1 {grad->x1, grad->y1};
|
||||
Point p2 {grad->x2, grad->y2};
|
||||
auto len = length(&p1, &p2);
|
||||
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) {
|
||||
fill->ctable = static_cast<uint32_t*>(malloc(GRADIENT_STOP_SIZE * sizeof(uint32_t)));
|
||||
if (!fill->ctable) return false;
|
||||
}
|
||||
|
||||
const Fill::ColorStop* colors;
|
||||
|
@ -145,7 +146,6 @@ static bool _updateColorTable(SwFill* fill, const Fill* fdata, const SwSurface*
|
|||
auto g = pColors->g;
|
||||
auto b = pColors->b;
|
||||
auto rgba = surface->join(r, g, b, a);
|
||||
|
||||
auto inc = 1.0f / static_cast<float>(GRADIENT_STOP_SIZE);
|
||||
auto pos = 1.5f * inc;
|
||||
uint32_t i = 0;
|
||||
|
@ -173,6 +173,7 @@ static bool _updateColorTable(SwFill* fill, const Fill* fdata, const SwSurface*
|
|||
auto next = curr + 1;
|
||||
auto delta = 1.0f / (next->offset - curr->offset);
|
||||
auto a2 = MULTIPLY(next->a, opacity);
|
||||
|
||||
if (!fill->translucent && a2 < 255) fill->translucent = true;
|
||||
|
||||
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 dist = static_cast<int32_t>(255 * t);
|
||||
auto dist2 = 255 - dist;
|
||||
|
||||
auto color = INTERPOLATE(rgba, rgba2, dist2);
|
||||
fill->ctable[i] = ALPHA_BLEND((color | 0xff000000), (color >> 24));
|
||||
|
||||
++i;
|
||||
pos += inc;
|
||||
}
|
||||
|
@ -195,8 +194,9 @@ static bool _updateColorTable(SwFill* fill, const Fill* fdata, const SwSurface*
|
|||
}
|
||||
rgba = ALPHA_BLEND((rgba | 0xff000000), a);
|
||||
|
||||
for (; i < GRADIENT_STOP_SIZE; ++i)
|
||||
for (; i < GRADIENT_STOP_SIZE; ++i) {
|
||||
fill->ctable[i] = rgba;
|
||||
}
|
||||
|
||||
//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.
|
||||
|
@ -210,7 +210,7 @@ static bool _updateColorTable(SwFill* fill, const Fill* fdata, const SwSurface*
|
|||
bool _prepareLinear(SwFill* fill, const LinearGradient* linear, const Matrix& pTransform)
|
||||
{
|
||||
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.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)
|
||||
{
|
||||
auto cx = P(radial)->cx;
|
||||
auto cy = P(radial)->cy;
|
||||
auto r = P(radial)->r;
|
||||
auto fx = P(radial)->fx;
|
||||
auto fy = P(radial)->fy;
|
||||
auto fr = P(radial)->fr;
|
||||
float cx, cy, r, fx, fy, fr;
|
||||
radial->radial(&cx, &cy, &r, &fx, &fy, &fr);
|
||||
|
||||
if (tvg::zero(r)) {
|
||||
fill->solid = true;
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
constexpr auto DOWN_SCALE_TOLERANCE = 0.5f;
|
||||
|
||||
struct FillLinear
|
||||
|
|
|
@ -23,13 +23,6 @@
|
|||
#include "tvgFrameModule.h"
|
||||
#include "tvgAnimation.h"
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
Animation::~Animation()
|
||||
{
|
||||
|
@ -44,7 +37,7 @@ Animation::Animation() : pImpl(new Impl)
|
|||
|
||||
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->animatable()) return Result::NonSupport;
|
||||
|
@ -62,7 +55,7 @@ Picture* Animation::picture() 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->animatable()) return 0;
|
||||
|
@ -73,7 +66,7 @@ float Animation::curFrame() 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->animatable()) return 0;
|
||||
|
@ -84,7 +77,7 @@ float Animation::totalFrame() 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->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;
|
||||
|
||||
auto loader = pImpl->picture->pImpl->loader;
|
||||
auto loader = PICTURE(pImpl->picture)->loader;
|
||||
if (!loader) return Result::InsufficientCondition;
|
||||
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
|
||||
{
|
||||
auto loader = pImpl->picture->pImpl->loader;
|
||||
auto loader = PICTURE(pImpl->picture)->loader;
|
||||
if (!loader) return Result::InsufficientCondition;
|
||||
if (!loader->animatable()) return Result::NonSupport;
|
||||
if (!begin && !end) return Result::InvalidArguments;
|
||||
|
|
|
@ -22,11 +22,7 @@
|
|||
|
||||
#include "tvgCanvas.h"
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
Canvas::Canvas(RenderMethod *pRenderer):pImpl(new Impl(pRenderer))
|
||||
Canvas::Canvas():pImpl(new Impl)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -34,10 +34,9 @@ struct Canvas::Impl
|
|||
RenderRegion vport = {0, 0, INT32_MAX, INT32_MAX};
|
||||
Status status = Status::Synced;
|
||||
|
||||
Impl(RenderMethod* pRenderer) : scene(Scene::gen()), renderer(pRenderer)
|
||||
Impl() : scene(Scene::gen())
|
||||
{
|
||||
scene->ref();
|
||||
renderer->ref();
|
||||
}
|
||||
|
||||
~Impl()
|
||||
|
@ -77,8 +76,8 @@ struct Canvas::Impl
|
|||
|
||||
auto m = Matrix{1, 0, 0, 0, 1, 0, 0, 0, 1};
|
||||
|
||||
if (paint) P(paint)->update(renderer, m, clips, 255, flag);
|
||||
else PP(scene)->update(renderer, m, clips, 255, flag);
|
||||
if (paint) PAINT(paint)->update(renderer, m, clips, 255, flag);
|
||||
else PAINT(scene)->update(renderer, m, clips, 255, flag);
|
||||
|
||||
status = Status::Updating;
|
||||
return Result::Success;
|
||||
|
@ -96,7 +95,7 @@ struct Canvas::Impl
|
|||
|
||||
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;
|
||||
|
||||
|
|
|
@ -75,10 +75,7 @@ using Size = Point;
|
|||
|
||||
uint16_t THORVG_VERSION_NUMBER();
|
||||
|
||||
|
||||
#define P(A) ((A)->pImpl) //Access to pimpl.
|
||||
#define PP(A) (((Paint*)(A))->pImpl) //Access to pimpl.
|
||||
|
||||
#define PIMPL(INST, CLASS) ((CLASS::Impl*)INST->pImpl) //Access to pimpl
|
||||
|
||||
#define TVG_DELETE(PAINT) \
|
||||
if (PAINT->refCnt() == 0) delete(PAINT)
|
||||
|
|
|
@ -22,60 +22,12 @@
|
|||
|
||||
#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
|
||||
{
|
||||
if ((!colorStops && cnt > 0) || (colorStops && cnt == 0)) return Result::InvalidArguments;
|
||||
|
||||
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;
|
||||
return pImpl->update(colorStops, cnt);
|
||||
}
|
||||
|
||||
|
||||
uint32_t Fill::colorStops(const ColorStop** colorStops) const noexcept
|
||||
{
|
||||
if (colorStops) *colorStops = pImpl->colorStops;
|
||||
|
||||
return pImpl->cnt;
|
||||
}
|
||||
|
||||
|
@ -121,7 +54,6 @@ uint32_t Fill::colorStops(const ColorStop** colorStops) const noexcept
|
|||
Result Fill::spread(FillSpread s) noexcept
|
||||
{
|
||||
pImpl->spread = s;
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
@ -151,34 +83,26 @@ Fill* Fill::duplicate() const noexcept
|
|||
}
|
||||
|
||||
|
||||
RadialGradient::RadialGradient():pImpl(new Impl())
|
||||
{
|
||||
Fill::pImpl->method(new FillDup<RadialGradient::Impl>(pImpl));
|
||||
}
|
||||
/************************************************************************/
|
||||
/* RadialGradient Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
|
||||
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
|
||||
{
|
||||
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
|
||||
{
|
||||
if (cx) *cx = pImpl->cx;
|
||||
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;
|
||||
return RADIAL(this)->radial(cx, cy, r, fx, fy, fr);
|
||||
}
|
||||
|
||||
|
||||
|
@ -194,37 +118,26 @@ Type RadialGradient::type() const noexcept
|
|||
}
|
||||
|
||||
|
||||
LinearGradient::LinearGradient():pImpl(new Impl())
|
||||
{
|
||||
Fill::pImpl->method(new FillDup<LinearGradient::Impl>(pImpl));
|
||||
}
|
||||
/************************************************************************/
|
||||
/* LinearGradient Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
|
||||
LinearGradient::~LinearGradient()
|
||||
LinearGradient::LinearGradient()
|
||||
{
|
||||
delete(pImpl);
|
||||
Fill::pImpl = new Impl;
|
||||
}
|
||||
|
||||
|
||||
Result LinearGradient::linear(float x1, float y1, float x2, float y2) noexcept
|
||||
{
|
||||
pImpl->x1 = x1;
|
||||
pImpl->y1 = y1;
|
||||
pImpl->x2 = x2;
|
||||
pImpl->y2 = y2;
|
||||
|
||||
return Result::Success;
|
||||
return LINEAR(this)->linear(x1, y1, x2, y2);
|
||||
}
|
||||
|
||||
|
||||
Result LinearGradient::linear(float* x1, float* y1, float* x2, float* y2) const noexcept
|
||||
{
|
||||
if (x1) *x1 = pImpl->x1;
|
||||
if (x2) *x2 = pImpl->x2;
|
||||
if (y1) *y1 = pImpl->y1;
|
||||
if (y2) *y2 = pImpl->y2;
|
||||
|
||||
return Result::Success;
|
||||
return LINEAR(this)->linear(x1, y1, x2, y2);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -27,80 +27,143 @@
|
|||
#include <cstring>
|
||||
#include "tvgCommon.h"
|
||||
|
||||
template<typename T>
|
||||
struct DuplicateMethod
|
||||
{
|
||||
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();
|
||||
}
|
||||
};
|
||||
#define LINEAR(A) PIMPL(A, LinearGradient)
|
||||
#define RADIAL(A) PIMPL(A, RadialGradient)
|
||||
|
||||
struct Fill::Impl
|
||||
{
|
||||
ColorStop* colorStops = nullptr;
|
||||
Matrix transform = {1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f};
|
||||
uint32_t cnt = 0;
|
||||
DuplicateMethod<Fill>* dup = nullptr;
|
||||
FillSpread spread;
|
||||
uint16_t cnt = 0;
|
||||
FillSpread spread = FillSpread::Pad;
|
||||
|
||||
~Impl()
|
||||
virtual ~Impl()
|
||||
{
|
||||
delete(dup);
|
||||
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;
|
||||
ret->pImpl->spread = spread;
|
||||
ret->pImpl->colorStops = static_cast<ColorStop*>(malloc(sizeof(ColorStop) * cnt));
|
||||
memcpy(ret->pImpl->colorStops, colorStops, sizeof(ColorStop) * cnt);
|
||||
ret->pImpl->transform = transform;
|
||||
|
||||
return ret;
|
||||
if (cnt == 0) {
|
||||
if (this->colorStops) {
|
||||
free(this->colorStops);
|
||||
this->colorStops = nullptr;
|
||||
this->cnt = 0;
|
||||
}
|
||||
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 fx = 0.0f, fy = 0.0f;
|
||||
float r = 0.0f, fr = 0.0f;
|
||||
|
||||
Fill* duplicate();
|
||||
Result radial(float cx, float cy, float r, float fx, float fy, float fr);
|
||||
Fill* duplicate() override
|
||||
{
|
||||
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 y1 = 0.0f;
|
||||
float x2 = 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;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -24,38 +24,20 @@
|
|||
|
||||
#ifdef THORVG_GL_RASTER_SUPPORT
|
||||
#include "tvgGlRenderer.h"
|
||||
#else
|
||||
class GlRenderer : public RenderMethod
|
||||
{
|
||||
//Non Supported. Dummy Class */
|
||||
};
|
||||
#endif
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
struct GlCanvas::Impl
|
||||
GlCanvas::GlCanvas()
|
||||
{
|
||||
};
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
#ifdef THORVG_GL_RASTER_SUPPORT
|
||||
GlCanvas::GlCanvas() : Canvas(GlRenderer::gen()), pImpl(nullptr)
|
||||
#else
|
||||
GlCanvas::GlCanvas() : Canvas(nullptr), pImpl(nullptr)
|
||||
pImpl->renderer = GlRenderer::gen();
|
||||
pImpl->renderer->ref();
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
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;
|
||||
}
|
||||
|
||||
//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->target(id, w, h)) return Result::Unknown;
|
||||
Canvas::pImpl->vport = {0, 0, (int32_t)w, (int32_t)h};
|
||||
renderer->viewport(Canvas::pImpl->vport);
|
||||
pImpl->vport = {0, 0, (int32_t)w, (int32_t)h};
|
||||
renderer->viewport(pImpl->vport);
|
||||
|
||||
//Paints must be updated again with this new target.
|
||||
Canvas::pImpl->status = Status::Damaged;
|
||||
pImpl->status = Status::Damaged;
|
||||
|
||||
return Result::Success;
|
||||
#endif
|
||||
|
|
|
@ -34,7 +34,7 @@ public:
|
|||
//Utility Method: Iterator Accessor
|
||||
static Iterator* iterator(const Paint* paint)
|
||||
{
|
||||
return paint->pImpl->iterator();
|
||||
return PAINT(paint)->iterator();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -33,10 +33,10 @@
|
|||
|
||||
#define PAINT_METHOD(ret, METHOD) \
|
||||
switch (paint->type()) { \
|
||||
case Type::Shape: ret = P((Shape*)paint)->METHOD; break; \
|
||||
case Type::Scene: ret = P((Scene*)paint)->METHOD; break; \
|
||||
case Type::Picture: ret = P((Picture*)paint)->METHOD; break; \
|
||||
case Type::Text: ret = P((Text*)paint)->METHOD; break; \
|
||||
case Type::Shape: ret = SHAPE(paint)->METHOD; break; \
|
||||
case Type::Scene: ret = SCENE(paint)->METHOD; break; \
|
||||
case Type::Picture: ret = PICTURE(paint)->METHOD; break; \
|
||||
case Type::Text: ret = TEXT(paint)->METHOD; break; \
|
||||
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)
|
||||
{
|
||||
if (opacity == 0) return true;
|
||||
|
@ -214,7 +180,7 @@ bool Paint::Impl::render(RenderMethod* renderer)
|
|||
RenderRegion region;
|
||||
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;
|
||||
cmp = renderer->target(region, MASK_TO_COLORSPACE(renderer, maskData->method), CompositionFlag::Masking);
|
||||
if (renderer->beginComposite(cmp, MaskMethod::None, 255)) {
|
||||
|
@ -251,7 +217,7 @@ RenderData Paint::Impl::update(RenderMethod* renderer, const Matrix& pm, Array<R
|
|||
if (maskData) {
|
||||
auto target = maskData->target;
|
||||
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,
|
||||
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;
|
||||
shape->fillColor(nullptr, nullptr, nullptr, &a);
|
||||
//no gradient fill & no maskings of the masking target.
|
||||
if (!shape->fill() && !(PP(shape)->maskData)) {
|
||||
if ((method == MaskMethod::Alpha && a == 255 && PP(shape)->opacity == 255) || (method == MaskMethod::InvAlpha && (a == 0 || PP(shape)->opacity == 0))) {
|
||||
if (!shape->fill() && !(PAINT(shape)->maskData)) {
|
||||
if ((method == MaskMethod::Alpha && a == 255 && PAINT(shape)->opacity == 255) || (method == MaskMethod::InvAlpha && (a == 0 || PAINT(shape)->opacity == 0))) {
|
||||
viewport = renderer->viewport();
|
||||
if ((compFastTrack = _compFastTrack(renderer, target, pm, viewport)) == Result::Success) {
|
||||
P(target)->ctxFlag |= ContextFlag::FastTrack;
|
||||
PAINT(target)->ctxFlag |= ContextFlag::FastTrack;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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 */
|
||||
if (this->clipper) {
|
||||
P(this->clipper)->ctxFlag &= ~ContextFlag::FastTrack; //reset
|
||||
PAINT(this->clipper)->ctxFlag &= ~ContextFlag::FastTrack; //reset
|
||||
viewport = renderer->viewport();
|
||||
/* TODO: Intersect the clipper's clipper, if both are FastTrack.
|
||||
Update the subsequent clipper first and check its ctxFlag. */
|
||||
if (!P(this->clipper)->clipper && (compFastTrack = _compFastTrack(renderer, this->clipper, pm, viewport)) == Result::Success) {
|
||||
P(this->clipper)->ctxFlag |= ContextFlag::FastTrack;
|
||||
if (!PAINT(this->clipper)->clipper && (compFastTrack = _compFastTrack(renderer, this->clipper, pm, viewport)) == Result::Success) {
|
||||
PAINT(this->clipper)->ctxFlag |= ContextFlag::FastTrack;
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -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 */
|
||||
/************************************************************************/
|
||||
|
||||
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
|
||||
{
|
||||
if (pImpl->maskData) {
|
||||
if (target) *target = pImpl->maskData->target;
|
||||
return pImpl->maskData->method;
|
||||
} else {
|
||||
if (target) *target = nullptr;
|
||||
return MaskMethod::None;
|
||||
}
|
||||
return pImpl->mask(target);
|
||||
}
|
||||
|
||||
|
||||
|
@ -495,37 +429,20 @@ Result Paint::blend(BlendMethod method) noexcept
|
|||
{
|
||||
//TODO: Remove later
|
||||
if (method == BlendMethod::Hue || method == BlendMethod::Saturation || method == BlendMethod::Color || method == BlendMethod::Luminosity || method == BlendMethod::HardMix) return Result::NonSupport;
|
||||
|
||||
if (pImpl->blendMethod != method) {
|
||||
pImpl->blendMethod = method;
|
||||
pImpl->renderFlag |= RenderUpdateFlag::Blend;
|
||||
}
|
||||
|
||||
pImpl->blend(method);
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
uint8_t Paint::ref() noexcept
|
||||
{
|
||||
if (pImpl->refCnt == UINT8_MAX) TVGERR("RENDERER", "Reference Count Overflow!");
|
||||
else ++pImpl->refCnt;
|
||||
|
||||
return pImpl->refCnt;
|
||||
return pImpl->ref();
|
||||
}
|
||||
|
||||
|
||||
uint8_t Paint::unref(bool free) noexcept
|
||||
{
|
||||
if (pImpl->refCnt > 0) --pImpl->refCnt;
|
||||
else TVGERR("RENDERER", "Corrupted Reference Count!");
|
||||
|
||||
if (free && pImpl->refCnt == 0) {
|
||||
//TODO: use the global dismiss function?
|
||||
delete(this);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return pImpl->refCnt;
|
||||
return pImpl->unref(free);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -26,6 +26,8 @@
|
|||
#include "tvgRender.h"
|
||||
#include "tvgMath.h"
|
||||
|
||||
#define PAINT(A) PIMPL(A, Paint)
|
||||
|
||||
namespace tvg
|
||||
{
|
||||
enum ContextFlag : uint8_t {Default = 0, FastTrack = 1};
|
||||
|
@ -83,7 +85,7 @@ namespace tvg
|
|||
reset();
|
||||
}
|
||||
|
||||
~Impl()
|
||||
virtual ~Impl()
|
||||
{
|
||||
if (maskData) {
|
||||
maskData->target->unref();
|
||||
|
@ -93,6 +95,32 @@ namespace tvg
|
|||
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)
|
||||
{
|
||||
if (&tr.m != &m) tr.m = m;
|
||||
|
@ -143,16 +171,87 @@ namespace tvg
|
|||
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;
|
||||
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);
|
||||
RenderData update(RenderMethod* renderer, const Matrix& pm, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper = false);
|
||||
bool render(RenderMethod* renderer);
|
||||
Paint* duplicate(Paint* ret = nullptr);
|
||||
void reset();
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -23,124 +23,9 @@
|
|||
#include "tvgPaint.h"
|
||||
#include "tvgPicture.h"
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
RenderUpdateFlag Picture::Impl::load()
|
||||
Picture::Picture()
|
||||
{
|
||||
if (loader) {
|
||||
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);
|
||||
pImpl = new Impl(this);
|
||||
}
|
||||
|
||||
|
||||
|
@ -160,8 +45,7 @@ Result Picture::load(const char* filename) noexcept
|
|||
{
|
||||
#ifdef THORVG_FILE_IO_SUPPORT
|
||||
if (!filename) return Result::InvalidArguments;
|
||||
|
||||
return pImpl->load(filename);
|
||||
return PICTURE(this)->load(filename);
|
||||
#else
|
||||
TVGLOG("RENDERER", "FILE IO is disabled!");
|
||||
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
|
||||
{
|
||||
if (!data || size <= 0) return Result::InvalidArguments;
|
||||
|
||||
return pImpl->load(data, size, mimeType, rpath, copy);
|
||||
return PICTURE(this)->load(data, size, mimeType, rpath, copy);
|
||||
}
|
||||
|
||||
|
||||
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 pImpl->load(data, w, h, cs, copy);
|
||||
return PICTURE(this)->load(data, w, h, cs, copy);
|
||||
}
|
||||
|
||||
|
||||
Result Picture::size(float w, float h) noexcept
|
||||
{
|
||||
if (pImpl->size(w, h)) return Result::Success;
|
||||
return Result::InsufficientCondition;
|
||||
PICTURE(this)->size(w, h);
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
Result Picture::size(float* w, float* h) const noexcept
|
||||
{
|
||||
if (!pImpl->loader) return Result::InsufficientCondition;
|
||||
if (w) *w = pImpl->w;
|
||||
if (h) *h = pImpl->h;
|
||||
return Result::Success;
|
||||
return PICTURE(this)->size(w, h);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -23,10 +23,10 @@
|
|||
#ifndef _TVG_PICTURE_H_
|
||||
#define _TVG_PICTURE_H_
|
||||
|
||||
#include <string>
|
||||
#include "tvgPaint.h"
|
||||
#include "tvgLoader.h"
|
||||
|
||||
#define PICTURE(A) PIMPL(A, Picture)
|
||||
|
||||
struct PictureIterator : Iterator
|
||||
{
|
||||
|
@ -55,44 +55,32 @@ struct PictureIterator : Iterator
|
|||
};
|
||||
|
||||
|
||||
struct Picture::Impl
|
||||
struct Picture::Impl : Paint::Impl
|
||||
{
|
||||
ImageLoader* loader = nullptr;
|
||||
|
||||
Paint* paint = nullptr; //vector picture uses
|
||||
RenderSurface* surface = nullptr; //bitmap picture uses
|
||||
RenderData rd = nullptr; //engine data
|
||||
Paint* vector = nullptr; //vector picture uses
|
||||
RenderSurface* bitmap = nullptr; //bitmap picture uses
|
||||
RenderData rd = nullptr;
|
||||
float w = 0, h = 0;
|
||||
Picture* picture = nullptr;
|
||||
uint8_t cFlag = CompositionFlag::Invalid;
|
||||
uint8_t compFlag = CompositionFlag::Invalid;
|
||||
bool resizing = false;
|
||||
|
||||
void queryComposition(uint8_t opacity);
|
||||
bool render(RenderMethod* renderer);
|
||||
bool size(float w, float h);
|
||||
RenderRegion bounds(RenderMethod* renderer);
|
||||
Result load(ImageLoader* ploader);
|
||||
|
||||
Impl(Picture* p) : picture(p)
|
||||
Impl(Picture* p) : Paint::Impl(p)
|
||||
{
|
||||
}
|
||||
|
||||
~Impl()
|
||||
{
|
||||
LoaderMgr::retrieve(loader);
|
||||
if (surface) {
|
||||
if (auto renderer = PP(picture)->renderer) {
|
||||
renderer->dispose(rd);
|
||||
}
|
||||
}
|
||||
delete(paint);
|
||||
if (bitmap && renderer) renderer->dispose(rd);
|
||||
delete(vector);
|
||||
}
|
||||
|
||||
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());
|
||||
|
||||
if (surface) {
|
||||
if (bitmap) {
|
||||
if (flag == RenderUpdateFlag::None) return rd;
|
||||
|
||||
//Overriding Transformation by the desired image size
|
||||
|
@ -101,18 +89,34 @@ struct Picture::Impl
|
|||
auto scale = sx < sy ? sx : sy;
|
||||
auto m = transform * Matrix{scale, 0, 0, 0, scale, 0, 0, 0, 1};
|
||||
|
||||
rd = renderer->prepare(surface, rd, m, clips, opacity, flag);
|
||||
} else if (paint) {
|
||||
rd = renderer->prepare(bitmap, rd, m, clips, opacity, flag);
|
||||
} else if (vector) {
|
||||
if (resizing) {
|
||||
loader->resize(paint, w, h);
|
||||
loader->resize(vector, w, h);
|
||||
resizing = false;
|
||||
}
|
||||
queryComposition(opacity);
|
||||
rd = paint->pImpl->update(renderer, transform, clips, opacity, flag, false);
|
||||
rd = vector->pImpl->update(renderer, transform, clips, opacity, flag, false);
|
||||
}
|
||||
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)
|
||||
{
|
||||
if (x) *x = 0;
|
||||
|
@ -124,7 +128,7 @@ struct Picture::Impl
|
|||
|
||||
Result load(const char* filename)
|
||||
{
|
||||
if (paint || surface) return Result::InsufficientCondition;
|
||||
if (vector || bitmap) return Result::InsufficientCondition;
|
||||
|
||||
bool invalid; //Invalid Path
|
||||
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)
|
||||
{
|
||||
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));
|
||||
if (!loader) return Result::NonSupport;
|
||||
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)
|
||||
{
|
||||
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));
|
||||
if (!loader) return Result::FailedAllocation;
|
||||
|
@ -160,17 +166,17 @@ struct Picture::Impl
|
|||
load();
|
||||
|
||||
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) {
|
||||
dup->loader = loader;
|
||||
++dup->loader->sharing;
|
||||
PP(picture)->renderFlag |= RenderUpdateFlag::Image;
|
||||
PAINT(picture)->renderFlag |= RenderUpdateFlag::Image;
|
||||
}
|
||||
|
||||
dup->surface = surface;
|
||||
dup->bitmap = bitmap;
|
||||
dup->w = w;
|
||||
dup->h = h;
|
||||
dup->resizing = resizing;
|
||||
|
@ -181,7 +187,7 @@ struct Picture::Impl
|
|||
Iterator* iterator()
|
||||
{
|
||||
load();
|
||||
return new PictureIterator(paint);
|
||||
return new PictureIterator(vector);
|
||||
}
|
||||
|
||||
uint32_t* data(uint32_t* w, uint32_t* h)
|
||||
|
@ -196,11 +202,96 @@ struct Picture::Impl
|
|||
if (w) *w = 0;
|
||||
if (h) *h = 0;
|
||||
}
|
||||
if (surface) return surface->buf32;
|
||||
if (bitmap) return bitmap->buf32;
|
||||
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_
|
||||
|
|
|
@ -23,14 +23,6 @@
|
|||
#include "tvgMath.h"
|
||||
#include "tvgRender.h"
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
uint32_t RenderMethod::ref()
|
||||
{
|
||||
|
|
|
@ -20,38 +20,12 @@
|
|||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <cstdarg>
|
||||
#include "tvgScene.h"
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
Result Scene::Impl::resetEffects()
|
||||
Scene::Scene()
|
||||
{
|
||||
if (effects) {
|
||||
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);
|
||||
pImpl = new Impl(this);
|
||||
}
|
||||
|
||||
|
||||
|
@ -69,56 +43,27 @@ Type Scene::type() const noexcept
|
|||
|
||||
Result Scene::push(Paint* target, Paint* at) noexcept
|
||||
{
|
||||
if (!target) return Result::InvalidArguments;
|
||||
target->ref();
|
||||
|
||||
return pImpl->insert(target, at);
|
||||
return SCENE(this)->insert(target, at);
|
||||
}
|
||||
|
||||
|
||||
Result Scene::remove(Paint* paint) noexcept
|
||||
{
|
||||
if (paint) return pImpl->remove(paint);
|
||||
else return pImpl->clearPaints();
|
||||
if (paint) return SCENE(this)->remove(paint);
|
||||
else return SCENE(this)->clearPaints();
|
||||
}
|
||||
|
||||
|
||||
const list<Paint*>& Scene::paints() const noexcept
|
||||
{
|
||||
return pImpl->paints;
|
||||
return SCENE(this)->paints;
|
||||
}
|
||||
|
||||
|
||||
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_start(args, effect);
|
||||
|
||||
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;
|
||||
|
||||
pImpl->effects->push(re);
|
||||
|
||||
return Result::Success;
|
||||
return SCENE(this)->push(effect, args);
|
||||
}
|
||||
|
|
|
@ -24,9 +24,12 @@
|
|||
#define _TVG_SCENE_H_
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdarg>
|
||||
#include "tvgMath.h"
|
||||
#include "tvgPaint.h"
|
||||
|
||||
#define SCENE(A) PIMPL(A, Scene)
|
||||
|
||||
struct SceneIterator : Iterator
|
||||
{
|
||||
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;
|
||||
Scene* scene = nullptr;
|
||||
RenderRegion vport = {0, 0, INT32_MAX, INT32_MAX};
|
||||
Array<RenderEffect*>* effects = nullptr;
|
||||
uint8_t compFlag = CompositionFlag::Invalid;
|
||||
uint8_t opacity; //for composition
|
||||
|
||||
Impl(Scene* s) : scene(s)
|
||||
Impl(Scene* s) : Paint::Impl(s)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -76,9 +78,7 @@ struct Scene::Impl
|
|||
|
||||
clearPaints();
|
||||
|
||||
if (auto renderer = PP(scene)->renderer) {
|
||||
renderer->dispose(rd);
|
||||
}
|
||||
if (renderer) renderer->dispose(rd);
|
||||
}
|
||||
|
||||
uint8_t needComposition(uint8_t opacity)
|
||||
|
@ -89,8 +89,8 @@ struct Scene::Impl
|
|||
|
||||
//post effects, masking, blending may require composition
|
||||
if (effects) compFlag |= CompositionFlag::PostProcessing;
|
||||
if (scene->mask(nullptr) != MaskMethod::None) compFlag |= CompositionFlag::Masking;
|
||||
if (PP(scene)->blendMethod != BlendMethod::Normal) compFlag |= CompositionFlag::Blending;
|
||||
if (paint->mask(nullptr) != MaskMethod::None) compFlag |= CompositionFlag::Masking;
|
||||
if (blendMethod != BlendMethod::Normal) compFlag |= CompositionFlag::Blending;
|
||||
|
||||
//Half translucent requires intermediate composition.
|
||||
if (opacity == 255) return compFlag;
|
||||
|
@ -127,7 +127,7 @@ struct Scene::Impl
|
|||
RenderCompositor* cmp = nullptr;
|
||||
auto ret = true;
|
||||
|
||||
renderer->blend(PP(scene)->blendMethod);
|
||||
renderer->blend(blendMethod);
|
||||
|
||||
if (compFlag) {
|
||||
cmp = renderer->target(bounds(renderer), renderer->colorSpace(), static_cast<CompositionFlag>(compFlag));
|
||||
|
@ -206,7 +206,7 @@ struct Scene::Impl
|
|||
auto w = 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
|
||||
if (x < x1) x1 = x;
|
||||
|
@ -228,7 +228,7 @@ struct Scene::Impl
|
|||
if (ret) TVGERR("RENDERER", "TODO: duplicate()");
|
||||
|
||||
auto scene = Scene::gen();
|
||||
auto dup = scene->pImpl;
|
||||
auto dup = SCENE(scene);
|
||||
|
||||
for (auto paint : paints) {
|
||||
auto cdup = paint->duplicate();
|
||||
|
@ -265,14 +265,17 @@ struct Scene::Impl
|
|||
for (auto p : paints) {
|
||||
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
|
||||
}
|
||||
|
||||
Result insert(Paint* target, Paint* at)
|
||||
{
|
||||
if (!target) return Result::InvalidArguments;
|
||||
target->ref();
|
||||
|
||||
//Relocated the paint to the current scene space
|
||||
P(target)->renderFlag |= RenderUpdateFlag::Transform;
|
||||
PAINT(target)->renderFlag |= RenderUpdateFlag::Transform;
|
||||
|
||||
if (at == nullptr) {
|
||||
paints.push_back(target);
|
||||
|
@ -290,7 +293,48 @@ struct Scene::Impl
|
|||
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_
|
||||
|
|
|
@ -23,23 +23,10 @@
|
|||
#include "tvgMath.h"
|
||||
#include "tvgShape.h"
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
Shape :: Shape() : pImpl(new Impl(this))
|
||||
Shape :: Shape()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Shape :: ~Shape()
|
||||
{
|
||||
delete(pImpl);
|
||||
pImpl = new Impl(this);
|
||||
}
|
||||
|
||||
|
||||
|
@ -57,301 +44,210 @@ Type Shape::type() const noexcept
|
|||
|
||||
Result Shape::reset() noexcept
|
||||
{
|
||||
pImpl->rs.path.cmds.clear();
|
||||
pImpl->rs.path.pts.clear();
|
||||
|
||||
pImpl->rFlag |= RenderUpdateFlag::Path;
|
||||
|
||||
SHAPE(this)->resetPath();
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
uint32_t Shape::pathCommands(const PathCommand** cmds) const noexcept
|
||||
{
|
||||
if (cmds) *cmds = pImpl->rs.path.cmds.data;
|
||||
return pImpl->rs.path.cmds.count;
|
||||
if (cmds) *cmds = SHAPE(this)->rs.path.cmds.data;
|
||||
return SHAPE(this)->rs.path.cmds.count;
|
||||
}
|
||||
|
||||
|
||||
uint32_t Shape::pathCoords(const Point** pts) const noexcept
|
||||
{
|
||||
if (pts) *pts = pImpl->rs.path.pts.data;
|
||||
return pImpl->rs.path.pts.count;
|
||||
if (pts) *pts = SHAPE(this)->rs.path.pts.data;
|
||||
return SHAPE(this)->rs.path.pts.count;
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
|
||||
pImpl->grow(cmdCnt, ptsCnt);
|
||||
pImpl->append(cmds, cmdCnt, pts, ptsCnt);
|
||||
|
||||
pImpl->rFlag |= RenderUpdateFlag::Path;
|
||||
|
||||
return Result::Success;
|
||||
return SHAPE(this)->appendPath(cmds, cmdCnt, pts, ptsCnt);
|
||||
}
|
||||
|
||||
|
||||
Result Shape::moveTo(float x, float y) noexcept
|
||||
{
|
||||
pImpl->moveTo(x, y);
|
||||
|
||||
SHAPE(this)->moveTo(x, y);
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
Result Shape::lineTo(float x, float y) noexcept
|
||||
{
|
||||
pImpl->lineTo(x, y);
|
||||
|
||||
pImpl->rFlag |= RenderUpdateFlag::Path;
|
||||
|
||||
SHAPE(this)->lineTo(x, y);
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
Result Shape::cubicTo(float cx1, float cy1, float cx2, float cy2, float x, float y) noexcept
|
||||
{
|
||||
pImpl->cubicTo(cx1, cy1, cx2, cy2, x, y);
|
||||
|
||||
pImpl->rFlag |= RenderUpdateFlag::Path;
|
||||
|
||||
SHAPE(this)->cubicTo(cx1, cy1, cx2, cy2, x, y);
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
Result Shape::close() noexcept
|
||||
{
|
||||
pImpl->close();
|
||||
|
||||
pImpl->rFlag |= RenderUpdateFlag::Path;
|
||||
|
||||
SHAPE(this)->close();
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
Result Shape::appendCircle(float cx, float cy, float rx, float ry) noexcept
|
||||
{
|
||||
auto rxKappa = rx * PATH_KAPPA;
|
||||
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;
|
||||
|
||||
SHAPE(this)->appendCircle(cx, cy, rx, ry);
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
Result Shape::appendRect(float x, float y, float w, float h, float rx, float ry) noexcept
|
||||
{
|
||||
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) {
|
||||
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;
|
||||
|
||||
SHAPE(this)->appendRect(x, y, w, h, rx, ry);
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
Result Shape::fill(uint8_t r, uint8_t g, uint8_t b, uint8_t a) noexcept
|
||||
{
|
||||
if (pImpl->rs.fill) {
|
||||
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;
|
||||
|
||||
SHAPE(this)->fill(r, g, b, a);
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
Result Shape::fill(Fill* f) noexcept
|
||||
{
|
||||
if (!f) return Result::InvalidArguments;
|
||||
|
||||
if (pImpl->rs.fill && pImpl->rs.fill != f) delete(pImpl->rs.fill);
|
||||
pImpl->rs.fill = f;
|
||||
pImpl->rFlag |= RenderUpdateFlag::Gradient;
|
||||
|
||||
return Result::Success;
|
||||
return SHAPE(this)->fill(f);
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
const Fill* Shape::fill() const noexcept
|
||||
{
|
||||
return pImpl->rs.fill;
|
||||
return SHAPE(this)->rs.fill;
|
||||
}
|
||||
|
||||
|
||||
Result Shape::order(bool strokeFirst) noexcept
|
||||
{
|
||||
pImpl->strokeFirst(strokeFirst);
|
||||
SHAPE(this)->strokeFirst(strokeFirst);
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
Result Shape::strokeWidth(float width) noexcept
|
||||
{
|
||||
pImpl->strokeWidth(width);
|
||||
SHAPE(this)->strokeWidth(width);
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
{
|
||||
pImpl->strokeFill(r, g, b, a);
|
||||
SHAPE(this)->strokeFill(r, g, b, a);
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
Result Shape::strokeFill(Fill* f) noexcept
|
||||
{
|
||||
return pImpl->strokeFill(f);
|
||||
return SHAPE(this)->strokeFill(f);
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
{
|
||||
return pImpl->strokeDash(dashPattern, cnt, offset);
|
||||
return SHAPE(this)->strokeDash(dashPattern, cnt, offset);
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
{
|
||||
pImpl->strokeCap(cap);
|
||||
SHAPE(this)->strokeCap(cap);
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
Result Shape::strokeJoin(StrokeJoin join) noexcept
|
||||
{
|
||||
pImpl->strokeJoin(join);
|
||||
SHAPE(this)->strokeJoin(join);
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
Result Shape::strokeMiterlimit(float miterlimit) noexcept
|
||||
{
|
||||
// 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;
|
||||
// TODO Find out a reasonable max value.
|
||||
pImpl->strokeMiterlimit(miterlimit);
|
||||
return Result::Success;
|
||||
return SHAPE(this)->strokeMiterlimit(miterlimit);
|
||||
}
|
||||
|
||||
|
||||
StrokeCap Shape::strokeCap() const noexcept
|
||||
{
|
||||
return pImpl->rs.strokeCap();
|
||||
return SHAPE(this)->rs.strokeCap();
|
||||
}
|
||||
|
||||
|
||||
StrokeJoin Shape::strokeJoin() const noexcept
|
||||
{
|
||||
return pImpl->rs.strokeJoin();
|
||||
return SHAPE(this)->rs.strokeJoin();
|
||||
}
|
||||
|
||||
|
||||
float Shape::strokeMiterlimit() const noexcept
|
||||
{
|
||||
return pImpl->rs.strokeMiterlimit();
|
||||
return SHAPE(this)->rs.strokeMiterlimit();
|
||||
}
|
||||
|
||||
|
||||
Result Shape::strokeTrim(float begin, float end, bool simultaneous) noexcept
|
||||
{
|
||||
pImpl->strokeTrim(begin, end, simultaneous);
|
||||
SHAPE(this)->strokeTrim(begin, end, simultaneous);
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
Result Shape::fill(FillRule r) noexcept
|
||||
{
|
||||
pImpl->rs.rule = r;
|
||||
|
||||
SHAPE(this)->rs.rule = r;
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
FillRule Shape::fillRule() const noexcept
|
||||
{
|
||||
return pImpl->rs.rule;
|
||||
return SHAPE(this)->rs.rule;
|
||||
}
|
||||
|
|
|
@ -27,25 +27,22 @@
|
|||
#include "tvgMath.h"
|
||||
#include "tvgPaint.h"
|
||||
|
||||
#define SHAPE(A) PIMPL(A, Shape)
|
||||
|
||||
struct Shape::Impl
|
||||
struct Shape::Impl : Paint::Impl
|
||||
{
|
||||
RenderShape rs; //shape data
|
||||
RenderData rd = nullptr; //engine data
|
||||
Shape* shape;
|
||||
uint8_t rFlag = RenderUpdateFlag::None;
|
||||
uint8_t cFlag = CompositionFlag::Invalid;
|
||||
RenderShape rs;
|
||||
RenderData rd = nullptr;
|
||||
uint8_t compFlag = CompositionFlag::Invalid;
|
||||
uint8_t opacity; //for composition
|
||||
|
||||
Impl(Shape* s) : shape(s)
|
||||
Impl(Shape* s) : Paint::Impl(s)
|
||||
{
|
||||
}
|
||||
|
||||
~Impl()
|
||||
{
|
||||
if (auto renderer = PP(shape)->renderer) {
|
||||
renderer->dispose(rd);
|
||||
}
|
||||
if (renderer) renderer->dispose(rd);
|
||||
}
|
||||
|
||||
bool render(RenderMethod* renderer)
|
||||
|
@ -54,10 +51,10 @@ struct Shape::Impl
|
|||
|
||||
RenderCompositor* cmp = nullptr;
|
||||
|
||||
renderer->blend(PP(shape)->blendMethod);
|
||||
renderer->blend(blendMethod);
|
||||
|
||||
if (cFlag) {
|
||||
cmp = renderer->target(bounds(renderer), renderer->colorSpace(), static_cast<CompositionFlag>(cFlag));
|
||||
if (compFlag) {
|
||||
cmp = renderer->target(bounds(renderer), renderer->colorSpace(), static_cast<CompositionFlag>(compFlag));
|
||||
renderer->beginComposite(cmp, MaskMethod::None, opacity);
|
||||
}
|
||||
|
||||
|
@ -68,7 +65,7 @@ struct Shape::Impl
|
|||
|
||||
bool needComposition(uint8_t opacity)
|
||||
{
|
||||
cFlag = CompositionFlag::Invalid;
|
||||
compFlag = CompositionFlag::Invalid;
|
||||
|
||||
if (opacity == 0) return false;
|
||||
|
||||
|
@ -78,13 +75,13 @@ struct Shape::Impl
|
|||
|
||||
//translucent fill & stroke
|
||||
if (opacity < 255) {
|
||||
cFlag = CompositionFlag::Opacity;
|
||||
compFlag = CompositionFlag::Opacity;
|
||||
return true;
|
||||
}
|
||||
|
||||
//Composition test
|
||||
const Paint* target;
|
||||
auto method = shape->mask(&target);
|
||||
auto method = paint->mask(&target);
|
||||
if (!target) return false;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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)) {
|
||||
/* Overriding opacity value. If this scene is half-translucent,
|
||||
|
@ -115,8 +112,7 @@ struct Shape::Impl
|
|||
opacity = 255;
|
||||
}
|
||||
|
||||
rd = renderer->prepare(rs, rd, transform, clips, opacity, static_cast<RenderUpdateFlag>(pFlag | rFlag), clipper);
|
||||
rFlag = RenderUpdateFlag::None;
|
||||
rd = renderer->prepare(rs, rd, transform, clips, opacity, static_cast<RenderUpdateFlag>(pFlag | renderFlag), clipper);
|
||||
return rd;
|
||||
}
|
||||
|
||||
|
@ -191,6 +187,7 @@ struct Shape::Impl
|
|||
{
|
||||
rs.path.cmds.push(PathCommand::LineTo);
|
||||
rs.path.pts.push({x, y});
|
||||
renderFlag |= RenderUpdateFlag::Path;
|
||||
}
|
||||
|
||||
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({cx2, cy2});
|
||||
rs.path.pts.push({x, y});
|
||||
|
||||
renderFlag |= RenderUpdateFlag::Path;
|
||||
}
|
||||
|
||||
void close()
|
||||
{
|
||||
//Don't close multiple times.
|
||||
if (rs.path.cmds.count > 0 && rs.path.cmds.last() == PathCommand::Close) return;
|
||||
|
||||
rs.path.cmds.push(PathCommand::Close);
|
||||
renderFlag |= RenderUpdateFlag::Path;
|
||||
}
|
||||
|
||||
void strokeWidth(float width)
|
||||
{
|
||||
if (!rs.stroke) rs.stroke = new RenderStroke();
|
||||
rs.stroke->width = width;
|
||||
rFlag |= RenderUpdateFlag::Stroke;
|
||||
renderFlag |= RenderUpdateFlag::Stroke;
|
||||
}
|
||||
|
||||
void strokeTrim(float begin, float end, bool simultaneous)
|
||||
|
@ -223,13 +222,12 @@ struct Shape::Impl
|
|||
rs.stroke = new RenderStroke();
|
||||
}
|
||||
|
||||
if (tvg::equal(rs.stroke->trim.begin, begin) && tvg::equal(rs.stroke->trim.end, end) &&
|
||||
rs.stroke->trim.simultaneous == simultaneous) return;
|
||||
if (tvg::equal(rs.stroke->trim.begin, begin) && tvg::equal(rs.stroke->trim.end, end) && rs.stroke->trim.simultaneous == simultaneous) return;
|
||||
|
||||
rs.stroke->trim.begin = begin;
|
||||
rs.stroke->trim.end = end;
|
||||
rs.stroke->trim.simultaneous = simultaneous;
|
||||
rFlag |= RenderUpdateFlag::Stroke;
|
||||
renderFlag |= RenderUpdateFlag::Stroke;
|
||||
}
|
||||
|
||||
bool strokeTrim(float* begin, float* end)
|
||||
|
@ -249,21 +247,26 @@ struct Shape::Impl
|
|||
{
|
||||
if (!rs.stroke) rs.stroke = new RenderStroke();
|
||||
rs.stroke->cap = cap;
|
||||
rFlag |= RenderUpdateFlag::Stroke;
|
||||
renderFlag |= RenderUpdateFlag::Stroke;
|
||||
}
|
||||
|
||||
void strokeJoin(StrokeJoin join)
|
||||
{
|
||||
if (!rs.stroke) rs.stroke = new RenderStroke();
|
||||
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();
|
||||
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)
|
||||
|
@ -272,12 +275,12 @@ struct Shape::Impl
|
|||
if (rs.stroke->fill) {
|
||||
delete(rs.stroke->fill);
|
||||
rs.stroke->fill = nullptr;
|
||||
rFlag |= RenderUpdateFlag::GradientStroke;
|
||||
renderFlag |= RenderUpdateFlag::GradientStroke;
|
||||
}
|
||||
|
||||
rs.stroke->color = {r, g, b, a};
|
||||
|
||||
rFlag |= RenderUpdateFlag::Stroke;
|
||||
renderFlag |= RenderUpdateFlag::Stroke;
|
||||
}
|
||||
|
||||
Result strokeFill(Fill* f)
|
||||
|
@ -289,8 +292,8 @@ struct Shape::Impl
|
|||
rs.stroke->fill = f;
|
||||
rs.stroke->color.a = 0;
|
||||
|
||||
rFlag |= RenderUpdateFlag::Stroke;
|
||||
rFlag |= RenderUpdateFlag::GradientStroke;
|
||||
renderFlag |= RenderUpdateFlag::Stroke;
|
||||
renderFlag |= RenderUpdateFlag::GradientStroke;
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
@ -325,7 +328,7 @@ struct Shape::Impl
|
|||
}
|
||||
rs.stroke->dashCnt = cnt;
|
||||
rs.stroke->dashOffset = offset;
|
||||
rFlag |= RenderUpdateFlag::Stroke;
|
||||
renderFlag |= RenderUpdateFlag::Stroke;
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
@ -340,12 +343,99 @@ struct Shape::Impl
|
|||
{
|
||||
if (!rs.stroke) rs.stroke = new RenderStroke();
|
||||
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)
|
||||
|
@ -354,11 +444,11 @@ struct Shape::Impl
|
|||
if (shape) shape->reset();
|
||||
else shape = Shape::gen();
|
||||
|
||||
auto dup = shape->pImpl;
|
||||
auto dup = SHAPE(shape);
|
||||
delete(dup->rs.fill);
|
||||
|
||||
//Default Properties
|
||||
dup->rFlag = RenderUpdateFlag::All;
|
||||
dup->renderFlag = RenderUpdateFlag::All;
|
||||
dup->rs.rule = rs.rule;
|
||||
dup->rs.color = rs.color;
|
||||
|
||||
|
@ -384,7 +474,7 @@ struct Shape::Impl
|
|||
|
||||
void reset()
|
||||
{
|
||||
PP(shape)->reset();
|
||||
PAINT(paint)->reset();
|
||||
rs.path.cmds.clear();
|
||||
rs.path.pts.clear();
|
||||
|
||||
|
|
|
@ -25,38 +25,21 @@
|
|||
|
||||
#ifdef THORVG_SW_RASTER_SUPPORT
|
||||
#include "tvgSwRenderer.h"
|
||||
#else
|
||||
class SwRenderer : public RenderMethod
|
||||
{
|
||||
//Non Supported. Dummy Class */
|
||||
};
|
||||
#endif
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
struct SwCanvas::Impl
|
||||
SwCanvas::SwCanvas()
|
||||
{
|
||||
};
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
#ifdef THORVG_SW_RASTER_SUPPORT
|
||||
SwCanvas::SwCanvas() : Canvas(SwRenderer::gen()), pImpl(nullptr)
|
||||
#else
|
||||
SwCanvas::SwCanvas() : Canvas(nullptr), pImpl(nullptr)
|
||||
pImpl->renderer = SwRenderer::gen();
|
||||
pImpl->renderer->ref();
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
SwCanvas::~SwCanvas()
|
||||
{
|
||||
delete(pImpl);
|
||||
//TODO:
|
||||
}
|
||||
|
||||
|
||||
|
@ -64,11 +47,11 @@ Result SwCanvas::mempool(MempoolPolicy policy) noexcept
|
|||
{
|
||||
#ifdef THORVG_SW_RASTER_SUPPORT
|
||||
//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;
|
||||
|
||||
//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);
|
||||
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::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;
|
||||
}
|
||||
|
||||
//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->target(buffer, stride, w, h, static_cast<ColorSpace>(cs))) return Result::InvalidArguments;
|
||||
Canvas::pImpl->vport = {0, 0, (int32_t)w, (int32_t)h};
|
||||
renderer->viewport(Canvas::pImpl->vport);
|
||||
pImpl->vport = {0, 0, (int32_t)w, (int32_t)h};
|
||||
renderer->viewport(pImpl->vport);
|
||||
|
||||
//FIXME: The value must be associated with an individual canvas instance.
|
||||
ImageLoader::cs = static_cast<ColorSpace>(cs);
|
||||
|
||||
//Paints must be updated again with this new target.
|
||||
Canvas::pImpl->status = Status::Damaged;
|
||||
pImpl->status = Status::Damaged;
|
||||
|
||||
return Result::Success;
|
||||
#endif
|
||||
|
|
|
@ -24,37 +24,21 @@
|
|||
#include "tvgText.h"
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
|
||||
Text::Text() : pImpl(new Impl(this))
|
||||
Text::Text()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Text::~Text()
|
||||
{
|
||||
delete(pImpl);
|
||||
pImpl = new Impl(this);
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
{
|
||||
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
|
||||
{
|
||||
return pImpl->shape->fill(r, g, b);
|
||||
return TEXT(this)->shape->fill(r, g, b);
|
||||
}
|
||||
|
||||
|
||||
Result Text::fill(Fill* f) noexcept
|
||||
{
|
||||
return pImpl->shape->fill(f);
|
||||
return TEXT(this)->shape->fill(f);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -28,17 +28,18 @@
|
|||
#include "tvgFill.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;
|
||||
Text* paint;
|
||||
Shape* shape;
|
||||
char* utf8 = nullptr;
|
||||
float fontSize;
|
||||
bool italic = 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)
|
||||
{
|
||||
return P(shape)->bounds(renderer);
|
||||
return SHAPE(shape)->bounds(renderer);
|
||||
}
|
||||
|
||||
bool render(RenderMethod* renderer)
|
||||
{
|
||||
if (!loader) return true;
|
||||
renderer->blend(PP(paint)->blendMethod);
|
||||
return PP(shape)->render(renderer);
|
||||
renderer->blend(blendMethod);
|
||||
return PAINT(shape)->render(renderer);
|
||||
}
|
||||
|
||||
bool load()
|
||||
|
@ -112,30 +113,31 @@ struct Text::Impl
|
|||
if (!load()) return nullptr;
|
||||
|
||||
//transform the gradient coordinates based on the final scaled font.
|
||||
auto fill = P(shape)->rs.fill;
|
||||
if (fill && P(shape)->rFlag & RenderUpdateFlag::Gradient) {
|
||||
auto fill = SHAPE(shape)->rs.fill;
|
||||
if (fill && SHAPE(shape)->renderFlag & RenderUpdateFlag::Gradient) {
|
||||
auto scale = 1.0f / loader->scale;
|
||||
if (fill->type() == Type::LinearGradient) {
|
||||
P(static_cast<LinearGradient*>(fill))->x1 *= scale;
|
||||
P(static_cast<LinearGradient*>(fill))->y1 *= scale;
|
||||
P(static_cast<LinearGradient*>(fill))->x2 *= scale;
|
||||
P(static_cast<LinearGradient*>(fill))->y2 *= scale;
|
||||
LINEAR(fill)->x1 *= scale;
|
||||
LINEAR(fill)->y1 *= scale;
|
||||
LINEAR(fill)->x2 *= scale;
|
||||
LINEAR(fill)->y2 *= scale;
|
||||
} else {
|
||||
P(static_cast<RadialGradient*>(fill))->cx *= scale;
|
||||
P(static_cast<RadialGradient*>(fill))->cy *= scale;
|
||||
P(static_cast<RadialGradient*>(fill))->r *= scale;
|
||||
P(static_cast<RadialGradient*>(fill))->fx *= scale;
|
||||
P(static_cast<RadialGradient*>(fill))->fy *= scale;
|
||||
P(static_cast<RadialGradient*>(fill))->fr *= scale;
|
||||
RADIAL(fill)->cx *= scale;
|
||||
RADIAL(fill)->cy *= scale;
|
||||
RADIAL(fill)->r *= scale;
|
||||
RADIAL(fill)->fx *= scale;
|
||||
RADIAL(fill)->fy *= 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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -146,8 +148,8 @@ struct Text::Impl
|
|||
load();
|
||||
|
||||
auto text = Text::gen();
|
||||
auto dup = text->pImpl;
|
||||
P(shape)->duplicate(dup->shape);
|
||||
auto dup = TEXT(text);
|
||||
SHAPE(shape)->duplicate(dup->shape);
|
||||
|
||||
if (loader) {
|
||||
dup->loader = loader;
|
||||
|
|
|
@ -26,32 +26,20 @@
|
|||
#include "tvgWgRenderer.h"
|
||||
#endif
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
struct WgCanvas::Impl
|
||||
WgCanvas::WgCanvas()
|
||||
{
|
||||
};
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
#ifdef THORVG_WG_RASTER_SUPPORT
|
||||
WgCanvas::WgCanvas() : Canvas(WgRenderer::gen()), pImpl(nullptr)
|
||||
#else
|
||||
WgCanvas::WgCanvas() : Canvas(nullptr), pImpl(nullptr)
|
||||
pImpl->renderer = WgRenderer::gen();
|
||||
pImpl->renderer->ref();
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
WgCanvas::~WgCanvas()
|
||||
{
|
||||
#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);
|
||||
#endif
|
||||
}
|
||||
|
@ -62,20 +50,20 @@ Result WgCanvas::target(void* device, void* instance, void* target, uint32_t w,
|
|||
#ifdef THORVG_WG_RASTER_SUPPORT
|
||||
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;
|
||||
}
|
||||
|
||||
//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->target((WGPUDevice)device, (WGPUInstance)instance, target, w, h, type)) return Result::Unknown;
|
||||
Canvas::pImpl->vport = {0, 0, (int32_t)w, (int32_t)h};
|
||||
renderer->viewport(Canvas::pImpl->vport);
|
||||
pImpl->vport = {0, 0, (int32_t)w, (int32_t)h};
|
||||
renderer->viewport(pImpl->vport);
|
||||
|
||||
//Paints must be updated again with this new target.
|
||||
Canvas::pImpl->status = Status::Damaged;
|
||||
pImpl->status = Status::Damaged;
|
||||
|
||||
return Result::Success;
|
||||
#endif
|
||||
|
|
|
@ -39,7 +39,7 @@ public:
|
|||
WGPUBindGroupLayout layoutBuffer1Un{};
|
||||
WGPUBindGroupLayout layoutBuffer2Un{};
|
||||
WGPUBindGroupLayout layoutBuffer3Un{};
|
||||
public:
|
||||
|
||||
WGPUBindGroup createBindGroupTexSampled(WGPUSampler sampler, WGPUTextureView texView);
|
||||
WGPUBindGroup createBindGroupTexSampledBuff1Un(WGPUSampler sampler, WGPUTextureView texView, WGPUBuffer buff);
|
||||
WGPUBindGroup createBindGroupTexSampledBuff2Un(WGPUSampler sampler, WGPUTextureView texView, WGPUBuffer buff0, WGPUBuffer buff1);
|
||||
|
@ -52,7 +52,7 @@ public:
|
|||
WGPUBindGroup createBindGroupBuffer3Un(WGPUBuffer buff0, WGPUBuffer buff1, WGPUBuffer buff2);
|
||||
void releaseBindGroup(WGPUBindGroup& bindGroup);
|
||||
void releaseBindGroupLayout(WGPUBindGroupLayout& bindGroupLayout);
|
||||
public:
|
||||
|
||||
void initialize(WGPUDevice device);
|
||||
void release();
|
||||
};
|
||||
|
|
|
@ -62,6 +62,29 @@ private:
|
|||
|
||||
// viewport utilities
|
||||
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:
|
||||
void initialize(WgContext& context, uint32_t width, uint32_t height);
|
||||
void initPools(WgContext& context);
|
||||
|
@ -81,28 +104,6 @@ public:
|
|||
|
||||
// blit render storage to texture view (f.e. screen buffer)
|
||||
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_
|
||||
|
|
|
@ -46,7 +46,7 @@ private:
|
|||
WGPUShaderModule shader_scene_compose{};
|
||||
// shader blit
|
||||
WGPUShaderModule shader_blit{};
|
||||
private:
|
||||
|
||||
// layouts helpers
|
||||
WGPUPipelineLayout layout_stencil{};
|
||||
WGPUPipelineLayout layout_depth{};
|
||||
|
@ -93,7 +93,6 @@ public:
|
|||
WGPURenderPipeline blit{};
|
||||
private:
|
||||
void releaseGraphicHandles(WgContext& context);
|
||||
private:
|
||||
WGPUShaderModule createShaderModule(WGPUDevice device, const char* label, const char* code);
|
||||
WGPUPipelineLayout createPipelineLayout(WGPUDevice device, const WGPUBindGroupLayout* bindGroupLayouts, const uint32_t bindGroupLayoutsCount);
|
||||
WGPURenderPipeline createRenderPipeline(
|
||||
|
|
Loading…
Add table
Reference in a new issue