Browse Source

Added brightness clamping system for when the color palette is too bright

* Brightnesses for backgrounds are now clamped based on a list of brightness
threshold values. This ensures even very bright color palettes result
in a satisfying dark theme
* Added more example images
pull/1/head
Macoy Madson 2 years ago
parent
commit
4579570feb
5 changed files with 72 additions and 28 deletions
  1. +60
    -19
      AutoBase16Theme.py
  2. +10
    -9
      Planning.org
  3. +2
    -0
      ReadMe.org
  4. BIN
      images/Example3.jpg
  5. BIN
      images/Example4.jpg

+ 60
- 19
AutoBase16Theme.py View File

@@ -39,8 +39,10 @@ outputFilename = 'base16-my-auto-theme.el'
# TODO: Make contrast ratio mode which meets accessibility guidelines (see https://webaim.org/resources/contrastchecker/)?

## Background
# Make sure the background is darker than this (for dark themes). In HSL Lightness (0-1)
maximumBackgroundBrightness = 0.29
# These values ensure the backgrounds are nice and dark, even if the color palette values are all bright
# Each background gets progressively lighter. We'll define a different max acceptible value for each level
# For example, the Base00 default background is darkest, so it will be clamped to 0.08 if necessary
maximumBackgroundBrightnessThresholds = [0.08, 0.15, 0.2, 0.25, 0.3, 0.4, 0.45]

## Foreground contrasts (i.e. text color HSL lightness - background color HSL lightness)
# These are relative values instead of ratios because you can't figure a ratio on a black background
@@ -72,6 +74,10 @@ def rgbColorFromStringHex(colorStringHex):
# From https://stackoverflow.com/questions/29643352/converting-hex-to-rgb-value-in-python
return tuple(int(colorStringHex.strip('#')[i:i+2], 16) for i in (0, 2 ,4))

def hlsToRgbStringHex(hlsColor):
rgbColor = colorsys.hls_to_rgb(hlsColor[0], hlsColor[1], hlsColor[2])
return '#{0:02x}{1:02x}{2:02x}'.format(int(rgbColor[0] * 255), int(rgbColor[1] * 255), int(rgbColor[2] * 255))

def rgb256ToHls(color):
rgbColor = color
if type(color) == str:
@@ -84,9 +90,9 @@ def rgb256ToHls(color):
return colorsys.rgb_to_hls(normalizedColor[0], normalizedColor[1], normalizedColor[2])
def getColorBrightness(color):
hsvColor = rgb256ToHls(color)
hlsColor = rgb256ToHls(color)
return hsvColor[1]
return hlsColor[1]

