common: optimization pImpl data structures

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

View file

@ -43,12 +43,16 @@
#define _TVG_DECLARE_PRIVATE(A) \
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);
};
/** @}*/

View file

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

View file

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

View file

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

View file

@ -20,8 +20,6 @@ namespace tvg
class TVG_API LottieAnimation final : public Animation
{
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

View file

@ -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);
}

View file

@ -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;

View file

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

View file

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

View file

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

View file

@ -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')) {

View file

@ -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;

View file

@ -479,8 +479,8 @@ bool TtfReader::convert(Shape* shape, TtfGlyphMetrics& gmetrics, const Point& of
if (!this->points(outline, flags, pts, ptsCnt, offset + kerning)) return false;
//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);

View file

@ -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;
}
}

View file

@ -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)
{

View file

@ -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 = {};

View file

@ -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;
}

View file

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

View file

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

View file

@ -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;
}

View file

@ -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;

View file

@ -66,15 +66,17 @@ static void _calculateCoefficients(const SwFill* fill, uint32_t x, uint32_t y, f
static uint32_t _estimateAAMargin(const Fill* fdata)
{
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;

View file

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

View file

@ -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;

View file

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

View file

@ -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;

View file

@ -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)

View file

@ -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);
}

View file

@ -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;
}
};

View file

@ -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

View file

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

View file

@ -33,10 +33,10 @@
#define PAINT_METHOD(ret, METHOD) \
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);
}

View file

@ -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();
};
}

View file

@ -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);
}

View file

@ -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_

View file

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

View file

@ -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);
}

View file

@ -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_

View file

@ -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;
}

View file

@ -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();

View file

@ -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

View file

@ -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);
}

View file

@ -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;

View file

@ -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

View file

@ -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();
};

View file

@ -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_

View file

@ -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(