From 42409987e2ce92e429e2d3833d0609c64983daea Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Fri, 17 May 2024 14:50:50 +0900 Subject: [PATCH] renderer/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 | 9 ++----- src/renderer/sw_engine/tvgSwRenderer.cpp | 10 ------- src/renderer/tvgCanvas.cpp | 6 +++++ src/renderer/tvgCanvas.h | 23 ++++++++++++++--- 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(+), 48 deletions(-) diff --git a/inc/thorvg.h b/inc/thorvg.h index d56c8ada..e02ec0d0 100644 --- a/inc/thorvg.h +++ b/inc/thorvg.h @@ -623,6 +623,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. * @@ -1611,7 +1635,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; @@ -1677,6 +1703,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 */ @@ -1715,6 +1743,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 9cc39f7d..94fdb14c 100644 --- a/src/renderer/gl_engine/tvgGlRenderer.cpp +++ b/src/renderer/gl_engine/tvgGlRenderer.cpp @@ -63,11 +63,6 @@ bool GlRenderer::target(int32_t id, uint32_t w, uint32_t h) surface.w = w; surface.h = h; - mViewport.x = 0; - mViewport.y = 0; - mViewport.w = surface.w; - mViewport.h = surface.h; - mTargetViewport.x = 0; mTargetViewport.y = 0; mTargetViewport.w = surface.w; @@ -459,7 +454,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; } @@ -503,7 +498,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 180a6831..fc4cc10e 100644 --- a/src/renderer/sw_engine/tvgSwRenderer.cpp +++ b/src/renderer/sw_engine/tvgSwRenderer.cpp @@ -429,22 +429,12 @@ 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); } bool SwRenderer::preRender() { - if (surface) { - vport.x = vport.y = 0; - vport.w = surface->w; - vport.h = surface->h; - } - return true; } diff --git a/src/renderer/tvgCanvas.cpp b/src/renderer/tvgCanvas.cpp index 9d51b07c..a87b8ce6 100644 --- a/src/renderer/tvgCanvas.cpp +++ b/src/renderer/tvgCanvas.cpp @@ -75,6 +75,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 9f54a1d8..16dace95 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,11 +45,11 @@ struct Canvas::Impl ~Impl() { //make it sure any deffered jobs - if (renderer) renderer->sync(); + renderer->sync(); clearPaints(); - if (renderer && (renderer->unref() == 0)) delete(renderer); + if (renderer->unref() == 0) delete(renderer); } void clearPaints() @@ -78,7 +79,7 @@ struct Canvas::Impl //Clear render target if (buffer) { - if (!renderer || !renderer->clear()) return Result::InsufficientCondition; + if (!renderer->clear()) return Result::InsufficientCondition; } if (paints) clearPaints(); @@ -137,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 080ac003..507b957f 100644 --- a/src/renderer/tvgRender.h +++ b/src/renderer/tvgRender.h @@ -98,34 +98,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();