Skip to content

Imaging: WicFrameInfo.HasResolution and palettes for Encode - #83

Merged
Lakritzator merged 2 commits into
masterfrom
feature/wic-has-resolution
Nov 2, 2026
Merged

Lakritzator merged 2 commits into
masterfrom
feature/wic-has-resolution

Conversation

@Lakritzator

Copy link
Copy Markdown
Member

Summary

WicFrameInfo.HasResolution: true when the file itself stores a resolution, false when DpiX/DpiY are only the default of the decoder. The rules per container are in Internal/ResolutionProbe.cs:

Container Rule
PNG /pHYs/UnitSpecifier through the query reader, otherwise a scan of the chunk headers up to IDAT; unit 1 (meter) → true, unit 0 or no pHYs → false
JPEG JFIF /app0/{ushort=1} 1 or 2, or EXIF /app1/ifd/{ushort=282} with {ushort=296} not 1 (missing = inch)
TIFF, DNG /ifd/{ushort=282} with 296 not 1
JPEG XR WIDTH_RESOLUTION (0xBC82, T.832) in the container IFD, read from the file
BMP biXPelsPerMeter (byte 38) not 0, BITMAPINFOHEADER or larger
GIF, ICO, DDS false
others an IFD block (found by its metadata format in the enumerated items) with XResolution and ResolutionUnit not 1

It's filled by GetFrameInfo and every Decode overload from the frame that is open anyway. The stream position is restored after the few byte reads.

Found on CI: the WIC JPEG decoder ignores the EXIF resolution. It reported 96 for a JPEG with only EXIF XResolution 72. When the resolution comes only from EXIF (JPEG without JFIF density, and the "others" rule), DpiX/DpiY now carry the EXIF values (unit cm converted), so HasResolution == true always comes with the file's own DPI.

Palette for Encode

  • WicEncodeOptions.Palette (≤ 256 colors as 0xAARRGGBB, alpha 0 = transparent), WicEncodeOptions.Dither (error diffusion), and WicPixelFormats.Format1bppIndexed to Format8bppIndexed.
  • Indexed pixels use the given palette (the source answers CopyPalette). Other pixels with a palette ask the encoder for 8bpp indexed and get the nearest color.
  • GIF without a palette: at most 256 colors and no partial alpha → exactly those colors; otherwise InitializeFromBitmap as before.
  • Also found on CI: the WIC format converter maps to a custom palette approximately (an exact color came out as a neighbor). The mapping is now done by WicIndexingPixelSource (exact match, otherwise nearest RGB, alpha < 128 → transparent entry). The WIC converter is only used for dithering.

Docs (resolution table, palette), changelog [3.19.0] and version 3.19.

Tests

WicResolutionTests: the files are made in the test (WIC encode, then the PNG/JPEG/BMP resolution fields are patched in the bytes, PNG with CRC). It covers PNG pHYs 72 / none / unit 0, JPEG JFIF inch / units 0 / EXIF-only 72 / EXIF unit none, TIFF, BMP 3780 and 0, GIF, JPEG XR, WebP (a 1x1 VP8L, skipped without the WebP codec), the Decode overloads, a stream at an offset, the GIF round trip with 256 colors, indexed 8bpp with a palette to GIF/PNG/BMP, nearest color, and > 256 colors with and without dither.

CI: net10 634 passed / 4 skipped, net48 636 passed / 4 skipped. The skips are HEIF, JPEG XL, WebP and a UI Automation test.


Generated by Claude Code

HasResolution tells a resolution stored in the file apart from the default
of the decoder, with the rules per container in ResolutionProbe.
WicEncodeOptions.Palette and Dither, indexed pixel formats, and GIF with
the exact colors when the pixels have at most 256.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017qu4NGxUZizEKzrG8d2DGY
The WIC format converter picked a neighbor of exact palette colors, the pixels
are now mapped by WicIndexingPixelSource. The JPEG decoder ignores the EXIF
resolution, DpiX/DpiY take it when the file stores only that.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017qu4NGxUZizEKzrG8d2DGY
@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@Lakritzator Lakritzator self-assigned this Nov 2, 2026
@coveralls

Copy link
Copy Markdown

Coverage Report for CI Build 41463335738

Coverage increased (+0.7%) to 54.961%

Details

  • Coverage increased (+0.7%) from the base build.
  • Patch coverage: 61 uncovered changes across 3 files (282 of 343 lines covered, 82.22%).
  • No coverage regressions found.

Uncovered Changes

File Changed Covered %
src/Dapplo.Windows.Imaging/Internal/ResolutionProbe.cs 168 132 78.57%
src/Dapplo.Windows.Imaging/Internal/WicPixelSource.cs 64 49 76.56%
src/Dapplo.Windows.Imaging/WicImage.cs 102 92 90.2%
Total (6 files) 343 282 82.22%

Coverage Regressions

No coverage regressions found.


Coverage Stats

Coverage Status
Relevant Lines: 11741
Covered Lines: 6667
Line Coverage: 56.78%
Relevant Branches: 6512
Covered Branches: 3365
Branch Coverage: 51.67%
Branches in Coverage %: Yes
Coverage Strength: 1053.37 hits per line

💛 - Coveralls

@Lakritzator
Lakritzator merged commit f4589e4 into master Nov 2, 2026
12 of 13 checks passed
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.

4 participants