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