#!/usr/bin/env python3 """Generate AgentCheckin app icons as PNG files (pure stdlib, no PIL). Design: rounded gradient tile + calendar outline + check mark (daily check-in). """ import os import struct import zlib BASE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "AgentCheckin") class Canvas: def __init__(self, w, h, scale=4): self.w, self.h, self.s = w, h, scale self.W, self.H = w * scale, h * scale self.px = [(0, 0, 0, 0)] * (self.W * self.H) def _set(self, x, y, rgba): if 0 <= x < self.W and 0 <= y < self.H: i = y * self.W + x r, g, b, a = rgba orr, og, ob, oa = self.px[i] na = a / 255.0 + oa / 255.0 * (1 - a / 255.0) if na == 0: self.px[i] = (0, 0, 0, 0) else: nr = (r * a / 255.0 + orr * oa / 255.0 * (1 - a / 255.0)) / na ng = (g * a / 255.0 + og * g / 255.0 * (1 - a / 255.0)) / na nb = (b * a / 255.0 + ob * oa / 255.0 * (1 - a / 255.0)) / na self.px[i] = (int(nr), int(ng), int(nb), int(na * 255)) def rect(self, x, y, w, h, rgba): for yy in range(int(y), int(y + h)): for xx in range(int(x), int(x + w)): self._set(xx, yy, rgba) def rounded(self, x, y, w, h, rad, rgba): for yy in range(y, y + h): for xx in range(x, x + w): dx = max(x + rad - xx, xx - (x + w - 1 - rad), 0) dy = max(y + rad - yy, yy - (y + h - 1 - rad), 0) if dx * dx + dy * dy <= rad * rad: self._set(xx, yy, rgba) def ring(self, x, y, w, h, rad, thick, rgba): for yy in range(y, y + h): for xx in range(x, x + w): def outside(px, py, shrink): ddx = max(x + rad + shrink - px, px - (x + w - 1 - rad - shrink), 0) ddy = max(y + rad + shrink - py, py - (y + h - 1 - rad - shrink), 0) return ddx * ddx + ddy * ddy > (rad + shrink) * (rad + shrink) if not outside(xx, yy, 0) and outside(xx, yy, thick): self._set(xx, yy, rgba) def line(self, x1, y1, x2, y2, thick, rgba): steps = int(max(abs(x2 - x1), abs(y2 - y1)) * 2) + 1 for i in range(steps + 1): t = i / steps cx = x1 + (x2 - x1) * t cy = y1 + (y2 - y1) * t r = thick / 2.0 for yy in range(int(cy - r) - 1, int(cy + r) + 2): for xx in range(int(cx - r) - 1, int(cx + r) + 2): if (xx - cx) ** 2 + (yy - cy) ** 2 <= r * r: self._set(xx, yy, rgba) def gradient_fill(self, x, y, w, h, rad, c1, c2): for yy in range(y, y + h): t = (yy - y) / max(h - 1, 1) r = int(c1[0] + (c2[0] - c1[0]) * t) g = int(c1[1] + (c2[1] - c1[1]) * t) b = int(c1[2] + (c2[2] - c1[2]) * t) for xx in range(x, x + w): dx = max(x + rad - xx, xx - (x + w - 1 - rad), 0) dy = max(y + rad - yy, yy - (y + h - 1 - rad), 0) if dx * dx + dy * dy <= rad * rad: self._set(xx, yy, (r, g, b, 255)) def save(self, path): s = self.s rows = [] for y in range(self.h): row = bytearray([0]) for x in range(self.w): rs = gs = bs = as_ = 0 cnt = 0 for dy in range(s): for dx in range(s): p = self.px[(y * s + dy) * self.W + (x * s + dx)] rs += p[0]; gs += p[1]; bs += p[2]; as_ += p[3] cnt += 1 row += bytes((rs // cnt, gs // cnt, bs // cnt, as_ // cnt)) rows.append(bytes(row)) raw = b"".join(rows) def chunk(tag, data): c = struct.pack(">I", len(data)) + tag + data return c + struct.pack(">I", zlib.crc32(tag + data) & 0xFFFFFFFF) png = b"\x89PNG\r\n\x1a\n" png += chunk(b"IHDR", struct.pack(">IIBBBBB", self.w, self.h, 8, 6, 0, 0, 0)) png += chunk(b"IDAT", zlib.compress(raw, 9)) png += chunk(b"IEND", b"") with open(path, "wb") as f: f.write(png) def draw_icon(size): c = Canvas(size, size) S = c.s u = S m = int(size * 0.05) * S # gradient tile c.gradient_fill(m, m, c.W - 2 * m, c.H - 2 * m, int(size * 0.22) * S, (59, 108, 255), (122, 90, 248)) # calendar body bx, by = int(size * 0.22) * S, int(size * 0.24) * S bw, bh = int(size * 0.56) * S, int(size * 0.52) * S rad = int(size * 0.09) * S thick = max(int(size * 0.05) * S, 2 * S) c.ring(bx, by, bw, bh, rad, thick, (255, 255, 255, 255)) # hangers hw, hh = int(size * 0.07) * S, int(size * 0.12) * S c.rounded(bx + bw // 2 - int(size * 0.16) * S, by - hh // 2, hw, hh, hw // 2, (255, 255, 255, 255)) c.rounded(bx + bw // 2 + int(size * 0.09) * S, by - hh // 2, hw, hh, hw // 2, (255, 255, 255, 255)) # header line ly = by + int(size * 0.13) * S c.line(bx + thick // 2, ly, bx + bw - thick // 2, ly, thick, (255, 255, 255, 255)) # check mark cx1 = bx + int(size * 0.13) * S cy1 = by + int(size * 0.30) * S t2 = max(int(size * 0.065) * S, 2 * S) c.line(cx1, cy1, cx1 + int(size * 0.09) * S, cy1 + int(size * 0.09) * S, t2, (126, 240, 178, 255)) c.line(cx1 + int(size * 0.09) * S, cy1 + int(size * 0.09) * S, cx1 + int(size * 0.26) * S, cy1 - int(size * 0.11) * S, t2, (126, 240, 178, 255)) return c def main(): paths = [ (64, os.path.join(BASE, "ICON.PNG")), (256, os.path.join(BASE, "ICON_256.PNG")), (64, os.path.join(BASE, "app/ui/images/icon_64.png")), (256, os.path.join(BASE, "app/ui/images/icon_256.png")), ] for size, path in paths: c = draw_icon(size) c.save(path) print("wrote", path, os.path.getsize(path), "bytes") if __name__ == "__main__": main()