github.com/amitbet/vnc2video@v0.0.0-20190616012314-9d50b9dab1d9/pixel_format.go (about)

     1  // Implementation of RFC 6143 ยง7.4 Pixel Format Data Structure.
     2  
     3  package vnc2video
     4  
     5  import (
     6  	"bytes"
     7  	"encoding/binary"
     8  	"fmt"
     9  	"io"
    10  )
    11  
    12  var (
    13  	// PixelFormat8bit returns 8 bit pixel format
    14  	PixelFormat8bit = NewPixelFormat(8)
    15  	// PixelFormat16bit returns 16 bit pixel format
    16  	PixelFormat16bit = NewPixelFormat(16)
    17  	// PixelFormat32bit returns 32 bit pixel format
    18  	PixelFormat32bit = NewPixelFormat(32)
    19  	// PixelFormatAten returns pixel format used in Aten IKVM
    20  	PixelFormatAten = NewPixelFormatAten()
    21  )
    22  
    23  // PixelFormat describes the way a pixel is formatted for a VNC connection
    24  type PixelFormat struct {
    25  	BPP                             uint8   // bits-per-pixel
    26  	Depth                           uint8   // depth
    27  	BigEndian                       uint8   // big-endian-flag
    28  	TrueColor                       uint8   // true-color-flag
    29  	RedMax, GreenMax, BlueMax       uint16  // red-, green-, blue-max (2^BPP-1)
    30  	RedShift, GreenShift, BlueShift uint8   // red-, green-, blue-shift
    31  	_                               [3]byte // padding
    32  }
    33  
    34  const pixelFormatLen = 16
    35  
    36  // NewPixelFormat returns a populated PixelFormat structure
    37  func NewPixelFormat(bpp uint8) PixelFormat {
    38  	bigEndian := uint8(0)
    39  	//	rgbMax := uint16(math.Exp2(float64(bpp))) - 1
    40  	rMax := uint16(255)
    41  	gMax := uint16(255)
    42  	bMax := uint16(255)
    43  	var (
    44  		tc         = uint8(1)
    45  		rs, gs, bs uint8
    46  		depth      uint8
    47  	)
    48  	switch bpp {
    49  	case 8:
    50  		tc = 0
    51  		depth = 8
    52  		rs, gs, bs = 0, 0, 0
    53  	case 16:
    54  		depth = 16
    55  		rs, gs, bs = 0, 4, 8
    56  	case 32:
    57  		depth = 24
    58  		//	rs, gs, bs = 0, 8, 16
    59  		rs, gs, bs = 16, 8, 0
    60  	}
    61  	return PixelFormat{bpp, depth, bigEndian, tc, rMax, gMax, bMax, rs, gs, bs, [3]byte{}}
    62  }
    63  
    64  // NewPixelFormatAten returns Aten IKVM pixel format
    65  func NewPixelFormatAten() PixelFormat {
    66  	return PixelFormat{16, 15, 0, 1, (1 << 5) - 1, (1 << 5) - 1, (1 << 5) - 1, 10, 5, 0, [3]byte{}}
    67  }
    68  
    69  // Marshal implements the Marshaler interface
    70  func (pf PixelFormat) Marshal() ([]byte, error) {
    71  	// Validation checks.
    72  	switch pf.BPP {
    73  	case 8, 16, 32:
    74  	default:
    75  		return nil, fmt.Errorf("Invalid BPP value %v; must be 8, 16, or 32", pf.BPP)
    76  	}
    77  
    78  	if pf.Depth < pf.BPP {
    79  		return nil, fmt.Errorf("Invalid Depth value %v; cannot be < BPP", pf.Depth)
    80  	}
    81  	switch pf.Depth {
    82  	case 8, 16, 32:
    83  	default:
    84  		return nil, fmt.Errorf("Invalid Depth value %v; must be 8, 16, or 32", pf.Depth)
    85  	}
    86  
    87  	// Create the slice of bytes
    88  	buf := bPool.Get().(*bytes.Buffer)
    89  	buf.Reset()
    90  	defer bPool.Put(buf)
    91  
    92  	if err := binary.Write(buf, binary.BigEndian, &pf); err != nil {
    93  		return nil, err
    94  	}
    95  
    96  	return buf.Bytes(), nil
    97  }
    98  
    99  // Read reads from an io.Reader, and populates the PixelFormat
   100  func (pf PixelFormat) Read(r io.Reader) error {
   101  	buf := make([]byte, pixelFormatLen)
   102  	if _, err := io.ReadAtLeast(r, buf, pixelFormatLen); err != nil {
   103  		return err
   104  	}
   105  	return pf.Unmarshal(buf)
   106  }
   107  
   108  // Unmarshal implements the Unmarshaler interface
   109  func (pf PixelFormat) Unmarshal(data []byte) error {
   110  	buf := bPool.Get().(*bytes.Buffer)
   111  	buf.Reset()
   112  	defer bPool.Put(buf)
   113  
   114  	if _, err := buf.Write(data); err != nil {
   115  		return err
   116  	}
   117  
   118  	if err := binary.Read(buf, binary.BigEndian, &pf); err != nil {
   119  		return err
   120  	}
   121  
   122  	return nil
   123  }
   124  
   125  // String implements the fmt.Stringer interface
   126  func (pf PixelFormat) String() string {
   127  	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 }",
   128  		pf.BPP, pf.Depth, pf.BigEndian, pf.TrueColor, pf.RedMax, pf.GreenMax, pf.BlueMax, pf.RedShift, pf.GreenShift, pf.BlueShift)
   129  }
   130  
   131  func (pf PixelFormat) order() binary.ByteOrder {
   132  	if pf.BigEndian == 1 {
   133  		return binary.BigEndian
   134  	}
   135  	return binary.LittleEndian
   136  }