From f70e5a42222082ec87ca087af7c028e160c05d66 Mon Sep 17 00:00:00 2001 From: Alek Ratzloff Date: Tue, 21 May 2024 18:23:14 -0700 Subject: [PATCH] Split functionality out into matrix and color passes * Matricizers turn a hash into a matrix * Colorizers turn a matrix into a colored matrix Signed-off-by: Alek Ratzloff --- colorhash/__main__.py | 12 ++++-- colorhash/colorizer.py | 24 ++++++++++++ colorhash/hash_colorizer.py | 78 ------------------------------------- colorhash/matricizer.py | 45 +++++++++++++++++++++ colorhash/svg.py | 24 ++++++++++++ 5 files changed, 101 insertions(+), 82 deletions(-) create mode 100644 colorhash/colorizer.py delete mode 100644 colorhash/hash_colorizer.py create mode 100644 colorhash/matricizer.py create mode 100644 colorhash/svg.py diff --git a/colorhash/__main__.py b/colorhash/__main__.py index c50b5fc..99cc44e 100644 --- a/colorhash/__main__.py +++ b/colorhash/__main__.py @@ -1,7 +1,9 @@ import hashlib import sys -from .hash_colorizer import PaletteColorizer +from .colorizer import PaletteColorizer +from .matricizer import NibbleMatricizer +from .svg import gensvg # TODO - WASM compile for embedding directly in HTML @@ -46,8 +48,8 @@ infile = sys.stdin.buffer if len(sys.argv) > 1: inpath = sys.argv[1] - if inpath != '-': - infile = open(inpath, 'rb') + if inpath != "-": + infile = open(inpath, "rb") hashdata = hashlib.file_digest(infile, hash_algo).digest() @@ -62,4 +64,6 @@ colorizer = PaletteColorizer(palette) # pprint.pprint([[hex(c) for c in row] for row in colors]) # Print SVG -print(colorizer.hash_to_svg(hashdata, w, h, 32)) +matrix = NibbleMatricizer(w, h).hash_to_matrix(hashdata) +colors = PaletteColorizer(palette).colorize(matrix) +print(gensvg(colors, 32)) diff --git a/colorhash/colorizer.py b/colorhash/colorizer.py new file mode 100644 index 0000000..343d364 --- /dev/null +++ b/colorhash/colorizer.py @@ -0,0 +1,24 @@ +import abc +from typing import Sequence + +from .matricizer import Matrix + + +class Colorizer(metaclass=abc.ABCMeta): + @abc.abstractmethod + def colorize(self, matrix: Matrix) -> Matrix: + """ + Colorize a matrix. + """ + + +Palette = Sequence[str] + + +class PaletteColorizer(Colorizer): + def __init__(self, palette: Palette) -> None: + assert len(palette) == 16, "palette must contain exactly 16 colors" + self.palette = palette + + def colorize(self, matrix: Matrix) -> Matrix: + return [[self.palette[v] for v in row] for row in matrix] diff --git a/colorhash/hash_colorizer.py b/colorhash/hash_colorizer.py deleted file mode 100644 index 2e8ba1d..0000000 --- a/colorhash/hash_colorizer.py +++ /dev/null @@ -1,78 +0,0 @@ -import abc -from typing import Sequence - - -Matrix = Sequence[Sequence[int]] - - -class HashColorizer(metaclass=abc.ABCMeta): - def hash_to_matrix(self, data: bytes, w: int, h: int) -> Matrix: - """ - Convert a set of bytes to a list of rows of nibbles. - """ - - nibbles = [] - for b in data: - top = (b & 0xF0) >> 4 - bottom = b & 0x0F - nibbles += [top, bottom] - - if len(nibbles) != w * h: - raise ValueError( - f"input data length ({len(nibbles)}) must match matrix dimensions ({w}x{h} = {w * h})" - ) - - cols = [] - row = [] - for b in nibbles: - row += [b] - if len(row) == w: - cols += [row] - row = [] - - return cols - - @abc.abstractmethod - def colorize(self, matrix: Matrix) -> Matrix: - """ - Colorize a matrix. - """ - - def gensvg(self, matrix: Matrix, square_size: int) -> str: - """ - Generate an SVG based on a given matrix. - """ - h = len(matrix) - w = len(matrix[0]) - - # Start SVG string - svg = f'\n' - - # Generate grid - for r in range(h): - for c in range(w): - x = c * square_size - y = r * square_size - color = matrix[r][c] - svg += f' \n' - - # Close SVG string - svg += "" - return svg - - def hash_to_svg(self, hash: bytes, w: int, h: int, square_size: int) -> str: - matrix = self.hash_to_matrix(hash, w, h) - colors = self.colorize(matrix) - return self.gensvg(colors, square_size) - - -Palette = list - - -class PaletteColorizer(HashColorizer): - def __init__(self, palette: Palette) -> None: - assert len(palette) == 16, "palette must contain exactly 16 colors" - self.palette = palette - - def colorize(self, matrix: Matrix) -> Matrix: - return [[self.palette[v] for v in row] for row in matrix] diff --git a/colorhash/matricizer.py b/colorhash/matricizer.py new file mode 100644 index 0000000..1a0334e --- /dev/null +++ b/colorhash/matricizer.py @@ -0,0 +1,45 @@ +import abc +from typing import Sequence + + +Matrix = Sequence[Sequence[int]] + + +class Matricizer(metaclass=abc.ABCMeta): + def __init__(self, w: int, h: int) -> None: + self.w = w + self.h = h + + @abc.abstractmethod + def hash_to_matrix(self, data: bytes) -> Matrix: + """ + Convert a hash to a matrix of given width and height. + """ + + +class NibbleMatricizer(Matricizer): + def hash_to_matrix(self, data: bytes) -> Matrix: + """ + Convert a set of bytes to a list of rows of nibbles. + """ + + nibbles = [] + for b in data: + top = (b & 0xF0) >> 4 + bottom = b & 0x0F + nibbles += [top, bottom] + + if len(nibbles) != self.w * self.h: + raise ValueError( + f"input data length ({len(nibbles)}) must match matrix dimensions ({self.w}x{self.h} = {self.w * self.h})" + ) + + cols = [] + row = [] + for b in nibbles: + row += [b] + if len(row) == self.w: + cols += [row] + row = [] + + return cols diff --git a/colorhash/svg.py b/colorhash/svg.py new file mode 100644 index 0000000..5b2f14a --- /dev/null +++ b/colorhash/svg.py @@ -0,0 +1,24 @@ +from .matricizer import Matrix + + +def gensvg(matrix: Matrix, square_size: int) -> str: + """ + Generate an SVG based on a given matrix. + """ + h = len(matrix) + w = len(matrix[0]) + + # Start SVG string + svg = f'\n' + + # Generate grid + for r in range(h): + for c in range(w): + x = c * square_size + y = r * square_size + color = matrix[r][c] + svg += f' \n' + + # Close SVG string + svg += "" + return svg