mirror of
https://github.com/robmcmullen/asmgen.git
synced 2024-09-27 15:55:09 +00:00
Added title screen image conversion
This commit is contained in:
parent
71e57e7433
commit
754b0e60c8
215
HiSprite.py
215
HiSprite.py
@ -8,6 +8,7 @@ import re
|
||||
|
||||
# external packages
|
||||
import png # package name is "pypng" on pypi.python.org
|
||||
import numpy as np
|
||||
|
||||
|
||||
def slugify(s):
|
||||
@ -206,13 +207,11 @@ class Listing(object):
|
||||
class Sprite(Listing):
|
||||
backing_store_sizes = set()
|
||||
|
||||
def __init__(self, pngfile, assembler, screen, xdraw=False, use_mask=False, backing_store=False, clobber=False, double_buffer=False, damage=False, processor="any", name=""):
|
||||
def __init__(self, slug, pngdata, assembler, screen, xdraw=False, use_mask=False, backing_store=False, clobber=False, double_buffer=False, damage=False, processor="any"):
|
||||
Listing.__init__(self, assembler)
|
||||
self.slug = slug
|
||||
self.screen = screen
|
||||
|
||||
reader = png.Reader(pngfile)
|
||||
pngdata = reader.asRGB8()
|
||||
|
||||
self.xdraw = xdraw
|
||||
self.use_mask = use_mask
|
||||
self.backing_store = backing_store
|
||||
@ -220,9 +219,6 @@ class Sprite(Listing):
|
||||
self.double_buffer = double_buffer
|
||||
self.damage = damage
|
||||
self.processor = processor
|
||||
if not name:
|
||||
name = os.path.splitext(pngfile)[0]
|
||||
self.slug = slugify(name)
|
||||
self.width = pngdata[0]
|
||||
self.height = pngdata[1]
|
||||
self.pixel_data = list(pngdata[2])
|
||||
@ -528,6 +524,17 @@ class HGR(ScreenFormat):
|
||||
|
||||
return "00"
|
||||
|
||||
def bits_for_bw(self, pixel):
|
||||
if pixel == self.white:
|
||||
return "1"
|
||||
else:
|
||||
return "0"
|
||||
|
||||
def bits_for_bw_mask(self, pixel):
|
||||
if pixel == self.key:
|
||||
return "1"
|
||||
return "0"
|
||||
|
||||
def high_bit_for_color(self, pixel):
|
||||
# Note that we prefer high-bit white because blue fringe is less noticeable than magenta.
|
||||
high_bit = "0"
|
||||
@ -643,15 +650,10 @@ class HGRBW(HGR):
|
||||
bits_per_pixel = 1
|
||||
|
||||
def bits_for_color(self, pixel):
|
||||
if pixel == self.white:
|
||||
return "1"
|
||||
else:
|
||||
return "0"
|
||||
return self.bits_for_bw(pixel)
|
||||
|
||||
def bits_for_mask(self, pixel):
|
||||
if pixel == self.key:
|
||||
return "1"
|
||||
return "0"
|
||||
return self.bits_for_bw_mask(pixel)
|
||||
|
||||
def pixel_color(self, pixel_data, row, col):
|
||||
r = pixel_data[row][col*3]
|
||||
@ -980,6 +982,165 @@ class FastFont(Listing):
|
||||
self.byte("$%02x" % ord(data[index]), 16)
|
||||
|
||||
|
||||
class HGRByLine(HGR):
|
||||
"""Either a color line or a BW line, depending on the contents of each
|
||||
line.
|
||||
|
||||
If the entire line has only BW pixels, look at all the pixel data to
|
||||
preserve all 280 pixels across.
|
||||
|
||||
Otherwise, look at every other pixel and convert as the normal HGR
|
||||
"""
|
||||
bits_per_pixel = 1
|
||||
|
||||
def __init__(self, color="line"):
|
||||
HGR.__init__(self)
|
||||
if color == "color":
|
||||
self.scan_line = self.scan_line_color
|
||||
elif color == "bw":
|
||||
self.scan_line = self.scan_line_bw
|
||||
else:
|
||||
self.scan_line = self.scan_line_default
|
||||
|
||||
def is_pixel_on(self, pixel_data, row, col):
|
||||
r = pixel_data[row][col*3]
|
||||
g = pixel_data[row][col*3+1]
|
||||
b = pixel_data[row][col*3+2]
|
||||
# any pixel that is not super dark is considered on
|
||||
return r>25 or g>25 or b>25
|
||||
|
||||
def scan_line_default(self, pixel_data, row, width):
|
||||
color_count = 0
|
||||
bw_count = 0
|
||||
for col in range(1, width):
|
||||
current = self.pixel_color(pixel_data, row, col)
|
||||
if current == self.white:
|
||||
bw_count += 1
|
||||
elif current != self.black and current != self.key:
|
||||
color_count += 1
|
||||
# print("row: %d, bw=%d, color=%d" % (row, bw_count, color_count))
|
||||
if color_count > 1:
|
||||
return self.color_processor
|
||||
return self.bw_processor
|
||||
|
||||
def scan_line_color(self, pixel_data, row, width):
|
||||
return self.color_processor
|
||||
|
||||
def scan_line_bw(self, pixel_data, row, width):
|
||||
return self.bw_processor
|
||||
|
||||
def color_processor(self, pixel_data, row, width):
|
||||
bit_stream = ""
|
||||
high_bits = ""
|
||||
# Compute raw bitstream for row from PNG pixels, skipping every other
|
||||
# for color rendering
|
||||
for byte_index in range(0, width, 2*7):
|
||||
h = None
|
||||
# take high bit for each byte using the first non-black pixel
|
||||
for bit_index in range(0, 2*7, 2):
|
||||
pixel_index = byte_index + bit_index
|
||||
pixel = self.pixel_color(pixel_data,row,pixel_index)
|
||||
if h is None and pixel != self.black and pixel != self.key:
|
||||
h = self.high_bit_for_color(pixel)
|
||||
bit_stream += self.bits_for_color(pixel)
|
||||
if h is None:
|
||||
h = "0"
|
||||
high_bits += h * 2*7
|
||||
|
||||
return self.split_bit_stream(width, bit_stream, high_bits)
|
||||
|
||||
def bw_processor(self, pixel_data, row, width):
|
||||
bit_stream = ""
|
||||
high_bits = ""
|
||||
# Compute raw bitstream for row from PNG pixels, skipping every other
|
||||
# for color rendering
|
||||
for pixel_index in range(0, width):
|
||||
pixel = self.pixel_color(pixel_data,row,pixel_index)
|
||||
bit_stream += self.bits_for_bw(pixel)
|
||||
h = self.high_bit_for_color(pixel)
|
||||
high_bits += h
|
||||
|
||||
return self.split_bit_stream(width, bit_stream, high_bits)
|
||||
|
||||
def split_bit_stream(self, width, bit_stream, high_bits):
|
||||
# print bit_stream
|
||||
# print high_bits
|
||||
|
||||
# Split bitstream into bytes
|
||||
byte_width = width // 7
|
||||
bit_pos = 0
|
||||
filler_bit = "0"
|
||||
byte_splits = np.zeros((byte_width), dtype=np.uint8)
|
||||
|
||||
for byte_index in range(byte_width):
|
||||
remaining_bits = len(bit_stream) - bit_pos
|
||||
|
||||
bit_chunk = ""
|
||||
|
||||
if remaining_bits < 0:
|
||||
bit_chunk = filler_bit * 7
|
||||
else:
|
||||
if remaining_bits < 7:
|
||||
bit_chunk = bit_stream[bit_pos:]
|
||||
bit_chunk += filler_bit * (7-remaining_bits)
|
||||
else:
|
||||
bit_chunk = bit_stream[bit_pos:bit_pos+7]
|
||||
|
||||
bit_chunk = bit_chunk[::-1]
|
||||
byte = high_bits[bit_pos] + bit_chunk
|
||||
#print("%d: %s" % (byte_index, byte))
|
||||
byte_splits[byte_index] = int(byte, 2)
|
||||
bit_pos += 7
|
||||
|
||||
return byte_splits
|
||||
|
||||
def lines_from_pixels(self, source):
|
||||
lines = np.zeros((192, 40), dtype=np.uint8)
|
||||
|
||||
bit_delegate = self.bits_for_color
|
||||
high_bit_delegate = self.high_bit_for_color
|
||||
filler_bit = "0"
|
||||
|
||||
for row in range(source.height):
|
||||
processor = self.scan_line(source.pixel_data, row, source.width)
|
||||
lines[row] = processor(source.pixel_data, row, source.width)
|
||||
|
||||
return lines
|
||||
|
||||
|
||||
class Image(object):
|
||||
def __init__(self, pngdata, fileroot, color):
|
||||
self.screen = HGRByLine(color)
|
||||
|
||||
self.width = pngdata[0]
|
||||
self.height = pngdata[1]
|
||||
self.pixel_data = list(pngdata[2])
|
||||
self.convert(fileroot)
|
||||
|
||||
def convert(self, fileroot):
|
||||
lines = self.screen.lines_from_pixels(self)
|
||||
output = "%s.hgr.png" % fileroot
|
||||
with open(output, "wb") as fh:
|
||||
# output PNG
|
||||
w = png.Writer(280, 192, greyscale=True, bitdepth=1)
|
||||
bits = np.fliplr(np.unpackbits(lines.ravel()).reshape(-1,8)[:,1:8])
|
||||
bw = bits.reshape((192, 280))
|
||||
w.write(fh, bw)
|
||||
print("created bw representation of HGR screen: %s" % output)
|
||||
|
||||
offsets = self.screen.generate_row_addresses(0)
|
||||
# print ["%04x" % i for i in offsets]
|
||||
screen = np.zeros(8192, dtype=np.uint8)
|
||||
for row in range(192):
|
||||
offset = offsets[row]
|
||||
screen[offset:offset+40] = lines[row]
|
||||
|
||||
output = "%s.hgr" % fileroot
|
||||
with open(output, "wb") as fh:
|
||||
fh.write(screen)
|
||||
print("created HGR screen: %s" % output)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
disclaimer = '''
|
||||
; This file was generated by HiSprite.py, a sprite compiler by Quinn Dunki.
|
||||
@ -996,6 +1157,7 @@ if __name__ == "__main__":
|
||||
parser.add_argument("-a", "--assembler", default="cc65", choices=["cc65","mac65"], help="Assembler syntax (default: %(default)s)")
|
||||
parser.add_argument("-p", "--processor", default="any", choices=["any","6502", "65C02"], help="Processor type (default: %(default)s)")
|
||||
parser.add_argument("-s", "--screen", default="hgrcolor", choices=["hgrcolor","hgrbw"], help="Screen format (default: %(default)s)")
|
||||
parser.add_argument("-i", "--image", default="line", choices=["line", "color","bw"], help="Screen format used for full page image conversion (default: %(default)s)")
|
||||
parser.add_argument("-n", "--name", default="", help="Name for generated assembly function (default: based on image filename)")
|
||||
parser.add_argument("-k", "--clobber", action="store_true", default=False, help="don't save the registers on the stack")
|
||||
parser.add_argument("-d", "--double-buffer", action="store_true", default=False, help="add code blit to either page (default: page 1 only)")
|
||||
@ -1028,19 +1190,30 @@ if __name__ == "__main__":
|
||||
|
||||
for pngfile in options.files:
|
||||
try:
|
||||
sprite_code = Sprite(pngfile, assembler, screen, options.xdraw, options.mask, options.backing_store, options.clobber, options.double_buffer, options.damage, options.processor, options.name)
|
||||
reader = png.Reader(pngfile)
|
||||
pngdata = reader.asRGB8()
|
||||
except RuntimeError, e:
|
||||
print "%s: %s" % (pngfile, e)
|
||||
sys.exit(1)
|
||||
except png.Error, e:
|
||||
print "%s: %s" % (pngfile, e)
|
||||
sys.exit(1)
|
||||
listings.append(sprite_code)
|
||||
if options.output_prefix:
|
||||
r = RowLookup(assembler, screen)
|
||||
luts[r.slug] = r
|
||||
c = ColLookup(assembler, screen)
|
||||
luts[c.slug] = c
|
||||
|
||||
name = options.name if options.name else os.path.splitext(pngfile)[0]
|
||||
slug = slugify(name)
|
||||
|
||||
w, h = pngdata[0:2]
|
||||
if w == 280 and h == 192:
|
||||
# Full screen conversion!
|
||||
Image(pngdata, name, options.image.lower())
|
||||
else:
|
||||
sprite_code = Sprite(slug, pngdata, assembler, screen, options.xdraw, options.mask, options.backing_store, options.clobber, options.double_buffer, options.damage, options.processor)
|
||||
listings.append(sprite_code)
|
||||
if options.output_prefix:
|
||||
r = RowLookup(assembler, screen)
|
||||
luts[r.slug] = r
|
||||
c = ColLookup(assembler, screen)
|
||||
luts[c.slug] = c
|
||||
|
||||
listings.extend([luts[k] for k in sorted(luts.keys())])
|
||||
|
||||
|
18
Makefile
18
Makefile
@ -3,7 +3,7 @@ COLORSPRITE = moldy_burger.png
|
||||
#COLORSPRITE = apple.png
|
||||
BWSPRITE = apple-sprite9x11.png
|
||||
|
||||
all: cpbg.dsk fonttest.dsk
|
||||
all: cpbg.dsk fonttest.dsk titles.dsk
|
||||
|
||||
rowlookup.s: HiSprite.py
|
||||
python HiSprite.py -a mac65 -p 6502 -r > rowlookup.s
|
||||
@ -48,8 +48,19 @@ cpbg.xex: cpbg.s cpbg-sprite-driver.s
|
||||
cpbg.dsk: HiSprite.py cpbg.xex
|
||||
atrcopy cpbg.dsk boot -b cpbg.xex --brun 6000 -f
|
||||
|
||||
titles.dsk: HiSprite.py cpbg.xex
|
||||
atrcopy titles.dsk boot -d partycrasher-software.hgr@2000 player-missile.hgr@4000 -b cpbg.xex --brun 6000 -f
|
||||
player-missile.hgr: HiSprite.py player-missile.png
|
||||
python HiSprite.py player-missile.png
|
||||
|
||||
kansasfest-disclaimer.hgr: HiSprite.py kansasfest-disclaimer.png
|
||||
python HiSprite.py -i color kansasfest-disclaimer.png
|
||||
|
||||
partycrasher-software.hgr: HiSprite.py partycrasher-software.png
|
||||
python HiSprite.py -i color partycrasher-software.png
|
||||
|
||||
titles.dsk: HiSprite.py cpbg.xex player-missile.hgr kansasfest-disclaimer.hgr partycrasher-software.hgr
|
||||
atrcopy titles.dsk boot -d kansasfest-disclaimer.hgr@2000 partycrasher-software.hgr@4000 player-missile.hgr@2000 -b cpbg.xex --brun 6000 -f
|
||||
#atrcopy titles.dsk boot -d partycrasher-software.bin@2000 kansasfest-disclaimer.bin@4000 player-missile-bg.bin@2000 -b cpbg.xex --brun 6000 -f
|
||||
#atrcopy titles.dsk boot -d player-missile.hgr@2000 -b cpbg.xex --brun 6000 -f
|
||||
|
||||
fonttest.dsk: fonttest.s fatfont.s
|
||||
atasm -ofonttest.xex fonttest.s -Lfonttest.var -gfonttest.lst
|
||||
@ -61,3 +72,4 @@ clean:
|
||||
rm -f colortest.dsk colortest.xex colortest.var colortest.lst
|
||||
rm -f multitest.dsk multitest.xex multitest.var multitest.lst multitest-sprite-driver.s multitest-bwsprite.s multitest-hgrcols-7x1.s multitest-hgrrows.s
|
||||
rm -f cpbg.dsk cpbg.xex cpbg.var cpbg.lst cpbg-sprite-driver.s cpbg-bwsprite.s cpbg-hgrcols-7x1.s cpbg-hgrrows.s
|
||||
rm -f player-missile.hgr kansasfest-disclaimer.hgr partycrasher-software.hgr
|
||||
|
Loading…
Reference in New Issue
Block a user