zcatgui/docs/research/TTF_DEBUG_SESSION_2025-12-17.md
reugenio 7a67a3b1ea docs: Actualizar estado TTF → zcatttf como solución
- CLAUDE.md: Bug TTF pausado, solución es nueva librería zcatttf
- TTF_DEBUG_SESSION: Documentar intentos adicionales y decisión final
- Proyectos relacionados: añadir zcatttf, zsimifactu, zcatp2p
- ttf.zig marcado como CONGELADO hasta integrar zcatttf

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-16 22:19:36 +01:00

7.6 KiB

Sesión de Debugging TTF - 17 Diciembre 2025

Estado: PAUSADO → Nueva librería zcatttf en desarrollo Problema: Texto TTF corrupto (garabatos + colores raros) en zsimifactu Proyectos afectados: zcatgui, zsimifactu Resolución: Reimplementar TTF desde cero en zcatttf (otra conversación)


1. CONTEXTO INICIAL

Síntomas reportados

  • Todo el texto TTF se ve como garabatos ilegibles
  • Colores raros/incorrectos en el texto
  • Afecta a TODO el texto, incluyendo ASCII básico (A-Z, a-z, 0-9)
  • El problema aparece en zsimifactu, no en tests aislados

Historial previo (sesiones anteriores)

Fecha Intento Resultado
2025-12-16 Cambiar AdwaitaSans → DroidSans No resolvió
2025-12-16 Y-flip en rasterización No resolvió
2025-12-16 UTF-8 decode en drawText No resolvió

Commit relevante

9a2beab wip: TTF diagnóstico - test aislado funciona, integración NO

Este commit indica que un test aislado con DroidSans funcionaba, pero la integración en zsimifactu no.


2. INVESTIGACIÓN (17 Dic 2025)

2.1 Revisión del código con perspectiva fresca

Archivos revisados:

  • src/render/ttf.zig - Parser TTF y renderizado
  • src/render/embedded_font.zig - Fuente DroidSans embebida
  • src/render/framebuffer.zig - Framebuffer y formato de pixel
  • src/render/software.zig - Renderer que usa TTF

2.2 Bug 1 Identificado: Formato Pixel ABGR vs RGBA

Ubicación: ttf.zig líneas 951-955

El problema: El código de alpha blending en drawGlyphBitmap() desempaqueta pixels como RGBA:

const bg = Color{
    .r = @intCast((bg_u32 >> 24) & 0xFF),  // ❌ Esto lee Alpha, no Red
    .g = @intCast((bg_u32 >> 16) & 0xFF),  // ❌ Esto lee Blue, no Green
    .b = @intCast((bg_u32 >> 8) & 0xFF),   // ❌ Esto lee Green, no Blue
    .a = @intCast(bg_u32 & 0xFF),          // ❌ Esto lee Red, no Alpha
};

Pero el framebuffer usa ABGR (verificado en framebuffer.zig:104-108):

const bg = Color{
    .r = @truncate(existing),              // bits 0-7 = R
    .g = @truncate(existing >> 8),         // bits 8-15 = G
    .b = @truncate(existing >> 16),        // bits 16-23 = B
    .a = @truncate(existing >> 24),        // bits 24-31 = A
};

Efecto: Colores incorrectos en el texto renderizado con TTF.

2.3 Bug 2 Sospechado: Tests usan fuente diferente

Hallazgo: Los tests en ttf.zig (línea 1086) cargan AdwaitaSans del sistema:

var font = TtfFont.loadFromFile(allocator, "/usr/share/fonts/adwaita-sans-fonts/AdwaitaSans-Regular.ttf")

Pero zsimifactu usa DroidSans (del sistema o embebida).

Esto significa que los tests NO cubren el caso real de uso. Si DroidSans tiene una estructura cmap diferente, el problema no se detectaría en los tests.

2.4 Archivo de diagnóstico existente

Encontrado src/render/cmap_debug.zig - herramienta de diagnóstico para analizar la tabla cmap de DroidSans. No está integrado en build.zig.


3. ACCIONES CORRECTIVAS

3.1 Fix Bug 1: Formato Pixel ABGR

Archivo: src/render/ttf.zig Líneas: 951-955 Acción: Cambiar interpretación de RGBA a ABGR

Antes:

const bg = Color{
    .r = @intCast((bg_u32 >> 24) & 0xFF),
    .g = @intCast((bg_u32 >> 16) & 0xFF),
    .b = @intCast((bg_u32 >> 8) & 0xFF),
    .a = @intCast(bg_u32 & 0xFF),
};

Después:

const bg = Color{
    .r = @truncate(bg_u32),
    .g = @truncate(bg_u32 >> 8),
    .b = @truncate(bg_u32 >> 16),
    .a = @truncate(bg_u32 >> 24),
};

Estado: PENDIENTE


3.2 Diagnóstico Bug 2: Ejecutar cmap_debug

Acción: Integrar cmap_debug.zig en build.zig y ejecutar para ver:

  • Formato de subtabla cmap que usa DroidSans
  • Mapeo de caracteres (A, B, C... → glyph indices)
  • Verificar si los glyph indices son correctos

Estado: PENDIENTE


4. REGISTRO DE CAMBIOS

Hora Acción Resultado
-- Inicio investigación --
-- Identificado Bug 1 (ABGR) Confirmado
-- Aplicado Fix Bug 1 ttf.zig:951-955 corregido
-- Tests zcatgui Pasan
-- Ejecutado cmap_debug Código base TTF funciona perfecto

5. RESULTADOS DEL DIAGNÓSTICO

Salida de cmap_debug

=== Diagnóstico cmap DroidSans (embebido) ===

Tamaño fuente embebida: 190776 bytes
num_glyphs: 901
units_per_em: 2048
cmap_offset: 22232
glyf_offset: 29196
loca_offset: 27392
index_to_loc_format: 0

cmap version: 0
cmap num_subtables: 3

Subtablas cmap:
  [0] platform=0 encoding=3 offset=28 format=4
  [1] platform=1 encoding=0 offset=804 format=6
  [2] platform=3 encoding=1 offset=1326 format=4

=== Mapeo de caracteres ===
  'A' (0x41) -> glyph 36
  'B' (0x42) -> glyph 37
  'a' (0x61) -> glyph 68
  '0' (0x30) -> glyph 19

=== Verificar glyphs ===
  'A' -> glyph 36, contours=2 bbox=(0,0)-(1245,1468)
  'B' -> glyph 37, contours=3 bbox=(199,0)-(1159,1462)
  'C' -> glyph 38, contours=1 bbox=(125,-20)-(1176,1483)

=== ASCII Art de 'A' ===
Bitmap: 17x20
*#.         *#.
.#*         ##
 ##        .#*
 *#.       ##
 .#*       ##
  ##......*#.
  .#########
   ##     #*
   ##    .#.
   .#.   ##
    ##  .#*
    *#  *#.
........##
     ####.
#########
      ###
      ##.

Conclusión del diagnóstico

EL CÓDIGO BASE TTF FUNCIONA PERFECTAMENTE:

  • Parsing de DroidSans correcto
  • Tabla cmap parseada correctamente (format 4)
  • Mapeo de caracteres correcto ('A'→36, etc.)
  • Extracción de contornos correcta
  • Rasterización correcta (ASCII Art de 'A' es claramente una 'A')

CONCLUSIÓN: El problema NO está en el código base TTF. El único bug identificado es el formato ABGR que ya fue corregido.


6. INTENTOS ADICIONALES (Sesión 17 Dic tarde)

Después del diagnóstico, se probaron más fixes:

Fix Archivo Cambio Resultado
Y-flip bitmap ttf.zig:142 data[(height-1-py)*width+px] ⚠️ Orientación OK, pero "eco"
bearing_x = 0 ttf.zig:150 Normalizar a (0,0) ⚠️ Mejoró, sigue corrupto
bearing_y = y_max ttf.zig:151 Distancia al baseline NO VERIFICADO

Problema persistente: Cada fix mejoraba algo pero introducía nuevos artefactos. El usuario detuvo los intentos por frustración con el ciclo de prueba-error.


7. DECISIÓN FINAL: Nueva librería zcatttf

Consenso alcanzado: En lugar de seguir parcheando código que no entendemos completamente, reimplementar TTF desde cero usando el algoritmo de áreas trapezoidales (como stb_truetype).

Justificación

  1. El código base funciona en tests aislados pero falla en integración
  2. El algoritmo actual (supersampling) tiene bugs difíciles de rastrear
  3. stb_truetype usa un algoritmo más eficiente y mejor documentado
  4. Una librería independiente es más testeable

Nueva librería

  • Proyecto: /mnt/cello2/arno/re/recode/zig/zcatttf/
  • Algoritmo: Áreas trapezoidales (no supersampling)
  • Estado: EN DESARROLLO (otra conversación Claude)
  • Plan: teamdocs/agenda/hitos/2025-12-16_zcatttf_plan_implementacion.md

8. ARCHIVOS MODIFICADOS

Archivo Cambio
src/render/ttf.zig Fix ABGR + Y-flip + bearings (CONGELADO)
src/render/cmap_debug.zig Herramienta de diagnóstico
build.zig Añadido target cmap-debug

9. ESTADO ACTUAL

  • ttf.zig: CONGELADO - No modificar hasta integrar zcatttf
  • zsimifactu: Usa bitmap fonts (funciona)
  • zcatttf: En desarrollo por otra conversación

Documento creado: 2025-12-17 Última actualización: 2025-12-17 - Decisión: Nueva librería zcatttf