GameCounter/Source/res/digits2code.py

156 lines
3.7 KiB
Python

# Converts digits.png to C code
#
# Image requirements:
# White pixels are considered "lit" on the OLED, black not. Height must be a
# multiple of 8.
#
# Each digit is assumed to be connected and needs to be separated by at least one vertical
# white line. The maximum character width is used for all characters. AAll characters are
# center-aligned to the maximum width.
#
# The output is arranged left-to-right, top-to-bottom, where each byte represents a column
# of 8 pixels. This corresponds to the page addressing mode used by the SSD1306xled library.
#
# Library requirements:
# Pillow (pip install pillow)
#
from __future__ import print_function
from PIL import Image
import math
InputFile = "digits.png"
OutputHeaderFile = "..\src\digits.h"
OutputCodeFile = "..\src\digits.c"
# Open image and convert to black and white
image = Image.open(InputFile).convert("L")
width, height = image.size
if height % 8 != 0:
print("Error: height must be a multiple of 8")
exit(1)
pixels = image.load()
def isEmptyColumn(x):
for y in range(height):
if pixels[x, y] != 0:
return False
return True
def getColumnValue(x, y):
value = 0
for yPos in range(8):
if pixels[x, y + yPos] != 0:
value = value | (1<<yPos)
return value
digits = []
maxWidth = 0
# Scan for start and end positions
x = 0
while x < width:
# Skip empty lines at the start
while x < width and isEmptyColumn(x):
x += 1
if x >= width:
break
startX = x
while x < width and not isEmptyColumn(x):
x += 1
digitWidth = x - startX
if digitWidth > maxWidth:
maxWidth = digitWidth
digits.append({ "start": startX, "stop": x, "width": digitWidth })
x += 1
if len(digits) != 10:
print("Error: expected 10 digits, found", len(digits))
exit(2)
rows = int(math.ceil(height / 8))
print("Found 10 digits")
print(" character width :", maxWidth)
print(" rows per character :", rows)
print(" total bytes :", rows * maxWidth * 10)
output = open(OutputHeaderFile, "w")
output.write("#ifndef __Digits\n")
output.write("#define __Digits\n")
output.write("\n")
output.write("#include <avr/pgmspace.h>\n")
output.write("\n")
output.write("#define DigitWidth " + str(maxWidth) + "\n")
output.write("#define DigitHeight " + str(height) + "\n")
output.write("#define DigitRows " + str(rows) + "\n")
output.write("#define DigitBytesPerChar " + str(rows * maxWidth) + "\n")
output.write("\n\n\n")
output.write("extern const uint8_t Digits[] PROGMEM;\n")
output.write("\n")
output.write("#endif\n")
output.close();
output = open(OutputCodeFile, "w")
output.write("#include \"digits.h\"\n")
output.write("\n")
output.write("const uint8_t Digits[] PROGMEM = {\n")
def outputColumn(value, isLast):
output.write("0x{:02x}".format(value))
if not isLast:
output.write(", ")
# Generate byte array for each digit
for digitIndex in range(len(digits)):
digit = digits[digitIndex]
output.write(" // " + str(digitIndex) + "\n")
paddingLeft = int(math.floor((maxWidth - digit["width"]) / 2))
paddingRight = maxWidth - digit["width"] - paddingLeft
for rowIndex in range(rows):
lastRow = digitIndex == len(digits) - 1 and rowIndex == rows - 1
output.write(" ")
# Add padding to center align the character
for paddingIndex in range(paddingLeft):
outputColumn(0, False)
# Output one column at a time
for x in range(digit["start"], digit["stop"]):
outputColumn(getColumnValue(x, rowIndex * 8), lastRow and paddingRight == 0 and x == digit["stop"] - 1)
for paddingIndex in range(paddingRight):
outputColumn(0, lastRow and paddingIndex == paddingRight - 1)
output.write("\n")
output.write("\n")
output.write("};\n")
output.close()
print("")
print("Wrote output to", OutputHeaderFile, "and", OutputCodeFile)