Files
Tahgibu/tools/gen_png_fixtures.py
T
2026-10-24 12:01:52 -03:00

76 lines
3.6 KiB
Python

#!/usr/bin/env python3
"""Genera tests/fixtures_png.h con PNG producidos por una implementación
INDEPENDIENTE (Pillow) y los píxeles RGBA esperados, para comprobar que nuestro
decodificador entiende PNG reales y no solo los que escribe Tahgibu.
Uso: python3 tools/gen_png_fixtures.py > tests/fixtures_png.h"""
import io, struct, zlib
from PIL import Image
W, H = 4, 3
def png_bytes(img, **kw):
b = io.BytesIO(); img.save(b, "PNG", **kw); return b.getvalue()
def expected(img):
rgba = img.convert("RGBA")
out = []
for y in range(H):
for x in range(W):
r, g, b, a = rgba.getpixel((x, y))
if a == 0: r = g = b = 0 # forma canónica de Tahgibu
out += [r, g, b, a]
return out
def pat(f):
return [f(x, y) for y in range(H) for x in range(W)]
fixtures = []
# RGBA 8
im = Image.new("RGBA", (W, H)); im.putdata(pat(lambda x, y: (x * 60, y * 100, 200 - x * 10, 255 if (x + y) % 3 else (x + y) * 20)))
fixtures.append(("rgba8", png_bytes(im), expected(im)))
# RGB 8
im = Image.new("RGB", (W, H)); im.putdata(pat(lambda x, y: (x * 50, y * 90, 17 * (x + y))))
fixtures.append(("rgb8", png_bytes(im), expected(im)))
# Gris 8
im = Image.new("L", (W, H)); im.putdata(pat(lambda x, y: x * 40 + y * 10))
fixtures.append(("gray8", png_bytes(im), expected(im)))
# Gris + alfa
im = Image.new("LA", (W, H)); im.putdata(pat(lambda x, y: (x * 30 + 5, 255 - y * 100)))
fixtures.append(("graya8", png_bytes(im), expected(im)))
# Paleta con transparencia (tRNS)
im = Image.new("P", (W, H)); pal = []
for i in range(4): pal += [i * 60, 255 - i * 60, i * 20]
im.putpalette(pal + [0] * (768 - len(pal))); im.putdata(pat(lambda x, y: (x + y) % 4))
fixtures.append(("palette_trns", png_bytes(im, transparency=bytes([255, 128, 0, 255])), None))
pm = Image.open(io.BytesIO(fixtures[-1][1])).convert("RGBA")
fixtures[-1] = (fixtures[-1][0], fixtures[-1][1], [v for y in range(H) for x in range(W) for v in ((0,0,0,0) if pm.getpixel((x,y))[3]==0 else pm.getpixel((x,y)))])
# 1 bit
im = Image.new("1", (W, H)); im.putdata(pat(lambda x, y: 255 if (x + y) % 2 else 0))
fixtures.append(("bilevel", png_bytes(im), expected(im)))
# Paleta de 2 bits (4 colores) con filtros variados
im = Image.new("P", (W, H)); im.putpalette([255,0,0, 0,255,0, 0,0,255, 255,255,0] + [0]*756); im.putdata(pat(lambda x, y: (x * 2 + y) % 4))
fixtures.append(("palette2bit", png_bytes(im, bits=2), None))
pm = Image.open(io.BytesIO(fixtures[-1][1])).convert("RGBA")
fixtures[-1] = (fixtures[-1][0], fixtures[-1][1], [v for y in range(H) for x in range(W) for v in pm.getpixel((x,y))])
# Gris 16 bits
im = Image.new("I;16", (W, H)); im.putdata(pat(lambda x, y: x * 4000 + y * 9000))
data = png_bytes(im)
exp = []
for y in range(H):
for x in range(W):
v = (x * 4000 + y * 9000) >> 8
exp += [v, v, v, 255]
fixtures.append(("gray16", data, exp))
print("/* GENERADO por tools/gen_png_fixtures.py con Pillow: no editar a mano. */")
print("#ifndef TGB_FIXTURES_PNG_H\n#define TGB_FIXTURES_PNG_H\n#include <stddef.h>\n#include <stdint.h>")
print("#define FIX_W %d\n#define FIX_H %d" % (W, H))
for name, data, exp in fixtures:
print("static const uint8_t fix_%s[] = {%s};" % (name, ",".join(str(b) for b in data)))
print("static const uint8_t fix_%s_px[] = {%s};" % (name, ",".join(str(v) for v in exp)))
print("typedef struct { const char *name; const uint8_t *png; size_t len; const uint8_t *px; } png_fixture;")
print("static const png_fixture png_fixtures[] = {")
for name, data, exp in fixtures:
print(' {"%s", fix_%s, sizeof fix_%s, fix_%s_px},' % (name, name, name, name))
print("};\n#endif")