1
0
mirror of https://github.com/imgproxy/imgproxy.git synced 2024-11-24 08:12:38 +02:00
imgproxy/imagemeta/image_meta.go
2022-11-28 22:38:17 +06:00

102 lines
1.6 KiB
Go

package imagemeta
import (
"bufio"
"errors"
"io"
"sync"
"sync/atomic"
"github.com/imgproxy/imgproxy/v3/imagetype"
)
type Meta interface {
Format() imagetype.Type
Width() int
Height() int
}
type DecodeMetaFunc func(io.Reader) (Meta, error)
type meta struct {
format imagetype.Type
width, height int
}
func (m *meta) Format() imagetype.Type {
return m.format
}
func (m *meta) Width() int {
return m.width
}
func (m *meta) Height() int {
return m.height
}
type format struct {
magic string
decodeMeta DecodeMetaFunc
}
type reader interface {
io.Reader
Peek(int) ([]byte, error)
}
var (
formatsMu sync.Mutex
atomicFormats atomic.Value
ErrFormat = errors.New("unknown image format")
)
func asReader(r io.Reader) reader {
if rr, ok := r.(reader); ok {
return rr
}
return bufio.NewReader(r)
}
func matchMagic(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
}
func RegisterFormat(magic string, decodeMeta DecodeMetaFunc) {
formatsMu.Lock()
defer formatsMu.Unlock()
formats, _ := atomicFormats.Load().([]format)
atomicFormats.Store(append(formats, format{magic, decodeMeta}))
}
func DecodeMeta(r io.Reader) (Meta, error) {
rr := asReader(r)
formats, _ := atomicFormats.Load().([]format)
for _, f := range formats {
if b, err := rr.Peek(len(f.magic)); err == nil || err == io.EOF {
if matchMagic(f.magic, b) {
return f.decodeMeta(rr)
}
} else {
return nil, err
}
}
if IsSVG(rr) {
return &meta{format: imagetype.SVG, width: 1, height: 1}, nil
}
return nil, ErrFormat
}