func palettedToRGBA(last *image.RGBA, in *image.Paletted) *image.RGBA { out := image.NewRGBA(image.Rect(0, 0, 16, 16)) draw.Draw(out, out.Bounds(), last, last.Rect.Min, draw.Src) draw.Draw(out, in.Bounds(), in, in.Rect.Min, draw.Over) return out }
func toPaletted(o *ora.ORA, name string, palette color.Palette) (*image.Paletted, error) { var p *image.Paletted if l := o.Layer(name); l != nil { p = image.NewPaletted(o.Bounds(), palette) i, err := l.Image() if err != nil { return nil, err } draw.Draw(p, image.Rect(0, 0, p.Bounds().Max.X, p.Bounds().Max.Y), i, image.Point{}, draw.Src) } return p, nil }
func (s *Simulation) DrawAll(initialImage *image.Paletted, frameCount int) []*image.Paletted { bounds := initialImage.Bounds() images := make([]*image.Paletted, frameCount) s.Draw(initialImage) images[0] = initialImage for f := 1; f < frameCount; f++ { newSimulation := s.Step() img := image.NewPaletted(bounds, initialImage.Palette) newSimulation.DiffDraw(s, img) images[f] = img s = newSimulation } return images }
// Write a text inside the image func WriteText(text string, f *truetype.Font, img *image.Paletted) { // Initialize the context. fg := image.Black c := freetype.NewContext() c.SetDPI(72) c.SetFont(f) c.SetFontSize(64) c.SetClip(img.Bounds()) c.SetDst(img) c.SetSrc(fg) // Draw the text. pt := freetype.Pt(40, 120) c.DrawString(text, pt) }
// uninterlace rearranges the pixels in m to account for interlaced input. func uninterlace(m *image.Paletted) { var nPix []uint8 dx := m.Bounds().Dx() dy := m.Bounds().Dy() nPix = make([]uint8, dx*dy) offset := 0 // steps through the input by sequential scan lines. for _, pass := range interlacing { nOffset := pass.start * dx // steps through the output as defined by pass. for y := pass.start; y < dy; y += pass.skip { copy(nPix[nOffset:nOffset+dx], m.Pix[offset:offset+dx]) offset += dx nOffset += dx * pass.skip } } m.Pix = nPix }
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 }
func encodeImageBlock(w io.Writer, img *image.Paletted) error { // start image litWidth := int(paletteBits(img.Palette)) if litWidth < 2 { litWidth = 2 } bounds := img.Bounds() if err := writeData(w, byte(0x2C), uint16(bounds.Min.X), uint16(bounds.Min.Y), uint16(bounds.Dx()), uint16(bounds.Dy()), byte(0), byte(litWidth), ); err != nil { return err } // start compression blocks := &blockWriter{w: w} compress := lzw.NewWriter(blocks, lzw.LSB, litWidth) // write each scan line (might not be contiguous) startX := img.Rect.Min.X stopX := img.Rect.Max.X stopY := img.Rect.Max.Y for y := img.Rect.Min.Y; y < stopY; y++ { start := img.PixOffset(startX, y) stop := img.PixOffset(stopX, y) if _, err := compress.Write(img.Pix[start:stop]); err != nil { return err } } if err := compress.Close(); err != nil { return err } return blocks.Close() }
// Merge les deux images de telle sorte qu'après l'opération les pixels de img1 : // - soient inchangés là où l'index dans img2 est 0 // - prennent la valeur de img2 ailleurs // Les deux images doivent être de mêmes dimensions exactement // La palette de img1 est enrichie si nécessaire func Fusionne(img1 *image.Paletted, img2 *image.Paletted) error { r1 := img1.Bounds() r2 := img2.Bounds() if r1.Min.X != r2.Min.X || r1.Max.X != r2.Max.X || r1.Min.Y != r2.Min.Y || r1.Max.Y != r2.Max.Y { return errors.New("image dimensions not compatible") } p1 := img1.Palette p2 := img2.Palette n1 := len(p1) n2 := len(p2) op := make([]uint8, n2) // tableau donnant l'index dans la palette de img1 d'une couleur de la palette de img2 for i2 := 0; i2 < n2; i2++ { c := p2[i2].(color.RGBA) if i2 < n1 && couleursEgales(c, p1[i2].(color.RGBA)) { op[i2] = uint8(i2) } else { found := false for i1 := 0; i1 < n1; i1++ { if couleursEgales(c, p1[i1].(color.RGBA)) { op[i2] = uint8(i1) found = true break } } if !found { op[i2] = uint8(len(p1)) p1 = append(p1, c) img1.Palette = p1 } } } l2 := len(img2.Pix) for x := 0; x < l2; x++ { if img2.Pix[x] != 0 { img1.Pix[x] = op[img2.Pix[x]] } } return nil }
func (e *encoder) writeImageBlock(pm *image.Paletted, delay int, disposal byte) { if e.err != nil { return } if len(pm.Palette) == 0 { e.err = errors.New("gif: cannot encode image block with empty palette") return } b := pm.Bounds() if b.Min.X < 0 || b.Max.X >= 1<<16 || b.Min.Y < 0 || b.Max.Y >= 1<<16 { e.err = errors.New("gif: image block is too large to encode") return } if !b.In(image.Rectangle{Max: image.Point{e.g.Config.Width, e.g.Config.Height}}) { e.err = errors.New("gif: image block is out of bounds") return } transparentIndex := -1 for i, c := range pm.Palette { if _, _, _, a := c.RGBA(); a == 0 { transparentIndex = i break } } if delay > 0 || disposal != 0 || transparentIndex != -1 { e.buf[0] = sExtension // Extension Introducer. e.buf[1] = gcLabel // Graphic Control Label. e.buf[2] = gcBlockSize // Block Size. if transparentIndex != -1 { e.buf[3] = 0x01 | disposal<<2 } else { e.buf[3] = 0x00 | disposal<<2 } writeUint16(e.buf[4:6], uint16(delay)) // Delay Time (1/100ths of a second) // Transparent color index. if transparentIndex != -1 { e.buf[6] = uint8(transparentIndex) } else { e.buf[6] = 0x00 } e.buf[7] = 0x00 // Block Terminator. e.write(e.buf[:8]) } e.buf[0] = sImageDescriptor writeUint16(e.buf[1:3], uint16(b.Min.X)) writeUint16(e.buf[3:5], uint16(b.Min.Y)) writeUint16(e.buf[5:7], uint16(b.Dx())) writeUint16(e.buf[7:9], uint16(b.Dy())) e.write(e.buf[:9]) paddedSize := log2(len(pm.Palette)) // Size of Local Color Table: 2^(1+n). ct := encodeColorTable(e.localColorTable[:], pm.Palette, paddedSize) if ct != e.globalCT || !bytes.Equal(e.globalColorTable[:ct], e.localColorTable[:ct]) { // Use a local color table. e.writeByte(fColorTable | uint8(paddedSize)) e.write(e.localColorTable[:ct]) } else { // Use the global color table. e.writeByte(0) } litWidth := paddedSize + 1 if litWidth < 2 { litWidth = 2 } e.writeByte(uint8(litWidth)) // LZW Minimum Code Size. lzww := lzw.NewWriter(blockWriter{e: e}, lzw.LSB, litWidth) if dx := b.Dx(); dx == pm.Stride { _, e.err = lzww.Write(pm.Pix) if e.err != nil { lzww.Close() return } } else { for i, y := 0, b.Min.Y; y < b.Max.Y; i, y = i+pm.Stride, y+1 { _, e.err = lzww.Write(pm.Pix[i : i+dx]) if e.err != nil { lzww.Close() return } } } lzww.Close() e.writeByte(0x00) // Block Terminator. }
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 }
func (e *encoder) writeImageBlock(pm *image.Paletted, delay, transparentIndex int) { if e.err != nil { return } if len(pm.Palette) == 0 { e.err = errors.New("gif: cannot encode image block with empty palette") return } b := pm.Bounds() if b.Dx() >= 1<<16 || b.Dy() >= 1<<16 || b.Min.X < 0 || b.Min.X >= 1<<16 || b.Min.Y < 0 || b.Min.Y >= 1<<16 { e.err = errors.New("gif: image block is too large to encode") return } // -1 means unknown transparent index; -2 means definitely no transparent index. if transparentIndex == -1 { for i, c := range pm.Palette { if _, _, _, a := c.RGBA(); a == 0 { transparentIndex = i break } } transparentIndex = -2 } if delay > 0 || transparentIndex >= 0 { e.buf[0] = sExtension // Extension Introducer. e.buf[1] = gcLabel // Graphic Control Label. e.buf[2] = gcBlockSize // Block Size. if transparentIndex >= 0 { e.buf[3] = 0x01 } else { e.buf[3] = 0x00 } writeUint16(e.buf[4:6], uint16(delay)) // Delay Time (1/100ths of a second) // Transparent color index. if transparentIndex >= 0 { e.buf[6] = uint8(transparentIndex) } else { e.buf[6] = 0x00 } e.buf[7] = 0x00 // Block Terminator. e.write(e.buf[:8]) } e.buf[0] = sImageDescriptor writeUint16(e.buf[1:3], uint16(b.Min.X)) writeUint16(e.buf[3:5], uint16(b.Min.Y)) writeUint16(e.buf[5:7], uint16(b.Dx())) writeUint16(e.buf[7:9], uint16(b.Dy())) e.write(e.buf[:9]) paddedSize := log2(len(pm.Palette)) // Size of Local Color Table: 2^(1+n). // Interlacing is not supported. e.writeByte(0x80 | uint8(paddedSize)) // Local Color Table. e.writeColorTable(pm.Palette, paddedSize) litWidth := paddedSize + 1 if litWidth < 2 { litWidth = 2 } e.writeByte(uint8(litWidth)) // LZW Minimum Code Size. lzww := lzw.NewWriter(blockWriter{e: e}, lzw.LSB, litWidth) _, e.err = lzww.Write(pm.Pix) if e.err != nil { lzww.Close() return } lzww.Close() e.writeByte(0x00) // Block Terminator. }
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} }