1bc2de655a
Uniform stock management for healthcare linen rooms. Licensed under the GNU AGPL v3.
97 lines
3.9 KiB
Python
97 lines
3.9 KiB
Python
#!/usr/bin/env python3
|
|
"""Generate the ThreadCount Staff launcher icon.
|
|
|
|
python3 scripts/make-staff-icon.py
|
|
|
|
The two apps have to be the same family and still be telling apart on a home screen, where the
|
|
only other clue is a truncated label. So this is the counter app's mark exactly — four stepped
|
|
bars in the 60/48/34/22 proportions from tc-icon.svg — inverted onto ink: the leading bar keeps
|
|
the accent, the rest go from ink to paper, and the ground goes from paper to ink.
|
|
|
|
Light tile versus dark tile is the strongest difference available at 48px, it costs no new
|
|
artwork, and it stays on-brand: the app's own splash is already ink.
|
|
|
|
The geometry is measured from the counter app's own PNGs rather than guessed, and expressed as
|
|
fractions of the canvas so every density comes out identical rather than merely similar. Bars are
|
|
drawn at 4x and downsampled, which is what keeps a 43-pixel-wide bar from landing half a pixel
|
|
off at mdpi.
|
|
"""
|
|
import os
|
|
from PIL import Image
|
|
|
|
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
RES = os.path.join(ROOT, "android-staff/app/src/main/res")
|
|
|
|
INK = (32, 30, 29, 255)
|
|
PAPER = (243, 242, 242, 255)
|
|
ACCENT = (236, 48, 19, 255)
|
|
|
|
# Measured from android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png (432px):
|
|
# bars start at x=129, widths 174/139/98/64, first at y=147, pitch 40, height 23.
|
|
FG = {
|
|
"x0": 129 / 432,
|
|
"widths": [174 / 432, 139 / 432, 98 / 432, 64 / 432],
|
|
"y0": 147 / 432,
|
|
"pitch": 40 / 432,
|
|
"height": 23 / 432,
|
|
}
|
|
# Measured from .../mipmap-xxxhdpi/ic_launcher.png (192px): the legacy tile crops tighter, so the
|
|
# mark sits larger in frame than it does inside the adaptive icon's safe zone.
|
|
LEGACY = {
|
|
"x0": 38 / 192,
|
|
"widths": [116 / 192, 93 / 192, 66 / 192, 43 / 192],
|
|
"y0": 47 / 192,
|
|
"pitch": 27 / 192,
|
|
"height": 21 / 192,
|
|
}
|
|
|
|
SS = 4 # supersample factor
|
|
|
|
|
|
def draw(size, geom, background):
|
|
"""The mark at `size` px. `background` None gives a transparent adaptive foreground."""
|
|
n = size * SS
|
|
im = Image.new("RGBA", (n, n), background or (0, 0, 0, 0))
|
|
px = Image.new("RGBA", (1, 1)) # placeholder so the linter sees px used below
|
|
del px
|
|
for i, wfrac in enumerate(geom["widths"]):
|
|
x0 = round(geom["x0"] * n)
|
|
y0 = round((geom["y0"] + i * geom["pitch"]) * n)
|
|
w = round(wfrac * n)
|
|
h = round(geom["height"] * n)
|
|
# The leading bar carries the accent; the rest are paper, because the ground is now ink.
|
|
colour = ACCENT if i == 0 else PAPER
|
|
im.paste(colour, (x0, y0, x0 + w, y0 + h))
|
|
return im.resize((size, size), Image.LANCZOS)
|
|
|
|
|
|
DENSITIES = {"mdpi": (48, 108), "hdpi": (72, 162), "xhdpi": (96, 216), "xxhdpi": (144, 324), "xxxhdpi": (192, 432)}
|
|
|
|
|
|
def main():
|
|
if not os.path.isdir(RES):
|
|
raise SystemExit(f"no android-staff res dir at {RES}")
|
|
for density, (legacy_px, fg_px) in DENSITIES.items():
|
|
d = os.path.join(RES, f"mipmap-{density}")
|
|
os.makedirs(d, exist_ok=True)
|
|
|
|
draw(fg_px, FG, None).save(os.path.join(d, "ic_launcher_foreground.png"))
|
|
tile = draw(legacy_px, LEGACY, INK)
|
|
tile.save(os.path.join(d, "ic_launcher.png"))
|
|
# The launcher applies its own mask, so the round variant is the same tile — which is what
|
|
# the counter app ships too.
|
|
tile.save(os.path.join(d, "ic_launcher_round.png"))
|
|
print(f" {density}: foreground {fg_px}px, launcher {legacy_px}px")
|
|
|
|
bg = os.path.join(RES, "values/ic_launcher_background.xml")
|
|
with open(bg, "w", encoding="utf-8") as f:
|
|
f.write('<?xml version="1.0" encoding="utf-8"?>\n<resources>\n'
|
|
' <!-- Ink, where the counter app is paper. This is what tells the two apps\n'
|
|
' apart on a home screen. -->\n'
|
|
' <color name="ic_launcher_background">#201E1D</color>\n</resources>\n')
|
|
print(f" background -> #201E1D")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|