mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-08 13:43:43 +00:00
renderer: improved the paint bounding box accuracy
previously, the bounding box calculation was simply determined by comparing all the points, which led to incorrect sizing due to Bezier control points. Now, it accurately computes the curve boundary, properly addressing this issue.
This commit is contained in:
parent
82a9a05405
commit
30a5f2891b
5 changed files with 100 additions and 21 deletions
|
@ -100,6 +100,13 @@ float _bezAt(const Bezier& bz, float at, float length, LengthFunc lineLengthFunc
|
||||||
|
|
||||||
namespace tvg {
|
namespace tvg {
|
||||||
|
|
||||||
|
|
||||||
|
uint8_t lerp(const uint8_t &start, const uint8_t &end, float t)
|
||||||
|
{
|
||||||
|
return static_cast<uint8_t>(tvg::clamp(static_cast<int>(start + (end - start) * t), 0, 255));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
float length(const PathCommand* cmds, uint32_t cmdsCnt, const Point* pts, uint32_t ptsCnt)
|
float length(const PathCommand* cmds, uint32_t cmdsCnt, const Point* pts, uint32_t ptsCnt)
|
||||||
{
|
{
|
||||||
if (ptsCnt < 2) return 0.0f;
|
if (ptsCnt < 2) return 0.0f;
|
||||||
|
@ -408,9 +415,38 @@ float Bezier::angle(float t) const
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
uint8_t lerp(const uint8_t &start, const uint8_t &end, float t)
|
void Bezier::bounds(Point& min, Point& max) const
|
||||||
{
|
{
|
||||||
return static_cast<uint8_t>(tvg::clamp(static_cast<int>(start + (end - start) * t), 0, 255));
|
if (min.x > start.x) min.x = start.x;
|
||||||
|
if (min.y > start.y) min.y = start.y;
|
||||||
|
if (min.x > end.x) min.x = end.x;
|
||||||
|
if (min.y > end.y) min.y = end.y;
|
||||||
|
|
||||||
|
if (max.x < start.x) max.x = start.x;
|
||||||
|
if (max.y < start.y) max.y = start.y;
|
||||||
|
if (max.x < end.x) max.x = end.x;
|
||||||
|
if (max.y < end.y) max.y = end.y;
|
||||||
|
|
||||||
|
//find x/y-direction extrema (solving derivative of Bezier curve)
|
||||||
|
auto findMinMax = [&](float start, float ctrl1, float ctrl2, float end, float& min, float& max) -> void {
|
||||||
|
auto a = -1.0f * start + 3.0f * ctrl1 - 3.0f * ctrl2 + end;
|
||||||
|
auto b = start - 2.0f * ctrl1 + ctrl2;
|
||||||
|
auto c = -1.0f * start + ctrl1;
|
||||||
|
auto h = b * b - a * c;
|
||||||
|
if (h <= 0.0f) return;
|
||||||
|
h = sqrtf(h);
|
||||||
|
float t[2] = {(-b - h) / a, (-b + h) / a};
|
||||||
|
for (int i = 0; i < 2; ++i) {
|
||||||
|
if (t[i] <= 0.0f || t[i] >= 1.0f) continue;
|
||||||
|
auto s = 1.0f - t[i];
|
||||||
|
auto q = s * s * s * start + 3.0f * s * s * t[i] * ctrl1 + 3.0f * s * t[i] * t[i] * ctrl2 + t[i] * t[i] * t[i] * end;
|
||||||
|
if (q < min) min = q;
|
||||||
|
if (q > max) max = q;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
findMinMax(start.x, ctrl1.x, ctrl2.x, end.x, min.x, max.x);
|
||||||
|
findMinMax(start.y, ctrl1.y, ctrl2.y, end.y, min.y, max.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -344,6 +344,7 @@ struct Bezier
|
||||||
float atApprox(float at, float length) const;
|
float atApprox(float at, float length) const;
|
||||||
Point at(float t) const;
|
Point at(float t) const;
|
||||||
float angle(float t) const;
|
float angle(float t) const;
|
||||||
|
void bounds(Point& min, Point& max) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,64 @@ uint32_t RenderMethod::unref()
|
||||||
return (--refCnt);
|
return (--refCnt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/************************************************************************/
|
||||||
|
/* RenderPath Class Implementation */
|
||||||
|
/************************************************************************/
|
||||||
|
|
||||||
|
bool RenderPath::bounds(float* x, float* y, float* w, float* h)
|
||||||
|
{
|
||||||
|
//unexpected
|
||||||
|
if (cmds.empty() || cmds.first() == PathCommand::CubicTo) return false;
|
||||||
|
|
||||||
|
auto min = Point{FLT_MAX, FLT_MAX};
|
||||||
|
auto max = Point{-FLT_MAX, -FLT_MAX};
|
||||||
|
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
|
||||||
|
while (cmd < cmds.end()) {
|
||||||
|
switch (*cmd) {
|
||||||
|
case PathCommand::MoveTo: {
|
||||||
|
//skip the invalid assignments
|
||||||
|
if (cmd + 1 < cmds.end()) {
|
||||||
|
auto next = *(cmd + 1);
|
||||||
|
if (next == PathCommand::LineTo || next == PathCommand::CubicTo) {
|
||||||
|
assign(pt, min, max);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
++pt;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case PathCommand::LineTo: {
|
||||||
|
assign(pt, min, max);
|
||||||
|
++pt;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case PathCommand::CubicTo: {
|
||||||
|
Bezier bz = {pt[-1], pt[0], pt[1], pt[2]};
|
||||||
|
bz.bounds(min, max);
|
||||||
|
pt += 3;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
++cmd;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (x) *x = min.x;
|
||||||
|
if (y) *y = min.y;
|
||||||
|
if (w) *w = max.x - min.x;
|
||||||
|
if (h) *h = max.y - min.y;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/************************************************************************/
|
/************************************************************************/
|
||||||
/* RenderRegion Class Implementation */
|
/* RenderRegion Class Implementation */
|
||||||
|
|
|
@ -104,6 +104,7 @@ struct RenderPath
|
||||||
cmds.clear();
|
cmds.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool bounds(float* x, float* y, float* w, float* h);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct RenderTrimPath
|
struct RenderTrimPath
|
||||||
|
|
|
@ -118,24 +118,7 @@ struct Shape::Impl : Paint::Impl
|
||||||
|
|
||||||
bool bounds(float* x, float* y, float* w, float* h, bool stroking)
|
bool bounds(float* x, float* y, float* w, float* h, bool stroking)
|
||||||
{
|
{
|
||||||
//Path bounding size
|
if (!rs.path.bounds(x, y, w, h)) return false;
|
||||||
if (rs.path.pts.count > 0 ) {
|
|
||||||
auto pts = rs.path.pts.begin();
|
|
||||||
Point min = { pts->x, pts->y };
|
|
||||||
Point max = { pts->x, pts->y };
|
|
||||||
|
|
||||||
for (auto pts2 = pts + 1; pts2 < rs.path.pts.end(); ++pts2) {
|
|
||||||
if (pts2->x < min.x) min.x = pts2->x;
|
|
||||||
if (pts2->y < min.y) min.y = pts2->y;
|
|
||||||
if (pts2->x > max.x) max.x = pts2->x;
|
|
||||||
if (pts2->y > max.y) max.y = pts2->y;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (x) *x = min.x;
|
|
||||||
if (y) *y = min.y;
|
|
||||||
if (w) *w = max.x - min.x;
|
|
||||||
if (h) *h = max.y - min.y;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Stroke feathering
|
//Stroke feathering
|
||||||
if (stroking && rs.stroke) {
|
if (stroking && rs.stroke) {
|
||||||
|
@ -144,7 +127,7 @@ struct Shape::Impl : Paint::Impl
|
||||||
if (w) *w += rs.stroke->width;
|
if (w) *w += rs.stroke->width;
|
||||||
if (h) *h += rs.stroke->width;
|
if (h) *h += rs.stroke->width;
|
||||||
}
|
}
|
||||||
return rs.path.pts.count > 0 ? true : false;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void reserveCmd(uint32_t cmdCnt)
|
void reserveCmd(uint32_t cmdCnt)
|
||||||
|
|
Loading…
Add table
Reference in a new issue