def make_spritelist(self, traversal, pal, config): """Convert data from the nametable to create spritelist. traversal: Method of traversal """ empty_did = self._dot_manifest.get(chr(0) * 64) empty_cid = self._color_manifest.get(chr(pal.bg_color) + chr(NULL) * 3) # TODO: Only set this to 1 if the sprite chr order is 1. tile_low_bit = 1 for (y, x) in self.get_generator(traversal): (cid_u, did_u, bcid_u) = self._artifacts[y][x] (cid_l, did_l, bcid_l) = self._artifacts[y + 1][x] if (empty_cid == cid_u and empty_did == did_u and empty_cid == cid_l and empty_did == did_l): continue tile = self._ppu_memory.gfx_0.nametable[y][x] if not 's' in config.allow_overflow: if len(self._ppu_memory.spritelist) == 0x40: if not config.is_locked_tiles: self._err.add(errors.SpritelistOverflow(y, x)) continue y_pos = y * 8 - 1 if y > 0 else 0 x_pos = x * 8 attr = self._ppu_memory.gfx_0.colorization[y][x] | self._flip_bits[ y][x] self._ppu_memory.spritelist.append( [y_pos, tile + tile_low_bit, attr, x_pos])
def make_spritelist(self, traversal, pal, config): """Convert data from the nametable to create spritelist. traversal: Method of traversal """ empty_did = self._dot_manifest.get(bytes(bytearray([0] * 64))) empty_cid = self._color_manifest.get( bytes(bytearray([pal.bg_color] + [NULL] * 3))) generator = ((y, x) for y in range(self.blocks_y * 2) for x in range(self.blocks_x * 2)) for (y, x) in generator: (cid, did, bcid) = self._artifacts[y][x] if empty_cid == cid and empty_did == did: continue tile = self._ppu_memory.gfx[0].nametable[y][x] if not config.allow_overflow or not 's' in config.allow_overflow: if len(self._ppu_memory.spritelist) >= 0x40: if not config.is_locked_tiles: self._err.add(errors.SpritelistOverflow(y, x)) continue y_pos = y * 8 - 1 if y > 0 else 0 x_pos = x * 8 attr = self._ppu_memory.gfx[0].colorization[y][ x] | self._flip_bits[y][x] self._ppu_memory.spritelist.append([y_pos, tile, attr, x_pos])
for i in xrange(0, len(artifacts), 2): (cid_u, did_u, unused, y, x) = artifacts[i] (cid_l, did_l, vcid, unused_y, unused_x) = artifacts[i + 1] color_needs = self._vert_color_manifest.at(vcid) (pid, palette_option) = pal.select(color_needs) chr_num_u, chr_num_l, flip_bits = ebs_processor.store_vert_pair( palette_option, cid_u, did_u, cid_l, did_l, config) if (config.is_locked_tiles and self._ppu_memory.chr_set.is_full() and chr_num_u == 0 and chr_num_l == 0): raise errors.ChrPageFull() # TODO: Only add this 1 if the sprite chr order is 1. chr_num = chr_num_u + 1 if not 's' in config.allow_overflow: if len(self._ppu_memory.spritelist) >= 0x40: self._err.add(errors.SpritelistOverflow(y, x)) continue self._ppu_memory.spritelist.append( [y - 1, chr_num, pid | flip_bits, x]) self._ppu_memory.palette_spr = pal def _find_zones(self, fill): """Scan the entire image. Calculate the positions of tile corners.""" if self._verbose: print('') self._regions = [] zones = [] # For each line of the image, starting from the top. for y in xrange(self.image_y): is_color = False x = 0
def process_image(self, img, palette_text, bg_color_mask, bg_color_fill, platform, is_locked_tiles, lock_sprite_flips, allow_overflow): """Process free sprites image, creating the ppu_memory it represents. The image represents the entire screen, and is mostly filled with bg_color_fill. Anywhere that contains other pixel data is considered to be made of sprites. Those sprites are located, treated as tiles, and processed to be converted into PPU memory. img: Pixel art image. palette_text: Optional string representing a palette to be parsed. bg_color_mask: Background color that masks existing free sprite tiles. bg_color_fill: Background color which fills up the outside space. is_locked_tiles: Whether tiles are locked or not. allow_overflow: List of components for which to allow overflows. """ self.initialize() self.load_image(img) self.set_platform(platform) config = ppu_memory.PpuMemoryConfig(is_sprite=True, is_locked_tiles=is_locked_tiles, lock_sprite_flips=lock_sprite_flips, allow_overflow=allow_overflow) is_tall = '8x16' in self.traversal # Scan the image, find corners of each tile based upon region merging. try: zones = self._find_zones(bg_color_fill) except errors.CouldntConvertRGB as e: self._err.add(e) return None # HACK: Assign zones to ppu_memory so that they can be used by the # view renderer. self._ppu_memory.zones = zones # Parse the palette if provided. pal = None if palette_text or self.img.palette: pal = self.parse_palette(palette_text, bg_color_mask) # Convert zones into artifacts. artifacts = [] for z in zones: vert_color_needs = None for sprite_y, sprite_x in z.each_sprite(is_tall): (color_needs, dot_profile) = self.process_tile( sprite_y // 8, sprite_x // 8, sprite_y % 8, sprite_x % 8) cid = self._color_manifest.id(color_needs) did = self._dot_manifest.id(dot_profile) artifacts.append([cid, did, None, sprite_y, sprite_x]) if not is_tall: continue elif vert_color_needs is None: vert_color_needs = color_needs else: try: self.combine_color_needs(vert_color_needs, color_needs) except errors.PaletteOverflowError as e: e.tile_y = sprite_y // 8 e.tile_x = sprite_x // 8 self._err.add(e) continue vcid = self._vert_color_manifest.id(vert_color_needs) artifacts[-1][ARTIFACT_VCID] = vcid vert_color_needs = None if self._err.has(): return self._needs_provider = self._color_manifest if is_tall: self._needs_provider = self._vert_color_manifest # Build the palette. if not pal: pal = self.make_palette(bg_color_mask, True) # Build the PPU memory. if not is_tall: for cid, did, unused, y, x in artifacts: color_needs = self._color_manifest.at(cid) (pid, palette_option) = pal.select(color_needs) dot_xlat = self.get_dot_xlat(color_needs, palette_option) try: (chr_num, flip_bits) = self.store_chrdata(dot_xlat, did, config) except errors.NametableOverflow as e: self._err.add(errors.NametableOverflow(e.chr_num, y, x)) chr_num = 0 if (config.is_locked_tiles and self._ppu_memory.chr_set.is_full() and chr_num == 0): raise errors.ChrPageFull() self._ppu_memory.spritelist.append([y - 1, chr_num, pid | flip_bits, x]) else: ebs_processor = eight_by_sixteen_processor.EightBySixteenProcessor() ebs_processor.link_from(self) for i in range(0, len(artifacts), 2): (cid_u, did_u, unused, y, x) = artifacts[i] (cid_l, did_l, vcid, unused_y, unused_x) = artifacts[i+1] color_needs = self._vert_color_manifest.at(vcid) (pid, palette_option) = pal.select(color_needs) chr_num_u, chr_num_l, flip_bits = ebs_processor.store_vert_pair( palette_option, cid_u, did_u, cid_l, did_l, config) if (config.is_locked_tiles and self._ppu_memory.chr_set.is_full() and chr_num_u == 0 and chr_num_l == 0): raise errors.ChrPageFull() # TODO: Only add this 1 if the sprite chr order is 1. chr_num = chr_num_u + 1 if not 's' in config.allow_overflow: if len(self._ppu_memory.spritelist) >= 0x40: self._err.add(errors.SpritelistOverflow(y, x)) continue self._ppu_memory.spritelist.append([y - 1, chr_num, pid | flip_bits, x]) self._ppu_memory.palette_spr = pal