github.com/ice-blockchain/go/src@v0.0.0-20240403114104-1564d284e521/image/format.go (about) 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 func 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. 101 func 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 }