mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-07 21:23:32 +00:00
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:
parent
42c56757df
commit
30ac2da1a3
6 changed files with 167 additions and 20 deletions
|
@ -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);
|
||||
|
|
|
@ -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_ */
|
|
@ -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_
|
||||
|
|
|
@ -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_
|
||||
|
|
|
@ -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());
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue