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 }