124 lines
3.7 KiB
Go
124 lines
3.7 KiB
Go
package compression
|
|
|
|
// 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 errCannotRepeatWordOnFirstTileOfRow
|
|
}
|
|
control = lastControl
|
|
}
|
|
if control.Type() == CtrlUnknown {
|
|
return errUnknownControl
|
|
}
|
|
|
|
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),
|
|
wordIndexOutOfRangeError{
|
|
Index: controlIndex,
|
|
Available: 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), errTooManyLongOffsets
|
|
}
|
|
bitstream.Advance(4)
|
|
offset := bitstream.Read(4)
|
|
controlIndex = control.LongOffset() + offset
|
|
if controlIndex > availableWords {
|
|
return ControlWordOf(0, CtrlUnknown, 0),
|
|
wordIndexOutOfRangeError{
|
|
Index: controlIndex,
|
|
Available: 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)
|
|
}
|
|
}
|