mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-07 21:23:32 +00:00
sw_engine: implment basic stroke functions.
Change-Id: Ib9203b4d133ce7ffd80b40d7ad0cac3519b5273d
This commit is contained in:
parent
6b54eb5c22
commit
c51241f26b
10 changed files with 938 additions and 103 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -12,3 +12,4 @@ testDirectUpdate
|
|||
testScene
|
||||
testTransform
|
||||
testSceneTransform
|
||||
testStroke
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
source_file = [
|
||||
'tvgSwCommon.h',
|
||||
'tvgSwRenderer.h',
|
||||
'tvgSwRenderer.cpp',
|
||||
'tvgSwShape.cpp',
|
||||
'tvgSwRle.cpp',
|
||||
'tvgSwRaster.cpp',
|
||||
'tvgSwRenderer.cpp',
|
||||
'tvgSwRle.cpp',
|
||||
'tvgSwShape.cpp',
|
||||
'tvgSwStroke.cpp',
|
||||
]
|
||||
|
||||
swraster_dep = declare_dependency(
|
||||
|
|
|
@ -23,15 +23,27 @@ using namespace tvg;
|
|||
|
||||
constexpr auto SW_CURVE_TYPE_POINT = 0;
|
||||
constexpr auto SW_CURVE_TYPE_CUBIC = 1;
|
||||
|
||||
constexpr auto SW_OUTLINE_FILL_WINDING = 0;
|
||||
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 SwFixed = signed long long;
|
||||
|
||||
struct SwPoint
|
||||
{
|
||||
SwCoord x, y;
|
||||
|
||||
SwPoint& operator+=(const SwPoint& rhs) {
|
||||
x += rhs.x;
|
||||
y += rhs.y;
|
||||
return *this;
|
||||
}
|
||||
|
||||
SwPoint operator+(const SwPoint& rhs) const {
|
||||
return {x + rhs.x, y + rhs.y};
|
||||
}
|
||||
|
@ -56,14 +68,15 @@ struct SwSize
|
|||
|
||||
struct SwOutline
|
||||
{
|
||||
size_t* cntrs; //the contour end points
|
||||
size_t cntrsCnt; //number of contours in glyph
|
||||
size_t reservedCntrsCnt;
|
||||
SwPoint* pts; //the outline's points
|
||||
size_t ptsCnt; //number of points in the glyph
|
||||
size_t reservedPtsCnt;
|
||||
uint8_t* types; //curve type
|
||||
uint8_t fillMode; //outline fill mode
|
||||
uint32_t* cntrs; //the contour end points
|
||||
uint32_t cntrsCnt; //number of contours in glyph
|
||||
uint32_t reservedCntrsCnt;
|
||||
SwPoint* pts; //the outline's points
|
||||
uint32_t ptsCnt; //number of points in the glyph
|
||||
uint32_t reservedPtsCnt;
|
||||
uint8_t* types; //curve type
|
||||
uint8_t fillMode; //outline fill mode
|
||||
bool opened; //opened path?
|
||||
};
|
||||
|
||||
struct SwSpan
|
||||
|
@ -76,8 +89,8 @@ struct SwSpan
|
|||
struct SwRleData
|
||||
{
|
||||
SwSpan *spans;
|
||||
size_t alloc;
|
||||
size_t size;
|
||||
uint32_t alloc;
|
||||
uint32_t size;
|
||||
};
|
||||
|
||||
struct SwBBox
|
||||
|
@ -85,19 +98,77 @@ struct SwBBox
|
|||
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
|
||||
{
|
||||
SwOutline* outline;
|
||||
SwRleData* rle;
|
||||
SwStroke* stroke;
|
||||
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);
|
||||
bool shapeGenOutline(const Shape& shape, SwShape& sdata);
|
||||
void shapeDelOutline(SwShape& sdata);
|
||||
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 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* rleStrokeRender(const SwShape& sdata);
|
||||
|
||||
bool rasterShape(Surface& surface, SwShape& sdata, uint8_t r, uint8_t g, uint8_t b, uint8_t a);
|
||||
|
||||
|
|
|
@ -24,52 +24,52 @@
|
|||
/* 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;
|
||||
}
|
||||
|
||||
|
||||
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) +
|
||||
((((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);
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
|
||||
if (cov < 255) color = COLOR_ALPHA_BLEND(color, cov);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
|
||||
//Fully Opaque
|
||||
if (cov == 255) {
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
for (uint32_t i = 0; i < len; ++i) {
|
||||
dst[i] = color;
|
||||
}
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -89,7 +89,7 @@ bool rasterShape(Surface& surface, SwShape& sdata, uint8_t r, uint8_t g, uint8_t
|
|||
auto stride = surface.stride;
|
||||
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);
|
||||
|
||||
auto dst = &surface.buffer[span->y * stride + span->x];
|
||||
|
|
|
@ -37,8 +37,8 @@ bool SwRenderer::clear()
|
|||
assert(surface.stride > 0 && surface.w > 0 && surface.h > 0);
|
||||
|
||||
//OPTIMIZE ME: SIMD!
|
||||
for (size_t i = 0; i < surface.h; i++) {
|
||||
for (size_t j = 0; j < surface.w; j++)
|
||||
for (uint32_t i = 0; i < surface.h; i++) {
|
||||
for (uint32_t j = 0; j < surface.w; j++)
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
bool SwRenderer::render(const Shape& shape, void *data)
|
||||
{
|
||||
SwShape* sdata = static_cast<SwShape*>(data);
|
||||
if (!sdata) return false;
|
||||
|
||||
//invisible?
|
||||
size_t 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
|
||||
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)
|
||||
{
|
||||
SwShape* sdata = static_cast<SwShape*>(data);
|
||||
auto sdata = static_cast<SwShape*>(data);
|
||||
if (!sdata) return false;
|
||||
shapeReset(*sdata);
|
||||
free(sdata);
|
||||
shapeFree(sdata);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -93,21 +96,33 @@ void* SwRenderer::prepare(const Shape& shape, void* data, const RenderTransform*
|
|||
if (flags == RenderUpdateFlag::None) return sdata;
|
||||
|
||||
//invisible?
|
||||
size_t alpha;
|
||||
shape.fill(nullptr, nullptr, nullptr, &alpha);
|
||||
if (alpha == 0) return sdata;
|
||||
size_t a, sa;
|
||||
shape.fill(nullptr, nullptr, nullptr, &a);
|
||||
shape.strokeColor(nullptr, nullptr, nullptr, &sa);
|
||||
if (a == 0 && sa == 0) return sdata;
|
||||
|
||||
//TODO: Threading
|
||||
|
||||
SwSize clip = {static_cast<SwCoord>(surface.w), static_cast<SwCoord>(surface.h)};
|
||||
|
||||
//Shape
|
||||
if (flags & (RenderUpdateFlag::Path | RenderUpdateFlag::Transform)) {
|
||||
shapeReset(*sdata);
|
||||
if (!shapeGenOutline(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;
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -141,5 +156,4 @@ SwRenderer* SwRenderer::inst()
|
|||
return dynamic_cast<SwRenderer*>(RenderInitializer::inst(renderInit));
|
||||
}
|
||||
|
||||
|
||||
#endif /* _TVG_SW_RENDERER_CPP_ */
|
|
@ -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)));
|
||||
}
|
||||
|
||||
static void _genSpan(SwRleData* rle, SwSpan* spans, size_t count)
|
||||
static void _genSpan(SwRleData* rle, SwSpan* spans, uint32_t count)
|
||||
{
|
||||
assert(rle && spans);
|
||||
|
||||
|
@ -598,15 +598,19 @@ static bool _decomposeOutline(RleWorker& rw)
|
|||
|
||||
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];
|
||||
if (last < 0) goto invalid_outline;
|
||||
|
||||
auto limit = outline->pts + last;
|
||||
assert(limit);
|
||||
|
||||
auto start = UPSCALE(outline->pts[first]);
|
||||
|
||||
auto pt = outline->pts + first;
|
||||
assert(pt);
|
||||
auto types = outline->types + first;
|
||||
assert(types);
|
||||
|
||||
/* A contour cannot start with a cubic control point! */
|
||||
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]));
|
||||
continue;
|
||||
}
|
||||
_cubicTo(rw, UPSCALE(pt[-2]), UPSCALE(pt[-1]), UPSCALE(outline->pts[first]));
|
||||
_cubicTo(rw, UPSCALE(pt[-2]), UPSCALE(pt[-1]), start);
|
||||
goto close;
|
||||
}
|
||||
}
|
||||
|
@ -665,6 +669,7 @@ static bool _genRle(RleWorker& rw)
|
|||
}
|
||||
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
@ -792,4 +797,13 @@ error:
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
SwRleData* rleStrokeRender(const SwShape& sdata)
|
||||
{
|
||||
auto stroke = sdata.stroke;
|
||||
assert(stroke);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#endif /* _TVG_SW_RLE_H_ */
|
||||
|
|
|
@ -23,13 +23,7 @@
|
|||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
static inline SwPoint TO_SWPOINT(const Point* pt)
|
||||
{
|
||||
return {SwCoord(pt->x * 64), SwCoord(pt->y * 64)};
|
||||
}
|
||||
|
||||
|
||||
static void _growOutlineContour(SwOutline& outline, size_t n)
|
||||
static void _growOutlineContour(SwOutline& outline, uint32_t n)
|
||||
{
|
||||
if (n == 0) {
|
||||
free(outline.cntrs);
|
||||
|
@ -42,12 +36,12 @@ static void _growOutlineContour(SwOutline& outline, size_t n)
|
|||
|
||||
//cout << "Grow Cntrs: " << outline.reservedCntrsCnt << " -> " << outline.cntrsCnt + n << endl;;
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
static void _growOutlinePoint(SwOutline& outline, size_t n)
|
||||
static void _growOutlinePoint(SwOutline& outline, uint32_t n)
|
||||
{
|
||||
if (n == 0) {
|
||||
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) {
|
||||
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
|
||||
if (outline.ptsCnt == i) return false;
|
||||
if (outline.ptsCnt == i) {
|
||||
outline.opened = true;
|
||||
return;
|
||||
}
|
||||
|
||||
//Close the path
|
||||
_growOutlinePoint(outline, 1);
|
||||
|
@ -152,7 +149,7 @@ static bool _outlineClose(SwOutline& outline)
|
|||
outline.types[outline.ptsCnt] = SW_CURVE_TYPE_POINT;
|
||||
++outline.ptsCnt;
|
||||
|
||||
return true;
|
||||
outline.opened = false;
|
||||
}
|
||||
|
||||
|
||||
|
@ -183,7 +180,7 @@ static bool _updateBBox(SwShape& sdata)
|
|||
|
||||
++pt;
|
||||
|
||||
for(size_t i = 1; i < outline->ptsCnt; ++i, ++pt) {
|
||||
for(uint32_t i = 1; i < outline->ptsCnt; ++i, ++pt) {
|
||||
assert(pt);
|
||||
if (xMin > pt->x) xMin = 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) {
|
||||
if (sdata.rle->spans) free(sdata.rle->spans);
|
||||
free(sdata.rle);
|
||||
}
|
||||
assert(sdata.outline);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
@ -220,7 +232,7 @@ void shapeTransformOutline(const Shape& shape, SwShape& sdata, const RenderTrans
|
|||
auto outline = sdata.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 dy = static_cast<float>(outline->pts[i].y >> 6);
|
||||
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)
|
||||
{
|
||||
if (sdata.outline->ptsCnt == 0 || sdata.outline->cntrsCnt <= 0) goto end;
|
||||
if (!_updateBBox(sdata)) 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;
|
||||
if (!_checkValid(sdata, clip)) goto end;
|
||||
|
||||
sdata.rle = rleRender(sdata, clip);
|
||||
|
||||
|
@ -251,14 +258,13 @@ end:
|
|||
|
||||
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->pts) free(outline->pts);
|
||||
if (outline->types) free(outline->types);
|
||||
free(outline);
|
||||
|
||||
sdata.outline = nullptr;
|
||||
}
|
||||
|
||||
|
@ -286,7 +292,7 @@ bool shapeGenOutline(const Shape& shape, SwShape& sdata)
|
|||
auto outlinePtsCnt = 0;
|
||||
auto outlineCntrsCnt = 0;
|
||||
|
||||
for (size_t i = 0; i < cmdCnt; ++i) {
|
||||
for (uint32_t i = 0; i < cmdCnt; ++i) {
|
||||
switch(*(cmds + i)) {
|
||||
case PathCommand::Close: {
|
||||
++outlinePtsCnt;
|
||||
|
@ -311,14 +317,9 @@ bool shapeGenOutline(const Shape& shape, SwShape& sdata)
|
|||
++outlinePtsCnt; //for close
|
||||
++outlineCntrsCnt; //for end
|
||||
|
||||
SwOutline* outline = sdata.outline;
|
||||
|
||||
if (!outline) {
|
||||
outline = static_cast<SwOutline*>(calloc(1, sizeof(SwOutline)));
|
||||
assert(outline);
|
||||
} else {
|
||||
cout << "Outline was already allocated? How?" << endl;
|
||||
}
|
||||
auto outline = sdata.outline;
|
||||
if (!outline) outline = static_cast<SwOutline*>(calloc(1, sizeof(SwOutline)));
|
||||
assert(outline);
|
||||
|
||||
_growOutlinePoint(*outline, outlinePtsCnt);
|
||||
_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_ */
|
||||
|
|
658
src/lib/sw_engine/tvgSwStroke.cpp
Normal file
658
src/lib/sw_engine/tvgSwStroke.cpp
Normal 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 zero’s
|
||||
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_ */
|
|
@ -1,12 +1,13 @@
|
|||
all:
|
||||
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 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 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 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 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 testSceneTransform testSceneTransform.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 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 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 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 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 testSceneTransform testSceneTransform.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg`
|
||||
gcc -o testStroke testStroke.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg`
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include <tizenvg.h>
|
||||
#include <Elementary.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
@ -7,33 +8,72 @@ using namespace std;
|
|||
|
||||
static uint32_t buffer[WIDTH * HEIGHT];
|
||||
|
||||
int main(int argc, char **argv)
|
||||
|
||||
void tvgtest()
|
||||
{
|
||||
//Initialize TizenVG Engine
|
||||
tvg::Engine::init();
|
||||
|
||||
//Create a Canvas
|
||||
auto canvas = tvg::SwCanvas::gen(buffer, WIDTH, HEIGHT);
|
||||
//Initialize TizenVG Engine
|
||||
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();
|
||||
shape1->rect(0, 0, 400, 400, 0.1); //x, y, w, h, cornerRadius
|
||||
shape1->fill(0, 255, 0, 255);
|
||||
shape1->appendRect(0, 0, 200, 200, 0); //x, y, w, h, cornerRadius
|
||||
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
|
||||
shape1->strokeColor(0, 0, 0, 255); //r, g, b, a
|
||||
shape1->strokeWidth(1); //1px
|
||||
shape1->strokeJoin(tvg::StrokeJoin::Miter);
|
||||
shape1->strokeLineCap(tvg::StrokeLineCap::Butt);
|
||||
shape1->stroke(255, 255, 255, 255); //color: r, g, b, a
|
||||
shape1->stroke(5); //width: 5px
|
||||
// shape1->strokeJoin(tvg::StrokeJoin::Miter);
|
||||
// shape1->strokeLineCap(tvg::StrokeLineCap::Butt);
|
||||
|
||||
uint32_t dash[] = {3, 1, 5, 1}; //dash pattern
|
||||
shape1->strokeDash(dash, 4);
|
||||
// uint32_t dash[] = {3, 1, 5, 1}; //dash pattern
|
||||
// shape1->strokeDash(dash, 4);
|
||||
|
||||
//Draw the Shape onto the Canvas
|
||||
canvas->push(move(shape1));
|
||||
|
||||
canvas->draw();
|
||||
canvas->sync();
|
||||
|
||||
//Terminate TizenVG Engine
|
||||
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();
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue