mirror of
https://github.com/wader/fq.git
synced 2024-12-23 13:22:58 +03:00
7b27e506f1
PSSH decoding probably got broken in some refactoring. system id is 16 bytes, also scalar mapper should use cloned reader to not affect reader which in the case broke reading system id bytes. Add test with playread and widevine pssh boxes Related to #282
156 lines
3.8 KiB
Go
156 lines
3.8 KiB
Go
package gif
|
|
|
|
// https://www.w3.org/Graphics/GIF/spec-gif87.txt
|
|
// https://en.wikipedia.org/wiki/GIF
|
|
// https://web.archive.org/web/20160304075538/http://qalle.net/gif89a.php#graphiccontrolextension
|
|
|
|
// TODO: local color map
|
|
// TODO: bit depth done correct?
|
|
|
|
import (
|
|
"bytes"
|
|
|
|
"github.com/wader/fq/format"
|
|
"github.com/wader/fq/format/registry"
|
|
"github.com/wader/fq/pkg/decode"
|
|
"github.com/wader/fq/pkg/scalar"
|
|
)
|
|
|
|
func init() {
|
|
registry.MustRegister(decode.Format{
|
|
Name: format.GIF,
|
|
Description: "Graphics Interchange Format",
|
|
Groups: []string{format.PROBE, format.IMAGE},
|
|
DecodeFn: gifDecode,
|
|
})
|
|
}
|
|
|
|
const (
|
|
extensionPlainText = 0x01
|
|
extensionGraphicalControl = 0xf9
|
|
extensionComment = 0xfe
|
|
extensionApplication = 0xff
|
|
)
|
|
|
|
var extensionNames = scalar.UToSymStr{
|
|
extensionPlainText: "PlainText",
|
|
extensionGraphicalControl: "GraphicalControl",
|
|
extensionComment: "Comment",
|
|
extensionApplication: "Application",
|
|
}
|
|
|
|
func fieldColorMap(d *decode.D, name string, bitDepth int) {
|
|
d.FieldArray(name, func(d *decode.D) {
|
|
for i := 0; i < 1<<bitDepth; i++ {
|
|
d.FieldArray("color", func(d *decode.D) {
|
|
d.FieldU8("r")
|
|
d.FieldU8("g")
|
|
d.FieldU8("b")
|
|
})
|
|
}
|
|
})
|
|
}
|
|
|
|
func gifDecode(d *decode.D, in any) any {
|
|
d.Endian = decode.LittleEndian
|
|
|
|
d.FieldUTF8("header", 6, d.AssertStr("GIF87a", "GIF89a"))
|
|
|
|
d.FieldU16("width")
|
|
d.FieldU16("height")
|
|
gcpFollows := d.FieldBool("gcp_follows")
|
|
d.FieldUFn("color_resolution", func(d *decode.D) uint64 { return d.U3() + 1 })
|
|
d.FieldU1("zero")
|
|
bitDepth := d.FieldUFn("bit_depth", func(d *decode.D) uint64 { return d.U3() + 1 })
|
|
d.FieldU8("black_color")
|
|
d.FieldU8("pixel_aspect_ratio")
|
|
|
|
if gcpFollows {
|
|
fieldColorMap(d, "global_color_map", int(bitDepth))
|
|
}
|
|
|
|
d.FieldArray("blocks", func(d *decode.D) {
|
|
blocks:
|
|
for {
|
|
switch d.PeekBits(8) {
|
|
case ';':
|
|
break blocks
|
|
case '!': /* "!" */
|
|
d.FieldStruct("extension_block", func(d *decode.D) {
|
|
d.FieldU8("introducer")
|
|
functionCode := d.FieldU8("function_code", extensionNames, scalar.ActualHex)
|
|
|
|
dataBytes := &bytes.Buffer{}
|
|
|
|
d.FieldArray("func_data_bytes", func(d *decode.D) {
|
|
seenTerminator := false
|
|
for !seenTerminator {
|
|
|
|
d.FieldStruct("func_data_byte", func(d *decode.D) {
|
|
byteCount := d.FieldU8("byte_count")
|
|
b := d.FieldRawLen("data", int64(byteCount*8))
|
|
if d.PeekBits(8) == 0 {
|
|
d.FieldU8("terminator")
|
|
seenTerminator = true
|
|
}
|
|
d.MustCopyBits(dataBytes, d.MustCloneReadSeeker(b))
|
|
})
|
|
}
|
|
})
|
|
|
|
_ = functionCode
|
|
|
|
// TODO: need a FieldStructBitBuf or something
|
|
// switch functionCode {
|
|
// case extensionGraphicalControl:
|
|
// d.FieldFormatBitBuf(
|
|
// "graphics_control",
|
|
// bitio.NewReader(dataBytes.Bytes(), -1),
|
|
// )
|
|
|
|
// }
|
|
})
|
|
case ',':
|
|
d.FieldStruct("image", func(d *decode.D) {
|
|
d.FieldU8("separator_character")
|
|
d.FieldU16("left")
|
|
d.FieldU16("top")
|
|
d.FieldU16("width")
|
|
d.FieldU16("height")
|
|
|
|
localFollows := d.FieldBool("local_color_map_follows")
|
|
d.FieldBool("image_interlaced")
|
|
d.FieldU3("zero")
|
|
d.FieldUFn("bit_depth", func(d *decode.D) uint64 { return d.U3() + 1 })
|
|
d.FieldU8("code_size")
|
|
|
|
if localFollows {
|
|
fieldColorMap(d, "local_color_map", int(bitDepth))
|
|
}
|
|
|
|
d.FieldArray("image_bytes", func(d *decode.D) {
|
|
seenTerminator := false
|
|
for !seenTerminator {
|
|
|
|
d.FieldStruct("func_data_byte", func(d *decode.D) {
|
|
byteCount := d.FieldU8("byte_count")
|
|
d.FieldRawLen("data", int64(byteCount*8))
|
|
if d.PeekBits(8) == 0 {
|
|
d.FieldU8("terminator")
|
|
seenTerminator = true
|
|
}
|
|
})
|
|
}
|
|
})
|
|
})
|
|
default:
|
|
d.Fatalf("unknown block")
|
|
}
|
|
}
|
|
})
|
|
|
|
d.FieldU8("terminator")
|
|
|
|
return nil
|
|
}
|