mirror of
https://github.com/golang/go
synced 2024-11-22 01:54:42 -07:00
PNG decoder for go.
R=rsc APPROVED=r DELTA=694 (675 added, 3 deleted, 16 changed) OCL=34427 CL=34554
This commit is contained in:
parent
1f11578614
commit
70eef675d5
@ -38,6 +38,7 @@ hash/adler32.install: hash.install os.install
|
||||
hash/crc32.install: hash.install os.install
|
||||
http.install: bufio.install bytes.install container/vector.install fmt.install io.install log.install net.install os.install path.install strconv.install strings.install utf8.install
|
||||
image.install:
|
||||
image/png.install: compress/zlib.install hash.install hash/crc32.install image.install io.install os.install
|
||||
io.install: bytes.install os.install strings.install sync.install
|
||||
json.install: bytes.install container/vector.install fmt.install math.install reflect.install strconv.install strings.install utf8.install
|
||||
log.install: fmt.install io.install os.install runtime.install time.install
|
||||
|
@ -52,6 +52,7 @@ DIRS=\
|
||||
hash/crc32\
|
||||
http\
|
||||
image\
|
||||
image/png\
|
||||
io\
|
||||
json\
|
||||
log\
|
||||
@ -86,6 +87,7 @@ NOTEST=\
|
||||
go/token\
|
||||
hash\
|
||||
image\
|
||||
image/png\
|
||||
malloc\
|
||||
rand\
|
||||
runtime\
|
||||
|
@ -62,7 +62,7 @@ type Inflater struct {
|
||||
OS byte; // operating system type
|
||||
|
||||
r flate.Reader;
|
||||
inflater io.Reader;
|
||||
inflater io.ReadCloser;
|
||||
digest hash.Hash32;
|
||||
size uint32;
|
||||
flg byte;
|
||||
@ -73,6 +73,7 @@ type Inflater struct {
|
||||
|
||||
// NewInflater creates a new Inflater reading the given reader.
|
||||
// The implementation buffers input and may read more data than necessary from r.
|
||||
// It is the caller's responsibility to call Close on the Inflater when done.
|
||||
func NewInflater(r io.Reader) (*Inflater, os.Error) {
|
||||
z := new(Inflater);
|
||||
z.r = makeReader(r);
|
||||
@ -221,3 +222,8 @@ func (z *Inflater) Read(p []byte) (n int, err os.Error) {
|
||||
return z.Read(p);
|
||||
}
|
||||
|
||||
// Calling Close does not close the wrapped io.Reader originally passed to NewInflater.
|
||||
func (z *Inflater) Close() os.Error {
|
||||
return z.inflater.Close();
|
||||
}
|
||||
|
||||
|
@ -289,6 +289,7 @@ func TestInflater(t *testing.T) {
|
||||
t.Errorf("%s: NewInflater: %s", tt.name, err);
|
||||
continue;
|
||||
}
|
||||
defer gzip.Close();
|
||||
if tt.name != gzip.Name {
|
||||
t.Errorf("%s: got name %s", tt.name, gzip.Name);
|
||||
}
|
||||
|
@ -23,30 +23,31 @@ var UnsupportedError os.Error = os.ErrorString("unsupported zlib format")
|
||||
|
||||
type reader struct {
|
||||
r flate.Reader;
|
||||
inflater io.Reader;
|
||||
inflater io.ReadCloser;
|
||||
digest hash.Hash32;
|
||||
err os.Error;
|
||||
scratch [4]byte;
|
||||
}
|
||||
|
||||
// NewInflater creates a new io.Reader that satisfies reads by decompressing data read from r.
|
||||
// NewInflater creates a new io.ReadCloser that satisfies reads by decompressing data read from r.
|
||||
// The implementation buffers input and may read more data than necessary from r.
|
||||
func NewInflater(r io.Reader) (io.Reader, os.Error) {
|
||||
// It is the caller's responsibility to call Close on the ReadCloser when done.
|
||||
func NewInflater(r io.Reader) (io.ReadCloser, os.Error) {
|
||||
z := new(reader);
|
||||
if fr, ok := r.(flate.Reader); ok {
|
||||
z.r = fr;
|
||||
} else {
|
||||
z.r = bufio.NewReader(r);
|
||||
}
|
||||
var buf [2]byte;
|
||||
n, err := io.ReadFull(z.r, buf[0:2]);
|
||||
n, err := io.ReadFull(z.r, z.scratch[0:2]);
|
||||
if err != nil {
|
||||
return nil, err;
|
||||
}
|
||||
h := uint(buf[0])<<8 | uint(buf[1]);
|
||||
if (buf[0] & 0x0f != zlibDeflate) || (h % 31 != 0) {
|
||||
h := uint(z.scratch[0])<<8 | uint(z.scratch[1]);
|
||||
if (z.scratch[0] & 0x0f != zlibDeflate) || (h % 31 != 0) {
|
||||
return nil, HeaderError;
|
||||
}
|
||||
if buf[1] & 0x20 != 0 {
|
||||
if z.scratch[1] & 0x20 != 0 {
|
||||
// BUG(nigeltao): The zlib package does not implement the FDICT flag.
|
||||
return nil, UnsupportedError;
|
||||
}
|
||||
@ -71,13 +72,12 @@ func (z *reader) Read(p []byte) (n int, err os.Error) {
|
||||
}
|
||||
|
||||
// Finished file; check checksum.
|
||||
var buf [4]byte;
|
||||
if _, err := io.ReadFull(z.r, buf[0:4]); err != nil {
|
||||
if _, err := io.ReadFull(z.r, z.scratch[0:4]); err != nil {
|
||||
z.err = err;
|
||||
return 0, err;
|
||||
}
|
||||
// ZLIB (RFC 1950) is big-endian, unlike GZIP (RFC 1952).
|
||||
checksum := uint32(buf[0])<<24 | uint32(buf[1])<<16 | uint32(buf[2])<<8 | uint32(buf[3]);
|
||||
checksum := uint32(z.scratch[0])<<24 | uint32(z.scratch[1])<<16 | uint32(z.scratch[2])<<8 | uint32(z.scratch[3]);
|
||||
if checksum != z.digest.Sum32() {
|
||||
z.err = ChecksumError;
|
||||
return 0, z.err;
|
||||
@ -85,3 +85,8 @@ func (z *reader) Read(p []byte) (n int, err os.Error) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Calling Close does not close the wrapped io.Reader originally passed to NewInflater.
|
||||
func (z *reader) Close() os.Error {
|
||||
return z.inflater.Close();
|
||||
}
|
||||
|
||||
|
@ -86,6 +86,7 @@ func TestInflater(t *testing.T) {
|
||||
}
|
||||
continue;
|
||||
}
|
||||
defer zlib.Close();
|
||||
b.Reset();
|
||||
n, err := io.Copy(zlib, b);
|
||||
if err != nil {
|
||||
|
@ -4,15 +4,14 @@
|
||||
|
||||
package image
|
||||
|
||||
// TODO(nigeltao): Clarify semantics wrt premultiplied vs unpremultiplied colors.
|
||||
// It's probably also worth thinking about floating-point color models.
|
||||
// TODO(nigeltao): Think about how floating-point color models work.
|
||||
|
||||
// All Colors can convert themselves, with a possible loss of precision, to 128-bit RGBA.
|
||||
// All Colors can convert themselves, with a possible loss of precision, to 128-bit alpha-premultiplied RGBA.
|
||||
type Color interface {
|
||||
RGBA() (r, g, b, a uint32);
|
||||
}
|
||||
|
||||
// An RGBAColor represents a traditional 32-bit color, having 8 bits for each of red, green, blue and alpha.
|
||||
// An RGBAColor represents a traditional 32-bit alpha-premultiplied color, having 8 bits for each of red, green, blue and alpha.
|
||||
type RGBAColor struct {
|
||||
R, G, B, A uint8;
|
||||
}
|
||||
@ -33,7 +32,7 @@ func (c RGBAColor) RGBA() (r, g, b, a uint32) {
|
||||
return;
|
||||
}
|
||||
|
||||
// An RGBA64Color represents a 64-bit color, having 16 bits for each of red, green, blue and alpha.
|
||||
// An RGBA64Color represents a 64-bit alpha-premultiplied color, having 16 bits for each of red, green, blue and alpha.
|
||||
type RGBA64Color struct {
|
||||
R, G, B, A uint16;
|
||||
}
|
||||
@ -50,6 +49,57 @@ func (c RGBA64Color) RGBA() (r, g, b, a uint32) {
|
||||
return;
|
||||
}
|
||||
|
||||
// An NRGBAColor represents a non-alpha-premultiplied 32-bit color.
|
||||
type NRGBAColor struct {
|
||||
R, G, B, A uint8;
|
||||
}
|
||||
|
||||
func (c NRGBAColor) RGBA() (r, g, b, a uint32) {
|
||||
r = uint32(c.R);
|
||||
r |= r<<8;
|
||||
r *= uint32(c.A);
|
||||
r /= 0xff;
|
||||
r |= r<<16;
|
||||
g = uint32(c.G);
|
||||
g |= g<<8;
|
||||
g *= uint32(c.A);
|
||||
g /= 0xff;
|
||||
g |= g<<16;
|
||||
b = uint32(c.B);
|
||||
b |= b<<8;
|
||||
b *= uint32(c.A);
|
||||
b /= 0xff;
|
||||
b |= b<<16;
|
||||
a = uint32(c.A);
|
||||
a |= a<<8;
|
||||
a |= a<<16;
|
||||
return;
|
||||
}
|
||||
|
||||
// An NRGBA64Color represents a non-alpha-premultiplied 64-bit color, having 16 bits for each of red, green, blue and alpha.
|
||||
type NRGBA64Color struct {
|
||||
R, G, B, A uint16;
|
||||
}
|
||||
|
||||
func (c NRGBA64Color) RGBA() (r, g, b, a uint32) {
|
||||
r = uint32(c.R);
|
||||
r *= uint32(c.A);
|
||||
r /= 0xffff;
|
||||
r |= r<<16;
|
||||
g = uint32(c.G);
|
||||
g *= uint32(c.A);
|
||||
g /= 0xffff;
|
||||
g |= g<<16;
|
||||
b = uint32(c.B);
|
||||
b *= uint32(c.A);
|
||||
b /= 0xffff;
|
||||
b |= b<<16;
|
||||
a = uint32(c.A);
|
||||
a |= a<<8;
|
||||
a |= a<<16;
|
||||
return;
|
||||
}
|
||||
|
||||
// A ColorModel can convert foreign Colors, with a possible loss of precision, to a Color
|
||||
// from its own color model.
|
||||
type ColorModel interface {
|
||||
@ -82,9 +132,59 @@ func toRGBA64Color(c Color) Color {
|
||||
return RGBA64Color{ uint16(r>>16), uint16(g>>16), uint16(b>>16), uint16(a>>16) };
|
||||
}
|
||||
|
||||
func toNRGBAColor(c Color) Color {
|
||||
if _, ok := c.(NRGBAColor); ok { // no-op conversion
|
||||
return c;
|
||||
}
|
||||
r, g, b, a := c.RGBA();
|
||||
a >>= 16;
|
||||
if a == 0xffff {
|
||||
return NRGBAColor{ uint8(r>>24), uint8(g>>24), uint8(b>>24), 0xff };
|
||||
}
|
||||
if a == 0 {
|
||||
return NRGBAColor{ 0, 0, 0, 0 };
|
||||
}
|
||||
r >>= 16;
|
||||
g >>= 16;
|
||||
b >>= 16;
|
||||
// Since Color.RGBA returns a alpha-premultiplied color, we should have r <= a && g <= a && b <= a.
|
||||
r = (r * 0xffff) / a;
|
||||
g = (g * 0xffff) / a;
|
||||
b = (b * 0xffff) / a;
|
||||
return NRGBAColor{ uint8(r>>8), uint8(g>>8), uint8(b>>8), uint8(a>>8) };
|
||||
}
|
||||
|
||||
func toNRGBA64Color(c Color) Color {
|
||||
if _, ok := c.(NRGBA64Color); ok { // no-op conversion
|
||||
return c;
|
||||
}
|
||||
r, g, b, a := c.RGBA();
|
||||
a >>= 16;
|
||||
r >>= 16;
|
||||
g >>= 16;
|
||||
b >>= 16;
|
||||
if a == 0xffff {
|
||||
return NRGBA64Color{ uint16(r), uint16(g), uint16(b), 0xffff };
|
||||
}
|
||||
if a == 0 {
|
||||
return NRGBA64Color{ 0, 0, 0, 0 };
|
||||
}
|
||||
// Since Color.RGBA returns a alpha-premultiplied color, we should have r <= a && g <= a && b <= a.
|
||||
r = (r * 0xffff) / a;
|
||||
g = (g * 0xffff) / a;
|
||||
b = (b * 0xffff) / a;
|
||||
return NRGBA64Color{ uint16(r), uint16(g), uint16(b), uint16(a) };
|
||||
}
|
||||
|
||||
// The ColorModel associated with RGBAColor.
|
||||
var RGBAColorModel ColorModel = ColorModelFunc(toRGBAColor);
|
||||
|
||||
// The ColorModel associated with RGBA64Color.
|
||||
var RGBA64ColorModel ColorModel = ColorModelFunc(toRGBA64Color);
|
||||
|
||||
// The ColorModel associated with NRGBAColor.
|
||||
var NRGBAColorModel ColorModel = ColorModelFunc(toNRGBAColor);
|
||||
|
||||
// The ColorModel associated with NRGBA64Color.
|
||||
var NRGBA64ColorModel ColorModel = ColorModelFunc(toNRGBA64Color);
|
||||
|
||||
|
@ -44,6 +44,15 @@ func (p *RGBA) Set(x, y int, c Color) {
|
||||
p.Pixel[y][x] = toRGBAColor(c).(RGBAColor);
|
||||
}
|
||||
|
||||
// NewRGBA returns a new RGBA with the given width and height.
|
||||
func NewRGBA(w, h int) *RGBA {
|
||||
pixel := make([][]RGBAColor, h);
|
||||
for y := 0; y < int(h); y++ {
|
||||
pixel[y] = make([]RGBAColor, w);
|
||||
}
|
||||
return &RGBA{ pixel };
|
||||
}
|
||||
|
||||
// An RGBA64 is an in-memory image backed by a 2-D slice of RGBA64Color values.
|
||||
type RGBA64 struct {
|
||||
// The Pixel field's indices are y first, then x, so that At(x, y) == Pixel[y][x].
|
||||
@ -73,6 +82,91 @@ func (p *RGBA64) Set(x, y int, c Color) {
|
||||
p.Pixel[y][x] = toRGBA64Color(c).(RGBA64Color);
|
||||
}
|
||||
|
||||
// NewRGBA64 returns a new RGBA64 with the given width and height.
|
||||
func NewRGBA64(w, h int) *RGBA64 {
|
||||
pixel := make([][]RGBA64Color, h);
|
||||
for y := 0; y < int(h); y++ {
|
||||
pixel[y] = make([]RGBA64Color, w);
|
||||
}
|
||||
return &RGBA64{ pixel };
|
||||
}
|
||||
|
||||
// A NRGBA is an in-memory image backed by a 2-D slice of NRGBAColor values.
|
||||
type NRGBA struct {
|
||||
// The Pixel field's indices are y first, then x, so that At(x, y) == Pixel[y][x].
|
||||
Pixel [][]NRGBAColor;
|
||||
}
|
||||
|
||||
func (p *NRGBA) ColorModel() ColorModel {
|
||||
return NRGBAColorModel;
|
||||
}
|
||||
|
||||
func (p *NRGBA) Width() int {
|
||||
if len(p.Pixel) == 0 {
|
||||
return 0;
|
||||
}
|
||||
return len(p.Pixel[0]);
|
||||
}
|
||||
|
||||
func (p *NRGBA) Height() int {
|
||||
return len(p.Pixel);
|
||||
}
|
||||
|
||||
func (p *NRGBA) At(x, y int) Color {
|
||||
return p.Pixel[y][x];
|
||||
}
|
||||
|
||||
func (p *NRGBA) Set(x, y int, c Color) {
|
||||
p.Pixel[y][x] = toNRGBAColor(c).(NRGBAColor);
|
||||
}
|
||||
|
||||
// NewNRGBA returns a new NRGBA with the given width and height.
|
||||
func NewNRGBA(w, h int) *NRGBA {
|
||||
pixel := make([][]NRGBAColor, h);
|
||||
for y := 0; y < int(h); y++ {
|
||||
pixel[y] = make([]NRGBAColor, w);
|
||||
}
|
||||
return &NRGBA{ pixel };
|
||||
}
|
||||
|
||||
// A NRGBA64 is an in-memory image backed by a 2-D slice of NRGBA64Color values.
|
||||
type NRGBA64 struct {
|
||||
// The Pixel field's indices are y first, then x, so that At(x, y) == Pixel[y][x].
|
||||
Pixel [][]NRGBA64Color;
|
||||
}
|
||||
|
||||
func (p *NRGBA64) ColorModel() ColorModel {
|
||||
return NRGBA64ColorModel;
|
||||
}
|
||||
|
||||
func (p *NRGBA64) Width() int {
|
||||
if len(p.Pixel) == 0 {
|
||||
return 0;
|
||||
}
|
||||
return len(p.Pixel[0]);
|
||||
}
|
||||
|
||||
func (p *NRGBA64) Height() int {
|
||||
return len(p.Pixel);
|
||||
}
|
||||
|
||||
func (p *NRGBA64) At(x, y int) Color {
|
||||
return p.Pixel[y][x];
|
||||
}
|
||||
|
||||
func (p *NRGBA64) Set(x, y int, c Color) {
|
||||
p.Pixel[y][x] = toNRGBA64Color(c).(NRGBA64Color);
|
||||
}
|
||||
|
||||
// NewNRGBA64 returns a new NRGBA64 with the given width and height.
|
||||
func NewNRGBA64(w, h int) *NRGBA64 {
|
||||
pixel := make([][]NRGBA64Color, h);
|
||||
for y := 0; y < int(h); y++ {
|
||||
pixel[y] = make([]NRGBA64Color, w);
|
||||
}
|
||||
return &NRGBA64{ pixel };
|
||||
}
|
||||
|
||||
// A PalettedColorModel represents a fixed palette of colors.
|
||||
type PalettedColorModel []Color;
|
||||
|
||||
@ -113,10 +207,10 @@ func (p PalettedColorModel) Convert(c Color) Color {
|
||||
return result;
|
||||
}
|
||||
|
||||
// A Paletted is an in-memory image backed by a 2-D slice of byte values and a PalettedColorModel.
|
||||
// A Paletted is an in-memory image backed by a 2-D slice of uint8 values and a PalettedColorModel.
|
||||
type Paletted struct {
|
||||
// The Pixel field's indices are y first, then x, so that At(x, y) == Palette[Pixel[y][x]].
|
||||
Pixel [][]byte;
|
||||
Pixel [][]uint8;
|
||||
Palette PalettedColorModel;
|
||||
}
|
||||
|
||||
@ -138,3 +232,17 @@ func (p *Paletted) Height() int {
|
||||
func (p *Paletted) At(x, y int) Color {
|
||||
return p.Palette[p.Pixel[y][x]];
|
||||
}
|
||||
|
||||
func (p *Paletted) SetColorIndex(x, y int, index uint8) {
|
||||
p.Pixel[y][x] = index;
|
||||
}
|
||||
|
||||
// NewPaletted returns a new Paletted with the given width, height and palette.
|
||||
func NewPaletted(w, h int, m PalettedColorModel) *Paletted {
|
||||
pixel := make([][]uint8, h);
|
||||
for y := 0; y < int(h); y++ {
|
||||
pixel[y] = make([]uint8, w);
|
||||
}
|
||||
return &Paletted{ pixel, m };
|
||||
}
|
||||
|
||||
|
11
src/pkg/image/png/Makefile
Normal file
11
src/pkg/image/png/Makefile
Normal file
@ -0,0 +1,11 @@
|
||||
# Copyright 2009 The Go Authors. All rights reserved.
|
||||
# Use of this source code is governed by a BSD-style
|
||||
# license that can be found in the LICENSE file.
|
||||
|
||||
include $(GOROOT)/src/Make.$(GOARCH)
|
||||
|
||||
TARG=image/png
|
||||
GOFILES=\
|
||||
reader.go\
|
||||
|
||||
include $(GOROOT)/src/Make.pkg
|
437
src/pkg/image/png/reader.go
Normal file
437
src/pkg/image/png/reader.go
Normal file
@ -0,0 +1,437 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// The png package implements a PNG image decoder (and eventually, an encoder).
|
||||
//
|
||||
// The PNG specification is at http://www.libpng.org/pub/png/spec/1.2/PNG-Contents.html
|
||||
package png
|
||||
|
||||
// TODO(nigeltao): Add tests.
|
||||
import (
|
||||
"compress/zlib";
|
||||
"hash";
|
||||
"hash/crc32";
|
||||
"image";
|
||||
"io";
|
||||
"os";
|
||||
)
|
||||
|
||||
// Color type, as per the PNG spec.
|
||||
const (
|
||||
ctGrayscale = 0;
|
||||
ctTrueColor = 2;
|
||||
ctPaletted = 3;
|
||||
ctGrayscaleAlpha = 4;
|
||||
ctTrueColorAlpha = 6;
|
||||
)
|
||||
|
||||
// Filter type, as per the PNG spec.
|
||||
const (
|
||||
ftNone = 0;
|
||||
ftSub = 1;
|
||||
ftUp = 2;
|
||||
ftAverage = 3;
|
||||
ftPaeth = 4;
|
||||
)
|
||||
|
||||
// Decoding stage.
|
||||
// The PNG specification says that the IHDR, PLTE (if present), IDAT and IEND
|
||||
// chunks must appear in that order. There may be multiple IDAT chunks, and
|
||||
// IDAT chunks must be sequential (i.e. they may not have any other chunks
|
||||
// between them).
|
||||
const (
|
||||
dsStart = iota;
|
||||
dsSeenIHDR;
|
||||
dsSeenPLTE;
|
||||
dsSeenIDAT;
|
||||
dsSeenIEND;
|
||||
)
|
||||
|
||||
type decoder struct {
|
||||
width, height int;
|
||||
image image.Image;
|
||||
colorType uint8;
|
||||
stage int;
|
||||
idatWriter io.WriteCloser;
|
||||
idatDone chan os.Error;
|
||||
scratch [3 * 256]byte;
|
||||
}
|
||||
|
||||
// A FormatError reports that the input is not a valid PNG.
|
||||
type FormatError string
|
||||
|
||||
func (e FormatError) String() string {
|
||||
return "invalid PNG format: " + e;
|
||||
}
|
||||
|
||||
// An IDATDecodingError wraps an inner error (such as a ZLIB decoding error) encountered while processing an IDAT chunk.
|
||||
type IDATDecodingError struct {
|
||||
Err os.Error;
|
||||
}
|
||||
|
||||
func (e IDATDecodingError) String() string {
|
||||
return "IDAT decoding error: " + e.Err.String();
|
||||
}
|
||||
|
||||
// An UnsupportedError reports that the input uses a valid but unimplemented PNG feature.
|
||||
type UnsupportedError string
|
||||
|
||||
func (e UnsupportedError) String() string {
|
||||
return "unsupported PNG feature: " + e;
|
||||
}
|
||||
|
||||
// Big-endian.
|
||||
func parseUint32(b []uint8) uint32 {
|
||||
return uint32(b[0])<<24 | uint32(b[1])<<16 | uint32(b[2])<<8 | uint32(b[3]);
|
||||
}
|
||||
|
||||
func abs(x int) int {
|
||||
if x < 0 {
|
||||
return -x;
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
func min(a, b int) int {
|
||||
if a < b {
|
||||
return a;
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
func (d *decoder) parseIHDR(r io.Reader, crc hash.Hash32, length uint32) os.Error {
|
||||
if length != 13 {
|
||||
return FormatError("bad IHDR length");
|
||||
}
|
||||
n, err := io.ReadFull(r, d.scratch[0:13]);
|
||||
if err != nil {
|
||||
return err;
|
||||
}
|
||||
crc.Write(d.scratch[0:13]);
|
||||
if d.scratch[8] != 8 {
|
||||
return UnsupportedError("bit depth");
|
||||
}
|
||||
if d.scratch[10] != 0 || d.scratch[11] != 0 || d.scratch[12] != 0 {
|
||||
return UnsupportedError("compression, filter or interlace method");
|
||||
}
|
||||
w := int32(parseUint32(d.scratch[0:4]));
|
||||
h := int32(parseUint32(d.scratch[4:8]));
|
||||
if w < 0 || h < 0 {
|
||||
return FormatError("negative dimension");
|
||||
}
|
||||
nPixels := int64(w) * int64(h);
|
||||
if nPixels != int64(int(nPixels)) {
|
||||
return UnsupportedError("dimension overflow");
|
||||
}
|
||||
d.colorType = d.scratch[9];
|
||||
switch d.colorType {
|
||||
case ctTrueColor:
|
||||
d.image = image.NewRGBA(int(w), int(h));
|
||||
case ctPaletted:
|
||||
d.image = image.NewPaletted(int(w), int(h), nil);
|
||||
case ctTrueColorAlpha:
|
||||
d.image = image.NewNRGBA(int(w), int(h));
|
||||
default:
|
||||
return UnsupportedError("color type");
|
||||
}
|
||||
d.width, d.height = int(w), int(h);
|
||||
return nil;
|
||||
}
|
||||
|
||||
func (d *decoder) parsePLTE(r io.Reader, crc hash.Hash32, length uint32) os.Error {
|
||||
np := int(length / 3); // The number of palette entries.
|
||||
if length % 3 != 0 || np <= 0 || np > 256 {
|
||||
return FormatError("bad PLTE length");
|
||||
}
|
||||
n, err := io.ReadFull(r, d.scratch[0:3 * np]);
|
||||
if err != nil {
|
||||
return err;
|
||||
}
|
||||
crc.Write(d.scratch[0:n]);
|
||||
switch d.colorType {
|
||||
case ctPaletted:
|
||||
palette := make([]image.Color, np);
|
||||
for i := 0; i < np; i++ {
|
||||
palette[i] = image.RGBAColor{ d.scratch[3*i+0], d.scratch[3*i+1], d.scratch[3*i+2], 0xff };
|
||||
}
|
||||
d.image.(*image.Paletted).Palette = image.PalettedColorModel(palette);
|
||||
case ctTrueColor, ctTrueColorAlpha:
|
||||
// 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).
|
||||
return nil;
|
||||
default:
|
||||
return FormatError("PLTE, color type mismatch");
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
// The Paeth filter function, as per the PNG specification.
|
||||
func paeth(a, b, c uint8) uint8 {
|
||||
p := int(a) + int(b) - int(c);
|
||||
pa := abs(p - int(a));
|
||||
pb := abs(p - int(b));
|
||||
pc := abs(p - int(c));
|
||||
if pa <= pb && pa <= pc {
|
||||
return a;
|
||||
} else if pb <= pc {
|
||||
return b;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
func (d *decoder) idatReader(idat io.Reader) os.Error {
|
||||
r, err := zlib.NewInflater(idat);
|
||||
if err != nil {
|
||||
return err;
|
||||
}
|
||||
defer r.Close();
|
||||
bpp := 0; // Bytes per pixel.
|
||||
maxPalette := uint8(0);
|
||||
var (
|
||||
rgba *image.RGBA;
|
||||
nrgba *image.NRGBA;
|
||||
paletted *image.Paletted;
|
||||
);
|
||||
switch d.colorType {
|
||||
case ctTrueColor:
|
||||
bpp = 3;
|
||||
rgba = d.image.(*image.RGBA);
|
||||
case ctPaletted:
|
||||
bpp = 1;
|
||||
paletted = d.image.(*image.Paletted);
|
||||
maxPalette = uint8(len(paletted.Palette) - 1);
|
||||
case ctTrueColorAlpha:
|
||||
bpp = 4;
|
||||
nrgba = d.image.(*image.NRGBA);
|
||||
}
|
||||
// cr and pr are the bytes for the current and previous row.
|
||||
cr := make([]uint8, bpp * d.width);
|
||||
pr := make([]uint8, bpp * d.width);
|
||||
|
||||
var filter [1]uint8;
|
||||
for y := 0; y < d.height; y++ {
|
||||
// Read the decompressed bytes.
|
||||
n, err := io.ReadFull(r, filter[0:1]);
|
||||
if err != nil {
|
||||
return err;
|
||||
}
|
||||
n, err = io.ReadFull(r, cr);
|
||||
if err != nil {
|
||||
return err;
|
||||
}
|
||||
|
||||
// Apply the filter.
|
||||
switch filter[0] {
|
||||
case ftNone:
|
||||
// No-op.
|
||||
case ftSub:
|
||||
for i := bpp; i < n; i++ {
|
||||
cr[i] += cr[i - bpp];
|
||||
}
|
||||
case ftUp:
|
||||
for i := 0; i < n; i++ {
|
||||
cr[i] += pr[i];
|
||||
}
|
||||
case ftAverage:
|
||||
for i := 0; i < bpp; i++ {
|
||||
cr[i] += pr[i] / 2;
|
||||
}
|
||||
for i := bpp; i < n; i++ {
|
||||
cr[i] += uint8((int(cr[i - bpp]) + int(pr[i])) / 2);
|
||||
}
|
||||
case ftPaeth:
|
||||
for i := 0; i < bpp; i++ {
|
||||
cr[i] += paeth(0, pr[i], 0);
|
||||
}
|
||||
for i := bpp; i < n; i++ {
|
||||
cr[i] += paeth(cr[i - bpp], pr[i], pr[i - bpp]);
|
||||
}
|
||||
default:
|
||||
return FormatError("bad filter type");
|
||||
}
|
||||
|
||||
// Convert from bytes to colors.
|
||||
switch d.colorType {
|
||||
case ctTrueColor:
|
||||
for x := 0; x < d.width; x++ {
|
||||
rgba.Set(x, y, image.RGBAColor{ cr[3*x+0], cr[3*x+1], cr[3*x+2], 0xff });
|
||||
}
|
||||
case ctPaletted:
|
||||
for x := 0; x < d.width; x++ {
|
||||
if cr[x] > maxPalette {
|
||||
return FormatError("palette index out of range");
|
||||
}
|
||||
paletted.SetColorIndex(x, y, cr[x]);
|
||||
}
|
||||
case ctTrueColorAlpha:
|
||||
for x := 0; x < d.width; x++ {
|
||||
nrgba.Set(x, y, image.NRGBAColor{ cr[4*x+0], cr[4*x+1], cr[4*x+2], cr[4*x+3] });
|
||||
}
|
||||
}
|
||||
|
||||
// The current row for y is the previous row for y+1.
|
||||
pr, cr = cr, pr;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
func (d *decoder) parseIDAT(r io.Reader, crc hash.Hash32, length uint32) os.Error {
|
||||
// There may be more than one IDAT chunk, but their contents must be
|
||||
// treated as if it was one continuous stream (to the zlib decoder).
|
||||
// We bring up an io.Pipe and write the IDAT chunks into the pipe as
|
||||
// we see them, and decode the stream in a separate go-routine, which
|
||||
// signals its completion (successful or not) via a channel.
|
||||
if d.idatWriter == nil {
|
||||
pr, pw := io.Pipe();
|
||||
d.idatWriter = pw;
|
||||
d.idatDone = make(chan os.Error);
|
||||
go func() {
|
||||
err := d.idatReader(pr);
|
||||
if err == os.EOF {
|
||||
err = FormatError("too little IDAT");
|
||||
}
|
||||
pr.CloseWithError(FormatError("too much IDAT"));
|
||||
d.idatDone <- err;
|
||||
}();
|
||||
}
|
||||
var buf [4096]byte;
|
||||
for length > 0 {
|
||||
n, err1 := r.Read(buf[0:min(len(buf), int(length))]);
|
||||
// We delay checking err1. It is possible to get n bytes and an error,
|
||||
// but if the n bytes themselves contain a FormatError, for example, we
|
||||
// want to report that error, and not the one that made the Read stop.
|
||||
n, err2 := d.idatWriter.Write(buf[0:n]);
|
||||
if err2 != nil {
|
||||
return err2;
|
||||
}
|
||||
if err1 != nil {
|
||||
return err1;
|
||||
}
|
||||
crc.Write(buf[0:n]);
|
||||
length -= uint32(n);
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
func (d *decoder) parseIEND(r io.Reader, crc hash.Hash32, length uint32) os.Error {
|
||||
if length != 0 {
|
||||
return FormatError("bad IEND length");
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
func (d *decoder) parseChunk(r io.Reader) os.Error {
|
||||
// Read the length.
|
||||
n, err := io.ReadFull(r, d.scratch[0:4]);
|
||||
if err == os.EOF {
|
||||
return io.ErrUnexpectedEOF;
|
||||
}
|
||||
if err != nil {
|
||||
return err;
|
||||
}
|
||||
length := parseUint32(d.scratch[0:4]);
|
||||
|
||||
// Read the chunk type.
|
||||
n, err = io.ReadFull(r, d.scratch[0:4]);
|
||||
if err == os.EOF {
|
||||
return io.ErrUnexpectedEOF;
|
||||
}
|
||||
if err != nil {
|
||||
return err;
|
||||
}
|
||||
crc := crc32.NewIEEE();
|
||||
crc.Write(d.scratch[0:4]);
|
||||
|
||||
// Read the chunk data.
|
||||
switch string(d.scratch[0:4]) {
|
||||
case "IHDR":
|
||||
if d.stage != dsStart {
|
||||
return FormatError("chunk out of order");
|
||||
}
|
||||
d.stage = dsSeenIHDR;
|
||||
err = d.parseIHDR(r, crc, length);
|
||||
case "PLTE":
|
||||
if d.stage != dsSeenIHDR {
|
||||
return FormatError("chunk out of order");
|
||||
}
|
||||
d.stage = dsSeenPLTE;
|
||||
err = d.parsePLTE(r, crc, length);
|
||||
case "IDAT":
|
||||
if d.stage < dsSeenIHDR || d.stage > dsSeenIDAT {
|
||||
return FormatError("chunk out of order");
|
||||
}
|
||||
d.stage = dsSeenIDAT;
|
||||
err = d.parseIDAT(r, crc, length);
|
||||
case "IEND":
|
||||
if d.stage != dsSeenIDAT {
|
||||
return FormatError("chunk out of order");
|
||||
}
|
||||
d.stage = dsSeenIEND;
|
||||
err = d.parseIEND(r, crc, length);
|
||||
default:
|
||||
// Ignore this chunk (of a known length).
|
||||
var ignored [4096]byte;
|
||||
for length > 0 {
|
||||
n, err = io.ReadFull(r, ignored[0:min(len(ignored), int(length))]);
|
||||
if err != nil {
|
||||
return err;
|
||||
}
|
||||
crc.Write(ignored[0:n]);
|
||||
length -= uint32(n);
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return err;
|
||||
}
|
||||
|
||||
// Read the checksum.
|
||||
n, err = io.ReadFull(r, d.scratch[0:4]);
|
||||
if err == os.EOF {
|
||||
return io.ErrUnexpectedEOF;
|
||||
}
|
||||
if err != nil {
|
||||
return err;
|
||||
}
|
||||
if parseUint32(d.scratch[0:4]) != crc.Sum32() {
|
||||
return FormatError("invalid checksum");
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
func (d *decoder) checkHeader(r io.Reader) os.Error {
|
||||
n, err := io.ReadFull(r, d.scratch[0:8]);
|
||||
if err != nil {
|
||||
return err;
|
||||
}
|
||||
if string(d.scratch[0:8]) != "\x89PNG\r\n\x1a\n" {
|
||||
return FormatError("not a PNG file");
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
func Decode(r io.Reader) (image.Image, os.Error) {
|
||||
var d decoder;
|
||||
err := d.checkHeader(r);
|
||||
if err != nil {
|
||||
return nil, err;
|
||||
}
|
||||
for d.stage = dsStart; d.stage != dsSeenIEND; {
|
||||
err = d.parseChunk(r);
|
||||
if err != nil {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if d.idatWriter != nil {
|
||||
d.idatWriter.Close();
|
||||
err1 := <-d.idatDone;
|
||||
if err == nil {
|
||||
err = err1;
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err;
|
||||
}
|
||||
return d.image, nil;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user