1// Copyright 2010 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5package image 6 7import ( 8 "bufio" 9 "errors" 10 "io" 11 "sync" 12 "sync/atomic" 13) 14 15// ErrFormat indicates that decoding encountered an unknown format. 16var ErrFormat = errors.New("image: unknown format") 17 18// A format holds an image format's name, magic header and how to decode it. 19type format struct { 20 name, magic string 21 decode func(io.Reader) (Image, error) 22 decodeConfig func(io.Reader) (Config, error) 23} 24 25// Formats is the list of registered formats. 26var ( 27 formatsMu sync.Mutex 28 atomicFormats atomic.Value 29) 30 31// RegisterFormat registers an image format for use by Decode. 32// Name is the name of the format, like "jpeg" or "png". 33// Magic is the magic prefix that identifies the format's encoding. The magic 34// string can contain "?" wildcards that each match any one byte. 35// Decode is the function that decodes the encoded image. 36// DecodeConfig is the function that decodes just its configuration. 37func RegisterFormat(name, magic string, decode func(io.Reader) (Image, error), decodeConfig func(io.Reader) (Config, error)) { 38 formatsMu.Lock() 39 formats, _ := atomicFormats.Load().([]format) 40 atomicFormats.Store(append(formats, format{name, magic, decode, decodeConfig})) 41 formatsMu.Unlock() 42} 43 44// A reader is an io.Reader that can also peek ahead. 45type reader interface { 46 io.Reader 47 Peek(int) ([]byte, error) 48} 49 50// asReader converts an io.Reader to a reader. 51func asReader(r io.Reader) reader { 52 if rr, ok := r.(reader); ok { 53 return rr 54 } 55 return bufio.NewReader(r) 56} 57 58// Match reports whether magic matches b. Magic may contain "?" wildcards. 59func match(magic string, b []byte) bool { 60 if len(magic) != len(b) { 61 return false 62 } 63 for i, c := range b { 64 if magic[i] != c && magic[i] != '?' { 65 return false 66 } 67 } 68 return true 69} 70 71// Sniff determines the format of r's data. 72func sniff(r reader) format { 73 formats, _ := atomicFormats.Load().([]format) 74 for _, f := range formats { 75 b, err := r.Peek(len(f.magic)) 76 if err == nil && match(f.magic, b) { 77 return f 78 } 79 } 80 return format{} 81} 82 83// Decode decodes an image that has been encoded in a registered format. 84// The string returned is the format name used during format registration. 85// Format registration is typically done by an init function in the codec- 86// specific package. 87func Decode(r io.Reader) (Image, string, error) { 88 rr := asReader(r) 89 f := sniff(rr) 90 if f.decode == nil { 91 return nil, "", ErrFormat 92 } 93 m, err := f.decode(rr) 94 return m, f.name, err 95} 96 97// DecodeConfig decodes the color model and dimensions of an image that has 98// been encoded in a registered format. The string returned is the format name 99// used during format registration. Format registration is typically done by 100// an init function in the codec-specific package. 101func DecodeConfig(r io.Reader) (Config, string, error) { 102 rr := asReader(r) 103 f := sniff(rr) 104 if f.decodeConfig == nil { 105 return Config{}, "", ErrFormat 106 } 107 c, err := f.decodeConfig(rr) 108 return c, f.name, err 109} 110