Code: Select all

```
"""AWJ's NTSC NES palette generator.
Based on Kevin Horton's palette generator
and on information from these web pages:
http://unusedino.de/ec64/technical/misc/vic656x/colors/
http://en.wikipedia.org/wiki/YIQ
http://en.wikipedia.org/wiki/YUV
"""
import array
import math
hue = (0, 285, 315, 345, 15, 45, 75, 105, 135, 165, 195, 225, 255, 0, 0, 0)
"""Table of hue values (theta coordinate on Q-I plane)
Assumption: That the 12 non-grey hues are equally spaced
30 degrees apart, and offset 15 degrees from the axes.
This assumption allows hue 8 (colors 08, 18, 28, 38) to be
very close to pure yellow (R = G > B), and for hue 6 (06, etc.)
to be fairly close to pure red (R > G = B).
The alternate assumption, that every third hue is aligned with
an axis, results in no hue being close to pure yellow or pure red.
TODO: (by someone else)
verify hues using a video signal separator and a real NES
"""
sat = 0.146
"""Saturation value (radius coordinate on Q-I plane)
Assumption: That all 12 non-grey hues have the same saturation.
According to Kevin Horton (and parsimony) this is the case.
Currently this is an arbitrary value, chosen such that
color 08 ends up with a blue level of exactly zero.
"""
luma = ((0.50, 0.75, 1.00, 1.00),
(0.29, 0.45, 0.73, 0.90),
(0.00, 0.24, 0.47, 0.77))
"""Table of luma (Y) values
Assumption: That these values (stolen from Kevin Horton's
QBASIC palette generator) are accurate :)
Are color 00 and 2D, and 10 and 3D, really this close?
All hand-hacked NES palettes I've seen make them
distinctly different shades of grey from each other
(3D brighter than 10, and 2D much darker than 00)
TODO: (by someone else)
verify values, ideally on several hardware versions
(frontloader, toploader, original Famicom, AV Famicom)
"""
def yiq2rgb(y, i, q):
"""Convert a YIQ color to RGB.
Transformation matrix stolen from here:
http://www.mathworks.com/access/helpdesk/help/toolbox/images/ntsc2rgb.html
"""
r = y + 0.956 * i + 0.621 * q
g = y - 0.272 * i - 0.647 * q
b = y - 1.106 * i + 1.703 * q
return (r, g, b)
# Main program begins here
nespalette = array.array('B')
for i in range(4):
for j in range(16):
h = math.radians(hue[j])
if j > 13:
s = 0
y = 0
elif j == 0:
s = 0
y = luma[0][i]
elif j == 13:
s = 0
y = luma[2][i]
else:
s = sat
y = luma[1][i]
r, g, b = yiq2rgb(y, s * math.sin(h), s * math.cos(h))
# clip RGB values
if r < 0.0: r = 0.0
if g < 0.0: g = 0.0
if b < 0.0: b = 0.0
if r > 1.0: r = 1.0
if g > 1.0: g = 1.0
if b > 1.0: b = 1.0
# TODO: add gamma correction
# convert from [0..1] to [0..255]
r = round(r * 255.0)
g = round(g * 255.0)
b = round(b * 255.0)
# add this color to the palette
nespalette.fromlist([int(r), int(g), int(b)])
outputfile = file("awj.pal", "wb")
nespalette.tofile(outputfile)
outputfile.close()
```