Skip to content

gamut-png: bKGD sample values are not rescaled when auto-reduce demotes 16→8 or packs sub-byte grey #501

Description

@justin13888

Summary

with_background_gray(u16) / with_background_rgb(u16, u16, u16) take sample values in the input's depth. Since #338, with_auto_reduce(true) may write the image at a lower depth than the input — 16-bit demoted to 8-bit when every sample is k·257, 8-bit grey packed to 1/2/4 bits when every sample is a multiple of the depth's scale — and the bKGD sample is not rescaled with the samples.

PR #485 makes bKGD and sBIT follow the colour type actually written and omits a sample the written depth cannot hold (libpng rejects value >= 1 << depth), so the chunk libpng drops is no longer emitted. What remains is the semantic gap inside the accepted range:

  • a 16-bit source whose samples all demote, with a background of (200, 200, 200) (16-bit, near black), is written at depth 8 with bKGD = (200, 200, 200) — now 78% grey;
  • an 8-bit grey source packed to depth 2 with a background of 170 (a legal sample, 2 × 85) has its bKGD omitted rather than written as code 2, because the writer cannot tell an 8-bit 170 that needs scaling from a depth-2 170 that is out of range.

Fix shape

ancillary::bkgd_for needs the source depth as well as the written one, so it can apply exactly the sample mapping reduce applied: v / 257 when v % 257 == 0 for 16→8, v / scale(depth) when divisible for sub-byte grey, and omission otherwise. That is a source_depth: u8 threaded through write_reduced_or_nativewrite_reducedwrite_png (the callers all know it: encode_8bit/encode_alpha8 are 8, encode_16bit/encode_alpha16 are 16, encode_indexed8 is 8). sBIT does not need it — a significant-bit count is absolute, and the depth check already covers it.

Why not in #485

The review finding that PR repaired names the colour type; this is the depth axis of the same defect, pre-dating the race (#338), and its repair widens four signatures the finding does not name. Recorded there as a residual.

Test

Oracle-free exact-byte: encode a demotable Rgb16 image with a k·257 background and assert bKGD carries k; encode a packable Gray8 image with a background of 170 and assert bKGD = [0, 2] at depth 2. Both through libpng's decode for the file around the chunk.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions