Don't prematurely apply palette, cache all background tiles

This commit is contained in:
Luke Hubmayer-Werner 2018-03-22 20:16:26 +10:30
parent f26f6dda67
commit 7f028017b7
2 changed files with 167 additions and 109 deletions

View File

@ -8,7 +8,9 @@ import os
from struct import unpack from struct import unpack
from itertools import chain from itertools import chain
from array import array from array import array
from snestile import generate_glyphs, generate_glyphs_large, generate_palette, create_tile, create_tile_mode7_compressed from snestile import generate_glyphs, generate_glyphs_large, generate_palette, create_tile, create_tile_indexed, create_tile_mode7_compressed
from snestile import Canvas, Canvas_Indexed
from snestile import bg_color, bg_trans
import const import const
import time import time
@ -71,8 +73,7 @@ if pyqt_version == 0:
'Make sure you installed the PyQt4 package.') 'Make sure you installed the PyQt4 package.')
sys.exit(-1) sys.exit(-1)
bg_color = QColor(0, 0, 128)
bg_trans = QColor(0, 0, 0, 0)
HEX_PREFIX = '#' # '$' or '0x' are also nice HEX_PREFIX = '#' # '$' or '0x' are also nice
@ -114,13 +115,17 @@ class FF5Reader(QMainWindow):
def __init__(self): def __init__(self):
QMainWindow.__init__(self, None) QMainWindow.__init__(self, None)
global glyph_sprites_en_large, glyph_sprites_en_small, glyph_sprites_jp_small, glyph_sprites_jp_large, glyph_sprites_kanji, glyph_sprites_jp_dialogue global glyph_sprites_en_large, glyph_sprites_en_small, glyph_sprites_jp_small, glyph_sprites_jp_large, glyph_sprites_kanji, glyph_sprites_jp_dialogue
perfcount()
print('Generating Glyphs')
glyph_sprites_en_small = generate_glyphs(ROM_en, 0x11F000) glyph_sprites_en_small = generate_glyphs(ROM_en, 0x11F000)
glyph_sprites_en_large = generate_glyphs_large(ROM_en, 0x03E800) glyph_sprites_en_large = generate_glyphs_large(ROM_en, 0x03E800)
glyph_sprites_jp_small = generate_glyphs(ROM_jp, 0x11F000) glyph_sprites_jp_small = generate_glyphs(ROM_jp, 0x11F000)
glyph_sprites_jp_large = generate_glyphs_large(ROM_jp, 0x03E800) glyph_sprites_jp_large = generate_glyphs_large(ROM_jp, 0x03E800)
glyph_sprites_kanji = generate_glyphs_large(ROM_jp, 0x1BD000, 0x1AA) # Kanji are unchanged in EN version glyph_sprites_kanji = generate_glyphs_large(ROM_jp, 0x1BD000, 0x1AA) # Kanji are unchanged in EN version
perfcount()
global zone_names global zone_names
print('Generating Strings')
zone_names = make_string_img_list(0x107000, 2, 0x100, start_str=0x270000, start_jp_str=0x107200, indirect=True, large=True) zone_names = make_string_img_list(0x107000, 2, 0x100, start_str=0x270000, start_jp_str=0x107200, indirect=True, large=True)
items = make_string_img_list(0x111380, 9, 256) items = make_string_img_list(0x111380, 9, 256)
magics = make_string_img_list(0x111C80, 6, 87) magics = make_string_img_list(0x111C80, 6, 87)
@ -130,7 +135,9 @@ class FF5Reader(QMainWindow):
job_names = make_string_img_list(0x115600, 8, 22) job_names = make_string_img_list(0x115600, 8, 22)
ability_names = make_string_img_list(0x116200, 8, 33) ability_names = make_string_img_list(0x116200, 8, 33)
battle_commands = make_string_img_list(0x201150, 7, 0x60, 0x115800, 5) battle_commands = make_string_img_list(0x201150, 7, 0x60, 0x115800, 5)
perfcount()
dialogue = make_string_img_list(0x2013F0, 3, 0x900, start_jp=0x082220, len_jp=2, start_str=0x0, start_jp_str=0x0A0000, indirect=True, large=True, macros_en=const.Dialogue_Macros_EN, macros_jp=const.Dialogue_Macros_JP) dialogue = make_string_img_list(0x2013F0, 3, 0x900, start_jp=0x082220, len_jp=2, start_str=0x0, start_jp_str=0x0A0000, indirect=True, large=True, macros_en=const.Dialogue_Macros_EN, macros_jp=const.Dialogue_Macros_JP)
perfcount()
def split_tilesets(data): def split_tilesets(data):
tilesets = [(data & 0x00003F), tilesets = [(data & 0x00003F),
@ -173,34 +180,21 @@ class FF5Reader(QMainWindow):
tileset_data = [] tileset_data = []
for i in range(0x1C): for i in range(0x1C):
offset = 0x0F0000 + (i*2) offset = 0x0F0000 + (i*2)
pointer = 0x0F0000 + indirect(ROM_en, offset) # int.from_bytes(ROM_en[offset:offset+2],'little') pointer = 0x0F0000 + indirect(ROM_en, offset)
length = indirect(ROM_en, offset+2) - indirect(ROM_en, offset) # int.from_bytes(ROM_en[offset+2:offset+4],'little') - int.from_bytes(ROM_en[offset:offset+2],'little') length = indirect(ROM_en, offset+2) - indirect(ROM_en, offset)
tileset_data.append((hex(i, 2), hex(offset, 6), hex(pointer, 6), hex(length, 4))) tileset_data.append((hex(i, 2), hex(offset, 6), hex(pointer, 6), hex(length, 4)))
npc_layers = [] npc_layers = []
offset = 0x0E59C0 offset = 0x0E59C0
for layer in range(const.npc_layer_count): for layer in range(const.npc_layer_count):
i = offset + (layer*2) i = offset + (layer*2)
start = indirect(ROM_en, i) + offset # int.from_bytes(ROM_en[i:i+2],'little') + offset start = indirect(ROM_en, i) + offset
next = indirect(ROM_en, i+2) + offset # int.from_bytes(ROM_en[i+2:i+4],'little') + offset next = indirect(ROM_en, i+2) + offset
npcs = (next - start) // 7 npcs = (next - start) // 7
for npc in range(npcs): for npc in range(npcs):
address = start + (npc*7) address = start + (npc*7)
npc_layers.append([hex(i, 6), hex(layer, 3)] + parse_struct(ROM_en, address, const.npc_layer_structure)) npc_layers.append([hex(i, 6), hex(layer, 3)] + parse_struct(ROM_en, address, const.npc_layer_structure))
#enemy_tile_layouts = []
#address = 0x10D004
#for i in range(0x66):
#offset = address + (i*8)
#img = QImage(8, 8, QImage.Format_Mono)
#img.setColorTable(const.mono_palette)
#for i in range(8):
#ptr = img.scanLine(i)
#ptr.setsize(32)
#ptr[0:1] = ROM_en[offset+i:offset+i+1]
#pixmap = QPixmap.fromImage(img)
#enemy_tile_layouts.append(pixmap.scaled(16, 16))
enemy_sprite_data = [] enemy_sprite_data = []
enemy_sprite_structure = [ enemy_sprite_structure = [
('Sprite data offset', 2, None), ('Sprite data offset', 2, None),
@ -212,20 +206,36 @@ class FF5Reader(QMainWindow):
for i in range(0x180): for i in range(0x180):
enemy_sprite_data.append(parse_struct(ROM_en, address + (i*5), enemy_sprite_structure) + enemy_names[i][2:4]) enemy_sprite_data.append(parse_struct(ROM_en, address + (i*5), enemy_sprite_structure) + enemy_names[i][2:4])
perfcount()
print('Generating map tiles')
worldmap_tiles = make_world_map_tiles(ROM_jp, 0x1B8000, 0x0FF9C0, 0x0FFCC0) worldmap_tiles = make_world_map_tiles(ROM_jp, 0x1B8000, 0x0FF9C0, 0x0FFCC0)
worldmap_tiles += make_world_map_tiles(ROM_jp, 0x1BA000, 0x0FFAC0, 0x0FFDC0) worldmap_tiles += make_world_map_tiles(ROM_jp, 0x1BA000, 0x0FFAC0, 0x0FFDC0)
worldmap_tiles += make_world_map_tiles(ROM_jp, 0x1BC000, 0x0FFBC0, 0x0FFEC0, length=128) worldmap_tiles += make_world_map_tiles(ROM_jp, 0x1BC000, 0x0FFBC0, 0x0FFEC0, length=128)
fieldmap_tiles = [make_field_map_tileset(ROM_jp, i) for i in range(const.zone_count)] perfcount()
field_tiles = make_all_field_tiles(ROM_jp)
field_minitiles = make_all_field_minitiles(ROM_jp)
perfcount()
st_field_tiles = [stitch_tileset(ts) for ts in field_tiles]
st_field_minitiles = [stitch_tileset(ts) for ts in field_minitiles]
perfcount()
fieldmap_tiles = [make_field_map_tile_pixmap(ROM_jp, i, st_field_tiles, st_field_minitiles) for i in range(const.zone_count)]
perfcount()
print('Generating other sprites')
self.battle_strips = make_character_battle_sprites(ROM_en) self.battle_strips = make_character_battle_sprites(ROM_en)
status_strips = make_character_status_sprites(ROM_en) status_strips = make_character_status_sprites(ROM_en)
enemy_sprites = make_enemy_sprites(ROM_en) enemy_sprites = make_enemy_sprites(ROM_en)
enemy_sprites_named = [stack_labels(s, d[-2]) for s, d in zip(enemy_sprites, enemy_sprite_data)]
perfcount()
print('Generating FF4 and FF6 stuff')
self.battle_strips_ff4 = make_character_battle_sprites_ff4(ROM_FF4jp) self.battle_strips_ff4 = make_character_battle_sprites_ff4(ROM_FF4jp)
self.field_strips_ff4 = make_character_field_sprites_ff4(ROM_FF4jp) self.field_strips_ff4 = make_character_field_sprites_ff4(ROM_FF4jp)
self.portraits_ff4 = make_character_portrait_sprites_ff4(ROM_FF4jp) self.portraits_ff4 = make_character_portrait_sprites_ff4(ROM_FF4jp)
self.battle_strips_ff6 = make_character_battle_sprites_ff6(ROM_FF6jp) self.battle_strips_ff6 = make_character_battle_sprites_ff6(ROM_FF6jp)
self.portraits_ff6 = make_character_portrait_sprites_ff6(ROM_FF6jp) self.portraits_ff6 = make_character_portrait_sprites_ff6(ROM_FF6jp)
perfcount()
enemy_sprites_named = [stack_labels(s, d[-2]) for s, d in zip(enemy_sprites, enemy_sprite_data)]
self.gamewidget = QTabWidget() self.gamewidget = QTabWidget()
self.ff4widget = QTabWidget() self.ff4widget = QTabWidget()
@ -250,7 +260,6 @@ class FF5Reader(QMainWindow):
sprites_tab.addTab(make_pixmap_table(fieldmap_tiles, cols=8, scale=2), 'Fieldmap Tiles') sprites_tab.addTab(make_pixmap_table(fieldmap_tiles, cols=8, scale=2), 'Fieldmap Tiles')
sprites_tab.addTab(make_pixmap_table(self.battle_strips, cols=22, scale=2), 'Character Battle Sprites') sprites_tab.addTab(make_pixmap_table(self.battle_strips, cols=22, scale=2), 'Character Battle Sprites')
sprites_tab.addTab(make_pixmap_table(status_strips, cols=22, scale=2), 'Status Sprites') sprites_tab.addTab(make_pixmap_table(status_strips, cols=22, scale=2), 'Status Sprites')
#sprites_tab.addTab(make_pixmap_table(enemy_sprites, scale=1), 'Enemy Sprites')
sprites_tab.addTab(make_pixmap_table(enemy_sprites_named, cols=32, scale=1), 'Enemy Sprites') sprites_tab.addTab(make_pixmap_table(enemy_sprites_named, cols=32, scale=1), 'Enemy Sprites')
self.ff4widget.addTab(make_pixmap_table(self.battle_strips_ff4, cols=16, scale=2), 'Character Battle Sprites') self.ff4widget.addTab(make_pixmap_table(self.battle_strips_ff4, cols=16, scale=2), 'Character Battle Sprites')
@ -305,35 +314,12 @@ class FF5Reader(QMainWindow):
self.decoder_input.setText('') self.decoder_input.setText('')
class Canvas:
def __init__(self, cols, rows, color=bg_trans):
self.image = QImage(8*cols, 8*rows, QImage.Format_ARGB32_Premultiplied)
self.image.fill(color)
self.painter = QtGui.QPainter(self.image)
self.max_x = 1
self.max_y = 1
def __del__(self):
del self.painter
def draw_pixmap(self, col, row, pixmap):
self.painter.drawPixmap(col*8, row*8, pixmap)
if col > self.max_x:
self.max_x = col
if row > self.max_y:
self.max_y = row
def pixmap(self, trim=False):
if trim:
return QPixmap.fromImage(self.image.copy(0, 0, self.max_x*8+8, self.max_y*8+8))
return QPixmap.fromImage(self.image)
def parse_struct(rom, offset, structure): def parse_struct(rom, offset, structure):
out = [hex(offset, 6)] out = [hex(offset, 6)]
j = 0 j = 0
for title, length, handler in structure: for title, length, handler in structure:
val = indirect(rom, offset+j, length=length) # int.from_bytes(rom[offset+j:offset+j+z[1]],'little') val = indirect(rom, offset+j, length=length)
if callable(handler): if callable(handler):
out.append(handler(val)) out.append(handler(val))
elif handler and val < len(handler): elif handler and val < len(handler):
@ -383,24 +369,27 @@ def make_world_map_tiles(rom, tiles_address, lut_address, palette_address, lengt
tiles.append(create_tile_mode7_compressed(rom[tiles_address+i*32:tiles_address+i*32+32], palette)) tiles.append(create_tile_mode7_compressed(rom[tiles_address+i*32:tiles_address+i*32+32], palette))
return tiles return tiles
def make_field_tiles(rom, id, palette): def make_field_tiles(rom, id):
#tile_offset = indirect(rom, 0x1C2D84 + id*4) + 0x2E24
#tile_bank = indirect(rom, 0x1C2D86 + id*4) + 0x1C
#tiles_address = tile_bank*0x10000 + tile_offset
tiles_address = indirect(rom, 0x1C2D84 + id*4, length=4) + 0x1C2E24 tiles_address = indirect(rom, 0x1C2D84 + id*4, length=4) + 0x1C2E24
tiles = [] return [create_tile_indexed(rom[tiles_address+i*32:tiles_address+i*32+32]) for i in range(256)]
for i in range(256):
tiles.append(create_tile(rom[tiles_address+i*32:tiles_address+i*32+32], palette))
return tiles
def make_field_minitiles(rom, id, palette): def make_field_minitiles(rom, id):
tiles_address = indirect(rom, 0x1C0000 + id*2) + 0x1C0024 tiles_address = indirect(rom, 0x1C0000 + id*2) + 0x1C0024
tiles = [] return [create_tile_indexed(rom[tiles_address+i*16:tiles_address+i*16+16]) for i in range(256)]
for i in range(256):
tiles.append(create_tile(rom[tiles_address+i*16:tiles_address+i*16+16], palette))
return tiles
def make_field_map_tiles(rom, id): def make_all_field_tiles(rom):
return [make_field_tiles(rom, i) for i in range(40)]
def make_all_field_minitiles(rom):
return [make_field_minitiles(rom, i) for i in range(18)]
def stitch_tileset(tiles):
canvas = Canvas_Indexed(16, len(tiles)//16)
for i, tile in enumerate(tiles):
canvas.draw_tile(i%16, i//16, tile)
return canvas
def get_field_map_tiles(rom, id):
''' '''
This is a bit of a mess of pointer chains for now, so generalising it will have to wait. This is a bit of a mess of pointer chains for now, so generalising it will have to wait.
Palette selection is probably determined by the tilemap which is outside the scope of this, so we'll just use #1. Palette selection is probably determined by the tilemap which is outside the scope of this, so we'll just use #1.
@ -417,19 +406,19 @@ def make_field_map_tiles(rom, id):
tile_index_0 = (tilesets & 0x00003F) # (indirect(rom, 0x0E9C09 + i8) & 0x003F) tile_index_0 = (tilesets & 0x00003F) # (indirect(rom, 0x0E9C09 + i8) & 0x003F)
tile_index_1 = (tilesets & 0x000FC0) >> 6 # (indirect(rom, 0x0E9C09 + i8) & 0x0FC0)>>6 tile_index_1 = (tilesets & 0x000FC0) >> 6 # (indirect(rom, 0x0E9C09 + i8) & 0x0FC0)>>6
tile_index_2 = (tilesets & 0x03F000) >> 12 # (indirect(rom, 0x0E9C0A + i8) & 0x03F0)>>4 tile_index_2 = (tilesets & 0x03F000) >> 12 # (indirect(rom, 0x0E9C0A + i8) & 0x03F0)>>4
tile_index_3 = (tilesets & 0xFC0000) >> 18 # (indirect(rom, 0x0E9C0A + i8) & 0x03F0)>>4 minitile_index = (tilesets & 0xFC0000) >> 18 # (indirect(rom, 0x0E9C0A + i8) & 0x03F0)>>4
pal_offset = indirect(rom, 0x0E9C16 + i8) * 0x100 pal_offset = indirect(rom, 0x0E9C16 + i8) * 0x100
palette_address = 0x03BB00 + pal_offset palette_address = 0x03BB00 + pal_offset
print(pal_offset, palette_address)
palettes = [generate_palette(rom, palette_address+i*32, transparent=True) for i in range(8)] palettes = [generate_palette(rom, palette_address+i*32, transparent=True) for i in range(8)]
return tile_index_0, tile_index_1, tile_index_2, minitile_index, palettes
return make_field_tiles(rom, tile_index_0, palettes[1])+make_field_tiles(rom, tile_index_1, palettes[1])+make_field_tiles(rom, tile_index_2, palettes[1])+make_field_minitiles(rom, tile_index_3, palettes[1]) def make_field_map_tile_pixmap(rom, id, st_tiles, st_minitiles):
*tiles, minitile, palettes = get_field_map_tiles(rom, id)
def make_field_map_tileset(rom, id): p = palettes[1]
tiles = make_field_map_tiles(rom, id) canvas = Canvas(16, 64)
canvas = Canvas(16, len(tiles)//16) for i, ts in enumerate(tiles):
for i, tile in enumerate(tiles): canvas.draw_pixmap(0, i*16, st_tiles[ts].pixmap(p))
canvas.draw_pixmap(i%16, i//16, tile) canvas.draw_pixmap(0, 48, st_minitiles[minitile].pixmap(p))
return canvas.pixmap() return canvas.pixmap()
def make_battle_strip(rom, palette_address, tile_address, num_tiles, bpp=4): def make_battle_strip(rom, palette_address, tile_address, num_tiles, bpp=4):
@ -438,7 +427,7 @@ def make_battle_strip(rom, palette_address, tile_address, num_tiles, bpp=4):
else: else:
palette = palette_address palette = palette_address
b = 24 if bpp==3 else 32 b = 24 if bpp==3 else 32
battle_strip = Canvas(2, divceil(num_tiles, 2)) # KO sprites are here which means more tiles than FFV battle_strip = Canvas(2, divceil(num_tiles, 2))
for j in range(num_tiles): for j in range(num_tiles):
offset = tile_address+(j*b) offset = tile_address+(j*b)
battle_strip.draw_pixmap(j%2, j//2, create_tile(rom[offset:offset+b], palette)) battle_strip.draw_pixmap(j%2, j//2, create_tile(rom[offset:offset+b], palette))
@ -482,29 +471,23 @@ def make_character_portrait_sprites_ff4(rom):
tile_address = 0xED3C0 tile_address = 0xED3C0
palette_address = 0x686D0 palette_address = 0x686D0
palettes = [generate_palette(rom, palette_address+i*16, transparent=True) for i in range(14)] palettes = [generate_palette(rom, palette_address+i*16, transparent=True) for i in range(14)]
portrait_images = []
for t_start in [tile_address+i*16*24 for i in range(17)]:
canvas = Canvas_Indexed(4, 4)
for t in range(16):
offset = t_start+(t*24)
canvas.draw_tile(t%4, t//4, create_tile_indexed(rom[offset:offset+24]))
portrait_images.append(canvas)
portraits = [] portraits = []
for palette, t_start in zip(palettes, [tile_address+i*16*24 for i in range(14)]): for palette, portrait in zip(palettes, portrait_images):
canvas = Canvas(4, 4) portraits.append(portrait.pixmap(palette))
for t in range(16): for portrait in portrait_images[14:]: # 14, 15, 16 are Pig, Mini, Toad and use character palettes
offset = t_start+(t*24)
canvas.draw_pixmap(t%4, t//4, create_tile(rom[offset:offset+24], palette))
portraits.append(canvas.pixmap())
# Pig, mini, toad
for t_start in [tile_address+i*16*24 for i in range(14, 17)]:
for palette in palettes: for palette in palettes:
canvas = Canvas(4, 4) portraits.append(portrait.pixmap(palette))
for t in range(16):
offset = t_start+(t*24)
canvas.draw_pixmap(t%4, t//4, create_tile(rom[offset:offset+24], palette))
portraits.append(canvas.pixmap())
# Palette-swap time!
for palette in palettes: for palette in palettes:
for t_start in [tile_address+i*16*24 for i in range(14)]: for portrait in portrait_images[:14]:
canvas = Canvas(4, 4) portraits.append(portrait.pixmap(palette))
for t in range(16):
offset = t_start+(t*24)
canvas.draw_pixmap(t%4, t//4, create_tile(rom[offset:offset+24], palette))
portraits.append(canvas.pixmap())
return portraits return portraits
@ -533,21 +516,20 @@ def make_character_portrait_sprites_ff6(rom):
palettes = [generate_palette(rom, palette_address+i*32, transparent=True) for i in range(19)] palettes = [generate_palette(rom, palette_address+i*32, transparent=True) for i in range(19)]
# Coordinates for each tile # Coordinates for each tile
LUT = [(0,0), (1,0), (2,0), (3,0), (0,2), (1,2), (2,2), (3,2), (4,0), (4,1), (4,2), (4,3), (4,4), (0,4), (1,4), (2,4), (0,1), (1,1), (2,1), (3,1), (0,3), (1,3), (2,3), (3,3), (3,4)] LUT = [(0,0), (1,0), (2,0), (3,0), (0,2), (1,2), (2,2), (3,2), (4,0), (4,1), (4,2), (4,3), (4,4), (0,4), (1,4), (2,4), (0,1), (1,1), (2,1), (3,1), (0,3), (1,3), (2,3), (3,3), (3,4)]
portraits = [] portrait_images = []
for palette, t_start in zip(palettes, [tile_address+i*25*32 for i in range(19)]):
canvas = Canvas(5, 5)
for t in range(25):
offset = t_start+(t*32)
canvas.draw_pixmap(*LUT[t], create_tile(rom[offset:offset+32], palette))
portraits.append(canvas.pixmap())
# Palette-swap time!
for palette in palettes:
for t_start in [tile_address+i*25*32 for i in range(19)]: for t_start in [tile_address+i*25*32 for i in range(19)]:
canvas = Canvas(5, 5) canvas = Canvas_Indexed(5, 5)
for t in range(25): for t in range(25):
offset = t_start+(t*32) offset = t_start+(t*32)
canvas.draw_pixmap(*LUT[t], create_tile(rom[offset:offset+32], palette)) canvas.draw_tile(*LUT[t], create_tile_indexed(rom[offset:offset+32]))
portraits.append(canvas.pixmap()) portrait_images.append(canvas)
portraits = []
for palette, portrait in zip(palettes, portrait_images):
portraits.append(portrait.pixmap(palette))
for palette in palettes:
for portrait in portrait_images:
portraits.append(portrait.pixmap(palette))
return portraits return portraits
@ -565,9 +547,7 @@ def make_character_status_sprites(rom):
palette_address = 0x14A660 palette_address = 0x14A660
pixmaps = [] pixmaps = []
for i in range(5): for i in range(5):
palette = generate_palette(rom, palette_address + (i*22*32)) # Freelance palette per character palette = generate_palette(rom, palette_address + (i*22*32), transparent=True) # Freelance palette per character
# We don't want the background drawn, so we'll make that colour transparent
palette[0] = 0
wounded = Canvas(3, 2) wounded = Canvas(3, 2)
for j in range(6): for j in range(6):
offset = tile_address+(i*192)+(j*32) offset = tile_address+(i*192)+(j*32)
@ -828,6 +808,19 @@ def hex(num, digits):
def indirect(rom, start, length=2): def indirect(rom, start, length=2):
return int.from_bytes(rom[start:start+length], 'little') return int.from_bytes(rom[start:start+length], 'little')
last_perfcount = None
def perfcount():
'''
Really basic timing for debugging
'''
global last_perfcount
t = time.perf_counter()
if last_perfcount:
print(t-last_perfcount)
else:
print('perfcount initialised')
last_perfcount = t
def main(): def main():
app = QApplication(sys.argv) app = QApplication(sys.argv)

View File

@ -26,9 +26,13 @@ if pyqt_version == 0:
raise raise
def create_tile(data, palette): bg_color = QColor(0, 0, 128)
bg_trans = QColor(0, 0, 0, 0)
def create_tile_indexed(data):
''' '''
Creates a QPixmap of a SNES tile. DO NOT USE OUTSIDE OF QApplication CONTEXT Creates a QImage of a SNES tile. Useful for assigning palettes later.
DO NOT USE OUTSIDE OF QApplication CONTEXT
''' '''
planes = len(data)//8 planes = len(data)//8
tile = array('B', range(64)) tile = array('B', range(64))
@ -38,11 +42,9 @@ def create_tile(data, palette):
if planes == 0: if planes == 0:
raise ValueError("Empty bytes passed") raise ValueError("Empty bytes passed")
if planes == 1: if planes == 1:
img.setColorTable([0x00000080, 0xFFFFFFFF])
for i, (j, x) in enumerate([(j,x) for j in range(8) for x in reversed(range(8))]): for i, (j, x) in enumerate([(j,x) for j in range(8) for x in reversed(range(8))]):
tile[i] = (data[j] >> x & 1) tile[i] = (data[j] >> x & 1)
else: else:
img.setColorTable(palette)
for i, (j, x) in enumerate([(j,x) for j in range(0, 16, 2) for x in reversed(range(8))]): for i, (j, x) in enumerate([(j,x) for j in range(0, 16, 2) for x in reversed(range(8))]):
tile[i] = (data[j] >> x & 1) | ((data[j+1] >> x & 1) << 1) tile[i] = (data[j] >> x & 1) | ((data[j+1] >> x & 1) << 1)
if planes == 3: if planes == 3:
@ -56,6 +58,14 @@ def create_tile(data, palette):
tile[i] |= ((data[j] >> x & 1) << 4) | ((data[j+1] >> x & 1) << 5) \ tile[i] |= ((data[j] >> x & 1) << 4) | ((data[j+1] >> x & 1) << 5) \
| ((data[j+16] >> x & 1) << 6) | ((data[j+17] >> x & 1) << 7) | ((data[j+16] >> x & 1) << 6) | ((data[j+17] >> x & 1) << 7)
imgbits[:64] = tile imgbits[:64] = tile
return img
def create_tile(data, palette=[0x00000080, 0xFFFFFFFF]):
'''
Creates a QPixmap of a SNES tile. DO NOT USE OUTSIDE OF QApplication CONTEXT
'''
img = create_tile_indexed(data)
img.setColorTable(palette)
return QPixmap.fromImage(img) return QPixmap.fromImage(img)
def create_tile_mode7(data, palette): def create_tile_mode7(data, palette):
@ -175,3 +185,58 @@ def generate_palette(rom, offset, length=32, transparent=False):
if transparent: if transparent:
palette[0] = 0 palette[0] = 0
return palette return palette
class Canvas:
def __init__(self, cols, rows, color=bg_trans):
self.image = QImage(8*cols, 8*rows, QImage.Format_ARGB32_Premultiplied)
self.image.fill(color)
self.painter = QtGui.QPainter(self.image)
self.max_x = 1
self.max_y = 1
def __del__(self):
del self.painter
def draw_pixmap(self, col, row, pixmap):
self.painter.drawPixmap(col*8, row*8, pixmap)
if col > self.max_x:
self.max_x = col
if row > self.max_y:
self.max_y = row
def pixmap(self, trim=False):
if trim:
return QPixmap.fromImage(self.image.copy(0, 0, self.max_x*8+8, self.max_y*8+8))
return QPixmap.fromImage(self.image)
class Canvas_Indexed:
def __init__(self, cols, rows, color=0):
self.image = QImage(8*cols, 8*rows, QImage.Format_Indexed8)
self.width = 8*cols
self.image.fill(0)
self.imgbits = self.image.bits()
self.imgbits.setsize(self.image.byteCount())
self.max_col = 1
self.max_row = 1
def draw_tile(self, col, row, image):
imgbits = image.bits()
imgbits.setsize(image.byteCount())
x = col*8
y = row*8
start = x + y*self.width
for i in range(8):
offset = i*self.width
self.imgbits[start+offset:start+offset+8] = imgbits[i*8:i*8+8]
self.max_col = max(col, self.max_col)
self.max_row = max(row, self.max_row)
def pixmap(self, palette, trim=False):
if trim:
img = self.image.copy(0, 0, self.max_col*8+8, self.max_row*8+8)
img.setColorTable(palette)
return QPixmap.fromImage(img)
self.image.setColorTable(palette)
return QPixmap.fromImage(self.image)