// 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 }