Source file src/image/format.go
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 5 package image 6 7 import ( 8 "bufio" 9 "errors" 10 "io" 11 "sync" 12 "sync/atomic" 13 ) 14 15 // ErrFormat indicates that decoding encountered an unknown format. 16 var ErrFormat = errors.New("image: unknown format") 17 18 // A format holds an image format's name, magic header and how to decode it. 19 type 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. 26 var ( 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. 37 func 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. 45 type reader interface { 46 io.Reader 47 Peek(int) ([]byte, error) 48 } 49 50 // asReader converts an io.Reader to a reader. 51 func 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. 59 func 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. 72 func 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. 87 // 88 // Decoding may allocate memory proportional to the width and height in the 89 // image header before all pixel data is consumed or validated. When 90 // decoding untrusted input, call [DecodeConfig] first to inspect dimensions 91 // and reject images that would exceed resource limits; see the "Security 92 // Considerations" section in the [image] package documentation. 93 func Decode(r io.Reader) (Image, string, error) { 94 rr := asReader(r) 95 f := sniff(rr) 96 if f.decode == nil { 97 return nil, "", ErrFormat 98 } 99 m, err := f.decode(rr) 100 return m, f.name, err 101 } 102 103 // DecodeConfig decodes the color model and dimensions of an image that has 104 // been encoded in a registered format. The string returned is the format name 105 // used during format registration. Format registration is typically done by 106 // an init function in the codec-specific package. 107 // 108 // DecodeConfig reads only format headers and does not allocate a full-size 109 // pixel buffer, so it can be used to check dimensions before calling [Decode]. 110 func DecodeConfig(r io.Reader) (Config, string, error) { 111 rr := asReader(r) 112 f := sniff(rr) 113 if f.decodeConfig == nil { 114 return Config{}, "", ErrFormat 115 } 116 c, err := f.decodeConfig(rr) 117 return c, f.name, err 118 } 119