-
-
Save makimoto/c59f2023c530f0e15715 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// decode decodes the IDAT data into an image. | |
func (d *decoder) decode() (image.Image, error) { | |
r, err := zlib.NewReader(d) | |
if err != nil { | |
return nil, err | |
} | |
defer r.Close() | |
bitsPerPixel := 0 | |
pixOffset := 0 | |
var ( | |
gray *image.Gray | |
rgba *image.RGBA | |
paletted *image.Paletted | |
nrgba *image.NRGBA | |
gray16 *image.Gray16 | |
rgba64 *image.RGBA64 | |
nrgba64 *image.NRGBA64 | |
img image.Image | |
) | |
switch d.cb { | |
case cbG1, cbG2, cbG4, cbG8: | |
bitsPerPixel = d.depth | |
gray = image.NewGray(image.Rect(0, 0, d.width, d.height)) | |
img = gray | |
case cbGA8: | |
bitsPerPixel = 16 | |
nrgba = image.NewNRGBA(image.Rect(0, 0, d.width, d.height)) | |
img = nrgba | |
case cbTC8: | |
bitsPerPixel = 24 | |
rgba = image.NewRGBA(image.Rect(0, 0, d.width, d.height)) | |
img = rgba | |
case cbP1, cbP2, cbP4, cbP8: | |
bitsPerPixel = d.depth | |
paletted = image.NewPaletted(image.Rect(0, 0, d.width, d.height), d.palette) | |
img = paletted | |
case cbTCA8: | |
bitsPerPixel = 32 | |
nrgba = image.NewNRGBA(image.Rect(0, 0, d.width, d.height)) | |
img = nrgba | |
case cbG16: | |
bitsPerPixel = 16 | |
gray16 = image.NewGray16(image.Rect(0, 0, d.width, d.height)) | |
img = gray16 | |
case cbGA16: | |
bitsPerPixel = 32 | |
nrgba64 = image.NewNRGBA64(image.Rect(0, 0, d.width, d.height)) | |
img = nrgba64 | |
case cbTC16: | |
bitsPerPixel = 48 | |
rgba64 = image.NewRGBA64(image.Rect(0, 0, d.width, d.height)) | |
img = rgba64 | |
case cbTCA16: | |
bitsPerPixel = 64 | |
nrgba64 = image.NewNRGBA64(image.Rect(0, 0, d.width, d.height)) | |
img = nrgba64 | |
} | |
bytesPerPixel := (bitsPerPixel + 7) / 8 | |
// 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]. | |
cr := make([]uint8, 1+(bitsPerPixel*d.width+7)/8) | |
pr := make([]uint8, 1+(bitsPerPixel*d.width+7)/8) | |
for y := 0; y < d.height; y++ { | |
// Read the decompressed bytes. | |
_, err := io.ReadFull(r, cr) | |
if err != nil { | |
return nil, err | |
} | |
// Apply the filter. | |
cdat := cr[1:] | |
pdat := pr[1:] | |
switch cr[0] { | |
case ftNone: | |
// No-op. | |
case ftSub: | |
for i := bytesPerPixel; i < len(cdat); i++ { | |
cdat[i] += cdat[i-bytesPerPixel] | |
} | |
case ftUp: | |
for i, p := range pdat { | |
cdat[i] += p | |
} | |
case ftAverage: | |
for i := 0; i < bytesPerPixel; i++ { | |
cdat[i] += pdat[i] / 2 | |
} | |
for i := bytesPerPixel; i < len(cdat); i++ { | |
cdat[i] += uint8((int(cdat[i-bytesPerPixel]) + int(pdat[i])) / 2) | |
} | |
case ftPaeth: | |
filterPaeth(cdat, pdat, bytesPerPixel) | |
default: | |
return nil, FormatError("bad filter type") | |
} | |
// Convert from bytes to colors. | |
switch d.cb { | |
case cbG1: | |
for x := 0; x < d.width; x += 8 { | |
b := cdat[x/8] | |
for x2 := 0; x2 < 8 && x+x2 < d.width; x2++ { | |
gray.SetGray(x+x2, y, color.Gray{(b >> 7) * 0xff}) | |
b <<= 1 | |
} | |
} | |
case cbG2: | |
for x := 0; x < d.width; x += 4 { | |
b := cdat[x/4] | |
for x2 := 0; x2 < 4 && x+x2 < d.width; x2++ { | |
gray.SetGray(x+x2, y, color.Gray{(b >> 6) * 0x55}) | |
b <<= 2 | |
} | |
} | |
case cbG4: | |
for x := 0; x < d.width; x += 2 { | |
b := cdat[x/2] | |
for x2 := 0; x2 < 2 && x+x2 < d.width; x2++ { | |
gray.SetGray(x+x2, y, color.Gray{(b >> 4) * 0x11}) | |
b <<= 4 | |
} | |
} | |
case cbG8: | |
copy(gray.Pix[pixOffset:], cdat) | |
pixOffset += gray.Stride | |
case cbGA8: | |
for x := 0; x < d.width; x++ { | |
ycol := cdat[2*x+0] | |
nrgba.SetNRGBA(x, y, color.NRGBA{ycol, ycol, ycol, cdat[2*x+1]}) | |
} | |
case cbTC8: | |
pix, i, j := rgba.Pix, pixOffset, 0 | |
for x := 0; x < d.width; x++ { | |
pix[i+0] = cdat[j+0] | |
pix[i+1] = cdat[j+1] | |
pix[i+2] = cdat[j+2] | |
pix[i+3] = 0xff | |
i += 4 | |
j += 3 | |
} | |
pixOffset += rgba.Stride | |
case cbP1: | |
for x := 0; x < d.width; x += 8 { | |
b := cdat[x/8] | |
for x2 := 0; x2 < 8 && x+x2 < d.width; x2++ { | |
idx := b >> 7 | |
if len(paletted.Palette) <= int(idx) { | |
paletted.Palette = paletted.Palette[:int(idx)+1] | |
} | |
paletted.SetColorIndex(x+x2, y, idx) | |
b <<= 1 | |
} | |
} | |
case cbP2: | |
for x := 0; x < d.width; x += 4 { | |
b := cdat[x/4] | |
for x2 := 0; x2 < 4 && x+x2 < d.width; x2++ { | |
idx := b >> 6 | |
if len(paletted.Palette) <= int(idx) { | |
paletted.Palette = paletted.Palette[:int(idx)+1] | |
} | |
paletted.SetColorIndex(x+x2, y, idx) | |
b <<= 2 | |
} | |
} | |
case cbP4: | |
for x := 0; x < d.width; x += 2 { | |
b := cdat[x/2] | |
for x2 := 0; x2 < 2 && x+x2 < d.width; x2++ { | |
idx := b >> 4 | |
if len(paletted.Palette) <= int(idx) { | |
paletted.Palette = paletted.Palette[:int(idx)+1] | |
} | |
paletted.SetColorIndex(x+x2, y, idx) | |
b <<= 4 | |
} | |
} | |
case cbP8: | |
if len(paletted.Palette) != 255 { | |
for x := 0; x < d.width; x++ { | |
if len(paletted.Palette) <= int(cdat[x]) { | |
paletted.Palette = paletted.Palette[:int(cdat[x])+1] | |
} | |
} | |
} | |
copy(paletted.Pix[pixOffset:], cdat) | |
pixOffset += paletted.Stride | |
case cbTCA8: | |
copy(nrgba.Pix[pixOffset:], cdat) | |
pixOffset += nrgba.Stride | |
case cbG16: | |
for x := 0; x < d.width; x++ { | |
ycol := uint16(cdat[2*x+0])<<8 | uint16(cdat[2*x+1]) | |
gray16.SetGray16(x, y, color.Gray16{ycol}) | |
} | |
case cbGA16: | |
for x := 0; x < d.width; x++ { | |
ycol := uint16(cdat[4*x+0])<<8 | uint16(cdat[4*x+1]) | |
acol := uint16(cdat[4*x+2])<<8 | uint16(cdat[4*x+3]) | |
nrgba64.SetNRGBA64(x, y, color.NRGBA64{ycol, ycol, ycol, acol}) | |
} | |
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.SetRGBA64(x, y, color.RGBA64{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.SetNRGBA64(x, y, color.NRGBA64{rcol, gcol, bcol, acol}) | |
} | |
} | |
// The current row for y is the previous row for y+1. | |
pr, cr = cr, pr | |
} | |
// Check for EOF, to verify the zlib checksum. | |
n := 0 | |
for i := 0; n == 0 && err == nil; i++ { | |
if i == 100 { | |
return nil, io.ErrNoProgress | |
} | |
n, err = r.Read(pr[:1]) | |
} | |
if err != nil && err != io.EOF { | |
return nil, FormatError(err.Error()) | |
} | |
if n != 0 || d.idatLength != 0 { | |
return nil, FormatError("too much pixel data") | |
} | |
return img, nil | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment