common shape: support scale/rotate transform

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.

Change-Id: I41f260271cdefc977eea01a778d49632440c777f
This commit is contained in:
Hermet Park 2020-05-02 22:30:48 +09:00
parent 42c56757df
commit 30ac2da1a3
6 changed files with 167 additions and 20 deletions

View file

@ -72,7 +72,13 @@ class TIZENVG_EXPORT Paint
{
public:
virtual ~Paint() {}
virtual int update(RenderMethod*) = 0;
virtual int rotate(float degree) = 0;
virtual int scale(float factor) = 0;
virtual int bounds(float&x, float& y, float& w, float& h) const = 0;
};
@ -130,9 +136,13 @@ public:
int fill(size_t r, size_t g, size_t b, size_t a) noexcept;
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;
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;
static std::unique_ptr<Shape> gen() noexcept;
@ -157,9 +167,13 @@ public:
~Scene();
int update(RenderMethod* engine) noexcept override;
int push(std::unique_ptr<Shape> shape) noexcept;
int rotate(float degree) noexcept override;
int scale(float factor) noexcept override;
int bounds(float&x, float& y, float& w, float& h) const noexcept override;
static std::unique_ptr<Scene> gen() noexcept;
_TIZENVG_DECLARE_PRIVATE(Scene);

View file

@ -64,4 +64,22 @@ int Scene::update(RenderMethod* engine) noexcept
return 0;
}
int Scene::scale(float scaleFacator) noexcept
{
return 0;
}
int Scene::rotate(float degree) noexcept
{
return 0;
}
int Scene::bounds(float& x, float& y, float& w, float& h) const noexcept
{
return 0;
}
#endif /* _TVG_SCENE_CPP_ */

View file

