/
codecs.go
136 lines (116 loc) · 3.11 KB
/
codecs.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
// Copyright 2022 Ronsor Labs. All rights reserved.
package henshin
import (
"bufio"
"image"
"io"
"sort"
"strings"
)
type ErrNoSuchCodec string
func (e ErrNoSuchCodec) Error() string { return "no such codec: " + string(e) }
var (
knownCodecs = map[string]Codec{}
knownCodecAliases = map[string]string{}
)
// Codec is any named image codec.
type Codec interface {
Name() string
New() Codec
}
// CodecWithAliases is an image codec that can have alternate
// names. Use this if you want to support format autodetection
// for multiple file extensions.
type CodecWithAliases interface {
Codec
Aliases() []string
}
// CodecWithParamParser is an image codec that allows getting
// codec-specific parameters from a string.
type CodecWithParamParser interface {
Codec
ParseParams(opt string) (any, error)
}
// Decoder is an image codec that can decode.
type Decoder interface {
Codec
Magic() []string
Decode(io.Reader, *DecodeOptions) (image.Image, error)
DecodeConfig(io.Reader, *DecodeOptions) (image.Config, error)
}
// Encoder is an image codec that can encode.
type Encoder interface {
Codec
Encode(io.Writer, image.Image, *EncodeOptions) error
}
// RegisterCodec registers a new image processing codec.
func RegisterCodec(c Codec) {
knownCodecs[c.Name()] = c
if withAliases, yes := c.(CodecWithAliases); yes {
for _, alias := range withAliases.Aliases() {
knownCodecAliases[alias] = c.Name()
}
}
}
// NewCodec creates a new instance of the codec corresponding
// to the specified name.
func NewCodec(name string) (Codec, error) {
alias, ok := knownCodecAliases[name]
if ok { name = alias }
codec, ok := knownCodecs[name]
if !ok { return nil, ErrNoSuchCodec(name) }
return codec.New(), nil
}
// Codecs returns a list of all codecs.
func Codecs() (ret []Codec) {
for _, codec := range knownCodecs {
ret = append(ret, codec)
}
sort.Slice(ret, func (i, j int) bool {
return strings.Compare(ret[i].Name(), ret[j].Name()) < 0
})
return
}
// peekableReader is an io.Reader that implements the Peek()
// function.
type peekableReader interface {
io.Reader
Peek(n int) ([]byte, error)
}
func match(a []byte, b string) bool {
if len(a) != len(b) { return false }
for i, v := range a {
if v == b[i] { continue }
if b[i] == '?' { continue }
return false
}
return true
}
// Decode decodes an image that has been encoded in a format understood
// by a registered codec.
func Decode(r io.Reader, o *DecodeOptions) (image.Image, error) {
pkr, ok := r.(peekableReader)
if !ok {
pkr = bufio.NewReader(r)
}
for _, v := range knownCodecs {
d, ok := v.(Decoder)
if !ok { continue }
for _, m := range d.Magic() {
toPeek, err := pkr.Peek(len(m))
if err == nil && match(toPeek, m) {
return d.New().(Decoder).Decode(pkr, o)
}
}
}
return nil, ErrNoSuchCodec("unknown")
}
// Encode encodes an image into the registered codec corresponding to
// the specified name.
func Encode(name string, w io.Writer, i image.Image, o *EncodeOptions) error {
codec, err := NewCodec(name)
if err != nil { return err }
encoder, ok := codec.(Encoder)
if !ok { return ErrNoSuchCodec(name) }
return encoder.Encode(w, i, o)
}