CNA-GAP-066: A runtime .cnj Texture2D colorKey leaves keyed texels as straight-alpha (key RGB, alpha 0), so they are invisible only under a non-premultiplied blend state

CNA snapshot 009d40f5  ·  Known Issues › Functional gaps  ·  source links pinned to 009d40f5

✓

Evidence basis: source-verified at the pinned commit; tests exist (not executed for this page). Claims on this page were checked by reading the CNA source at commit 009d40f5; unless a sentence says otherwise, nothing here was built or executed. Nothing on this page was executed unless the Evidence section says so.

The loose-tier .cnj Texture2D reader writes Color(keyR, keyG, keyB, 0) into a straight-alpha texture, deliberately: plans/plan_cnj.md CNB-9 defines the result as transparent with alpha 0, and CnjSourceFileTests pins it. Under SpriteBatch's default premultiplied AlphaBlend such a texel adds the key colour instead of vanishing; the compiled route premultiplies after keying and clears it.

Identifier
CNA-GAP-066
Category
Functional gap
Subsystem
Content & XNB/CNB/CNJ
Status
Open
Verified against
CNA 009d40f5 (009d40f5dd085c4e674d3479675fac84b12b3e0a)
Evidence basis
Source-verified: read at TARGET, not executed
Tests touching this area
Yes: see Current tests
Affected contract
ContentManager::Load<Texture2D>(name) for a .cnj Texture2D document with colorKey; SpriteBatch::Begin() default BlendState::AlphaBlend

Expected behaviour

A colour-keyed texel is not visible when the texture is drawn with SpriteBatch::Begin()'s default blend state, as it is when the same document is compiled to .cnb.

Actual behaviour at TARGET

ApplyColorKey in ContentManager.cpp replaces each matching texel with Color(keyR, keyG, keyB, 0) and does not premultiply; the texture is straight alpha like every loose-loaded image. BlendState::AlphaBlend (One, InverseSourceAlpha) then adds the key colour to the destination inside the sprite's rectangle. BlendState::NonPremultiplied hides the texel. The pipeline comment in Texture2DContentPipeline.cpp records that an authored key keeps its colour on purpose.

Source locations

Evidence

Checked by reading at 009d40f5; not executed; the draw result follows from the blend equation. The pipeline comment records a deliberate difference (a build-requested key clears the colour, a .cnj-authored key keeps it, XNAP-96); on the compiled route premultiplication then clears it, so the runtime route is the only place where the kept colour reaches a draw. CnjCacheIsolationTests.cpp asserts cache isolation and alpha only.

Independent re-verification: Read at 009d40f5; not executed. The original entry omitted CnjSourceFileTests.cpp (ColorKeyMakesMatchingPixelsTransparentOthersUnchanged), which asserts Color(255, 0, 255, 0) for keyed pixels, and plan_cnj.md CNB-9, which specifies 'magenta pixels become transparent (alpha 0)'. Writing Color(0,0,0,0) in ApplyColorKey would need that test changed. No CNA document promises invisibility under the default blend state for runtime-loaded straight-alpha textures.

Focused reproduction

// Illustrative; not compiled or run. sprite.cnj: {"sourceFile": "sprite.png", "colorKey": [255, 0, 255]}
auto texture = content.Load<Texture2D>("sprite");
spriteBatch.Begin();                 // BlendState::AlphaBlend
spriteBatch.Draw(&texture, Vector2(0, 0), Color::White);
spriteBatch.End();
// Expected: keyed texels invisible. Actual by reading: background + (255, 0, 255).

Current tests

CnjCacheIsolationTests.cpp (keyed alpha and cache isolation); no draw test of a keyed .cnj texture.

Regression test

Load a keyed .cnj texture, draw it with the default SpriteBatch::Begin() over a known background, and read back: the keyed area must equal the background. The fix is writing Color(0, 0, 0, 0) in ApplyColorKey (or premultiplying on this route).

Blast radius

Runtime-loaded .cnj Texture2D documents with colorKey drawn with a premultiplied blend state, typically ports of colour-keyed 2D games. Build-requested keys and compiled .cnb textures are unaffected.

Workaround

Draw such textures with BlendState::NonPremultiplied, or compile the document to .cnb.

The same subject is explained at several altitudes. These are the neighbouring pages at each one.