sergio/takana
> Una distribución Linux construida con un laboratorio funcional y hermético en el > sótano, y una terminal mutable, clásica y anárquica en el piso de arriba — diseñada > desde el suelo para que una IA programadora entienda, modifique y co…
recetas: la suite ArtCraft entera (12 apps egui+wgpu) + el arnés que mide si pintan
1#!/usr/bin/env python3
2"""Cuenta colores únicos de un PNG sin PIL (no hay PIL ni en el hub ni en el worker).
3
4 python3 png-colores.py captura.png [antes.png ...]
5
6La medida que no se puede falsear (scripts/wlr/sway-headless.sh): 1 color = sólo el fondo del
7swaybg ⇒ NINGÚN cliente dibujó, aunque el árbol del compositor liste la ventana.
8"""
9import sys, zlib, struct
10from collections import Counter
11
12
13def leer_png(ruta):
14 d = open(ruta, "rb").read()
15 assert d[:8] == b"\x89PNG\r\n\x1a\n", "no es un PNG"
16 i, idat, ihdr = 8, b"", None
17 while i < len(d):
18 ln, tipo = struct.unpack(">I4s", d[i:i + 8])
19 cuerpo = d[i + 8:i + 8 + ln]
20 if tipo == b"IHDR":
21 ihdr = struct.unpack(">IIBBBBB", cuerpo)
22 elif tipo == b"IDAT":
23 idat += cuerpo
24 elif tipo == b"IEND":
25 break
26 i += 12 + ln
27 w, h, prof, color, _, _, entre = ihdr
28 assert prof == 8 and entre == 0, f"sólo 8 bits sin entrelazado (prof={prof} entrelazado={entre})"
29 canales = {0: 1, 2: 3, 4: 2, 6: 4}[color]
30 cruda, bpp, salida, prev = zlib.decompress(idat), canales, [], bytearray(w * canales)
31 pos = 0
32 for _ in range(h):
33 filtro = cruda[pos]; pos += 1
34 linea = bytearray(cruda[pos:pos + w * canales]); pos += w * canales
35 for x in range(len(linea)):
36 a = linea[x - bpp] if x >= bpp else 0
37 b = prev[x]
38 c = prev[x - bpp] if x >= bpp else 0
39 if filtro == 1: linea[x] = (linea[x] + a) & 255
40 elif filtro == 2: linea[x] = (linea[x] + b) & 255
41 elif filtro == 3: linea[x] = (linea[x] + (a + b) // 2) & 255
42 elif filtro == 4:
43 p = a + b - c
44 pa, pb, pc = abs(p - a), abs(p - b), abs(p - c)
45 linea[x] = (linea[x] + (a if pa <= pb and pa <= pc else b if pb <= pc else c)) & 255
46 salida.append(bytes(linea)); prev = linea
47 return w, h, canales, salida
48
49
50for ruta in sys.argv[1:]:
51 w, h, canales, filas = leer_png(ruta)
52 c = Counter()
53 for f in filas:
54 for x in range(0, len(f), canales):
55 c[bytes(f[x:x + 3])] += 1
56 print(f"{ruta}: {w}x{h} colores únicos = {len(c)}")
57 for px, n in c.most_common(6):
58 print(f" #{px.hex()} {n * 100.0 / (w * h):5.1f} %")