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
This commit is contained in:
Hermet Park 2024-05-17 14:50:50 +09:00 committed by Hermet Park
parent 70708211fe
commit 42409987e2
10 changed files with 102 additions and 48 deletions

View file

@ -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;

View file

@ -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<int32_t>(surface.w), static_cast<int32_t>(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()
{
}

View file

@ -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;
}

View file

@ -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();

View file

@ -32,6 +32,7 @@ struct Canvas::Impl
list<Paint*> 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_ */

View file

@ -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();

View file

@ -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;
}

View file

@ -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;
}
};

View file

@ -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<ColorSpace>(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();

View file

@ -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();