diff options
Diffstat (limited to 'extras')
| -rw-r--r-- | extras/gfx.py | 304 | 
1 files changed, 297 insertions, 7 deletions
| diff --git a/extras/gfx.py b/extras/gfx.py index 79592d3ba..65eac7183 100644 --- a/extras/gfx.py +++ b/extras/gfx.py @@ -1,17 +1,14 @@  # -*- coding: utf-8 -*-  import os -import sys -import errno -import string -from copy import copy, deepcopy -import random +import png  import argparse  from math import sqrt, floor, ceil -from datetime import datetime  from crystal import load_rom +from pokemon_constants import pokemon_constants +  rom = load_rom() @@ -1078,9 +1075,290 @@ def grab_palettes(address, length = 0x80):  	return output + + + + + +def dump_monster_pals(): +	pals = 0xa8d6 +	pal_length = 0x4 +	for mon in range(251): +		 +		name     = pokemon_constants[mon+1].title().replace('_','') +		num      = str(mon+1).zfill(3) +		dir      = 'gfx/pics/'+num+'/' +		 +		address  = pals + mon*pal_length*2 +		 +		 +		pal_data = [] +		for byte in range(pal_length): +			pal_data.append(ord(rom[address])) +			address += 1 +		 +		filename = 'normal.pal' +		to_file('../'+dir+filename, pal_data) +		 +		spacing  = ' ' * (15 - len(name)) +		#print name+'Palette:'+spacing+' INCBIN "'+dir+filename+'"' +		 +		 +		pal_data = [] +		for byte in range(pal_length): +			pal_data.append(ord(rom[address])) +			address += 1 +		 +		filename = 'shiny.pal' +		to_file('../'+dir+filename, pal_data) +		 +		spacing  = ' ' * (10 - len(name)) +		#print name+'ShinyPalette:'+spacing+' INCBIN "'+dir+filename+'"' + + + +def flatten(planar): +	""" +	Flattens planar 2bpp image data into a quaternary pixel map. +	""" +	strips = [] +	for pair in range(len(planar)/2): +		bottom = ord(planar[(pair*2)  ]) +		top    = ord(planar[(pair*2)+1]) +		strip  = [] +		for i in range(7,-1,-1): +			color = ((bottom >> i) & 1) + (((top >> i-1) if i > 0 else (top << 1-i)) & 2) +			strip.append(color) +		strips += strip +	return strips + + +def to_lines(image, width): +	""" +	Converts a tiled quaternary pixel map to lines of quaternary pixels. +	""" +	 +	tile = 8 * 8 +	 +	# so we know how many strips of 8px we're putting into a line +	num_columns = width / 8 +	# number of lines +	height = len(image) / width +	 +	lines = [] +	for cur_line in range(height): +		tile_row = int(cur_line / 8) +		line = [] +		for column in range(num_columns): +			anchor = num_columns*tile_row*tile + column*tile + (cur_line%8)*8 +			line += image[anchor:anchor+8] +		lines.append(line) +	return lines + +def dmg2rgb(word): +	red = word & 0b11111 +	word >>= 5 +	green = word & 0b11111 +	word >>= 5 +	blue = word & 0b11111 +	alpha = 255 +	return ((red<<3)+0b100, (green<<3)+0b100, (blue<<3)+0b100, alpha) + + +def png_pal(filename): +	palette = [] +	palette.append((255,255,255,255)) +	with open(filename, 'rb') as pal_data: +		words = pal_data.read() +		dmg_pals = [] +		for word in range(len(words)/2): +			dmg_pals.append(ord(words[word*2]) + ord(words[word*2+1])*0x100) +	for word in dmg_pals: +		palette.append(dmg2rgb(word)) +	palette.append((000,000,000,255)) +	return palette + + +def to_png(filein, fileout=None, pal_file=None, height=None, width=None): +	""" +	Takes a planar 2bpp graphics file and converts it to png. +	""" +	 +	if fileout == None: fileout = ''.join(filein.split('.')[:-1]) + '.png' +	 +	image = open(filein, 'rb').read() +	 + +	# unless the pic is square, at least one dimension should be given +	 +	if height   == None and width == None: +		height = int(sqrt(len(image)*4)) +		width = height +	 +	elif height == None: height = len(image)*4 / width + +	elif width  == None: width = len(image)*4 / height +	 +	assert height * width == len(image)*4, 'Please specify dimensions for non-square image!' +	 +	 +	# map it out +	 +	lines = to_lines(flatten(image), width) +	 +	 +	if pal_file == None: +		palette   = None +		greyscale = True +		bitdepth  = 2 +		inverse   = { 0:3, 1:2, 2:1, 3:0 } +		map       = [[inverse[pixel] for pixel in line] for line in lines] +		 +	else: # gbc color +		palette   = png_pal(pal_file) +		greyscale = False +		bitdepth  = 8 +		map       = [[pixel for pixel in line] for line in lines] +	 +	 +	w = png.Writer(width, height, palette=palette, compression = 9, greyscale = greyscale, bitdepth = bitdepth) +	with open(fileout, 'wb') as file: +		w.write(file, map) + + + + +def to_2bpp(filein, fileout=None, palout=None): +	""" +	Takes a png and converts it to planar 2bpp. +	""" +	 +	if fileout == None: fileout = ''.join(filein.split('.')[:-1]) + '.2bpp' +	 +	with open(filein, 'rb') as file: + +		r = png.Reader(file)	 +		info  = r.asRGBA8() +		 +		width     = info[0] +		height    = info[1] +		 +		rgba      = list(info[2]) +		greyscale = info[3]['greyscale'] +	 +	 +	# commented out for the moment +	 +	padding = { 'left':   0, +	            'right':  0, +	            'top':    0, +	            'bottom': 0, } +	 +	#if width  % 8 != 0: +	#	padding['left']   =    int(ceil((width / 8 + 8 - width) / 2)) +	#	padding['right']  =   int(floor((width / 8 + 8 - width) / 2)) +	 +	#if height % 8 != 0: +	#	padding['top']    =  int(ceil((height / 8 + 8 - height) / 2)) +	#	padding['bottom'] = int(floor((height / 8 + 8 - height) / 2)) +	 +	 +	# turn the flat values into something more workable +	 +	pixel_length = 4 # rgba +	image   = [] +	 +	# while we're at it, let's size up the palette +	 +	palette = [] + +	for line in rgba: +		newline = [] +		for pixel in range(len(line)/pixel_length): +			i = pixel*pixel_length +			color = { 'r': line[i  ], +			          'g': line[i+1], +			          'b': line[i+2], +			          'a': line[i+3], } +			newline.append(color) +			if color not in palette: palette.append(color) +		image.append(newline) +	 +	 +	# sort by luminance, because we can +	 +	def luminance(color): +		# this is actually in reverse, thanks to dmg/cgb palette ordering +		rough = { 'r':  4.7, +		          'g':  1.4, +		          'b': 13.8, } +		return sum(color[key] * -rough[key] for key in rough.keys()) +	 +	palette = sorted(palette, key = lambda x:luminance(x)) +	 +	# no palette fixing for now +	 +	assert len(palette) <= 4, 'Palette should be 4 colors, is really ' + str(len(palette)) +	 + +	# spit out new palette (disabled for now) +	 +	def rgb_to_dmg(color): +		word =  (color['r'] / 8) << 10 +		word += (color['g'] / 8) <<  5 +		word += (color['b'] / 8) +		return word +	 +	palout = None +	 +	if palout != None: +		output = [] +		for color in palette[1:3]: +			word = rgb_to_dmg(color) +			output.append(word>>8) +			output.append(word&0xff) +		to_file(palout, output) +	 +	 +	# create a new map consisting of quaternary color ids +	 +	map = [] +	if padding['top']: map += [0] * (width + padding['left'] + padding['right']) * padding['top'] +	for line in image: +		if padding['left']: map += [0] * padding['left'] +		for color in line: +			map.append(palette.index(color)) +		if padding['right']: map += [0] * padding['right'] +	if padding['bottom']: map += [0] * (width + padding['left'] + padding['right']) * padding['bottom'] +	 +	# split it into strips of 8, and make them planar +	 +	num_columns = width / 8 +	num_rows = height / 8 +	 +	tile = 8 * 8 +	 +	image = [] +	for row in range(num_rows): +		for column in range(num_columns): +			for strip in range(tile / 8): +				anchor = row*num_columns*tile + column*tile/8 + strip*width +				line   = map[anchor:anchor+8] +				bottom = 0 +				top    = 0 +				for bit, quad in enumerate(line): +					bottom += (quad & 1) << (7-bit) +					top    += ((quad & 2) >> 1) << (7-bit) +				image.append(bottom) +				image.append(top) +	 +	to_file(fileout, image) + + +  if __name__ == "__main__":  	parser = argparse.ArgumentParser() -	parser.add_argument('cmd', nargs='?', metavar='cmd', type=str) +	parser.add_argument('cmd',  nargs='?', metavar='cmd',  type=str)  	parser.add_argument('arg1', nargs='?', metavar='arg1', type=str)  	parser.add_argument('arg2', nargs='?', metavar='arg2', type=str)  	parser.add_argument('arg3', nargs='?', metavar='arg3', type=str) @@ -1116,7 +1394,19 @@ if __name__ == "__main__":  		# python gfx.py pal [address] [length]  		print grab_palettes(int(args.arg1,16), int(args.arg2)) +	elif args.cmd == 'png': +		 +		if '.2bpp' in args.arg1: +			if args.arg3 == 'greyscale': +				to_png(args.arg1, args.arg2) +			else: +				to_png(args.arg1, args.arg2, args.arg3) +		 +		elif '.png' in args.arg1: +			to_2bpp(args.arg1, args.arg2) +	  	#else:  		## python gfx.py  		#decompress_all()  		#if debug: print 'decompressed known gfx to ../gfx/!' + | 
