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:
Hermet Park 2020-05-03 02:02:43 +09:00
parent 30ac2da1a3
commit 682bc25298
12 changed files with 143 additions and 96 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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