1
1
mirror of https://github.com/wader/fq.git synced 2024-10-04 07:27:08 +03:00
fq/format/jpeg/jp2c.go
2024-04-10 23:54:22 +02:00

145 lines
4.3 KiB
Go

package jpeg
import (
"github.com/wader/fq/format"
"github.com/wader/fq/pkg/decode"
"github.com/wader/fq/pkg/interp"
"github.com/wader/fq/pkg/scalar"
)
func init() {
interp.RegisterFormat(
format.JP2C,
&decode.Format{
Description: "JPEG 2000 codestream",
Groups: []*decode.Group{format.Probe, format.Image},
DecodeFn: jp2cDecode,
RootName: "segments",
RootArray: true,
})
}
const (
JP2_SOC = 0xff_4f /* start of codestream */
JP2_SOT = 0xff_90 /* start of tile */
JP2_SOD = 0xff_93 /* start of data */
JP2_EOC = 0xff_d9 /* end of codestream */
/* fixed information segment */
JP2_SIZ = 0xff_51 /* image and tile size */
/* functional segments */
JP2_COD = 0xff_52 /* coding style default */
JP2_COC = 0xff_53 /* coding style component */
JP2_RGN = 0xff_5e /* region of interest */
JP2_QCD = 0xff_5c /* quantization default */
JP2_QCC = 0xff_5d /* quantization component */
JP2_POC = 0xff_5f /* progression order change */
/* pointer segments */
JP2_TLM = 0xff_55 /* tile-part lengths */
JP2_PLM = 0xff_57 /* packet length (main header) */
JP2_PLT = 0xff_58 /* packet length (tile-part header) */
JP2_PPM = 0xff_60 /* packed packet headers (main header) */
JP2_PPT = 0xff_61 /* packet packet headers (tile-part header) */
/* bitstream internal markers and segments */
JP2_SOP = 0xff_91 /* start of packet */
JP2_EPH = 0xff_92 /* end of packet header */
/* informational segments */
JP2_CRG = 0xff_63 /* component registration */
JP2_COM = 0xff_64 /* comment */
)
var jp2Markers = scalar.UintMap{
JP2_SOC: {Sym: "soc", Description: "Start of codestream"},
JP2_SOT: {Sym: "sot", Description: "Start of tile"},
JP2_SOD: {Sym: "sod", Description: "Start of data"},
JP2_EOC: {Sym: "eoc", Description: "End of codestream"},
JP2_SIZ: {Sym: "siz", Description: "Image and tile size"},
JP2_COD: {Sym: "cod", Description: "Coding style default"},
JP2_COC: {Sym: "coc", Description: "Coding style component"},
JP2_RGN: {Sym: "rgn", Description: "Region of interest"},
JP2_QCD: {Sym: "qcd", Description: "Quantization default"},
JP2_QCC: {Sym: "qcc", Description: "Quantization component"},
JP2_POC: {Sym: "poc", Description: "Progression order change"},
JP2_TLM: {Sym: "tlm", Description: "Tile-part lengths"},
JP2_PLM: {Sym: "plm", Description: "Packet length (main header)"},
JP2_PLT: {Sym: "plt", Description: "Packet length (tile-part header)"},
JP2_PPM: {Sym: "ppm", Description: "Packed packet headers (main header)"},
JP2_PPT: {Sym: "ppt", Description: "Packet packet headers (tile-part header)"},
JP2_SOP: {Sym: "sop", Description: "Start of packet"},
JP2_EPH: {Sym: "eph", Description: "End of packet header"},
JP2_CRG: {Sym: "crg", Description: "Component registration"},
JP2_COM: {Sym: "com", Description: "Comment"},
}
func jp2cDecode(d *decode.D) any {
if d.PeekUintBits(16) != JP2_SOC {
d.Fatalf("no SOC marker")
}
seenSOC := false
seenSIZ := false
seenEOC := false
for !seenEOC && !d.End() {
d.FieldStruct("segment", func(d *decode.D) {
marker := d.FieldU16("marker", jp2Markers, scalar.UintHex)
switch marker {
case JP2_SOC:
// zero length
seenSOC = true
return
case JP2_SOD:
l, _ := d.PeekFind(16, 8, d.BitsLeft(), func(v uint64) bool {
return v == JP2_SOT || v == JP2_EOC
})
d.FieldRawLen("data", l)
case JP2_SOT:
d.FieldU16("l_sot")
d.FieldU16("i_sot")
d.FieldU32("p_sot")
d.FieldU8("tp_sot")
d.FieldU8("tn_sot")
case JP2_SIZ:
seenSIZ = true
d.FieldU16("l_siz")
d.FieldU16("r_siz")
d.FieldU32("x_siz")
d.FieldU32("y_siz")
d.FieldU32("xo_siz")
d.FieldU32("yo_siz")
d.FieldU32("xt_siz")
d.FieldU32("yt_siz")
d.FieldU32("xto_siz")
d.FieldU32("yto_siz")
cSiz := d.FieldU16("c_siz")
d.FieldArray("components", func(d *decode.D) {
for i := 0; i < int(cSiz); i++ {
d.FieldStruct("component", func(d *decode.D) {
d.FieldU8("s_sizi")
d.FieldU8("xr_sizi")
d.FieldU8("yr_sizi")
})
}
})
case JP2_COM:
length := d.FieldU16("length")
d.FieldU16("r_cme")
d.FieldUTF8("data", int(length-4))
case JP2_EOC:
// zero length
seenEOC = true
return
default:
length := d.FieldU16("length")
d.FieldRawLen("data", int64(length-2)*8)
}
})
}
if !(seenSOC && seenSIZ && seenEOC) {
d.Fatalf("SOC, SIZ or EOC marker not found")
}
return nil
}