diff --git a/inc/thorvg.h b/inc/thorvg.h index 7e8988a6..efba9380 100644 --- a/inc/thorvg.h +++ b/inc/thorvg.h @@ -183,6 +183,33 @@ struct Matrix float e31, e32, e33; }; +/** + * @brief A data structure representing a texture mesh vertex + * + * @param pt The vertex coordinate + * @param uv The normalized texture coordinate in the range (0.0..1.0, 0.0..1.0) + * + * @BETA_API + */ +struct Vertex +{ + Point pt; + Point uv; +}; + + +/** + * @brief A data structure representing a triange in a texture mesh + * + * @param vertex The three vertices that make up the polygon + * + * @BETA_API + */ +struct Polygon +{ + Vertex vertex[3]; +}; + /** * @class Paint @@ -1177,6 +1204,42 @@ public: */ Result load(uint32_t* data, uint32_t w, uint32_t h, bool copy) noexcept; + /** + * @brief Sets or removes the triangle mesh to deform the image. + * + * If a mesh is provided, the transform property of the Picture will apply to the triangle mesh, and the + * image data will be used as the texture. + * + * If triangles is null, or triangleCount is 0, the mesh will be removed. + * + * Only raster image types are supported at this time (png, jpg). Vector types like svg and tvg do not support + * mesh deformation. However, if required you should be able to render a vector image to a raster image and then apply a mesh. + * + * @param[in] triangles An array of Polygon objects (triangles) that make up the mesh, or null to remove the mesh + * @param[in] triangleCount The number of Polygon objects (triangles) provided, or 0 to remove the mesh + * + * @retval Result::Success When succeed. + * @retval Result::Unknown If fails + * + * @note The Polygons are copied internally, so modifying them after calling Mesh::mesh has no affect. + * + * @BETA_API + */ + Result mesh(const Polygon* triangles, const uint32_t triangleCount) noexcept; + + /** + * @brief Return the number of triangles in the mesh, and optionally get a pointer to the array of triangles in the mesh. + * + * @param[out] triangles Optional. A pointer to the array of Polygons used by this mesh + * + * @return uint32_t The number of polygons in the array + * + * @note Modifying the triangles returned by this method will modify them directly within the mesh + * + * @BETA_API + */ + uint32_t mesh(const Polygon** triangles) const noexcept; + /** * @brief Gets the position and the size of the loaded SVG picture. * diff --git a/src/lib/gl_engine/tvgGlRenderer.cpp b/src/lib/gl_engine/tvgGlRenderer.cpp index 2d11fb77..5a4ba35e 100644 --- a/src/lib/gl_engine/tvgGlRenderer.cpp +++ b/src/lib/gl_engine/tvgGlRenderer.cpp @@ -129,6 +129,12 @@ bool GlRenderer::renderImage(TVG_UNUSED void* data) } +bool GlRenderer::renderImageMesh(TVG_UNUSED void* data) +{ + return false; +} + + bool GlRenderer::renderShape(RenderData data) { auto sdata = static_cast(data); diff --git a/src/lib/gl_engine/tvgGlRenderer.h b/src/lib/gl_engine/tvgGlRenderer.h index e0e8ccee..7767a625 100644 --- a/src/lib/gl_engine/tvgGlRenderer.h +++ b/src/lib/gl_engine/tvgGlRenderer.h @@ -35,6 +35,7 @@ public: bool preRender() override; bool renderShape(RenderData data) override; bool renderImage(RenderData data) override; + bool renderImageMesh(RenderData data) override; bool postRender() override; bool dispose(RenderData data) override;; RenderRegion region(RenderData data) override; diff --git a/src/lib/sw_engine/tvgSwCommon.h b/src/lib/sw_engine/tvgSwCommon.h index 47b0cb83..888f4fd9 100644 --- a/src/lib/sw_engine/tvgSwCommon.h +++ b/src/lib/sw_engine/tvgSwCommon.h @@ -321,7 +321,7 @@ bool strokeParseOutline(SwStroke* stroke, const SwOutline& outline); SwOutline* strokeExportOutline(SwStroke* stroke, SwMpool* mpool, unsigned tid); void strokeFree(SwStroke* stroke); -bool imagePrepare(SwImage* image, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid); +bool imagePrepare(SwImage* image, Polygon* triangles, uint32_t triangleCount, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid); bool imageGenRle(SwImage* image, const SwBBox& renderRegion, bool antiAlias); void imageDelOutline(SwImage* image, SwMpool* mpool, uint32_t tid); void imageReset(SwImage* image); @@ -351,6 +351,7 @@ bool rasterCompositor(SwSurface* surface); bool rasterGradientShape(SwSurface* surface, SwShape* shape, unsigned id); bool rasterShape(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a); bool rasterImage(SwSurface* surface, SwImage* image, const Matrix* transform, const SwBBox& bbox, uint32_t opacity); +bool rasterImageMesh(SwSurface* surface, SwImage* image, const Polygon* triangles, const uint32_t triangleCount, const Matrix* transform, const SwBBox& bbox, uint32_t opacity); bool rasterStroke(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a); bool rasterGradientStroke(SwSurface* surface, SwShape* shape, unsigned id); bool rasterClear(SwSurface* surface); diff --git a/src/lib/sw_engine/tvgSwImage.cpp b/src/lib/sw_engine/tvgSwImage.cpp index c02e28b4..a1b8960e 100644 --- a/src/lib/sw_engine/tvgSwImage.cpp +++ b/src/lib/sw_engine/tvgSwImage.cpp @@ -33,7 +33,7 @@ static inline bool _onlyShifted(const Matrix* m) } -static bool _genOutline(SwImage* image, const Matrix* transform, SwMpool* mpool, unsigned tid) +static bool _genOutline(SwImage* image, Polygon* triangles, uint32_t triangleCount, const Matrix* transform, SwMpool* mpool, unsigned tid) { image->outline = mpoolReqOutline(mpool, tid); auto outline = image->outline; @@ -51,10 +51,49 @@ static bool _genOutline(SwImage* image, const Matrix* transform, SwMpool* mpool, outline->closed[0] = true; } - auto w = static_cast(image->w); - auto h = static_cast(image->h); + Point to[4]; + if (triangleCount > 0) { + // TODO: Optimise me. We appear to calculate this exact min/max bounding area in multiple + // places. We should be able to re-use one we have already done? Also see: + // tvgPictureImpl.h --> bounds + // tvgSwRasterTexmap.h --> _rasterTexmapPolygonMesh + // + // TODO: Should we calculate the exact path(s) of the triangle mesh instead? + // i.e. copy tvgSwShape.capp -> _genOutline? + // + // TODO: Cntrs? + Point min = { triangles[0].vertex[0].pt.x, triangles[0].vertex[0].pt.y }; + Point max = { triangles[0].vertex[0].pt.x, triangles[0].vertex[0].pt.y }; - Point to[4] = {{0 ,0}, {w, 0}, {w, h}, {0, h}}; + for (uint32_t i = 0; i < triangleCount; ++i) { + if (triangles[i].vertex[0].pt.x < min.x) min.x = triangles[i].vertex[0].pt.x; + else if (triangles[i].vertex[0].pt.x > max.x) max.x = triangles[i].vertex[0].pt.x; + if (triangles[i].vertex[0].pt.y < min.y) min.y = triangles[i].vertex[0].pt.y; + else if (triangles[i].vertex[0].pt.y > max.y) max.y = triangles[i].vertex[0].pt.y; + + if (triangles[i].vertex[1].pt.x < min.x) min.x = triangles[i].vertex[1].pt.x; + else if (triangles[i].vertex[1].pt.x > max.x) max.x = triangles[i].vertex[1].pt.x; + if (triangles[i].vertex[1].pt.y < min.y) min.y = triangles[i].vertex[1].pt.y; + else if (triangles[i].vertex[1].pt.y > max.y) max.y = triangles[i].vertex[1].pt.y; + + if (triangles[i].vertex[2].pt.x < min.x) min.x = triangles[i].vertex[2].pt.x; + else if (triangles[i].vertex[2].pt.x > max.x) max.x = triangles[i].vertex[2].pt.x; + if (triangles[i].vertex[2].pt.y < min.y) min.y = triangles[i].vertex[2].pt.y; + else if (triangles[i].vertex[2].pt.y > max.y) max.y = triangles[i].vertex[2].pt.y; + } + + to[0] = {min.x, min.y}; + to[1] = {max.x, min.y}; + to[2] = {max.x, max.y}; + to[3] = {min.x, max.y}; + } else { + auto w = static_cast(image->w); + auto h = static_cast(image->h); + to[0] = {0, 0}; + to[1] = {w, 0}; + to[2] = {w, h}; + to[3] = {0, h}; + } for (int i = 0; i < 4; i++) { outline->pts[outline->ptsCnt] = mathTransform(&to[i], transform); outline->types[outline->ptsCnt] = SW_CURVE_TYPE_POINT; @@ -78,7 +117,7 @@ static bool _genOutline(SwImage* image, const Matrix* transform, SwMpool* mpool, /* External Class Implementation */ /************************************************************************/ -bool imagePrepare(SwImage* image, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid) +bool imagePrepare(SwImage* image, Polygon* triangles, uint32_t triangleCount, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid) { image->direct = _onlyShifted(transform); @@ -96,7 +135,7 @@ bool imagePrepare(SwImage* image, const Matrix* transform, const SwBBox& clipReg else image->scaled = false; } - if (!_genOutline(image, transform, mpool, tid)) return false; + if (!_genOutline(image, triangles, triangleCount, transform, mpool, tid)) return false; return mathUpdateOutlineBBox(image->outline, clipRegion, renderRegion, image->direct); } diff --git a/src/lib/sw_engine/tvgSwRaster.cpp b/src/lib/sw_engine/tvgSwRaster.cpp index 14e26e4b..7512efc9 100644 --- a/src/lib/sw_engine/tvgSwRaster.cpp +++ b/src/lib/sw_engine/tvgSwRaster.cpp @@ -687,6 +687,22 @@ static bool _transformedRGBAImage(SwSurface* surface, const SwImage* image, cons return false; } +static bool _transformedRGBAImageMesh(SwSurface* surface, const SwImage* image, const Polygon* triangles, const uint32_t count, const Matrix* transform, const SwBBox* region, uint32_t opacity) +{ + if (_compositing(surface)) { + if (surface->compositor->method == CompositeMethod::AlphaMask) { + return _rasterTexmapPolygonMesh(surface, image, triangles, count, transform, region, opacity, _alpha); + } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { + return _rasterTexmapPolygonMesh(surface, image, triangles, count, transform, region, opacity, _ialpha); + } else if (surface->compositor->method == CompositeMethod::LumaMask) { + return _rasterTexmapPolygonMesh(surface, image, triangles, count, transform, region, opacity, surface->blender.lumaValue); + } + } else { + return _rasterTexmapPolygonMesh(surface, image, triangles, count, transform, region, opacity, nullptr); + } + return false; +} + /************************************************************************/ /*Scaled RGBA Image */ @@ -1556,3 +1572,15 @@ bool rasterImage(SwSurface* surface, SwImage* image, const Matrix* transform, co //TODO: case: _rasterAlphaImage() return _rasterRGBAImage(surface, image, transform, bbox, opacity); } + +bool rasterImageMesh(SwSurface* surface, SwImage* image, const Polygon* triangles, const uint32_t count, const Matrix* transform, const SwBBox& bbox, uint32_t opacity) +{ + //Verify Boundary + if (bbox.max.x < 0 || bbox.max.y < 0 || bbox.min.x >= static_cast(surface->w) || bbox.min.y >= static_cast(surface->h)) return false; + + //TOOD: switch (image->format) + //TODO: case: _rasterRGBImageMesh() + //TODO: case: _rasterGrayscaleImageMesh() + //TODO: case: _rasterAlphaImageMesh() + return _transformedRGBAImageMesh(surface, image, triangles, count, transform, &bbox, opacity); +} \ No newline at end of file diff --git a/src/lib/sw_engine/tvgSwRasterTexmap.h b/src/lib/sw_engine/tvgSwRasterTexmap.h index abb57d7c..32a772ed 100644 --- a/src/lib/sw_engine/tvgSwRasterTexmap.h +++ b/src/lib/sw_engine/tvgSwRasterTexmap.h @@ -20,17 +20,6 @@ * SOFTWARE. */ -struct Vertex -{ - Point pt; - Point uv; -}; - -struct Polygon -{ - Vertex vertex[3]; -}; - struct AALine { int32_t x[2]; @@ -287,18 +276,10 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const } -static AASpans* _AASpans(const Vertex* vertices, const SwImage* image, const SwBBox* region) +static AASpans* _AASpans(float ymin, float ymax, const SwImage* image, const SwBBox* region) { - //Initialize Y range - float ys = FLT_MAX, ye = -1.0f; - - for (int i = 0; i < 4; i++) { - if (vertices[i].pt.y < ys) ys = vertices[i].pt.y; - if (vertices[i].pt.y > ye) ye = vertices[i].pt.y; - } - - auto yStart = static_cast(ys); - auto yEnd = static_cast(ye); + auto yStart = static_cast(ymin); + auto yEnd = static_cast(ymax); if (!_arrange(image, region, yStart, yEnd)) return nullptr; @@ -577,9 +558,15 @@ static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const vertices[2] = {{float(image->w), float(image->h)}, {float(image->w), float(image->h)}}; vertices[3] = {{0.0f, float(image->h)}, {0.0f, float(image->h)}}; - for (int i = 0; i < 4; i++) mathMultiply(&vertices[i].pt, transform); + float ys = FLT_MAX, ye = -1.0f; + for (int i = 0; i < 4; i++) { + mathMultiply(&vertices[i].pt, transform); - auto aaSpans = _AASpans(vertices, image, region); + if (vertices[i].pt.y < ys) ys = vertices[i].pt.y; + if (vertices[i].pt.y > ye) ye = vertices[i].pt.y; + } + + auto aaSpans = _AASpans(ys, ye, image, region); if (!aaSpans) return true; Polygon polygon; @@ -600,3 +587,61 @@ static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const return _apply(surface, aaSpans); } + + +/* + Provide any number of triangles to draw a mesh using the supplied image. + Indexes are not used, so each triangle (Polygon) vertex has to be defined, even if they copy the previous one. + Example: + + 0 -- 1 0 -- 1 0 + | / | --> | / / | + | / | | / / | + 2 -- 3 2 1 -- 2 + + Should provide two Polygons, one for each triangle. + // TODO: region? +*/ +static bool _rasterTexmapPolygonMesh(SwSurface* surface, const SwImage* image, const Polygon* triangles, const uint32_t triangleCount, const Matrix* transform, const SwBBox* region, uint32_t opacity, uint32_t (*blendMethod)(uint32_t)) +{ + //Exceptions: No dedicated drawing area? + if (!region && image->rle->size == 0) return false; + + // Step polygons once to transform + auto transformedTris = (Polygon*)malloc(sizeof(Polygon) * triangleCount); + float ys = FLT_MAX, ye = -1.0f; + for (uint32_t i = 0; i < triangleCount; i++) { + transformedTris[i] = triangles[i]; + mathMultiply(&transformedTris[i].vertex[0].pt, transform); + mathMultiply(&transformedTris[i].vertex[1].pt, transform); + mathMultiply(&transformedTris[i].vertex[2].pt, transform); + + if (transformedTris[i].vertex[0].pt.y < ys) ys = transformedTris[i].vertex[0].pt.y; + else if (transformedTris[i].vertex[0].pt.y > ye) ye = transformedTris[i].vertex[0].pt.y; + if (transformedTris[i].vertex[1].pt.y < ys) ys = transformedTris[i].vertex[1].pt.y; + else if (transformedTris[i].vertex[1].pt.y > ye) ye = transformedTris[i].vertex[1].pt.y; + if (transformedTris[i].vertex[2].pt.y < ys) ys = transformedTris[i].vertex[2].pt.y; + else if (transformedTris[i].vertex[2].pt.y > ye) ye = transformedTris[i].vertex[2].pt.y; + + // Convert normalized UV coordinates to image coordinates + transformedTris[i].vertex[0].uv.x *= (float)image->w; + transformedTris[i].vertex[0].uv.y *= (float)image->h; + transformedTris[i].vertex[1].uv.x *= (float)image->w; + transformedTris[i].vertex[1].uv.y *= (float)image->h; + transformedTris[i].vertex[2].uv.x *= (float)image->w; + transformedTris[i].vertex[2].uv.y *= (float)image->h; + } + + // Get AA spans and step polygons again to draw + auto aaSpans = _AASpans(ys, ye, image, region); + if (aaSpans) { + for (uint32_t i = 0; i < triangleCount; i++) { + _rasterPolygonImage(surface, image, region, opacity, transformedTris[i], blendMethod, aaSpans); + } + // Apply to surface (note: frees the AA spans) + _apply(surface, aaSpans); + } + free(transformedTris); + + return true; +} diff --git a/src/lib/sw_engine/tvgSwRenderer.cpp b/src/lib/sw_engine/tvgSwRenderer.cpp index c3872f32..692d5922 100644 --- a/src/lib/sw_engine/tvgSwRenderer.cpp +++ b/src/lib/sw_engine/tvgSwRenderer.cpp @@ -181,6 +181,8 @@ struct SwShapeTask : SwTask struct SwImageTask : SwTask { SwImage image; + Polygon* triangles; + uint32_t triangleCount; void run(unsigned tid) override { @@ -191,9 +193,10 @@ struct SwImageTask : SwTask imageReset(&image); if (!image.data || image.w == 0 || image.h == 0) goto end; - if (!imagePrepare(&image, transform, clipRegion, bbox, mpool, tid)) goto end; + if (!imagePrepare(&image, triangles, triangleCount, transform, clipRegion, bbox, mpool, tid)) goto end; - if (clips.count > 0) { + // TODO: How do we clip the triangle mesh? Only clip non-meshed images for now + if (triangleCount == 0 && clips.count > 0) { if (!imageGenRle(&image, bbox, false)) goto end; if (image.rle) { for (auto clip = clips.data; clip < (clips.data + clips.count); ++clip) { @@ -356,6 +359,17 @@ bool SwRenderer::renderImage(RenderData data) } +bool SwRenderer::renderImageMesh(RenderData data) +{ + auto task = static_cast(data); + task->done(); + + if (task->opacity == 0) return true; + + return rasterImageMesh(surface, &task->image, task->triangles, task->triangleCount, task->transform, task->bbox, task->opacity); +} + + bool SwRenderer::renderShape(RenderData data) { auto task = static_cast(data); @@ -610,18 +624,18 @@ void* SwRenderer::prepareCommon(SwTask* task, const RenderTransform* transform, } -RenderData SwRenderer::prepare(Surface* image, RenderData data, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag flags) +RenderData SwRenderer::prepare(Surface* image, Polygon* triangles, uint32_t triangleCount, RenderData data, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag flags) { //prepare task auto task = static_cast(data); - if (!task) { - task = new SwImageTask; - if (flags & RenderUpdateFlag::Image) { - task->image.data = image->buffer; - task->image.w = image->w; - task->image.h = image->h; - task->image.stride = image->stride; - } + if (!task) task = new SwImageTask; + if (flags & RenderUpdateFlag::Image) { + task->image.data = image->buffer; + task->image.w = image->w; + task->image.h = image->h; + task->image.stride = image->stride; + task->triangles = triangles; + task->triangleCount = triangleCount; } return prepareCommon(task, transform, opacity, clips, flags); } diff --git a/src/lib/sw_engine/tvgSwRenderer.h b/src/lib/sw_engine/tvgSwRenderer.h index 574d9d48..9bc1c835 100644 --- a/src/lib/sw_engine/tvgSwRenderer.h +++ b/src/lib/sw_engine/tvgSwRenderer.h @@ -36,10 +36,11 @@ class SwRenderer : public RenderMethod { public: RenderData prepare(const Shape& shape, RenderData data, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag flags) override; - RenderData prepare(Surface* image, RenderData data, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag flags) override; + RenderData prepare(Surface* image, Polygon* triangles, uint32_t triangleCnt, RenderData data, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag flags) override; bool preRender() override; bool renderShape(RenderData data) override; bool renderImage(RenderData data) override; + bool renderImageMesh(RenderData data) override; bool postRender() override; bool dispose(RenderData data) override; RenderRegion region(RenderData data) override; diff --git a/src/lib/tvgPicture.cpp b/src/lib/tvgPicture.cpp index 1e04e254..4449d2d7 100644 --- a/src/lib/tvgPicture.cpp +++ b/src/lib/tvgPicture.cpp @@ -119,3 +119,17 @@ const uint32_t* Picture::data(uint32_t* w, uint32_t* h) const noexcept if (pImpl->surface) return pImpl->surface->buffer; else return nullptr; } + + +Result Picture::mesh(const Polygon* triangles, const uint32_t triangleCount) noexcept +{ + if (pImpl->mesh(triangles, triangleCount)) return Result::Success; + return Result::Unknown; +} + + +uint32_t Picture::mesh(const Polygon** triangles) const noexcept +{ + if (triangles) *triangles = pImpl->triangles; + return pImpl->triangleCount; +} diff --git a/src/lib/tvgPictureImpl.h b/src/lib/tvgPictureImpl.h index b2e097d4..d8aaec03 100644 --- a/src/lib/tvgPictureImpl.h +++ b/src/lib/tvgPictureImpl.h @@ -63,6 +63,8 @@ struct Picture::Impl Paint* paint = nullptr; //vector picture uses Surface* surface = nullptr; //bitmap picture uses + Polygon* triangles = nullptr; //mesh data + uint32_t triangleCount = 0; //mesh triangle count void* rdata = nullptr; //engine data float w = 0, h = 0; bool resizing = false; @@ -70,6 +72,7 @@ struct Picture::Impl ~Impl() { if (paint) delete(paint); + free(triangles); free(surface); } @@ -128,7 +131,7 @@ struct Picture::Impl if (surface) { auto transform = resizeTransform(pTransform); - rdata = renderer.prepare(surface, rdata, &transform, opacity, clips, static_cast(pFlag | flag)); + rdata = renderer.prepare(surface, triangles, triangleCount, rdata, &transform, opacity, clips, static_cast(pFlag | flag)); } else if (paint) { if (resizing) { loader->resize(paint, w, h); @@ -141,7 +144,10 @@ struct Picture::Impl bool render(RenderMethod &renderer) { - if (surface) return renderer.renderImage(rdata); + if (surface) { + if (triangles) return renderer.renderImageMesh(rdata); + else return renderer.renderImage(rdata); + } else if (paint) return paint->pImpl->render(renderer); return false; } @@ -166,10 +172,41 @@ struct Picture::Impl bool bounds(float* x, float* y, float* w, float* h) { - if (x) *x = 0; - if (y) *y = 0; - if (w) *w = this->w; - if (h) *h = this->h; + if (triangleCount > 0) { + + Point min = { triangles[0].vertex[0].pt.x, triangles[0].vertex[0].pt.y }; + Point max = { triangles[0].vertex[0].pt.x, triangles[0].vertex[0].pt.y }; + + for (uint32_t i = 0; i < triangleCount; ++i) { + if (triangles[i].vertex[0].pt.x < min.x) min.x = triangles[i].vertex[0].pt.x; + else if (triangles[i].vertex[0].pt.x > max.x) max.x = triangles[i].vertex[0].pt.x; + if (triangles[i].vertex[0].pt.y < min.y) min.y = triangles[i].vertex[0].pt.y; + else if (triangles[i].vertex[0].pt.y > max.y) max.y = triangles[i].vertex[0].pt.y; + + if (triangles[i].vertex[1].pt.x < min.x) min.x = triangles[i].vertex[1].pt.x; + else if (triangles[i].vertex[1].pt.x > max.x) max.x = triangles[i].vertex[1].pt.x; + if (triangles[i].vertex[1].pt.y < min.y) min.y = triangles[i].vertex[1].pt.y; + else if (triangles[i].vertex[1].pt.y > max.y) max.y = triangles[i].vertex[1].pt.y; + + if (triangles[i].vertex[2].pt.x < min.x) min.x = triangles[i].vertex[2].pt.x; + else if (triangles[i].vertex[2].pt.x > max.x) max.x = triangles[i].vertex[2].pt.x; + if (triangles[i].vertex[2].pt.y < min.y) min.y = triangles[i].vertex[2].pt.y; + else if (triangles[i].vertex[2].pt.y > max.y) max.y = triangles[i].vertex[2].pt.y; + } + + if (x) *x = min.x; + if (y) *y = min.y; + if (w) *w = max.x - min.x; + if (h) *h = max.y - min.y; + + } else { + + if (x) *x = 0; + if (y) *y = 0; + if (w) *w = this->w; + if (h) *h = this->h; + + } return true; } @@ -220,6 +257,20 @@ struct Picture::Impl return Result::Success; } + bool mesh(const Polygon* triangles, const uint32_t triangleCount) + { + if (triangles && triangleCount > 0) { + this->triangleCount = triangleCount; + this->triangles = (Polygon*)malloc(sizeof(Polygon) * triangleCount); + for (uint32_t i = 0; i < triangleCount; i++) this->triangles[i] = triangles[i]; + } else { + free(this->triangles); + this->triangles = nullptr; + this->triangleCount = 0; + } + return true; + } + Paint* duplicate() { reload(); @@ -238,6 +289,12 @@ struct Picture::Impl dup->h = h; dup->resizing = resizing; + if (triangleCount > 0) { + dup->triangleCount = triangleCount; + dup->triangles = (Polygon*)malloc(sizeof(Polygon) * triangleCount); + for (uint32_t i = 0; i < triangleCount; i++) dup->triangles[i] = triangles[i]; + } + return ret.release(); } diff --git a/src/lib/tvgRender.h b/src/lib/tvgRender.h index ed66f393..54ca5243 100644 --- a/src/lib/tvgRender.h +++ b/src/lib/tvgRender.h @@ -90,10 +90,11 @@ class RenderMethod public: virtual ~RenderMethod() {} virtual RenderData prepare(const Shape& shape, RenderData data, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag flags) = 0; - virtual RenderData prepare(Surface* image, RenderData data, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag flags) = 0; + virtual RenderData prepare(Surface* image, Polygon* triangles, uint32_t triangleCnt, RenderData data, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag flags) = 0; virtual bool preRender() = 0; virtual bool renderShape(RenderData data) = 0; virtual bool renderImage(RenderData data) = 0; + virtual bool renderImageMesh(RenderData data) = 0; virtual bool postRender() = 0; virtual bool dispose(RenderData data) = 0; virtual RenderRegion region(RenderData data) = 0;