ponydos/process_wallpaper.py
Juhani Krekelä d4c15687c8 Add option for 8-colour mode
Some BIOSs initialize the VGA card by default into a mode where the high
bit of background nybble signals that the cell should blink. The simple
way to avoid this is by restricting the background colours to the range
0…7. However, since our mouse cursor is implemented by swapping the
foreground and the background colours, we also need to restrict the
foreground colours to the range 0…7.
2023-05-11 22:02:18 +03:00

141 lines
3.9 KiB
Python

import os
import sys
from collections import namedtuple
Attribute = namedtuple('Attribute', ['fg', 'bg', 'bold'])
WIDTH = 80
HEIGHT = 25
# ANSI orders the colours black, red, green, yellow, blue, magenta, cyan, white
# VGA orders them black, blue, green, cyan, red, magenta, yellow, white
color_map = [0, 4, 2, 6, 1, 5, 3, 7]
if len(sys.argv) != 7:
print("Usage: {sys.argv[0]} outfile infile default_fgcolor default_bgcolor origin_x origin_y", file=sys.stderr)
sys.exit(1)
blinky_vgamode = False
if 'BUILDOPTS' in os.environ:
for opt in os.environ['BUILDOPTS'].split():
if opt == '-DBLINKY':
blinky_vgamode = True
else:
print(f"Error: Unrecognized build option {opt}", file=sys.stderr)
sys.exit(1)
outfile = sys.argv[1]
infile = sys.argv[2]
default_fgcolor = int(sys.argv[3])
assert 0 <= default_fgcolor <= 15
default_bgcolor = int(sys.argv[4])
assert 0 <= default_bgcolor <= 15
origin_x = int(sys.argv[5])
assert 0 <= origin_x < WIDTH
origin_y = int(sys.argv[6])
assert 0 <= origin_y < HEIGHT
chars = [bytearray([0]*HEIGHT) for _ in range(WIDTH)]
attributes = [[(Attribute(default_fgcolor, default_bgcolor, False))]*HEIGHT for _ in range(WIDTH)]
with open(infile, 'rb') as f:
ansitext = f.read()
x = origin_x
y = origin_y
fgcolor = default_fgcolor
bgcolor = default_bgcolor
bold = False
error = False
line = 1
line_start = 0
index = 0
while index < len(ansitext):
if ansitext[index:].startswith(b'\x1b['):
index += len(b'\x1b[')
escape_start = index
while index < len(ansitext) and ansitext[index] not in b'CDHJhm':
index += 1
escape_params = ansitext[escape_start:index]
escape_type = ansitext[index:index+1]
index += 1
if escape_type == b'C':
x += int(escape_params)
elif escape_type == b'D':
x -= int(escape_params)
elif escape_type == b'H':
row, column = escape_params.split(b';')
y = int(row) - 1
x = int(column) - 1
elif escape_type == b'J':
# Erase display
# We just ignore this, because why would you have
# erase command anywhere but at the start
pass
elif escape_type == b'h':
# Something nonstandard, ignore
pass
elif escape_type == b'm':
for color_parameter in escape_params.split(b';'):
color_parameter = int(color_parameter)
if color_parameter == 0:
bold = False
elif color_parameter == 1:
bold = True
elif color_parameter == 39:
fgcolor = default_fgcolor
elif color_parameter == 49:
bgcolor = default_bgcolor
elif 30 <= color_parameter <= 37:
fgcolor = color_map[color_parameter - 30]
elif 40 <= color_parameter <= 47:
bgcolor = color_map[color_parameter - 40]
elif 90 <= color_parameter <= 97:
fgcolor = color_map[color_parameter - 90] + 8
elif 100 <= color_parameter <= 107:
bgcolor = color_map[color_parameter - 100] + 8
else:
print(f'{line},{escape_start-line_start+1}: Unknown colour escape {color_parameter}')
error = True
else:
print(f'{line},{escape_start-line_start+1}: Unknown escape ^[[{escape_params.decode()}{escape_type.decode()}')
error = True
elif ansitext[index] == 13:
x = origin_x
index += 1
elif ansitext[index] == 10:
for i in range(x, WIDTH):
attributes[i][y] = Attribute(fgcolor, bgcolor, bold)
x = origin_x
y += 1
index += 1
line += 1
line_start = index
else:
chars[x][y] = ansitext[index]
attributes[x][y] = Attribute(fgcolor, bgcolor, bold)
index += 1
x += 1
if error:
sys.exit(1)
with open(outfile, 'wb') as f:
for y in range(HEIGHT):
for x in range(WIDTH):
fgcolor, bgcolor, bold = attributes[x][y]
fgcolor = fgcolor | (8 if bold else 0)
if blinky_vgamode:
# VGA mode can be set up so that the highest
# bit of the bg colour marks that the cell
# should blink instead of intensity
# Restrict the colours (including foreground
# to account for swapping of the two) to 0…7
fgcolor = fgcolor & 0x7
bgcolor = bgcolor & 0x7
char = chars[x][y]
f.write(bytes([char, (bgcolor<<4) | fgcolor]))