sw_engine: implement stroke rle part

Current stroke fails to merged shapes case...

you can test with testStroke example

Change-Id: I488af728949cba1d01b88723eb1dc4c49bac6c9b
This commit is contained in:
Hermet Park 2020-06-01 20:27:43 +09:00
parent 41dbd9774a
commit 1686af7643
8 changed files with 271 additions and 141 deletions

View file

@ -121,8 +121,6 @@ struct SwStrokeBorder
struct SwStroke struct SwStroke
{ {
SwRleData* rle;
SwFixed angleIn; SwFixed angleIn;
SwFixed angleOut; SwFixed angleOut;
SwPoint center; SwPoint center;
@ -146,8 +144,9 @@ struct SwStroke
struct SwShape struct SwShape
{ {
SwOutline* outline; SwOutline* outline;
SwRleData* rle;
SwStroke* stroke; SwStroke* stroke;
SwRleData* rle;
SwRleData* strokeRle;
SwBBox bbox; SwBBox bbox;
}; };
@ -196,11 +195,13 @@ void shapeFree(SwShape* sdata);
void strokeReset(SwStroke& stroke, float width, StrokeCap cap, StrokeJoin join); void strokeReset(SwStroke& stroke, float width, StrokeCap cap, StrokeJoin join);
bool strokeParseOutline(SwStroke& stroke, SwOutline& outline); bool strokeParseOutline(SwStroke& stroke, SwOutline& outline);
SwOutline* strokeExportOutline(SwStroke& stroke);
void strokeFree(SwStroke* stroke); void strokeFree(SwStroke* stroke);
SwRleData* rleRender(const SwShape& sdata, const SwSize& clip); SwRleData* rleRender(const SwOutline* outline, const SwBBox& bbox, const SwSize& clip);
SwRleData* rleStrokeRender(const SwShape& sdata); void rleFree(SwRleData* rle);
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);
bool rasterStroke(Surface& surface, SwShape& sdata, uint8_t r, uint8_t g, uint8_t b, uint8_t a);
#endif /* _TVG_SW_COMMON_H_ */ #endif /* _TVG_SW_COMMON_H_ */

View file

@ -24,7 +24,7 @@
/* Internal Class Implementation */ /* Internal Class Implementation */
/************************************************************************/ /************************************************************************/
constexpr auto CORDIC_FACTOR = 0xDBD95B16UL; //the Cordic shrink factor 0.858785336480436 * 2^32 constexpr SwCoord CORDIC_FACTOR = 0xDBD95B16UL; //the Cordic shrink factor 0.858785336480436 * 2^32
//this table was generated for SW_FT_PI = 180L << 16, i.e. degrees //this table was generated for SW_FT_PI = 180L << 16, i.e. degrees
constexpr static auto ATAN_MAX = 23; constexpr static auto ATAN_MAX = 23;
@ -45,15 +45,14 @@ static inline SwFixed PAD_ROUND(const SwFixed x, int32_t n)
} }
static SwCoord _downscale(SwCoord x) static SwCoord _downscale(SwFixed x)
{ {
//multiply a give value by the CORDIC shrink factor //multiply a give value by the CORDIC shrink factor
auto s = abs(x);
abs(x); int64_t t = (s * static_cast<int64_t>(CORDIC_FACTOR)) + 0x100000000UL;
int64_t t = (x * static_cast<int64_t>(CORDIC_FACTOR)) + 0x100000000UL; s = static_cast<SwFixed>(t >> 32);
x = static_cast<SwFixed>(t >> 32); if (x < 0) s = -s;
if (x < 0) x = -x; return s;
return x;
} }
@ -139,6 +138,47 @@ static void _polarize(SwPoint& pt)
} }
static void _rotate(SwPoint& pt, SwFixed theta)
{
auto v = pt;
//Rotate inside [-PI/4, PI/4] sector
while (theta < -ANGLE_PI4) {
auto tmp = v.y;
v.y = -v.x;
v.x = tmp;
theta += ANGLE_PI2;
}
while (theta > ANGLE_PI4) {
auto tmp = -v.y;
v.y = v.x;
v.x = tmp;
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 tmp = v.x + ((v.y + j) >> i);
v.y = v.y - ((v.x + j) >> i);
v.x = tmp;
theta += *atan++;
}else {
auto tmp = v.x - ((v.y + j) >> i);
v.y = v.y + ((v.x + j) >> i);
v.x = tmp;
theta -= *atan++;
}
}
pt = v;
}
/************************************************************************/ /************************************************************************/
/* External Class Implementation */ /* External Class Implementation */
/************************************************************************/ /************************************************************************/
@ -267,57 +307,26 @@ void mathRotate(SwPoint& pt, SwFixed angle)
auto shift = _normalize(v); auto shift = _normalize(v);
auto theta = angle; auto theta = angle;
//Rotate inside [-PI/4, PI/4] sector _rotate(v, theta);
while (theta < -ANGLE_PI4) {
auto tmp = v.y;
v.y = -v.x;
v.x = tmp;
theta += ANGLE_PI2;
}
while (theta > ANGLE_PI4) {
auto tmp = -v.y;
v.y = v.x;
v.x = tmp;
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 tmp = v.x + ((v.y + j) >> i);
v.y = v.y - ((v.x + j) >> i);
v.x = tmp;
theta += *atan++;
}else {
auto tmp = v.x - ((v.y + j) >> i);
v.y = v.y + ((v.x + j) >> i);
v.x = tmp;
theta -= *atan++;
}
}
v.x = _downscale(v.x); v.x = _downscale(v.x);
v.y = _downscale(v.y); v.y = _downscale(v.y);
if (shift > 0) { if (shift > 0) {
auto half = static_cast<int32_t>(1L << (shift - 1)); auto half = static_cast<int32_t>(1L << (shift - 1));
v.x = (v.x + half + SATURATE(v.x)) >> shift; pt.x = (v.x + half + SATURATE(v.x)) >> shift;
v.y = (v.y + half + SATURATE(v.y)) >> shift; pt.y = (v.y + half + SATURATE(v.y)) >> shift;
} else { } else {
shift = -shift; shift = -shift;
v.x = static_cast<SwCoord>((unsigned long)v.x << shift); pt.x = static_cast<SwCoord>((unsigned long)v.x << shift);
v.y = static_cast<SwCoord>((unsigned long)v.y << shift); pt.y = static_cast<SwCoord>((unsigned long)v.y << shift);
} }
} }
SwFixed mathTan(SwFixed angle) SwFixed mathTan(SwFixed angle)
{ {
SwPoint v = {CORDIC_FACTOR >> 8, 0}; SwPoint v = {CORDIC_FACTOR >> 8, 0};
mathRotate(v, angle); _rotate(v, angle);
return mathDivide(v.y, v.x); return mathDivide(v.y, v.x);
} }
@ -343,7 +352,7 @@ SwFixed mathSin(SwFixed angle)
SwFixed mathCos(SwFixed angle) SwFixed mathCos(SwFixed angle)
{ {
SwPoint v = {CORDIC_FACTOR >> 8, 0}; SwPoint v = {CORDIC_FACTOR >> 8, 0};
mathRotate(v, angle); _rotate(v, angle);
return (v.x + 0x80L) >> 8; return (v.x + 0x80L) >> 8;
} }

