mirror of
https://github.com/golang/go
synced 2024-11-21 19:54:41 -07:00
image: introduce Config type and DecodeConfig function, to decode an
image's color model and dimensions without allocating and decoding its actual pixels. Fixes #695. R=r CC=golang-dev https://golang.org/cl/2151042
This commit is contained in:
parent
1a5d3c224d
commit
2b4218f321
@ -15,8 +15,9 @@ var UnknownFormatErr = os.NewError("image: unknown format")
|
|||||||
|
|
||||||
// A format holds an image format's name, magic header and how to decode it.
|
// A format holds an image format's name, magic header and how to decode it.
|
||||||
type format struct {
|
type format struct {
|
||||||
name, magic string
|
name, magic string
|
||||||
decode func(io.Reader) (Image, os.Error)
|
decode func(io.Reader) (Image, os.Error)
|
||||||
|
decodeConfig func(io.Reader) (Config, os.Error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Formats is the list of registered formats.
|
// Formats is the list of registered formats.
|
||||||
@ -26,7 +27,8 @@ var formats []format
|
|||||||
// Name is the name of the format, like "jpeg" or "png".
|
// Name is the name of the format, like "jpeg" or "png".
|
||||||
// Magic is the magic prefix that identifies the format's encoding.
|
// Magic is the magic prefix that identifies the format's encoding.
|
||||||
// Decode is the function that decodes the encoded image.
|
// Decode is the function that decodes the encoded image.
|
||||||
func RegisterFormat(name, magic string, decode func(io.Reader) (Image, os.Error)) {
|
// DecodeConfig is the function that decodes just its configuration.
|
||||||
|
func RegisterFormat(name, magic string, decode func(io.Reader) (Image, os.Error), decodeConfig func(io.Reader) (Config, os.Error)) {
|
||||||
n := len(formats)
|
n := len(formats)
|
||||||
if n == cap(formats) {
|
if n == cap(formats) {
|
||||||
x := make([]format, n+1, 2*n+4)
|
x := make([]format, n+1, 2*n+4)
|
||||||
@ -35,7 +37,7 @@ func RegisterFormat(name, magic string, decode func(io.Reader) (Image, os.Error)
|
|||||||
} else {
|
} else {
|
||||||
formats = formats[0 : n+1]
|
formats = formats[0 : n+1]
|
||||||
}
|
}
|
||||||
formats[n] = format{name, magic, decode}
|
formats[n] = format{name, magic, decode, decodeConfig}
|
||||||
}
|
}
|
||||||
|
|
||||||
// A reader is an io.Reader that can also peek ahead.
|
// A reader is an io.Reader that can also peek ahead.
|
||||||
@ -52,22 +54,41 @@ func asReader(r io.Reader) reader {
|
|||||||
return bufio.NewReader(r)
|
return bufio.NewReader(r)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decode decodes an image that has been encoded in a registered format.
|
// sniff determines the format of r's data.
|
||||||
// Format registration is typically done by the init method of the codec-
|
func sniff(r reader) format {
|
||||||
// specific package.
|
for _, f := range formats {
|
||||||
func Decode(r io.Reader) (m Image, formatName string, err os.Error) {
|
s, err := r.Peek(len(f.magic))
|
||||||
var f format
|
if err == nil && string(s) == f.magic {
|
||||||
rr := asReader(r)
|
return f
|
||||||
for _, g := range formats {
|
|
||||||
s, err := rr.Peek(len(g.magic))
|
|
||||||
if err == nil && string(s) == g.magic {
|
|
||||||
f = g
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return format{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode decodes an image that has been encoded in a registered format.
|
||||||
|
// The string returned is the format name used during format registration.
|
||||||
|
// Format registration is typically done by the init method of the codec-
|
||||||
|
// specific package.
|
||||||
|
func Decode(r io.Reader) (Image, string, os.Error) {
|
||||||
|
rr := asReader(r)
|
||||||
|
f := sniff(rr)
|
||||||
if f.decode == nil {
|
if f.decode == nil {
|
||||||
return nil, "", UnknownFormatErr
|
return nil, "", UnknownFormatErr
|
||||||
}
|
}
|
||||||
m, err = f.decode(rr)
|
m, err := f.decode(rr)
|
||||||
return m, f.name, err
|
return m, f.name, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DecodeConfig decodes the color model and dimensions of an image that has
|
||||||
|
// been encoded in a registered format. The string returned is the format name
|
||||||
|
// used during format registration. Format registration is typically done by
|
||||||
|
// the init method of the codec-specific package.
|
||||||
|
func DecodeConfig(r io.Reader) (Config, string, os.Error) {
|
||||||
|
rr := asReader(r)
|
||||||
|
f := sniff(rr)
|
||||||
|
if f.decodeConfig == nil {
|
||||||
|
return Config{}, "", UnknownFormatErr
|
||||||
|
}
|
||||||
|
c, err := f.decodeConfig(rr)
|
||||||
|
return c, f.name, err
|
||||||
|
}
|
||||||
|
@ -5,6 +5,12 @@
|
|||||||
// The image package implements a basic 2-D image library.
|
// The image package implements a basic 2-D image library.
|
||||||
package image
|
package image
|
||||||
|
|
||||||
|
// A Config consists of an image's color model and dimensions.
|
||||||
|
type Config struct {
|
||||||
|
ColorModel ColorModel
|
||||||
|
Width, Height int
|
||||||
|
}
|
||||||
|
|
||||||
// An Image is a finite rectangular grid of Colors drawn from a ColorModel.
|
// An Image is a finite rectangular grid of Colors drawn from a ColorModel.
|
||||||
type Image interface {
|
type Image interface {
|
||||||
// ColorModel returns the Image's ColorModel.
|
// ColorModel returns the Image's ColorModel.
|
||||||
|
@ -147,7 +147,6 @@ func (d *decoder) processSOF(n int) os.Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
d.image = image.NewRGBA(d.width, d.height)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -240,7 +239,7 @@ func (d *decoder) convertMCU(mx, my, h0, v0 int) {
|
|||||||
// Specified in section B.2.3.
|
// Specified in section B.2.3.
|
||||||
func (d *decoder) processSOS(n int) os.Error {
|
func (d *decoder) processSOS(n int) os.Error {
|
||||||
if d.image == nil {
|
if d.image == nil {
|
||||||
return FormatError("missing SOF segment")
|
d.image = image.NewRGBA(d.width, d.height)
|
||||||
}
|
}
|
||||||
if n != 4+2*nComponent {
|
if n != 4+2*nComponent {
|
||||||
return UnsupportedError("SOS has wrong length")
|
return UnsupportedError("SOS has wrong length")
|
||||||
@ -365,9 +364,8 @@ func (d *decoder) processDRI(n int) os.Error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decode reads a JPEG formatted image from r and returns it as an image.Image.
|
// decode reads a JPEG image from r and returns it as an image.Image.
|
||||||
func Decode(r io.Reader) (image.Image, os.Error) {
|
func (d *decoder) decode(r io.Reader, configOnly bool) (image.Image, os.Error) {
|
||||||
var d decoder
|
|
||||||
if rr, ok := r.(Reader); ok {
|
if rr, ok := r.(Reader); ok {
|
||||||
d.r = rr
|
d.r = rr
|
||||||
} else {
|
} else {
|
||||||
@ -411,6 +409,9 @@ func Decode(r io.Reader) (image.Image, os.Error) {
|
|||||||
switch {
|
switch {
|
||||||
case marker == sof0Marker: // Start Of Frame (Baseline).
|
case marker == sof0Marker: // Start Of Frame (Baseline).
|
||||||
err = d.processSOF(n)
|
err = d.processSOF(n)
|
||||||
|
if configOnly {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
case marker == sof2Marker: // Start Of Frame (Progressive).
|
case marker == sof2Marker: // Start Of Frame (Progressive).
|
||||||
err = UnsupportedError("progressive mode")
|
err = UnsupportedError("progressive mode")
|
||||||
case marker == dhtMarker: // Define Huffman Table.
|
case marker == dhtMarker: // Define Huffman Table.
|
||||||
@ -433,6 +434,22 @@ func Decode(r io.Reader) (image.Image, os.Error) {
|
|||||||
return d.image, nil
|
return d.image, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
// Decode reads a JPEG image from r and returns it as an image.Image.
|
||||||
image.RegisterFormat("jpeg", "\xff\xd8", Decode)
|
func Decode(r io.Reader) (image.Image, os.Error) {
|
||||||
|
var d decoder
|
||||||
|
return d.decode(r, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeConfig returns the color model and dimensions of a JPEG image without
|
||||||
|
// decoding the entire image.
|
||||||
|
func DecodeConfig(r io.Reader) (image.Config, os.Error) {
|
||||||
|
var d decoder
|
||||||
|
if _, err := d.decode(r, true); err != nil {
|
||||||
|
return image.Config{}, err
|
||||||
|
}
|
||||||
|
return image.Config{image.RGBAColorModel, d.width, d.height}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
image.RegisterFormat("jpeg", "\xff\xd8", Decode, DecodeConfig)
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,8 @@ const (
|
|||||||
|
|
||||||
// A cb is a combination of color type and bit depth.
|
// A cb is a combination of color type and bit depth.
|
||||||
const (
|
const (
|
||||||
cbG8 = iota
|
cbInvalid = iota
|
||||||
|
cbG8
|
||||||
cbTC8
|
cbTC8
|
||||||
cbP8
|
cbP8
|
||||||
cbTCA8
|
cbTCA8
|
||||||
@ -62,20 +63,25 @@ const (
|
|||||||
|
|
||||||
const pngHeader = "\x89PNG\r\n\x1a\n"
|
const pngHeader = "\x89PNG\r\n\x1a\n"
|
||||||
|
|
||||||
|
type imgOrErr struct {
|
||||||
|
img image.Image
|
||||||
|
err os.Error
|
||||||
|
}
|
||||||
|
|
||||||
type decoder struct {
|
type decoder struct {
|
||||||
width, height int
|
width, height int
|
||||||
image image.Image
|
palette image.PalettedColorModel
|
||||||
cb int
|
cb int
|
||||||
stage int
|
stage int
|
||||||
idatWriter io.WriteCloser
|
idatWriter io.WriteCloser
|
||||||
idatDone chan os.Error
|
idatDone chan imgOrErr
|
||||||
tmp [3 * 256]byte
|
tmp [3 * 256]byte
|
||||||
}
|
}
|
||||||
|
|
||||||
// A FormatError reports that the input is not a valid PNG.
|
// A FormatError reports that the input is not a valid PNG.
|
||||||
type FormatError string
|
type FormatError string
|
||||||
|
|
||||||
func (e FormatError) String() string { return "invalid PNG format: " + string(e) }
|
func (e FormatError) String() string { return "png: invalid format: " + string(e) }
|
||||||
|
|
||||||
var chunkOrderError = FormatError("chunk out of order")
|
var chunkOrderError = FormatError("chunk out of order")
|
||||||
|
|
||||||
@ -84,12 +90,12 @@ type IDATDecodingError struct {
|
|||||||
Err os.Error
|
Err os.Error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e IDATDecodingError) String() string { return "IDAT decoding error: " + e.Err.String() }
|
func (e IDATDecodingError) String() string { return "png: IDAT decoding error: " + e.Err.String() }
|
||||||
|
|
||||||
// An UnsupportedError reports that the input uses a valid but unimplemented PNG feature.
|
// An UnsupportedError reports that the input uses a valid but unimplemented PNG feature.
|
||||||
type UnsupportedError string
|
type UnsupportedError string
|
||||||
|
|
||||||
func (e UnsupportedError) String() string { return "unsupported PNG feature: " + string(e) }
|
func (e UnsupportedError) String() string { return "png: unsupported feature: " + string(e) }
|
||||||
|
|
||||||
// Big-endian.
|
// Big-endian.
|
||||||
func parseUint32(b []uint8) uint32 {
|
func parseUint32(b []uint8) uint32 {
|
||||||
@ -131,36 +137,30 @@ func (d *decoder) parseIHDR(r io.Reader, crc hash.Hash32, length uint32) os.Erro
|
|||||||
if nPixels != int64(int(nPixels)) {
|
if nPixels != int64(int(nPixels)) {
|
||||||
return UnsupportedError("dimension overflow")
|
return UnsupportedError("dimension overflow")
|
||||||
}
|
}
|
||||||
|
d.cb = cbInvalid
|
||||||
switch d.tmp[8] {
|
switch d.tmp[8] {
|
||||||
case 8:
|
case 8:
|
||||||
switch d.tmp[9] {
|
switch d.tmp[9] {
|
||||||
case ctGrayscale:
|
case ctGrayscale:
|
||||||
d.image = image.NewGray(int(w), int(h))
|
|
||||||
d.cb = cbG8
|
d.cb = cbG8
|
||||||
case ctTrueColor:
|
case ctTrueColor:
|
||||||
d.image = image.NewRGBA(int(w), int(h))
|
|
||||||
d.cb = cbTC8
|
d.cb = cbTC8
|
||||||
case ctPaletted:
|
case ctPaletted:
|
||||||
d.image = image.NewPaletted(int(w), int(h), nil)
|
|
||||||
d.cb = cbP8
|
d.cb = cbP8
|
||||||
case ctTrueColorAlpha:
|
case ctTrueColorAlpha:
|
||||||
d.image = image.NewNRGBA(int(w), int(h))
|
|
||||||
d.cb = cbTCA8
|
d.cb = cbTCA8
|
||||||
}
|
}
|
||||||
case 16:
|
case 16:
|
||||||
switch d.tmp[9] {
|
switch d.tmp[9] {
|
||||||
case ctGrayscale:
|
case ctGrayscale:
|
||||||
d.image = image.NewGray16(int(w), int(h))
|
|
||||||
d.cb = cbG16
|
d.cb = cbG16
|
||||||
case ctTrueColor:
|
case ctTrueColor:
|
||||||
d.image = image.NewRGBA64(int(w), int(h))
|
|
||||||
d.cb = cbTC16
|
d.cb = cbTC16
|
||||||
case ctTrueColorAlpha:
|
case ctTrueColorAlpha:
|
||||||
d.image = image.NewNRGBA64(int(w), int(h))
|
|
||||||
d.cb = cbTCA16
|
d.cb = cbTCA16
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if d.image == nil {
|
if d.cb == cbInvalid {
|
||||||
return UnsupportedError(fmt.Sprintf("bit depth %d, color type %d", d.tmp[8], d.tmp[9]))
|
return UnsupportedError(fmt.Sprintf("bit depth %d, color type %d", d.tmp[8], d.tmp[9]))
|
||||||
}
|
}
|
||||||
d.width, d.height = int(w), int(h)
|
d.width, d.height = int(w), int(h)
|
||||||
@ -179,11 +179,10 @@ func (d *decoder) parsePLTE(r io.Reader, crc hash.Hash32, length uint32) os.Erro
|
|||||||
crc.Write(d.tmp[0:n])
|
crc.Write(d.tmp[0:n])
|
||||||
switch d.cb {
|
switch d.cb {
|
||||||
case cbP8:
|
case cbP8:
|
||||||
palette := make([]image.Color, np)
|
d.palette = image.PalettedColorModel(make([]image.Color, np))
|
||||||
for i := 0; i < np; i++ {
|
for i := 0; i < np; i++ {
|
||||||
palette[i] = image.RGBAColor{d.tmp[3*i+0], d.tmp[3*i+1], d.tmp[3*i+2], 0xff}
|
d.palette[i] = image.RGBAColor{d.tmp[3*i+0], d.tmp[3*i+1], d.tmp[3*i+2], 0xff}
|
||||||
}
|
}
|
||||||
d.image.(*image.Paletted).Palette = image.PalettedColorModel(palette)
|
|
||||||
case cbTC8, cbTCA8, cbTC16, cbTCA16:
|
case cbTC8, cbTCA8, cbTC16, cbTCA16:
|
||||||
// As per the PNG spec, a PLTE chunk is optional (and for practical purposes,
|
// As per the PNG spec, a PLTE chunk is optional (and for practical purposes,
|
||||||
// ignorable) for the ctTrueColor and ctTrueColorAlpha color types (section 4.1.2).
|
// ignorable) for the ctTrueColor and ctTrueColorAlpha color types (section 4.1.2).
|
||||||
@ -208,13 +207,12 @@ func (d *decoder) parsetRNS(r io.Reader, crc hash.Hash32, length uint32) os.Erro
|
|||||||
case cbTC8, cbTC16:
|
case cbTC8, cbTC16:
|
||||||
return UnsupportedError("truecolor transparency")
|
return UnsupportedError("truecolor transparency")
|
||||||
case cbP8:
|
case cbP8:
|
||||||
p := d.image.(*image.Paletted).Palette
|
if n > len(d.palette) {
|
||||||
if n > len(p) {
|
|
||||||
return FormatError("bad tRNS length")
|
return FormatError("bad tRNS length")
|
||||||
}
|
}
|
||||||
for i := 0; i < n; i++ {
|
for i := 0; i < n; i++ {
|
||||||
rgba := p[i].(image.RGBAColor)
|
rgba := d.palette[i].(image.RGBAColor)
|
||||||
p[i] = image.RGBAColor{rgba.R, rgba.G, rgba.B, d.tmp[i]}
|
d.palette[i] = image.RGBAColor{rgba.R, rgba.G, rgba.B, d.tmp[i]}
|
||||||
}
|
}
|
||||||
case cbTCA8, cbTCA16:
|
case cbTCA8, cbTCA16:
|
||||||
return FormatError("tRNS, color type mismatch")
|
return FormatError("tRNS, color type mismatch")
|
||||||
@ -236,10 +234,10 @@ func paeth(a, b, c uint8) uint8 {
|
|||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *decoder) idatReader(idat io.Reader) os.Error {
|
func (d *decoder) idatReader(idat io.Reader) (image.Image, os.Error) {
|
||||||
r, err := zlib.NewReader(idat)
|
r, err := zlib.NewReader(idat)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer r.Close()
|
defer r.Close()
|
||||||
bpp := 0 // Bytes per pixel.
|
bpp := 0 // Bytes per pixel.
|
||||||
@ -252,30 +250,38 @@ func (d *decoder) idatReader(idat io.Reader) os.Error {
|
|||||||
gray16 *image.Gray16
|
gray16 *image.Gray16
|
||||||
rgba64 *image.RGBA64
|
rgba64 *image.RGBA64
|
||||||
nrgba64 *image.NRGBA64
|
nrgba64 *image.NRGBA64
|
||||||
|
img image.Image
|
||||||
)
|
)
|
||||||
switch d.cb {
|
switch d.cb {
|
||||||
case cbG8:
|
case cbG8:
|
||||||
bpp = 1
|
bpp = 1
|
||||||
gray = d.image.(*image.Gray)
|
gray = image.NewGray(d.width, d.height)
|
||||||
|
img = gray
|
||||||
case cbTC8:
|
case cbTC8:
|
||||||
bpp = 3
|
bpp = 3
|
||||||
rgba = d.image.(*image.RGBA)
|
rgba = image.NewRGBA(d.width, d.height)
|
||||||
|
img = rgba
|
||||||
case cbP8:
|
case cbP8:
|
||||||
bpp = 1
|
bpp = 1
|
||||||
paletted = d.image.(*image.Paletted)
|
paletted = image.NewPaletted(d.width, d.height, d.palette)
|
||||||
maxPalette = uint8(len(paletted.Palette) - 1)
|
img = paletted
|
||||||
|
maxPalette = uint8(len(d.palette) - 1)
|
||||||
case cbTCA8:
|
case cbTCA8:
|
||||||
bpp = 4
|
bpp = 4
|
||||||
nrgba = d.image.(*image.NRGBA)
|
nrgba = image.NewNRGBA(d.width, d.height)
|
||||||
|
img = nrgba
|
||||||
case cbG16:
|
case cbG16:
|
||||||
bpp = 2
|
bpp = 2
|
||||||
gray16 = d.image.(*image.Gray16)
|
gray16 = image.NewGray16(d.width, d.height)
|
||||||
|
img = gray16
|
||||||
case cbTC16:
|
case cbTC16:
|
||||||
bpp = 6
|
bpp = 6
|
||||||
rgba64 = d.image.(*image.RGBA64)
|
rgba64 = image.NewRGBA64(d.width, d.height)
|
||||||
|
img = rgba64
|
||||||
case cbTCA16:
|
case cbTCA16:
|
||||||
bpp = 8
|
bpp = 8
|
||||||
nrgba64 = d.image.(*image.NRGBA64)
|
nrgba64 = image.NewNRGBA64(d.width, d.height)
|
||||||
|
img = nrgba64
|
||||||
}
|
}
|
||||||
// cr and pr are the bytes for the current and previous row.
|
// cr and pr are the bytes for the current and previous row.
|
||||||
// The +1 is for the per-row filter type, which is at cr[0].
|
// The +1 is for the per-row filter type, which is at cr[0].
|
||||||
@ -286,7 +292,7 @@ func (d *decoder) idatReader(idat io.Reader) os.Error {
|
|||||||
// Read the decompressed bytes.
|
// Read the decompressed bytes.
|
||||||
_, err := io.ReadFull(r, cr)
|
_, err := io.ReadFull(r, cr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply the filter.
|
// Apply the filter.
|
||||||
@ -318,7 +324,7 @@ func (d *decoder) idatReader(idat io.Reader) os.Error {
|
|||||||
cdat[i] += paeth(cdat[i-bpp], pdat[i], pdat[i-bpp])
|
cdat[i] += paeth(cdat[i-bpp], pdat[i], pdat[i-bpp])
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return FormatError("bad filter type")
|
return nil, FormatError("bad filter type")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert from bytes to colors.
|
// Convert from bytes to colors.
|
||||||
@ -334,7 +340,7 @@ func (d *decoder) idatReader(idat io.Reader) os.Error {
|
|||||||
case cbP8:
|
case cbP8:
|
||||||
for x := 0; x < d.width; x++ {
|
for x := 0; x < d.width; x++ {
|
||||||
if cdat[x] > maxPalette {
|
if cdat[x] > maxPalette {
|
||||||
return FormatError("palette index out of range")
|
return nil, FormatError("palette index out of range")
|
||||||
}
|
}
|
||||||
paletted.SetColorIndex(x, y, cdat[x])
|
paletted.SetColorIndex(x, y, cdat[x])
|
||||||
}
|
}
|
||||||
@ -367,7 +373,7 @@ func (d *decoder) idatReader(idat io.Reader) os.Error {
|
|||||||
// The current row for y is the previous row for y+1.
|
// The current row for y is the previous row for y+1.
|
||||||
pr, cr = cr, pr
|
pr, cr = cr, pr
|
||||||
}
|
}
|
||||||
return nil
|
return img, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *decoder) parseIDAT(r io.Reader, crc hash.Hash32, length uint32) os.Error {
|
func (d *decoder) parseIDAT(r io.Reader, crc hash.Hash32, length uint32) os.Error {
|
||||||
@ -379,14 +385,14 @@ func (d *decoder) parseIDAT(r io.Reader, crc hash.Hash32, length uint32) os.Erro
|
|||||||
if d.idatWriter == nil {
|
if d.idatWriter == nil {
|
||||||
pr, pw := io.Pipe()
|
pr, pw := io.Pipe()
|
||||||
d.idatWriter = pw
|
d.idatWriter = pw
|
||||||
d.idatDone = make(chan os.Error)
|
d.idatDone = make(chan imgOrErr)
|
||||||
go func() {
|
go func() {
|
||||||
err := d.idatReader(pr)
|
img, err := d.idatReader(pr)
|
||||||
if err == os.EOF {
|
if err == os.EOF {
|
||||||
err = FormatError("too little IDAT")
|
err = FormatError("too little IDAT")
|
||||||
}
|
}
|
||||||
pr.CloseWithError(FormatError("too much IDAT"))
|
pr.CloseWithError(FormatError("too much IDAT"))
|
||||||
d.idatDone <- err
|
d.idatDone <- imgOrErr{img, err}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
var buf [4096]byte
|
var buf [4096]byte
|
||||||
@ -509,7 +515,7 @@ func (d *decoder) checkHeader(r io.Reader) os.Error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decode reads a PNG formatted image from r and returns it as an image.Image.
|
// Decode reads a PNG image from r and returns it as an image.Image.
|
||||||
// The type of Image returned depends on the PNG contents.
|
// The type of Image returned depends on the PNG contents.
|
||||||
func Decode(r io.Reader) (image.Image, os.Error) {
|
func Decode(r io.Reader) (image.Image, os.Error) {
|
||||||
var d decoder
|
var d decoder
|
||||||
@ -517,25 +523,66 @@ func Decode(r io.Reader) (image.Image, os.Error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
for d.stage = dsStart; d.stage != dsSeenIEND; {
|
for d.stage != dsSeenIEND {
|
||||||
err = d.parseChunk(r)
|
err = d.parseChunk(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
var img image.Image
|
||||||
if d.idatWriter != nil {
|
if d.idatWriter != nil {
|
||||||
d.idatWriter.Close()
|
d.idatWriter.Close()
|
||||||
err1 := <-d.idatDone
|
ie := <-d.idatDone
|
||||||
if err == nil {
|
if err == nil {
|
||||||
err = err1
|
img, err = ie.img, ie.err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return d.image, nil
|
return img, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeConfig returns the color model and dimensions of a PNG image without
|
||||||
|
// decoding the entire image.
|
||||||
|
func DecodeConfig(r io.Reader) (image.Config, os.Error) {
|
||||||
|
var d decoder
|
||||||
|
err := d.checkHeader(r)
|
||||||
|
if err != nil {
|
||||||
|
return image.Config{}, err
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
err = d.parseChunk(r)
|
||||||
|
if err != nil {
|
||||||
|
return image.Config{}, err
|
||||||
|
}
|
||||||
|
if d.stage == dsSeenIHDR && d.cb != cbP8 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if d.stage == dsSeenPLTE && d.cb == cbP8 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var cm image.ColorModel
|
||||||
|
switch d.cb {
|
||||||
|
case cbG8:
|
||||||
|
cm = image.GrayColorModel
|
||||||
|
case cbTC8:
|
||||||
|
cm = image.RGBAColorModel
|
||||||
|
case cbP8:
|
||||||
|
cm = d.palette
|
||||||
|
case cbTCA8:
|
||||||
|
cm = image.NRGBAColorModel
|
||||||
|
case cbG16:
|
||||||
|
cm = image.Gray16ColorModel
|
||||||
|
case cbTC16:
|
||||||
|
cm = image.RGBA64ColorModel
|
||||||
|
case cbTCA16:
|
||||||
|
cm = image.NRGBA64ColorModel
|
||||||
|
}
|
||||||
|
return image.Config{cm, d.width, d.height}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
image.RegisterFormat("png", pngHeader, Decode)
|
image.RegisterFormat("png", pngHeader, Decode, DecodeConfig)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user