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
|
// The rectangle the corner radius sweeps around. Distance to it is zero across
|
||||||
// across the whole flat middle and grows only near a corner. Taking coverage
|
// the whole flat middle and grows only near a corner. Taking coverage from that
|
||||||
// from that rather than from a plain inside test keeps the curves smooth
|
// rather than from a plain inside test keeps the curves smooth instead of
|
||||||
// instead of stepped.
|
// 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) {
|
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
|
// 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
|
// swept rectangle inside out, which matters while something is growing from
|
||||||
// nothing.
|
// nothing.
|
||||||
r = min(r, min(w, h)/2)
|
r = min(r, min(w, h)/2)
|
||||||
ix0, iy0 := float64(x0+r), float64(y0+r)
|
s := sweep{float64(x0 + r), float64(y0 + r),
|
||||||
ix1, iy1 := float64(x0+w-1-r), float64(y0+h-1-r)
|
float64(x0 + w - 1 - r), float64(y0 + h - 1 - r), r}
|
||||||
for y := y0; y < y0+h; y++ {
|
|
||||||
for x := x0; x < x0+w; x++ {
|
// With no radius the sweep never reaches a whole pixel, so the sliver it
|
||||||
fx, fy := float64(x), float64(y)
|
// leaves is partly covered throughout rather than solid anywhere.
|
||||||
dx := math.Max(math.Max(ix0-fx, fx-ix1), 0)
|
if r == 0 {
|
||||||
dy := math.Max(math.Max(iy0-fy, fy-iy1), 0)
|
for y := y0; y < y0+h; y++ {
|
||||||
cov := float64(r) - math.Sqrt(dx*dx+dy*dy) + 0.5
|
for x := x0; x < x0+w; x++ {
|
||||||
if cov <= 0 {
|
s.pixel(fb, x, y, c)
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
fb.blend(x, y, c, uint8(math.Min(cov, 1)*255))
|
}
|
||||||
|
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.
|
// Blends src over the existing pixel, with cov as 0-255 coverage.
|
||||||
|
|||||||
Reference in New Issue
Block a user