Preserve blend modes when flattening/merging layers in indexed color mode - #157
Open
Nidrax wants to merge 3 commits into
Open
Preserve blend modes when flattening/merging layers in indexed color mode#157Nidrax wants to merge 3 commits into
Nidrax wants to merge 3 commits into
Conversation
FlattenLayers pre-filled its working buffer with the background color before rendering, so when flattening onto a visible background layer every pixel looked like real content to the layers' blend functions. Special blend modes then blended against that synthetic backdrop instead of falling back to Normal where the sprite has no content. Render the frame onto an empty backdrop and drop the background color in behind it afterwards, the same way render.cpp handles the checked background. The temporary image also needs its mask color to match the sprite's transparent color, as the indexed blender uses it to tell which source pixels are empty. Refs #156 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FbyTCR1vPETPFq3BhtWfVW
Merging onto a layer without any content of its own copies the source cels verbatim, which is the right result for the pixels (blending against an empty backdrop is Normal blending) but silently dropped the source layer's blend mode and folded its opacity into the cel. The layers under the destination then stopped blending the way they did before the merge. Let the destination take over the source blend mode and opacity when it holds no cels at all, which is the case where merging is just the source layer moved one step down. Refs #156 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FbyTCR1vPETPFq3BhtWfVW
Compositing an indexed image onto another indexed image ignored the blend mode entirely and copied the source indexes over the destination. The editor hid this because it composites indexed sprites into an RGB surface, which does blend through the palette; flattening and merging down composite indexed onto indexed, so every special blend mode was lost and the top layer simply obscured everything beneath it. Compute the blend in RGB through the palette and map the result back to the closest existing entry. Normal and SRC keep the plain index copy, as indexed pixels are either fully opaque or the transparent index and there is nothing to interpolate. The palette itself is left untouched, so the merged result is only as close as its entries allow. Fixes #156 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FbyTCR1vPETPFq3BhtWfVW
Nidrax
force-pushed
the
nidrax/156-fix-layers-blending-when-merging
branch
from
August 27, 2026 10:20
0212174 to
6f7f39c
Compare
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.
Fixes #156.
Flatten and Merge Down lost special blend modes for anything other than a
fully-opaque backdrop. Three independent gaps, fixed as three commits:
FlattenLayerspre-filled the working buffer with the backgroundcolor before rendering, so flattening onto a visible background layer
made every pixel look like real content to the blend functions —
defeating the transparent-backdrop fallback from Blending with transparency should use normal blending #148/Use normal blending when backdrop is fully transparent #154. Now it
renders onto an empty backdrop and composites the background color in
underneath afterwards, matching how
render.cppalready handles thechecked background.
MergeDownLayerCommand, when the destination has no cel at all,copied the source cel verbatim and silently dropped the source layer's
blend mode and opacity. Since that case is really "move the source
layer one step down," the destination now takes over the source's
blend mode and opacity so layers beneath it keep blending the way they
did before the merge.
The real bug:
BlenderHelper<IndexedTraits, IndexedTraits>inrender.cppignored blend mode entirely and just copied sourceindexes over the destination. The editor/export preview never showed
this because it composites indexed sprites into an RGB surface (a
different, blend-aware specialization); Flatten and Merge Down
composite indexed onto indexed, so on indexed sprites every special
blend mode was lost outright — the reported "top layer becomes
Normal-blend and obscures everything beneath it." Fixed by blending in
RGB through the palette and mapping the result back to the closest
existing entry via
Palette::findBestfit. Normal/SRC keep the plainindex-copy fast path. The palette itself is never modified, so results
are only as close as the existing palette allows (documented in the
test).
Added a regression test in
render_tests.cppcovering Normal vs.Multiply blending between two indexed images, including the
falls-back-to-Normal-over-a-transparent-backdrop case. I could not run
the test suite in this environment —
third_party/gtestisn't present inthis checkout — so I verified all assertions by hand-driving the same
call sequence against the built
doc/renderlibraries.Manually retested in the app in both indexed and RGB color modes.
🤖 Generated with Claude Code