From 090ebfba86ebff61e2a4095d670ba1bc95e5fa6b Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Fri, 17 May 2024 14:50:50 +0900 Subject: [PATCH] xrenderer/engines: support the canvas viewport function. The viewport function defines the rectangular area of the canvas that will be used for drawing operations. It is used to clip the rendering output to the boundaries of the rectangle. Apps can use this function to set the drawing region within the canvas. When the ThorVG canvas is partially inside the screen area such as during scrolling it could help enhance rendering performance. New Experimental API: - Result Canvas::viewport(int32_t x, int32_t y, int32_t w, int32_t h) noexcept; Issue: https://github.com/thorvg/thorvg/issues/2274 --- inc/thorvg.h | 31 +++++++++++++++++++++- src/renderer/gl_engine/tvgGlRenderer.cpp | 4 +-- src/renderer/sw_engine/tvgSwRenderer.cpp | 4 --- src/renderer/tvgCanvas.cpp | 6 +++++ src/renderer/tvgCanvas.h | 25 ++++++++++++++---- src/renderer/tvgGlCanvas.cpp | 2 ++ src/renderer/tvgRender.cpp | 32 +++++++++++++++++++++++ src/renderer/tvgRender.h | 33 +++++------------------- src/renderer/tvgSwCanvas.cpp | 2 ++ src/renderer/tvgWgCanvas.cpp | 2 ++ 10 files changed, 102 insertions(+), 39 deletions(-) diff --git a/inc/thorvg.h b/inc/thorvg.h index e9257dc4..ab3cb976 100644 --- a/inc/thorvg.h +++ b/inc/thorvg.h @@ -649,6 +649,30 @@ public: */ virtual Result draw() noexcept; + /** + * @brief Sets the drawing region in the canvas. + * + * This function defines the rectangular area of the canvas that will be used for drawing operations. + * The specified viewport is used to clip the rendering output to the boundaries of the rectangle. + * + * @param[in] x The x-coordinate of the upper-left corner of the rectangle. + * @param[in] y The y-coordinate of the upper-left corner of the rectangle. + * @param[in] w The width of the rectangle. + * @param[in] h The height of the rectangle. + * + * @retval Result::Success when succeed, Result::InsufficientCondition otherwise. + * + * @see SwCanvas::target() + * @see GlCanvas::target() + * @see WgCanvas::target() + * + * @warning It's not allowed to change the viewport during Canvas::push() - Canvas::sync() or Canvas::update() - Canvas::sync(). + * + * @note The specified viewport region will be intersected with the target region. + * @note Experimental API + */ + virtual Result viewport(int32_t x, int32_t y, int32_t w, int32_t h) noexcept; + /** * @brief Guarantees that drawing task is finished. * @@ -1660,7 +1684,9 @@ public: * @retval Result::InvalidArguments In case no valid pointer is provided or the width, or the height or the stride is zero. * @retval Result::NonSupport In case the software engine is not supported. * - * @warning Do not access @p buffer during Canvas::draw() - Canvas::sync(). It should not be accessed while TVG is writing on it. + * @warning Do not access @p buffer during Canvas::push() - Canvas::sync(). It should not be accessed while the engine is writing on it. + * + * @see Canvas::viewport() */ Result target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h, Colorspace cs) noexcept; @@ -1726,6 +1752,8 @@ public: * @warning This API is experimental and not officially supported. It may be modified or removed in future versions. * @warning Drawing on the main surface is currently not permitted. If the identifier (@p id) is set to @c 0, the operation will be aborted. * + * @see Canvas::viewport() + * * @note Currently, this only allows the GL_RGBA8 color space format. * @note Experimental API */ @@ -1764,6 +1792,7 @@ public: * @warning Please do not use it, this API is not official one. It could be modified in the next version. * * @note Experimental API + * @see Canvas::viewport() */ Result target(void* window, uint32_t w, uint32_t h) noexcept; diff --git a/src/renderer/gl_engine/tvgGlRenderer.cpp b/src/renderer/gl_engine/tvgGlRenderer.cpp index 02df065a..6f52ff7a 100644 --- a/src/renderer/gl_engine/tvgGlRenderer.cpp +++ b/src/renderer/gl_engine/tvgGlRenderer.cpp @@ -460,7 +460,7 @@ RenderData GlRenderer::prepare(const RenderShape& rshape, RenderData data, const RenderRegion GlRenderer::viewport() { - return {0, 0, static_cast(surface.w), static_cast(surface.h)}; + return mViewport; } @@ -504,7 +504,7 @@ GlRenderer* GlRenderer::gen() return new GlRenderer(); } -GlRenderer::GlRenderer() :mViewport() ,mGpuBuffer(new GlStageBuffer), mPrograms(), mComposePool() +GlRenderer::GlRenderer() :mGpuBuffer(new GlStageBuffer), mPrograms(), mComposePool() { } diff --git a/src/renderer/sw_engine/tvgSwRenderer.cpp b/src/renderer/sw_engine/tvgSwRenderer.cpp index 3055616d..0b2940c3 100644 --- a/src/renderer/sw_engine/tvgSwRenderer.cpp +++ b/src/renderer/sw_engine/tvgSwRenderer.cpp @@ -434,10 +434,6 @@ bool SwRenderer::target(pixel_t* data, uint32_t stride, uint32_t w, uint32_t h, surface->channelSize = CHANNEL_SIZE(cs); surface->premultiplied = true; - vport.x = vport.y = 0; - vport.w = surface->w; - vport.h = surface->h; - return rasterCompositor(surface); } diff --git a/src/renderer/tvgCanvas.cpp b/src/renderer/tvgCanvas.cpp index 11d6b778..1a36e41e 100644 --- a/src/renderer/tvgCanvas.cpp +++ b/src/renderer/tvgCanvas.cpp @@ -81,6 +81,12 @@ Result Canvas::update(Paint* paint) noexcept } +Result Canvas::viewport(int32_t x, int32_t y, int32_t w, int32_t h) noexcept +{ + return pImpl->viewport(x, y, w, h); +} + + Result Canvas::sync() noexcept { return pImpl->sync(); diff --git a/src/renderer/tvgCanvas.h b/src/renderer/tvgCanvas.h index cdad9ede..a4b866ea 100644 --- a/src/renderer/tvgCanvas.h +++ b/src/renderer/tvgCanvas.h @@ -32,6 +32,7 @@ struct Canvas::Impl list paints; RenderMethod* renderer; + RenderRegion vport = {0, 0, INT32_MAX, INT32_MAX}; Status status = Status::Synced; bool refresh = false; //if all paints should be updated by force. @@ -44,14 +45,12 @@ struct Canvas::Impl ~Impl() { //make it sure any deffered jobs - if (renderer) { - renderer->sync(); - renderer->clear(); - } + renderer->sync(); + renderer->clear(); clearPaints(); - if (renderer && (renderer->unref() == 0)) delete(renderer); + if (renderer->unref() == 0) delete(renderer); } void clearPaints() @@ -139,6 +138,22 @@ struct Canvas::Impl return Result::InsufficientCondition; } + + Result viewport(int32_t x, int32_t y, int32_t w, int32_t h) + { + if (status != Status::Synced) return Result::InsufficientCondition; + RenderRegion val = {x, y, w, h}; + //intersect if the target buffer is already set. + auto surface = renderer->mainSurface(); + if (surface && surface->w > 0 && surface->h > 0) { + val.intersect({0, 0, (int32_t)surface->w, (int32_t)surface->h}); + } + if (vport == val) return Result::Success; + renderer->viewport(val); + vport = val; + needRefresh(); + return Result::Success; + } }; #endif /* _TVG_CANVAS_H_ */ diff --git a/src/renderer/tvgGlCanvas.cpp b/src/renderer/tvgGlCanvas.cpp index 940e6682..9e568a72 100644 --- a/src/renderer/tvgGlCanvas.cpp +++ b/src/renderer/tvgGlCanvas.cpp @@ -68,6 +68,8 @@ Result GlCanvas::target(int32_t id, uint32_t w, uint32_t h) noexcept if (!renderer) return Result::MemoryCorruption; if (!renderer->target(id, w, h)) return Result::Unknown; + Canvas::pImpl->vport.intersect({0, 0, (int32_t)w, (int32_t)h}); + renderer->viewport(Canvas::pImpl->vport); //Paints must be updated again with this new target. Canvas::pImpl->needRefresh(); diff --git a/src/renderer/tvgRender.cpp b/src/renderer/tvgRender.cpp index bdfb9f32..3b638c23 100644 --- a/src/renderer/tvgRender.cpp +++ b/src/renderer/tvgRender.cpp @@ -60,3 +60,35 @@ RenderTransform::RenderTransform(const RenderTransform* lhs, const RenderTransfo else if (rhs) m = rhs->m; else mathIdentity(&m); } + + +void RenderRegion::intersect(const RenderRegion& rhs) +{ + auto x1 = x + w; + auto y1 = y + h; + auto x2 = rhs.x + rhs.w; + auto y2 = rhs.y + rhs.h; + + x = (x > rhs.x) ? x : rhs.x; + y = (y > rhs.y) ? y : rhs.y; + w = ((x1 < x2) ? x1 : x2) - x; + h = ((y1 < y2) ? y1 : y2) - y; + + if (w < 0) w = 0; + if (h < 0) h = 0; +} + + +void RenderRegion::add(const RenderRegion& rhs) +{ + if (rhs.x < x) { + w += (x - rhs.x); + x = rhs.x; + } + if (rhs.y < y) { + h += (y - rhs.y); + y = rhs.y; + } + if (rhs.x + rhs.w > x + w) w = (rhs.x + rhs.w) - x; + if (rhs.y + rhs.h > y + h) h = (rhs.y + rhs.h) - y; +} \ No newline at end of file diff --git a/src/renderer/tvgRender.h b/src/renderer/tvgRender.h index b2506e01..6ea516c2 100644 --- a/src/renderer/tvgRender.h +++ b/src/renderer/tvgRender.h @@ -100,34 +100,13 @@ struct RenderRegion { int32_t x, y, w, h; - void intersect(const RenderRegion& rhs) + void intersect(const RenderRegion& rhs); + void add(const RenderRegion& rhs); + + bool operator==(const RenderRegion& rhs) { - auto x1 = x + w; - auto y1 = y + h; - auto x2 = rhs.x + rhs.w; - auto y2 = rhs.y + rhs.h; - - x = (x > rhs.x) ? x : rhs.x; - y = (y > rhs.y) ? y : rhs.y; - w = ((x1 < x2) ? x1 : x2) - x; - h = ((y1 < y2) ? y1 : y2) - y; - - if (w < 0) w = 0; - if (h < 0) h = 0; - } - - void add(const RenderRegion& rhs) - { - if (rhs.x < x) { - w += (x - rhs.x); - x = rhs.x; - } - if (rhs.y < y) { - h += (y - rhs.y); - y = rhs.y; - } - if (rhs.x + rhs.w > x + w) w = (rhs.x + rhs.w) - x; - if (rhs.y + rhs.h > y + h) h = (rhs.y + rhs.h) - y; + if (x == rhs.x && y == rhs.y && w == rhs.w && h == rhs.h) return true; + return false; } }; diff --git a/src/renderer/tvgSwCanvas.cpp b/src/renderer/tvgSwCanvas.cpp index d154a600..24350ef1 100644 --- a/src/renderer/tvgSwCanvas.cpp +++ b/src/renderer/tvgSwCanvas.cpp @@ -87,6 +87,8 @@ Result SwCanvas::target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t if (!renderer) return Result::MemoryCorruption; if (!renderer->target(buffer, stride, w, h, static_cast(cs))) return Result::InvalidArguments; + Canvas::pImpl->vport.intersect({0, 0, (int32_t)w, (int32_t)h}); + renderer->viewport(Canvas::pImpl->vport); //Paints must be updated again with this new target. Canvas::pImpl->needRefresh(); diff --git a/src/renderer/tvgWgCanvas.cpp b/src/renderer/tvgWgCanvas.cpp index 5205df17..5391de0a 100644 --- a/src/renderer/tvgWgCanvas.cpp +++ b/src/renderer/tvgWgCanvas.cpp @@ -63,6 +63,8 @@ Result WgCanvas::target(void* window, uint32_t w, uint32_t h) noexcept if (!renderer) return Result::MemoryCorruption; if (!renderer->target(window, w, h)) return Result::Unknown; + Canvas::pImpl->vport.intersect({0, 0, (int32_t)w, (int32_t)h}); + renderer->viewport(Canvas::pImpl->vport); //Paints must be updated again with this new target. Canvas::pImpl->needRefresh();