202 lines
5.6 KiB
Go
202 lines
5.6 KiB
Go
package color
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"gopkg.in/yaml.v3"
|
|
"io"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
type Space struct {
|
|
ChromaSampling ChromaSampling
|
|
ChromaSamplePosition ChromaSamplePosition
|
|
BitDepth byte
|
|
}
|
|
|
|
func (c *Space) UnmarshalJSON(buf []byte) error {
|
|
if len(buf) < 2 {
|
|
return io.ErrUnexpectedEOF
|
|
}
|
|
s, err := NewColorFormatFromString(string(buf[1 : len(buf)-1]))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
*c = s
|
|
return nil
|
|
}
|
|
|
|
func (c *Space) UnmarshalYAML(node *yaml.Node) error {
|
|
s, err := NewColorFormatFromString(node.Value)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
*c = s
|
|
return nil
|
|
}
|
|
|
|
func (c Space) MarshalJSON() ([]byte, error) {
|
|
return []byte("\"" + c.String() + "\""), nil
|
|
}
|
|
|
|
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 MustColorFormatFromString(colorFormat string) Space {
|
|
s, err := NewColorFormatFromString(colorFormat)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return s
|
|
}
|
|
|
|
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")
|
|
)
|