Fill what a rounded rectangle contains outright as spans instead of pixel by pixel
This commit is contained in:
@@ -352,29 +352,68 @@ func (fb *framebuffer) rect(x0, y0, w, h int, c rgb) {
|
||||
}
|
||||
}
|
||||
|
||||
// Distance to the rectangle the corner radius sweeps around, which is zero
|
||||
// across the whole flat middle and grows only near a corner. Taking coverage
|
||||
// from that rather than from a plain inside test keeps the curves smooth
|
||||
// instead of stepped.
|
||||
// The rectangle the corner radius sweeps around. Distance to it is zero across
|
||||
// the whole flat middle and grows only near a corner. Taking coverage from that
|
||||
// rather than from a plain inside test keeps the curves smooth instead of
|
||||
// stepped.
|
||||
type sweep struct {
|
||||
ix0, iy0, ix1, iy1 float64
|
||||
r int
|
||||
}
|
||||
|
||||
func (s sweep) pixel(fb *framebuffer, x, y int, c rgb) {
|
||||
fx, fy := float64(x), float64(y)
|
||||
dx := math.Max(math.Max(s.ix0-fx, fx-s.ix1), 0)
|
||||
dy := math.Max(math.Max(s.iy0-fy, fy-s.iy1), 0)
|
||||
cov := float64(s.r) - math.Sqrt(dx*dx+dy*dy) + 0.5
|
||||
if cov <= 0 {
|
||||
return
|
||||
}
|
||||
fb.blend(x, y, c, uint8(math.Min(cov, 1)*255))
|
||||
}
|
||||
|
||||
// Partial coverage reaches no further than the corner blocks and the one line
|
||||
// of pixels the curve runs tangent to along each flat edge. The sweep contains
|
||||
// everything else outright, which fills as spans instead of a pixel at a time.
|
||||
func (fb *framebuffer) roundRect(x0, y0, w, h, r int, c rgb) {
|
||||
// A radius past half the shorter side has no meaning and would put the
|
||||
// swept rectangle inside out, which matters while something is growing from
|
||||
// nothing.
|
||||
r = min(r, min(w, h)/2)
|
||||
ix0, iy0 := float64(x0+r), float64(y0+r)
|
||||
ix1, iy1 := float64(x0+w-1-r), float64(y0+h-1-r)
|
||||
s := sweep{float64(x0 + r), float64(y0 + r),
|
||||
float64(x0 + w - 1 - r), float64(y0 + h - 1 - r), r}
|
||||
|
||||
// With no radius the sweep never reaches a whole pixel, so the sliver it
|
||||
// leaves is partly covered throughout rather than solid anywhere.
|
||||
if r == 0 {
|
||||
for y := y0; y < y0+h; y++ {
|
||||
for x := x0; x < x0+w; x++ {
|
||||
fx, fy := float64(x), float64(y)
|
||||
dx := math.Max(math.Max(ix0-fx, fx-ix1), 0)
|
||||
dy := math.Max(math.Max(iy0-fy, fy-iy1), 0)
|
||||
cov := float64(r) - math.Sqrt(dx*dx+dy*dy) + 0.5
|
||||
if cov <= 0 {
|
||||
continue
|
||||
}
|
||||
fb.blend(x, y, c, uint8(math.Min(cov, 1)*255))
|
||||
s.pixel(fb, x, y, c)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
for j := 0; j < r; j++ {
|
||||
for i := 0; i < r; i++ {
|
||||
s.pixel(fb, x0+i, y0+j, c)
|
||||
s.pixel(fb, x0+w-1-i, y0+j, c)
|
||||
s.pixel(fb, x0+i, y0+h-1-j, c)
|
||||
s.pixel(fb, x0+w-1-i, y0+h-1-j, c)
|
||||
}
|
||||
}
|
||||
for x := x0 + r; x < x0+w-r; x++ {
|
||||
s.pixel(fb, x, y0, c)
|
||||
s.pixel(fb, x, y0+h-1, c)
|
||||
}
|
||||
for y := y0 + r; y < y0+h-r; y++ {
|
||||
s.pixel(fb, x0, y, c)
|
||||
s.pixel(fb, x0+w-1, y, c)
|
||||
}
|
||||
|
||||
fb.rect(x0+r, y0+1, w-2*r, h-2, c)
|
||||
fb.rect(x0+1, y0+r, r-1, h-2*r, c)
|
||||
fb.rect(x0+w-r, y0+r, r-1, h-2*r, c)
|
||||
}
|
||||
|
||||
// Blends src over the existing pixel, with cov as 0-255 coverage.
|
||||
|
||||
Reference in New Issue
Block a user