Initial zone/fieldmap drawing

This commit is contained in:
Luke Hubmayer-Werner 2018-03-27 23:19:20 +10:30
parent 7fe8fb7351
commit 5d701a02fc
5 changed files with 205 additions and 104 deletions

View File

@ -152,30 +152,32 @@ class FF5Reader(QMainWindow):
(data & 0x03F000) >> 12,
(data & 0xFC0000) >> 18]
return ' '.join([hex(i,2) for i in tilesets])
def split_blockmaps(data):
blockmaps = [(data & 0x000003FF) - 1,
((data & 0x000FFC00) >> 10) - 1,
((data & 0x3FF00000) >> 20) - 1]
return ' '.join([hex(i,3) for i in blockmaps])
zone_structure = [('NPC Layer', 2, None),
('Name', 1, [z[2] for z in zone_names]),
('ShadowFlags', 1, None),
(hex(4, 2), 1, None),
(hex(5, 2), 1, None),
('Flags '+hex(6,2),1, None),
(hex(7, 2), 1, None),
('Tilesets', 3, split_tilesets),
(hex(11, 2), 1, None),
('Collision Layer',1, None),
(hex(13, 2), 1, None),
(hex(14, 2), 1, None),
(hex(15, 2), 1, None),
(hex(16, 2), 1, None),
(hex(17, 2), 1, None),
(hex(18, 2), 1, None),
(hex(19, 2), 1, None),
(hex(20, 2), 1, None),
(hex(21, 2), 1, None),
('Palette', 1, None),
(hex(23, 2), 1, None),
(hex(24, 2), 1, None),
('Music', 1, const.BGM_Tracks)]
zone_structure = [('NPC Layer', 2, None), # 00 01
('Name', 1, [z[2] for z in zone_names]), # 02
('ShadowFlags', 1, None), # 03
('Graphic maths', 1, None), # 04
('Tile properties', 1, None), # 05
('Flags '+hex(6), 1, None), # 06
(hex(7), 1, None), # 07
('Blockset', 1, None), # 08
('Tilesets', 3, split_tilesets), # 09 0A 0B
('Blockmaps', 4, split_blockmaps), # 0C 0D 0E 0F
(hex(16), 1, None), # 10
(hex(17), 1, None), # 11
(hex(18), 1, None), # 12
(hex(19), 1, None), # 13
(hex(20), 1, None), # 14
(hex(21), 1, None), # 15
('Palette', 1, None), # 16
(hex(23), 1, None), # 17
(hex(24), 1, None), # 18
('Music', 1, const.BGM_Tracks)] # 19
zone_headers = ['Address'] + [z[0] for z in zone_structure]
zone_data = [parse_struct(ROM_en, 0x0E9C00 + (i*0x1A), zone_structure) for i in range(const.zone_count)]
@ -223,20 +225,21 @@ class FF5Reader(QMainWindow):
perfcount()
print('Generating map tiles')
worldmap_palettes = [generate_palette(ROM_jp, 0x0FFCC0+(i*0x100), length=0x160, transparent=True) for i in range(3)]
world_tiles = [make_worldmap_tiles(ROM_jp, 0x0FF0C0+(i*0x300), 0x1B8000+(i*0x2000), 0x0FF9C0+(i*0x100)) for i in range(3)]
world_tiles = [make_worldmap_blocks(ROM_jp, 0x0FF0C0+(i*0x300), 0x1B8000+(i*0x2000), 0x0FF9C0+(i*0x100)) for i in range(3)]
worldpixmaps = [make_worldmap_pixmap(ROM_jp, i, 0x0FFCC0+(t*0x100), world_tiles[t]) for i, t in enumerate([0, 1, 0, 2, 2])]
world_tiles_pixmaps = []
world_blocks_pixmaps = []
for i, tiles in enumerate(world_tiles):
a = []
for t in tiles:
t.setColorTable(worldmap_palettes[i])
a.append(QPixmap.fromImage(t))
world_tiles_pixmaps.append(a)
world_blocks_pixmaps.append(a)
perfcount()
worldmap_subtiles = make_worldmap_subtiles_pixmap(ROM_jp, 0x1B8000, 0x0FF9C0, 0x0FFCC0)
worldmap_subtiles += make_worldmap_subtiles_pixmap(ROM_jp, 0x1BA000, 0x0FFAC0, 0x0FFDC0)
worldmap_subtiles += make_worldmap_subtiles_pixmap(ROM_jp, 0x1BC000, 0x0FFBC0, 0x0FFEC0, length=128)
worldmap_tiles = make_worldmap_tiles_pixmap(ROM_jp, 0x1B8000, 0x0FF9C0, 0x0FFCC0)
worldmap_tiles += make_worldmap_tiles_pixmap(ROM_jp, 0x1BA000, 0x0FFAC0, 0x0FFDC0)
worldmap_tiles += make_worldmap_tiles_pixmap(ROM_jp, 0x1BC000, 0x0FFBC0, 0x0FFEC0, length=128)
perfcount()
field_tiles = make_all_field_tiles(ROM_jp)
field_minitiles = make_all_field_minitiles(ROM_jp)
perfcount()
@ -245,6 +248,19 @@ class FF5Reader(QMainWindow):
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 field map blocks')
zones = [parse_zone(ROM_jp, i) for i in range(const.zone_count)]
field_blocksets = [get_field_map_block_layouts(ROM_jp, i) for i in range(28)]
perfcount()
blockmaps = get_blockmaps(ROM_jp)
field_blocks = []
zone_pxs = []
for z in zones:
blocks = make_field_map_blocks_px(ROM_jp, z.blockset, field_tiles, field_minitiles, field_blocksets)
field_blocks.append(stitch_tileset_px(blocks))
zone_pxs += make_zone_pxs(blocks, [blockmaps[b] for b in z.blockmaps if b!=-1])
perfcount()
print('Generating Battle backgrounds')
battle_bgs = make_battle_backgrounds(ROM_jp)
perfcount()
@ -275,31 +291,36 @@ class FF5Reader(QMainWindow):
strings_tab = QTabWidget()
structs_tab = QTabWidget()
sprites_tab = QTabWidget()
backgrounds_tab = QTabWidget()
self.ff5widget.addTab(strings_tab, 'Strings')
self.ff5widget.addTab(structs_tab, 'Structs')
self.ff5widget.addTab(sprites_tab, 'Images')
self.ff5widget.addTab(backgrounds_tab, 'Backgrounds')
sprites_tab.addTab(make_pixmap_table(self.glyph_sprites['glyphs_en_s'], scale=4), 'Glyphs (EN)')
sprites_tab.addTab(make_pixmap_table(self.glyph_sprites['glyphs_en_l'], scale=2), 'Glyphs (Dialogue EN)')
sprites_tab.addTab(make_pixmap_table(self.glyph_sprites['glyphs_jp_s'], scale=4), 'Glyphs (JP)')
sprites_tab.addTab(make_pixmap_table(self.glyph_sprites['glyphs_jp_l'], scale=2), 'Glyphs (Large JP)')
sprites_tab.addTab(make_pixmap_table(self.glyph_sprites['glyphs_kanji'], scale=2),'Glyphs (Kanji)')
sprites_tab.addTab(make_pixmap_table(worldmap_subtiles, cols=16, scale=4), 'Worldmap Subtiles')
sprites_tab.addTab(make_pixmap_table(world_tiles_pixmaps[0], cols=16, scale=4), 'World 1 Tiles')
sprites_tab.addTab(make_pixmap_table(world_tiles_pixmaps[1], cols=16, scale=4), 'World 2 Tiles')
sprites_tab.addTab(make_pixmap_table(world_tiles_pixmaps[2], cols=16, scale=4), 'Underwater Tiles')
sprites_tab.addTab(make_pixmap_table(worldpixmaps, cols=1, scale=1, large=True), 'Worldmaps')
sprites_tab.addTab(make_pixmap_table(fieldmap_tiles, cols=8, scale=2), 'Fieldmap Tiles')
sprites_tab.addTab(make_pixmap_table(battle_bgs, cols=8, scale=1), 'Battle BGs')
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(enemy_sprites_named, cols=32, scale=1), 'Enemy Sprites')
sprites_tab.addTab(make_px_table(self.glyph_sprites['glyphs_en_s'], scale=4), 'Glyphs (EN)')
sprites_tab.addTab(make_px_table(self.glyph_sprites['glyphs_en_l'], scale=2), 'Glyphs (Dialogue EN)')
sprites_tab.addTab(make_px_table(self.glyph_sprites['glyphs_jp_s'], scale=4), 'Glyphs (JP)')
sprites_tab.addTab(make_px_table(self.glyph_sprites['glyphs_jp_l'], scale=2), 'Glyphs (Large JP)')
sprites_tab.addTab(make_px_table(self.glyph_sprites['glyphs_kanji'], scale=2),'Glyphs (Kanji)')
sprites_tab.addTab(make_px_table(self.battle_strips, cols=22, scale=2), 'Character Battle Sprites')
sprites_tab.addTab(make_px_table(status_strips, cols=22, scale=2), 'Status Sprites')
sprites_tab.addTab(make_px_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.portraits_ff4, cols=14, scale=2), 'Character Portraits')
self.ff4widget.addTab(make_pixmap_table(self.field_strips_ff4, cols=17, scale=2), 'Character Field Sprites')
self.ff6widget.addTab(make_pixmap_table(self.battle_strips_ff6, cols=32, scale=2), 'Character Sprites')
self.ff6widget.addTab(make_pixmap_table(self.portraits_ff6, cols=19, scale=2), 'Character Portraits')
backgrounds_tab.addTab(make_px_table(worldmap_tiles, cols=16, scale=4), 'Worldmap Tiles')
backgrounds_tab.addTab(make_px_table(world_blocks_pixmaps[0], cols=16, scale=4), 'World 1 Blocks')
backgrounds_tab.addTab(make_px_table(world_blocks_pixmaps[1], cols=16, scale=4), 'World 2 Blocks')
backgrounds_tab.addTab(make_px_table(world_blocks_pixmaps[2], cols=16, scale=4), 'Underwater Blocks')
backgrounds_tab.addTab(make_px_table(worldpixmaps, cols=1, scale=1, large=True), 'Worldmaps')
backgrounds_tab.addTab(make_px_table(fieldmap_tiles, cols=8, scale=1), 'Fieldmap Tiles')
backgrounds_tab.addTab(make_px_table(field_blocks, cols=16, scale=1), 'Field Blocks')
backgrounds_tab.addTab(make_px_table(zone_pxs, cols=4, scale=1, large=1), 'Zone')
backgrounds_tab.addTab(make_px_table(battle_bgs, cols=8, scale=1), 'Battle BGs')
self.ff4widget.addTab(make_px_table(self.battle_strips_ff4, cols=16, scale=2), 'Character Battle Sprites')
self.ff4widget.addTab(make_px_table(self.portraits_ff4, cols=14, scale=2), 'Character Portraits')
self.ff4widget.addTab(make_px_table(self.field_strips_ff4, cols=17, scale=2), 'Character Field Sprites')
self.ff6widget.addTab(make_px_table(self.battle_strips_ff6, cols=32, scale=2), 'Character Sprites')
self.ff6widget.addTab(make_px_table(self.portraits_ff6, cols=19, scale=2), 'Character Portraits')
structs_tab.addTab(make_table(zone_headers, zone_data, True), 'Zones')

