videosink: Add options for JPEG and PNG encoders

Expose the quality settings for the JPEG encoder and the compression
level of the PNG encoder via the Options structure.

Signed-off-by: Michael Hanselmann <public@hansmi.ch>
pull/30/head
Michael Hanselmann 4 years ago
parent 42ee8553ed
commit e989a2f6ad

@ -21,12 +21,16 @@ import (
"image"
"image/color"
"image/draw"
"image/jpeg"
"image/png"
"net/http"
"sync"
"periph.io/x/conn/v3/display"
)
const defaultJPEGQuality = 95
// Options for videosink devices.
type Options struct {
// Width and height of the image buffer.
@ -35,11 +39,21 @@ type Options struct {
// Format specifies the image format to send to clients.
Format ImageFormat
// TODO: Add options for JPEG and PNG encoder settings
// JPEG controls options for the JPEG encoder.
JPEG jpeg.Options
// PNG controls options for the PNG encoder.
PNG struct {
// CompressionLevel is the amount of compression applied by the PNG
// encoder. Defaults to png.DefaultCompression.
CompressionLevel png.CompressionLevel
}
}
type Display struct {
defaultFormat ImageFormat
jpegOptions jpeg.Options
pngCompressionLevel png.CompressionLevel
mu sync.Mutex
buffer *image.RGBA
@ -58,12 +72,21 @@ func New(opt *Options) *Display {
// draw operation makes it opaque.
draw.Draw(buffer, buffer.Bounds(), image.Black, image.Point{}, draw.Src)
return &Display{
d := &Display{
jpegOptions: opt.JPEG,
pngCompressionLevel: opt.PNG.CompressionLevel,
buffer: buffer,
clients: map[*client]struct{}{},
snapshot: map[imageConfig][]byte{},
defaultFormat: opt.Format,
}
if d.jpegOptions.Quality == 0 {
d.jpegOptions.Quality = defaultJPEGQuality
}
return d
}
// String returns the name of the device.

@ -5,15 +5,10 @@
package videosink
import (
"image/jpeg"
"image/png"
"sync"
)
var jpegOptions = jpeg.Options{
Quality: 95,
}
type pngEncoderBufferPool sync.Pool
func (p *pngEncoderBufferPool) Get() *png.EncoderBuffer {
@ -26,21 +21,33 @@ func (p *pngEncoderBufferPool) Put(buf *png.EncoderBuffer) {
}
type pngEncoderManager struct {
once sync.Once
mu sync.Mutex
pool pngEncoderBufferPool
enc *png.Encoder
enc map[png.CompressionLevel]*png.Encoder
}
var pngEncoder pngEncoderManager
// get returns a PNG encoder with a globally shared buffer pool.
func (m *pngEncoderManager) get() *png.Encoder {
m.once.Do(func() {
m.enc = &png.Encoder{
CompressionLevel: png.BestSpeed,
func (m *pngEncoderManager) get(level png.CompressionLevel) *png.Encoder {
m.mu.Lock()
defer m.mu.Unlock()
enc := m.enc[level]
if enc == nil {
if m.enc == nil {
// The vast majority of use cases will involve exactly one
// compression level.
m.enc = make(map[png.CompressionLevel]*png.Encoder, 1)
}
enc = &png.Encoder{
CompressionLevel: level,
BufferPool: &m.pool,
}
})
return m.enc
m.enc[level] = enc
}
return enc
}

@ -80,12 +80,12 @@ func (d *Display) encodeBufferLocked(format ImageFormat) ([]byte, error) {
switch format {
case PNG:
if err := pngEncoder.get().Encode(buf, d.buffer); err != nil {
if err := pngEncoder.get(d.pngCompressionLevel).Encode(buf, d.buffer); err != nil {
return nil, err
}
case JPEG:
if err := jpeg.Encode(buf, d.buffer, &jpegOptions); err != nil {
if err := jpeg.Encode(buf, d.buffer, &d.jpegOptions); err != nil {
return nil, err
}

Loading…
Cancel
Save