tawasuyu

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…

TOML 1430Shell 145Markdown 87Rust 852973 Definitions

⎇ 534014c3ec69daea789a1d0523e1d00a69c6ed65 scripts/png-colores.py Python · 58 lines · 2.3 KB History Who touched what raw

recetas: la suite ArtCraft entera (12 apps egui+wgpu) + el arnés que mide si pintan 584247c3d · Sergio ·

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} %")