Files
amethyst/commons
nrobi144 e6711845a0 feat(commons): add ImageReencoder + AmethystTempDir for image uploads
Phase 1 part B. The core re-encode + downscale pipeline:

  - AmethystTempDir resolves ~/.amethyst/tmp/ at mode 0700 with a
    boot-time sweep of amethyst_* files > 24h. Defends against
    /tmp tmpfs OOM on Linux VMs and against multi-user temp races
    on shared systems. Overridable via -Damethyst.tmp.dir=.

  - ImageReencoder.reencode(File, CompressionQuality) returns a
    sealed ReencodeResult (Reencoded(file) | PassThrough(reason))
    and throws CompressionException for fatal cases.
    * Format sniffer first → pass-through for animated GIF /
      animated WebP / SVG; refuse AVIF / HEIC with UnsupportedFormat.
    * Pre-decode pixel guard: stream header dims via
      ImageReader.getWidth(0)/getHeight(0), refuse > 50 MP before
      any pixel buffer is allocated.
    * Subsampled decode (floor stride) so a 4032×3024 source decodes
      to ~2016×1512 in heap before Thumbnailator's final resize.
    * Never upscale: Thumbnails.of(...).size() is only called when
      the source actually exceeds the target box.
    * CPU-bound work runs on Dispatchers.Default.limitedParallelism(1)
      with ensureActive() between stages.
    * Cleanup on cancellation/failure runs in NonCancellable.

12 unit tests cover preset routing, never-upscale, InputTooLarge,
AVIF/HEIC refused, animated-GIF pass-through, SVG pass-through,
JPEG SOI verification, and temp-file placement under
AmethystTempDir.

ICC profile preservation and the wide-gamut warning path land in
the next commit alongside UploadOrchestrator wiring.
2026-06-09 11:41:59 +03:00
..