def colorHasBeenUsed(base16Colors, color):
for base16Color in base16Colors:
@@ -105,14 +111,53 @@ def isColorWithinContrastRange(color, backgroundColor, minimumContrast, maximumC
return (contrast >= minimumContrast and contrast <= maximumContrast)

# This is required so backgrounds get progressively lighter
currentMaximumBackgroundBrightnessThresholdIndex = 0

def resetMaximumBackgroundBrightnessThresholdIndex():
global currentMaximumBackgroundBrightnessThresholdIndex
currentMaximumBackgroundBrightnessThresholdIndex = 0
def popMaximumBackgroundBrightnessThreshold():
global currentMaximumBackgroundBrightnessThresholdIndex
threshold = maximumBackgroundBrightnessThresholds[currentMaximumBackgroundBrightnessThresholdIndex]
currentMaximumBackgroundBrightnessThresholdIndex += 1
return threshold

"""

Selection heuristics

"""

# Pick darkest, most grey color for background. If the color is already taken, pick the next unique darkest
def pickDarkestGreyestColorUnique(base16Colors, currentBase16Color, colorPool):
# Used for the background of dark themes. Make sure it is dark, damn it; change the color if you have to :)
def pickDarkestColorForceDarkThreshold(base16Colors, currentBase16Color, colorPool):
bestColor = None
bestColorBrightness = 10000
for color in colorPool:
rgbColorBrightness = getColorBrightness(color)
if rgbColorBrightness < bestColorBrightness:
bestColor = color
bestColorBrightness = rgbColorBrightness

# Clamp brightness
if bestColor:
hlsColor = rgb256ToHls(bestColor)
clampedColor = (hlsColor[0],
min(hlsColor[1], popMaximumBackgroundBrightnessThreshold()),
hlsColor[2])

if debugColorsVerbose:
print('Clamped {} lightness {} to {} (threshold index {})'
.format(bestColor, hlsColor[1], clampedColor[1],
currentMaximumBackgroundBrightnessThresholdIndex))
return hlsToRgbStringHex(clampedColor)

return bestColor

# Pick darkest color. If the color is already taken, pick the next unique darkest
def pickDarkestColorUnique(base16Colors, currentBase16Color, colorPool):
bestColor = None
bestColorBrightness = 10000
for color in colorPool:
@@ -181,21 +226,21 @@ def main():
base16Colors = [
# These go from darkest to lightest via implicit unique ordering
# base00 - Default Background
Base16Color('base00', pickDarkestGreyestColorUnique),
Base16Color('base00', pickDarkestColorForceDarkThreshold),
# base01 - Lighter Background (Used for status bars)
Base16Color('base01', pickDarkestGreyestColorUnique),
Base16Color('base01', pickDarkestColorForceDarkThreshold),
# base02 - Selection Background
Base16Color('base02', pickDarkestGreyestColorUnique),
Base16Color('base02', pickDarkestColorForceDarkThreshold),
# base03 - Comments, Invisibles, Line Highlighting
Base16Color('base03', pickDarkestHighContrastColorUnique),
# base04 - Dark Foreground (Used for status bars)
Base16Color('base04', pickDarkestGreyestColorUnique),
Base16Color('base04', pickDarkestHighContrastColorUnique),
# base05 - Default Foreground, Caret, Delimiters, Operators
Base16Color('base05', pickDarkestHighContrastColorUnique),
# base06 - Light Foreground (Not often used)
Base16Color('base06', pickDarkestGreyestColorUnique),
Base16Color('base06', pickDarkestColorForceDarkThreshold),
# base07 - Light Background (Not often used)
Base16Color('base07', pickDarkestGreyestColorUnique),
Base16Color('base07', pickDarkestColorForceDarkThreshold),
# base08 - Variables, XML Tags, Markup Link Text, Markup Lists, Diff Deleted
Base16Color('base08', pickHighContrastBrightColorUniqueOrRandom),
# base09 - Integers, Boolean, Constants, XML Attributes, Markup Link Url
@@ -232,6 +277,9 @@ def main():
rgbColor = rgbColorFromStringHex(color)
print('RGB =', rgbColor)

# Make sure we start at the darkest threshold
resetMaximumBackgroundBrightnessThresholdIndex()

# Select a color from the color pool for each base16 color
for i, base16Color in enumerate(base16Colors):
color = base16Color.selectionFunction(base16Colors, base16Color, colorPool)
@@ -244,13 +292,6 @@ def main():
print('Selected {} for {}'.format(base16Colors[i].color, base16Color.name))

# Ensure backgrounds are dark enough
# backgroundColor = base16Colors[BACKGROUND_COLOR_INDEX]
# for i in [0, 1, 2]:
# color = base16Colors[i].color
# if getColorBrightness

# Output selected colors
outputTemplateFile = open(outputTemplateFilename, 'r')
outputTemplate = ''.join(outputTemplateFile.readlines())


+ 10
- 9
Planning.org View File

@@ -1,20 +1,21 @@
* Left off
** Make it so hawaii.jpg and georgia.jpg look good (don't let background get too colorful?)
*** blade2.jpg makes it seem like I need some background brightness clamping system
* To Do
** TODO Test selections with more images
** TODO Emacs interface doesn't update correctly (status bars), which doesn't give an accurate picture for those colors
** TODO Check contrast better (i.e. with some fancy algorithm)
** TESTING Make sure base03 comments has a high enough contrast
** TODO Green and red tint for diff colors? Either way, establish relationship between those two (and maybe others) ensuring they are different
*** Relationships
**** base08, base0B, base0E should be unique between eachother (diff colors)
**** Always have base08 variables be darker than 0A classes, 0D functions, and 0E keywords?
**** Ensure no other foreground text values are the same as base03 comments?
** DONE Contrast ratio between text colors shouldn't be too great (try georgia.jpg for a bad result)
** TODO Take template to use as a command line argument
** TODO Support different heuristic modes (e.g. light theme)
** TODO Support different heuristic modes (e.g. light theme, more muted, more contrasty)
** TODO IMPORTANT Make sure things are contrasty relative to line highlight (base01?)
** TODO Figure out how to update base16 theme colors without having to reload emacs (just unset and set in customize-themes?)
*** TODO Emacs interface doesn't update correctly (status bars) without having to reload, which doesn't give an accurate picture for those colors
** TODO Post on /r/unixporn, pull request to add to readme of https://github.com/chriskempson/base16
* Done
** DONE Figure out how to update base16 theme colors without having to reload emacs (just unset and set in customize-themes?)
** DONE Consider making a maximum contrast to avoid starkly bright text
*** Test maximum brightness code
** DONE Make sure base03 comments has a high enough contrast
** DONE Check contrast better (i.e. with some fancy algorithm)
** DONE Contrast ratio between text colors shouldn't be too great (try georgia.jpg for a bad result)
** DONE Make it so hawaii.jpg and georgia.jpg look good (don't let background get too colorful?)
*** blade2.jpg makes it seem like I need some background brightness clamping system

+ 2
- 0
ReadMe.org View File

@@ -16,3 +16,5 @@ Automating the execution of this so that it updates with your desktop background
** Example results
[[./images/Example1.jpg]]
[[./images/Example2.jpg]]
[[./images/Example3.jpg]]
[[./images/Example4.jpg]]

BIN
images/Example3.jpg View File

Before After
Width: 3828  |  Height: 2013  |  Size: 907 KiB

BIN
images/Example4.jpg View File

Before After
Width: 3793  |  Height: 2047  |  Size: 1.0 MiB

Loading…
Cancel
Save