View File

@ -29,7 +29,7 @@ def hex_length(i):
'''
return divceil(i.bit_length(), 4)
def hex(num, digits):
def hex(num, digits=2):
'''
Consolidate hex formatting for consistency
'''
@ -60,12 +60,16 @@ def parse_struct(rom, offset, structure):
return out
def decompress_lzss(rom, start, header=False):
def decompress_lzss(rom, start, header=False, length=None):
'''
Algorithm from http://slickproductions.org/slickwiki/index.php/Noisecross:Final_Fantasy_V_Compression
'''
ptr = start
if length:
uncompressed_length = length
else:
uncompressed_length = indirect(rom, start)
ptr = start+2
ptr += 2
output = []
buffer = [0 for i in range(0x800)]
buffer_p = 0x07DE

View File

@ -156,7 +156,7 @@ def make_table(headers, items, sortable=False, row_labels=True, scale=2):
return table
def make_pixmap_table(items, cols=16, scale=4, large=False):
def make_px_table(items, cols=16, scale=4, large=False):
rows = divceil(len(items), cols)
rd = hex_length(rows-1)+1
cd = hex_length(cols-1)

View File

@ -20,6 +20,29 @@ Functions common to SNES FFs
from includes.helpers import *
from includes.snestile import *
from collections import namedtuple
zone = namedtuple('zone', 'npcs name shadowflags blockset tilesets blockmaps palette music')
def parse_zone(rom, id, start_address=0x0E9C00):
ptr = start_address+(id*0x1A)
npcs = indirect(rom, ptr)
name = rom[ptr+2]
shadowflags = rom[ptr+3]
blockset = rom[ptr+8]
tilesets_b = indirect(rom, ptr+9, length=3)
tilesets = [(tilesets_b & 0x00003F),
(tilesets_b & 0x000FC0) >> 6,
(tilesets_b & 0x03F000) >> 12,
(tilesets_b & 0xFC0000) >> 18]
pal_address = 0x03BB00 + (indirect(rom, ptr+0x16)<<8)
blockmaps_b = indirect(rom, ptr+0xC, length=4)
blockmaps = [(blockmaps_b & 0x000003FF) - 1,
((blockmaps_b & 0x000FFC00) >> 10) - 1,
((blockmaps_b & 0x3FF00000) >> 20) - 1]
palettes = [generate_palette(rom, pal_address+i*32, transparent=True) for i in range(8)]
music = rom[ptr+0x19]
return zone(npcs, name, shadowflags, blockset, tilesets, blockmaps, palettes, music)
def make_battle_strip(rom, palette_address, tile_address, num_tiles, bpp=4):
@ -96,27 +119,27 @@ def make_character_status_sprites(rom):
return pixmaps
def make_worldmap_subtiles(rom, tiles_address, lut_address, length=0x100):
def make_worldmap_tiles(rom, tiles_address, lut_address, length=0x100):
subtiles = []
for i in range(length):
pal_index = rom[lut_address+i]//16
subtiles.append(create_tile_mode7_compressed_indexed(rom[tiles_address+i*32:tiles_address+i*32+32], pal_index))
return subtiles
def stitch_worldmap_tiles(rom, subtiles, offset=0x0FF0C0):
tiles = []
def stitch_worldmap_tiles(rom, tiles, offset=0x0FF0C0):
blocks = []
for i in range(0xC0):
canvas = Canvas_Indexed(2, 2)
for j in range(4):
k = indirect(rom, offset+(j*0xC0)+i, length=1)
canvas.draw_tile(j%2, j//2, subtiles[k])
tiles.append(canvas.image)
return tiles
canvas.draw_tile(j%2, j//2, tiles[k])
blocks.append(canvas.image)
return blocks
def make_worldmap_tiles(rom, tiles_address, subtiles_address, lut_address, length=0x100):
return stitch_worldmap_tiles(rom, make_worldmap_subtiles(rom, subtiles_address, lut_address, length=length), tiles_address)
def make_worldmap_blocks(rom, blocks_address, tiles_address, lut_address, length=0x100):
return stitch_worldmap_tiles(rom, make_worldmap_tiles(rom, tiles_address, lut_address, length=length), blocks_address)
def make_worldmap_subtiles_pixmap(rom, tiles_address, lut_address, palette_address, length=0x100):
def make_worldmap_tiles_pixmap(rom, tiles_address, lut_address, palette_address, length=0x100):
tiles = []
palettes = [generate_palette(rom, palette_address+i*32, transparent=True) for i in range(16)]
for i in range(length):
@ -182,6 +205,12 @@ def stitch_tileset(tiles):
canvas.draw_tile(i%16, i//16, tile)
return canvas
def stitch_tileset_px(tiles_px):
canvas = Canvas(16, len(tiles_px)//16)
for i, tile in enumerate(tiles_px):
canvas.draw_pixmap(i%16, i//16, tile)
return canvas.pixmap()
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.
@ -214,6 +243,46 @@ def make_field_map_tile_pixmap(rom, id, st_tiles, st_minitiles):
canvas.draw_pixmap(0, 48, st_minitiles[minitile].pixmap(p))
return canvas.pixmap()
def get_field_map_block_layouts(rom, id, start_address=0x0F0000):
ptr = indirect(rom, start_address+(id*2)) + start_address
data = decompress_lzss(rom, ptr)
output = []
for i in range(0, 0x200, 2):
output.append([data[j+i+k] for j in range(0, 0x800, 0x200) for k in range(2)])
#for i in range(0, 0x800, 8):
#output.append([data[i+k] for k in range(8)])
return output
def make_field_map_blocks_px(rom, id, tilesets, minitilesets, blockmaps):
*i_tiles, i_minitiles, palettes = get_field_map_tiles(rom, id)
tiles = tilesets[i_tiles[0]] + tilesets[i_tiles[1]] + tilesets[i_tiles[2]]
tiles += minitilesets[i_minitiles]
i_blockmap = rom[0x0E9C08 + (id * 0x1A)]
blockmap = blockmaps[i_blockmap]
blocks = [make_tilemap_canvas(tm, tiles, cols=2, rows=2, pal_adjust=0, tile_modulo=0x1000) for tm in blockmap]
return [b.pixmap(palettes) for b in blocks]
def get_blockmaps(rom, start_address=0x0B0000, num=0x148):
bank = 0x0B0000
ptrs = [indirect(rom, start_address)+bank]
for i in range(1, num):
ptr = indirect(rom, start_address+(i*2))
if (ptr+bank) < ptrs[-1]:
bank += 0x010000
ptrs.append(ptr+bank)
blockmaps = [decompress_lzss(rom, ptr) for ptr in ptrs]
return blockmaps
def make_zone_pxs(blocks, blockmaps):
output = []
for bm in blockmaps:
canvas = Canvas(64, 64, tilesize=16)
for i, b in enumerate(bm):
canvas.draw_pixmap(i%64, i//64, blocks[b])
output.append(canvas.pixmap())
return output
def decompress_battle_tilemap(rom, address):
'''
Decompresses the tilemap for a battle background.
@ -248,14 +317,13 @@ def decompress_battle_tilemap(rom, address):
output[1::2] = [i&0xDF for i in o2[:length//2]]
return bytes(output)
def apply_battle_tilemap_flips(rom, id, battle_terrain):
def apply_battle_tilemap_flips(rom, id, tilemap):
if id==0xFF:
return battle_terrain
return tilemap
ptr = indirect(rom, 0x14C736+(id*2))+0x140000
length = len(battle_terrain)//2
output = list(battle_terrain)
length = len(tilemap)//2
output = list(tilemap)
buffer = []
while len(buffer) < length:
a = rom[ptr]
ptr += 1
@ -267,49 +335,54 @@ def apply_battle_tilemap_flips(rom, id, battle_terrain):
for b in reversed(range(0, 8, 1)):
buffer.append((a>>b)&0x01)
for i in range(len(battle_terrain)//2):
for i in range(len(tilemap)//2):
output[i*2+1] |= (buffer[i] << 6)
return bytes(output)
def make_tilemap_canvas(tilemap, tiles, tile_adjust=0, pal_adjust=-1):
def parse_tileset_word(data):
a, b = data[:2]
tile_index = a|((b & 0x03) << 8)
palette = (b & 0x1C) >> 2
priority = (b & 0x20) >> 5
h_flip = (b & 0x40) >> 6
v_flip = (b & 0x80) >> 7
return tile_index, palette, h_flip, v_flip, priority
def make_tilemap_canvas(tilemap, tiles, cols=64, rows=64, tile_adjust=0, pal_adjust=-1, tile_modulo=0x80):
'''
Battle bg is 64x64 map size, 8x8 tile size
4bpp tiles
'''
canvas = Canvas_Indexed(64, 64)
canvas = Canvas_Indexed(cols, rows)
for i in range(len(tilemap)//2):
a, b = tilemap[i*2:(i+1)*2]
tile_index = a|((b & 0x02) << 8)
p = (b & 0x1C) >> 2
priority = (b & 0x20) >> 5
h_flip = (b & 0x40) >> 6
v_flip = (b & 0x80) >> 7
tile_index, p, h_flip, v_flip, priority = parse_tileset_word(tilemap[i*2:(i+1)*2])
if cols > 32:
x = (i % 32) + 32*((i//1024) % 2)
y = (i //32) - 32*((i//1024) % 2)
else:
x = i % cols
y = i //cols
try:
tile = tiles[(tile_index+tile_adjust)%0x80]
tile = tiles[(tile_index+tile_adjust)%tile_modulo]
canvas.draw_tile(x, y, tile, h_flip, v_flip, p+pal_adjust)
except BaseException as e:
print(e, p, hex(tile_index,2), hex(tile_adjust,2), hex(tile_index+tile_adjust,2))
return canvas
def make_tilemap_pixmap(tilemap, tiles, palettes, tile_adjust=0, pal_adjust=-1):
def make_tilemap_pixmap(tilemap, tiles, palettes, cols=64, rows=64, tile_adjust=0, pal_adjust=-1):
'''
Battle bg is 64x64 map size, 8x8 tile size
4bpp tiles
'''
canvas = Canvas(64, 64)
canvas = Canvas(cols, rows)
for i in range(len(tilemap)//2):
a, b = tilemap[i*2:(i+1)*2]
tile_index = a|((b & 0x02) << 8)
p = (b & 0x1C) >> 2
priority = (b & 0x20) >> 5
h_flip = (b & 0x40) >> 6
v_flip = (b & 0x80) >> 7
tile_index, p, h_flip, v_flip, priority = parse_tileset_word(tilemap[i*2:(i+1)*2])
if cols > 32:
x = (i % 32) + 32*((i//1024) % 2)
y = (i //32) - 32*((i//1024) % 2)
else:
x = i % cols
y = i //cols
try:
palette = palettes[p+pal_adjust]
tile = tiles[(tile_index+tile_adjust)%0x80]
@ -366,7 +439,7 @@ def make_battle_backgrounds(rom):
a.append(b)
a = [(i, j) for i, j in zip(a[0::2], a[1::2])]
animations.append(a)
animation_time = 15 # Frames before changing
animation_time = 8 # Frames before changing
pal_cycle_ptr_start = 0x14C6CD
pal_cycle_ptrs = [indirect(rom, pal_cycle_ptr_start+(i*2))+0x140000 for i in range(3)]

View File

@ -213,12 +213,13 @@ def generate_palette(rom, offset, length=32, transparent=False):
class Canvas:
def __init__(self, cols, rows, color=bg_trans):
self.image = QImage(8*cols, 8*rows, QImage.Format_ARGB32_Premultiplied)
def __init__(self, cols, rows, color=bg_trans, tilesize=8):
self.image = QImage(tilesize*cols, tilesize*rows, QImage.Format_ARGB32_Premultiplied)
self.tilesize = tilesize
self.image.fill(color)
self.painter = QtGui.QPainter(self.image)
self.max_x = 1
self.max_y = 1
self.max_col = 1
self.max_row = 1
def __del__(self):
del self.painter
@ -226,19 +227,19 @@ class Canvas:
def draw_pixmap(self, col, row, pixmap, h_flip=False, v_flip=False):
h_s = -1 if h_flip else 1
v_s = -1 if v_flip else 1
x = (col+h_flip)*8*h_s
y = (row+v_flip)*8*v_s
x = (col+h_flip)*self.tilesize*h_s
y = (row+v_flip)*self.tilesize*v_s
self.painter.scale(h_s, v_s)
self.painter.drawPixmap(x, y, pixmap)
self.painter.scale(h_s, v_s) # Invert it again to restore it to normal
if col > self.max_x:
self.max_x = col
if row > self.max_y:
self.max_y = row
if col > self.max_col:
self.max_col = col
if row > self.max_row:
self.max_row = 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.copy(0, 0, (self.max_col+1)*self.tilesize, (self.max_row+1)*self.tilesize))
return QPixmap.fromImage(self.image)
@ -270,6 +271,8 @@ class Canvas_Indexed:
self.max_row = max(row, self.max_row)
def pixmap(self, palette, trim=False):
if isinstance(palette[0], list):
palette = [i for p in palette for i in p] # Flatten
if trim:
img = self.image.copy(0, 0, (self.max_col+1)*self.tilesize, (self.max_row+1)*self.tilesize)
img.setColorTable(palette)