Consistently cache display device Bounds()

Improve Draw() performance.
pull/1/head
Marc-Antoine Ruel 8 years ago
parent 0a22d3193e
commit 6f8bd69f03

@ -78,6 +78,7 @@ func New(p spi.Port, o *Opts) (*Dev, error) {
numPixels: o.NumPixels, numPixels: o.NumPixels,
rawBuf: buf, rawBuf: buf,
pixels: buf[4 : 4+4*o.NumPixels], pixels: buf[4 : 4+4*o.NumPixels],
rect: image.Rect(0, 0, o.NumPixels, 1),
}, nil }, nil
} }
@ -96,11 +97,12 @@ type Dev struct {
// It can be changed, it will take effect on the next Draw() or Write() call. // It can be changed, it will take effect on the next Draw() or Write() call.
Temperature uint16 Temperature uint16
s spi.Conn // s spi.Conn //
l lut // Updated at each .Write() call. l lut // Updated at each .Write() call.
numPixels int // numPixels int //
rawBuf []byte // Raw buffer sent over SPI. Cached to reduce heap fragmentation. rawBuf []byte // Raw buffer sent over SPI. Cached to reduce heap fragmentation.
pixels []byte // Double buffer of pixels, to enable partial painting via Draw(). Effectively points inside rawBuf. pixels []byte // Double buffer of pixels, to enable partial painting via Draw(). Effectively points inside rawBuf.
rect image.Rectangle // Device bounds
} }
func (d *Dev) String() string { func (d *Dev) String() string {
@ -115,7 +117,7 @@ func (d *Dev) ColorModel() color.Model {
// Bounds implements devices.Display. Min is guaranteed to be {0, 0}. // Bounds implements devices.Display. Min is guaranteed to be {0, 0}.
func (d *Dev) Bounds() image.Rectangle { func (d *Dev) Bounds() image.Rectangle {
return image.Rectangle{Max: image.Point{X: d.numPixels, Y: 1}} return d.rect
} }
// Draw implements devices.Display. // Draw implements devices.Display.
@ -123,7 +125,7 @@ func (d *Dev) Bounds() image.Rectangle {
// Using something else than image.NRGBA is 10x slower. When using image.NRGBA, // Using something else than image.NRGBA is 10x slower. When using image.NRGBA,
// the alpha channel is ignored. // the alpha channel is ignored.
func (d *Dev) Draw(r image.Rectangle, src image.Image, sp image.Point) { func (d *Dev) Draw(r image.Rectangle, src image.Image, sp image.Point) {
r = r.Intersect(d.Bounds()) r = r.Intersect(d.rect)
srcR := src.Bounds() srcR := src.Bounds()
srcR.Min = srcR.Min.Add(sp) srcR.Min = srcR.Min.Add(sp)
if dX := r.Dx(); dX < srcR.Dx() { if dX := r.Dx(); dX < srcR.Dx() {

@ -189,7 +189,7 @@ func (d *Dev) Bounds() image.Rectangle {
// It discards any failure. // It discards any failure.
func (d *Dev) Draw(r image.Rectangle, src image.Image, sp image.Point) { func (d *Dev) Draw(r image.Rectangle, src image.Image, sp image.Point) {
var next []byte var next []byte
if img, ok := src.(*image1bit.VerticalLSB); ok && r == d.Bounds() && src.Bounds() == d.rect && sp.X == 0 && sp.Y == 0 { if img, ok := src.(*image1bit.VerticalLSB); ok && r == d.rect && src.Bounds() == d.rect && sp.X == 0 && sp.Y == 0 {
// Exact size, full frame, image1bit encoding: fast path! // Exact size, full frame, image1bit encoding: fast path!
next = img.Pix next = img.Pix
} else { } else {

@ -82,6 +82,7 @@ func New(p gpiostream.PinOut, opts *Opts) (*Dev, error) {
Bits: make([]byte, opts.NumPixels*3*opts.Channels), Bits: make([]byte, opts.NumPixels*3*opts.Channels),
LSBF: false, LSBF: false,
}, },
rect: image.Rect(0, 0, opts.NumPixels, 1),
}, nil }, nil
} }
@ -92,6 +93,7 @@ type Dev struct {
channels int // Number of channels per pixel channels int // Number of channels per pixel
b gpiostream.BitStream // NRZ encoded bits; cached to reduce heap fragmentation b gpiostream.BitStream // NRZ encoded bits; cached to reduce heap fragmentation
buf []byte // Double buffer of RGB/RGBW pixels; enables partial Draw() buf []byte // Double buffer of RGB/RGBW pixels; enables partial Draw()
rect image.Rectangle // Device bounds
} }
func (d *Dev) String() string { func (d *Dev) String() string {
@ -126,7 +128,7 @@ func (d *Dev) ColorModel() color.Model {
// Bounds implements devices.Display. Min is guaranteed to be {0, 0}. // Bounds implements devices.Display. Min is guaranteed to be {0, 0}.
func (d *Dev) Bounds() image.Rectangle { func (d *Dev) Bounds() image.Rectangle {
return image.Rectangle{Max: image.Point{X: d.numPixels, Y: 1}} return d.rect
} }
// Draw implements devices.Display. // Draw implements devices.Display.
@ -138,7 +140,7 @@ func (d *Dev) Bounds() image.Rectangle {
// A back buffer is kept so that partial updates are supported, albeit the full // A back buffer is kept so that partial updates are supported, albeit the full
// LED strip is updated synchronously. // LED strip is updated synchronously.
func (d *Dev) Draw(r image.Rectangle, src image.Image, sp image.Point) { func (d *Dev) Draw(r image.Rectangle, src image.Image, sp image.Point) {
r = r.Intersect(d.Bounds()) r = r.Intersect(d.rect)
srcR := src.Bounds() srcR := src.Bounds()
srcR.Min = srcR.Min.Add(sp) srcR.Min = srcR.Min.Add(sp)
if dX := r.Dx(); dX < srcR.Dx() { if dX := r.Dx(); dX < srcR.Dx() {

Loading…
Cancel
Save