diff --git a/src/lib/gl_engine/tvgGlRaster.cpp b/src/lib/gl_engine/tvgGlRaster.cpp index c3bb4079..50ed465f 100644 --- a/src/lib/gl_engine/tvgGlRaster.cpp +++ b/src/lib/gl_engine/tvgGlRaster.cpp @@ -29,7 +29,7 @@ struct GlShape }; -void* GlRaster::prepare(const ShapeNode& shape, void* data) +void* GlRaster::prepare(const ShapeNode& shape, void* data, UpdateFlag flags) { //prepare shape data GlShape* sdata = static_cast(data); diff --git a/src/lib/gl_engine/tvgGlRaster.h b/src/lib/gl_engine/tvgGlRaster.h index d6da2fe7..08f750c3 100644 --- a/src/lib/gl_engine/tvgGlRaster.h +++ b/src/lib/gl_engine/tvgGlRaster.h @@ -23,7 +23,7 @@ namespace tvg class GlRaster : public RasterMethod { public: - void* prepare(const ShapeNode& shape, void* data) override; + void* prepare(const ShapeNode& shape, void* data, UpdateFlag flags) override; static GlRaster* inst(); static int init(); static int term(); diff --git a/src/lib/sw_engine/meson.build b/src/lib/sw_engine/meson.build index df72f386..4937d595 100644 --- a/src/lib/sw_engine/meson.build +++ b/src/lib/sw_engine/meson.build @@ -3,6 +3,7 @@ source_file = [ 'tvgSwRaster.h', 'tvgSwRaster.cpp', 'tvgSwShape.cpp', + 'tvgSwRle.cpp', ] swraster_dep = declare_dependency( diff --git a/src/lib/sw_engine/tvgSwCommon.h b/src/lib/sw_engine/tvgSwCommon.h index a1531025..2269cfb4 100644 --- a/src/lib/sw_engine/tvgSwCommon.h +++ b/src/lib/sw_engine/tvgSwCommon.h @@ -20,31 +20,58 @@ #include "tvgCommon.h" using namespace tvg; +using SwPoint = Point; +constexpr auto SW_CURVE_TAG_CONIC = 0; constexpr auto SW_CURVE_TAG_ON = 1; constexpr auto SW_CURVE_TAG_CUBIC = 2; struct SwOutline { - size_t* cntrs; //the contour end points - size_t cntrsCnt; //number of contours in glyph - size_t reservedCntrsCnt; - Point* pts; //the outline's points - size_t ptsCnt; //number of points in the glyph - size_t reservedPtsCnt; - char* tags; //the points flags - size_t flags; //outline masks + 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; + char* tags; //the points flags + size_t flags; //outline masks +}; + +struct SwSpan +{ + size_t x; + size_t y; + size_t len; + uint8_t coverage; +}; + +struct SwRleData +{ + size_t alloc; + size_t size; + SwSpan *spans; +}; + +struct SwBBox +{ + size_t xMin, yMin; + size_t xMax, yMax; }; struct SwShape { -// SwRleRaster raster; SwOutline* outline; + SwRleData rle; + SwBBox bbox; }; bool shapeGenOutline(const ShapeNode& shape, SwShape& sdata); void shapeDelOutline(const ShapeNode& shape, SwShape& sdata); bool shapeGenRle(const ShapeNode& shape, SwShape& sdata); -bool shapeUpdateBBox(const ShapeNode& shape, SwShape& sdata); +void shapeDelRle(const ShapeNode& shape, SwShape& sdata); +bool shapeTransformOutline(const ShapeNode& shape, SwShape& sdata); + +bool rleRender(SwShape& sdata); #endif /* _TVG_SW_COMMON_H_ */ diff --git a/src/lib/sw_engine/tvgSwRaster.cpp b/src/lib/sw_engine/tvgSwRaster.cpp index a1afdda2..a7d2928b 100644 --- a/src/lib/sw_engine/tvgSwRaster.cpp +++ b/src/lib/sw_engine/tvgSwRaster.cpp @@ -26,11 +26,12 @@ static SwRaster* pInst = nullptr; + /************************************************************************/ /* External Class Implementation */ /************************************************************************/ -void* SwRaster::prepare(const ShapeNode& shape, void* data) +void* SwRaster::prepare(const ShapeNode& shape, void* data, UpdateFlag flags) { //prepare shape data SwShape* sdata = static_cast(data); @@ -39,18 +40,20 @@ void* SwRaster::prepare(const ShapeNode& shape, void* data) assert(sdata); } + if (flags == UpdateFlag::None) return nullptr; + //invisible? size_t alpha; shape.fill(nullptr, nullptr, nullptr, &alpha); if (alpha == 0) return sdata; - if (!shapeGenOutline(shape, *sdata)) return sdata; - - //TODO: From below sequence starts threading? - if (!shapeGenRle(shape, *sdata)) return sdata; - if (!shapeUpdateBBox(shape, *sdata)) return sdata; - - shapeDelOutline(shape, *sdata); + if (flags & UpdateFlag::Path) { + if (!shapeGenOutline(shape, *sdata)) return sdata; + //TODO: From below sequence starts threading? + if (!shapeTransformOutline(shape, *sdata)) return sdata; + if (!shapeGenRle(shape, *sdata)) return sdata; + shapeDelOutline(shape, *sdata); + } return sdata; } diff --git a/src/lib/sw_engine/tvgSwRaster.h b/src/lib/sw_engine/tvgSwRaster.h index 07fc274e..c690be37 100644 --- a/src/lib/sw_engine/tvgSwRaster.h +++ b/src/lib/sw_engine/tvgSwRaster.h @@ -20,7 +20,7 @@ class SwRaster : public RasterMethod { public: - void* prepare(const ShapeNode& shape, void* data) override; + void* prepare(const ShapeNode& shape, void* data, UpdateFlag flags) override; static SwRaster* inst(); static int init(); static int term(); diff --git a/src/lib/sw_engine/tvgSwRle.cpp b/src/lib/sw_engine/tvgSwRle.cpp new file mode 100644 index 00000000..d208c534 --- /dev/null +++ b/src/lib/sw_engine/tvgSwRle.cpp @@ -0,0 +1,336 @@ +/* + * 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_RLE_H_ +#define _TVG_SW_RLE_H_ + +#include +#include "tvgSwCommon.h" + +/************************************************************************/ +/* Internal Class Implementation */ +/************************************************************************/ +constexpr auto MAX_SPANS = 256; + +using SwPtrDist = ptrdiff_t; +using TPos = long; +using TCoord = long; +using TArea = int; + +struct TBand +{ + TPos min, max; +}; + +struct TCell +{ + TPos x; + TCoord cover; + TArea area; + TCell *next; +}; + +struct RleWorker +{ + TCoord ex, ey; + TPos exMin, exMax; + TPos eyMin, eyMax; + TPos exCnt, eyCnt; + + TArea area; + TCoord cover; + + TCell* cells; + SwPtrDist maxCells; + SwPtrDist numCells; + + TPos x, y; + + Point bezStack[32 * 3 + 1]; + int levStack[32]; + + SwOutline* outline; + //SwBBox clipBox; + + SwSpan spans[MAX_SPANS]; + int spansCnt; + + //render_span + //render_span_data; + int ySpan; + + int bandSize; + int bandShoot; + + jmp_buf jmpBuf; + + void* buffer; + long bufferSize; + + TCell** yCells; + TPos yCnt; + + bool invalid; +}; + +static bool rleSweep(RleWorker& rw) +{ + //TODO: + return true; +} + +static bool moveTo(SwPoint& pt) +{ + printf("moveTo = %f %f\n", pt.x, pt.y); + return true; +} + + +static bool lineTo(SwPoint& pt) +{ + printf("lineTo = %f %f\n", pt.x, pt.y); + return true; +} + + +static bool cubicTo(SwPoint& ctrl1, SwPoint& ctrl2, SwPoint& pt) +{ + printf("cubicTo = ctrl1(%f %f) ctrl2(%f %f) pt(%f %f)\n", ctrl1.x, ctrl1.y, ctrl2.x, ctrl2.y, pt.x, pt.y); + return true; +} + + +static bool decomposeOutline(RleWorker& rw) +{ + printf("decomposOutline\n"); + auto outline = rw.outline; + assert(outline); + + auto first = 0; //index of first point in contour + + for (size_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 pt = outline->pts + first; + auto tags = outline->tags + first; + + /* A contour cannot start with a cubic control point! */ + if (tags[0] == SW_CURVE_TAG_CUBIC) goto invalid_outline; + + if (!moveTo(outline->pts[first])) return false; + + while (pt < limit) { + assert(++pt); + assert(++tags); + + //emit a single line_to + if (tags[0] == SW_CURVE_TAG_ON) { + if (!lineTo(*pt)) return false; + continue; + //tag cubic + } else { + if (pt + 1 > limit || tags[1] != SW_CURVE_TAG_CUBIC) + goto invalid_outline; + + pt += 2; + tags += 2; + + if (pt <= limit) { + if (!cubicTo(pt[-2], pt[-1], pt[0])) return false; + continue; + } + if (!cubicTo(pt[-2], pt[-1], outline->pts[first])) return false; + goto close; + } + } + + //Close the contour with a line segment? + //if (!lineTo(outline->pts[first])); + close: + first = last + 1; + } + + return true; + +invalid_outline: + cout << "Invalid Outline!" << endl; + return false; +} + + +static TCell* findCell(RleWorker& rw) +{ + //TODO: + return nullptr; +} + + +static void recordCell(RleWorker& rw) +{ + if (rw.area | rw.cover) { + TCell* cell = findCell(rw); + assert(cell); + cell->area += rw.area; + cell->cover += rw.cover; + } +} + + +static bool genRle(RleWorker& rw) +{ + bool ret = false; + + if (setjmp(rw.jmpBuf) == 0) { + ret = decomposeOutline(rw); + if (!rw.invalid) recordCell(rw); + } else { + cout << "Memory Overflow" << endl; + } + return ret; +} + + +/************************************************************************/ +/* External Class Implementation */ +/************************************************************************/ + +bool rleRender(SwShape& sdata) +{ + constexpr auto RENDER_POOL_SIZE = 16384L; + constexpr auto BAND_SIZE = 40; + + auto outline = sdata.outline; + assert(outline); + + if (outline->ptsCnt == 0 || outline->cntrsCnt <= 0) return false; + + assert(outline->cntrs && outline->pts); + assert(outline->ptsCnt == outline->cntrs[outline->cntrsCnt - 1] + 1); + + //TODO: We can preserve several static workers in advance + RleWorker rw; + TCell buffer[RENDER_POOL_SIZE / sizeof(TCell)]; + + //Init Cells + rw.buffer = buffer; + rw.bufferSize = sizeof(buffer); + rw.yCells = reinterpret_cast(buffer); + rw.cells = nullptr; + rw.maxCells = 0; + rw.numCells = 0; + rw.area = 0; + rw.cover = 0; + rw.invalid = true; + rw.exMin = sdata.bbox.xMin; + rw.exMax = sdata.bbox.xMax; + rw.eyMin = sdata.bbox.yMin; + rw.eyMax = sdata.bbox.yMax; + rw.exCnt = rw.exMax - rw.exMin; + rw.eyCnt = rw.eyMax - rw.eyMin; + rw.outline = outline; + rw.bandSize = rw.bufferSize / (sizeof(TCell) * 8); //bandSize: 64 + rw.bandShoot = 0; + //printf("bufferSize = %d, bbox(%d %d %d %d), exCnt(%d), eyCnt(%d), bandSize(%d)\n", rw.bufferSize, rw.exMin, rw.eyMin, rw.exMax, rw.eyMax, rw.exCnt, rw.eyCnt, rw.bandSize); + + //Generate RLE + TBand bands[BAND_SIZE]; + TBand* band; + + /* set up vertical bands */ + auto bandCnt = (rw.eyMax - rw.eyMin) / rw.bandSize; + if (bandCnt == 0) bandCnt = 1; + else if (bandCnt >= BAND_SIZE) bandCnt = BAND_SIZE - 1; + + auto min = rw.eyMin; + auto yMax = rw.eyMax; + TPos max; +//printf("bandCnt(%d)\n", bandCnt); + + for (int n = 0; n < bandCnt; ++n, min = max) { + max = min + rw.bandSize; + if (n == bandCnt -1 || max > yMax) max = yMax; + + bands[0].min = min; + bands[0].max = max; + band = bands; + + while (band >= bands) { + rw.yCells = static_cast(rw.buffer); + rw.yCnt = band->max - band->min; + + auto cellStart = sizeof(TCell*) * rw.yCnt; + auto cellMod = cellStart % sizeof(TCell); + + if (cellMod > 0) cellStart += sizeof(TCell) - cellMod; + + auto cellEnd = rw.bufferSize; + cellEnd -= cellEnd % sizeof(TCell); +//printf("n:%d, cellStart(%d), cellEnd(%d) cellMod(%d)\n", n, cellStart, cellEnd, cellMod); + + auto cellsMax = reinterpret_cast((char*)rw.buffer + cellEnd); + rw.cells = reinterpret_cast((char*)rw.buffer + cellStart); + + if (rw.cells >= cellsMax) goto reduce_bands; + + rw.maxCells = cellsMax - rw.cells; + if (rw.maxCells < 2) goto reduce_bands; + + for (auto y = 0; y < rw.yCnt; ++y) + rw.yCells[y] = nullptr; + + rw.numCells = 0; + rw.invalid = true; + rw.eyMin = band->min; + rw.eyMax = band->max; + rw.eyCnt = band->max - band->min; + + if (!genRle(rw)) return -1; + + rleSweep(rw); + --band; + continue; + + reduce_bands: + /* render pool overflow: we will reduce the render band by half */ + auto bottom = band->min; + auto top = band->max; + auto middle = bottom + ((top - bottom) >> 1); + + /* This is too complex for a single scanline; there must + be some problems */ + if (middle == bottom) return -1; + + if (bottom - top >= rw.bandSize) ++rw.bandShoot; + + band[1].min = bottom; + band[1].max = middle; + band[0].min = middle; + band[0].max = top; + ++band; + } + } + + if (rw.bandShoot > 8 && rw.bandSize > 16) + rw.bandSize = rw.bandSize / 2; + + return true; +} + +#endif /* _TVG_SW_RLE_H_ */ diff --git a/src/lib/sw_engine/tvgSwShape.cpp b/src/lib/sw_engine/tvgSwShape.cpp index 364ac3e9..746fa891 100644 --- a/src/lib/sw_engine/tvgSwShape.cpp +++ b/src/lib/sw_engine/tvgSwShape.cpp @@ -69,7 +69,7 @@ static void outlineEnd(SwOutline& outline) growOutlineContour(outline, 1); if (outline.ptsCnt > 0) { outline.cntrs[outline.cntrsCnt] = outline.ptsCnt - 1; - ++outline.cntrs; + ++outline.cntrsCnt; } } @@ -150,25 +150,77 @@ static bool outlineClose(SwOutline& outline) } -/************************************************************************/ -/* External Class Implementation */ -/************************************************************************/ - -bool shapeGenRle(const ShapeNode& shape, SwShape& sdata) +static void initBBox(SwShape& sdata) { - //TODO: rle + sdata.bbox.xMin = sdata.bbox.yMin = 0; + sdata.bbox.xMax = sdata.bbox.yMax = 0; +} + + +static bool updateBBox(SwShape& sdata) +{ + auto outline = sdata.outline; + assert(outline); + + auto pt = outline->pts; + assert(pt); + + if (outline->ptsCnt <= 0) { + initBBox(sdata); + return false; + } + + auto xMin = pt->x; + auto xMax = pt->y; + auto yMin = pt->y; + auto yMax = pt->y; + + ++pt; + + for(size_t i = 1; i < outline->ptsCnt; ++i, ++pt) { + assert(pt); + if (xMin > pt->x) xMin = pt->x; + if (xMax < pt->y) xMax = pt->x; + if (yMin > pt->y) yMin = pt->y; + if (yMax < pt->y) yMax = pt->y; + } + sdata.bbox.xMin = round(xMin - 0.49); + sdata.bbox.xMax = round(xMax + 0.49); + sdata.bbox.yMin = round(yMin - 0.49); + sdata.bbox.yMax = round(yMax + 0.49); + + if (xMax - xMin < 1 || yMax - yMin < 1) return false; return true; } -bool shapeUpdateBBox(const ShapeNode& shape, SwShape& sdata) +/************************************************************************/ +/* External Class Implementation */ +/************************************************************************/ + +bool shapeTransformOutline(const ShapeNode& shape, SwShape& sdata) { //TODO: return true; } +void shapeDelRle(const ShapeNode& shape, SwShape& sdata) +{ + if (sdata.rle.spans) free(sdata.rle.spans); + sdata.rle.spans = nullptr; +} + + +bool shapeGenRle(const ShapeNode& shape, SwShape& sdata) +{ + shapeDelRle(shape, sdata); + if (!updateBBox(sdata)) return false; + return rleRender(sdata); +} + + void shapeDelOutline(const ShapeNode& shape, SwShape& sdata) { if (!sdata.outline) return; @@ -185,6 +237,8 @@ void shapeDelOutline(const ShapeNode& shape, SwShape& sdata) bool shapeGenOutline(const ShapeNode& shape, SwShape& sdata) { + initBBox(sdata); + const PathCommand* cmds = nullptr; auto cmdCnt = shape.pathCommands(&cmds); @@ -233,6 +287,7 @@ bool shapeGenOutline(const ShapeNode& shape, SwShape& sdata) cout << "Outline was already allocated? How?" << endl; } + //TODO: Probabry we can copy pts from shape directly. growOutlinePoint(*outline, outlinePtsCnt); growOutlineContour(*outline, outlineCntrsCnt); @@ -264,6 +319,9 @@ bool shapeGenOutline(const ShapeNode& shape, SwShape& sdata) outlineEnd(*outline); + //FIXME: + //outline->flags = SwOutline::FillRule::Winding; + sdata.outline = outline; return true; diff --git a/src/lib/tvgCommon.h b/src/lib/tvgCommon.h index dfd908f0..e9dc2490 100644 --- a/src/lib/tvgCommon.h +++ b/src/lib/tvgCommon.h @@ -20,6 +20,7 @@ #include #include #include +#include #include "tizenvg.h" using namespace std; @@ -31,8 +32,9 @@ namespace tvg class RasterMethod { public: + enum UpdateFlag { None = 0, Path = 1, Fill = 2, All = 3 }; virtual ~RasterMethod() {} - virtual void* prepare(const ShapeNode& shape, void* data) = 0; + virtual void* prepare(const ShapeNode& shape, void* data, UpdateFlag flags) = 0; }; } diff --git a/src/lib/tvgShapeNode.cpp b/src/lib/tvgShapeNode.cpp index 26735c08..81d97dd5 100644 --- a/src/lib/tvgShapeNode.cpp +++ b/src/lib/tvgShapeNode.cpp @@ -89,7 +89,7 @@ int ShapeNode :: update(RasterMethod* engine) noexcept auto impl = pImpl.get(); assert(impl); - impl->edata = engine->prepare(*this, impl->edata); + impl->edata = engine->prepare(*this, impl->edata, RasterMethod::UpdateFlag::All); if (impl->edata) return 0; return - 1; } @@ -180,9 +180,9 @@ int ShapeNode :: fill(size_t* r, size_t* g, size_t* b, size_t* a) const noexcept assert(impl); if (r) *r = impl->color[0]; - if (g) *g = impl->color[0]; - if (b) *b = impl->color[0]; - if (a) *a = impl->color[0]; + if (g) *g = impl->color[1]; + if (b) *b = impl->color[2]; + if (a) *a = impl->color[3]; return 0; }