179 lines
4.4 KiB
Python
179 lines
4.4 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
|
|
|
|
# Only measure actual digits for the maximum, skip the minus sign
|
|
if len(digits) > 0 and digitWidth > maxWidth:
|
|
maxWidth = digitWidth
|
|
|
|
digits.append({ "start": startX, "stop": x, "width": digitWidth })
|
|
x += 1
|
|
|
|
|
|
if len(digits) != 11:
|
|
print("Error: expected 11 sections (minus sign + 10 digits), found", len(digits))
|
|
exit(2)
|
|
|
|
rows = int(math.ceil(height / 8))
|
|
|
|
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")
|
|
output.write("#define MinusSignWidth " + str(digits[0]["width"]) + "\n")
|
|
output.write("#define MinusSignBytes " + str(rows * digits[0]["width"]) + "\n")
|
|
output.write("\n\n\n")
|
|
output.write("extern const uint8_t Digits[] PROGMEM;\n")
|
|
output.write("extern const uint8_t MinusSign[] 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(1, len(digits)):
|
|
digit = digits[digitIndex]
|
|
output.write(" // " + str(digitIndex - 1) + "\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.write("\n");
|
|
|
|
|
|
output.write("const uint8_t MinusSign[] PROGMEM = {\n")
|
|
digit = digits[0]
|
|
|
|
for rowIndex in range(rows):
|
|
lastRow = rowIndex == rows - 1
|
|
output.write(" ")
|
|
|
|
# Output one column at a time
|
|
for x in range(digit["start"], digit["stop"]):
|
|
outputColumn(getColumnValue(x, rowIndex * 8), lastRow and x == digit["stop"] - 1)
|
|
|
|
output.write("\n")
|
|
|
|
output.write("};\n")
|
|
output.close()
|
|
|
|
|
|
print("")
|
|
print("Wrote output to", OutputHeaderFile, "and", OutputCodeFile) |