Skip to content

fix(png): mask unused grayscale transparency bits - #9644

Merged
oneandonlydean merged 1 commit into
boykopovar:mainfrom
Noxy-HNR:fix/png-gray-transparency
Oct 23, 2026
Merged

oneandonlydean merged 1 commit into
boykopovar:mainfrom
Noxy-HNR:fix/png-gray-transparency

Conversation

@Noxy-HNR

@Noxy-HNR Noxy-HNR commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

What

Fix transparency in 1-, 2- and 4-bit grayscale PNGs when unused bits are set in the tRNS key. The bundled decoder masks the key to eight bits before scaling it. At lower bit depths, that can leave every pixel opaque instead of making the matching sample transparent.

Mask the key to the image's sample depth and correct alpha after a successful decode. This follows PNG section 11.3.1.1 and the key masking in libpng 1.6.43's png_do_expand. The correction runs only for the affected grayscale inputs. The existing decoder's last-key choice is preserved when it accepts duplicate tRNS chunks.

Tested

Windows 11 26H2 (build 10.0.26300), WinLibs GCC 15.2.0 r7, full Release build, head ada8c0b on main d6e7ab4.

  • decoder_png, guest_png_dec and guest_png_enc pass, 3/3.
  • Pixel and alpha checks cover all three low bit depths, canonical keys, unused bits in each byte, all-one keys, missing transparency, truncated metadata, partial packed rows and unchanged input buffers. The original decoder returns opaque black and white for a 2x1 one-bit image with key 0x0002, where the PNG specification requires transparent black and opaque white.
  • jotitadev's Linux run (Ubuntu 26.04, GCC 15.2, Release and ASan/UBSan) passes, and decoder_png aborts with main's decoder.
  • tools/check_conventions.py --base origin/main and tools/check_test_registration.py pass. Merge tests against every open pull request that touches the PNG decoder or libraries find no new conflict.
  • PS5 hardware and title behavior are untested.

Checklist

  • Based on current main; no other open PR implements the same functions
  • One topic per PR; follow-ups go in a new PR
  • No comments in code except technical debt
  • Unimplemented paths throw (NotImplemented_nid_no_patch); silent stubs are listed under Silent stubs in their library's technical debt file
  • No notes, investigation .md files or images added to the repository (attach them to this PR)
  • New third-party code is a submodule built from source
  • Generic behaviour, not specific to one title
  • Depends on: none
  • Fixes: none
  • Unblocks: none
  • GPUs tested: n/a
  • AI-assisted: yes

@MarcZX10

MarcZX10 commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

The reported PNG behavior and the correction are consistent with the specification: for 1/2/4-bit grayscale, only the sample-depth bits of the tRNS key should participate in matching. The post-decode repair is scoped to those grayscale inputs, and the tests cover unused bits, all-one keys, missing/truncated metadata, partial packed rows and input preservation. Current-main python3 tools/check_conventions.py --base upstream/main and whitespace checks pass. The Windows decoder/component tests are author-reported; hosted product jobs were skipped, and I did not reproduce them locally. This remains a draft at the author’s discretion;

@jotitadev jotitadev left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Linux run, since Tested covers Windows only.

Machine: Linux x86-64, Ubuntu 26.04, Intel Core i7-8700, GCC 15.2.
Head: e03bf1864, merged onto main 4777caca3 (clean merge). Full build, BUILD_TESTING=ON.

  • Release, ctest -R '^(decoder_png|guest_png_dec)$': 2/2 pass.
  • Red check: core/Decoder/Png/src/Png.cpp from main with this PR's tests/Png.cpp: decoder_png aborts; guest_png_dec still passes.
  • ANYPS5_ENABLE_ASAN=ON build (-fsanitize=address,undefined, RelWithDebInfo): both pass with no ASan or UBSan reports; the red check aborts the same way.

Written with AI assistance (Claude Code). I ran the builds and tests above on my machine.

@MarcZX10

MarcZX10 commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

Thanks for adding the independent Linux run; the decoder regression passes in Release and under ASan/UBSan, and the red check against main demonstrates the test catches the original behavior. I do not have a new code finding on this unchanged head. Separately, this PR still targets b27924d, while current main is 47 commits ahead at 8b3a6ba; CONTRIBUTING.md requires PRs to be based on current main. If you decide to move this draft forward, please rebase it and rerun the focused PNG tests on the rebased result. The hosted product jobs are skipped by path filters, and I did not run a local decoder build in this session.

@Noxy-HNR
Noxy-HNR force-pushed the fix/png-gray-transparency branch from e03bf18 to bf26afb Compare October 22, 2026 17:58
@Noxy-HNR

Copy link
Copy Markdown
Contributor Author

Rebased onto main b7d1d47. decoder_png, guest_png_dec and guest_png_enc pass, 3/3, on Windows 11 with GCC 15.2.0; conventions and overlap are clean. Tested is updated and links @jotitadev's Linux run. Thanks for it.

@Noxy-HNR
Noxy-HNR force-pushed the fix/png-gray-transparency branch from bf26afb to ada8c0b Compare October 22, 2026 20:11
@Noxy-HNR
Noxy-HNR marked this pull request as ready for review October 22, 2026 20:20
@MarcZX10

Copy link
Copy Markdown
Contributor

I’ve noticed that asking you to hold this PR was unnecessary. It’s fine to keep submitting PRs; maintainers will handle them in due course.

@oneandonlydean oneandonlydean self-assigned this Oct 23, 2026
@oneandonlydean
oneandonlydean merged commit c73dc20 into boykopovar:main Oct 23, 2026
11 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