View file

@ -76,18 +76,13 @@ _rasterSolid(uint32_t* dst, uint32_t len, uint32_t color, uint32_t cov)
} }
/************************************************************************/ static bool
/* External Class Implementation */ _rasterRle(Surface& surface, SwRleData* rle, uint32_t color, uint8_t a)
/************************************************************************/
bool rasterShape(Surface& surface, SwShape& sdata, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
{ {
SwRleData* rle = sdata.rle;
if (!rle) return false; if (!rle) return false;
auto span = rle->spans; auto span = rle->spans;
auto stride = surface.stride; auto stride = surface.stride;
auto color = COLOR_ARGB_JOIN(r, g, b, a);
for (uint32_t i = 0; i < rle->size; ++i) { for (uint32_t i = 0; i < rle->size; ++i) {
assert(span); assert(span);
@ -103,4 +98,21 @@ bool rasterShape(Surface& surface, SwShape& sdata, uint8_t r, uint8_t g, uint8_t
return true; return true;
} }
/************************************************************************/
/* External Class Implementation */
/************************************************************************/
bool rasterShape(Surface& surface, SwShape& sdata, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
{
return _rasterRle(surface, sdata.rle, COLOR_ARGB_JOIN(r, g, b, a), a);
}
bool rasterStroke(Surface& surface, SwShape& sdata, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
{
return _rasterRle(surface, sdata.strokeRle, COLOR_ARGB_JOIN(r, g, b, a), a);
}
#endif /* _TVG_SW_RASTER_CPP_ */ #endif /* _TVG_SW_RASTER_CPP_ */

View file

@ -63,16 +63,14 @@ bool SwRenderer::render(const Shape& shape, void *data)
if (!sdata) return false; if (!sdata) return false;
size_t r, g, b, a; size_t r, g, b, a;
shape.fill(&r, &g, &b, &a); shape.fill(&r, &g, &b, &a);
if (a > 0) rasterShape(surface, *sdata, r, g, b, a);
size_t sa; shape.strokeColor(&r, &g, &b, &a);
shape.strokeColor(nullptr, nullptr, nullptr, &sa); if (a > 0) rasterStroke(surface, *sdata, r, g, b, a);
//invisible? return true;
if (a == 0 && sa == 0) return false;
//TODO: Threading
return rasterShape(surface, *sdata, r, g, b, a);
} }

View file

@ -612,9 +612,7 @@ static bool _decomposeOutline(RleWorker& rw)
goto close; goto close;
} }
} }
_lineTo(rw, UPSCALE(outline->pts[first]));
//FIXME: Close the contour with a line segment?
//_lineTo(rw, UPSCALE(outline->pts[first]));
close: close:
first = last + 1; first = last + 1;
} }
@ -646,13 +644,12 @@ static bool _genRle(RleWorker& rw)
/* External Class Implementation */ /* External Class Implementation */
/************************************************************************/ /************************************************************************/
SwRleData* rleRender(const SwShape& sdata, const SwSize& clip) SwRleData* rleRender(const SwOutline* outline, const SwBBox& bbox, const SwSize& clip)
{ {
//Please adjust when you out of cell memory (default: 16384L) //Please adjust when you out of cell memory (default: 16384L)
constexpr auto RENDER_POOL_SIZE = 166641L; constexpr auto RENDER_POOL_SIZE = 166641L;
constexpr auto BAND_SIZE = 40; constexpr auto BAND_SIZE = 40;
auto outline = sdata.outline;
assert(outline); assert(outline);
assert(outline->cntrs && outline->pts); assert(outline->cntrs && outline->pts);
assert(outline->ptsCnt == outline->cntrs[outline->cntrsCnt - 1] + 1); assert(outline->ptsCnt == outline->cntrs[outline->cntrsCnt - 1] + 1);
@ -671,11 +668,11 @@ SwRleData* rleRender(const SwShape& sdata, const SwSize& clip)
rw.area = 0; rw.area = 0;
rw.cover = 0; rw.cover = 0;
rw.invalid = true; rw.invalid = true;
rw.cellMin = sdata.bbox.min; rw.cellMin = bbox.min;
rw.cellMax = sdata.bbox.max; rw.cellMax = bbox.max;
rw.cellXCnt = rw.cellMax.x - rw.cellMin.x; rw.cellXCnt = rw.cellMax.x - rw.cellMin.x;
rw.cellYCnt = rw.cellMax.y - rw.cellMin.y; rw.cellYCnt = rw.cellMax.y - rw.cellMin.y;
rw.outline = outline; rw.outline = const_cast<SwOutline*>(outline);
rw.bandSize = rw.bufferSize / (sizeof(Cell) * 8); //bandSize: 64 rw.bandSize = rw.bufferSize / (sizeof(Cell) * 8); //bandSize: 64
rw.bandShoot = 0; rw.bandShoot = 0;
rw.clip = clip; rw.clip = clip;
@ -770,12 +767,13 @@ error:
} }
SwRleData* rleStrokeRender(const SwShape& sdata) void rleFree(SwRleData* rle)
{ {
auto stroke = sdata.stroke; if (!rle) return;
assert(stroke); if (rle->spans) free(rle->spans);
free(rle);
return nullptr;
} }
#endif /* _TVG_SW_RLE_H_ */ #endif /* _TVG_SW_RLE_H_ */

View file

@ -64,6 +64,17 @@ static void _growOutlinePoint(SwOutline& outline, uint32_t n)
} }
static void _freeOutline(SwOutline* outline)
{
if (!outline) return;
if (outline->cntrs) free(outline->cntrs);
if (outline->pts) free(outline->pts);
if (outline->types) free(outline->types);
free(outline);
}
static void _outlineEnd(SwOutline& outline) static void _outlineEnd(SwOutline& outline)
{ {
_growOutlineContour(outline, 1); _growOutlineContour(outline, 1);
@ -153,23 +164,22 @@ static void _outlineClose(SwOutline& outline)
} }
static void _initBBox(SwShape& sdata) static void _initBBox(SwBBox& bbox)
{ {
sdata.bbox.min.x = sdata.bbox.min.y = 0; bbox.min.x = bbox.min.y = 0;
sdata.bbox.max.x = sdata.bbox.max.y = 0; bbox.max.x = bbox.max.y = 0;
} }
static bool _updateBBox(SwShape& sdata) static bool _updateBBox(SwOutline* outline, SwBBox& bbox)
{ {
auto outline = sdata.outline; if (!outline) return false;
assert(outline);
auto pt = outline->pts; auto pt = outline->pts;
assert(pt); assert(pt);
if (outline->ptsCnt <= 0) { if (outline->ptsCnt <= 0) {
_initBBox(sdata); _initBBox(bbox);
return false; return false;
} }
@ -187,10 +197,10 @@ static bool _updateBBox(SwShape& sdata)
if (yMin > pt->y) yMin = pt->y; if (yMin > pt->y) yMin = pt->y;
if (yMax < pt->y) yMax = pt->y; if (yMax < pt->y) yMax = pt->y;
} }
sdata.bbox.min.x = xMin >> 6; bbox.min.x = xMin >> 6;
sdata.bbox.max.x = (xMax + 63) >> 6; bbox.max.x = (xMax + 63) >> 6;
sdata.bbox.min.y = yMin >> 6; bbox.min.y = yMin >> 6;
sdata.bbox.max.y = (yMax + 63) >> 6; bbox.max.y = (yMax + 63) >> 6;
if (xMax - xMin < 1 || yMax - yMin < 1) return false; if (xMax - xMin < 1 || yMax - yMin < 1) return false;
@ -213,16 +223,6 @@ static bool _checkValid(SwShape& sdata, const SwSize& clip)
} }
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 */ /* External Class Implementation */
/************************************************************************/ /************************************************************************/
@ -245,10 +245,10 @@ 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 (!_updateBBox(sdata)) goto end; if (!_updateBBox(sdata.outline, sdata.bbox)) goto end;
if (!_checkValid(sdata, clip)) goto end; if (!_checkValid(sdata, clip)) goto end;
sdata.rle = rleRender(sdata, clip); sdata.rle = rleRender(sdata.outline, sdata.bbox, clip);
end: end:
if (sdata.rle) return true; if (sdata.rle) return true;
@ -259,12 +259,7 @@ end:
void shapeDelOutline(SwShape& sdata) void shapeDelOutline(SwShape& sdata)
{ {
auto outline = sdata.outline; auto outline = sdata.outline;
if (!outline) return; _freeOutline(outline);
if (outline->cntrs) free(outline->cntrs);
if (outline->pts) free(outline->pts);
if (outline->types) free(outline->types);
free(outline);
sdata.outline = nullptr; sdata.outline = nullptr;
} }
@ -272,8 +267,9 @@ void shapeDelOutline(SwShape& sdata)
void shapeReset(SwShape& sdata) void shapeReset(SwShape& sdata)
{ {
shapeDelOutline(sdata); shapeDelOutline(sdata);
_deleteRle(sdata); rleFree(sdata.rle);
_initBBox(sdata); sdata.rle = nullptr;
_initBBox(sdata.bbox);
} }
@ -366,8 +362,13 @@ void shapeFree(SwShape* sdata)
assert(sdata); assert(sdata);
shapeDelOutline(*sdata); shapeDelOutline(*sdata);
_deleteRle(*sdata); rleFree(sdata->rle);
strokeFree(sdata->stroke);
if (sdata->stroke) {
rleFree(sdata->strokeRle);
strokeFree(sdata->stroke);
}
free(sdata); free(sdata);
} }
@ -377,8 +378,9 @@ void shapeResetStroke(const Shape& shape, SwShape& sdata)
if (!sdata.stroke) sdata.stroke = static_cast<SwStroke*>(calloc(1, sizeof(SwStroke))); if (!sdata.stroke) sdata.stroke = static_cast<SwStroke*>(calloc(1, sizeof(SwStroke)));
auto stroke = sdata.stroke; auto stroke = sdata.stroke;
assert(stroke); assert(stroke);
strokeReset(*stroke, shape.strokeWidth(), shape.strokeCap(), shape.strokeJoin()); strokeReset(*stroke, shape.strokeWidth(), shape.strokeCap(), shape.strokeJoin());
rleFree(sdata.strokeRle);
sdata.strokeRle = nullptr;
} }
@ -392,6 +394,16 @@ bool shapeGenStrokeRle(const Shape& shape, SwShape& sdata, const SwSize& clip)
if (!strokeParseOutline(*sdata.stroke, *sdata.outline)) return false; if (!strokeParseOutline(*sdata.stroke, *sdata.outline)) return false;
auto outline = strokeExportOutline(*sdata.stroke);
if (!outline) return false;
SwBBox bbox;
_updateBBox(outline, bbox);
sdata.strokeRle = rleRender(outline, bbox, clip);
_freeOutline(outline);
return true; return true;
} }

View file

@ -23,7 +23,7 @@
/************************************************************************/ /************************************************************************/
/* Internal Class Implementation */ /* Internal Class Implementation */
/************************************************************************/ /************************************************************************/
static constexpr auto SW_STROKE_TAG_ON = 1; static constexpr auto SW_STROKE_TAG_POINT = 1;
static constexpr auto SW_STROKE_TAG_CUBIC = 2; static constexpr auto SW_STROKE_TAG_CUBIC = 2;
static constexpr auto SW_STROKE_TAG_BEGIN = 4; static constexpr auto SW_STROKE_TAG_BEGIN = 4;
static constexpr auto SW_STROKE_TAG_END = 8; static constexpr auto SW_STROKE_TAG_END = 8;
@ -36,6 +36,8 @@ static inline SwFixed SIDE_TO_ROTATE(const int32_t s)
static void _growBorder(SwStrokeBorder* border, uint32_t newPts) static void _growBorder(SwStrokeBorder* border, uint32_t newPts)
{ {
assert(border);
auto maxOld = border->maxPts; auto maxOld = border->maxPts;
auto maxNew = border->ptsCnt + newPts; auto maxNew = border->ptsCnt + newPts;
@ -53,8 +55,6 @@ static void _growBorder(SwStrokeBorder* border, uint32_t newPts)
assert(border->tags); assert(border->tags);
border->maxPts = maxCur; border->maxPts = maxCur;
printf("realloc border!!! (%u => %u)\n", maxOld, maxCur);
} }
@ -111,7 +111,7 @@ static void _borderClose(SwStrokeBorder* border, bool reverse)
static void _borderCubicTo(SwStrokeBorder* border, SwPoint& ctrl1, SwPoint& ctrl2, SwPoint& to) static void _borderCubicTo(SwStrokeBorder* border, SwPoint& ctrl1, SwPoint& ctrl2, SwPoint& to)
{ {
assert(border->start >= 0); assert(border && border->start >= 0);
_growBorder(border, 3); _growBorder(border, 3);
@ -124,9 +124,10 @@ static void _borderCubicTo(SwStrokeBorder* border, SwPoint& ctrl1, SwPoint& ctrl
tag[0] = SW_STROKE_TAG_CUBIC; tag[0] = SW_STROKE_TAG_CUBIC;
tag[1] = SW_STROKE_TAG_CUBIC; tag[1] = SW_STROKE_TAG_CUBIC;
tag[2] = SW_STROKE_TAG_ON; tag[2] = SW_STROKE_TAG_POINT;
border->ptsCnt += 3; border->ptsCnt += 3;
border->movable = false; border->movable = false;
} }
@ -188,13 +189,13 @@ static void _borderLineTo(SwStrokeBorder* border, SwPoint& to, bool movable)
//move last point //move last point
border->pts[border->ptsCnt - 1] = to; border->pts[border->ptsCnt - 1] = to;
} else { } else {
//don't add zero-length line_to //don't add zero-length line_to
auto diff = border->pts[border->ptsCnt - 1] - to; if (border->ptsCnt > 0 && (border->pts[border->ptsCnt - 1] - to).small()) return;
if (border->ptsCnt > 0 && diff.small()) return;
_growBorder(border, 1); _growBorder(border, 1);
border->pts[border->ptsCnt] = to; border->pts[border->ptsCnt] = to;
border->tags[border->ptsCnt] = SW_STROKE_TAG_ON; border->tags[border->ptsCnt] = SW_STROKE_TAG_POINT;
border->ptsCnt += 1; border->ptsCnt += 1;
} }
@ -207,8 +208,7 @@ static void _borderMoveTo(SwStrokeBorder* border, SwPoint& to)
assert(border); assert(border);
//close current open path if any? //close current open path if any?
if (border->start >= 0) if (border->start >= 0) _borderClose(border, false);
_borderClose(border, false);
border->start = border->ptsCnt; border->start = border->ptsCnt;
border->movable = false; border->movable = false;
@ -318,8 +318,11 @@ static void _inside(SwStroke& stroke, int32_t side, SwFixed lineLength)
border->movable = false; border->movable = false;
} else { } else {
//compute median angle //compute median angle
delta = {mathDivide(stroke.width, mathCos(theta)), 0}; auto phi = stroke.angleIn + theta;
mathRotate(delta, stroke.angleIn + theta + rotate); auto thcos = mathCos(theta);
auto length = mathDivide(stroke.width, thcos);
delta = {length, 0};
mathRotate(delta, phi + rotate);
delta += stroke.center; delta += stroke.center;
} }
@ -652,11 +655,9 @@ static void _addReverseLeft(SwStroke& stroke, bool opened)
static void _beginSubPath(SwStroke& stroke, SwPoint& to, bool opened) 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 /* We cannot process the first point because there is not enought
information regarding its corner/cap. Later, it will be processed information regarding its corner/cap. Later, it will be processed
in the _strokeEndSubPath() */ in the _endSubPath() */
stroke.firstPt = true; stroke.firstPt = true;
stroke.center = to; stroke.center = to;
@ -712,13 +713,13 @@ static void _endSubPath(SwStroke& stroke)
if (turn != 0) { if (turn != 0) {
//when we turn to the right, the inside is 0 //when we turn to the right, the inside is 0
auto inside = 0; int32_t inside = 0;
//otherwise, the inside is 1 //otherwise, the inside is 1
if (turn < 0) inside = 1; if (turn < 0) inside = 1;
_inside(stroke, inside, stroke.subPathLineLength); //inside _inside(stroke, inside, stroke.subPathLineLength); //inside
_inside(stroke, 1 - inside, stroke.subPathLineLength); //outside _outside(stroke, 1 - inside, stroke.subPathLineLength); //outside
} }
_borderClose(stroke.borders + 0, false); _borderClose(stroke.borders + 0, false);
@ -727,14 +728,81 @@ static void _endSubPath(SwStroke& stroke)
} }
static void _deleteRle(SwStroke& stroke) static void _getCounts(SwStrokeBorder* border, uint32_t& ptsCnt, uint32_t& cntrsCnt)
{ {
if (!stroke.rle) return; assert(border);
if (stroke.rle->spans) free(stroke.rle->spans);
free(stroke.rle); auto count = border->ptsCnt;
stroke.rle = nullptr; auto tags = border->tags;
uint32_t _ptsCnt = 0;
uint32_t _cntrsCnt = 0;
bool inCntr = false;
while (count > 0) {
if (tags[0] & SW_STROKE_TAG_BEGIN) {
if (inCntr) goto fail;
inCntr = true;
} else if (!inCntr) goto fail;
if (tags[0] & SW_STROKE_TAG_END) {
inCntr = false;
++_cntrsCnt;
}
--count;
++_ptsCnt;
++tags;
}
if (inCntr) goto fail;
border->valid = true;
ptsCnt = _ptsCnt;
cntrsCnt = _cntrsCnt;
return;
fail:
ptsCnt = 0;
cntrsCnt = 0;
} }
static void _exportBorderOutline(SwStroke& stroke, SwOutline* outline, uint32_t side)
{
auto border = stroke.borders + side;
assert(border);
if (!border->valid) return;
memcpy(outline->pts + outline->ptsCnt, border->pts, border->ptsCnt * sizeof(SwPoint));
auto cnt = border->ptsCnt;
auto src = border->tags;
auto tags = outline->types + outline->ptsCnt;
auto cntrs = outline->cntrs + outline->cntrsCnt;
uint16_t idx = outline->ptsCnt;
while (cnt > 0) {
if (*src & SW_STROKE_TAG_POINT) *tags = SW_CURVE_TYPE_POINT;
else if (*src & SW_STROKE_TAG_CUBIC) *tags = SW_CURVE_TYPE_CUBIC;
else cout << "what type of stroke outline??" << endl;
if (*src & SW_STROKE_TAG_END) {
*cntrs = idx;
++cntrs;
++outline->cntrsCnt;
}
++src;
++tags;
++idx;
--cnt;
}
outline->ptsCnt = outline->ptsCnt + border->ptsCnt;
}
/************************************************************************/ /************************************************************************/
/* External Class Implementation */ /* External Class Implementation */
/************************************************************************/ /************************************************************************/
@ -742,16 +810,20 @@ static void _deleteRle(SwStroke& stroke)
void strokeFree(SwStroke* stroke) void strokeFree(SwStroke* stroke)
{ {
if (!stroke) return; if (!stroke) return;
_deleteRle(*stroke);
//free borders
if (stroke->borders[0].pts) free(stroke->borders[0].pts);
if (stroke->borders[0].tags) free(stroke->borders[0].tags);
if (stroke->borders[1].pts) free(stroke->borders[1].pts);
if (stroke->borders[1].tags) free(stroke->borders[1].tags);
free(stroke); free(stroke);
} }
void strokeReset(SwStroke& stroke, float width, StrokeCap cap, StrokeJoin join) void strokeReset(SwStroke& stroke, float width, StrokeCap cap, StrokeJoin join)
{ {
_deleteRle(stroke); stroke.width = TO_SWCOORD(width * 0.5);
stroke.width = TO_SWCOORD(width * 0.5f);
stroke.cap = cap; stroke.cap = cap;
//Save line join: it can be temporarily changed when stroking curves... //Save line join: it can be temporarily changed when stroking curves...
@ -826,4 +898,33 @@ bool strokeParseOutline(SwStroke& stroke, SwOutline& outline)
} }
SwOutline* strokeExportOutline(SwStroke& stroke)
{
uint32_t count1, count2, count3, count4;
_getCounts(stroke.borders + 0, count1, count2);
_getCounts(stroke.borders + 1, count3, count4);
auto ptsCnt = count1 + count3;
auto cntrsCnt = count2 + count4;
auto outline = static_cast<SwOutline*>(calloc(1, sizeof(SwOutline)));
assert(outline);
outline->pts = static_cast<SwPoint*>(malloc(sizeof(SwPoint) * ptsCnt));
assert(outline->pts);
outline->types = static_cast<uint8_t*>(malloc(sizeof(uint8_t) * ptsCnt));
assert(outline->types);
outline->cntrs = static_cast<uint32_t*>(malloc(sizeof(uint32_t) * cntrsCnt));
assert(outline->cntrs);
_exportBorderOutline(stroke, outline, 0); //left
_exportBorderOutline(stroke, outline, 1); //right
return outline;
}
#endif /* _TVG_SW_STROKER_H_ */ #endif /* _TVG_SW_STROKER_H_ */

View file

@ -23,11 +23,10 @@ void tvgtest()
//Prepare a Shape (Rectangle + Rectangle + Circle + Circle) //Prepare a Shape (Rectangle + Rectangle + Circle + Circle)
auto shape1 = tvg::Shape::gen(); auto shape1 = tvg::Shape::gen();
shape1->appendRect(0, 0, 200, 200, 0); //x, y, w, h, cornerRadius shape1->appendRect(50, 50, 200, 200, 0); //x, y, w, h, cornerRadius
shape1->appendRect(100, 100, 300, 300, 100); //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, 400, 100, 100); //cx, cy, radiusW, radiusH
shape1->appendCircle(400, 500, 170, 100); //cx, cy, radiusW, radiusH shape1->fill(50, 50, 50, 255); //r, g, b, a
shape1->fill(255, 255, 0, 255); //r, g, b, a
//Stroke Style //Stroke Style
shape1->stroke(255, 255, 255, 255); //color: r, g, b, a shape1->stroke(255, 255, 255, 255); //color: r, g, b, a