Commit 70eef675 authored by Nigel Tao's avatar Nigel Tao

PNG decoder for go.

R=rsc
APPROVED=r
DELTA=694  (675 added, 3 deleted, 16 changed)
OCL=34427
CL=34554
parent 1f115786
......@@ -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 };
}
# 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
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment