added options for equiluminance and not to darken the brown color; script now exports settings
This commit is contained in:
parent
1287b93ef4
commit
26ccf45539
26
README.md
26
README.md
@ -1,8 +1,28 @@
|
|||||||
# acid16
|
# acid16
|
||||||
It makes base16 schemes. Pass a background color, an accent color, and optionally a foreground text color, and out comes a color scheme.
|
It makes base16 schemes. Pass a background color, an accent color, and optionally a foreground text color, and out comes a color scheme.
|
||||||
|
|
||||||
Color arguments are in the form of "hue,sat,lum" for background and "hue,sat" for the other two. All values must be numbers between 0.0 and 1.0 (you're welcome to try other values, just don't be shocked if it breaks). Also, keep in mind that luminance here is *relative* luminance, or the color's brightness as perceived by the human eye. This is different from the usual HSL space.
|
Color arguments are in the form of "hue,chroma,luminance" for background and "hue,chroma" for the other two. All values must be numbers between 0.0 and 1.0 (you're welcome to try other values, just don't be shocked if it breaks). Also, keep in mind luminance in HCL is *relative* luminance, or the color's brightness as perceived by the human eye. It also uses linear color ramping instead of the usual gamma-encoded sRGB, so something like "0.0,0.0,0.25" won't get you the 25% gray you're used to.
|
||||||
|
|
||||||
The script will do its best to make sure everything is readable. It tries to keep a contrast ratio of 7:1 between the foreground and background colors, while trying to stay above 3:1 between foreground and highlight, the "rainbow" colors (i.e. the last 8 colors) and highlight, and the "dark" foreground and background. Usually it does a good job. But it tends not to like a background luminance that hovers around 0.3, that's when stuff gets to be an eyesore.
|
The script will do its best to make sure everything is readable. It tries to keep a contrast ratio of 7:1 between the foreground and background colors, while trying to stay above 3:1 for foreground and highlight, the "rainbow" colors (i.e. the last 8 colors) and highlight, and the "dark" foreground and background. Usually it does a good job. But it tends not to like a background luminance that hovers around 0.3, that's when stuff gets to be an eyesore.
|
||||||
|
|
||||||
For the last 8 colors, you can make them blend with the background hue by passing `-r (amount)`, the amount being between 0.0 and 1.0. 0.0 means no blending, 1.0 makes them all the same hue.
|
## Prerequisites
|
||||||
|
|
||||||
|
- Python 3
|
||||||
|
- Pillow
|
||||||
|
- ImageMagick (for previews)
|
||||||
|
|
||||||
|
## Options
|
||||||
|
|
||||||
|
- `-n name`, `-a author`: Set the scheme and author name.
|
||||||
|
- `-e`: Equiluminant mode; uses the same luminance for the last 8 colors. This keeps their brightness uniform but can make them harder to tell apart.
|
||||||
|
- `-r amount`: Make the last 8 colors blend with the background hue. 0.0 means no blending, 1.0 makes them the same exact color. If you'd like to have them be the same hue but with different brightnesses, use a value like 0.999.
|
||||||
|
- `-p`: Generate a preview image demonstrating color contrast and the palette itself.
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
### On luminance options
|
||||||
|
Seems like the only usable ranges for background luminance at the moment are 0.00 to 0.10 and 0.31 to 1.00.
|
||||||
|
|
||||||
|
While 0.08 to 0.10 *are* usable, colors base05 to base07 are extremely similar to one another. Not that it matters since base06 and base07 are allegedly uncommon. Starting at 0.11, the script can no longer maintain its contrast levels since the foreground brightness it wants is out of range. This only gets worse until 0.25, at which point the "rainbow" hues just break down (though this could be mitigated).
|
||||||
|
|
||||||
|
From 0.26 to 0.30 the contrasts are just a huge mess. The foreground and comment colors are brighter than the background, but the rainbow hues are extremely dark. This stops being a problem at 0.31, at which case all text colors are darker than the background.
|
||||||
|
67
acid16.py
67
acid16.py
@ -7,7 +7,7 @@ parser.add_option("-p", "--preview",
|
|||||||
action="store_true",
|
action="store_true",
|
||||||
dest="showPreview",
|
dest="showPreview",
|
||||||
default=False,
|
default=False,
|
||||||
help="show preview")
|
help="generate a preview image")
|
||||||
parser.add_option("-n", "--name",
|
parser.add_option("-n", "--name",
|
||||||
action="store",
|
action="store",
|
||||||
dest="name",
|
dest="name",
|
||||||
@ -23,6 +23,16 @@ parser.add_option("-r", "--rainbow-blend",
|
|||||||
dest="rainbowBlend",
|
dest="rainbowBlend",
|
||||||
default=0.0,
|
default=0.0,
|
||||||
help="mix ratio between last 8 colors' hues and background")
|
help="mix ratio between last 8 colors' hues and background")
|
||||||
|
parser.add_option("-e", "--equiluminant",
|
||||||
|
action="store_true",
|
||||||
|
dest="equiluminant",
|
||||||
|
default=0.0,
|
||||||
|
help="use the same luminance for last 8 colors")
|
||||||
|
parser.add_option("-b", "--light-brown",
|
||||||
|
action="store_true",
|
||||||
|
dest="lightBrown",
|
||||||
|
default=0.0,
|
||||||
|
help="don't darken last color")
|
||||||
|
|
||||||
(options, args) = parser.parse_args()
|
(options, args) = parser.parse_args()
|
||||||
if len(args) < 2:
|
if len(args) < 2:
|
||||||
@ -31,16 +41,19 @@ if len(args) < 2:
|
|||||||
def parseTuple(st):
|
def parseTuple(st):
|
||||||
return tuple(float(x) for x in st.split(","))
|
return tuple(float(x) for x in st.split(","))
|
||||||
|
|
||||||
|
def floatOrDie(fl, errSt):
|
||||||
|
try:
|
||||||
|
return float(fl)
|
||||||
|
except:
|
||||||
|
parser.error(errSt)
|
||||||
|
|
||||||
bgHue, bgSat, bgLum = parseTuple(args[0])
|
bgHue, bgSat, bgLum = parseTuple(args[0])
|
||||||
|
|
||||||
accentHue, accentSat = parseTuple(args[1])
|
accentHue, accentSat = parseTuple(args[1])
|
||||||
|
|
||||||
foreHue, foreSat = parseTuple(args[2]) if len(args) > 2 else (0.0, 0.0)
|
foreHue, foreSat = parseTuple(args[2]) if len(args) > 2 else (0.0, 0.0)
|
||||||
|
|
||||||
try:
|
rainbowBlend = floatOrDie(options.rainbowBlend, "rainbow blend must be a number")
|
||||||
rainbowBlend = float(options.rainbowBlend)
|
|
||||||
except:
|
|
||||||
parser.error("rainbow blend must be a number")
|
|
||||||
|
|
||||||
def lerp(a, b, t):
|
def lerp(a, b, t):
|
||||||
return a + (b - a) * t
|
return a + (b - a) * t
|
||||||
@ -61,7 +74,7 @@ def contrast(a, b):
|
|||||||
def getContrast(lum, ratio):
|
def getContrast(lum, ratio):
|
||||||
hi = ratio*lum + (ratio - 1)*0.05
|
hi = ratio*lum + (ratio - 1)*0.05
|
||||||
lo = (lum + 0.05 - ratio * 0.05) / ratio
|
lo = (lum + 0.05 - ratio * 0.05) / ratio
|
||||||
return hi if hi < 1.0 or lo < 0.0 else lo
|
return hi if hi < 0.99 or lo < 0.0 else lo
|
||||||
|
|
||||||
def makeColor(hue, lum, sat = 1.0):
|
def makeColor(hue, lum, sat = 1.0):
|
||||||
orig = luminance(colorsys.hls_to_rgb(hue, 0.5, sat))
|
orig = luminance(colorsys.hls_to_rgb(hue, 0.5, sat))
|
||||||
@ -74,7 +87,7 @@ def toSRGB(col):
|
|||||||
return tuple(x*12.92 if x < 0.0031308 else 1.055*x**(1/2.4) - 0.055 for x in col)
|
return tuple(x*12.92 if x < 0.0031308 else 1.055*x**(1/2.4) - 0.055 for x in col)
|
||||||
|
|
||||||
def usable(col):
|
def usable(col):
|
||||||
return tuple(int(x*255) for x in toSRGB(col))
|
return tuple(int(x*255+0.5) for x in toSRGB(col))
|
||||||
|
|
||||||
def toHex(col):
|
def toHex(col):
|
||||||
return "".join(["{:02x}".format(x) for x in usable(col)])
|
return "".join(["{:02x}".format(x) for x in usable(col)])
|
||||||
@ -96,21 +109,35 @@ lastTwo = [lerp(foreLum, 1.0 if foreLum > bgLum else 0.0, (x + 1) / 3) for x in
|
|||||||
foreLight = makeColor(foreHue, lastTwo[0], foreSat)
|
foreLight = makeColor(foreHue, lastTwo[0], foreSat)
|
||||||
backLight = makeColor(bgHue, lastTwo[1], bgSat)
|
backLight = makeColor(bgHue, lastTwo[1], bgSat)
|
||||||
|
|
||||||
rainHues = [0, 20, 60, 120, 180, 240, 300, 20]
|
rainHues = [0, 23, 60, 120, 200, 240, 300, 15]
|
||||||
backFull = colorsys.hls_to_rgb(bgHue, 0.5, bgSat)
|
backFull = colorsys.hls_to_rgb(bgHue, 0.5, bgSat)
|
||||||
|
|
||||||
rainbow = [mixColors(colorsys.hls_to_rgb(hue/360.0, 0.5, 1.0), backFull, rainbowBlend) for hue in rainHues]
|
rainbow = [mixColors(colorsys.hls_to_rgb(hue/360.0, 0.5, 1.0), backFull, rainbowBlend) for hue in rainHues]
|
||||||
|
|
||||||
rainLums = [luminance(col) for col in rainbow]
|
rainLums = [luminance(col) for col in rainbow]
|
||||||
hiLum, loLum = max(rainLums), min(rainLums)
|
hiLum, loLum = max(rainLums), min(rainLums)
|
||||||
minLum, maxLum = sorted((getContrast(bgLum, 4), getContrast(bgLum, 6)))
|
minLum, maxLum = sorted((getContrast(bgLum, 3.5), getContrast(bgLum, 3.5 if options.equiluminant else 6 )))
|
||||||
|
maxLum = min(1.0, maxLum)
|
||||||
|
|
||||||
|
darkenBrown = (options.equiluminant or options.lightBrown)
|
||||||
|
|
||||||
rainHLS = [colorsys.rgb_to_hls(*col) for col in rainbow]
|
rainHLS = [colorsys.rgb_to_hls(*col) for col in rainbow]
|
||||||
rainbow = [makeColor(rainHLS[c][0], reposition(loLum, hiLum+0.00001, rainLums[c], minLum, maxLum) if c < 7 else minLum, rainHLS[c][2]) for c in range(len(rainbow))]
|
rainbow = [makeColor(rainHLS[c][0], reposition(loLum, hiLum+0.00001, rainLums[c], minLum, maxLum) if c < 7 or darkenBrown else minLum, rainHLS[c][2] / (1 if c < 7 else 1.5)) for c in range(len(rainbow))]
|
||||||
|
|
||||||
cols = [background, backLighter, accent, comment, foreDark, foreground, foreLight, backLight] + rainbow
|
cols = [background, backLighter, accent, comment, foreDark, foreground, foreLight, backLight] + rainbow
|
||||||
|
|
||||||
print("scheme: \"{}\"".format(options.name))
|
print("# generated by acid16 - https://git.lain.church/whut/acid16")
|
||||||
|
print("# --- settings ---")
|
||||||
|
print("# background HCL: {} {} {}".format(bgHue, bgSat, bgLum))
|
||||||
|
print("# accent HC : {} {}".format(accentHue, accentSat))
|
||||||
|
print("# foreground HC : {} {}".format(foreHue, foreSat))
|
||||||
|
print("# rainbow blend : {}".format(rainbowBlend))
|
||||||
|
if options.equiluminant:
|
||||||
|
print("# equiluminant \"rainbow\" hues")
|
||||||
|
if options.lightBrown:
|
||||||
|
print("# not darkening last color (brown)")
|
||||||
|
|
||||||
|
print("\nscheme: \"{}\"".format(options.name))
|
||||||
print("author: \"{}\"".format(options.author))
|
print("author: \"{}\"".format(options.author))
|
||||||
|
|
||||||
for i in range(len(cols)):
|
for i in range(len(cols)):
|
||||||
@ -118,27 +145,27 @@ for i in range(len(cols)):
|
|||||||
|
|
||||||
if options.showPreview:
|
if options.showPreview:
|
||||||
font = ImageFont.truetype("LiberationMono-Regular.ttf", 20)
|
font = ImageFont.truetype("LiberationMono-Regular.ttf", 20)
|
||||||
h, w = imSize = (640, 640)
|
w, h = imSize = (640, 480)
|
||||||
|
|
||||||
im = Image.new("RGB", (640, 640), usable(background))
|
im = Image.new("RGB", imSize, usable(background))
|
||||||
draw = ImageDraw.Draw(im)
|
draw = ImageDraw.Draw(im)
|
||||||
desc = lambda t, a, b: "{} (contrast {:.1f}:1)".format(t, contrast(luminance(a), luminance(b)))
|
desc = lambda t, a, b: "{} (contrast {:.1f}:1)".format(t, contrast(luminance(a), luminance(b)))
|
||||||
text = lambda x, y, t, col, other: draw.text((x, y), desc(t, col, other), usable(col), font)
|
text = lambda x, y, t, col, other: draw.text((x, y), desc(t, col, other), usable(col), font)
|
||||||
|
|
||||||
draw.rectangle((2, 2, 638, 32), usable(accent))
|
draw.rectangle((2, 2, w-2, 32), usable(accent))
|
||||||
text(5, 5, "Foreground on highlight", foreground, accent)
|
text(5, 5, "Foreground on highlight", foreground, accent)
|
||||||
text(5, 45, "Foreground on background", foreground, background)
|
text(5, 45, "Foreground on background", foreground, background)
|
||||||
text(5, 85, "# Comment color", comment, background)
|
text(5, 85, "# Comment color", comment, background)
|
||||||
|
|
||||||
for i in range(len(rainbow)):
|
for i in range(len(rainbow)):
|
||||||
draw.rectangle((4, 120+i*40, 84, 150+i*40), usable(rainbow[i]))
|
draw.rectangle((4, 120+i*35, 84, 145+i*35), usable(rainbow[i]))
|
||||||
text(90, 125+i*40, "Colored text", rainbow[i], background)
|
text(90, 123+i*35, "Colored text", rainbow[i], background)
|
||||||
|
|
||||||
draw.rectangle((0, 560, 640, 600), usable(backLighter))
|
draw.rectangle((0, h-80, w, h-40), usable(backLighter))
|
||||||
text(5, 565, "'Dark' foreground", foreDark, backLighter)
|
text(5, h-75, "'Dark' foreground", foreDark, backLighter)
|
||||||
draw.line((0, 598, 640, 598), usable(foreground), 2)
|
draw.line((0, h-42, w, h-42), usable(foreground), 2)
|
||||||
|
|
||||||
for i in range(len(cols)):
|
for i in range(len(cols)):
|
||||||
draw.rectangle((i*40,600,i*40+39,639), usable(cols[i]))
|
draw.rectangle((i*40,h-40,i*40+39,h), usable(cols[i]))
|
||||||
|
|
||||||
im.show()
|
im.show()
|
||||||
|
Loading…
Reference in New Issue
Block a user