Commit 07053ac6 authored by Nigel Tao's avatar Nigel Tao

image/png: support 16-bit color.

R=r, mpl
CC=golang-dev
https://golang.org/cl/1944043
parent 132d2f11
...@@ -9,6 +9,7 @@ package png ...@@ -9,6 +9,7 @@ package png
import ( import (
"compress/zlib" "compress/zlib"
"fmt"
"hash" "hash"
"hash/crc32" "hash/crc32"
"image" "image"
...@@ -25,6 +26,17 @@ const ( ...@@ -25,6 +26,17 @@ const (
ctTrueColorAlpha = 6 ctTrueColorAlpha = 6
) )
// A cb is a combination of color type and bit depth.
const (
cbG8 = iota
cbTC8
cbP8
cbTCA8
cbG16
cbTC16
cbTCA16
)
// Filter type, as per the PNG spec. // Filter type, as per the PNG spec.
const ( const (
ftNone = 0 ftNone = 0
...@@ -53,7 +65,7 @@ const pngHeader = "\x89PNG\r\n\x1a\n" ...@@ -53,7 +65,7 @@ const pngHeader = "\x89PNG\r\n\x1a\n"
type decoder struct { type decoder struct {
width, height int width, height int
image image.Image image image.Image
colorType uint8 cb int
stage int stage int
idatWriter io.WriteCloser idatWriter io.WriteCloser
idatDone chan os.Error idatDone chan os.Error
...@@ -107,9 +119,6 @@ func (d *decoder) parseIHDR(r io.Reader, crc hash.Hash32, length uint32) os.Erro ...@@ -107,9 +119,6 @@ func (d *decoder) parseIHDR(r io.Reader, crc hash.Hash32, length uint32) os.Erro
return err return err
} }
crc.Write(d.tmp[0:13]) crc.Write(d.tmp[0:13])
if d.tmp[8] != 8 {
return UnsupportedError("bit depth")
}
if d.tmp[10] != 0 || d.tmp[11] != 0 || d.tmp[12] != 0 { if d.tmp[10] != 0 || d.tmp[11] != 0 || d.tmp[12] != 0 {
return UnsupportedError("compression, filter or interlace method") return UnsupportedError("compression, filter or interlace method")
} }
...@@ -122,18 +131,37 @@ func (d *decoder) parseIHDR(r io.Reader, crc hash.Hash32, length uint32) os.Erro ...@@ -122,18 +131,37 @@ 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.colorType = d.tmp[9] switch d.tmp[8] {
switch d.colorType { case 8:
switch d.tmp[9] {
case ctGrayscale: case ctGrayscale:
d.image = image.NewGray(int(w), int(h)) d.image = image.NewGray(int(w), int(h))
d.cb = cbG8
case ctTrueColor: case ctTrueColor:
d.image = image.NewRGBA(int(w), int(h)) d.image = image.NewRGBA(int(w), int(h))
d.cb = cbTC8
case ctPaletted: case ctPaletted:
d.image = image.NewPaletted(int(w), int(h), nil) d.image = image.NewPaletted(int(w), int(h), nil)
d.cb = cbP8
case ctTrueColorAlpha: case ctTrueColorAlpha:
d.image = image.NewNRGBA(int(w), int(h)) d.image = image.NewNRGBA(int(w), int(h))
default: d.cb = cbTCA8
return UnsupportedError("color type") }
case 16:
switch d.tmp[9] {
case ctGrayscale:
d.image = image.NewGray16(int(w), int(h))
d.cb = cbG16
case ctTrueColor:
d.image = image.NewRGBA64(int(w), int(h))
d.cb = cbTC16
case ctTrueColorAlpha:
d.image = image.NewNRGBA64(int(w), int(h))
d.cb = cbTCA16
}
}
if d.image == nil {
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)
return nil return nil
...@@ -149,17 +177,16 @@ func (d *decoder) parsePLTE(r io.Reader, crc hash.Hash32, length uint32) os.Erro ...@@ -149,17 +177,16 @@ func (d *decoder) parsePLTE(r io.Reader, crc hash.Hash32, length uint32) os.Erro
return err return err
} }
crc.Write(d.tmp[0:n]) crc.Write(d.tmp[0:n])
switch d.colorType { switch d.cb {
case ctPaletted: case cbP8:
palette := make([]image.Color, np) palette := 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} 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) d.image.(*image.Paletted).Palette = image.PalettedColorModel(palette)
case ctGrayscale, ctTrueColor, ctTrueColorAlpha: 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).
return nil
default: default:
return FormatError("PLTE, color type mismatch") return FormatError("PLTE, color type mismatch")
} }
...@@ -175,12 +202,12 @@ func (d *decoder) parsetRNS(r io.Reader, crc hash.Hash32, length uint32) os.Erro ...@@ -175,12 +202,12 @@ func (d *decoder) parsetRNS(r io.Reader, crc hash.Hash32, length uint32) os.Erro
return err return err
} }
crc.Write(d.tmp[0:n]) crc.Write(d.tmp[0:n])
switch d.colorType { switch d.cb {
case ctGrayscale: case cbG8, cbG16:
return UnsupportedError("grayscale transparency") return UnsupportedError("grayscale transparency")
case ctTrueColor: case cbTC8, cbTC16:
return UnsupportedError("truecolor transparency") return UnsupportedError("truecolor transparency")
case ctPaletted: case cbP8:
p := d.image.(*image.Paletted).Palette p := d.image.(*image.Paletted).Palette
if n > len(p) { if n > len(p) {
return FormatError("bad tRNS length") return FormatError("bad tRNS length")
...@@ -189,7 +216,7 @@ func (d *decoder) parsetRNS(r io.Reader, crc hash.Hash32, length uint32) os.Erro ...@@ -189,7 +216,7 @@ func (d *decoder) parsetRNS(r io.Reader, crc hash.Hash32, length uint32) os.Erro
rgba := p[i].(image.RGBAColor) rgba := p[i].(image.RGBAColor)
p[i] = image.RGBAColor{rgba.R, rgba.G, rgba.B, d.tmp[i]} p[i] = image.RGBAColor{rgba.R, rgba.G, rgba.B, d.tmp[i]}
} }
case ctTrueColorAlpha: case cbTCA8, cbTCA16:
return FormatError("tRNS, color type mismatch") return FormatError("tRNS, color type mismatch")
} }
return nil return nil
...@@ -222,21 +249,33 @@ func (d *decoder) idatReader(idat io.Reader) os.Error { ...@@ -222,21 +249,33 @@ func (d *decoder) idatReader(idat io.Reader) os.Error {
rgba *image.RGBA rgba *image.RGBA
paletted *image.Paletted paletted *image.Paletted
nrgba *image.NRGBA nrgba *image.NRGBA
gray16 *image.Gray16
rgba64 *image.RGBA64
nrgba64 *image.NRGBA64
) )
switch d.colorType { switch d.cb {
case ctGrayscale: case cbG8:
bpp = 1 bpp = 1
gray = d.image.(*image.Gray) gray = d.image.(*image.Gray)
case ctTrueColor: case cbTC8:
bpp = 3 bpp = 3
rgba = d.image.(*image.RGBA) rgba = d.image.(*image.RGBA)
case ctPaletted: case cbP8:
bpp = 1 bpp = 1
paletted = d.image.(*image.Paletted) paletted = d.image.(*image.Paletted)
maxPalette = uint8(len(paletted.Palette) - 1) maxPalette = uint8(len(paletted.Palette) - 1)
case ctTrueColorAlpha: case cbTCA8:
bpp = 4 bpp = 4
nrgba = d.image.(*image.NRGBA) nrgba = d.image.(*image.NRGBA)
case cbG16:
bpp = 2
gray16 = d.image.(*image.Gray16)
case cbTC16:
bpp = 6
rgba64 = d.image.(*image.RGBA64)
case cbTCA16:
bpp = 8
nrgba64 = d.image.(*image.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].
...@@ -283,26 +322,46 @@ func (d *decoder) idatReader(idat io.Reader) os.Error { ...@@ -283,26 +322,46 @@ func (d *decoder) idatReader(idat io.Reader) os.Error {
} }
// Convert from bytes to colors. // Convert from bytes to colors.
switch d.colorType { switch d.cb {
case ctGrayscale: case cbG8:
for x := 0; x < d.width; x++ { for x := 0; x < d.width; x++ {
gray.Set(x, y, image.GrayColor{cdat[x]}) gray.Set(x, y, image.GrayColor{cdat[x]})
} }
case ctTrueColor: case cbTC8:
for x := 0; x < d.width; x++ { for x := 0; x < d.width; x++ {
rgba.Set(x, y, image.RGBAColor{cdat[3*x+0], cdat[3*x+1], cdat[3*x+2], 0xff}) rgba.Set(x, y, image.RGBAColor{cdat[3*x+0], cdat[3*x+1], cdat[3*x+2], 0xff})
} }
case ctPaletted: 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 FormatError("palette index out of range")
} }
paletted.SetColorIndex(x, y, cdat[x]) paletted.SetColorIndex(x, y, cdat[x])
} }
case ctTrueColorAlpha: case cbTCA8:
for x := 0; x < d.width; x++ { for x := 0; x < d.width; x++ {
nrgba.Set(x, y, image.NRGBAColor{cdat[4*x+0], cdat[4*x+1], cdat[4*x+2], cdat[4*x+3]}) nrgba.Set(x, y, image.NRGBAColor{cdat[4*x+0], cdat[4*x+1], cdat[4*x+2], cdat[4*x+3]})
} }
case cbG16:
for x := 0; x < d.width; x++ {
ycol := uint16(cdat[2*x+0])<<8 | uint16(cdat[2*x+1])
gray16.Set(x, y, image.Gray16Color{ycol})
}
case cbTC16:
for x := 0; x < d.width; x++ {
rcol := uint16(cdat[6*x+0])<<8 | uint16(cdat[6*x+1])
gcol := uint16(cdat[6*x+2])<<8 | uint16(cdat[6*x+3])
bcol := uint16(cdat[6*x+4])<<8 | uint16(cdat[6*x+5])
rgba64.Set(x, y, image.RGBA64Color{rcol, gcol, bcol, 0xffff})
}
case cbTCA16:
for x := 0; x < d.width; x++ {
rcol := uint16(cdat[8*x+0])<<8 | uint16(cdat[8*x+1])
gcol := uint16(cdat[8*x+2])<<8 | uint16(cdat[8*x+3])
bcol := uint16(cdat[8*x+4])<<8 | uint16(cdat[8*x+5])
acol := uint16(cdat[8*x+6])<<8 | uint16(cdat[8*x+7])
nrgba64.Set(x, y, image.NRGBA64Color{rcol, gcol, bcol, acol})
}
} }
// The current row for y is the previous row for y+1. // The current row for y is the previous row for y+1.
...@@ -398,7 +457,7 @@ func (d *decoder) parseChunk(r io.Reader) os.Error { ...@@ -398,7 +457,7 @@ func (d *decoder) parseChunk(r io.Reader) os.Error {
} }
err = d.parsetRNS(r, crc, length) err = d.parsetRNS(r, crc, length)
case "IDAT": case "IDAT":
if d.stage < dsSeenIHDR || d.stage > dsSeenIDAT || (d.colorType == ctPaletted && d.stage == dsSeenIHDR) { if d.stage < dsSeenIHDR || d.stage > dsSeenIDAT || (d.cb == cbP8 && d.stage == dsSeenIHDR) {
return chunkOrderError return chunkOrderError
} }
d.stage = dsSeenIDAT d.stage = dsSeenIDAT
......
...@@ -14,23 +14,24 @@ import ( ...@@ -14,23 +14,24 @@ import (
) )
// The go PNG library currently supports only a subset of the full PNG specification. // The go PNG library currently supports only a subset of the full PNG specification.
// In particular, bit depths other than 8 are not supported, and neither are grayscale images. // In particular, bit depths other than 8 or 16 are not supported, nor are grayscale-
// alpha images.
var filenames = []string{ var filenames = []string{
//"basn0g01", // bit depth is not 8 //"basn0g01", // bit depth is not 8 or 16
//"basn0g02", // bit depth is not 8 //"basn0g02", // bit depth is not 8 or 16
//"basn0g04", // bit depth is not 8 //"basn0g04", // bit depth is not 8 or 16
"basn0g08", "basn0g08",
//"basn0g16", // bit depth is not 8 "basn0g16",
"basn2c08", "basn2c08",
//"basn2c16", // bit depth is not 8 "basn2c16",
//"basn3p01", // bit depth is not 8 //"basn3p01", // bit depth is not 8 or 16
//"basn3p02", // bit depth is not 8 //"basn3p02", // bit depth is not 8 or 16
//"basn3p04", // bit depth is not 8 //"basn3p04", // bit depth is not 8 or 16
"basn3p08", "basn3p08",
//"basn4a08", // grayscale color model //"basn4a08", // grayscale-alpha color model
//"basn4a16", // bit depth is not 8 //"basn4a16", // grayscale-alpha color model
"basn6a08", "basn6a08",
//"basn6a16", // bit depth is not 8 "basn6a16",
} }
func readPng(filename string) (image.Image, os.Error) { func readPng(filename string) (image.Image, os.Error) {
...@@ -46,25 +47,33 @@ func readPng(filename string) (image.Image, os.Error) { ...@@ -46,25 +47,33 @@ func readPng(filename string) (image.Image, os.Error) {
func sng(w io.WriteCloser, filename string, png image.Image) { func sng(w io.WriteCloser, filename string, png image.Image) {
defer w.Close() defer w.Close()
bounds := png.Bounds() bounds := png.Bounds()
// For now, the go PNG parser only reads bitdepths of 8. cm := png.ColorModel()
bitdepth := 8 var bitdepth int
switch cm {
case image.RGBAColorModel, image.NRGBAColorModel, image.AlphaColorModel, image.GrayColorModel:
bitdepth = 8
default:
bitdepth = 16
}
cpm, _ := cm.(image.PalettedColorModel)
var paletted *image.Paletted
if cpm != nil {
bitdepth = 8
paletted = png.(*image.Paletted)
}
// Write the filename and IHDR. // Write the filename and IHDR.
io.WriteString(w, "#SNG: from "+filename+".png\nIHDR {\n") io.WriteString(w, "#SNG: from "+filename+".png\nIHDR {\n")
fmt.Fprintf(w, " width: %d; height: %d; bitdepth: %d;\n", bounds.Dx(), bounds.Dy(), bitdepth) fmt.Fprintf(w, " width: %d; height: %d; bitdepth: %d;\n", bounds.Dx(), bounds.Dy(), bitdepth)
cm := png.ColorModel()
var paletted *image.Paletted
cpm, _ := cm.(image.PalettedColorModel)
switch { switch {
case cm == image.GrayColorModel: case cm == image.RGBAColorModel, cm == image.RGBA64ColorModel:
io.WriteString(w, " using grayscale;\n")
case cm == image.RGBAColorModel:
io.WriteString(w, " using color;\n") io.WriteString(w, " using color;\n")
case cm == image.NRGBAColorModel: case cm == image.NRGBAColorModel, cm == image.NRGBA64ColorModel:
io.WriteString(w, " using color alpha;\n") io.WriteString(w, " using color alpha;\n")
case cm == image.GrayColorModel, cm == image.Gray16ColorModel:
io.WriteString(w, " using grayscale;\n")
case cpm != nil: case cpm != nil:
io.WriteString(w, " using color palette;\n") io.WriteString(w, " using color palette;\n")
paletted = png.(*image.Paletted)
default: default:
io.WriteString(w, "unknown PNG decoder color model\n") io.WriteString(w, "unknown PNG decoder color model\n")
} }
...@@ -96,16 +105,31 @@ func sng(w io.WriteCloser, filename string, png image.Image) { ...@@ -96,16 +105,31 @@ func sng(w io.WriteCloser, filename string, png image.Image) {
gray := png.At(x, y).(image.GrayColor) gray := png.At(x, y).(image.GrayColor)
fmt.Fprintf(w, "%02x", gray.Y) fmt.Fprintf(w, "%02x", gray.Y)
} }
case cm == image.Gray16ColorModel:
for x := bounds.Min.X; x < bounds.Max.X; x++ {
gray16 := png.At(x, y).(image.Gray16Color)
fmt.Fprintf(w, "%04x ", gray16.Y)
}
case cm == image.RGBAColorModel: case cm == image.RGBAColorModel:
for x := bounds.Min.X; x < bounds.Max.X; x++ { for x := bounds.Min.X; x < bounds.Max.X; x++ {
rgba := png.At(x, y).(image.RGBAColor) rgba := png.At(x, y).(image.RGBAColor)
fmt.Fprintf(w, "%02x%02x%02x ", rgba.R, rgba.G, rgba.B) fmt.Fprintf(w, "%02x%02x%02x ", rgba.R, rgba.G, rgba.B)
} }
case cm == image.RGBA64ColorModel:
for x := bounds.Min.X; x < bounds.Max.X; x++ {
rgba64 := png.At(x, y).(image.RGBA64Color)
fmt.Fprintf(w, "%04x%04x%04x ", rgba64.R, rgba64.G, rgba64.B)
}
case cm == image.NRGBAColorModel: case cm == image.NRGBAColorModel:
for x := bounds.Min.X; x < bounds.Max.X; x++ { for x := bounds.Min.X; x < bounds.Max.X; x++ {
nrgba := png.At(x, y).(image.NRGBAColor) nrgba := png.At(x, y).(image.NRGBAColor)
fmt.Fprintf(w, "%02x%02x%02x%02x ", nrgba.R, nrgba.G, nrgba.B, nrgba.A) fmt.Fprintf(w, "%02x%02x%02x%02x ", nrgba.R, nrgba.G, nrgba.B, nrgba.A)
} }
case cm == image.NRGBA64ColorModel:
for x := bounds.Min.X; x < bounds.Max.X; x++ {
nrgba64 := png.At(x, y).(image.NRGBA64Color)
fmt.Fprintf(w, "%04x%04x%04x%04x ", nrgba64.R, nrgba64.G, nrgba64.B, nrgba64.A)
}
case cpm != nil: case cpm != nil:
for x := bounds.Min.X; x < bounds.Max.X; x++ { for x := bounds.Min.X; x < bounds.Max.X; x++ {
fmt.Fprintf(w, "%02x", paletted.ColorIndexAt(x, y)) fmt.Fprintf(w, "%02x", paletted.ColorIndexAt(x, y))
......
...@@ -17,7 +17,7 @@ import ( ...@@ -17,7 +17,7 @@ import (
type encoder struct { type encoder struct {
w io.Writer w io.Writer
m image.Image m image.Image
colorType uint8 cb int
err os.Error err os.Error
header [8]byte header [8]byte
footer [4]byte footer [4]byte
...@@ -95,8 +95,30 @@ func (e *encoder) writeIHDR() { ...@@ -95,8 +95,30 @@ func (e *encoder) writeIHDR() {
b := e.m.Bounds() b := e.m.Bounds()
writeUint32(e.tmp[0:4], uint32(b.Dx())) writeUint32(e.tmp[0:4], uint32(b.Dx()))
writeUint32(e.tmp[4:8], uint32(b.Dy())) writeUint32(e.tmp[4:8], uint32(b.Dy()))
e.tmp[8] = 8 // bit depth // Set bit depth and color type.
e.tmp[9] = e.colorType switch e.cb {
case cbG8:
e.tmp[8] = 8
e.tmp[9] = ctGrayscale
case cbTC8:
e.tmp[8] = 8
e.tmp[9] = ctTrueColor
case cbP8:
e.tmp[8] = 8
e.tmp[9] = ctPaletted
case cbTCA8:
e.tmp[8] = 8
e.tmp[9] = ctTrueColorAlpha
case cbG16:
e.tmp[8] = 16
e.tmp[9] = ctGrayscale
case cbTC16:
e.tmp[8] = 16
e.tmp[9] = ctTrueColor
case cbTCA16:
e.tmp[8] = 16
e.tmp[9] = ctTrueColorAlpha
}
e.tmp[10] = 0 // default compression method e.tmp[10] = 0 // default compression method
e.tmp[11] = 0 // default filter method e.tmp[11] = 0 // default filter method
e.tmp[12] = 0 // non-interlaced e.tmp[12] = 0 // non-interlaced
...@@ -233,7 +255,7 @@ func filter(cr [][]byte, pr []byte, bpp int) int { ...@@ -233,7 +255,7 @@ func filter(cr [][]byte, pr []byte, bpp int) int {
return filter return filter
} }
func writeImage(w io.Writer, m image.Image, ct uint8) os.Error { func writeImage(w io.Writer, m image.Image, cb int) os.Error {
zw, err := zlib.NewWriter(w) zw, err := zlib.NewWriter(w)
if err != nil { if err != nil {
return err return err
...@@ -242,16 +264,22 @@ func writeImage(w io.Writer, m image.Image, ct uint8) os.Error { ...@@ -242,16 +264,22 @@ func writeImage(w io.Writer, m image.Image, ct uint8) os.Error {
bpp := 0 // Bytes per pixel. bpp := 0 // Bytes per pixel.
var paletted *image.Paletted var paletted *image.Paletted
switch ct { switch cb {
case ctGrayscale: case cbG8:
bpp = 1 bpp = 1
case ctTrueColor: case cbTC8:
bpp = 3 bpp = 3
case ctPaletted: case cbP8:
bpp = 1 bpp = 1
paletted = m.(*image.Paletted) paletted = m.(*image.Paletted)
case ctTrueColorAlpha: case cbTCA8:
bpp = 4 bpp = 4
case cbTC16:
bpp = 6
case cbTCA16:
bpp = 8
case cbG16:
bpp = 2
} }
// cr[*] and pr are the bytes for the current and previous row. // cr[*] and pr are the bytes for the current and previous row.
// cr[0] is unfiltered (or equivalently, filtered with the ftNone filter). // cr[0] is unfiltered (or equivalently, filtered with the ftNone filter).
...@@ -268,13 +296,13 @@ func writeImage(w io.Writer, m image.Image, ct uint8) os.Error { ...@@ -268,13 +296,13 @@ func writeImage(w io.Writer, m image.Image, ct uint8) os.Error {
for y := b.Min.Y; y < b.Max.Y; y++ { for y := b.Min.Y; y < b.Max.Y; y++ {
// Convert from colors to bytes. // Convert from colors to bytes.
switch ct { switch cb {
case ctGrayscale: case cbG8:
for x := b.Min.X; x < b.Max.X; x++ { for x := b.Min.X; x < b.Max.X; x++ {
c := image.GrayColorModel.Convert(m.At(x, y)).(image.GrayColor) c := image.GrayColorModel.Convert(m.At(x, y)).(image.GrayColor)
cr[0][x+1] = c.Y cr[0][x+1] = c.Y
} }
case ctTrueColor: case cbTC8:
for x := b.Min.X; x < b.Max.X; x++ { for x := b.Min.X; x < b.Max.X; x++ {
// We have previously verified that the alpha value is fully opaque. // We have previously verified that the alpha value is fully opaque.
r, g, b, _ := m.At(x, y).RGBA() r, g, b, _ := m.At(x, y).RGBA()
...@@ -282,11 +310,11 @@ func writeImage(w io.Writer, m image.Image, ct uint8) os.Error { ...@@ -282,11 +310,11 @@ func writeImage(w io.Writer, m image.Image, ct uint8) os.Error {
cr[0][3*x+2] = uint8(g >> 8) cr[0][3*x+2] = uint8(g >> 8)
cr[0][3*x+3] = uint8(b >> 8) cr[0][3*x+3] = uint8(b >> 8)
} }
case ctPaletted: case cbP8:
for x := b.Min.X; x < b.Max.X; x++ { for x := b.Min.X; x < b.Max.X; x++ {
cr[0][x+1] = paletted.ColorIndexAt(x, y) cr[0][x+1] = paletted.ColorIndexAt(x, y)
} }
case ctTrueColorAlpha: case cbTCA8:
// Convert from image.Image (which is alpha-premultiplied) to PNG's non-alpha-premultiplied. // Convert from image.Image (which is alpha-premultiplied) to PNG's non-alpha-premultiplied.
for x := b.Min.X; x < b.Max.X; x++ { for x := b.Min.X; x < b.Max.X; x++ {
c := image.NRGBAColorModel.Convert(m.At(x, y)).(image.NRGBAColor) c := image.NRGBAColorModel.Convert(m.At(x, y)).(image.NRGBAColor)
...@@ -295,6 +323,36 @@ func writeImage(w io.Writer, m image.Image, ct uint8) os.Error { ...@@ -295,6 +323,36 @@ func writeImage(w io.Writer, m image.Image, ct uint8) os.Error {
cr[0][4*x+3] = c.B cr[0][4*x+3] = c.B
cr[0][4*x+4] = c.A cr[0][4*x+4] = c.A
} }
case cbG16:
for x := b.Min.X; x < b.Max.X; x++ {
c := image.Gray16ColorModel.Convert(m.At(x, y)).(image.Gray16Color)
cr[0][2*x+1] = uint8(c.Y >> 8)
cr[0][2*x+2] = uint8(c.Y)
}
case cbTC16:
for x := b.Min.X; x < b.Max.X; x++ {
// We have previously verified that the alpha value is fully opaque.
r, g, b, _ := m.At(x, y).RGBA()
cr[0][6*x+1] = uint8(r >> 8)
cr[0][6*x+2] = uint8(r)
cr[0][6*x+3] = uint8(g >> 8)
cr[0][6*x+4] = uint8(g)
cr[0][6*x+5] = uint8(b >> 8)
cr[0][6*x+6] = uint8(b)
}
case cbTCA16:
// Convert from image.Image (which is alpha-premultiplied) to PNG's non-alpha-premultiplied.
for x := b.Min.X; x < b.Max.X; x++ {
c := image.NRGBA64ColorModel.Convert(m.At(x, y)).(image.NRGBA64Color)
cr[0][8*x+1] = uint8(c.R >> 8)
cr[0][8*x+2] = uint8(c.R)
cr[0][8*x+3] = uint8(c.G >> 8)
cr[0][8*x+4] = uint8(c.G)
cr[0][8*x+5] = uint8(c.B >> 8)
cr[0][8*x+6] = uint8(c.B)
cr[0][8*x+7] = uint8(c.A >> 8)
cr[0][8*x+8] = uint8(c.A)
}
} }
// Apply the filter. // Apply the filter.
...@@ -322,7 +380,7 @@ func (e *encoder) writeIDATs() { ...@@ -322,7 +380,7 @@ func (e *encoder) writeIDATs() {
if e.err != nil { if e.err != nil {
return return
} }
e.err = writeImage(bw, e.m, e.colorType) e.err = writeImage(bw, e.m, e.cb)
if e.err != nil { if e.err != nil {
return return
} }
...@@ -345,17 +403,26 @@ func Encode(w io.Writer, m image.Image) os.Error { ...@@ -345,17 +403,26 @@ func Encode(w io.Writer, m image.Image) os.Error {
var e encoder var e encoder
e.w = w e.w = w
e.m = m e.m = m
e.colorType = ctTrueColorAlpha
pal, _ := m.(*image.Paletted) pal, _ := m.(*image.Paletted)
if pal != nil { if pal != nil {
e.colorType = ctPaletted e.cb = cbP8
} else { } else {
switch m.ColorModel() { switch m.ColorModel() {
case image.GrayColorModel: case image.GrayColorModel:
e.colorType = ctGrayscale e.cb = cbG8
case image.Gray16ColorModel:
e.cb = cbG16
case image.RGBAColorModel, image.NRGBAColorModel, image.AlphaColorModel:
if opaque(m) {
e.cb = cbTC8
} else {
e.cb = cbTCA8
}
default: default:
if opaque(m) { if opaque(m) {
e.colorType = ctTrueColor e.cb = cbTC16
} else {
e.cb = cbTCA16
} }
} }
} }
......
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