package color import ( "errors" "fmt" "strconv" "strings" ) type Space struct { ChromaSampling ChromaSampling ChromaSamplePosition ChromaSamplePosition BitDepth byte } func (c Space) String() string { if c.ChromaSampling.J == 4 && c.ChromaSampling.A == 2 && c.ChromaSampling.B == 0 && c.BitDepth == 8 { switch c.ChromaSamplePosition { case ChromaSamplePositionCenter: return "420jpeg" case ChromaSamplePositionTopLeft: return "420paldv" case ChromaSamplePositionLeft: return "420mpeg2" } } if c.ChromaSampling.J == 4 && c.ChromaSampling.A == 0 && c.ChromaSampling.B == 0 { return fmt.Sprintf("mono%d", c.BitDepth) } return fmt.Sprintf("%d%d%dp%d", c.ChromaSampling.J, c.ChromaSampling.A, c.ChromaSampling.B, c.BitDepth) } func (c Space) FrameSizePacked(width, height int) (int, error) { a1 := int64(width) * int64(height) a2 := int64(c.ChromaSampling.ElementPixels()) if a1%a2 != 0 { return 0, errors.New("not divisible pixels") } a3 := (a1 / a2) * int64(c.ChromaSampling.ElementSamples()) * int64(c.BitDepth) if a3%8 != 0 { return 0, errors.New("not divisible size") } return int(a3 / 8), nil } func (c Space) FrameSize(width, height int) (int, error) { actualBitDepth := int64(c.BitDepth) if actualBitDepth&0b111 != 0 { actualBitDepth = ((actualBitDepth + 8) >> 3) << 3 } a1 := int64(width) * int64(height) a2 := int64(c.ChromaSampling.ElementPixels()) if a1%a2 != 0 { return 0, errors.New("not divisible pixels") } a3 := (a1 / a2) * int64(c.ChromaSampling.ElementSamples()) * actualBitDepth if a3%8 != 0 { return 0, errors.New("not divisible size") } return int(a3 / 8), nil } func (c Space) check() error { if c.ChromaSampling.J <= 0 { return fmt.Errorf("unsupported J %d", c.ChromaSampling.J) } if c.ChromaSampling.A < 0 && c.ChromaSampling.A > c.ChromaSampling.J { return fmt.Errorf("unsupported A %d", c.ChromaSampling.A) } if c.ChromaSampling.B != 0 && c.ChromaSampling.B != c.ChromaSampling.A { return fmt.Errorf("unsupported B %d", c.ChromaSampling.B) } if c.BitDepth != 8 && c.BitDepth != 10 && c.BitDepth != 12 && c.BitDepth != 14 && c.BitDepth != 16 { return fmt.Errorf("unsupported BitDepth %d", c.BitDepth) } return nil } func NewColorFormatFromString(colorFormat string) (Space, error) { colorFormat = strings.ToLower(colorFormat) if colorFormat == "420paldv" { c, err := NewColorFormatFromString("420p8") c.ChromaSamplePosition = ChromaSamplePositionTopLeft return c, err } else if colorFormat == "420mpeg2" { c, err := NewColorFormatFromString("420p8") c.ChromaSamplePosition = ChromaSamplePositionLeft return c, err } else if colorFormat == "420" || colorFormat == "420jpeg" || colorFormat == "420j" { c, err := NewColorFormatFromString("420p8") c.ChromaSamplePosition = ChromaSamplePositionCenter return c, err } else if colorFormat == "mono" { return NewColorFormatFromString("400p8") } else if colorFormat == "mono10" { return NewColorFormatFromString("400p10") } else if colorFormat == "mono12" { return NewColorFormatFromString("400p12") } else if colorFormat == "mono14" { return NewColorFormatFromString("400p14") } else if colorFormat == "mono16" { return NewColorFormatFromString("400p16") } splits := strings.Split(colorFormat, "p") if len(splits) == 1 { //default 8 bit splits = append(splits, "8") } space := Space{ ChromaSamplePosition: ChromaSamplePositionUnspecified, } switch strings.ReplaceAll(splits[0], ":", "") { case "400": space.ChromaSampling.J = 4 space.ChromaSampling.A = 0 space.ChromaSampling.B = 0 case "420": space.ChromaSampling.J = 4 space.ChromaSampling.A = 2 space.ChromaSampling.B = 0 case "422": space.ChromaSampling.J = 4 space.ChromaSampling.A = 2 space.ChromaSampling.B = 2 case "444": space.ChromaSampling.J = 4 space.ChromaSampling.A = 4 space.ChromaSampling.B = 4 default: return space, fmt.Errorf("unsupported Chroma ChromaSampling %s", splits[0]) } n, err := strconv.Atoi(splits[1]) if err != nil { return space, err } space.BitDepth = byte(n) return space, space.check() } func newColorFormatFromStringInternal(colorFormat string) Space { space, _ := NewColorFormatFromString(colorFormat) return space } var ( Space420 Space = newColorFormatFromStringInternal("420") Space422 Space = newColorFormatFromStringInternal("422") Space444 Space = newColorFormatFromStringInternal("444") Space420P10 Space = newColorFormatFromStringInternal("420p10") Space422P10 Space = newColorFormatFromStringInternal("422p10") Space444P10 Space = newColorFormatFromStringInternal("444p10") Space420P12 Space = newColorFormatFromStringInternal("420p12") Space422P12 Space = newColorFormatFromStringInternal("422p12") Space444P12 Space = newColorFormatFromStringInternal("444p12") Space420P16 Space = newColorFormatFromStringInternal("420p16") Space422P16 Space = newColorFormatFromStringInternal("422p16") Space444P16 Space = newColorFormatFromStringInternal("444p16") )