diff --git a/Makefile b/Makefile index 0a48dbf..216917b 100644 --- a/Makefile +++ b/Makefile @@ -3,6 +3,7 @@ BUILD := build RGBASM := rgbasm RGBLINK := rgblink RGBFIX := rgbfix +PYTHON := python NAME := anise-cheezball-rising TARGET := $(BUILD)/$(NAME).gbc @@ -17,7 +18,17 @@ DEPS := $(foreach src,$(SOURCES),$(patsubst $(SRC)/%,$(BUILD)/%.deps,$(src))) all: $(TARGET) -$(BUILD)/%.rgbasm.o: $(SRC)/%.rgbasm +# Special intermediate targets + +$(BUILD)/font.inc: util/font-to-tiles.py data/font.png + $(PYTHON) util/font-to-tiles.py data/font.png > $(BUILD)/font.inc + +# The regular expected stuff +# TODO: i've manually listed font.inc here because otherwise, on first build, +# rgbasm will balk that it doesn't exist, so it'll never create the deps file, +# so make will never know it needs to be built first. this enforces build +# order without supplying the explicit dependency. is there a better fix? +$(BUILD)/%.rgbasm.o: $(SRC)/%.rgbasm | $(BUILD)/font.inc $(RGBASM) -i $(SRC)/ -i $(BUILD)/ -M $(BUILD)/$*.rgbasm.deps -o $@ $< $(TARGET): $(OBJECTS) @@ -32,4 +43,6 @@ clean: rm -f $(SYMFILE) # Include generated dependency files +# TODO: if i remove a dep, make will be unable to recreate it, but these +# lingering files will still say it's necessary. is that fixable? -include $(DEPS) diff --git a/data/font.png b/data/font.png new file mode 100644 index 0000000..cac23e2 Binary files /dev/null and b/data/font.png differ diff --git a/src/main.rgbasm b/src/main.rgbasm index f7f65ec..93c82d9 100644 --- a/src/main.rgbasm +++ b/src/main.rgbasm @@ -527,47 +527,10 @@ include "tilesets/testmap.rgbasm" include "tilesets/testanise.rgbasm" SECTION "Font", ROMX -; FONT -font: -; A - db 6 - dw `00000000 - dw `00000000 - dw `01110000 - dw `10001000 - dw `10001000 - dw `10001000 - dw `11111000 - dw `10001000 - dw `10001000 - dw `10001000 - dw `10001000 - dw `00000000 - dw `00000000 - dw `00000000 - dw `00000000 - dw `00000000 -; B - db 6 - dw `00000000 - dw `00000000 - dw `11110000 - dw `10001000 - dw `10001000 - dw `10001000 - dw `11110000 - dw `10001000 - dw `10001000 - dw `10001000 - dw `11110000 - dw `00000000 - dw `00000000 - dw `00000000 - dw `00000000 - dw `00000000 - text: - db "ABABAAA", 0 + db "Hello, world!", 0 +; FONT +font: include "font.inc" SECTION "Text buffer", WRAM0[$C200] text_buffer: @@ -689,7 +652,7 @@ show_dialogue: ; character address in hl and /then/ put it in de. But I ; already pushed de, so I can use that as scratch space. push hl - sub a, 65 ; TODO temporary + sub a, 32 ; TODO temporary...? ld hl, font ld de, 33 ; 1 width byte + 16 * 2 tiles ; TODO can we speed striding up with long mult? @@ -784,6 +747,7 @@ show_dialogue: ; right? wait, no, it comes /before/... well fuck ; TODO actually that might make something weird happen due ; to the inc b above, maybe...? + dec b add a, b ; a <- new x offset ld bc, -32 ; move the VRAM pointer back... add hl, bc ; ...to the start of the tile diff --git a/util/font-to-tiles.py b/util/font-to-tiles.py new file mode 100644 index 0000000..838abb6 --- /dev/null +++ b/util/font-to-tiles.py @@ -0,0 +1,77 @@ +"""Converts a PNG containing a variable-width font into a source +file for rgbasm. + +The image format is similar to the one LÖVE uses: the upper-left +pixel of the whole image is considered to mark the start of a +new letter whenever it appears in the top row. Unlike LÖVE, +that column is also part of the following letter, and is +replaced with color zero. Also, no letter may be wider than 8 +pixels (though the renderer includes a 1px gap after every +character). + +The palette is ignored and indices are used directly, but +generally color 0 should be the background and color 1 should be +the primary text color. +""" +from pathlib import Path +import sys + +import PIL.Image + + +TILE_SIZE = 8 +METATILE_SIZE = 16 + + +def main(font_image_path): + im = PIL.Image.open(font_image_path) + + width, height = im.size + if height != 16: + # TODO well, not necessarily! in fact i'd like to cut + # it down to 12. but for now, yes + raise RuntimeError("Font image must be exactly 16 pixels tall") + + pixels = im.load() + indicator = pixels[0, 0] # this color starts a new glyph + glyph_starts = [] # x-offsets of where glyphs begin + glyphs = [] # lists of rows of pixels + + # Deal with the first row first, so we know where the + # divisions are + for x in range(width): + pixel = pixels[x, 0] + if pixel == indicator: + glyph_starts.append(x) + glyphs.append([[]]) + pixel = 0 + glyphs[-1][0].append(pixel) + # TODO enforce no more than 8 pixels + + # Continue with subsequent rows + for y in range(1, height): + g = -1 + for x in range(width): + if g + 1 < len(glyph_starts) and x == glyph_starts[g + 1]: + g += 1 + glyphs[g].append([]) + pixel = pixels[x, y] + glyphs[g][-1].append(pixel) + + # Write it out + for g, glyph in enumerate(glyphs): + if g + 1 < len(glyphs): + glyph_width = glyph_starts[g + 1] - glyph_starts[g] + else: + glyph_width = width - glyph_starts[g] + + print(f"; {chr(g + 32)}") + print(f" db {glyph_width}") + + for row in glyph: + row = (row + [0] * 8)[:8] + print(' dw `' + ''.join(map(str, row))) + + +if __name__ == '__main__': + main(*sys.argv[1:])