opencode-config/docs/decisions/068-pr-161-density-1024.md
Sergey 746bb7438e
fix(draw-image): use density 72 for 1024x1024 PNG output (#161)
* fix(draw-image): use density 72 for 1024x1024 PNG output

* test(draw-image): assert real PNG dimensions via IHDR decode

* chore(assets): regenerate cover.png at 1024x1024

* docs(handoff): add handoff and ADR for density fix

* docs(handoff): set PR number

---------

Co-authored-by: opencode-agent <agent@opencode.local>
2026-07-30 23:31:02 +03:00

2.2 KiB
Raw Permalink Blame History

ADR-068: Use sharp density 72 for pixel-perfect 1024×1024 PNG output

Статус

Accepted (2026-07-30)

Контекст

render.mjs рендерил SVG-шаблон cover (1024×1024 по viewBox) в PNG 1365×1365 вместо ожидаемых 1024×1024. Root cause: sharp(Buffer.from(svg), { density: 96 }) — density в DPI, sharp интерпретирует viewBox 1024 (в user units, по умолчанию 1 unit = 1pt = 1/72in) и рендерит 1024pt × 96 DPI / 72 = 1365px. Тесты assertPng1024 проверяли только PNG magic bytes (0x89 0x50 0x4e 0x47), не размеры — баг маскировался. meta.size: 1024 в cli.ts:78 был контрактом, но фактически нарушался.

Рассматривались варианты:

  • density: 72 → 1024 × 72/72 = 1024px (точное совпадение).
  • density: 96 + resize до 1024 → лишний resample, потеря качества.
  • Явный .resize(1024, 1024) после density → double-pass, медленнее.

Решение

Установить density: 72 в sharp() вызове (render.mjs:36). 72 DPI = canonical SVG default (1pt = 1/72in), даёт pixel-perfect 1:1 маппинг viewBox → px. Дополнительно: assertions в тестах декодируют PNG IHDR chunk (width bytes 16-19, height bytes 20-23, big-endian uint32) и проверяют === 1024 — регрессия на размер теперь детектируется.

Альтернативы

  • density 96 + explicit resize(1024): отвергнут — лишний resample снижает качество текста (subpixel), double-pass рендер.
  • density 96 + viewBox в px вместо pt: потребовало бы правки всех SVG-шаблонов (cover.svg etc.), больший scope, риск regressions в layout.
  • assertions через sharp().metadata() вместо IHDR decode: добавил бы зависимость от sharp в test-слой (сейчас тесты читают bytes напрямую, без dep). IHDR decode — zero-dependency, быстрее.