| // Copyright 2010 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package image |
| |
| import ( |
| "bufio" |
| "errors" |
| "io" |
| "sync" |
| "sync/atomic" |
| ) |
| |
| // ErrFormat indicates that decoding encountered an unknown format. |
| var ErrFormat = errors.New("image: unknown format") |
| |
| // A format holds an image format's name, magic header and how to decode it. |
| type format struct { |
| name, magic string |
| decode func(io.Reader) (Image, error) |
| decodeConfig func(io.Reader) (Config, error) |
| } |
| |
| // Formats is the list of registered formats. |
| var ( |
| formatsMu sync.Mutex |
| atomicFormats atomic.Value |
| ) |
| |
| // RegisterFormat registers an image format for use by [Decode]. |
| // Name is the name of the format, like "jpeg" or "png". |
| // Magic is the magic prefix that identifies the format's encoding. The magic |
| // string can contain "?" wildcards that each match any one byte. |
| // [Decode] is the function that decodes the encoded image. |
| // [DecodeConfig] is the function that decodes just its configuration. |
| 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() |
| } |
| |
| // A reader is an io.Reader that can also peek ahead. |
| type reader interface { |
| io.Reader |
| Peek(int) ([]byte, error) |
| } |
| |
| // asReader converts an io.Reader to a reader. |
| func asReader(r io.Reader) reader { |
| if rr, ok := r.(reader); ok { |
| return rr |
| } |
| return bufio.NewReader(r) |
| } |
| |
| // match reports whether magic matches b. Magic may contain "?" wildcards. |
| 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 |
| } |
| |
| // sniff determines the format of r's data. |
| 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{} |
| } |
| |
| // Decode decodes an image that has been encoded in a registered format. |
| // The string returned is the format name used during format registration. |
| // Format registration is typically done by an init function in the codec- |
| // specific package. |
| 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 |
| } |
| |
| // DecodeConfig decodes the color model and dimensions of an image that has |
| // been encoded in a registered format. The string returned is the format name |
| // used during format registration. Format registration is typically done by |
| // an init function in the codec-specific package. |
| 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 |
| } |