@ -35,19 +35,14 @@ struct ShapeStroke
};
struct ShapeTransform
{
float e[4*4];
};
struct Shape::Impl
{
ShapeTransform *transform = nullptr;
ShapeFill *fill = nullptr;
ShapeStroke *stroke = nullptr;
ShapePath *path = nullptr;
uint8_t color[4] = {0, 0, 0, 0}; //r, g, b, a
float scale = 1;
float rotate = 0;
void *edata = nullptr; //engine data
Impl() : path(new ShapePath)
@ -59,7 +54,14 @@ struct Shape::Impl
if (path) delete(path);
if (stroke) delete(stroke);
if (fill) delete(fill);
if (transform) delete(transform);
}
bool update()
{
if (path->scale(scale)) scale = 1;
if (path->rotate(rotate)) rotate = 0;
return true;
}
};
@ -97,6 +99,7 @@ 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);
if (impl->edata) return 0;
return - 1;
@ -106,7 +109,7 @@ int Shape::update(RenderMethod* engine) noexcept
int Shape::reset() noexcept
{
auto impl = pImpl.get();
assert(impl);
assert(impl && impl->path);
impl->path->reset();
@ -117,7 +120,7 @@ int Shape::reset() noexcept
int Shape::pathCommands(const PathCommand** cmds) const noexcept
{
auto impl = pImpl.get();
assert(impl && cmds);
assert(impl && impl->path && cmds);
*cmds = impl->path->cmds;
@ -128,7 +131,7 @@ int Shape::pathCommands(const PathCommand** cmds) const noexcept
int Shape::pathCoords(const Point** pts) const noexcept
{
auto impl = pImpl.get();
assert(impl && pts);
assert(impl && impl->path && pts);
*pts = impl->path->pts;
@ -142,7 +145,7 @@ int Shape::appendPath(const PathCommand *cmds, size_t cmdCnt, const Point* pts,
assert(cmds && pts);
auto impl = pImpl.get();
assert(impl);
assert(impl && impl->path);
impl->path->grow(cmdCnt, ptsCnt);
impl->path->append(cmds, cmdCnt, pts, ptsCnt);
@ -154,7 +157,7 @@ int Shape::appendPath(const PathCommand *cmds, size_t cmdCnt, const Point* pts,
int Shape::moveTo(float x, float y) noexcept
{
auto impl = pImpl.get();
assert(impl);
assert(impl && impl->path);
impl->path->moveTo(x, y);
@ -165,7 +168,7 @@ int Shape::moveTo(float x, float y) noexcept
int Shape::lineTo(float x, float y) noexcept
{
auto impl = pImpl.get();
assert(impl);
assert(impl && impl->path);
impl->path->lineTo(x, y);
@ -176,7 +179,7 @@ int Shape::lineTo(float x, float y) noexcept
int Shape::cubicTo(float cx1, float cy1, float cx2, float cy2, float x, float y) noexcept
{
auto impl = pImpl.get();
assert(impl);
assert(impl && impl->path);
impl->path->cubicTo(cx1, cy1, cx2, cy2, x, y);
@ -187,7 +190,7 @@ int Shape::cubicTo(float cx1, float cy1, float cx2, float cy2, float x, float y)
int Shape::close() noexcept
{
auto impl = pImpl.get();
assert(impl);
assert(impl && impl->path);
impl->path->close();
@ -198,7 +201,7 @@ int Shape::close() noexcept
int Shape::appendCircle(float cx, float cy, float radiusW, float radiusH) noexcept
{
auto impl = pImpl.get();
assert(impl);
assert(impl && impl->path);
auto halfKappaW = radiusW * PATH_KAPPA;
auto halfKappaH = radiusH * PATH_KAPPA;
@ -218,7 +221,7 @@ int Shape::appendCircle(float cx, float cy, float radiusW, float radiusH) noexce
int Shape::appendRect(float x, float y, float w, float h, float cornerRadius) noexcept
{
auto impl = pImpl.get();
assert(impl);
assert(impl && impl->path);
//clamping cornerRadius by minimum size
auto min = (w < h ? w : h) * 0.5f;
@ -281,4 +284,41 @@ int Shape::fill(size_t* r, size_t* g, size_t* b, size_t* a) const noexcept
return 0;
}
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;
return 0;
}
int Shape::rotate(float degree) noexcept
{
if (fabsf(degree) <= FLT_EPSILON) return -1;
auto impl = pImpl.get();
assert(impl);
impl->rotate += degree;
return 0;
}
int Shape::bounds(float& x, float& y, float& w, float& h) const noexcept
{
auto impl = pImpl.get();
assert(impl && impl->path);
if (!impl->path->bounds(x, y, w, h)) return -1;
return 0;
}
#endif //_TVG_SHAPE_CPP_

View file

@ -112,6 +112,71 @@ struct ShapePath
if (cmdCnt + 1 > reservedCmdCnt) reserveCmd((cmdCnt + 1) * 2);
cmds[cmdCnt++] = PathCommand::Close;
}
bool bounds(float& x, float& y, float& w, float& h)
{
if (ptsCnt == 0) return false;
Point min = { pts[0].x, pts[0].y };
Point max = { pts[0].x, pts[0].y };
for(size_t i = 1; i < ptsCnt; ++i) {
if (pts[i].x < min.x) min.x = pts[i].x;
if (pts[i].y < min.y) min.y = pts[i].y;
if (pts[i].x > max.x) max.x = pts[i].x;
if (pts[i].y > max.y) max.y = pts[i].y;
}
x = min.x;
y = min.y;
w = max.x - min.x;
h = max.y - min.y;
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

@ -25,6 +25,7 @@ void tvgtest()
shape->appendRect(-100, -100, 200, 200, 0);
shape->fill(127, 255, 255, 255);
shape->rotate(45);
canvas->push(move(shape));
//Draw first frame
@ -42,6 +43,12 @@ void transit_cb(Elm_Transit_Effect *effect, Elm_Transit* transit, double progres
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)
pShape->update(canvas->engine());

View file

@ -35,6 +35,9 @@ void transit_cb(Elm_Transit_Effect *effect, Elm_Transit* transit, double progres
auto shape = tvg::Shape::gen();
shape->appendRect(-100 + (800 * progress), -100 + (800 * progress), 200, 200, (100 * progress));
shape->fill(rand()%255, rand()%255, rand()%255, 255);
shape->scale(1 - 0.75 * progress);
shape->rotate(360 * progress);
canvas->push(move(shape));
//Draw Next frames