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::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 Canvas::update()
|
||||
*
|
||||
|
@ -449,9 +447,9 @@ public:
|
|||
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] 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).
|
||||
*
|
||||
* @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 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[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_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_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_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_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)
|
||||
{
|
||||
m->e11 *= p.x;
|
||||
|
@ -178,6 +184,20 @@ Point normal(const Point& p1, const Point& p2);
|
|||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
Point in = {x, y};
|
||||
if (transform) in *= *transform;
|
||||
auto in = Point{x, y} * transform;
|
||||
shape->moveTo(in.x, in.y);
|
||||
|
||||
for (size_t i = 0; i < numPoints; i++) {
|
||||
|
@ -595,18 +594,12 @@ void LottieBuilder::updateStar(LottiePolyStar* star, float frameNo, Matrix* tran
|
|||
cp2x *= partialPointAmount;
|
||||
cp2y *= partialPointAmount;
|
||||
}
|
||||
Point in2 = {previousX - cp1x, previousY - cp1y};
|
||||
Point in3 = {x + cp2x, y + cp2y};
|
||||
Point in4 = {x, y};
|
||||
if (transform) {
|
||||
in2 *= *transform;
|
||||
in3 *= *transform;
|
||||
in4 *= *transform;
|
||||
}
|
||||
auto in2 = Point{previousX - cp1x, previousY - cp1y} * transform;
|
||||
auto in3 = Point{x + cp2x, y + cp2y} * transform;
|
||||
auto in4 = Point{x, y} * transform;
|
||||
shape->cubicTo(in2.x, in2.y, in3.x, in3.y, in4.x, in4.y);
|
||||
} else {
|
||||
Point in = {x, y};
|
||||
if (transform) in *= *transform;
|
||||
auto in = Point{x, y} * transform;
|
||||
shape->lineTo(in.x, in.y);
|
||||
}
|
||||
angle += dTheta * direction;
|
||||
|
@ -651,8 +644,7 @@ void LottieBuilder::updatePolygon(LottieGroup* parent, LottiePolyStar* star, flo
|
|||
}
|
||||
}
|
||||
|
||||
Point in = {x, y};
|
||||
if (transform) in *= *transform;
|
||||
auto in = Point{x, y} * transform;
|
||||
shape->moveTo(in.x, in.y);
|
||||
|
||||
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 cp2y = radius * outerRoundness * POLYGON_MAGIC_NUMBER * cp2Dy;
|
||||
|
||||
Point in2 = {previousX - cp1x, previousY - cp1y};
|
||||
Point in3 = {x + cp2x, y + cp2y};
|
||||
Point in4 = {x, y};
|
||||
if (transform) {
|
||||
in2 *= *transform;
|
||||
in3 *= *transform;
|
||||
in4 *= *transform;
|
||||
}
|
||||
auto in2 = Point{previousX - cp1x, previousY - cp1y} * transform;
|
||||
auto in3 = Point{x + cp2x, y + cp2y} * transform;
|
||||
auto in4 = Point{x, y} * transform;
|
||||
shape->cubicTo(in2.x, in2.y, in3.x, in3.y, in4.x, in4.y);
|
||||
} else {
|
||||
Point in = {x, y};
|
||||
|
|
|
@ -2996,7 +2996,7 @@ static void _inheritGradient(SvgLoaderData* loader, SvgStyleGradient* to, SvgSty
|
|||
|
||||
if (!to->transform && from->transform) {
|
||||
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) {
|
||||
|
@ -3061,7 +3061,7 @@ static SvgStyleGradient* _cloneGradient(SvgStyleGradient* from)
|
|||
|
||||
if (from->transform) {
|
||||
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) {
|
||||
|
|
|
@ -50,7 +50,7 @@ static inline bool _isGroupType(SvgNodeType type)
|
|||
static Box _boundingBox(Paint* shape)
|
||||
{
|
||||
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};
|
||||
}
|
||||
|
||||
|
@ -219,7 +219,7 @@ static bool _appendClipChild(SvgLoaderData& loaderData, SvgNode* node, Shape* sh
|
|||
if (node->type == SvgNodeType::Use) {
|
||||
if (node->child.count != 1) return false;
|
||||
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->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};
|
||||
|
@ -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)
|
||||
{
|
||||
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.
|
||||
if (node->transform && type != SvgNodeType::Mask) {
|
||||
m = *node->transform;
|
||||
|
@ -244,7 +244,7 @@ static Matrix _compositionTransform(Paint* paint, const SvgNode* node, const Svg
|
|||
}
|
||||
if (!compNode->node.clip.userSpace) {
|
||||
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};
|
||||
}
|
||||
return m;
|
||||
|
@ -685,7 +685,7 @@ static Paint* _imageBuildHelper(SvgLoaderData& loaderData, SvgNode* node, const
|
|||
auto sy = node->node.image.h / h;
|
||||
m = {sx, 0, node->node.image.x, 0, sy, node->node.image.y, 0, 0, 1};
|
||||
} else {
|
||||
m = {1, 0, 0, 0, 1, 0, 0, 0, 1};
|
||||
m = tvg::identity();
|
||||
}
|
||||
if (node->transform) m = *node->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);
|
||||
|
||||
// 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->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};
|
||||
|
@ -789,7 +789,7 @@ static Scene* _useBuildHelper(SvgLoaderData& loaderData, const SvgNode* node, co
|
|||
auto vw = (symbol.hasViewBox ? symbol.vw : width);
|
||||
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) {
|
||||
Box box = {symbol.vx, symbol.vy, vw, vh};
|
||||
mViewBox = _calculateAspectRatioMatrix(symbol.align, symbol.meetOrSlice, width, height, box);
|
||||
|
@ -864,7 +864,7 @@ static Paint* _textBuildHelper(SvgLoaderData& loaderData, const SvgNode* node, c
|
|||
|
||||
Matrix textTransform;
|
||||
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});
|
||||
text->transform(textTransform);
|
||||
|
|
|
@ -630,7 +630,7 @@ bool SwRenderer::endComposite(RenderCompositor* cmp)
|
|||
|
||||
//Default is alpha blending
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -71,7 +71,7 @@ struct Canvas::Impl
|
|||
auto flag = RenderUpdateFlag::None;
|
||||
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;
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#define _TVG_FILL_H_
|
||||
|
||||
#include "tvgCommon.h"
|
||||
#include "tvgMath.h"
|
||||
|
||||
#define LINEAR(A) PIMPL(A, LinearGradient)
|
||||
#define RADIAL(A) PIMPL(A, RadialGradient)
|
||||
|
@ -31,7 +32,7 @@
|
|||
struct Fill::Impl
|
||||
{
|
||||
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;
|
||||
FillSpread spread = FillSpread::Pad;
|
||||
|
||||
|
|
|
@ -267,8 +267,7 @@ RenderData Paint::Impl::update(RenderMethod* renderer, const Matrix& pm, Array<R
|
|||
|
||||
RenderData rd = nullptr;
|
||||
|
||||
tr.cm = pm * tr.m;
|
||||
PAINT_METHOD(rd, update(renderer, tr.cm, clips, opacity, newFlag, clipper));
|
||||
PAINT_METHOD(rd, update(renderer, pm * tr.m, clips, opacity, newFlag, clipper));
|
||||
|
||||
/* 4. Composition Post Processing */
|
||||
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];
|
||||
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 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 (w) *w = max.x - min.x;
|
||||
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;
|
||||
PAINT_METHOD(ret, bounds(pt4, stroking));
|
||||
|
||||
if (!ret || !transformed) return ret;
|
||||
|
||||
const auto& m = this->transform(origin);
|
||||
pt4[0] *= m;
|
||||
pt4[1] *= m;
|
||||
pt4[2] *= m;
|
||||
pt4[3] *= m;
|
||||
auto m = this->transform();
|
||||
if (pm) m = *pm * m;
|
||||
|
||||
Result ret;
|
||||
PAINT_METHOD(ret, bounds(pt4, m, obb, stroking));
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -369,16 +362,16 @@ Matrix& Paint::transform() 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;
|
||||
return Result::InsufficientCondition;
|
||||
auto pm = pImpl->ptransform();
|
||||
return pImpl->bounds(x, y, w, h, &pm, true);
|
||||
}
|
||||
|
||||
|
||||
Result Paint::bounds(Point* pt4) const noexcept
|
||||
{
|
||||
if (!pt4) return Result::InvalidArguments;
|
||||
if (pImpl->bounds(pt4, true, true, true)) return Result::Success;
|
||||
return Result::InsufficientCondition;
|
||||
auto pm = pImpl->ptransform();
|
||||
return pImpl->bounds(pt4, &pm, true, true);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -59,7 +59,6 @@ namespace tvg
|
|||
|
||||
struct {
|
||||
Matrix m; //input matrix
|
||||
Matrix cm; //multipled parents matrix
|
||||
float degree; //rotation degree
|
||||
float scale; //scale factor
|
||||
bool overriding; //user transform?
|
||||
|
@ -144,14 +143,24 @@ namespace tvg
|
|||
return true;
|
||||
}
|
||||
|
||||
Matrix& transform(bool origin = false)
|
||||
Matrix& transform()
|
||||
{
|
||||
//update transform
|
||||
if (renderFlag & RenderUpdateFlag::Transform) tr.update();
|
||||
if (origin) return tr.cm;
|
||||
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)
|
||||
{
|
||||
if (PAINT(clp)->parent) return Result::InsufficientCondition;
|
||||
|
@ -263,8 +272,8 @@ namespace tvg
|
|||
|
||||
RenderRegion bounds(RenderMethod* renderer) const;
|
||||
Iterator* iterator();
|
||||
bool bounds(float* x, float* y, float* w, float* h, bool stroking);
|
||||
bool bounds(Point* pt4, bool transformed, bool stroking, bool origin = false);
|
||||
Result bounds(float* x, float* y, float* w, float* h, Matrix* pm, bool stroking);
|
||||
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);
|
||||
bool render(RenderMethod* renderer);
|
||||
Paint* duplicate(Paint* ret = nullptr);
|
||||
|
|
|
@ -114,13 +114,13 @@ struct Picture::Impl : Paint::Impl
|
|||
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[1] = {w, 0.0f};
|
||||
pt4[2] = {w, h};
|
||||
pt4[3] = {0.0f, h};
|
||||
return true;
|
||||
pt4[0] = Point{0.0f, 0.0f} * m;
|
||||
pt4[1] = Point{w, 0.0f} * m;
|
||||
pt4[2] = Point{w, h} * m;
|
||||
pt4[3] = Point{0.0f, h} * m;
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
Result load(const char* filename)
|
||||
|
|
|
@ -44,7 +44,7 @@ uint32_t RenderMethod::unref()
|
|||
/* 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
|
||||
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 cmd = cmds.begin();
|
||||
|
||||
auto assign = [&](Point* pt, Point& min, Point& max) -> void {
|
||||
if (pt->x < min.x) min.x = pt->x;
|
||||
if (pt->y < min.y) min.y = pt->y;
|
||||
if (pt->x > max.x) max.x = pt->x;
|
||||
if (pt->y > max.y) max.y = pt->y;
|
||||
auto assign = [&](const Point& pt, Point& min, Point& max) -> void {
|
||||
if (pt.x < min.x) min.x = pt.x;
|
||||
if (pt.y < min.y) min.y = pt.y;
|
||||
if (pt.x > max.x) max.x = pt.x;
|
||||
if (pt.y > max.y) max.y = pt.y;
|
||||
};
|
||||
|
||||
while (cmd < cmds.end()) {
|
||||
|
@ -69,19 +69,19 @@ bool RenderPath::bounds(float* x, float* y, float* w, float* h)
|
|||
if (cmd + 1 < cmds.end()) {
|
||||
auto next = *(cmd + 1);
|
||||
if (next == PathCommand::LineTo || next == PathCommand::CubicTo) {
|
||||
assign(pt, min, max);
|
||||
assign(*pt * m, min, max);
|
||||
}
|
||||
}
|
||||
++pt;
|
||||
break;
|
||||
}
|
||||
case PathCommand::LineTo: {
|
||||
assign(pt, min, max);
|
||||
assign(*pt * m, min, max);
|
||||
++pt;
|
||||
break;
|
||||
}
|
||||
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);
|
||||
pt += 3;
|
||||
break;
|
||||
|
|
|
@ -104,7 +104,7 @@ struct RenderPath
|
|||
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
|
||||
|
|
|
@ -194,16 +194,16 @@ struct Scene::Impl : Paint::Impl
|
|||
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 max = {-FLT_MAX, -FLT_MAX};
|
||||
|
||||
for (auto paint : paints) {
|
||||
Point tmp[4];
|
||||
if (!PAINT(paint)->bounds(tmp, true, stroking)) continue;
|
||||
if (PAINT(paint)->bounds(tmp, nullptr, false, stroking) != Result::Success) continue;
|
||||
//Merge regions
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
pt4[0] = min;
|
||||
pt4[1] = {max.x, min.y};
|
||||
pt4[2] = max;
|
||||
pt4[3] = {min.x, max.y};
|
||||
pt4[0] = min * m;
|
||||
pt4[1] = Point{max.x, min.y} * m;
|
||||
pt4[2] = max * m;
|
||||
pt4[3] = Point{min.x, max.y} * m;
|
||||
|
||||
return true;
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
Paint* duplicate(Paint* ret)
|
||||
|
|
|
@ -116,10 +116,10 @@ struct Shape::Impl : Paint::Impl
|
|||
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;
|
||||
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
|
||||
if (stroking && rs.stroke) {
|
||||
|
@ -134,7 +134,14 @@ struct Shape::Impl : Paint::Impl
|
|||
pt4[2] = {x + w, 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)
|
||||
|
|
|
@ -135,11 +135,10 @@ struct Text::Impl : Paint::Impl
|
|||
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;
|
||||
PAINT(shape)->bounds(pt4, true, true, false);
|
||||
return true;
|
||||
if (!load()) return Result::InsufficientCondition;
|
||||
return PAINT(shape)->bounds(pt4, &m, obb, true);
|
||||
}
|
||||
|
||||
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->translate(100.0f, 111.0f) == Result::Success);
|
||||
REQUIRE(shape->bounds(&x, &y, &w, &h) == Result::Success);
|
||||
REQUIRE(x == 0.0f);
|
||||
REQUIRE(y == 10.0f);
|
||||
REQUIRE(x == 100.0f);
|
||||
REQUIRE(y == 121.0f);
|
||||
REQUIRE(w == 20.0f);
|
||||
REQUIRE(h == 100.0f);
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue