sw_engine: implment basic stroke functions.

Change-Id: Ib9203b4d133ce7ffd80b40d7ad0cac3519b5273d
This commit is contained in:
Hermet Park 2020-05-23 20:06:40 +09:00
parent 6b54eb5c22
commit c51241f26b
10 changed files with 938 additions and 103 deletions

1
.gitignore vendored
View file

@ -12,3 +12,4 @@ testDirectUpdate
testScene testScene
testTransform testTransform
testSceneTransform testSceneTransform
testStroke

View file

@ -1,10 +1,11 @@
source_file = [ source_file = [
'tvgSwCommon.h', 'tvgSwCommon.h',
'tvgSwRenderer.h', 'tvgSwRenderer.h',
'tvgSwRenderer.cpp',
'tvgSwShape.cpp',
'tvgSwRle.cpp',
'tvgSwRaster.cpp', 'tvgSwRaster.cpp',
'tvgSwRenderer.cpp',
'tvgSwRle.cpp',
'tvgSwShape.cpp',
'tvgSwStroke.cpp',
] ]
swraster_dep = declare_dependency( swraster_dep = declare_dependency(

View file

@ -23,15 +23,27 @@ using namespace tvg;
constexpr auto SW_CURVE_TYPE_POINT = 0; constexpr auto SW_CURVE_TYPE_POINT = 0;
constexpr auto SW_CURVE_TYPE_CUBIC = 1; constexpr auto SW_CURVE_TYPE_CUBIC = 1;
constexpr auto SW_OUTLINE_FILL_WINDING = 0; constexpr auto SW_OUTLINE_FILL_WINDING = 0;
constexpr auto SW_OUTLINE_FILL_EVEN_ODD = 1; constexpr auto SW_OUTLINE_FILL_EVEN_ODD = 1;
constexpr auto SW_STROKE_TAG_ON = 1;
constexpr auto SW_STROKE_TAG_BEGIN = 4;
constexpr auto SW_STROKE_TAG_END = 8;
using SwCoord = signed long; using SwCoord = signed long;
using SwFixed = signed long long;
struct SwPoint struct SwPoint
{ {
SwCoord x, y; SwCoord x, y;
SwPoint& operator+=(const SwPoint& rhs) {
x += rhs.x;
y += rhs.y;
return *this;
}
SwPoint operator+(const SwPoint& rhs) const { SwPoint operator+(const SwPoint& rhs) const {
return {x + rhs.x, y + rhs.y}; return {x + rhs.x, y + rhs.y};
} }
@ -56,14 +68,15 @@ struct SwSize
struct SwOutline struct SwOutline
{ {
size_t* cntrs; //the contour end points uint32_t* cntrs; //the contour end points
size_t cntrsCnt; //number of contours in glyph uint32_t cntrsCnt; //number of contours in glyph
size_t reservedCntrsCnt; uint32_t reservedCntrsCnt;
SwPoint* pts; //the outline's points SwPoint* pts; //the outline's points
size_t ptsCnt; //number of points in the glyph uint32_t ptsCnt; //number of points in the glyph
size_t reservedPtsCnt; uint32_t reservedPtsCnt;
uint8_t* types; //curve type uint8_t* types; //curve type
uint8_t fillMode; //outline fill mode uint8_t fillMode; //outline fill mode
bool opened; //opened path?
}; };
struct SwSpan struct SwSpan
@ -76,8 +89,8 @@ struct SwSpan
struct SwRleData struct SwRleData
{ {
SwSpan *spans; SwSpan *spans;
size_t alloc; uint32_t alloc;
size_t size; uint32_t size;
}; };
struct SwBBox struct SwBBox
@ -85,19 +98,77 @@ struct SwBBox
SwPoint min, max; SwPoint min, max;
}; };
struct SwStrokeBorder
{
uint32_t ptsCnt;
uint32_t maxPts;
SwPoint* pts;
uint8_t* tags;
int32_t start; //index of current sub-path start point
bool movable; //true: for ends of lineto borders
bool valid;
};
struct SwStroke
{
SwRleData* rle;
SwFixed angleIn;
SwFixed angleOut;
SwPoint center;
SwFixed lineLength;
SwPoint subPathStart;
SwFixed subPathLineLength;
SwFixed width;
StrokeCap cap;
StrokeJoin join;
StrokeJoin joinSaved;
SwStrokeBorder borders[2];
bool firstPt;
bool subPathOpen;
bool subPathAngle;
bool handleWideStrokes;
};
struct SwShape struct SwShape
{ {
SwOutline* outline; SwOutline* outline;
SwRleData* rle; SwRleData* rle;
SwStroke* stroke;
SwBBox bbox; SwBBox bbox;
}; };
static inline SwPoint TO_SWPOINT(const Point* pt)
{
return {SwCoord(pt->x * 64), SwCoord(pt->y * 64)};
}
static inline SwCoord TO_SWCOORD(float val)
{
return SwCoord(val * 64);
}
void shapeReset(SwShape& sdata); void shapeReset(SwShape& sdata);
bool shapeGenOutline(const Shape& shape, SwShape& sdata); bool shapeGenOutline(const Shape& shape, SwShape& sdata);
void shapeDelOutline(SwShape& sdata);
bool shapeGenRle(const Shape& shape, SwShape& sdata, const SwSize& clip); bool shapeGenRle(const Shape& shape, SwShape& sdata, const SwSize& clip);
void shapeDelOutline(SwShape& sdata);
void shapeResetStroke(const Shape& shape, SwShape& sdata);
bool shapeGenStrokeOutline(const Shape& shape, SwShape& sdata);
bool shapeGenStrokeRle(const Shape& shape, SwShape& sdata, const SwSize& clip);
void shapeTransformOutline(const Shape& shape, SwShape& sdata, const RenderTransform& transform); void shapeTransformOutline(const Shape& shape, SwShape& sdata, const RenderTransform& transform);
void shapeFree(SwShape* sdata);
void strokeReset(SwStroke& stroke, float width, StrokeCap cap, StrokeJoin join);
bool strokeParseOutline(SwStroke& stroke, SwOutline& outline);
void strokeFree(SwStroke* stroke);
SwRleData* rleRender(const SwShape& sdata, const SwSize& clip); SwRleData* rleRender(const SwShape& sdata, const SwSize& clip);
SwRleData* rleStrokeRender(const SwShape& sdata);
bool rasterShape(Surface& surface, SwShape& sdata, uint8_t r, uint8_t g, uint8_t b, uint8_t a); bool rasterShape(Surface& surface, SwShape& sdata, uint8_t r, uint8_t g, uint8_t b, uint8_t a);

View file

@ -24,52 +24,52 @@
/* Internal Class Implementation */ /* Internal Class Implementation */
/************************************************************************/ /************************************************************************/
static inline size_t COLOR_ALPHA(size_t color) static inline uint32_t COLOR_ALPHA(uint32_t color)
{ {
return (color >> 24) & 0xff; return (color >> 24) & 0xff;
} }
static inline size_t COLOR_ALPHA_BLEND(size_t color, size_t alpha) static inline uint32_t COLOR_ALPHA_BLEND(uint32_t color, uint32_t alpha)
{ {
return (((((color >> 8) & 0x00ff00ff) * alpha) & 0xff00ff00) + return (((((color >> 8) & 0x00ff00ff) * alpha) & 0xff00ff00) +
((((color & 0x00ff00ff) * alpha) >> 8) & 0x00ff00ff)); ((((color & 0x00ff00ff) * alpha) >> 8) & 0x00ff00ff));
} }
static inline size_t COLOR_ARGB_JOIN(uint8_t r, uint8_t g, uint8_t b, uint8_t a) static inline uint32_t COLOR_ARGB_JOIN(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
{ {
return (a << 24 | r << 16 | g << 8 | b); return (a << 24 | r << 16 | g << 8 | b);
} }
static void static void
_rasterTranslucent(uint32_t* dst, size_t len, size_t color, size_t cov) _rasterTranslucent(uint32_t* dst, uint32_t len, uint32_t color, uint32_t cov)
{ {
//OPTIMIZE ME: SIMD //OPTIMIZE ME: SIMD
if (cov < 255) color = COLOR_ALPHA_BLEND(color, cov); if (cov < 255) color = COLOR_ALPHA_BLEND(color, cov);
auto ialpha = 255 - COLOR_ALPHA(color); auto ialpha = 255 - COLOR_ALPHA(color);
for (size_t i = 0; i < len; ++i) { for (uint32_t i = 0; i < len; ++i) {
dst[i] = color + COLOR_ALPHA_BLEND(dst[i], ialpha); dst[i] = color + COLOR_ALPHA_BLEND(dst[i], ialpha);
} }
} }
static void static void
_rasterSolid(uint32_t* dst, size_t len, size_t color, size_t cov) _rasterSolid(uint32_t* dst, uint32_t len, uint32_t color, uint32_t cov)
{ {
//OPTIMIZE ME: SIMD //OPTIMIZE ME: SIMD
//Fully Opaque //Fully Opaque
if (cov == 255) { if (cov == 255) {
for (size_t i = 0; i < len; ++i) { for (uint32_t i = 0; i < len; ++i) {
dst[i] = color; dst[i] = color;
} }
} else { } else {
auto ialpha = 255 - cov; auto ialpha = 255 - cov;
for (size_t i = 0; i < len; ++i) { for (uint32_t i = 0; i < len; ++i) {
dst[i] = COLOR_ALPHA_BLEND(color, cov) + COLOR_ALPHA_BLEND(dst[i], ialpha); dst[i] = COLOR_ALPHA_BLEND(color, cov) + COLOR_ALPHA_BLEND(dst[i], ialpha);
} }
} }
@ -89,7 +89,7 @@ bool rasterShape(Surface& surface, SwShape& sdata, uint8_t r, uint8_t g, uint8_t
auto stride = surface.stride; auto stride = surface.stride;
auto color = COLOR_ARGB_JOIN(r, g, b, a); auto color = COLOR_ARGB_JOIN(r, g, b, a);
for (size_t i = 0; i < rle->size; ++i) { for (uint32_t i = 0; i < rle->size; ++i) {
assert(span); assert(span);
auto dst = &surface.buffer[span->y * stride + span->x]; auto dst = &surface.buffer[span->y * stride + span->x];

View file

@ -37,8 +37,8 @@ bool SwRenderer::clear()
assert(surface.stride > 0 && surface.w > 0 && surface.h > 0); assert(surface.stride > 0 && surface.w > 0 && surface.h > 0);
//OPTIMIZE ME: SIMD! //OPTIMIZE ME: SIMD!
for (size_t i = 0; i < surface.h; i++) { for (uint32_t i = 0; i < surface.h; i++) {
for (size_t j = 0; j < surface.w; j++) for (uint32_t j = 0; j < surface.w; j++)
surface.buffer[surface.stride * i + j] = 0xff000000; //Solid Black surface.buffer[surface.stride * i + j] = 0xff000000; //Solid Black
} }
@ -57,16 +57,20 @@ bool SwRenderer::target(uint32_t* buffer, size_t stride, size_t w, size_t h)
return true; return true;
} }
bool SwRenderer::render(const Shape& shape, void *data) bool SwRenderer::render(const Shape& shape, void *data)
{ {
SwShape* sdata = static_cast<SwShape*>(data); SwShape* sdata = static_cast<SwShape*>(data);
if (!sdata) return false; if (!sdata) return false;
//invisible?
size_t r, g, b, a; size_t r, g, b, a;
shape.fill(&r, &g, &b, &a); shape.fill(&r, &g, &b, &a);
size_t sa;
shape.strokeColor(nullptr, nullptr, nullptr, &sa);
//invisible?
if (a == 0 && sa == 0) return false;
//TODO: Threading //TODO: Threading
return rasterShape(surface, *sdata, r, g, b, a); return rasterShape(surface, *sdata, r, g, b, a);
} }
@ -74,10 +78,9 @@ bool SwRenderer::render(const Shape& shape, void *data)
bool SwRenderer::dispose(const Shape& shape, void *data) bool SwRenderer::dispose(const Shape& shape, void *data)
{ {
SwShape* sdata = static_cast<SwShape*>(data); auto sdata = static_cast<SwShape*>(data);
if (!sdata) return false; if (!sdata) return false;
shapeReset(*sdata); shapeFree(sdata);
free(sdata);
return true; return true;
} }
@ -93,21 +96,33 @@ void* SwRenderer::prepare(const Shape& shape, void* data, const RenderTransform*
if (flags == RenderUpdateFlag::None) return sdata; if (flags == RenderUpdateFlag::None) return sdata;
//invisible? //invisible?
size_t alpha; size_t a, sa;
shape.fill(nullptr, nullptr, nullptr, &alpha); shape.fill(nullptr, nullptr, nullptr, &a);
if (alpha == 0) return sdata; shape.strokeColor(nullptr, nullptr, nullptr, &sa);
if (a == 0 && sa == 0) return sdata;
//TODO: Threading //TODO: Threading
SwSize clip = {static_cast<SwCoord>(surface.w), static_cast<SwCoord>(surface.h)};
//Shape
if (flags & (RenderUpdateFlag::Path | RenderUpdateFlag::Transform)) { if (flags & (RenderUpdateFlag::Path | RenderUpdateFlag::Transform)) {
shapeReset(*sdata); shapeReset(*sdata);
if (!shapeGenOutline(shape, *sdata)) return sdata; if (!shapeGenOutline(shape, *sdata)) return sdata;
if (transform) shapeTransformOutline(shape, *sdata, *transform); 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; if (!shapeGenRle(shape, *sdata, clip)) return sdata;
shapeDelOutline(*sdata);
} }
//Stroke
if (flags & RenderUpdateFlag::Stroke) {
shapeResetStroke(shape, *sdata);
if (shape.strokeWidth() > 0.01) {
if (!shapeGenStrokeRle(shape, *sdata, clip)) return sdata;
}
}
shapeDelOutline(*sdata);
return sdata; return sdata;
} }
@ -141,5 +156,4 @@ SwRenderer* SwRenderer::inst()
return dynamic_cast<SwRenderer*>(RenderInitializer::inst(renderInit)); return dynamic_cast<SwRenderer*>(RenderInitializer::inst(renderInit));
} }
#endif /* _TVG_SW_RENDERER_CPP_ */
#endif /* _TVG_SW_RENDERER_CPP_ */

View file

@ -137,7 +137,7 @@ static inline SwCoord HYPOT(SwPoint pt)
return ((pt.x > pt.y) ? (pt.x + (3 * pt.y >> 3)) : (pt.y + (3 * pt.x >> 3))); return ((pt.x > pt.y) ? (pt.x + (3 * pt.y >> 3)) : (pt.y + (3 * pt.x >> 3)));
} }
static void _genSpan(SwRleData* rle, SwSpan* spans, size_t count) static void _genSpan(SwRleData* rle, SwSpan* spans, uint32_t count)
{ {
assert(rle && spans); assert(rle && spans);
@ -598,15 +598,19 @@ static bool _decomposeOutline(RleWorker& rw)
auto first = 0; //index of first point in contour auto first = 0; //index of first point in contour
for (size_t n = 0; n < outline->cntrsCnt; ++n) { for (uint32_t n = 0; n < outline->cntrsCnt; ++n) {
auto last = outline->cntrs[n]; auto last = outline->cntrs[n];
if (last < 0) goto invalid_outline; if (last < 0) goto invalid_outline;
auto limit = outline->pts + last; auto limit = outline->pts + last;
assert(limit); assert(limit);
auto start = UPSCALE(outline->pts[first]);
auto pt = outline->pts + first; auto pt = outline->pts + first;
assert(pt);
auto types = outline->types + first; auto types = outline->types + first;
assert(types);
/* A contour cannot start with a cubic control point! */ /* A contour cannot start with a cubic control point! */
if (types[0] == SW_CURVE_TYPE_CUBIC) goto invalid_outline; if (types[0] == SW_CURVE_TYPE_CUBIC) goto invalid_outline;
@ -632,7 +636,7 @@ static bool _decomposeOutline(RleWorker& rw)
_cubicTo(rw, UPSCALE(pt[-2]), UPSCALE(pt[-1]), UPSCALE(pt[0])); _cubicTo(rw, UPSCALE(pt[-2]), UPSCALE(pt[-1]), UPSCALE(pt[0]));
continue; continue;
} }
_cubicTo(rw, UPSCALE(pt[-2]), UPSCALE(pt[-1]), UPSCALE(outline->pts[first])); _cubicTo(rw, UPSCALE(pt[-2]), UPSCALE(pt[-1]), start);
goto close; goto close;
} }
} }
@ -665,6 +669,7 @@ static bool _genRle(RleWorker& rw)
} }
/************************************************************************/ /************************************************************************/
/* External Class Implementation */ /* External Class Implementation */
/************************************************************************/ /************************************************************************/
@ -792,4 +797,13 @@ error:
return nullptr; return nullptr;
} }
SwRleData* rleStrokeRender(const SwShape& sdata)
{
auto stroke = sdata.stroke;
assert(stroke);
return nullptr;
}
#endif /* _TVG_SW_RLE_H_ */ #endif /* _TVG_SW_RLE_H_ */

View file

@ -23,13 +23,7 @@
/* Internal Class Implementation */ /* Internal Class Implementation */
/************************************************************************/ /************************************************************************/
static inline SwPoint TO_SWPOINT(const Point* pt) static void _growOutlineContour(SwOutline& outline, uint32_t n)
{
return {SwCoord(pt->x * 64), SwCoord(pt->y * 64)};
}
static void _growOutlineContour(SwOutline& outline, size_t n)
{ {
if (n == 0) { if (n == 0) {
free(outline.cntrs); free(outline.cntrs);
@ -42,12 +36,12 @@ static void _growOutlineContour(SwOutline& outline, size_t n)
//cout << "Grow Cntrs: " << outline.reservedCntrsCnt << " -> " << outline.cntrsCnt + n << endl;; //cout << "Grow Cntrs: " << outline.reservedCntrsCnt << " -> " << outline.cntrsCnt + n << endl;;
outline.reservedCntrsCnt = n; outline.reservedCntrsCnt = n;
outline.cntrs = static_cast<size_t*>(realloc(outline.cntrs, n * sizeof(size_t))); outline.cntrs = static_cast<uint32_t*>(realloc(outline.cntrs, n * sizeof(uint32_t)));
assert(outline.cntrs); assert(outline.cntrs);
} }
static void _growOutlinePoint(SwOutline& outline, size_t n) static void _growOutlinePoint(SwOutline& outline, uint32_t n)
{ {
if (n == 0) { if (n == 0) {
free(outline.pts); free(outline.pts);
@ -132,9 +126,9 @@ static void _outlineCubicTo(SwOutline& outline, const Point* ctrl1, const Point*
} }
static bool _outlineClose(SwOutline& outline) static void _outlineClose(SwOutline& outline)
{ {
size_t i = 0; uint32_t i = 0;
if (outline.cntrsCnt > 0) { if (outline.cntrsCnt > 0) {
i = outline.cntrs[outline.cntrsCnt - 1] + 1; i = outline.cntrs[outline.cntrsCnt - 1] + 1;
@ -143,7 +137,10 @@ static bool _outlineClose(SwOutline& outline)
} }
//Make sure there is at least one point in the current path //Make sure there is at least one point in the current path
if (outline.ptsCnt == i) return false; if (outline.ptsCnt == i) {
outline.opened = true;
return;
}
//Close the path //Close the path
_growOutlinePoint(outline, 1); _growOutlinePoint(outline, 1);
@ -152,7 +149,7 @@ static bool _outlineClose(SwOutline& outline)
outline.types[outline.ptsCnt] = SW_CURVE_TYPE_POINT; outline.types[outline.ptsCnt] = SW_CURVE_TYPE_POINT;
++outline.ptsCnt; ++outline.ptsCnt;
return true; outline.opened = false;
} }
@ -183,7 +180,7 @@ static bool _updateBBox(SwShape& sdata)
++pt; ++pt;
for(size_t i = 1; i < outline->ptsCnt; ++i, ++pt) { for(uint32_t i = 1; i < outline->ptsCnt; ++i, ++pt) {
assert(pt); assert(pt);
if (xMin > pt->x) xMin = pt->x; if (xMin > pt->x) xMin = pt->x;
if (xMax < pt->x) xMax = pt->x; if (xMax < pt->x) xMax = pt->x;
@ -201,16 +198,31 @@ static bool _updateBBox(SwShape& sdata)
} }
void _deleteRle(SwShape& sdata) static bool _checkValid(SwShape& sdata, const SwSize& clip)
{ {
if (sdata.rle) { assert(sdata.outline);
if (sdata.rle->spans) free(sdata.rle->spans);
free(sdata.rle); if (sdata.outline->ptsCnt == 0 || sdata.outline->cntrsCnt <= 0) return false;
}
//Check boundary
if ((sdata.bbox.min.x > clip.w || sdata.bbox.min.y > clip.h) ||
(sdata.bbox.min.x + sdata.bbox.max.x < 0) ||
(sdata.bbox.min.y + sdata.bbox.max.y < 0)) return false;
return true;
}
static void _deleteRle(SwShape& sdata)
{
if (!sdata.rle) return;
if (sdata.rle->spans) free(sdata.rle->spans);
free(sdata.rle);
sdata.rle = nullptr; sdata.rle = nullptr;
} }
/************************************************************************/ /************************************************************************/
/* External Class Implementation */ /* External Class Implementation */
/************************************************************************/ /************************************************************************/
@ -220,7 +232,7 @@ void shapeTransformOutline(const Shape& shape, SwShape& sdata, const RenderTrans
auto outline = sdata.outline; auto outline = sdata.outline;
assert(outline); assert(outline);
for(size_t i = 0; i < outline->ptsCnt; ++i) { for(uint32_t i = 0; i < outline->ptsCnt; ++i) {
auto dx = static_cast<float>(outline->pts[i].x >> 6); auto dx = static_cast<float>(outline->pts[i].x >> 6);
auto dy = static_cast<float>(outline->pts[i].y >> 6); auto dy = static_cast<float>(outline->pts[i].y >> 6);
auto tx = dx * transform.e11 + dy * transform.e12 + transform.e13; auto tx = dx * transform.e11 + dy * transform.e12 + transform.e13;
@ -233,13 +245,8 @@ void shapeTransformOutline(const Shape& shape, SwShape& sdata, const RenderTrans
bool shapeGenRle(const Shape& shape, SwShape& sdata, const SwSize& clip) bool shapeGenRle(const Shape& shape, SwShape& sdata, const SwSize& clip)
{ {
if (sdata.outline->ptsCnt == 0 || sdata.outline->cntrsCnt <= 0) goto end;
if (!_updateBBox(sdata)) goto end; if (!_updateBBox(sdata)) goto end;
if (!_checkValid(sdata, clip)) goto end;
//Check boundary
if ((sdata.bbox.min.x > clip.w || sdata.bbox.min.y > clip.h) ||
(sdata.bbox.min.x + sdata.bbox.max.x < 0) ||
(sdata.bbox.min.y + sdata.bbox.max.y < 0)) goto end;
sdata.rle = rleRender(sdata, clip); sdata.rle = rleRender(sdata, clip);
@ -251,14 +258,13 @@ end:
void shapeDelOutline(SwShape& sdata) void shapeDelOutline(SwShape& sdata)
{ {
if (!sdata.outline) return; auto outline = sdata.outline;
if (!outline) return;
SwOutline* outline = sdata.outline;
if (outline->cntrs) free(outline->cntrs); if (outline->cntrs) free(outline->cntrs);
if (outline->pts) free(outline->pts); if (outline->pts) free(outline->pts);
if (outline->types) free(outline->types); if (outline->types) free(outline->types);
free(outline); free(outline);
sdata.outline = nullptr; sdata.outline = nullptr;
} }
@ -286,7 +292,7 @@ bool shapeGenOutline(const Shape& shape, SwShape& sdata)
auto outlinePtsCnt = 0; auto outlinePtsCnt = 0;
auto outlineCntrsCnt = 0; auto outlineCntrsCnt = 0;
for (size_t i = 0; i < cmdCnt; ++i) { for (uint32_t i = 0; i < cmdCnt; ++i) {
switch(*(cmds + i)) { switch(*(cmds + i)) {
case PathCommand::Close: { case PathCommand::Close: {
++outlinePtsCnt; ++outlinePtsCnt;
@ -311,14 +317,9 @@ bool shapeGenOutline(const Shape& shape, SwShape& sdata)
++outlinePtsCnt; //for close ++outlinePtsCnt; //for close
++outlineCntrsCnt; //for end ++outlineCntrsCnt; //for end
SwOutline* outline = sdata.outline; auto outline = sdata.outline;
if (!outline) outline = static_cast<SwOutline*>(calloc(1, sizeof(SwOutline)));
if (!outline) { assert(outline);
outline = static_cast<SwOutline*>(calloc(1, sizeof(SwOutline)));
assert(outline);
} else {
cout << "Outline was already allocated? How?" << endl;
}
_growOutlinePoint(*outline, outlinePtsCnt); _growOutlinePoint(*outline, outlinePtsCnt);
_growOutlineContour(*outline, outlineCntrsCnt); _growOutlineContour(*outline, outlineCntrsCnt);
@ -360,4 +361,38 @@ bool shapeGenOutline(const Shape& shape, SwShape& sdata)
} }
void shapeFree(SwShape* sdata)
{
assert(sdata);
shapeDelOutline(*sdata);
_deleteRle(*sdata);
strokeFree(sdata->stroke);
free(sdata);
}
void shapeResetStroke(const Shape& shape, SwShape& sdata)
{
if (!sdata.stroke) sdata.stroke = static_cast<SwStroke*>(calloc(1, sizeof(SwStroke)));
auto stroke = sdata.stroke;
assert(stroke);
strokeReset(*stroke, shape.strokeWidth(), shape.strokeCap(), shape.strokeJoin());
}
bool shapeGenStrokeRle(const Shape& shape, SwShape& sdata, const SwSize& clip)
{
if (!sdata.outline) {
if (!shapeGenOutline(shape, sdata)) return false;
}
if (!_checkValid(sdata, clip)) return false;
if (!strokeParseOutline(*sdata.stroke, *sdata.outline)) return false;
return true;
}
#endif /* _TVG_SW_SHAPE_H_ */ #endif /* _TVG_SW_SHAPE_H_ */

View file

@ -0,0 +1,658 @@
/*
* Copyright (c) 2020 Samsung Electronics Co., Ltd All Rights Reserved
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#ifndef _TVG_SW_STROKER_H_
#define _TVG_SW_STROKER_H_
#include "tvgSwCommon.h"
/************************************************************************/
/* Internal Class Implementation */
/************************************************************************/
constexpr auto CORDIC_FACTOR = 0xDBD95B16UL; //the Cordic shrink factor 0.858785336480436 * 2^32
constexpr static SwFixed ANGLE_PI = (180L << 16);
constexpr static SwFixed ANGLE_2PI = (ANGLE_PI << 1);
constexpr static SwFixed ANGLE_PI2 = (ANGLE_PI >> 1);
constexpr static SwFixed ANGLE_PI4 = (ANGLE_PI >> 2);
//this table was generated for SW_FT_PI = 180L << 16, i.e. degrees
constexpr static auto ATAN_MAX = 23;
constexpr static SwFixed ATAN_TBL[] = {
1740967L, 919879L, 466945L, 234379L, 117304L, 58666L, 29335L,
14668L, 7334L, 3667L, 1833L, 917L, 458L, 229L, 115L,
57L, 29L, 14L, 7L, 4L, 2L, 1L};
static inline SwCoord SATURATE(const SwCoord x)
{
return (x >> (sizeof(long) * 8 - 1));
}
static inline SwFixed SIDE_TO_ROTATE(int32_t s)
{
return (ANGLE_PI2 - (s) * ANGLE_PI);
}
static int64_t _multiply(int64_t a, int64_t b)
{
int32_t s = 1;
//move sign
if (a < 0) {
a = -a;
s = -s;
}
//move sign
if (b < 0) {
b = -b;
s = -s;
}
int64_t c = (a * b + 0x8000L ) >> 16;
return (s > 0) ? c : -c;
}
static int64_t _divide(int64_t a, int64_t b)
{
int32_t s = 1;
//move sign
if (a < 0) {
a = -a;
s = -s;
}
//move sign
if (b < 0) {
b = -b;
s = -s;
}
int64_t q = b > 0 ? ((a << 16) + (b >> 1)) / b : 0x7FFFFFFFL;
return (s < 0 ? -q : q);
}
static SwFixed _angleDiff(SwFixed angle1, SwFixed angle2)
{
auto delta = angle2 - angle1;
delta %= ANGLE_2PI;
if (delta < 0) delta += ANGLE_2PI;
if (delta > ANGLE_PI) delta -= ANGLE_2PI;
return delta;
}
static void _trigDownscale(SwPoint& pt)
{
//multiply a give value by the CORDIC shrink factor
auto s = pt;
//abs
if (pt.x < 0) pt.x = -pt.x;
if (pt.y < 0) pt.y = -pt.y;
int64_t vx = (pt.x * static_cast<int64_t>(CORDIC_FACTOR)) + 0x100000000UL;
int64_t vy = (pt.y * static_cast<int64_t>(CORDIC_FACTOR)) + 0x100000000UL;
pt.x = static_cast<SwFixed>(vx >> 32);
pt.y = static_cast<SwFixed>(vy >> 32);
if (s.x < 0) pt.x = -pt.x;
if (s.y < 0) pt.y = -pt.y;
}
static int32_t _trigPrenorm(SwPoint& pt)
{
/* the highest bit in overflow-safe vector components
MSB of 0.858785336480436 * sqrt(0.5) * 2^30 */
constexpr auto TRIG_SAFE_MSB = 29;
auto v = pt;
//High order bit(MSB)
//clz: count leading zeros
auto shift = 31 - __builtin_clz(abs(v.x) | abs(v.y));
if (shift <= TRIG_SAFE_MSB) {
shift = TRIG_SAFE_MSB - shift;
pt.x = static_cast<SwCoord>((unsigned long)v.x << shift);
pt.y = static_cast<SwCoord>((unsigned long)v.y << shift);
} else {
shift -= TRIG_SAFE_MSB;
pt.x = v.x >> shift;
pt.y = v.y >> shift;
shift = -shift;
}
return shift;
}
static void _trigPseudoRotate(SwPoint& pt, SwFixed theta)
{
auto v = pt;
//Rotate inside [-PI/4, PI/4] sector
while (theta < -ANGLE_PI4) {
auto temp = v.y;
v.y = -v.x;
v.x = temp;
theta += ANGLE_PI2;
}
while (theta > ANGLE_PI4) {
auto temp = -v.y;
v.y = v.x;
v.x = temp;
theta -= ANGLE_PI2;
}
auto atan = ATAN_TBL;
uint32_t i;
SwFixed j;
for (i = 1, j = 1; i < ATAN_MAX; j <<= 1, ++i) {
if (theta < 0) {
auto temp = v.x + ((v.y + j) >> i);
v.y = v.y - ((v.x + j) >> i);
v.x = temp;
theta += *atan++;
}else {
auto temp = v.x - ((v.y + j) >> i);
v.y = v.y + ((v.x + j) >> i);
v.x = temp;
theta -= *atan++;
}
}
pt = v;
}
static void _rotate(SwPoint& pt, SwFixed angle)
{
if (angle == 0 || (pt.x == 0 && pt.y == 0)) return;
auto v = pt;
auto shift = _trigPrenorm(v);
_trigPseudoRotate(v, angle);
_trigDownscale(v);
if (shift > 0) {
auto half = static_cast<int32_t>(1L << (shift - 1));
v.x = (v.x + half + SATURATE(v.x)) >> shift;
v.y = (v.y + half + SATURATE(v.y)) >> shift;
} else {
shift = -shift;
v.x = static_cast<SwCoord>((unsigned long)v.x << shift);
v.y = static_cast<SwCoord>((unsigned long)v.y << shift);
}
}
static SwFixed _tan(SwFixed angle)
{
SwPoint v = {CORDIC_FACTOR >> 8, 0};
_rotate(v, angle);
return _divide(v.y, v.x);
}
static SwFixed _cos(SwFixed angle)
{
SwPoint v = {CORDIC_FACTOR >> 8, 0};
_rotate(v, angle);
return (v.x + 0x80L) >> 8;
}
static void _lineTo(SwStroke& stroke, const SwPoint& to)
{
}
static void _cubicTo(SwStroke& stroke, const SwPoint& ctrl1, const SwPoint& ctrl2, const SwPoint& to)
{
}
static void _arcTo(SwStroke& stroke, int32_t side)
{
}
static void _growBorder(SwStrokeBorder* border, uint32_t newPts)
{
auto maxOld = border->maxPts;
auto maxNew = border->ptsCnt + newPts;
if (maxNew <= maxOld) return;
auto maxCur = maxOld;
while (maxCur < maxNew)
maxCur += (maxCur >> 1) + 16;
border->pts = static_cast<SwPoint*>(realloc(border->pts, maxCur * sizeof(SwPoint)));
assert(border->pts);
border->tags = static_cast<uint8_t*>(realloc(border->tags, maxCur * sizeof(uint8_t)));
assert(border->tags);
border->maxPts = maxCur;
printf("realloc border!!! (%u => %u)\n", maxOld, maxCur);
}
static void _borderLineTo(SwStrokeBorder* border, SwPoint& to, bool movable)
{
constexpr SwPoint EPSILON = 2;
assert(border && border->start >= 0);
if (border->movable) {
//move last point
border->pts[border->ptsCnt - 1] = to;
} else {
//don't add zero-length line_to
auto diff = border->pts[border->ptsCnt - 1] - to;
if (border->ptsCnt > 0 && abs(diff.x) < EPSILON && abs(diff.y) < EPSILON) return;
_growBorder(border, 1);
border->pts[border->ptsCnt] = to;
border->tags[border->ptsCnt] = SW_STROKE_TAG_ON;
border->ptsCnt += 1;
}
border->movable = movable;
}
static void _addCap(SwStroke& stroke, SwFixed angle, int32_t side)
{
if (stroke.cap == StrokeCap::Square) {
auto rotate = SIDE_TO_ROTATE(side);
auto border = stroke.borders + side;
SwPoint delta = {stroke.width, 0};
_rotate(delta, angle);
SwPoint delta2 = {stroke.width, 0};
_rotate(delta2, angle + rotate);
delta += stroke.center + delta2;
_borderLineTo(border, delta, false);
delta = {stroke.width, 0};
_rotate(delta, angle);
delta2 = {stroke.width, 0};
_rotate(delta2, angle - rotate);
delta += delta2 + stroke.center;
_borderLineTo(border, delta, false);
} else if (stroke.cap == StrokeCap::Round) {
stroke.angleIn = angle;
stroke.angleOut = angle + ANGLE_PI;
_arcTo(stroke, side);
return;
} else { //Butt
auto rotate = SIDE_TO_ROTATE(side);
auto border = stroke.borders + side;
SwPoint delta = {stroke.width, 0};
_rotate(delta, angle + rotate);
delta += stroke.center;
_borderLineTo(border, delta, false);
delta = {stroke.width, 0};
_rotate(delta, angle - rotate);
delta += stroke.center;
_borderLineTo(border, delta, false);
}
}
static void _addReverseLeft(SwStroke& stroke, bool opened)
{
auto right = stroke.borders + 0;
auto left = stroke.borders + 1;
assert(left->start >= 0);
auto newPts = left->ptsCnt - left->start;
if (newPts <= 0) return;
_growBorder(right, newPts);
auto dstPt = right->pts + right->ptsCnt;
auto dstTag = right->tags + right->ptsCnt;
auto srcPt = left->pts + left->ptsCnt - 1;
auto srcTag = left->tags + left->ptsCnt - 1;
while (srcPt >= left->pts + left->start) {
*dstPt = *srcPt;
*dstTag = *srcTag;
if (opened) {
dstTag[0] &= ~(SW_STROKE_TAG_BEGIN | SW_STROKE_TAG_END);
} else {
//switch begin/end tags if necessary
auto ttag = dstTag[0] & (SW_STROKE_TAG_BEGIN | SW_STROKE_TAG_END);
if (ttag == (SW_STROKE_TAG_BEGIN || SW_STROKE_TAG_END)) {
dstTag[0] ^= (SW_STROKE_TAG_BEGIN || SW_STROKE_TAG_END);
}
}
--srcPt;
--srcTag;
--dstPt;
--dstTag;
}
left->ptsCnt = left->start;
right->ptsCnt += newPts;
right->movable = false;
left->movable = false;
}
static void _closeBorder(SwStrokeBorder* border, bool reverse)
{
assert(border && border->start >= 0);
uint32_t start = border->start;
uint32_t count = border->ptsCnt;
//Don't record empty paths!
if (count <= start + 1U) {
border->ptsCnt = start;
} else {
/* Copy the last point to the start of this sub-path,
since it contains the adjusted starting coordinates */
border->ptsCnt = --count;
border->pts[start] = border->pts[count];
if (reverse) {
//reverse the points
auto pt1 = border->pts + start + 1;
auto pt2 = border->pts + count - 1;
for (; pt1 < pt2; pt1++, pt2--) {
auto tmp = *pt1;
*pt1 = *pt2;
*pt2 = tmp;
}
//reverse the tags
auto tag1 = border->tags + start + 1;
auto tag2 = border->tags + count - 1;
for (; tag1 < tag2; tag1++, tag2++) {
auto tmp = *tag1;
*tag1 = *tag2;
*tag2 = tmp;
}
}
border->tags[start] |= SW_STROKE_TAG_BEGIN;
border->tags[count - 1] |= SW_STROKE_TAG_END;
}
border->start = -1;
border->movable = false;
}
static void _inside(SwStroke& stroke, int32_t side, SwFixed lineLength)
{
auto border = stroke.borders + side;
auto theta = _angleDiff(stroke.angleIn, stroke.angleOut) / 2;
SwPoint delta;
bool intersect;
/* Only intersect borders if between two line_to's and both
lines are long enough (line length is zero fur curves). */
if (!border->movable || lineLength == 0) {
intersect = false;
} else {
//compute minimum required length of lines
SwFixed minLength = abs(_multiply(stroke.width, _tan(theta)));
if (stroke.lineLength >= minLength && lineLength >= minLength) intersect = true;
}
auto rotate = SIDE_TO_ROTATE(side);
if (!intersect) {
delta = {stroke.width, 0};
_rotate(delta, stroke.angleOut + rotate);
delta += stroke.center;
border->movable = false;
} else {
//compute median angle
delta = {_divide(stroke.width, _cos(theta)), 0};
_rotate(delta, stroke.angleIn + theta + rotate);
delta += stroke.center;
}
_borderLineTo(border, delta, false);
}
static void _beginSubPath(SwStroke& stroke, SwPoint& to, bool opened)
{
cout << "stroke opened? = " << opened << endl;
/* We cannot process the first point because there is not enought
information regarding its corner/cap. Later, it will be processed
in the _strokeEndSubPath() */
stroke.firstPt = true;
stroke.center = to;
stroke.subPathOpen = opened;
/* Determine if we need to check whether the border radius is greater
than the radius of curvature of a curve, to handle this case specially.
This is only required if bevel joins or butt caps may be created because
round & miter joins and round & square caps cover the nagative sector
created with wide strokes. */
if ((stroke.join != StrokeJoin::Round) || (stroke.subPathOpen && stroke.cap == StrokeCap::Butt))
stroke.handleWideStrokes = true;
else
stroke.handleWideStrokes = false;
stroke.subPathStart = to;
stroke.angleIn = 0;
}
static void _endSubPath(SwStroke& stroke)
{
if (stroke.subPathOpen) {
auto right = stroke.borders;
assert(right);
/* all right, this is an opened path, we need to add a cap between
right & left, add the reverse of left, then add a final cap
between left & right */
_addCap(stroke, stroke.angleIn, 0);
//add reversed points from 'left' to 'right'
_addReverseLeft(stroke, true);
//now add the final cap
stroke.center = stroke.subPathStart;
_addCap(stroke, stroke.subPathAngle + ANGLE_PI, 0);
/* now end the right subpath accordingly. The left one is rewind
and deosn't need further processing */
_closeBorder(right, false);
} else {
//close the path if needed
if (stroke.center != stroke.subPathStart)
_lineTo(stroke, stroke.subPathStart);
//process the corner
stroke.angleOut = stroke.subPathAngle;
auto turn = _angleDiff(stroke.angleIn, stroke.angleOut);
//No specific corner processing is required if the turn is 0
if (turn != 0) {
//when we turn to the right, the inside is 0
auto inside = 0;
//otherwise, the inside is 1
if (turn < 0) inside = 1;
_inside(stroke, inside, stroke.subPathLineLength); //inside
_inside(stroke, 1 - inside, stroke.subPathLineLength); //outside
}
_closeBorder(stroke.borders + 0, false);
_closeBorder(stroke.borders + 1, true);
}
}
static void _deleteRle(SwStroke& stroke)
{
if (!stroke.rle) return;
if (stroke.rle->spans) free(stroke.rle->spans);
free(stroke.rle);
stroke.rle = nullptr;
}
/************************************************************************/
/* External Class Implementation */
/************************************************************************/
void strokeFree(SwStroke* stroke)
{
if (!stroke) return;
_deleteRle(*stroke);
free(stroke);
}
void strokeReset(SwStroke& stroke, float width, StrokeCap cap, StrokeJoin join)
{
_deleteRle(stroke);
#if 0
miterLimit = 4 * (1 >> 16);
/* ensure miter limit has sensible value */
if ( stroker->miter_limit < 0x10000 )
stroker->miter_limit = 0x10000;
#endif
stroke.width = TO_SWCOORD(width * 0.5f);
stroke.cap = cap;
//Save line join: it can be temporarily changed when stroking curves...
stroke.joinSaved = stroke.join = join;
stroke.borders[0].ptsCnt = 0;
stroke.borders[0].start = -1;
stroke.borders[0].valid = false;
stroke.borders[1].ptsCnt = 0;
stroke.borders[1].start = -1;
stroke.borders[1].valid = false;
}
bool strokeParseOutline(SwStroke& stroke, SwOutline& outline)
{
uint32_t first = 0;
for (uint32_t i = 0; i < outline.cntrsCnt; ++i) {
auto last = outline.cntrs[i]; //index of last point in contour
auto limit = outline.pts + last;
assert(limit);
//Skip empty points
if (last <= first) {
first = last + 1;
continue;
}
auto start = outline.pts[first];
auto pt = outline.pts + first;
assert(pt);
auto types = outline.types + first;
assert(types);
auto type = types[0];
//A contour cannot start with a cubic control point
if (type == SW_CURVE_TYPE_CUBIC) return false;
_beginSubPath(stroke, start, outline.opened);
while (pt < limit) {
assert(++pt);
assert(++types);
//emit a signel line_to
if (types[0] == SW_CURVE_TYPE_POINT) {
_lineTo(stroke, *pt);
//types cubic
} else {
if (pt + 1 > limit || types[1] != SW_CURVE_TYPE_CUBIC) return false;
pt += 2;
types += 2;
if (pt <= limit) {
_cubicTo(stroke, pt[-2], pt[-1], pt[0]);
continue;
}
_cubicTo(stroke, pt[-2], pt[-1], start);
goto close;
}
}
close:
if (!stroke.firstPt) _endSubPath(stroke);
first = last + 1;
}
return true;
}
#endif /* _TVG_SW_STROKER_H_ */

View file

@ -1,12 +1,13 @@
all: all:
gcc -o testShape testShape.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` # gcc -o testShape testShape.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg`
gcc -o testMultiShapes testMultiShapes.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` # gcc -o testMultiShapes testMultiShapes.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg`
gcc -o testBoundary testBoundary.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` # gcc -o testBoundary testBoundary.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg`
gcc -o testPath testPath.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` # gcc -o testPath testPath.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg`
gcc -o testPathCopy testPathCopy.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` # gcc -o testPathCopy testPathCopy.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg`
gcc -o testBlending testBlending.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` # gcc -o testBlending testBlending.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg`
gcc -o testUpdate testUpdate.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` # gcc -o testUpdate testUpdate.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg`
gcc -o testDirectUpdate testDirectUpdate.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` # gcc -o testDirectUpdate testDirectUpdate.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg`
gcc -o testScene testScene.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` # gcc -o testScene testScene.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg`
gcc -o testTransform testTransform.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` # gcc -o testTransform testTransform.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg`
gcc -o testSceneTransform testSceneTransform.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` # gcc -o testSceneTransform testSceneTransform.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg`
gcc -o testStroke testStroke.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg`

View file

@ -1,4 +1,5 @@
#include <tizenvg.h> #include <tizenvg.h>
#include <Elementary.h>
using namespace std; using namespace std;
@ -7,33 +8,72 @@ using namespace std;
static uint32_t buffer[WIDTH * HEIGHT]; static uint32_t buffer[WIDTH * HEIGHT];
int main(int argc, char **argv)
void tvgtest()
{ {
//Initialize TizenVG Engine //Initialize TizenVG Engine
tvg::Engine::init(); tvg::Engine::init();
//Create a Canvas //Initialize TizenVG Engine
auto canvas = tvg::SwCanvas::gen(buffer, WIDTH, HEIGHT); tvg::Engine::init();
//Prepare a Shape //Create a Canvas
auto canvas = tvg::SwCanvas::gen();
canvas->target(buffer, WIDTH, WIDTH, HEIGHT);
//Prepare a Shape (Rectangle + Rectangle + Circle + Circle)
auto shape1 = tvg::Shape::gen(); auto shape1 = tvg::Shape::gen();
shape1->rect(0, 0, 400, 400, 0.1); //x, y, w, h, cornerRadius shape1->appendRect(0, 0, 200, 200, 0); //x, y, w, h, cornerRadius
shape1->fill(0, 255, 0, 255); shape1->appendRect(100, 100, 300, 300, 100); //x, y, w, h, cornerRadius
shape1->appendCircle(400, 400, 100, 100); //cx, cy, radiusW, radiusH
shape1->appendCircle(400, 500, 170, 100); //cx, cy, radiusW, radiusH
shape1->fill(255, 255, 0, 255); //r, g, b, a
//Stroke Style //Stroke Style
shape1->strokeColor(0, 0, 0, 255); //r, g, b, a shape1->stroke(255, 255, 255, 255); //color: r, g, b, a
shape1->strokeWidth(1); //1px shape1->stroke(5); //width: 5px
shape1->strokeJoin(tvg::StrokeJoin::Miter); // shape1->strokeJoin(tvg::StrokeJoin::Miter);
shape1->strokeLineCap(tvg::StrokeLineCap::Butt); // shape1->strokeLineCap(tvg::StrokeLineCap::Butt);
uint32_t dash[] = {3, 1, 5, 1}; //dash pattern // uint32_t dash[] = {3, 1, 5, 1}; //dash pattern
shape1->strokeDash(dash, 4); // shape1->strokeDash(dash, 4);
//Draw the Shape onto the Canvas
canvas->push(move(shape1)); canvas->push(move(shape1));
canvas->draw(); canvas->draw();
canvas->sync(); canvas->sync();
//Terminate TizenVG Engine //Terminate TizenVG Engine
tvg::Engine::term(); tvg::Engine::term();
} }
void
win_del(void *data, Evas_Object *o, void *ev)
{
elm_exit();
}
int main(int argc, char **argv)
{
tvgtest();
//Show the result using EFL...
elm_init(argc, argv);
Eo* win = elm_win_util_standard_add(NULL, "TizenVG Test");
evas_object_smart_callback_add(win, "delete,request", win_del, 0);
Eo* img = evas_object_image_filled_add(evas_object_evas_get(win));
evas_object_image_size_set(img, WIDTH, HEIGHT);
evas_object_image_data_set(img, buffer);
evas_object_size_hint_weight_set(img, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
evas_object_show(img);
elm_win_resize_object_add(win, img);
evas_object_geometry_set(win, 0, 0, WIDTH, HEIGHT);
evas_object_show(win);
elm_run();
elm_shutdown();
}