PNG: Portable Network Graphics — Análisis Técnico Profundo Completo
PNG (Portable Network Graphics) fue creado en 1995 específicamente para reemplazar GIF sin sus limitaciones — sin compresión LZW encumbrada por patentes, soporte completo de color verdadero y transparencia alfa apropiada.
Filosofía de Diseño de PNG
PNG fue diseñado directamente en respuesta a la controversia de patentes LZW que rodeaba a GIF. Los objetivos de diseño clave:
- Sin pérdida: Los valores de píxel se preservan exactamente
- Profundidad de color total: Hasta 16 bits por canal, no solo índices de 8 bits
- Alfa verdadero: Canal alfa de 8 o 16 bits (256 niveles de transparencia)
- Extensible: La arquitectura basada en chunks permite agregar metadatos nuevos
Estructura del Fichero PNG: Chunks
Estructura del Fichero PNG
├── Firma PNG (8 bytes): 0x89 'P' 'N' 'G' '\r' '\n' 0x1A '\n'
│
├── Chunk IHDR (debe ser el primero) — Cabecera de Imagen
│ ├── Ancho (4 bytes)
│ ├── Alto (4 bytes)
│ ├── Profundidad de bits: 1, 2, 4, 8, o 16
│ └── Tipo de color:
│ 0 = Escala de grises
│ 2 = Color verdadero (RGB)
│ 3 = Color indexado (paleta)
│ 4 = Escala de grises + alfa
│ 6 = Color verdadero + alfa (RGBA) ← Más común para gráficos web
│
├── [Chunks auxiliares opcionales]
│ ├── iCCP — Perfil ICC incrustado
│ ├── sRGB — Declaración de espacio de color sRGB
│ ├── pHYs — Dimensiones físicas de píxel (DPI)
│ ├── tEXt/iTXt — Metadatos de texto
│ └── tRNS — Transparencia (para PNG sin canal alfa)
│
├── Chunk PLTE (requerido para color indexado)
│ └── Entradas de paleta RGB
│
├── Chunk(s) IDAT — Datos de Imagen (comprimidos con DEFLATE)
│
└── Chunk IEND — Fin de imagen (siempre el último)
Tipos de Color PNG y Profundidades de Bits
| Tipo de color | Valor | Prof. de bits permitidas | Canales | Uso |
|---|---|---|---|---|
| Escala de grises | 0 | 1, 2, 4, 8, 16 | 1 | Escaneos, máscaras |
| Color verdadero | 2 | 8, 16 | 3 (RGB) | Fotos sin alfa |
| Indexado | 3 | 1, 2, 4, 8 | 1+PLTE | Iconos, gráficos pequeños |
| Grises+Alfa | 4 | 8, 16 | 2 | Escala de grises con transparencia |
| RGBA | 6 | 8, 16 | 4 | Gráficos web, superposiciones |
La mayoría de los gráficos web usan tipo de color 6 (RGBA), profundidad de bits 8.
Filtrado PNG: Transformación Pre-compresión
PNG aplica un filtro reversible a cada línea de exploración antes de la compresión DEFLATE:
| Filtro | ID | Fórmula | Mejor para |
|---|---|---|---|
| Ninguno | 0 | Filt(x) = Orig(x) |
Color indexado, paleta |
| Sub | 1 | Filt(x) = Orig(x) - Orig(x-1) |
Degradados horizontales |
| Arriba | 2 | Filt(x) = Orig(x) - Anterior(x) |
Degradados verticales |
| Promedio | 3 | Promedio de izquierda y arriba | Fotos |
| Paeth | 4 | Predictor Paeth (usa izquierda, arriba, arriba-izquierda) | General (generalmente mejor) |
Trabajar con PNG en Python
from PIL import Image, PngImagePlugin
import struct, zlib
# ── Operaciones básicas PNG ──────────────────────
with Image.open('entrada.png') as img:
print(f"Tamaño: {img.size}, Modo: {img.mode}")
# Acceder a datos de píxel
pixeles = img.load()
r, g, b, a = pixeles[100, 100] # Valores RGBA en (x=100, y=100)
# Convertir a tipo de color diferente
img.convert('RGB').save('sin_alfa.png')
img.convert('L').save('escala_grises.png')
# Guardar con compresión máxima
img.save('comprimido.png', optimize=True, compress_level=9)
# ── Crear PNG con metadatos ──────────────────────
img = Image.new('RGBA', (400, 300), (74, 144, 226, 255))
meta = PngImagePlugin.PngInfo()
meta.add_text("Title", "Imagen de Prueba KaijuConverter")
meta.add_text("Software", "Python Pillow")
img.save('con_metadatos.png', pnginfo=meta, dpi=(96, 96))
# ── Leer chunks PNG directamente ─────────────────
def analizar_chunks_png(ruta_fichero):
"""Leer estructura de chunks PNG sin decodificar datos de imagen."""
chunks = []
with open(ruta_fichero, 'rb') as f:
firma = f.read(8)
assert firma == b'\x89PNG\r\n\x1a\n', "No es un fichero PNG"
while True:
cabecera = f.read(8)
if len(cabecera) < 8: break
longitud, tipo_chunk = struct.unpack('>I4s', cabecera)
tipo_chunk = tipo_chunk.decode('ascii')
datos = f.read(longitud)
crc = struct.unpack('>I', f.read(4))[0]
crc_calculado = zlib.crc32(tipo_chunk.encode() + datos) & 0xFFFFFFFF
chunks.append({'tipo': tipo_chunk, 'longitud': longitud, 'crc_ok': crc == crc_calculado})
if tipo_chunk == 'IEND': break
return chunks
for chunk in analizar_chunks_png('imagen.png'):
estado = '✓' if chunk['crc_ok'] else '✗'
print(f" {estado} {chunk['tipo']:4s} ({chunk['longitud']:,} bytes)")
Herramientas de Optimización PNG
# oxipng — Basado en Rust, muy rápido, excelentes resultados
oxipng --opt max --strip all entrada.png -o salida.png
# pngcrush — Herramienta clásica, prueba muchas estrategias
pngcrush -brute entrada.png salida.png
# pngquant — Reducción de colores con pérdida (reduce RGBA PNG un 60-80%)
pngquant --quality 80-95 --speed 1 entrada.png -o salida-cuantizada.png
# Optimización sin pérdida con zopflipng
zopflipng -m entrada.png salida.png
APNG: Extensión PNG Animado
APNG añade animación a PNG usando tres nuevos tipos de chunk:
- acTL: Control de Animación (recuento de fotogramas, recuento de reproducción)
- fcTL: Control de Fotograma (retraso, dimensiones, operaciones por fotograma)
- fdAT: Datos de Fotograma (datos de píxel comprimidos)
import apng # pip install apng
a = apng.APNG()
for i in range(10):
a.append(apng.PNG.open(f'fotograma_{i:04d}.png'), delay=100)
a.save('animacion.apng')
PNG vs JPEG vs WebP vs AVIF
| Escenario | Mejor formato | Razón |
|---|---|---|
| Foto para web | WebP (o AVIF) | 30-50% más pequeño que PNG sin pérdida |
| Logo con transparencia | PNG o SVG | Sin pérdida + alfa |
| Captura de pantalla | PNG | Sin pérdida, texto exacto |
| Gráfico animado | WebP anim o APNG | Mejor que GIF |
| Impresión/archivo | TIFF o PNG-16 | Sin pérdida, profundidad 16 bits |
Resumen
La arquitectura de chunks de PNG, el sistema de filtros y la compresión DEFLATE lo convierten en el formato definitivo para gráficos web sin pérdida — particularmente todo lo que requiere transparencia precisa. Para optimización, las acciones de mayor impacto son: (1) reducir a la profundidad de bits y tipo de color mínimos necesarios, (2) aplicar cuantización de paleta con pngquant para fotografías, (3) usar oxipng para reducción de tamaño sin pérdida en todos los ficheros PNG antes del despliegue en producción.