func modeTerrain(p *image.Paletted, l int) uint8 { b := p.Bounds() modeMap := make([]uint8, l) var most, mode uint8 for i := b.Min.X; i < b.Max.X; i++ { for j := b.Min.Y; j < b.Max.Y; j++ { pos := p.ColorIndexAt(i, j) modeMap[pos]++ if m := modeMap[pos]; m > most { most = m mode = pos } } } return mode }
// An approximation of the sng command-line tool. func sng(w io.WriteCloser, filename string, png image.Image) { defer w.Close() bounds := png.Bounds() cm := png.ColorModel() var bitdepth int switch cm { case color.RGBAModel, color.NRGBAModel, color.AlphaModel, color.GrayModel: bitdepth = 8 default: bitdepth = 16 } cpm, _ := cm.(color.Palette) var paletted *image.Paletted if cpm != nil { switch { case len(cpm) <= 2: bitdepth = 1 case len(cpm) <= 4: bitdepth = 2 case len(cpm) <= 16: bitdepth = 4 default: bitdepth = 8 } paletted = png.(*image.Paletted) } // Write the filename and IHDR. io.WriteString(w, "#SNG: from "+filename+".png\nIHDR {\n") fmt.Fprintf(w, " width: %d; height: %d; bitdepth: %d;\n", bounds.Dx(), bounds.Dy(), bitdepth) switch { case cm == color.RGBAModel, cm == color.RGBA64Model: io.WriteString(w, " using color;\n") case cm == color.NRGBAModel, cm == color.NRGBA64Model: io.WriteString(w, " using color alpha;\n") case cm == color.GrayModel, cm == color.Gray16Model: io.WriteString(w, " using grayscale;\n") case cpm != nil: io.WriteString(w, " using color palette;\n") default: io.WriteString(w, "unknown PNG decoder color model\n") } io.WriteString(w, "}\n") // We fake a gAMA output. The test files have a gAMA chunk but the go PNG parser ignores it // (the PNG spec section 11.3 says "Ancillary chunks may be ignored by a decoder"). io.WriteString(w, "gAMA {1.0000}\n") // Write the PLTE and tRNS (if applicable). if cpm != nil { lastAlpha := -1 io.WriteString(w, "PLTE {\n") for i, c := range cpm { r, g, b, a := c.RGBA() if a != 0xffff { lastAlpha = i } r >>= 8 g >>= 8 b >>= 8 fmt.Fprintf(w, " (%3d,%3d,%3d) # rgb = (0x%02x,0x%02x,0x%02x)\n", r, g, b, r, g, b) } io.WriteString(w, "}\n") if lastAlpha != -1 { io.WriteString(w, "tRNS {\n") for i := 0; i <= lastAlpha; i++ { _, _, _, a := cpm[i].RGBA() a >>= 8 fmt.Fprintf(w, " %d", a) } io.WriteString(w, "}\n") } } // Write the IMAGE. io.WriteString(w, "IMAGE {\n pixels hex\n") for y := bounds.Min.Y; y < bounds.Max.Y; y++ { switch { case cm == color.GrayModel: for x := bounds.Min.X; x < bounds.Max.X; x++ { gray := png.At(x, y).(color.Gray) fmt.Fprintf(w, "%02x", gray.Y) } case cm == color.Gray16Model: for x := bounds.Min.X; x < bounds.Max.X; x++ { gray16 := png.At(x, y).(color.Gray16) fmt.Fprintf(w, "%04x ", gray16.Y) } case cm == color.RGBAModel: for x := bounds.Min.X; x < bounds.Max.X; x++ { rgba := png.At(x, y).(color.RGBA) fmt.Fprintf(w, "%02x%02x%02x ", rgba.R, rgba.G, rgba.B) } case cm == color.RGBA64Model: for x := bounds.Min.X; x < bounds.Max.X; x++ { rgba64 := png.At(x, y).(color.RGBA64) fmt.Fprintf(w, "%04x%04x%04x ", rgba64.R, rgba64.G, rgba64.B) } case cm == color.NRGBAModel: for x := bounds.Min.X; x < bounds.Max.X; x++ { nrgba := png.At(x, y).(color.NRGBA) fmt.Fprintf(w, "%02x%02x%02x%02x ", nrgba.R, nrgba.G, nrgba.B, nrgba.A) } case cm == color.NRGBA64Model: for x := bounds.Min.X; x < bounds.Max.X; x++ { nrgba64 := png.At(x, y).(color.NRGBA64) fmt.Fprintf(w, "%04x%04x%04x%04x ", nrgba64.R, nrgba64.G, nrgba64.B, nrgba64.A) } case cpm != nil: var b, c int for x := bounds.Min.X; x < bounds.Max.X; x++ { b = b<<uint(bitdepth) | int(paletted.ColorIndexAt(x, y)) c++ if c == 8/bitdepth { fmt.Fprintf(w, "%02x", b) b = 0 c = 0 } } } io.WriteString(w, "\n") } io.WriteString(w, "}\n") }
func writeImage(w io.Writer, m image.Image, ct uint8) os.Error { zw, err := zlib.NewDeflater(w) if err != nil { return err } defer zw.Close() bpp := 0 // Bytes per pixel. var paletted *image.Paletted switch ct { case ctTrueColor: bpp = 3 case ctPaletted: bpp = 1 paletted = m.(*image.Paletted) case ctTrueColorAlpha: bpp = 4 } // cr[*] and pr are the bytes for the current and previous row. // cr[0] is unfiltered (or equivalently, filtered with the ftNone filter). // cr[ft], for non-zero filter types ft, are buffers for transforming cr[0] under the // other PNG filter types. These buffers are allocated once and re-used for each row. // The +1 is for the per-row filter type, which is at cr[*][0]. var cr [nFilter][]uint8 for i := 0; i < len(cr); i++ { cr[i] = make([]uint8, 1+bpp*m.Width()) cr[i][0] = uint8(i) } pr := make([]uint8, 1+bpp*m.Width()) for y := 0; y < m.Height(); y++ { // Convert from colors to bytes. switch ct { case ctTrueColor: for x := 0; x < m.Width(); x++ { // We have previously verified that the alpha value is fully opaque. r, g, b, _ := m.At(x, y).RGBA() cr[0][3*x+1] = uint8(r >> 24) cr[0][3*x+2] = uint8(g >> 24) cr[0][3*x+3] = uint8(b >> 24) } case ctPaletted: for x := 0; x < m.Width(); x++ { cr[0][x+1] = paletted.ColorIndexAt(x, y) } case ctTrueColorAlpha: // Convert from image.Image (which is alpha-premultiplied) to PNG's non-alpha-premultiplied. for x := 0; x < m.Width(); x++ { c := image.NRGBAColorModel.Convert(m.At(x, y)).(image.NRGBAColor) cr[0][4*x+1] = c.R cr[0][4*x+2] = c.G cr[0][4*x+3] = c.B cr[0][4*x+4] = c.A } } // Apply the filter. f := filter(cr[0:nFilter], pr, bpp) // Write the compressed bytes. _, err = zw.Write(cr[f]) if err != nil { return err } // The current row for y is the previous row for y+1. pr, cr[0] = cr[0], pr } return nil }
func (g *generator) buildTerrain(mpath minecraft.Path, level *level, terrain, biomes, plants *image.Paletted, height, water *image.Gray, c chan paint) error { b := terrain.Bounds() proceed := make(chan uint8, 10) errChan := make(chan error, 1) go func() { defer close(proceed) cc := newCache(g.Terrain.Blocks) for j := 0; j < b.Max.Y; j += 16 { chunkZ := int32(j >> 4) for i := 0; i < b.Max.X; i += 16 { chunkX := int32(i >> 4) h := int32(meanHeight(height.SubImage(image.Rect(i, j, i+16, j+16)).(*image.Gray))) wh := int32(meanHeight(water.SubImage(image.Rect(i, j, i+16, j+16)).(*image.Gray))) var t uint8 if wh >= h<<1 { // more water than land... c <- paint{ color.RGBA{0, 0, 255, 255}, chunkX, chunkZ, } t = uint8(len(g.Terrain.Blocks) - 1) h = wh } else { t = modeTerrain(terrain.SubImage(image.Rect(i, j, i+16, j+16)).(*image.Paletted), len(g.Terrain.Palette)) c <- paint{ g.Terrain.Palette[t], chunkX, chunkZ, } } if err := mpath.SetChunk(cc.getFromCache(chunkX, chunkZ, t, h)); err != nil { errChan <- err return } proceed <- t } } }() ts := make([]uint8, 0, 1024) for i := 0; i < (b.Max.X>>4)+2; i++ { ts = append(ts, <-proceed) // get far enough ahead so all chunks are surrounded before shaping, to get correct lighting } select { case err := <-errChan: return err default: } for j := int32(0); j < int32(b.Max.Y); j += 16 { chunkZ := j >> 4 for i := int32(0); i < int32(b.Max.X); i += 16 { chunkX := i >> 4 var totalHeight int32 ot := ts[0] ts = ts[1:] oy, _ := level.GetHeight(i, j) for x := i; x < i+16; x++ { for z := j; z < j+16; z++ { if biomes != nil { level.SetBiome(x, z, g.Biomes.Values[biomes.ColorIndexAt(int(x), int(z))]) } h := int32(height.GrayAt(int(x), int(z)).Y) totalHeight += h wl := int32(water.GrayAt(int(x), int(z)).Y) y := oy if h > y { y = h } if wl > y { y = wl } for ; y > h && y > wl; y-- { level.SetBlock(x, y, z, minecraft.Block{}) } if plants != nil { p := g.Plants.Blocks[plants.ColorIndexAt(int(x), int(z))] py := int32(1) for ; py <= int32(p.Level); py++ { level.SetBlock(x, y+py, z, p.Base) } level.SetBlock(x, y+py, z, p.Top) } for ; y > h; y-- { level.SetBlock(x, y, z, minecraft.Block{ID: 9}) } t := terrain.ColorIndexAt(int(x), int(z)) tb := g.Terrain.Blocks[t] for ; y > h-int32(tb.Level); y-- { level.SetBlock(x, y, z, tb.Top) } if t != ot { h = 0 } else { h = oy } for ; y >= h; y-- { level.SetBlock(x, y, z, tb.Base) } } } c <- paint{ color.Alpha{uint8(totalHeight >> 8)}, chunkX, chunkZ, } select { case p, ok := <-proceed: if ok { ts = append(ts, p) } case err := <-errChan: return err } } } return nil }
// An approximation of the sng command-line tool. func sng(w io.WriteCloser, filename string, png image.Image) { defer w.Close() bounds := png.Bounds() cm := png.ColorModel() var bitdepth int switch cm { case color.RGBAModel, color.NRGBAModel, color.AlphaModel, color.GrayModel: bitdepth = 8 default: bitdepth = 16 } cpm, _ := cm.(color.Palette) var paletted *image.Paletted if cpm != nil { switch { case len(cpm) <= 2: bitdepth = 1 case len(cpm) <= 4: bitdepth = 2 case len(cpm) <= 16: bitdepth = 4 default: bitdepth = 8 } paletted = png.(*image.Paletted) } // Write the filename and IHDR. io.WriteString(w, "#SNG: from "+filename+".png\nIHDR {\n") fmt.Fprintf(w, " width: %d; height: %d; bitdepth: %d;\n", bounds.Dx(), bounds.Dy(), bitdepth) if s, ok := fakeIHDRUsings[filename]; ok { io.WriteString(w, s) } else { switch { case cm == color.RGBAModel, cm == color.RGBA64Model: io.WriteString(w, " using color;\n") case cm == color.NRGBAModel, cm == color.NRGBA64Model: io.WriteString(w, " using color alpha;\n") case cm == color.GrayModel, cm == color.Gray16Model: io.WriteString(w, " using grayscale;\n") case cpm != nil: io.WriteString(w, " using color palette;\n") default: io.WriteString(w, "unknown PNG decoder color model\n") } } io.WriteString(w, "}\n") // We fake a gAMA chunk. The test files have a gAMA chunk but the go PNG // parser ignores it (the PNG spec section 11.3 says "Ancillary chunks may // be ignored by a decoder"). if s, ok := fakegAMAs[filename]; ok { io.WriteString(w, s) } else { io.WriteString(w, "gAMA {1.0000}\n") } // Write the PLTE and tRNS (if applicable). useTransparent := false if cpm != nil { lastAlpha := -1 io.WriteString(w, "PLTE {\n") for i, c := range cpm { var r, g, b, a uint8 switch c := c.(type) { case color.RGBA: r, g, b, a = c.R, c.G, c.B, 0xff case color.NRGBA: r, g, b, a = c.R, c.G, c.B, c.A default: panic("unknown palette color type") } if a != 0xff { lastAlpha = i } fmt.Fprintf(w, " (%3d,%3d,%3d) # rgb = (0x%02x,0x%02x,0x%02x)\n", r, g, b, r, g, b) } io.WriteString(w, "}\n") if s, ok := fakebKGDs[filename]; ok { io.WriteString(w, s) } if lastAlpha != -1 { io.WriteString(w, "tRNS {\n") for i := 0; i <= lastAlpha; i++ { _, _, _, a := cpm[i].RGBA() a >>= 8 fmt.Fprintf(w, " %d", a) } io.WriteString(w, "}\n") } } else if strings.HasPrefix(filename, "ft") { if s, ok := fakebKGDs[filename]; ok { io.WriteString(w, s) } // We fake a tRNS chunk. The test files' grayscale and truecolor // transparent images all have their top left corner transparent. switch c := png.At(0, 0).(type) { case color.NRGBA: if c.A == 0 { useTransparent = true io.WriteString(w, "tRNS {\n") switch filename { case "ftbbn0g01", "ftbbn0g02", "ftbbn0g04": // The standard image package doesn't have a "gray with // alpha" type. Instead, we use an image.NRGBA. fmt.Fprintf(w, " gray: %d;\n", c.R) default: fmt.Fprintf(w, " red: %d; green: %d; blue: %d;\n", c.R, c.G, c.B) } io.WriteString(w, "}\n") } case color.NRGBA64: if c.A == 0 { useTransparent = true io.WriteString(w, "tRNS {\n") switch filename { case "ftbwn0g16": // The standard image package doesn't have a "gray16 with // alpha" type. Instead, we use an image.NRGBA64. fmt.Fprintf(w, " gray: %d;\n", c.R) default: fmt.Fprintf(w, " red: %d; green: %d; blue: %d;\n", c.R, c.G, c.B) } io.WriteString(w, "}\n") } } } // Write the IMAGE. io.WriteString(w, "IMAGE {\n pixels hex\n") for y := bounds.Min.Y; y < bounds.Max.Y; y++ { switch { case cm == color.GrayModel: for x := bounds.Min.X; x < bounds.Max.X; x++ { gray := png.At(x, y).(color.Gray) fmt.Fprintf(w, "%02x", gray.Y) } case cm == color.Gray16Model: for x := bounds.Min.X; x < bounds.Max.X; x++ { gray16 := png.At(x, y).(color.Gray16) fmt.Fprintf(w, "%04x ", gray16.Y) } case cm == color.RGBAModel: for x := bounds.Min.X; x < bounds.Max.X; x++ { rgba := png.At(x, y).(color.RGBA) fmt.Fprintf(w, "%02x%02x%02x ", rgba.R, rgba.G, rgba.B) } case cm == color.RGBA64Model: for x := bounds.Min.X; x < bounds.Max.X; x++ { rgba64 := png.At(x, y).(color.RGBA64) fmt.Fprintf(w, "%04x%04x%04x ", rgba64.R, rgba64.G, rgba64.B) } case cm == color.NRGBAModel: for x := bounds.Min.X; x < bounds.Max.X; x++ { nrgba := png.At(x, y).(color.NRGBA) switch filename { case "ftbbn0g01", "ftbbn0g02", "ftbbn0g04": fmt.Fprintf(w, "%02x", nrgba.R) default: if useTransparent { fmt.Fprintf(w, "%02x%02x%02x ", nrgba.R, nrgba.G, nrgba.B) } else { fmt.Fprintf(w, "%02x%02x%02x%02x ", nrgba.R, nrgba.G, nrgba.B, nrgba.A) } } } case cm == color.NRGBA64Model: for x := bounds.Min.X; x < bounds.Max.X; x++ { nrgba64 := png.At(x, y).(color.NRGBA64) switch filename { case "ftbwn0g16": fmt.Fprintf(w, "%04x ", nrgba64.R) default: if useTransparent { fmt.Fprintf(w, "%04x%04x%04x ", nrgba64.R, nrgba64.G, nrgba64.B) } else { fmt.Fprintf(w, "%04x%04x%04x%04x ", nrgba64.R, nrgba64.G, nrgba64.B, nrgba64.A) } } } case cpm != nil: var b, c int for x := bounds.Min.X; x < bounds.Max.X; x++ { b = b<<uint(bitdepth) | int(paletted.ColorIndexAt(x, y)) c++ if c == 8/bitdepth { fmt.Fprintf(w, "%02x", b) b = 0 c = 0 } } if c != 0 { for c != 8/bitdepth { b = b << uint(bitdepth) c++ } fmt.Fprintf(w, "%02x", b) } } io.WriteString(w, "\n") } io.WriteString(w, "}\n") }
func NewSimulation(img *image.Paletted) *Simulation { size := img.Bounds().Size() groups := make(map[*group]struct{}, 0) matrix := newBucketMatrix(size.X, size.Y) for y := 0; y < size.Y; y++ { for x := 0; x < size.X; x++ { c := img.ColorIndexAt(x, y) if c > 0 && c <= maxCharge+1 { topLeftBucket := matrix.get(x-1, y-1) topBucket := matrix.get(x, y-1) leftBucket := matrix.get(x-1, y) var currentBucket *bucket switch { case nil == topBucket && nil == leftBucket: currentBucket = newBucket() groups[currentBucket.group] = struct{}{} case nil == topBucket && nil != leftBucket: currentBucket = leftBucket case (nil != topBucket && nil == leftBucket) || topBucket == leftBucket || topBucket.group == leftBucket.group: currentBucket = topBucket default: currentBucket = topBucket delete(groups, topBucket.group) topBucket.group.moveBucketsTo(leftBucket.group) } if nil != topLeftBucket && nil != topBucket && nil != leftBucket { currentBucket.group.wire.isPowerSource = true } matrix.set(x, y, currentBucket) currentBucket.addPixel(image.Point{x, y}) } } } for y := 0; y < size.Y; y++ { for x := 0; x < size.X; x++ { if nil != matrix.get(x, y) { continue } topBucket := matrix.get(x, y-1) topRightBucket := matrix.get(x+1, y-1) rightBucket := matrix.get(x+1, y) bottomRightBucket := matrix.get(x+1, y+1) bottomBucket := matrix.get(x, y+1) bottomLeftBucket := matrix.get(x-1, y+1) leftBucket := matrix.get(x-1, y) topLeftBucket := matrix.get(x-1, y-1) if nil == topLeftBucket && nil == topRightBucket && nil == bottomLeftBucket && nil == bottomRightBucket && nil != topBucket && nil != rightBucket && nil != bottomBucket && nil != leftBucket { delete(groups, topBucket.group) topBucket.group.moveBucketsTo(bottomBucket.group) delete(groups, leftBucket.group) leftBucket.group.moveBucketsTo(rightBucket.group) } } } transistors := make([]*transistor, 0) for y := 0; y < size.Y; y++ { for x := 0; x < size.X; x++ { if nil != matrix.get(x, y) { continue } topBucket := matrix.get(x, y-1) topRightBucket := matrix.get(x+1, y-1) rightBucket := matrix.get(x+1, y) bottomRightBucket := matrix.get(x+1, y+1) bottomBucket := matrix.get(x, y+1) bottomLeftBucket := matrix.get(x-1, y+1) leftBucket := matrix.get(x-1, y) topLeftBucket := matrix.get(x-1, y-1) switch { case nil == bottomLeftBucket && nil == bottomRightBucket && nil == topBucket && nil != rightBucket && nil != bottomBucket && nil != leftBucket: transistors = append(transistors, newTransistor(image.Point{x, y}, bottomBucket.group.wire, rightBucket.group.wire, leftBucket.group.wire)) case nil == bottomLeftBucket && nil == topLeftBucket && nil != topBucket && nil == rightBucket && nil != bottomBucket && nil != leftBucket: transistors = append(transistors, newTransistor(image.Point{x, y}, leftBucket.group.wire, topBucket.group.wire, bottomBucket.group.wire)) case nil == topLeftBucket && nil == topRightBucket && nil != topBucket && nil != rightBucket && nil == bottomBucket && nil != leftBucket: transistors = append(transistors, newTransistor(image.Point{x, y}, topBucket.group.wire, rightBucket.group.wire, leftBucket.group.wire)) case nil == bottomRightBucket && nil == topRightBucket && nil != topBucket && nil != rightBucket && nil != bottomBucket && nil == leftBucket: transistors = append(transistors, newTransistor(image.Point{x, y}, rightBucket.group.wire, topBucket.group.wire, bottomBucket.group.wire)) } } } wires := make([]*wire, len(groups)) wireStates := make([]wireState, len(groups)) i := 0 for k := range groups { k.wire.index = i wires[i] = k.wire var charge uint8 if k.wire.isPowerSource { charge = maxCharge } else { charge = 0 } wireStates[i] = wireState{charge, k.wire} i++ } return &Simulation{&circuit{wires: wires, transistors: transistors}, wireStates} }
// An approximation of the sng command-line tool. func sng(w io.WriteCloser, filename string, png image.Image) { defer w.Close() bounds := png.Bounds() cm := png.ColorModel() 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. io.WriteString(w, "#SNG: from "+filename+".png\nIHDR {\n") fmt.Fprintf(w, " width: %d; height: %d; bitdepth: %d;\n", bounds.Dx(), bounds.Dy(), bitdepth) switch { case cm == image.RGBAColorModel, cm == image.RGBA64ColorModel: io.WriteString(w, " using color;\n") case cm == image.NRGBAColorModel, cm == image.NRGBA64ColorModel: io.WriteString(w, " using color alpha;\n") case cm == image.GrayColorModel, cm == image.Gray16ColorModel: io.WriteString(w, " using grayscale;\n") case cpm != nil: io.WriteString(w, " using color palette;\n") default: io.WriteString(w, "unknown PNG decoder color model\n") } io.WriteString(w, "}\n") // We fake a gAMA output. The test files have a gAMA chunk but the go PNG parser ignores it // (the PNG spec section 11.3 says "Ancillary chunks may be ignored by a decoder"). io.WriteString(w, "gAMA {1.0000}\n") // Write the PLTE (if applicable). if cpm != nil { io.WriteString(w, "PLTE {\n") for i := 0; i < len(cpm); i++ { r, g, b, _ := cpm[i].RGBA() r >>= 8 g >>= 8 b >>= 8 fmt.Fprintf(w, " (%3d,%3d,%3d) # rgb = (0x%02x,0x%02x,0x%02x)\n", r, g, b, r, g, b) } io.WriteString(w, "}\n") } // Write the IMAGE. io.WriteString(w, "IMAGE {\n pixels hex\n") for y := bounds.Min.Y; y < bounds.Max.Y; y++ { switch { case cm == image.GrayColorModel: for x := bounds.Min.X; x < bounds.Max.X; x++ { gray := png.At(x, y).(image.GrayColor) 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: for x := bounds.Min.X; x < bounds.Max.X; x++ { rgba := png.At(x, y).(image.RGBAColor) 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: for x := bounds.Min.X; x < bounds.Max.X; x++ { nrgba := png.At(x, y).(image.NRGBAColor) 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: for x := bounds.Min.X; x < bounds.Max.X; x++ { fmt.Fprintf(w, "%02x", paletted.ColorIndexAt(x, y)) } } io.WriteString(w, "\n") } io.WriteString(w, "}\n") }
// An approximation of the sng command-line tool. func sng(w io.WriteCloser, filename string, png image.Image) { defer w.Close() // For now, the go PNG parser only reads bitdepths of 8. bitdepth := 8 // Write the filename and IHDR. io.WriteString(w, "#SNG: from "+filename+".png\nIHDR {\n") fmt.Fprintf(w, " width: %d; height: %d; bitdepth: %d;\n", png.Width(), png.Height(), bitdepth) cm := png.ColorModel() var paletted *image.Paletted cpm, _ := cm.(image.PalettedColorModel) switch { case cm == image.RGBAColorModel: io.WriteString(w, " using color;\n") case cm == image.NRGBAColorModel: io.WriteString(w, " using color alpha;\n") case cpm != nil: io.WriteString(w, " using color palette;\n") paletted = png.(*image.Paletted) default: io.WriteString(w, "unknown PNG decoder color model\n") } io.WriteString(w, "}\n") // We fake a gAMA output. The test files have a gAMA chunk but the go PNG parser ignores it // (the PNG spec section 11.3 says "Ancillary chunks may be ignored by a decoder"). io.WriteString(w, "gAMA {1.0000}\n") // Write the PLTE (if applicable). if cpm != nil { io.WriteString(w, "PLTE {\n") for i := 0; i < len(cpm); i++ { r, g, b, _ := cpm[i].RGBA() r >>= 24 g >>= 24 b >>= 24 fmt.Fprintf(w, " (%3d,%3d,%3d) # rgb = (0x%02x,0x%02x,0x%02x)\n", r, g, b, r, g, b) } io.WriteString(w, "}\n") } // Write the IMAGE. io.WriteString(w, "IMAGE {\n pixels hex\n") for y := 0; y < png.Height(); y++ { switch { case cm == image.RGBAColorModel: for x := 0; x < png.Width(); x++ { rgba := png.At(x, y).(image.RGBAColor) fmt.Fprintf(w, "%02x%02x%02x ", rgba.R, rgba.G, rgba.B) } case cm == image.NRGBAColorModel: for x := 0; x < png.Width(); x++ { nrgba := png.At(x, y).(image.NRGBAColor) fmt.Fprintf(w, "%02x%02x%02x%02x ", nrgba.R, nrgba.G, nrgba.B, nrgba.A) } case cpm != nil: for x := 0; x < png.Width(); x++ { fmt.Fprintf(w, "%02x", paletted.ColorIndexAt(x, y)) } } io.WriteString(w, "\n") } io.WriteString(w, "}\n") }