mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-08 13:43:43 +00:00
common shape: revise scale/rotate approach.
Come to think of it, this optimized method is not so useful, it could just bring the user misunderstanding and not to efficient as I expected in the most cases. So, changed policy for transformation behaviors. it keeps the properties as others but leaves it to the backend implementation. Plus, this change contains the correct RenderUpdateFlag. You can refer the flag in the backend to figure out which kinds of properites has been updated Change-Id: Ibe0494712598a8161950b9ae2e22ac45bed1c47b
This commit is contained in:
parent
30ac2da1a3
commit
682bc25298
12 changed files with 143 additions and 96 deletions
|
@ -79,6 +79,8 @@ public:
|
|||
virtual int scale(float factor) = 0;
|
||||
|
||||
virtual int bounds(float&x, float& y, float& w, float& h) const = 0;
|
||||
virtual float scale() const = 0;
|
||||
virtual float rotate() const = 0;
|
||||
};
|
||||
|
||||
|
||||
|
@ -139,10 +141,12 @@ public:
|
|||
int rotate(float degree) noexcept override;
|
||||
int scale(float factor) noexcept override;
|
||||
|
||||
int pathCommands(const PathCommand** cmds) const noexcept;
|
||||
int pathCoords(const Point** pts) const noexcept;
|
||||
size_t pathCommands(const PathCommand** cmds) const noexcept;
|
||||
size_t pathCoords(const Point** pts) const noexcept;
|
||||
int fill(size_t* r, size_t* g, size_t* b, size_t* a) const noexcept;
|
||||
int bounds(float&x, float& y, float& w, float& h) const noexcept override;
|
||||
float scale() const noexcept override;
|
||||
float rotate() const noexcept override;
|
||||
|
||||
static std::unique_ptr<Shape> gen() noexcept;
|
||||
|
||||
|
@ -173,6 +177,8 @@ public:
|
|||
int scale(float factor) noexcept override;
|
||||
|
||||
int bounds(float&x, float& y, float& w, float& h) const noexcept override;
|
||||
float scale() const noexcept override;
|
||||
float rotate() const noexcept override;
|
||||
|
||||
static std::unique_ptr<Scene> gen() noexcept;
|
||||
|
||||
|
|
|
@ -65,7 +65,7 @@ bool GlRenderer::dispose(const Shape& shape, void *data)
|
|||
}
|
||||
|
||||
|
||||
void* GlRenderer::prepare(const Shape& shape, void* data, UpdateFlag flags)
|
||||
void* GlRenderer::prepare(const Shape& shape, void* data, RenderUpdateFlag flags)
|
||||
{
|
||||
//prepare shape data
|
||||
GlShape* sdata = static_cast<GlShape*>(data);
|
||||
|
|
|
@ -23,7 +23,7 @@ namespace tvg
|
|||
class GlRenderer : public RenderMethod
|
||||
{
|
||||
public:
|
||||
void* prepare(const Shape& shape, void* data, UpdateFlag flags) override;
|
||||
void* prepare(const Shape& shape, void* data, RenderUpdateFlag flags) override;
|
||||
bool dispose(const Shape& shape, void *data) override;
|
||||
bool render(const Shape& shape, void *data) override;
|
||||
bool clear() override;
|
||||
|
|
|
@ -94,7 +94,7 @@ struct SwShape
|
|||
|
||||
void shapeReset(SwShape& sdata);
|
||||
bool shapeGenOutline(const Shape& shape, SwShape& sdata);
|
||||
void shapeDelOutline(const Shape& shape, SwShape& sdata);
|
||||
void shapeDelOutline(SwShape& sdata);
|
||||
bool shapeGenRle(const Shape& shape, SwShape& sdata, const SwSize& clip);
|
||||
bool shapeTransformOutline(const Shape& shape, SwShape& sdata);
|
||||
SwRleData* rleRender(const SwShape& sdata, const SwSize& clip);
|
||||
|
|
|
@ -81,7 +81,7 @@ bool SwRenderer::dispose(const Shape& shape, void *data)
|
|||
return true;
|
||||
}
|
||||
|
||||
void* SwRenderer::prepare(const Shape& shape, void* data, UpdateFlag flags)
|
||||
void* SwRenderer::prepare(const Shape& shape, void* data, RenderUpdateFlag flags)
|
||||
{
|
||||
//prepare shape data
|
||||
SwShape* sdata = static_cast<SwShape*>(data);
|
||||
|
@ -90,7 +90,7 @@ void* SwRenderer::prepare(const Shape& shape, void* data, UpdateFlag flags)
|
|||
assert(sdata);
|
||||
}
|
||||
|
||||
if (flags == UpdateFlag::None) return nullptr;
|
||||
if (flags == RenderUpdateFlag::None) return sdata;
|
||||
|
||||
//invisible?
|
||||
size_t alpha;
|
||||
|
@ -98,13 +98,14 @@ void* SwRenderer::prepare(const Shape& shape, void* data, UpdateFlag flags)
|
|||
if (alpha == 0) return sdata;
|
||||
|
||||
//TODO: Threading
|
||||
if (flags & UpdateFlag::Path) {
|
||||
if (flags & (RenderUpdateFlag::Path | RenderUpdateFlag::Transform)) {
|
||||
shapeReset(*sdata);
|
||||
if (!shapeGenOutline(shape, *sdata)) return sdata;
|
||||
if (!shapeTransformOutline(shape, *sdata)) return sdata;
|
||||
|
||||
SwSize clip = {static_cast<SwCoord>(surface.w), static_cast<SwCoord>(surface.h)};
|
||||
if (!shapeGenRle(shape, *sdata, clip)) return sdata;
|
||||
shapeDelOutline(*sdata);
|
||||
}
|
||||
|
||||
return sdata;
|
||||
|
|
|
@ -22,7 +22,7 @@ class SwRenderer : public RenderMethod
|
|||
public:
|
||||
Surface surface;
|
||||
|
||||
void* prepare(const Shape& shape, void* data, UpdateFlag flags) override;
|
||||
void* prepare(const Shape& shape, void* data, RenderUpdateFlag flags) override;
|
||||
bool dispose(const Shape& shape, void *data) override;
|
||||
bool render(const Shape& shape, void *data) override;
|
||||
bool target(uint32_t* buffer, size_t stride, size_t w, size_t h);
|
||||
|
|
|
@ -211,26 +211,57 @@ void _deleteRle(SwShape& sdata)
|
|||
}
|
||||
|
||||
|
||||
void _deleteOutline(SwShape& sdata)
|
||||
{
|
||||
if (!sdata.outline) return;
|
||||
|
||||
SwOutline* outline = sdata.outline;
|
||||
if (outline->cntrs) free(outline->cntrs);
|
||||
if (outline->pts) free(outline->pts);
|
||||
if (outline->types) free(outline->types);
|
||||
free(outline);
|
||||
|
||||
sdata.outline = nullptr;
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
bool shapeTransformOutline(const Shape& shape, SwShape& sdata)
|
||||
{
|
||||
//TODO:
|
||||
constexpr auto PI = 3.141592f;
|
||||
|
||||
auto degree = shape.rotate();
|
||||
auto scale = shape.scale();
|
||||
bool rotateOn = false;
|
||||
bool scaleOn = false;
|
||||
|
||||
if (fabsf(degree) > FLT_EPSILON) rotateOn = true;
|
||||
if (fabsf(scale - 1) > FLT_EPSILON) scaleOn = true;
|
||||
|
||||
if (!rotateOn && !scaleOn) return true;
|
||||
|
||||
auto outline = sdata.outline;
|
||||
assert(outline);
|
||||
|
||||
float x, y, w, h;
|
||||
shape.bounds(x, y, w, h);
|
||||
|
||||
auto cx = x + w * 0.5f;
|
||||
auto cy = y + h * 0.5f;
|
||||
|
||||
float radian, cosVal, sinVal;
|
||||
if (rotateOn) {
|
||||
radian = degree / 180.0f * PI;
|
||||
cosVal = cosf(radian);
|
||||
sinVal = sinf(radian);
|
||||
}
|
||||
|
||||
for(size_t i = 0; i < outline->ptsCnt; ++i) {
|
||||
auto dx = static_cast<float>(outline->pts[i].x >> 6) - cx;
|
||||
auto dy = static_cast<float>(outline->pts[i].y >> 6) - cy;
|
||||
if (rotateOn) {
|
||||
auto tx = (cosVal * dx - sinVal * dy);
|
||||
auto ty = (sinVal * dx + cosVal * dy);
|
||||
dx = tx;
|
||||
dy = ty;
|
||||
}
|
||||
if (scaleOn) {
|
||||
dx *= scale;
|
||||
dy *= scale;
|
||||
}
|
||||
auto pt = Point{dx + cx, dy + cy};
|
||||
outline->pts[i] = TO_SWPOINT(&pt);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -246,7 +277,6 @@ bool shapeGenRle(const Shape& shape, SwShape& sdata, const SwSize& clip)
|
|||
(sdata.bbox.min.y + sdata.bbox.max.y < 0)) goto end;
|
||||
|
||||
sdata.rle = rleRender(sdata, clip);
|
||||
_deleteOutline(sdata);
|
||||
|
||||
end:
|
||||
if (sdata.rle) return true;
|
||||
|
@ -254,9 +284,23 @@ end:
|
|||
}
|
||||
|
||||
|
||||
void shapeDelOutline(SwShape& sdata)
|
||||
{
|
||||
if (!sdata.outline) return;
|
||||
|
||||
SwOutline* outline = sdata.outline;
|
||||
if (outline->cntrs) free(outline->cntrs);
|
||||
if (outline->pts) free(outline->pts);
|
||||
if (outline->types) free(outline->types);
|
||||
free(outline);
|
||||
|
||||
sdata.outline = nullptr;
|
||||
}
|
||||
|
||||
|
||||
void shapeReset(SwShape& sdata)
|
||||
{
|
||||
_deleteOutline(sdata);
|
||||
shapeDelOutline(sdata);
|
||||
_deleteRle(sdata);
|
||||
_initBBox(sdata);
|
||||
}
|
||||
|
@ -277,7 +321,7 @@ bool shapeGenOutline(const Shape& shape, SwShape& sdata)
|
|||
auto outlinePtsCnt = 0;
|
||||
auto outlineCntrsCnt = 0;
|
||||
|
||||
for (auto i = 0; i < cmdCnt; ++i) {
|
||||
for (size_t i = 0; i < cmdCnt; ++i) {
|
||||
switch(*(cmds + i)) {
|
||||
case PathCommand::Close: {
|
||||
++outlinePtsCnt;
|
||||
|
@ -311,7 +355,6 @@ bool shapeGenOutline(const Shape& shape, SwShape& sdata)
|
|||
cout << "Outline was already allocated? How?" << endl;
|
||||
}
|
||||
|
||||
//TODO: Probabry we can copy pts from shape directly.
|
||||
_growOutlinePoint(*outline, outlinePtsCnt);
|
||||
_growOutlineContour(*outline, outlineCntrsCnt);
|
||||
|
||||
|
|
|
@ -28,12 +28,13 @@ struct Surface
|
|||
size_t w, h;
|
||||
};
|
||||
|
||||
enum RenderUpdateFlag {None = 0, Path = 1, Fill = 2, Transform = 4, All = 8};
|
||||
|
||||
class RenderMethod
|
||||
{
|
||||
public:
|
||||
enum UpdateFlag { None = 0, Path = 1, Fill = 2, All = 3 };
|
||||
virtual ~RenderMethod() {}
|
||||
virtual void* prepare(const Shape& shape, void* data, UpdateFlag flags) = 0;
|
||||
virtual void* prepare(const Shape& shape, void* data, RenderUpdateFlag flags) = 0;
|
||||
virtual bool dispose(const Shape& shape, void *data) = 0;
|
||||
virtual bool render(const Shape& shape, void *data) = 0;
|
||||
virtual bool clear() = 0;
|
||||
|
|
|
@ -82,4 +82,16 @@ int Scene::bounds(float& x, float& y, float& w, float& h) const noexcept
|
|||
return 0;
|
||||
}
|
||||
|
||||
|
||||
float Scene::scale() const noexcept
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
float Scene::rotate() const noexcept
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* _TVG_SCENE_CPP_ */
|
|
@ -44,6 +44,7 @@ struct Shape::Impl
|
|||
float scale = 1;
|
||||
float rotate = 0;
|
||||
void *edata = nullptr; //engine data
|
||||
size_t flag = RenderUpdateFlag::None;
|
||||
|
||||
Impl() : path(new ShapePath)
|
||||
{
|
||||
|
@ -55,14 +56,6 @@ struct Shape::Impl
|
|||
if (stroke) delete(stroke);
|
||||
if (fill) delete(fill);
|
||||
}
|
||||
|
||||
bool update()
|
||||
{
|
||||
if (path->scale(scale)) scale = 1;
|
||||
if (path->rotate(rotate)) rotate = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
@ -99,10 +92,10 @@ int Shape::update(RenderMethod* engine) noexcept
|
|||
auto impl = pImpl.get();
|
||||
assert(impl);
|
||||
|
||||
if (!impl->update()) return -1;
|
||||
impl->edata = engine->prepare(*this, impl->edata, RenderMethod::UpdateFlag::All);
|
||||
impl->edata = engine->prepare(*this, impl->edata, static_cast<RenderUpdateFlag>(impl->flag));
|
||||
impl->flag = RenderUpdateFlag::None;
|
||||
if (impl->edata) return 0;
|
||||
return - 1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
@ -113,11 +106,13 @@ int Shape::reset() noexcept
|
|||
|
||||
impl->path->reset();
|
||||
|
||||
impl->flag |= RenderUpdateFlag::Path;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int Shape::pathCommands(const PathCommand** cmds) const noexcept
|
||||
size_t Shape::pathCommands(const PathCommand** cmds) const noexcept
|
||||
{
|
||||
auto impl = pImpl.get();
|
||||
assert(impl && impl->path && cmds);
|
||||
|
@ -128,7 +123,7 @@ int Shape::pathCommands(const PathCommand** cmds) const noexcept
|
|||
}
|
||||
|
||||
|
||||
int Shape::pathCoords(const Point** pts) const noexcept
|
||||
size_t Shape::pathCoords(const Point** pts) const noexcept
|
||||
{
|
||||
auto impl = pImpl.get();
|
||||
assert(impl && impl->path && pts);
|
||||
|
@ -150,6 +145,8 @@ int Shape::appendPath(const PathCommand *cmds, size_t cmdCnt, const Point* pts,
|
|||
impl->path->grow(cmdCnt, ptsCnt);
|
||||
impl->path->append(cmds, cmdCnt, pts, ptsCnt);
|
||||
|
||||
impl->flag |= RenderUpdateFlag::Path;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -161,6 +158,8 @@ int Shape::moveTo(float x, float y) noexcept
|
|||
|
||||
impl->path->moveTo(x, y);
|
||||
|
||||
impl->flag |= RenderUpdateFlag::Path;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -172,6 +171,8 @@ int Shape::lineTo(float x, float y) noexcept
|
|||
|
||||
impl->path->lineTo(x, y);
|
||||
|
||||
impl->flag |= RenderUpdateFlag::Path;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -183,6 +184,8 @@ int Shape::cubicTo(float cx1, float cy1, float cx2, float cy2, float x, float y)
|
|||
|
||||
impl->path->cubicTo(cx1, cy1, cx2, cy2, x, y);
|
||||
|
||||
impl->flag |= RenderUpdateFlag::Path;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -194,6 +197,8 @@ int Shape::close() noexcept
|
|||
|
||||
impl->path->close();
|
||||
|
||||
impl->flag |= RenderUpdateFlag::Path;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -214,6 +219,8 @@ int Shape::appendCircle(float cx, float cy, float radiusW, float radiusH) noexce
|
|||
impl->path->cubicTo(cx - radiusW, cy - halfKappaH, cx - halfKappaW, cy - radiusH, cx, cy - radiusH);
|
||||
impl->path->close();
|
||||
|
||||
impl->flag |= RenderUpdateFlag::Path;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -253,6 +260,8 @@ int Shape::appendRect(float x, float y, float w, float h, float cornerRadius) no
|
|||
impl->path->close();
|
||||
}
|
||||
|
||||
impl->flag |= RenderUpdateFlag::Path;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -266,6 +275,7 @@ int Shape::fill(size_t r, size_t g, size_t b, size_t a) noexcept
|
|||
impl->color[1] = g;
|
||||
impl->color[2] = b;
|
||||
impl->color[3] = a;
|
||||
impl->flag |= RenderUpdateFlag::Fill;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -287,12 +297,13 @@ int Shape::fill(size_t* r, size_t* g, size_t* b, size_t* a) const noexcept
|
|||
|
||||
int Shape::scale(float factor) noexcept
|
||||
{
|
||||
if (factor < FLT_EPSILON || fabsf(factor - 1) <= FLT_EPSILON) return -1;
|
||||
|
||||
auto impl = pImpl.get();
|
||||
assert(impl);
|
||||
|
||||
impl->scale *= factor;
|
||||
if (fabsf(factor) < FLT_EPSILON || fabsf(factor - impl->scale) <= FLT_EPSILON) return -1;
|
||||
|
||||
impl->scale = factor;
|
||||
impl->flag |= RenderUpdateFlag::Transform;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -300,12 +311,13 @@ int Shape::scale(float factor) noexcept
|
|||
|
||||
int Shape::rotate(float degree) noexcept
|
||||
{
|
||||
if (fabsf(degree) <= FLT_EPSILON) return -1;
|
||||
|
||||
auto impl = pImpl.get();
|
||||
assert(impl);
|
||||
|
||||
impl->rotate += degree;
|
||||
if (fabsf(degree - impl->rotate) <= FLT_EPSILON) return -1;
|
||||
|
||||
impl->rotate = degree;
|
||||
impl->flag |= RenderUpdateFlag::Transform;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -321,4 +333,22 @@ int Shape::bounds(float& x, float& y, float& w, float& h) const noexcept
|
|||
return 0;
|
||||
}
|
||||
|
||||
|
||||
float Shape::scale() const noexcept
|
||||
{
|
||||
auto impl = pImpl.get();
|
||||
assert(impl);
|
||||
|
||||
return impl->scale;
|
||||
}
|
||||
|
||||
|
||||
float Shape::rotate() const noexcept
|
||||
{
|
||||
auto impl = pImpl.get();
|
||||
assert(impl);
|
||||
|
||||
return impl->rotate;
|
||||
}
|
||||
|
||||
#endif //_TVG_SHAPE_CPP_
|
||||
|
|
|
@ -134,49 +134,6 @@ struct ShapePath
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool rotate(float degree)
|
||||
{
|
||||
constexpr auto PI = 3.141592f;
|
||||
|
||||
if (fabsf(degree) <= FLT_EPSILON) return false;
|
||||
|
||||
float x, y, w, h;
|
||||
if (!bounds(x, y, w, h)) return false;
|
||||
|
||||
auto radian = degree / 180.0f * PI;
|
||||
auto cx = x + w * 0.5f;
|
||||
auto cy = y + h * 0.5f;
|
||||
auto cosVal = cosf(radian);
|
||||
auto sinVal = sinf(radian);
|
||||
|
||||
for(size_t i = 0; i < ptsCnt; ++i) {
|
||||
auto dx = pts[i].x - cx;
|
||||
auto dy = pts[i].y - cy;
|
||||
pts[i].x = (cosVal * dx - sinVal * dy) + cx;
|
||||
pts[i].y = (sinVal * dx + cosVal * dy) + cy;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool scale(float factor)
|
||||
{
|
||||
if (fabsf(factor - 1) <= FLT_EPSILON) return false;
|
||||
|
||||
float x, y, w, h;
|
||||
if (!bounds(x, y, w, h)) return false;
|
||||
|
||||
auto cx = x + w * 0.5f;
|
||||
auto cy = y + h * 0.5f;
|
||||
|
||||
for(size_t i = 0; i < ptsCnt; ++i) {
|
||||
pts[i].x = (pts[i].x - cx) * factor + cx;
|
||||
pts[i].y = (pts[i].y - cy) * factor + cy;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
#endif //_TVG_SHAPE_PATH_CPP_
|
||||
|
|
|
@ -24,6 +24,8 @@ void tvgtest()
|
|||
pShape = shape.get();
|
||||
|
||||
shape->appendRect(-100, -100, 200, 200, 0);
|
||||
|
||||
//fill and rotate properties will be retained
|
||||
shape->fill(127, 255, 255, 255);
|
||||
shape->rotate(45);
|
||||
canvas->push(move(shape));
|
||||
|
@ -42,11 +44,6 @@ void transit_cb(Elm_Transit_Effect *effect, Elm_Transit* transit, double progres
|
|||
pShape->reset(); //reset path
|
||||
|
||||
pShape->appendRect(-100 + (800 * progress), -100 + (800 * progress), 200, 200, (100 * progress));
|
||||
|
||||
/* rotate, scale won't be retained, when you call reset() for the shape, these values will be reset as well.
|
||||
These are working in fire & forget method, it actually modify the path data for avoiding compuatation every frames.
|
||||
Thus user needs to keep the last values to understand the final accumulated values. */
|
||||
pShape->rotate(45);
|
||||
pShape->scale(1 - 0.75 * progress);
|
||||
|
||||
//Update shape for drawing (this may work asynchronously)
|
||||
|
|
Loading…
Add table
Reference in a new issue