aboutsummaryrefslogtreecommitdiffstats
path: root/tools/make_logo_ans.py
diff options
context:
space:
mode:
Diffstat (limited to 'tools/make_logo_ans.py')
-rw-r--r--tools/make_logo_ans.py90
1 files changed, 90 insertions, 0 deletions
diff --git a/tools/make_logo_ans.py b/tools/make_logo_ans.py
new file mode 100644
index 0000000..9736e30
--- /dev/null
+++ b/tools/make_logo_ans.py
@@ -0,0 +1,90 @@
+#!/usr/bin/env python3
+"""Regenerate logo.ans (the block-art splash) from logo.png.
+
+logo.ans is the fallback for terminals that can't draw an image, so it has to be
+rendered from the same artwork or the two drift apart -- which is exactly what
+happened when logo.png was replaced and the old block art kept a scan line that
+no longer existed.
+
+Each cell is a lower half-block: the background colour paints the upper pixel,
+the foreground the lower one, so one cell carries two pixel rows. Transparent
+pixels emit no colour at all, letting the terminal background show through, and
+a cell whose lower pixel is transparent uses an upper half-block instead so the
+one opaque pixel still lands on the correct half.
+
+Needs Pillow, so run it with a python that has it (NOT ~/ida-venv):
+
+ /usr/bin/python3 tools/make_logo_ans.py [--cols 60] [-o logo.ans]
+"""
+from __future__ import annotations
+
+import argparse
+import os
+import sys
+
+from PIL import Image
+
+REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+ALPHA_ON = 128 # at/above this a pixel counts as present
+
+UPPER, LOWER = "\u2580", "\u2584" # upper half block, lower half block
+
+
+def main() -> int:
+ ap = argparse.ArgumentParser()
+ ap.add_argument("--png", default=os.path.join(REPO, "logo.png"))
+ ap.add_argument("-o", "--out", default=os.path.join(REPO, "logo.ans"))
+ ap.add_argument("--cols", type=int, default=60)
+ ap.add_argument("--cell", default="9x22",
+ help="terminal cell size WxH in px, for the aspect ratio")
+ args = ap.parse_args()
+
+ cw, ch = (int(v) for v in args.cell.lower().split("x"))
+ im = Image.open(args.png).convert("RGBA")
+ bbox = im.getchannel("A").getbbox()
+ if bbox:
+ im = im.crop(bbox)
+ w, h = im.size
+ cols = args.cols
+ # two pixel rows per cell, and cells are taller than they are wide
+ rows = max(1, round((h / w) * cols * cw / ch))
+ im = im.resize((cols, rows * 2), Image.LANCZOS)
+ px = im.load()
+
+ out: list[str] = []
+ for r in range(rows):
+ line = []
+ prev = None
+ for c in range(cols):
+ top = px[c, r * 2]
+ bot = px[c, r * 2 + 1]
+ t_on, b_on = top[3] >= ALPHA_ON, bot[3] >= ALPHA_ON
+ if not t_on and not b_on:
+ sgr, ch_ = "\033[0m", " "
+ elif t_on and b_on:
+ sgr = (f"\033[38;2;{bot[0]};{bot[1]};{bot[2]}m"
+ f"\033[48;2;{top[0]};{top[1]};{top[2]}m")
+ ch_ = LOWER
+ elif b_on: # only the lower pixel is present
+ sgr = f"\033[0m\033[38;2;{bot[0]};{bot[1]};{bot[2]}m"
+ ch_ = LOWER
+ else: # only the upper pixel is present
+ sgr = f"\033[0m\033[38;2;{top[0]};{top[1]};{top[2]}m"
+ ch_ = UPPER
+ if sgr != prev:
+ line.append(sgr)
+ prev = sgr
+ line.append(ch_)
+ line.append("\033[0m")
+ out.append("".join(line))
+
+ text = "\n".join(out) + "\n"
+ with open(args.out, "w", encoding="utf-8") as f:
+ f.write(text)
+ print(f"{args.png} {w}x{h} -> {args.out} {cols}x{rows} cells "
+ f"({len(text):,} bytes, cell {cw}x{ch})")
+ return 0
+
+
+if __name__ == "__main__":
+ sys.exit(main())