-
Notifications
You must be signed in to change notification settings - Fork 7
/
FrameDecoder.go
121 lines (105 loc) · 3.78 KB
/
FrameDecoder.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
package compression
import (
"errors"
"fmt"
)
// FrameDecoder is for decoding compressed frames with the help of data streams.
// A new instance of a decoder is created with a FrameDecoderBuilder.
type FrameDecoder struct {
horizontalTiles int
verticalTiles int
colorer TileColorFunction
paletteLookupList []byte
controlWords []ControlWord
}
func newFrameDecoder(builder *FrameDecoderBuilder) *FrameDecoder {
listLen := len(builder.paletteLookupList)
decoder := &FrameDecoder{
horizontalTiles: builder.width / TileSideLength,
verticalTiles: builder.height / TileSideLength,
colorer: builder.colorer,
paletteLookupList: make([]byte, listLen+16),
controlWords: builder.controlWords}
copy(decoder.paletteLookupList[:listLen], builder.paletteLookupList)
return decoder
}
// Decode reads the provided streams to paint a frame.
func (decoder *FrameDecoder) Decode(bitstreamData []byte, maskstreamData []byte) error {
bitstream := NewBitstreamReader(bitstreamData)
maskstream := NewMaskstreamReader(maskstreamData)
for vTile := 0; vTile < decoder.verticalTiles && !bitstream.Exhausted(); vTile++ {
lastControl := ControlWordOf(0, CtrlUnknown, 0)
for hTile := 0; hTile < decoder.horizontalTiles && !bitstream.Exhausted(); hTile++ {
control, err := decoder.readNextControlWord(bitstream)
if err != nil {
return err
}
if control.Type() == CtrlRepeatPrevious {
if hTile == 0 {
return errors.New("can't repeat word on first tile of row")
}
control = lastControl
}
if control.Type() == CtrlUnknown {
return errors.New("found unknown control, can not proceed")
}
if control.Type() == CtrlSkip {
skipCount := bitstream.Read(5)
bitstream.Advance(5)
if skipCount == 0x1F {
hTile = decoder.horizontalTiles
} else {
hTile += int(skipCount)
}
} else {
decoder.colorTile(hTile, vTile, control, maskstream)
}
lastControl = control
}
}
return nil
}
func (decoder *FrameDecoder) readNextControlWord(bitstream *BitstreamReader) (ControlWord, error) {
availableWords := uint32(len(decoder.controlWords))
controlIndex := bitstream.Read(12)
if controlIndex > availableWords {
return ControlWordOf(0, CtrlUnknown, 0),
fmt.Errorf("control word index out of range: %v/%v", controlIndex, availableWords)
}
control := decoder.controlWords[controlIndex]
if control.IsLongOffset() {
longOffsetCount := 0
bitstream.Advance(8)
for control.IsLongOffset() {
longOffsetCount++
if longOffsetCount > 100 {
return ControlWordOf(0, CtrlUnknown, 0), errors.New("too many long offsets")
}
bitstream.Advance(4)
offset := bitstream.Read(4)
controlIndex = control.LongOffset() + offset
if controlIndex > availableWords {
return ControlWordOf(0, CtrlUnknown, 0),
fmt.Errorf("control word index out of range: %v/%v", controlIndex, availableWords)
}
control = decoder.controlWords[controlIndex]
}
}
bitstream.Advance(control.Count())
return control, nil
}
func (decoder *FrameDecoder) colorTile(hTile, vTile int, control ControlWord, maskstream *MaskstreamReader) {
param := control.Parameter()
switch control.Type() {
case CtrlColorTile2ColorsStatic:
decoder.colorer(hTile, vTile, []byte{byte(param & 0xFF), byte(param >> 8 & 0xFF)}, 0xAAAA, 1)
case CtrlColorTile2ColorsMasked:
decoder.colorer(hTile, vTile, []byte{byte(param & 0xFF), byte(param >> 8 & 0xFF)}, maskstream.Read(2), 1)
case CtrlColorTile4ColorsMasked:
decoder.colorer(hTile, vTile, decoder.paletteLookupList[param:param+4], maskstream.Read(4), 2)
case CtrlColorTile8ColorsMasked:
decoder.colorer(hTile, vTile, decoder.paletteLookupList[param:param+8], maskstream.Read(6), 3)
case CtrlColorTile16ColorsMasked:
decoder.colorer(hTile, vTile, decoder.paletteLookupList[param:param+16], maskstream.Read(8), 4)
}
}