Skip to content

Instantly share code, notes, and snippets.

@jwodder
Forked from HaleTom/print256colours.sh
Last active November 2, 2021 02:38
Show Gist options
  • Save jwodder/4bf350e2d72b547b22dc9de52148ccbe to your computer and use it in GitHub Desktop.
Save jwodder/4bf350e2d72b547b22dc9de52148ccbe to your computer and use it in GitHub Desktop.
Print a 256-colour test pattern in the terminal
#!/usr/bin/env python3
import argparse
from dataclasses import dataclass
from typing import Optional
# John Thorvald Wodder II, 2016-2021.
# Tom Hale, 2016. MIT Licence.
# Print out 256 colours, with each number printed in its corresponding colour
# See http://askubuntu.com/questions/821157/print-a-256-color-test-pattern-in-the-terminal/821163#821163
PRINTABLE_COLOURS = 256
@dataclass
class ColorBlockPrinter:
# True: colored text on white/black background
# False: white/black text on colored background
reverse: bool
fg: Optional[int]
bg: Optional[int]
# Print a coloured block with the number of that colour
def print_colour(self, colour):
contrast = contrast_colour(colour)
if self.reverse:
fg = colour
bg = contrast
else:
fg = contrast
bg = colour
if self.fg is not None:
fg = self.fg
if self.bg is not None:
bg = self.bg
print(colorize(f"{colour:3}", fg=fg, bg=bg), end=" ")
# Starting at ``start``, print a run of ``qty`` colours
def print_run(self, start, qty):
for i in range(start, min(start + qty, PRINTABLE_COLOURS)):
self.print_colour(i)
print(" ", end="")
# Print blocks of colours
def print_blocks(self, start, end, block_cols, block_rows, blocks_per_line):
# ``end`` is inclusive
block_length = block_cols * block_rows
# Print sets of blocks
for i in range(start, end + 1, blocks_per_line * block_length):
print() # Space before each set of blocks
# For each block row
for _ in range(block_rows):
# Print block columns for all blocks on the line
for block in range(blocks_per_line):
self.print_run(i + (block * block_length), block_cols)
i += block_cols # Prepare to print the next row
print()
def run(self):
# The first 16 colours are spread over the whole spectrum
self.print_run(0, 16)
print()
# 6x6x6 colour cube between 16 and 231 inclusive
self.print_blocks(16, 231, 6, 6, 3)
# Not 50, but 24 Shades of Grey
self.print_blocks(232, 255, 12, 2, 1)
def colorize(txt, fg=None, bg=None):
def sgrcolor(index, start):
if index is None:
return ()
elif index == -1:
return (start + 9,)
elif index < 16:
a, b = divmod(index, 8)
return (start + 60 * a + b,)
else:
return (start + 8, 5, index)
s = ";".join(map(str, sgrcolor(fg, 30) + sgrcolor(bg, 40)))
return f"\x1B[{s}m{txt}\x1B[0m" if s else txt
# Return a colour that contrasts with the given colour
def contrast_colour(colour):
if colour < 16: # Initial 16 ANSI colours
return 15 if colour == 0 else 0
# Greyscale # rgb_R = rgb_G = rgb_B = (number - 232) * 10 + 8
if colour > 231: # Greyscale ramp
return 15 if colour < 244 else 0
# All other colours:
# 6x6x6 colour cube = 16 + 36*R + 6*G + B # Where RGB are [0..5]
# See http://stackoverflow.com/a/27165165/5353461
# r=(colour-16) // 36
g = ((colour - 16) % 36) // 6
# b=(colour-16) % 6
# If luminance is bright, print number in black, white otherwise.
# Green contributes 587/1000 to human perceived luminance - ITU R-REC-BT.601
return 0 if g > 2 else 15
# Uncomment the below for more precise luminance calculations
# # Calculate percieved brightness
# # See https://www.w3.org/TR/AERT#color-contrast
# # and http://www.itu.int/rec/R-REC-BT.601
# # Luminance is in range 0..5000 as each value is 0..5
# luminance=(r * 299) + (g * 587) + (b * 114)
# return 0 if luminance > 2500 else 15
def main():
parser = argparse.ArgumentParser()
parser.add_argument(
"--bg", type=int, help="Use a constant background color", metavar="INT"
)
parser.add_argument(
"--fg", type=int, help="Use a constant foreground color", metavar="INT"
)
parser.add_argument(
"--reverse",
action="store_true",
help="Color the text instead of the backgrounds",
)
args = parser.parse_args()
colorer = ColorBlockPrinter(reverse=args.reverse, fg=args.fg, bg=args.bg)
colorer.run()
if __name__ == "__main__":
main() # pragma: no cover
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment