| | |
| | |
| | |
| |
|
| | package image |
| |
|
| | import ( |
| | "bufio" |
| | "errors" |
| | "io" |
| | "sync" |
| | "sync/atomic" |
| | ) |
| |
|
| | |
| | var ErrFormat = errors.New("image: unknown format") |
| |
|
| | |
| | type format struct { |
| | name, magic string |
| | decode func(io.Reader) (Image, error) |
| | decodeConfig func(io.Reader) (Config, error) |
| | } |
| |
|
| | |
| | var ( |
| | formatsMu sync.Mutex |
| | atomicFormats atomic.Value |
| | ) |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | func RegisterFormat(name, magic string, decode func(io.Reader) (Image, error), decodeConfig func(io.Reader) (Config, error)) { |
| | formatsMu.Lock() |
| | formats, _ := atomicFormats.Load().([]format) |
| | atomicFormats.Store(append(formats, format{name, magic, decode, decodeConfig})) |
| | formatsMu.Unlock() |
| | } |
| |
|
| | |
| | type reader interface { |
| | io.Reader |
| | Peek(int) ([]byte, error) |
| | } |
| |
|
| | |
| | func asReader(r io.Reader) reader { |
| | if rr, ok := r.(reader); ok { |
| | return rr |
| | } |
| | return bufio.NewReader(r) |
| | } |
| |
|
| | |
| | func match(magic string, b []byte) bool { |
| | if len(magic) != len(b) { |
| | return false |
| | } |
| | for i, c := range b { |
| | if magic[i] != c && magic[i] != '?' { |
| | return false |
| | } |
| | } |
| | return true |
| | } |
| |
|
| | |
| | func sniff(r reader) format { |
| | formats, _ := atomicFormats.Load().([]format) |
| | for _, f := range formats { |
| | b, err := r.Peek(len(f.magic)) |
| | if err == nil && match(f.magic, b) { |
| | return f |
| | } |
| | } |
| | return format{} |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | func Decode(r io.Reader) (Image, string, error) { |
| | rr := asReader(r) |
| | f := sniff(rr) |
| | if f.decode == nil { |
| | return nil, "", ErrFormat |
| | } |
| | m, err := f.decode(rr) |
| | return m, f.name, err |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | func DecodeConfig(r io.Reader) (Config, string, error) { |
| | rr := asReader(r) |
| | f := sniff(rr) |
| | if f.decodeConfig == nil { |
| | return Config{}, "", ErrFormat |
| | } |
| | c, err := f.decodeConfig(rr) |
| | return c, f.name, err |
| | } |
| |
|