mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-07 21:23:32 +00:00
renderer: revise the Bounding Box Behavior
- modify the concept of AABB to apply only to transformed shapes. - transform points before computing the bounding box min/max to obtain a more compact shape region. - trimmming memory by removing the cached matrix, about 36kb of memory has been reduced per paint instance.
This commit is contained in:
parent
a7efd0f199
commit
333e65ef7a
18 changed files with 113 additions and 105 deletions
|
@ -439,8 +439,6 @@ public:
|
||||||
* @retval Result::InvalidArguments @p pt4 is @c nullptr.
|
* @retval Result::InvalidArguments @p pt4 is @c nullptr.
|
||||||
* @retval Result::InsufficientCondition If it failed to compute the bounding box (mostly due to invalid path information).
|
* @retval Result::InsufficientCondition If it failed to compute the bounding box (mostly due to invalid path information).
|
||||||
*
|
*
|
||||||
* @note The paint must be pushed into a canvas and updated before calling this function.
|
|
||||||
*
|
|
||||||
* @see Paint::bounds(float* x, float* y, folat* w, float* h)
|
* @see Paint::bounds(float* x, float* y, folat* w, float* h)
|
||||||
* @see Canvas::update()
|
* @see Canvas::update()
|
||||||
*
|
*
|
||||||
|
@ -449,9 +447,9 @@ public:
|
||||||
Result bounds(Point* pt4) const noexcept;
|
Result bounds(Point* pt4) const noexcept;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Retrieves the axis-aligned bounding box (AABB) of the paint object in local space.
|
* @brief Retrieves the axis-aligned bounding box (AABB) of the paint object in canvas space.
|
||||||
*
|
*
|
||||||
* This function returns the bounding box of the paint object relative to its local coordinate system, without applying any transformations.
|
* This function returns the bounding box of the paint, as an axis-aligned bounding box (AABB) after transformations are applied.
|
||||||
*
|
*
|
||||||
* @param[out] x The x-coordinate of the upper-left corner of the bounding box.
|
* @param[out] x The x-coordinate of the upper-left corner of the bounding box.
|
||||||
* @param[out] y The y-coordinate of the upper-left corner of the bounding box.
|
* @param[out] y The y-coordinate of the upper-left corner of the bounding box.
|
||||||
|
@ -460,8 +458,6 @@ public:
|
||||||
*
|
*
|
||||||
* @retval Result::InsufficientCondition If it failed to compute the bounding box (mostly due to invalid path information).
|
* @retval Result::InsufficientCondition If it failed to compute the bounding box (mostly due to invalid path information).
|
||||||
*
|
*
|
||||||
* @note The bounding box is calculated in the object's local space, meaning transformations such as scaling, rotation, or translation are not applied.
|
|
||||||
*
|
|
||||||
* @see Paint::bounds(Point* pt4)
|
* @see Paint::bounds(Point* pt4)
|
||||||
* @see Canvas::update()
|
* @see Canvas::update()
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -912,9 +912,9 @@ TVG_API Tvg_Paint* tvg_paint_duplicate(Tvg_Paint* paint);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Retrieves the axis-aligned bounding box (AABB) of the paint object in local space.
|
* @brief Retrieves the axis-aligned bounding box (AABB) of the paint object in canvas space.
|
||||||
*
|
*
|
||||||
* This function returns the bounding box of the paint object relative to its local coordinate system, without applying any transformations.
|
* This function returns the bounding box of the paint, as an axis-aligned bounding box (AABB) after transformations are applied.
|
||||||
*
|
*
|
||||||
* @param[in] paint The Tvg_Paint object of which to get the bounds.
|
* @param[in] paint The Tvg_Paint object of which to get the bounds.
|
||||||
* @param[out] x The x-coordinate of the upper-left corner of the bounding box.
|
* @param[out] x The x-coordinate of the upper-left corner of the bounding box.
|
||||||
|
@ -926,8 +926,6 @@ TVG_API Tvg_Paint* tvg_paint_duplicate(Tvg_Paint* paint);
|
||||||
* @retval TVG_RESULT_INVALID_ARGUMENT An invalid @p paint.
|
* @retval TVG_RESULT_INVALID_ARGUMENT An invalid @p paint.
|
||||||
* @retval TVG_RESULT_INSUFFICIENT_CONDITION If it failed to compute the bounding box (mostly due to invalid path information).
|
* @retval TVG_RESULT_INSUFFICIENT_CONDITION If it failed to compute the bounding box (mostly due to invalid path information).
|
||||||
*
|
*
|
||||||
* @note The bounding box is calculated in the object's local space, meaning transformations such as scaling, rotation, or translation are not applied.
|
|
||||||
*
|
|
||||||
* @see tvg_paint_get_obb()
|
* @see tvg_paint_get_obb()
|
||||||
* @see tvg_canvas_update_paint()
|
* @see tvg_canvas_update_paint()
|
||||||
*/
|
*/
|
||||||
|
@ -946,8 +944,6 @@ TVG_API Tvg_Result tvg_paint_get_aabb(const Tvg_Paint* paint, float* x, float* y
|
||||||
* @retval TVG_RESULT_INVALID_ARGUMENT @p paint or @p pt4 is invalid.
|
* @retval TVG_RESULT_INVALID_ARGUMENT @p paint or @p pt4 is invalid.
|
||||||
* @retval TVG_RESULT_INSUFFICIENT_CONDITION If it failed to compute the bounding box (mostly due to invalid path information).
|
* @retval TVG_RESULT_INSUFFICIENT_CONDITION If it failed to compute the bounding box (mostly due to invalid path information).
|
||||||
*
|
*
|
||||||
* @note The paint must be pushed into a canvas and updated before calling this function.
|
|
||||||
*
|
|
||||||
* @see tvg_paint_get_aabb()
|
* @see tvg_paint_get_aabb()
|
||||||
* @see tvg_canvas_update_paint()
|
* @see tvg_canvas_update_paint()
|
||||||
*
|
*
|
||||||
|
|
|
@ -115,6 +115,12 @@ static inline void identity(Matrix* m)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline constexpr const Matrix identity()
|
||||||
|
{
|
||||||
|
return {1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static inline void scale(Matrix* m, const Point& p)
|
static inline void scale(Matrix* m, const Point& p)
|
||||||
{
|
{
|
||||||
m->e11 *= p.x;
|
m->e11 *= p.x;
|
||||||
|
@ -178,6 +184,20 @@ Point normal(const Point& p1, const Point& p2);
|
||||||
void normalize(Point& pt);
|
void normalize(Point& pt);
|
||||||
|
|
||||||
|
|
||||||
|
static inline constexpr const Point operator*=(Point& pt, const Matrix* m)
|
||||||
|
{
|
||||||
|
if (m) pt *= *m;
|
||||||
|
return pt;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline Point operator*(const Point& pt, const Matrix* m)
|
||||||
|
{
|
||||||
|
if (!m) return pt;
|
||||||
|
return pt * *m;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static inline Point min(const Point& lhs, const Point& rhs)
|
static inline Point min(const Point& lhs, const Point& rhs)
|
||||||
{
|
{
|
||||||
return {std::min(lhs.x, rhs.x), std::min(lhs.y, rhs.y)};
|
return {std::min(lhs.x, rhs.x), std::min(lhs.y, rhs.y)};
|
||||||
|
|
|
@ -553,8 +553,7 @@ void LottieBuilder::updateStar(LottiePolyStar* star, float frameNo, Matrix* tran
|
||||||
hasRoundness = true;
|
hasRoundness = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Point in = {x, y};
|
auto in = Point{x, y} * transform;
|
||||||
if (transform) in *= *transform;
|
|
||||||
shape->moveTo(in.x, in.y);
|
shape->moveTo(in.x, in.y);
|
||||||
|
|
||||||
for (size_t i = 0; i < numPoints; i++) {
|
for (size_t i = 0; i < numPoints; i++) {
|
||||||
|
@ -595,18 +594,12 @@ void LottieBuilder::updateStar(LottiePolyStar* star, float frameNo, Matrix* tran
|
||||||
cp2x *= partialPointAmount;
|
cp2x *= partialPointAmount;
|
||||||
cp2y *= partialPointAmount;
|
cp2y *= partialPointAmount;
|
||||||
}
|
}
|
||||||
Point in2 = {previousX - cp1x, previousY - cp1y};
|
auto in2 = Point{previousX - cp1x, previousY - cp1y} * transform;
|
||||||
Point in3 = {x + cp2x, y + cp2y};
|
auto in3 = Point{x + cp2x, y + cp2y} * transform;
|
||||||
Point in4 = {x, y};
|
auto in4 = Point{x, y} * transform;
|
||||||
if (transform) {
|
|
||||||
in2 *= *transform;
|
|
||||||
in3 *= *transform;
|
|
||||||
in4 *= *transform;
|
|
||||||
}
|
|
||||||
shape->cubicTo(in2.x, in2.y, in3.x, in3.y, in4.x, in4.y);
|
shape->cubicTo(in2.x, in2.y, in3.x, in3.y, in4.x, in4.y);
|
||||||
} else {
|
} else {
|
||||||
Point in = {x, y};
|
auto in = Point{x, y} * transform;
|
||||||
if (transform) in *= *transform;
|
|
||||||
shape->lineTo(in.x, in.y);
|
shape->lineTo(in.x, in.y);
|
||||||
}
|
}
|
||||||
angle += dTheta * direction;
|
angle += dTheta * direction;
|
||||||
|
@ -651,8 +644,7 @@ void LottieBuilder::updatePolygon(LottieGroup* parent, LottiePolyStar* star, flo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Point in = {x, y};
|
auto in = Point{x, y} * transform;
|
||||||
if (transform) in *= *transform;
|
|
||||||
shape->moveTo(in.x, in.y);
|
shape->moveTo(in.x, in.y);
|
||||||
|
|
||||||
for (size_t i = 0; i < ptsCnt; i++) {
|
for (size_t i = 0; i < ptsCnt; i++) {
|
||||||
|
@ -674,14 +666,9 @@ void LottieBuilder::updatePolygon(LottieGroup* parent, LottiePolyStar* star, flo
|
||||||
auto cp2x = radius * outerRoundness * POLYGON_MAGIC_NUMBER * cp2Dx;
|
auto cp2x = radius * outerRoundness * POLYGON_MAGIC_NUMBER * cp2Dx;
|
||||||
auto cp2y = radius * outerRoundness * POLYGON_MAGIC_NUMBER * cp2Dy;
|
auto cp2y = radius * outerRoundness * POLYGON_MAGIC_NUMBER * cp2Dy;
|
||||||
|
|
||||||
Point in2 = {previousX - cp1x, previousY - cp1y};
|
auto in2 = Point{previousX - cp1x, previousY - cp1y} * transform;
|
||||||
Point in3 = {x + cp2x, y + cp2y};
|
auto in3 = Point{x + cp2x, y + cp2y} * transform;
|
||||||
Point in4 = {x, y};
|
auto in4 = Point{x, y} * transform;
|
||||||
if (transform) {
|
|
||||||
in2 *= *transform;
|
|
||||||
in3 *= *transform;
|
|
||||||
in4 *= *transform;
|
|
||||||
}
|
|
||||||
shape->cubicTo(in2.x, in2.y, in3.x, in3.y, in4.x, in4.y);
|
shape->cubicTo(in2.x, in2.y, in3.x, in3.y, in4.x, in4.y);
|
||||||
} else {
|
} else {
|
||||||
Point in = {x, y};
|
Point in = {x, y};
|
||||||
|
|
|
@ -2996,7 +2996,7 @@ static void _inheritGradient(SvgLoaderData* loader, SvgStyleGradient* to, SvgSty
|
||||||
|
|
||||||
if (!to->transform && from->transform) {
|
if (!to->transform && from->transform) {
|
||||||
to->transform = tvg::malloc<Matrix*>(sizeof(Matrix));
|
to->transform = tvg::malloc<Matrix*>(sizeof(Matrix));
|
||||||
if (to->transform) memcpy(to->transform, from->transform, sizeof(Matrix));
|
if (to->transform) *to->transform = *from->transform;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (to->type == SvgGradientType::Linear) {
|
if (to->type == SvgGradientType::Linear) {
|
||||||
|
@ -3061,7 +3061,7 @@ static SvgStyleGradient* _cloneGradient(SvgStyleGradient* from)
|
||||||
|
|
||||||
if (from->transform) {
|
if (from->transform) {
|
||||||
grad->transform = tvg::calloc<Matrix*>(1, sizeof(Matrix));
|
grad->transform = tvg::calloc<Matrix*>(1, sizeof(Matrix));
|
||||||
if (grad->transform) memcpy(grad->transform, from->transform, sizeof(Matrix));
|
if (grad->transform) *grad->transform = *from->transform;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (grad->type == SvgGradientType::Linear) {
|
if (grad->type == SvgGradientType::Linear) {
|
||||||
|
|
|
@ -50,7 +50,7 @@ static inline bool _isGroupType(SvgNodeType type)
|
||||||
static Box _boundingBox(Paint* shape)
|
static Box _boundingBox(Paint* shape)
|
||||||
{
|
{
|
||||||
float x, y, w, h;
|
float x, y, w, h;
|
||||||
PAINT(shape)->bounds(&x, &y, &w, &h, false);
|
PAINT(shape)->bounds(&x, &y, &w, &h, nullptr, false);
|
||||||
return {x, y, w, h};
|
return {x, y, w, h};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -219,7 +219,7 @@ static bool _appendClipChild(SvgLoaderData& loaderData, SvgNode* node, Shape* sh
|
||||||
if (node->type == SvgNodeType::Use) {
|
if (node->type == SvgNodeType::Use) {
|
||||||
if (node->child.count != 1) return false;
|
if (node->child.count != 1) return false;
|
||||||
auto child = *(node->child.data);
|
auto child = *(node->child.data);
|
||||||
Matrix finalTransform = {1, 0, 0, 0, 1, 0, 0, 0, 1};
|
auto finalTransform = tvg::identity();
|
||||||
if (node->transform) finalTransform = *node->transform;
|
if (node->transform) finalTransform = *node->transform;
|
||||||
if (node->node.use.x != 0.0f || node->node.use.y != 0.0f) {
|
if (node->node.use.x != 0.0f || node->node.use.y != 0.0f) {
|
||||||
finalTransform *= {1, 0, node->node.use.x, 0, 1, node->node.use.y, 0, 0, 1};
|
finalTransform *= {1, 0, node->node.use.x, 0, 1, node->node.use.y, 0, 0, 1};
|
||||||
|
@ -234,7 +234,7 @@ static bool _appendClipChild(SvgLoaderData& loaderData, SvgNode* node, Shape* sh
|
||||||
|
|
||||||
static Matrix _compositionTransform(Paint* paint, const SvgNode* node, const SvgNode* compNode, SvgNodeType type)
|
static Matrix _compositionTransform(Paint* paint, const SvgNode* node, const SvgNode* compNode, SvgNodeType type)
|
||||||
{
|
{
|
||||||
Matrix m = {1, 0, 0, 0, 1, 0, 0, 0, 1};
|
auto m = tvg::identity();
|
||||||
//The initial mask transformation ignored according to the SVG standard.
|
//The initial mask transformation ignored according to the SVG standard.
|
||||||
if (node->transform && type != SvgNodeType::Mask) {
|
if (node->transform && type != SvgNodeType::Mask) {
|
||||||
m = *node->transform;
|
m = *node->transform;
|
||||||
|
@ -244,7 +244,7 @@ static Matrix _compositionTransform(Paint* paint, const SvgNode* node, const Svg
|
||||||
}
|
}
|
||||||
if (!compNode->node.clip.userSpace) {
|
if (!compNode->node.clip.userSpace) {
|
||||||
float x, y, w, h;
|
float x, y, w, h;
|
||||||
PAINT(paint)->bounds(&x, &y, &w, &h, false);
|
PAINT(paint)->bounds(&x, &y, &w, &h, nullptr, false);
|
||||||
m *= {w, 0, x, 0, h, y, 0, 0, 1};
|
m *= {w, 0, x, 0, h, y, 0, 0, 1};
|
||||||
}
|
}
|
||||||
return m;
|
return m;
|
||||||
|
@ -685,7 +685,7 @@ static Paint* _imageBuildHelper(SvgLoaderData& loaderData, SvgNode* node, const
|
||||||
auto sy = node->node.image.h / h;
|
auto sy = node->node.image.h / h;
|
||||||
m = {sx, 0, node->node.image.x, 0, sy, node->node.image.y, 0, 0, 1};
|
m = {sx, 0, node->node.image.x, 0, sy, node->node.image.y, 0, 0, 1};
|
||||||
} else {
|
} else {
|
||||||
m = {1, 0, 0, 0, 1, 0, 0, 0, 1};
|
m = tvg::identity();
|
||||||
}
|
}
|
||||||
if (node->transform) m = *node->transform * m;
|
if (node->transform) m = *node->transform * m;
|
||||||
picture->transform(m);
|
picture->transform(m);
|
||||||
|
@ -773,7 +773,7 @@ static Scene* _useBuildHelper(SvgLoaderData& loaderData, const SvgNode* node, co
|
||||||
auto scene = _sceneBuildHelper(loaderData, node, vBox, svgPath, false, depth + 1);
|
auto scene = _sceneBuildHelper(loaderData, node, vBox, svgPath, false, depth + 1);
|
||||||
|
|
||||||
// mUseTransform = mUseTransform * mTranslate
|
// mUseTransform = mUseTransform * mTranslate
|
||||||
Matrix mUseTransform = {1, 0, 0, 0, 1, 0, 0, 0, 1};
|
auto mUseTransform = tvg::identity();
|
||||||
if (node->transform) mUseTransform = *node->transform;
|
if (node->transform) mUseTransform = *node->transform;
|
||||||
if (node->node.use.x != 0.0f || node->node.use.y != 0.0f) {
|
if (node->node.use.x != 0.0f || node->node.use.y != 0.0f) {
|
||||||
Matrix mTranslate = {1, 0, node->node.use.x, 0, 1, node->node.use.y, 0, 0, 1};
|
Matrix mTranslate = {1, 0, node->node.use.x, 0, 1, node->node.use.y, 0, 0, 1};
|
||||||
|
@ -789,7 +789,7 @@ static Scene* _useBuildHelper(SvgLoaderData& loaderData, const SvgNode* node, co
|
||||||
auto vw = (symbol.hasViewBox ? symbol.vw : width);
|
auto vw = (symbol.hasViewBox ? symbol.vw : width);
|
||||||
auto vh = (symbol.hasViewBox ? symbol.vh : height);
|
auto vh = (symbol.hasViewBox ? symbol.vh : height);
|
||||||
|
|
||||||
Matrix mViewBox = {1, 0, 0, 0, 1, 0, 0, 0, 1};
|
auto mViewBox = tvg::identity();
|
||||||
if ((!tvg::equal(width, vw) || !tvg::equal(height, vh)) && vw > 0 && vh > 0) {
|
if ((!tvg::equal(width, vw) || !tvg::equal(height, vh)) && vw > 0 && vh > 0) {
|
||||||
Box box = {symbol.vx, symbol.vy, vw, vh};
|
Box box = {symbol.vx, symbol.vy, vw, vh};
|
||||||
mViewBox = _calculateAspectRatioMatrix(symbol.align, symbol.meetOrSlice, width, height, box);
|
mViewBox = _calculateAspectRatioMatrix(symbol.align, symbol.meetOrSlice, width, height, box);
|
||||||
|
@ -864,7 +864,7 @@ static Paint* _textBuildHelper(SvgLoaderData& loaderData, const SvgNode* node, c
|
||||||
|
|
||||||
Matrix textTransform;
|
Matrix textTransform;
|
||||||
if (node->transform) textTransform = *node->transform;
|
if (node->transform) textTransform = *node->transform;
|
||||||
else textTransform = {1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f};
|
else textTransform = tvg::identity();
|
||||||
|
|
||||||
translateR(&textTransform, {node->node.text.x, node->node.text.y - textNode->fontSize});
|
translateR(&textTransform, {node->node.text.x, node->node.text.y - textNode->fontSize});
|
||||||
text->transform(textTransform);
|
text->transform(textTransform);
|
||||||
|
|
|
@ -630,7 +630,7 @@ bool SwRenderer::endComposite(RenderCompositor* cmp)
|
||||||
|
|
||||||
//Default is alpha blending
|
//Default is alpha blending
|
||||||
if (p->method == MaskMethod::None) {
|
if (p->method == MaskMethod::None) {
|
||||||
Matrix m = {1, 0, 0, 0, 1, 0, 0, 0, 1};
|
auto m = tvg::identity();
|
||||||
return rasterImage(surface, &p->image, m, p->bbox, p->opacity);
|
return rasterImage(surface, &p->image, m, p->bbox, p->opacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -71,7 +71,7 @@ struct Canvas::Impl
|
||||||
auto flag = RenderUpdateFlag::None;
|
auto flag = RenderUpdateFlag::None;
|
||||||
if (status == Status::Damaged || force) flag = RenderUpdateFlag::All;
|
if (status == Status::Damaged || force) flag = RenderUpdateFlag::All;
|
||||||
|
|
||||||
auto m = Matrix{1, 0, 0, 0, 1, 0, 0, 0, 1};
|
auto m = tvg::identity();
|
||||||
|
|
||||||
if (!renderer->preUpdate()) return Result::InsufficientCondition;
|
if (!renderer->preUpdate()) return Result::InsufficientCondition;
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#define _TVG_FILL_H_
|
#define _TVG_FILL_H_
|
||||||
|
|
||||||
#include "tvgCommon.h"
|
#include "tvgCommon.h"
|
||||||
|
#include "tvgMath.h"
|
||||||
|
|
||||||
#define LINEAR(A) PIMPL(A, LinearGradient)
|
#define LINEAR(A) PIMPL(A, LinearGradient)
|
||||||
#define RADIAL(A) PIMPL(A, RadialGradient)
|
#define RADIAL(A) PIMPL(A, RadialGradient)
|
||||||
|
@ -31,7 +32,7 @@
|
||||||
struct Fill::Impl
|
struct Fill::Impl
|
||||||
{
|
{
|
||||||
ColorStop* colorStops = nullptr;
|
ColorStop* colorStops = nullptr;
|
||||||
Matrix transform = {1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f};
|
Matrix transform = tvg::identity();
|
||||||
uint16_t cnt = 0;
|
uint16_t cnt = 0;
|
||||||
FillSpread spread = FillSpread::Pad;
|
FillSpread spread = FillSpread::Pad;
|
||||||
|
|
||||||
|
|
|
@ -267,8 +267,7 @@ RenderData Paint::Impl::update(RenderMethod* renderer, const Matrix& pm, Array<R
|
||||||
|
|
||||||
RenderData rd = nullptr;
|
RenderData rd = nullptr;
|
||||||
|
|
||||||
tr.cm = pm * tr.m;
|
PAINT_METHOD(rd, update(renderer, pm * tr.m, clips, opacity, newFlag, clipper));
|
||||||
PAINT_METHOD(rd, update(renderer, tr.cm, clips, opacity, newFlag, clipper));
|
|
||||||
|
|
||||||
/* 4. Composition Post Processing */
|
/* 4. Composition Post Processing */
|
||||||
if (compFastTrack == Result::Success) renderer->viewport(viewport);
|
if (compFastTrack == Result::Success) renderer->viewport(viewport);
|
||||||
|
@ -278,10 +277,10 @@ RenderData Paint::Impl::update(RenderMethod* renderer, const Matrix& pm, Array<R
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool Paint::Impl::bounds(float* x, float* y, float* w, float* h, bool stroking)
|
Result Paint::Impl::bounds(float* x, float* y, float* w, float* h, Matrix* pm, bool stroking)
|
||||||
{
|
{
|
||||||
Point pts[4];
|
Point pts[4];
|
||||||
if (!bounds(pts, false, stroking, false)) return false;
|
if (bounds(pts, pm, false, stroking) != Result::Success) return Result::InsufficientCondition;
|
||||||
|
|
||||||
Point min = {FLT_MAX, FLT_MAX};
|
Point min = {FLT_MAX, FLT_MAX};
|
||||||
Point max = {-FLT_MAX, -FLT_MAX};
|
Point max = {-FLT_MAX, -FLT_MAX};
|
||||||
|
@ -297,23 +296,17 @@ bool Paint::Impl::bounds(float* x, float* y, float* w, float* h, bool stroking)
|
||||||
if (y) *y = min.y;
|
if (y) *y = min.y;
|
||||||
if (w) *w = max.x - min.x;
|
if (w) *w = max.x - min.x;
|
||||||
if (h) *h = max.y - min.y;
|
if (h) *h = max.y - min.y;
|
||||||
return true;
|
return Result::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool Paint::Impl::bounds(Point* pt4, bool transformed, bool stroking, bool origin)
|
Result Paint::Impl::bounds(Point* pt4, Matrix* pm, bool obb, bool stroking)
|
||||||
{
|
{
|
||||||
bool ret;
|
auto m = this->transform();
|
||||||
PAINT_METHOD(ret, bounds(pt4, stroking));
|
if (pm) m = *pm * m;
|
||||||
|
|
||||||
if (!ret || !transformed) return ret;
|
|
||||||
|
|
||||||
const auto& m = this->transform(origin);
|
|
||||||
pt4[0] *= m;
|
|
||||||
pt4[1] *= m;
|
|
||||||
pt4[2] *= m;
|
|
||||||
pt4[3] *= m;
|
|
||||||
|
|
||||||
|
Result ret;
|
||||||
|
PAINT_METHOD(ret, bounds(pt4, m, obb, stroking));
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -369,16 +362,16 @@ Matrix& Paint::transform() noexcept
|
||||||
|
|
||||||
Result Paint::bounds(float* x, float* y, float* w, float* h) const noexcept
|
Result Paint::bounds(float* x, float* y, float* w, float* h) const noexcept
|
||||||
{
|
{
|
||||||
if (pImpl->bounds(x, y, w, h, true)) return Result::Success;
|
auto pm = pImpl->ptransform();
|
||||||
return Result::InsufficientCondition;
|
return pImpl->bounds(x, y, w, h, &pm, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Result Paint::bounds(Point* pt4) const noexcept
|
Result Paint::bounds(Point* pt4) const noexcept
|
||||||
{
|
{
|
||||||
if (!pt4) return Result::InvalidArguments;
|
if (!pt4) return Result::InvalidArguments;
|
||||||
if (pImpl->bounds(pt4, true, true, true)) return Result::Success;
|
auto pm = pImpl->ptransform();
|
||||||
return Result::InsufficientCondition;
|
return pImpl->bounds(pt4, &pm, true, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -59,7 +59,6 @@ namespace tvg
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
Matrix m; //input matrix
|
Matrix m; //input matrix
|
||||||
Matrix cm; //multipled parents matrix
|
|
||||||
float degree; //rotation degree
|
float degree; //rotation degree
|
||||||
float scale; //scale factor
|
float scale; //scale factor
|
||||||
bool overriding; //user transform?
|
bool overriding; //user transform?
|
||||||
|
@ -144,14 +143,24 @@ namespace tvg
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Matrix& transform(bool origin = false)
|
Matrix& transform()
|
||||||
{
|
{
|
||||||
//update transform
|
//update transform
|
||||||
if (renderFlag & RenderUpdateFlag::Transform) tr.update();
|
if (renderFlag & RenderUpdateFlag::Transform) tr.update();
|
||||||
if (origin) return tr.cm;
|
|
||||||
return tr.m;
|
return tr.m;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Matrix ptransform()
|
||||||
|
{
|
||||||
|
auto p = this;
|
||||||
|
auto tm = identity();
|
||||||
|
while (p->parent) {
|
||||||
|
p = PAINT(p->parent);
|
||||||
|
tm = p->transform() * tm;
|
||||||
|
}
|
||||||
|
return tm;
|
||||||
|
}
|
||||||
|
|
||||||
Result clip(Paint* clp)
|
Result clip(Paint* clp)
|
||||||
{
|
{
|
||||||
if (PAINT(clp)->parent) return Result::InsufficientCondition;
|
if (PAINT(clp)->parent) return Result::InsufficientCondition;
|
||||||
|
@ -263,8 +272,8 @@ namespace tvg
|
||||||
|
|
||||||
RenderRegion bounds(RenderMethod* renderer) const;
|
RenderRegion bounds(RenderMethod* renderer) const;
|
||||||
Iterator* iterator();
|
Iterator* iterator();
|
||||||
bool bounds(float* x, float* y, float* w, float* h, bool stroking);
|
Result bounds(float* x, float* y, float* w, float* h, Matrix* pm, bool stroking);
|
||||||
bool bounds(Point* pt4, bool transformed, bool stroking, bool origin = false);
|
Result bounds(Point* pt4, Matrix* pm, bool obb, bool stroking);
|
||||||
RenderData update(RenderMethod* renderer, const Matrix& pm, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper = false);
|
RenderData update(RenderMethod* renderer, const Matrix& pm, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper = false);
|
||||||
bool render(RenderMethod* renderer);
|
bool render(RenderMethod* renderer);
|
||||||
Paint* duplicate(Paint* ret = nullptr);
|
Paint* duplicate(Paint* ret = nullptr);
|
||||||
|
|
|
@ -114,13 +114,13 @@ struct Picture::Impl : Paint::Impl
|
||||||
return Result::Success;
|
return Result::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool bounds(Point* pt4, bool stroking)
|
Result bounds(Point* pt4, Matrix& m, TVG_UNUSED bool obb, TVG_UNUSED bool stroking)
|
||||||
{
|
{
|
||||||
pt4[0] = {0.0f, 0.0f};
|
pt4[0] = Point{0.0f, 0.0f} * m;
|
||||||
pt4[1] = {w, 0.0f};
|
pt4[1] = Point{w, 0.0f} * m;
|
||||||
pt4[2] = {w, h};
|
pt4[2] = Point{w, h} * m;
|
||||||
pt4[3] = {0.0f, h};
|
pt4[3] = Point{0.0f, h} * m;
|
||||||
return true;
|
return Result::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result load(const char* filename)
|
Result load(const char* filename)
|
||||||
|
|
|
@ -44,7 +44,7 @@ uint32_t RenderMethod::unref()
|
||||||
/* RenderPath Class Implementation */
|
/* RenderPath Class Implementation */
|
||||||
/************************************************************************/
|
/************************************************************************/
|
||||||
|
|
||||||
bool RenderPath::bounds(float* x, float* y, float* w, float* h)
|
bool RenderPath::bounds(Matrix* m, float* x, float* y, float* w, float* h)
|
||||||
{
|
{
|
||||||
//unexpected
|
//unexpected
|
||||||
if (cmds.empty() || cmds.first() == PathCommand::CubicTo) return false;
|
if (cmds.empty() || cmds.first() == PathCommand::CubicTo) return false;
|
||||||
|
@ -55,11 +55,11 @@ bool RenderPath::bounds(float* x, float* y, float* w, float* h)
|
||||||
auto pt = pts.begin();
|
auto pt = pts.begin();
|
||||||
auto cmd = cmds.begin();
|
auto cmd = cmds.begin();
|
||||||
|
|
||||||
auto assign = [&](Point* pt, Point& min, Point& max) -> void {
|
auto assign = [&](const Point& pt, Point& min, Point& max) -> void {
|
||||||
if (pt->x < min.x) min.x = pt->x;
|
if (pt.x < min.x) min.x = pt.x;
|
||||||
if (pt->y < min.y) min.y = pt->y;
|
if (pt.y < min.y) min.y = pt.y;
|
||||||
if (pt->x > max.x) max.x = pt->x;
|
if (pt.x > max.x) max.x = pt.x;
|
||||||
if (pt->y > max.y) max.y = pt->y;
|
if (pt.y > max.y) max.y = pt.y;
|
||||||
};
|
};
|
||||||
|
|
||||||
while (cmd < cmds.end()) {
|
while (cmd < cmds.end()) {
|
||||||
|
@ -69,19 +69,19 @@ bool RenderPath::bounds(float* x, float* y, float* w, float* h)
|
||||||
if (cmd + 1 < cmds.end()) {
|
if (cmd + 1 < cmds.end()) {
|
||||||
auto next = *(cmd + 1);
|
auto next = *(cmd + 1);
|
||||||
if (next == PathCommand::LineTo || next == PathCommand::CubicTo) {
|
if (next == PathCommand::LineTo || next == PathCommand::CubicTo) {
|
||||||
assign(pt, min, max);
|
assign(*pt * m, min, max);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
++pt;
|
++pt;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PathCommand::LineTo: {
|
case PathCommand::LineTo: {
|
||||||
assign(pt, min, max);
|
assign(*pt * m, min, max);
|
||||||
++pt;
|
++pt;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PathCommand::CubicTo: {
|
case PathCommand::CubicTo: {
|
||||||
Bezier bz = {pt[-1], pt[0], pt[1], pt[2]};
|
Bezier bz = {pt[-1] * m, pt[0] * m, pt[1] * m, pt[2] * m};
|
||||||
bz.bounds(min, max);
|
bz.bounds(min, max);
|
||||||
pt += 3;
|
pt += 3;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -104,7 +104,7 @@ struct RenderPath
|
||||||
cmds.clear();
|
cmds.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool bounds(float* x, float* y, float* w, float* h);
|
bool bounds(Matrix* m, float* x, float* y, float* w, float* h);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct RenderTrimPath
|
struct RenderTrimPath
|
||||||
|
|
|
@ -194,16 +194,16 @@ struct Scene::Impl : Paint::Impl
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool bounds(Point* pt4, bool stroking)
|
Result bounds(Point* pt4, Matrix& m, TVG_UNUSED bool obb, bool stroking)
|
||||||
{
|
{
|
||||||
if (paints.empty()) return false;
|
if (paints.empty()) return Result::InsufficientCondition;
|
||||||
|
|
||||||
Point min = {FLT_MAX, FLT_MAX};
|
Point min = {FLT_MAX, FLT_MAX};
|
||||||
Point max = {-FLT_MAX, -FLT_MAX};
|
Point max = {-FLT_MAX, -FLT_MAX};
|
||||||
|
|
||||||
for (auto paint : paints) {
|
for (auto paint : paints) {
|
||||||
Point tmp[4];
|
Point tmp[4];
|
||||||
if (!PAINT(paint)->bounds(tmp, true, stroking)) continue;
|
if (PAINT(paint)->bounds(tmp, nullptr, false, stroking) != Result::Success) continue;
|
||||||
//Merge regions
|
//Merge regions
|
||||||
for (int i = 0; i < 4; ++i) {
|
for (int i = 0; i < 4; ++i) {
|
||||||
if (tmp[i].x < min.x) min.x = tmp[i].x;
|
if (tmp[i].x < min.x) min.x = tmp[i].x;
|
||||||
|
@ -212,12 +212,12 @@ struct Scene::Impl : Paint::Impl
|
||||||
if (tmp[i].y > max.y) max.y = tmp[i].y;
|
if (tmp[i].y > max.y) max.y = tmp[i].y;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pt4[0] = min;
|
pt4[0] = min * m;
|
||||||
pt4[1] = {max.x, min.y};
|
pt4[1] = Point{max.x, min.y} * m;
|
||||||
pt4[2] = max;
|
pt4[2] = max * m;
|
||||||
pt4[3] = {min.x, max.y};
|
pt4[3] = Point{min.x, max.y} * m;
|
||||||
|
|
||||||
return true;
|
return Result::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
Paint* duplicate(Paint* ret)
|
Paint* duplicate(Paint* ret)
|
||||||
|
|
|
@ -116,10 +116,10 @@ struct Shape::Impl : Paint::Impl
|
||||||
return renderer->region(rd);
|
return renderer->region(rd);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool bounds(Point* pt4, bool stroking)
|
Result bounds(Point* pt4, Matrix& m, bool obb, bool stroking)
|
||||||
{
|
{
|
||||||
float x, y, w, h;
|
float x, y, w, h;
|
||||||
if (!rs.path.bounds(&x, &y, &w, &h)) return false;
|
if (!rs.path.bounds(obb ? nullptr : &m, &x, &y, &w, &h)) return Result::InsufficientCondition;
|
||||||
|
|
||||||
//Stroke feathering
|
//Stroke feathering
|
||||||
if (stroking && rs.stroke) {
|
if (stroking && rs.stroke) {
|
||||||
|
@ -134,7 +134,14 @@ struct Shape::Impl : Paint::Impl
|
||||||
pt4[2] = {x + w, y + h};
|
pt4[2] = {x + w, y + h};
|
||||||
pt4[3] = {x, y + h};
|
pt4[3] = {x, y + h};
|
||||||
|
|
||||||
return true;
|
if (obb) {
|
||||||
|
pt4[0] *= m;
|
||||||
|
pt4[1] *= m;
|
||||||
|
pt4[2] *= m;
|
||||||
|
pt4[3] *= m;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
void reserveCmd(uint32_t cmdCnt)
|
void reserveCmd(uint32_t cmdCnt)
|
||||||
|
|
|
@ -135,11 +135,10 @@ struct Text::Impl : Paint::Impl
|
||||||
return PAINT(shape)->update(renderer, transform, clips, opacity, pFlag, false);
|
return PAINT(shape)->update(renderer, transform, clips, opacity, pFlag, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool bounds(Point* pt4, TVG_UNUSED bool stroking)
|
Result bounds(Point* pt4, Matrix& m, bool obb, TVG_UNUSED bool stroking)
|
||||||
{
|
{
|
||||||
if (!load()) return false;
|
if (!load()) return Result::InsufficientCondition;
|
||||||
PAINT(shape)->bounds(pt4, true, true, false);
|
return PAINT(shape)->bounds(pt4, &m, obb, true);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Paint* duplicate(Paint* ret)
|
Paint* duplicate(Paint* ret)
|
||||||
|
|
|
@ -133,8 +133,8 @@ TEST_CASE("Bounding Box", "[tvgPaint]")
|
||||||
REQUIRE(shape->appendRect(0.0f, 10.0f, 20.0f, 100.0f, 50.0f, 50.0f) == Result::Success);
|
REQUIRE(shape->appendRect(0.0f, 10.0f, 20.0f, 100.0f, 50.0f, 50.0f) == Result::Success);
|
||||||
REQUIRE(shape->translate(100.0f, 111.0f) == Result::Success);
|
REQUIRE(shape->translate(100.0f, 111.0f) == Result::Success);
|
||||||
REQUIRE(shape->bounds(&x, &y, &w, &h) == Result::Success);
|
REQUIRE(shape->bounds(&x, &y, &w, &h) == Result::Success);
|
||||||
REQUIRE(x == 0.0f);
|
REQUIRE(x == 100.0f);
|
||||||
REQUIRE(y == 10.0f);
|
REQUIRE(y == 121.0f);
|
||||||
REQUIRE(w == 20.0f);
|
REQUIRE(w == 20.0f);
|
||||||
REQUIRE(h == 100.0f);
|
REQUIRE(h == 100.0f);
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue