def create_output(self, mem, args, traversal, platform): config = ppu_memory.PpuMemoryConfig( chr_order=args.order, traversal=traversal, platform=platform, is_sprite=args.is_sprite, is_locked_tiles=args.is_locked_tiles, lock_sprite_flips=args.lock_sprite_flips, select_chr_plane=args.select_chr_plane) if args.vertical_pixel_display: mem.chr_set.vertical_pixel_display() if args.output == '/dev/null': # Ignore output. pass elif args.output and args.output.endswith('.o'): # Output as a valiant object file. mem.save_valiant(args.output, config) elif args.output and args.output.endswith('.png'): # Render an image. renderer = pixel_art_renderer.PixelArtRenderer() img = renderer.render(mem) img.save(args.output) else: # Output as multiple files using a template. out_tmpl = args.output or '%s.dat' if out_tmpl[-1] == '/' or os.path.isdir(out_tmpl): out_tmpl = os.path.join(out_tmpl, '%s.dat') if not '%s' in out_tmpl: raise errors.CommandLineArgError( 'output needs "%s" in its template') mem.save_template(out_tmpl, config) if args.compile: # Compile a runnable ROM. builder = rom_builder.RomBuilder() builder.build(mem, args.compile)
def create_output(self, mem, args, traversal): config = ppu_memory.PpuMemoryConfig( chr_order=args.order, traversal=traversal, is_sprite=args.is_sprite, is_locked_tiles=args.is_locked_tiles, lock_sprite_flips=args.lock_sprite_flips) if args.output == '/dev/null': pass elif args.output and args.output.endswith('.o'): mem.save_valiant(args.output, config) elif args.output and args.output.endswith('.png'): renderer = pixel_art_renderer.PixelArtRenderer() img = renderer.render(mem) img.save(args.output) else: out_tmpl = args.output or '%s.dat' if out_tmpl[-1] == '/' or os.path.isdir(out_tmpl): out_tmpl = os.path.join(out_tmpl, '%s.dat') if not '%s' in out_tmpl: raise errors.CommandLineArgError( 'output needs "%s" in its template') mem.save_template(out_tmpl, config) if args.compile: builder = rom_builder.RomBuilder() builder.build(mem, args.compile)
def store_vert_pair(self, palette_option, cid_u, did_u, cid_l, did_l, config): """Build vertical tile pair, and either retrieve from cache or add chr data. palette_option: Chosen palette option for creating the chr data. cid_u: Color needs id for upper tile. did_u: Dot profile id for upper tile. cid_l: Color needs id for lower tile. did_l: Dot profile id for lower tile. config: Configuration. """ # Create upper tile. force = ppu_memory.PpuMemoryConfig(is_sprite=config.is_sprite, is_locked_tiles=True) color_needs = self._color_manifest.at(cid_u) xlat_u = self.get_dot_xlat(color_needs, palette_option) tile_u = self.build_tile(xlat_u, did_u) # Create lower tile. color_needs = self._color_manifest.at(cid_l) xlat_l = self.get_dot_xlat(color_needs, palette_option) tile_l = self.build_tile(xlat_l, did_l) # Check if the cache contains this key. vert = chr_data.VertTilePair(tile_u, tile_l) key = str(vert) if key in self._chrdata_cache and not config.is_locked_tiles: (chr_num_u, chr_num_l, flip_bits) = self._chrdata_cache[key] else: # Otherwise, force both tiles to be created. (chr_num_u, flip_bits) = self.store_chrdata(xlat_u, did_u, force) (chr_num_l, flip_bits) = self.store_chrdata(xlat_l, did_l, force) self._chrdata_cache[key] = (chr_num_u, chr_num_l, flip_bits) if not config.lock_sprite_flips: self.assign_tile_flips(vert, [chr_num_u, chr_num_l], self._chrdata_cache) return chr_num_u, chr_num_l, flip_bits
def BuildConfigFromOptions(self): is_locked_tiles = self.lockedTilesCheckBox.GetValue() is_sprite = self.spriteModeCheckBox.GetValue() allow_s = 's' if self.allowSpriteOverflowCheckBox.GetValue() else '' traversal = self.traversalChoices[ self.traversalComboBox.GetCurrentSelection()] allow_c = 'c' if self.allowChrOverflowCheckBox.GetValue() else '' config = ppu_memory.PpuMemoryConfig(traversal=traversal, is_sprite=is_sprite, is_locked_tiles=is_locked_tiles, allow_overflow=[allow_s, allow_c]) return config
def process_image(self, img, palette_text, bg_color_mask, bg_color_fill, 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) 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. zones = self._find_zones(bg_color_fill) # HACK: Assign zones to ppu_memory so that they can be used by the # view renderer. self._ppu_memory.zones = zones # 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. pal = self.make_palette(palette_text, 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, 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])
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
def process_image(self, img, palette_text, bg_color_mask, bg_color_fill, traversal, is_sprite, is_locked_tiles, lock_sprite_flips, allow_overflow): """Process an image, creating the ppu_memory necessary to display it. img: Pixel art image. palette_text: Optional string representing a palette to be parsed. bg_color_mask: Background color mask, if a mask is begin used. bg_color_fill: Background color fill. traversal: Strategy for traversing the nametable. is_sprite: Whether the image is of sprites. is_locked_tiles: Whether tiles are locked into place. If so, do not merge duplicates, and only handle first 256 tiles. lock_sprite_flips: Whether to only lock sprite flip bits. allow_overflow: Characters representing components. Only 's' is supported. """ self.initialize() self.load_image(img) self.blocks_y = NUM_BLOCKS_Y self.blocks_x = NUM_BLOCKS_X # Assign configuration. config = ppu_memory.PpuMemoryConfig( is_sprite=is_sprite, is_locked_tiles=is_locked_tiles, lock_sprite_flips=lock_sprite_flips, allow_overflow=allow_overflow) if 'c' in config.allow_overflow: self._ppu_memory.upgrade_chr_set_to_bank() # TODO: Not being used anywhere. self._ppu_memory.nt_width = NUM_BLOCKS_X * 2 # If image is exactly 128x128 and uses locked tiles, treat it as though it # represents CHR memory. if self.image_x == self.image_y == SMALL_SQUARE and config.is_locked_tiles: self.blocks_y = NUM_BLOCKS_SMALL_SQUARE self.blocks_x = NUM_BLOCKS_SMALL_SQUARE self._ppu_memory.nt_width = NUM_BLOCKS_SMALL_SQUARE * 2 # In order to auto detect the background color, have to count color needs. if config.is_sprite and bg_color_fill is None: self._color_manifest = id_manifest.CountingIdManifest() # Process each block and tile to build artifacts. self.process_to_artifacts(bg_color_mask, bg_color_fill, config) if self._err.has(): return # Make the palette, and store it. pal = self.make_palette(palette_text, bg_color_fill, config.is_sprite) if not pal: return if not config.is_sprite: self._ppu_memory.palette_nt = pal else: self._ppu_memory.palette_spr = pal # Replace mask with fill. self.replace_mask_with_fill(bg_color_mask, bg_color_fill) # Make colorization for each block and tile. self.make_colorization(pal, config) if self._err.has(): return # Traverse the artifacts, building chr and other ppu_memory. self.traverse_artifacts(traversal, pal, config) if self._err.has(): return # Build spritelist if necessary. if config.is_sprite: self.make_spritelist(traversal, pal, config)
def process_image(self, img, palette_text, bg_color_mask, bg_color_fill, traversal, is_sprite, is_locked_tiles, lock_sprite_flips, allow_overflow): """Process an image, creating the ppu_memory necessary to display it. img: Pixel art image. palette_text: Optional string representing a palette to be parsed. bg_color_mask: Background color mask, if a mask is being used. bg_color_fill: Background color fill. traversal: Strategy for traversing the nametable. is_sprite: Whether the image is of sprites. is_locked_tiles: Whether tiles are locked into place. If so, do not merge duplicates, and only handle first 256 tiles. lock_sprite_flips: Whether to only lock sprite flip bits. allow_overflow: Characters representing components. Only 'c' and 's' are supported. """ self.initialize() self.load_image(img) # Assign configuration. config = ppu_memory.PpuMemoryConfig( is_sprite=is_sprite, is_locked_tiles=is_locked_tiles, lock_sprite_flips=lock_sprite_flips, allow_overflow=allow_overflow) if 'c' in config.allow_overflow: self._ppu_memory.upgrade_chr_set_to_bank() # Parse the palette if provided. pal = None if palette_text or self.img.palette: pal = self.parse_palette(palette_text, bg_color_fill) # In order to auto detect the background color, have to count color needs. # Counting is slower, so don't do it by default. if config.is_sprite and bg_color_fill is None: self._color_manifest = id_manifest.CountingIdManifest() # Process each block and tile to build artifacts. self.process_to_artifacts(bg_color_mask, bg_color_fill, config) if self._err.has(): return # Make the palette, if it doesn't already exist. if not pal: pal = self.make_palette(bg_color_fill, config.is_sprite) if not pal: self.maybe_find_palette_subset_errors() return if not config.is_sprite: self._ppu_memory.palette_nt = pal else: self._ppu_memory.palette_spr = pal # Replace mask with fill. self.replace_mask_with_fill(bg_color_mask, bg_color_fill) # Make colorization for each block and tile. self.make_colorization(pal, config) if self._err.has(): return # Traverse the artifacts, building chr and other ppu_memory. self.traverse_artifacts(traversal, pal, config) if self._err.has(): return # Build spritelist if necessary. if config.is_sprite: self.make_spritelist(traversal, pal, config)