common shape: introduce transformation matrix

Paint supports translate, rotate, scale functions for transformation

The origin of these transformation is center of the paint,
thus you have to consider the center-aligned vertices if you'd like to use
these transformation functions.

This policy has been considered for scene transformation.

Change-Id: I78b63d7965faec0ec5b9a98a7776993744534b54
This commit is contained in:
Hermet Park 2020-05-06 01:37:50 +09:00
parent 17af011eae
commit b08d144dc9
13 changed files with 144 additions and 91 deletions

View file

@ -79,10 +79,9 @@ public:
virtual int rotate(float degree) = 0;
virtual int scale(float factor) = 0;
virtual int translate(float x, float y) = 0;
virtual int bounds(float&x, float& y, float& w, float& h) const = 0;
virtual float scale() const = 0;
virtual float rotate() const = 0;
};
@ -141,13 +140,12 @@ public:
int rotate(float degree) noexcept override;
int scale(float factor) noexcept override;
int translate(float x, float y) noexcept override;
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;
@ -175,10 +173,9 @@ public:
int rotate(float degree) noexcept override;
int scale(float factor) noexcept override;
int translate(float x, float y) 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, RenderUpdateFlag flags)
void* GlRenderer::prepare(const Shape& shape, void* data, const RenderTransform* transform, RenderUpdateFlag flags)
{
//prepare shape data
GlShape* sdata = static_cast<GlShape*>(data);
@ -74,6 +74,14 @@ void* GlRenderer::prepare(const Shape& shape, void* data, RenderUpdateFlag flags
assert(sdata);
}
if (RenderUpdateFlag::Path) {
//TODO: Updated Vertices
}
if (RenderUpdateFlag::Transform) {
//TODO: Updated Transform
}
//TODO:
return sdata;

View file

@ -23,7 +23,7 @@ namespace tvg
class GlRenderer : public RenderMethod
{
public:
void* prepare(const Shape& shape, void* data, RenderUpdateFlag flags) override;
void* prepare(const Shape& shape, void* data, const RenderTransform* transform, RenderUpdateFlag flags) override;
bool dispose(const Shape& shape, void *data) override;
bool render(const Shape& shape, void *data) override;
bool clear() override;

View file

@ -96,7 +96,7 @@ void shapeReset(SwShape& sdata);
bool shapeGenOutline(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);
void shapeTransformOutline(const Shape& shape, SwShape& sdata, const RenderTransform& transform);
SwRleData* rleRender(const SwShape& sdata, const SwSize& clip);
bool rasterShape(Surface& surface, SwShape& sdata, uint8_t r, uint8_t g, uint8_t b, uint8_t a);

View file

@ -81,7 +81,7 @@ bool SwRenderer::dispose(const Shape& shape, void *data)
return true;
}
void* SwRenderer::prepare(const Shape& shape, void* data, RenderUpdateFlag flags)
void* SwRenderer::prepare(const Shape& shape, void* data, const RenderTransform* transform, RenderUpdateFlag flags)
{
//prepare shape data
SwShape* sdata = static_cast<SwShape*>(data);
@ -98,10 +98,10 @@ void* SwRenderer::prepare(const Shape& shape, void* data, RenderUpdateFlag flags
if (alpha == 0) return sdata;
//TODO: Threading
if (flags & (RenderUpdateFlag::Path | RenderUpdateFlag::Transform)) {
if (flags & RenderUpdateFlag::Path || RenderUpdateFlag::Transform) {
shapeReset(*sdata);
if (!shapeGenOutline(shape, *sdata)) return sdata;
if (!shapeTransformOutline(shape, *sdata)) return sdata;
if (transform) shapeTransformOutline(shape, *sdata, *transform);
SwSize clip = {static_cast<SwCoord>(surface.w), static_cast<SwCoord>(surface.h)};
if (!shapeGenRle(shape, *sdata, clip)) return sdata;

View file

@ -22,7 +22,7 @@ class SwRenderer : public RenderMethod
public:
Surface surface;
void* prepare(const Shape& shape, void* data, RenderUpdateFlag flags) override;
void* prepare(const Shape& shape, void* data, const RenderTransform* transform, 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

@ -215,54 +215,19 @@ void _deleteRle(SwShape& sdata)
/* External Class Implementation */
/************************************************************************/
bool shapeTransformOutline(const Shape& shape, SwShape& sdata)
void shapeTransformOutline(const Shape& shape, SwShape& sdata, const RenderTransform& transform)
{
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};
auto dx = static_cast<float>(outline->pts[i].x >> 6);
auto dy = static_cast<float>(outline->pts[i].y >> 6);
auto tx = dx * transform.e11 + dy * transform.e12 + transform.e13;
auto ty = dx * transform.e21 + dy * transform.e22 + transform.e23;
auto pt = Point{tx + transform.e31, ty + transform.e32};
outline->pts[i] = TO_SWPOINT(&pt);
}
return true;
}

View file

@ -30,11 +30,87 @@ struct Surface
enum RenderUpdateFlag {None = 0, Path = 1, Fill = 2, Transform = 4, All = 8};
struct RenderTransform
{
float e11, e12, e13;
float e21, e22, e23;
float e31, e32, e33;
void identity()
{
e11 = 1.0f;
e12 = 0.0f;
e13 = 0.0f;
e21 = 0.0f;
e22 = 1.0f;
e23 = 0.0f;
e31 = 0.0f;
e32 = 0.0f;
e33 = 1.0f;
}
void rotate(float degree)
{
constexpr auto PI = 3.141592f;
if (fabsf(degree) <= FLT_EPSILON) return;
auto radian = degree / 180.0f * PI;
auto cosVal = cosf(radian);
auto sinVal = sinf(radian);
auto t11 = e11 * cosVal + e12 * sinVal;
auto t12 = e11 * -sinVal + e12 * cosVal;
auto t21 = e21 * cosVal + e22 * sinVal;
auto t22 = e21 * -sinVal + e22 * cosVal;
auto t31 = e31 * cosVal + e32 * sinVal;
auto t32 = e31 * -sinVal + e32 * cosVal;
e11 = t11;
e12 = t12;
e21 = t21;
e22 = t22;
e31 = t31;
e32 = t32;
}
void translate(float x, float y)
{
e31 += x;
e32 += y;
}
void scale(float factor)
{
e11 *= factor;
e22 *= factor;
e33 *= factor;
}
RenderTransform& operator*=(const RenderTransform rhs)
{
e11 = e11 * rhs.e11 + e12 * rhs.e21 + e13 * rhs.e31;
e12 = e11 * rhs.e12 + e12 * rhs.e22 + e13 * rhs.e32;
e13 = e11 * rhs.e13 + e12 * rhs.e23 + e13 * rhs.e33;
e21 = e21 * rhs.e11 + e22 * rhs.e21 + e23 * rhs.e31;
e22 = e21 * rhs.e12 + e22 * rhs.e22 + e23 * rhs.e32;
e23 = e21 * rhs.e13 + e22 * rhs.e23 + e23 * rhs.e33;
e31 = e31 * rhs.e11 + e32 * rhs.e21 + e33 * rhs.e31;
e32 = e31 * rhs.e12 + e32 * rhs.e22 + e33 * rhs.e32;
e33 = e31 * rhs.e13 + e32 * rhs.e23 + e33 * rhs.e33;
return *this;
}
};
class RenderMethod
{
public:
virtual ~RenderMethod() {}
virtual void* prepare(const Shape& shape, void* data, RenderUpdateFlag flags) = 0;
virtual void* prepare(const Shape& shape, void* data, const RenderTransform* transform, 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

@ -76,6 +76,12 @@ int Scene::rotate(float degree) noexcept
}
int Scene::translate(float x, float y) noexcept
{
return 0;
}
int Scene::bounds(float& x, float& y, float& w, float& h) const noexcept
{
auto impl = pImpl.get();
@ -91,16 +97,4 @@ 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

@ -247,7 +247,7 @@ int Shape::scale(float factor) noexcept
auto impl = pImpl.get();
assert(impl);
if (fabsf(factor) < FLT_EPSILON || fabsf(factor - impl->scale) <= FLT_EPSILON) return -1;
if (fabsf(factor - impl->scale) <= FLT_EPSILON) return -1;
impl->scale = factor;
impl->flag |= RenderUpdateFlag::Transform;
@ -270,6 +270,21 @@ int Shape::rotate(float degree) noexcept
}
int Shape::translate(float x, float y) noexcept
{
auto impl = pImpl.get();
assert(impl);
if (fabsf(x - impl->x) <= FLT_EPSILON && fabsf(y - impl->y) <= FLT_EPSILON) return -1;
impl->x = x;
impl->y = y;
impl->flag |= RenderUpdateFlag::Transform;
return 0;
}
int Shape::bounds(float& x, float& y, float& w, float& h) const noexcept
{
auto impl = pImpl.get();
@ -281,21 +296,4 @@ int Shape::bounds(float& x, float& y, float& w, float& h) const noexcept
}
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

@ -41,6 +41,8 @@ struct Shape::Impl
uint8_t color[4] = {0, 0, 0, 0}; //r, g, b, a
float scale = 1;
float rotate = 0;
float x = 0;
float y = 0;
void *edata = nullptr; //engine data
size_t flag = RenderUpdateFlag::None;
@ -67,8 +69,19 @@ struct Shape::Impl
bool update(Shape& shape, RenderMethod& renderer)
{
edata = renderer.prepare(shape, edata, static_cast<RenderUpdateFlag>(flag));
if (flag & RenderUpdateFlag::Transform) {
RenderTransform transform;
transform.identity();
transform.rotate(rotate);
transform.scale(scale);
transform.translate(x, y);
edata = renderer.prepare(shape, edata, &transform, static_cast<RenderUpdateFlag>(flag));
} else {
edata = renderer.prepare(shape, edata, nullptr, static_cast<RenderUpdateFlag>(flag));
}
flag = RenderUpdateFlag::None;
if (edata) return true;
return false;
}

View file

@ -23,11 +23,12 @@ void tvgtest()
instead, you should consider not to interrupt this pointer life-cycle. */
pShape = shape.get();
shape->appendRect(0, 0, 200, 200, 0);
shape->appendRect(100, 100, 300, 300, 100);
shape->appendCircle(400, 400, 100, 100);
shape->appendCircle(400, 500, 170, 100);
shape->appendRect(-285, -300, 200, 200, 0);
shape->appendRect(-185, -200, 300, 300, 100);
shape->appendCircle(115, 100, 100, 100);
shape->appendCircle(115, 200, 170, 100);
shape->fill(255, 255, 255, 255);
shape->translate(285, 300);
canvas->push(move(shape));

View file

@ -33,8 +33,9 @@ void transit_cb(Elm_Transit_Effect *effect, Elm_Transit* transit, double progres
//Shape
auto shape = tvg::Shape::gen();
shape->appendRect(-100 + (800 * progress), -100 + (800 * progress), 200, 200, (100 * progress));
shape->appendRect(-100, -100, 200, 200, (100 * progress));
shape->fill(rand()%255, rand()%255, rand()%255, 255);
shape->translate(800 * progress, 800 * progress);
shape->scale(1 - 0.75 * progress);
shape->rotate(360 * progress);