2017-06-12 14:11:23 +03:00
|
|
|
// Implementation of RFC 6143 §7.4 Pixel Format Data Structure.
|
|
|
|
|
2018-07-27 16:55:06 +03:00
|
|
|
package rfb
|
2017-06-12 14:11:23 +03:00
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"encoding/binary"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
2017-07-04 01:36:10 +03:00
|
|
|
// PixelFormat8bit returns 8 bit pixel format
|
|
|
|
PixelFormat8bit = NewPixelFormat(8)
|
|
|
|
// PixelFormat16bit returns 16 bit pixel format
|
|
|
|
PixelFormat16bit = NewPixelFormat(16)
|
|
|
|
// PixelFormat32bit returns 32 bit pixel format
|
|
|
|
PixelFormat32bit = NewPixelFormat(32)
|
|
|
|
// PixelFormatAten returns pixel format used in Aten IKVM
|
|
|
|
PixelFormatAten = NewPixelFormatAten()
|
2017-06-12 14:11:23 +03:00
|
|
|
)
|
|
|
|
|
2017-07-04 01:36:10 +03:00
|
|
|
// PixelFormat describes the way a pixel is formatted for a VNC connection
|
2017-06-12 14:11:23 +03:00
|
|
|
type PixelFormat struct {
|
|
|
|
BPP uint8 // bits-per-pixel
|
|
|
|
Depth uint8 // depth
|
|
|
|
BigEndian uint8 // big-endian-flag
|
|
|
|
TrueColor uint8 // true-color-flag
|
|
|
|
RedMax, GreenMax, BlueMax uint16 // red-, green-, blue-max (2^BPP-1)
|
|
|
|
RedShift, GreenShift, BlueShift uint8 // red-, green-, blue-shift
|
|
|
|
_ [3]byte // padding
|
|
|
|
}
|
|
|
|
|
|
|
|
const pixelFormatLen = 16
|
|
|
|
|
2017-07-04 01:36:10 +03:00
|
|
|
// NewPixelFormat returns a populated PixelFormat structure
|
2017-06-30 00:24:39 +03:00
|
|
|
func NewPixelFormat(bpp uint8) PixelFormat {
|
2017-06-13 01:52:07 +03:00
|
|
|
bigEndian := uint8(0)
|
2017-06-13 16:20:35 +03:00
|
|
|
// rgbMax := uint16(math.Exp2(float64(bpp))) - 1
|
|
|
|
rMax := uint16(255)
|
|
|
|
gMax := uint16(255)
|
|
|
|
bMax := uint16(255)
|
2017-06-12 14:11:23 +03:00
|
|
|
var (
|
|
|
|
tc = uint8(1)
|
|
|
|
rs, gs, bs uint8
|
2017-06-13 16:20:35 +03:00
|
|
|
depth uint8
|
2017-06-12 14:11:23 +03:00
|
|
|
)
|
|
|
|
switch bpp {
|
|
|
|
case 8:
|
|
|
|
tc = 0
|
2017-06-13 16:20:35 +03:00
|
|
|
depth = 8
|
2017-06-12 14:11:23 +03:00
|
|
|
rs, gs, bs = 0, 0, 0
|
|
|
|
case 16:
|
2017-06-13 16:20:35 +03:00
|
|
|
depth = 16
|
2017-06-12 14:11:23 +03:00
|
|
|
rs, gs, bs = 0, 4, 8
|
|
|
|
case 32:
|
2017-06-13 16:20:35 +03:00
|
|
|
depth = 24
|
|
|
|
// rs, gs, bs = 0, 8, 16
|
|
|
|
rs, gs, bs = 16, 8, 0
|
2017-06-12 14:11:23 +03:00
|
|
|
}
|
2017-06-30 00:24:39 +03:00
|
|
|
return PixelFormat{bpp, depth, bigEndian, tc, rMax, gMax, bMax, rs, gs, bs, [3]byte{}}
|
2017-06-12 14:11:23 +03:00
|
|
|
}
|
|
|
|
|
2017-07-04 01:36:10 +03:00
|
|
|
// NewPixelFormatAten returns Aten IKVM pixel format
|
2017-06-30 16:13:01 +03:00
|
|
|
func NewPixelFormatAten() PixelFormat {
|
|
|
|
return PixelFormat{16, 15, 0, 1, (1 << 5) - 1, (1 << 5) - 1, (1 << 5) - 1, 10, 5, 0, [3]byte{}}
|
|
|
|
}
|
|
|
|
|
2017-07-04 01:36:10 +03:00
|
|
|
// Marshal implements the Marshaler interface
|
2017-06-30 00:24:39 +03:00
|
|
|
func (pf PixelFormat) Marshal() ([]byte, error) {
|
2017-06-12 14:11:23 +03:00
|
|
|
// Validation checks.
|
|
|
|
switch pf.BPP {
|
2017-06-13 16:20:35 +03:00
|
|
|
case 8, 16, 32:
|
2017-06-12 14:11:23 +03:00
|
|
|
default:
|
2017-07-04 01:36:10 +03:00
|
|
|
return nil, fmt.Errorf("Invalid BPP value %v; must be 8, 16, or 32", pf.BPP)
|
2017-06-12 14:11:23 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if pf.Depth < pf.BPP {
|
|
|
|
return nil, fmt.Errorf("Invalid Depth value %v; cannot be < BPP", pf.Depth)
|
|
|
|
}
|
|
|
|
switch pf.Depth {
|
2017-06-13 16:20:35 +03:00
|
|
|
case 8, 16, 32:
|
2017-06-12 14:11:23 +03:00
|
|
|
default:
|
2017-07-04 01:36:10 +03:00
|
|
|
return nil, fmt.Errorf("Invalid Depth value %v; must be 8, 16, or 32", pf.Depth)
|
2017-06-12 14:11:23 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Create the slice of bytes
|
2017-06-18 22:38:59 +03:00
|
|
|
buf := bPool.Get().(*bytes.Buffer)
|
|
|
|
buf.Reset()
|
|
|
|
defer bPool.Put(buf)
|
|
|
|
|
2017-06-12 14:11:23 +03:00
|
|
|
if err := binary.Write(buf, binary.BigEndian, &pf); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return buf.Bytes(), nil
|
|
|
|
}
|
|
|
|
|
2017-07-04 01:36:10 +03:00
|
|
|
// Read reads from an io.Reader, and populates the PixelFormat
|
2017-06-30 00:24:39 +03:00
|
|
|
func (pf PixelFormat) Read(r io.Reader) error {
|
2017-06-12 14:11:23 +03:00
|
|
|
buf := make([]byte, pixelFormatLen)
|
|
|
|
if _, err := io.ReadAtLeast(r, buf, pixelFormatLen); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return pf.Unmarshal(buf)
|
|
|
|
}
|
|
|
|
|
2017-07-04 01:36:10 +03:00
|
|
|
// Unmarshal implements the Unmarshaler interface
|
2017-06-30 00:24:39 +03:00
|
|
|
func (pf PixelFormat) Unmarshal(data []byte) error {
|
2017-06-18 22:38:59 +03:00
|
|
|
buf := bPool.Get().(*bytes.Buffer)
|
|
|
|
buf.Reset()
|
|
|
|
defer bPool.Put(buf)
|
|
|
|
|
|
|
|
if _, err := buf.Write(data); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-06-12 14:11:23 +03:00
|
|
|
|
2017-06-30 00:24:39 +03:00
|
|
|
if err := binary.Read(buf, binary.BigEndian, &pf); err != nil {
|
2017-06-12 14:11:23 +03:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2017-07-04 01:36:10 +03:00
|
|
|
// String implements the fmt.Stringer interface
|
2017-06-30 00:24:39 +03:00
|
|
|
func (pf PixelFormat) String() string {
|
2017-06-29 00:09:31 +03:00
|
|
|
return fmt.Sprintf("{ bpp: %d depth: %d big-endian: %d true-color: %d red-max: %d green-max: %d blue-max: %d red-shift: %d green-shift: %d blue-shift: %d }",
|
2017-06-12 14:11:23 +03:00
|
|
|
pf.BPP, pf.Depth, pf.BigEndian, pf.TrueColor, pf.RedMax, pf.GreenMax, pf.BlueMax, pf.RedShift, pf.GreenShift, pf.BlueShift)
|
|
|
|
}
|
|
|
|
|
2017-06-30 00:24:39 +03:00
|
|
|
func (pf PixelFormat) order() binary.ByteOrder {
|
2017-06-12 14:11:23 +03:00
|
|
|
if pf.BigEndian == 1 {
|
|
|
|
return binary.BigEndian
|
|
|
|
}
|
|
|
|
return binary.LittleEndian
|
|
|
|
}
|