Skip to content

Instantly share code, notes, and snippets.

@magical
Created May 30, 2021 22:52
Show Gist options
  • Save magical/6c7772a3a071866fa4ce353c1dcf5f91 to your computer and use it in GitHub Desktop.
Save magical/6c7772a3a071866fa4ce353c1dcf5f91 to your computer and use it in GitHub Desktop.
convert TW small format to MSCC (44x44)
package main
import (
"bytes"
"flag"
"fmt"
"image"
"image/color"
"io"
"os"
"golang.org/x/image/bmp"
"golang.org/x/image/draw"
_ "image/png"
)
const (
tileWidth = 44
tileHeight = 44
height = tileHeight * 16
width = tileWidth * 7
widthMasked = tileWidth * 13
crStart = tileWidth * 4
crWidth = tileWidth * 3
)
func main() {
flag.Parse()
filename := flag.Arg(0)
outname := flag.Arg(1)
if filename == "" || outname == "" {
fmt.Fprintln(os.Stderr, "usage: makemasked input output.bmp")
os.Exit(1)
}
var confBuf bytes.Buffer
origfp, err := os.Open(filename)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
defer origfp.Close()
conf, _, err := image.DecodeConfig(io.TeeReader(origfp, &confBuf))
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
if conf.Width != width || conf.Height != height {
fmt.Fprintf(os.Stderr, "image is %dx%d, must be %dx%d\n", conf.Width, conf.Height, width, height)
os.Exit(1)
}
origImage, _, err := image.Decode(io.MultiReader(&confBuf, origfp))
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
maskedImage := makeMasked(origImage)
if !maskedImage.Opaque() {
fmt.Fprintln(os.Stderr, "warning: result is not fully opaque")
}
outfp, err := os.Create(outname)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
bmp.Encode(outfp, maskedImage)
}
// Column returns a rectangle with the given x coordinates and a fixed height.
func column(x0, x1 int) image.Rectangle {
return image.Rect(x0, 0, x1, height)
}
func sameColor(c0, c1 color.Color) bool {
if c0 == c1 {
return true
}
r0, g0, b0, a0 := c0.RGBA()
r1, g1, b1, a1 := c1.RGBA()
return r0 == r1 && g0 == g1 && b0 == b1 && a0 == a1
}
func makeMasked(src image.Image) *image.RGBA {
var magenta = color.RGBA{0xff, 0, 0xff, 0xff}
cr := image.NewRGBA(column(0, crWidth))
p := image.NewRGBA(column(0, widthMasked))
// Copy normal tiles to p
draw.Src.Draw(p, column(0, crStart), src, image.ZP)
// Copy creatures to cr
draw.Src.Draw(cr, column(0, crStart), src, image.Pt(crStart, 0))
// Make creatures transparent
for y := 0; y < cr.Rect.Dy(); y++ {
for x := 0; x < cr.Rect.Dx(); x++ {
c := cr.RGBAAt(x, y)
if sameColor(c, magenta) {
cr.Set(x, y, color.Transparent)
}
}
}
// Draw creatures on top of floors
floors := tiled{src, image.Rect(0, 0, tileHeight, tileWidth)}
draw.Src.Draw(p, column(crStart, width), &floors, image.ZP)
draw.Over.Draw(p, column(crStart, width), cr, image.ZP)
// Draw creatures on top of white
draw.Src.Draw(p, column(width, width+crWidth), image.White, image.ZP)
draw.Over.Draw(p, column(width, width+crWidth), cr, image.ZP)
// Draw creature masks
draw.Src.Draw(p, column(width+crWidth, width+2*crWidth), &masked{cr}, image.ZP)
return p
}
// tiled repeats a portion of m infinitely in all directions
type tiled struct {
m image.Image
r image.Rectangle
}
func (t *tiled) ColorModel() color.Model { return t.m.ColorModel() }
func (t *tiled) Bounds() image.Rectangle { return image.Rect(-1e9, -1e9, 1e9, 1e9) }
func (t *tiled) At(x, y int) color.Color {
p := image.Pt(x, y).Mod(t.r)
return t.m.At(p.X, p.Y)
}
type masked struct {
m image.Image
}
func (m *masked) ColorModel() color.Model { return m.m.ColorModel() }
func (m *masked) Bounds() image.Rectangle { return m.m.Bounds() }
func (m *masked) At(x, y int) color.Color {
c := m.m.At(x, y)
if _, _, _, a := c.RGBA(); a > 0x7fff {
return color.White
}
return color.Black
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment