Skip to content

Sniff stream signature to preserve sub-format detection - #540

Merged
davidbyttow merged 1 commit into
masterfrom
stream-subformat-sniff
Aug 7, 2026
Merged

Sniff stream signature to preserve sub-format detection#540
davidbyttow merged 1 commit into
masterfrom
stream-subformat-sniff

Conversation

@davidbyttow

@davidbyttow davidbyttow commented Aug 7, 2026

Copy link
Copy Markdown
Owner

Summary

Follow-up to #539, fixing the issue Macroscope flagged there: the streaming load derived the image type from the loader nickname alone, so AVIF (via heifload_source) reported ImageTypeHEIF, and BMP/PSD (via magickload_source) reported ImageTypeMagick with the original format lost. The buffer path detects these from the file signature — and ExportNative on a streamed AVIF would have picked the HEIC encoder.

Fix: sniff the first 12 bytes before loading (vips_source_sniff buffers and rewinds, so nothing is consumed, and it works for every source kind at that point) and refine the ambiguous HEIF/MAGICK loader types through the existing DetermineImageType, mirroring vipsLoadFromBuffer's current/original format split exactly.

Tests

  • New TestLoadImageFromReader_SubFormatDetection: for avif-8bit.avif, bmp.bmp, and psd.example.psd, streaming Format()/OriginalFormat() must equal the buffer path's. BMP/PSD verified locally; AVIF exercises on CI (local HEIF module is broken — missing libx265).
  • Full streaming suite locally: identical results to the pre-change baseline (only the known environmental HEIC failures).

🤖 Generated with Claude Code

Note

Sniff stream signature to preserve sub-format detection in LoadImageFromReader

  • Adds a source_sniff_header C helper in stream.c that reads up to N bytes from a VipsSourceCustom without consuming the stream, using vips_source_sniff and memcpy.
  • LoadImageFromReader in stream.go now sniffs up to 12 bytes before loading, then uses DetermineImageType to refine OriginalFormat when the loader reports HEIF or Magick (e.g. resolving to AVIF, BMP, or PSD).
  • Format is also set to the sniffed sub-format when the detected type does not require Magick, matching the behavior of LoadImageFromBuffer.
  • A new test in stream_test.go asserts that Format and OriginalFormat agree between the buffer and reader load paths for AVIF, BMP, and PSD inputs.

Macroscope summarized 14a9aee.

The streaming load derived the image type from the loader nickname
alone, so AVIF (loaded via heifload) reported ImageTypeHEIF and
BMP/PSD (loaded via magickload) reported ImageTypeMagick with the
original format lost. The buffer path detects these from the file
signature; ExportNative on a streamed AVIF would pick the HEIC encoder.

Sniff the first 12 bytes up front (vips_source_sniff buffers and
rewinds, so nothing is consumed) and refine the HEIF/MAGICK loader
types through DetermineImageType, mirroring vipsLoadFromBuffer's
current/original format split.

Flagged by Macroscope on #539.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@davidbyttow
davidbyttow merged commit 33bd986 into master Aug 7, 2026
3 checks passed
@davidbyttow
davidbyttow deleted the stream-subformat-sniff branch August 7, 2026 15:43
davidbyttow added a commit that referenced this pull request Aug 8, 2026
Setup, commands, package layout, and the conventions that have caused
real bugs when broken: libvips input-ref ownership (#531), band-count
background vectors (#534), libvips version guards for new properties
(#535), signature-based format detection over loader nicknames (#540).
Also documents the golden-file scheme, leak detector, byte-identity
requirement for streaming savers, and known macOS-local test failures.

CLAUDE.md now defers to AGENTS.md for shared content and keeps only the
Claude-specific worktree flow.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant