vector: avoid float32 min/max at (*Path).Bounds

Apparently min/max built-in functions for float32 seems slow, especially
on browsers. Let's use integers instead.

Updates #3275
This commit is contained in:
Hajime Hoshi
2025-07-19 22:05:52 +09:00
parent 86fd3e8d66
commit 1284f09201

View File

@@ -1084,28 +1084,28 @@ func ceil(x float32) int {
// Bounds returns the minimum bounding rectangle of the path. // Bounds returns the minimum bounding rectangle of the path.
func (p *Path) Bounds() image.Rectangle { func (p *Path) Bounds() image.Rectangle {
// Note that (image.Rectangle).Union doesn't work well with empty rectangles. // Note that (image.Rectangle).Union doesn't work well with empty rectangles.
totalMinX := float32(math.Inf(1)) totalMinX := math.MaxInt
totalMinY := float32(math.Inf(1)) totalMinY := math.MaxInt
totalMaxX := float32(math.Inf(-1)) totalMaxX := math.MinInt
totalMaxY := float32(math.Inf(-1)) totalMaxY := math.MinInt
for _, subPath := range p.subPaths { for _, subPath := range p.subPaths {
if !subPath.isValid() { if !subPath.isValid() {
continue continue
} }
minX := float32(math.Inf(1)) minX := math.MaxInt
minY := float32(math.Inf(1)) minY := math.MaxInt
maxX := float32(math.Inf(-1)) maxX := math.MinInt
maxY := float32(math.Inf(-1)) maxY := math.MinInt
cur := subPath.start cur := subPath.start
for _, op := range subPath.ops { for _, op := range subPath.ops {
switch op.typ { switch op.typ {
case opTypeLineTo: case opTypeLineTo:
minX = min(minX, cur.x, op.p1.x) minX = min(minX, floor(cur.x), floor(op.p1.x))
minY = min(minY, cur.y, op.p1.y) minY = min(minY, floor(cur.y), floor(op.p1.y))
maxX = max(maxX, cur.x, op.p1.x) maxX = max(maxX, ceil(cur.x), ceil(op.p1.x))
maxY = max(maxY, cur.y, op.p1.y) maxY = max(maxY, ceil(cur.y), ceil(op.p1.y))
cur = op.p1 cur = op.p1
case opTypeQuadTo: case opTypeQuadTo:
// The candidates are the two control points on the edges (cur and op.p2), and an extremum point. // The candidates are the two control points on the edges (cur and op.p2), and an extremum point.
@@ -1116,29 +1116,29 @@ func (p *Path) Bounds() image.Rectangle {
if denom := cur.x - 2*op.p1.x + op.p2.x; abs(denom) >= 1.0/16.0 { if denom := cur.x - 2*op.p1.x + op.p2.x; abs(denom) >= 1.0/16.0 {
if t := (cur.x - op.p1.x) / denom; t > 0 && t < 1 { if t := (cur.x - op.p1.x) / denom; t > 0 && t < 1 {
ex := (1-t)*(1-t)*cur.x + 2*t*(1-t)*op.p1.x + t*t*op.p2.x ex := (1-t)*(1-t)*cur.x + 2*t*(1-t)*op.p1.x + t*t*op.p2.x
minX = min(minX, cur.x, ex, op.p2.x) minX = min(minX, floor(cur.x), floor(ex), floor(op.p2.x))
maxX = max(maxX, cur.x, ex, op.p2.x) maxX = max(maxX, ceil(cur.x), ceil(ex), ceil(op.p2.x))
} else { } else {
minX = min(minX, cur.x, op.p2.x) minX = min(minX, floor(cur.x), floor(op.p2.x))
maxX = max(maxX, cur.x, op.p2.x) maxX = max(maxX, ceil(cur.x), ceil(op.p2.x))
} }
} else { } else {
// The curve is almost linear. Include all the points for safety. // The curve is almost linear. Include all the points for safety.
minX = min(minX, cur.x, op.p1.x, op.p2.x) minX = min(minX, floor(cur.x), floor(op.p1.x), floor(op.p2.x))
maxX = max(maxX, cur.x, op.p1.x, op.p2.x) maxX = max(maxX, ceil(cur.x), ceil(op.p1.x), ceil(op.p2.x))
} }
if denom := cur.y - 2*op.p1.y + op.p2.y; abs(denom) >= 1.0/16.0 { if denom := cur.y - 2*op.p1.y + op.p2.y; abs(denom) >= 1.0/16.0 {
if t := (cur.y - op.p1.y) / denom; t > 0 && t < 1 { if t := (cur.y - op.p1.y) / denom; t > 0 && t < 1 {
ex := (1-t)*(1-t)*cur.y + 2*t*(1-t)*op.p1.y + t*t*op.p2.y ex := (1-t)*(1-t)*cur.y + 2*t*(1-t)*op.p1.y + t*t*op.p2.y
minY = min(minY, cur.y, ex, op.p2.y) minY = min(minY, floor(cur.y), floor(ex), floor(op.p2.y))
maxY = max(maxY, cur.y, ex, op.p2.y) maxY = max(maxY, ceil(cur.y), ceil(ex), ceil(op.p2.y))
} else { } else {
minY = min(minY, cur.y, op.p2.y) minY = min(minY, floor(cur.y), floor(op.p2.y))
maxY = max(maxY, cur.y, op.p2.y) maxY = max(maxY, ceil(cur.y), ceil(op.p2.y))
} }
} else { } else {
minY = min(minY, cur.y, op.p1.y, op.p2.y) minY = min(minY, floor(cur.y), floor(op.p1.y), floor(op.p2.y))
maxY = max(maxY, cur.y, op.p1.y, op.p2.y) maxY = max(maxY, ceil(cur.y), ceil(op.p1.y), ceil(op.p2.y))
} }
cur = op.p2 cur = op.p2
} }
@@ -1151,5 +1151,5 @@ func (p *Path) Bounds() image.Rectangle {
if totalMinX >= totalMaxX || totalMinY >= totalMaxY { if totalMinX >= totalMaxX || totalMinY >= totalMaxY {
return image.Rectangle{} return image.Rectangle{}
} }
return image.Rect(floor(totalMinX), floor(totalMinY), ceil(totalMaxX), ceil(totalMaxY)) return image.Rect(totalMinX, totalMinY, totalMaxX, totalMaxY)
} }