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