Repository navigation
fix: correct green YUV shadows with neutral chroma fallback - #128
Merged
Merged
Conversation
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Problem and reproduction
Users reported a hazy green tint in game output. Starting Bloody Blade from
EPOP/EBBLADE.smf, skipping the video, and confirming the title screen and menus reproduces incorrect green pixels in character shadows and some image edges inNA32SSL/ENGLISH/BBLADE/BBPLAY10.SSL.The reproduced defect affects pixels with unresolved zero chroma. A uniform green tint across the entire screen was not reproduced, so this PR does not establish that every reported tint has the same cause.
The original decoder was run first and a failing regression was recorded. The real image's compressed blocks were then unpacked independently to inspect the original Y/U/V values, followed by a fix and before/after comparisons.
SHA256 of the tested
BBPLAY10.SSL:d8cb739d81559cf4031e9e44386b9cfe17aa69da82f3fb5847c624e447844745.Root cause
Image #636 is a 46×74 character sprite. Independently unpacking its compressed data reveals alternating black/transparent shadow blocks encoded as
Y=[0,16,16,0], U=V=0.The decoder already borrows neighboring nonzero chroma samples vertically and then horizontally. However, if a component remains zero after both steps, that zero is passed directly to the RGB conversion. The existing
unwrap_or(128)handles an absent buffer entry, but not an entry whose value is zero.For an opaque shadow pixel with
Y=16,U=V=0, the conversion usesC=Y-16=0, D=U-128=-128, E=V-128=-128and produces RGB(0,154,0), or#009A00. Black shadow stippling therefore becomes green.Fix
Preserve the existing vertical-then-horizontal chroma recovery. After both steps, substitute neutral chroma 128 independently for each U or V component that is still zero, then apply the existing RGB conversion.
Applying the fallback after recovery preserves usable neighboring colors. Applying it earlier would replace missing samples with nonzero values and prevent the existing recovery from borrowing those colors. When only one component is missing, only that component changes; nonzero components retain their original values.
Y=0remains transparent, whileY=16,U=V=0becomes opaque black.This changes the game-image YUV decoder shared by the standalone frontend and libretro core. ARGB images and MPEG video decoding are unchanged. The fix operates on chroma values rather than removing pixels based on their final RGB color, so legitimate green artwork remains visible.
Results
The 113 pixels in the identified shadow blocks are a subset of the full image, which explains the different counts. Full-image results come from before/after PNG comparisons. The asset regression independently unpacks the shadow blocks and asserts their decoded colors and transparency.
Local captures cover the title screen, menus, first level, and character sprite. The standalone title captures at frame 200 differ by only one pixel. The first-level captures at frame 600 differ in 42 RGB pixels, with no alpha changes. Captures are stored locally under
tmp/color-beforeandtmp/color-afterand are not included in the PR.Regression coverage and validation
Four new unit tests require no external assets and cover literal/repeat compression commands, black/transparent shadows, independently missing U/V components, legitimate green, recovery from all four neighboring directions, and odd dimensions.
The new ignored asset test,
bloody_blade_shadow_and_gameplay, copies the launcher and SSL scenes into a temporary directory to isolate user saves. It runs 1,200 frames fromEPOP/EBBLADE.smf, enters the first level, dismisses dialogue, and moves right. It then independently unpacks image #636 and verifies shadow colors and transparency. Game assets are not distributed with the repository.Completed local checks:
cargo fmt --all -- --checkandgit diff --check: passed.cargo test --workspace: 266 default tests passed; tests requiring game assets remain ignored by default.cargo clippy --workspace --all-targets --all-features -- -D warnings: passed.cargo test -p native32emu-core --test yuv_color -- --ignored --nocapture: passed with real Bloody Blade assets. Before the fix, the regression identified 110 incorrect green shadow pixels and failed.cargo test -p native32emu-core --test smoke smoke_all_games -- --ignored --nocapture: 185 local SMF/SSL files ran for 150 frames each without crashes. TwoESTORM.smflaunchers produced blank-frame warnings. This checks runtime stability, not color correctness in every game.cargo build -p native32emu --release: passed; the updated standalone executable launched Bloody Blade and captured a screenshot successfully.cargo build -p native32emu-libretro --release: passed. The Windows x64 DLL was installed into the local RetroArch core directory after backing up the previous DLL, and its SHA256 matched the build artifact. Manual visual verification in RetroArch is still awaiting results and is not counted as completed validation.To run the asset regression in PowerShell:
Interpretation and limitations
The original compressed blocks, the failing regression on the old decoder, and before/after pixel comparisons support this correction. Treating unresolved zero chroma as missing color remains an interpretation supported by these assets, rather than a rule confirmed by original hardware or an authoritative format specification. If another game uses zero U/V as valid extreme chroma, its output would be affected by this fallback. Neutral fallback preserves luminance but cannot reconstruct missing original color information.
This PR addresses the confirmed shadow/edge defect; it does not establish the cause of every report of a uniform green tint. Only the decoder fix and regression tests are included. The reproduction, explanation, results, and limitations are documented here without adding
docs/YUV-Color-Fix.md.