Current stroke fails to merged shapes case...
you can test with testStroke example
Change-Id: I488af728949cba1d01b88723eb1dc4c49bac6c9b
struct SwStroke
{
- SwRleData* rle;
-
SwFixed angleIn;
SwFixed angleOut;
SwPoint center;
struct SwShape
{
SwOutline* outline;
- SwRleData* rle;
SwStroke* stroke;
+ SwRleData* rle;
+ SwRleData* strokeRle;
SwBBox bbox;
};
void strokeReset(SwStroke& stroke, float width, StrokeCap cap, StrokeJoin join);
bool strokeParseOutline(SwStroke& stroke, SwOutline& outline);
+SwOutline* strokeExportOutline(SwStroke& stroke);
void strokeFree(SwStroke* stroke);
-SwRleData* rleRender(const SwShape& sdata, const SwSize& clip);
-SwRleData* rleStrokeRender(const SwShape& sdata);
+SwRleData* rleRender(const SwOutline* outline, const SwBBox& bbox, const SwSize& clip);
+void rleFree(SwRleData* rle);
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_ */
/* 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
constexpr static auto ATAN_MAX = 23;
}
-static SwCoord _downscale(SwCoord x)
+static SwCoord _downscale(SwFixed x)
{
//multiply a give value by the CORDIC shrink factor
-
- abs(x);
- int64_t t = (x * static_cast<int64_t>(CORDIC_FACTOR)) + 0x100000000UL;
- x = static_cast<SwFixed>(t >> 32);
- if (x < 0) x = -x;
- return x;
+ auto s = abs(x);
+ int64_t t = (s * static_cast<int64_t>(CORDIC_FACTOR)) + 0x100000000UL;
+ s = static_cast<SwFixed>(t >> 32);
+ if (x < 0) s = -s;
+ return s;
}
}
+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 */
/************************************************************************/
auto shift = _normalize(v);
auto theta = angle;
- //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++;
- }
- }
+ _rotate(v, theta);
v.x = _downscale(v.x);
v.y = _downscale(v.y);
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;
+ pt.x = (v.x + half + SATURATE(v.x)) >> shift;
+ pt.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);
+ pt.x = static_cast<SwCoord>((unsigned long)v.x << shift);
+ pt.y = static_cast<SwCoord>((unsigned long)v.y << shift);
}
}
SwFixed mathTan(SwFixed angle)
{
SwPoint v = {CORDIC_FACTOR >> 8, 0};
- mathRotate(v, angle);
+ _rotate(v, angle);
return mathDivide(v.y, v.x);
}
SwFixed mathCos(SwFixed angle)
{
SwPoint v = {CORDIC_FACTOR >> 8, 0};
- mathRotate(v, angle);
+ _rotate(v, angle);
return (v.x + 0x80L) >> 8;
}
}
-/************************************************************************/
-/* External Class Implementation */
-/************************************************************************/
-
-bool rasterShape(Surface& surface, SwShape& sdata, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
+static bool
+_rasterRle(Surface& surface, SwRleData* rle, uint32_t color, uint8_t a)
{
- SwRleData* rle = sdata.rle;
if (!rle) return false;
auto span = rle->spans;
auto stride = surface.stride;
- auto color = COLOR_ARGB_JOIN(r, g, b, a);
for (uint32_t i = 0; i < rle->size; ++i) {
assert(span);
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_ */
\ No newline at end of file
if (!sdata) return false;
size_t r, g, b, a;
- shape.fill(&r, &g, &b, &a);
- size_t sa;
- shape.strokeColor(nullptr, nullptr, nullptr, &sa);
+ shape.fill(&r, &g, &b, &a);
+ if (a > 0) rasterShape(surface, *sdata, r, g, b, a);
- //invisible?
- if (a == 0 && sa == 0) return false;
+ shape.strokeColor(&r, &g, &b, &a);
+ if (a > 0) rasterStroke(surface, *sdata, r, g, b, a);
- //TODO: Threading
- return rasterShape(surface, *sdata, r, g, b, a);
+ return true;
}
goto close;
}
}
-
- //FIXME: Close the contour with a line segment?
- //_lineTo(rw, UPSCALE(outline->pts[first]));
+ _lineTo(rw, UPSCALE(outline->pts[first]));
close:
first = last + 1;
}
/* 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)
constexpr auto RENDER_POOL_SIZE = 166641L;
constexpr auto BAND_SIZE = 40;
- auto outline = sdata.outline;
assert(outline);
assert(outline->cntrs && outline->pts);
assert(outline->ptsCnt == outline->cntrs[outline->cntrsCnt - 1] + 1);
rw.area = 0;
rw.cover = 0;
rw.invalid = true;
- rw.cellMin = sdata.bbox.min;
- rw.cellMax = sdata.bbox.max;
+ rw.cellMin = bbox.min;
+ rw.cellMax = bbox.max;
rw.cellXCnt = rw.cellMax.x - rw.cellMin.x;
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.bandShoot = 0;
rw.clip = clip;
}
-SwRleData* rleStrokeRender(const SwShape& sdata)
+void rleFree(SwRleData* rle)
{
- auto stroke = sdata.stroke;
- assert(stroke);
-
- return nullptr;
+ if (!rle) return;
+ if (rle->spans) free(rle->spans);
+ free(rle);
}
+
+
#endif /* _TVG_SW_RLE_H_ */
}
+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)
{
_growOutlineContour(outline, 1);
}
-static void _initBBox(SwShape& sdata)
+static void _initBBox(SwBBox& bbox)
{
- sdata.bbox.min.x = sdata.bbox.min.y = 0;
- sdata.bbox.max.x = sdata.bbox.max.y = 0;
+ bbox.min.x = bbox.min.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;
- assert(outline);
+ if (!outline) return false;
auto pt = outline->pts;
assert(pt);
if (outline->ptsCnt <= 0) {
- _initBBox(sdata);
+ _initBBox(bbox);
return false;
}
if (yMin > pt->y) yMin = pt->y;
if (yMax < pt->y) yMax = pt->y;
}
- sdata.bbox.min.x = xMin >> 6;
- sdata.bbox.max.x = (xMax + 63) >> 6;
- sdata.bbox.min.y = yMin >> 6;
- sdata.bbox.max.y = (yMax + 63) >> 6;
+ bbox.min.x = xMin >> 6;
+ bbox.max.x = (xMax + 63) >> 6;
+ bbox.min.y = yMin >> 6;
+ bbox.max.y = (yMax + 63) >> 6;
if (xMax - xMin < 1 || yMax - yMin < 1) return false;
}
-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 */
/************************************************************************/
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;
- sdata.rle = rleRender(sdata, clip);
+ sdata.rle = rleRender(sdata.outline, sdata.bbox, clip);
end:
if (sdata.rle) return true;
void shapeDelOutline(SwShape& sdata)
{
auto outline = sdata.outline;
- if (!outline) return;
-
- if (outline->cntrs) free(outline->cntrs);
- if (outline->pts) free(outline->pts);
- if (outline->types) free(outline->types);
- free(outline);
+ _freeOutline(outline);
sdata.outline = nullptr;
}
void shapeReset(SwShape& sdata)
{
shapeDelOutline(sdata);
- _deleteRle(sdata);
- _initBBox(sdata);
+ rleFree(sdata.rle);
+ sdata.rle = nullptr;
+ _initBBox(sdata.bbox);
}
assert(sdata);
shapeDelOutline(*sdata);
- _deleteRle(*sdata);
- strokeFree(sdata->stroke);
+ rleFree(sdata->rle);
+
+ if (sdata->stroke) {
+ rleFree(sdata->strokeRle);
+ strokeFree(sdata->stroke);
+ }
+
free(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());
+ rleFree(sdata.strokeRle);
+ sdata.strokeRle = nullptr;
}
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;
}
/************************************************************************/
/* 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_BEGIN = 4;
static constexpr auto SW_STROKE_TAG_END = 8;
static void _growBorder(SwStrokeBorder* border, uint32_t newPts)
{
+ assert(border);
+
auto maxOld = border->maxPts;
auto maxNew = border->ptsCnt + newPts;
assert(border->tags);
border->maxPts = maxCur;
-
- printf("realloc border!!! (%u => %u)\n", maxOld, maxCur);
}
static void _borderCubicTo(SwStrokeBorder* border, SwPoint& ctrl1, SwPoint& ctrl2, SwPoint& to)
{
- assert(border->start >= 0);
+ assert(border && border->start >= 0);
_growBorder(border, 3);
tag[0] = 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->movable = false;
}
//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 && diff.small()) return;
+ if (border->ptsCnt > 0 && (border->pts[border->ptsCnt - 1] - to).small()) return;
_growBorder(border, 1);
border->pts[border->ptsCnt] = to;
- border->tags[border->ptsCnt] = SW_STROKE_TAG_ON;
+ border->tags[border->ptsCnt] = SW_STROKE_TAG_POINT;
border->ptsCnt += 1;
}
assert(border);
//close current open path if any?
- if (border->start >= 0)
- _borderClose(border, false);
+ if (border->start >= 0) _borderClose(border, false);
border->start = border->ptsCnt;
border->movable = false;
border->movable = false;
} else {
//compute median angle
- delta = {mathDivide(stroke.width, mathCos(theta)), 0};
- mathRotate(delta, stroke.angleIn + theta + rotate);
+ auto phi = stroke.angleIn + theta;
+ auto thcos = mathCos(theta);
+ auto length = mathDivide(stroke.width, thcos);
+ delta = {length, 0};
+ mathRotate(delta, phi + rotate);
delta += stroke.center;
}
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() */
+ in the _endSubPath() */
stroke.firstPt = true;
stroke.center = to;
if (turn != 0) {
//when we turn to the right, the inside is 0
- auto inside = 0;
+ int32_t 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
+ _outside(stroke, 1 - inside, stroke.subPathLineLength); //outside
}
_borderClose(stroke.borders + 0, false);
}
-static void _deleteRle(SwStroke& stroke)
+static void _getCounts(SwStrokeBorder* border, uint32_t& ptsCnt, uint32_t& cntrsCnt)
+{
+ assert(border);
+
+ auto count = border->ptsCnt;
+ 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)
{
- if (!stroke.rle) return;
- if (stroke.rle->spans) free(stroke.rle->spans);
- free(stroke.rle);
- stroke.rle = nullptr;
+ 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 */
/************************************************************************/
void strokeFree(SwStroke* stroke)
{
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);
}
void strokeReset(SwStroke& stroke, float width, StrokeCap cap, StrokeJoin join)
{
- _deleteRle(stroke);
-
- stroke.width = TO_SWCOORD(width * 0.5f);
+ stroke.width = TO_SWCOORD(width * 0.5);
stroke.cap = cap;
//Save line join: it can be temporarily changed when stroking curves...
}
+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_ */
//Prepare a Shape (Rectangle + Rectangle + Circle + Circle)
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->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
+ shape1->fill(50, 50, 50, 255); //r, g, b, a
//Stroke Style
shape1->stroke(255, 255, 255, 255); //color: r, g, b, a