Where it lives
ColorOperation enum: Engines/FlatRedBallXNA/FlatRedBall/Graphics/GraphicalEmumerations.cs.
It's not Sprite-specific — consumers include:
| Type | File | Notes |
|---|---|---|
Sprite |
Engines/FlatRedBallXNA/FlatRedBall/Sprite.cs |
Full support, all enum values; setter calls UpdateVertexColorsAccordingToAlpha |
AnimationFrame |
Engines/FlatRedBallXNA/FlatRedBall/Graphics/Animation/AnimationFrame.cs |
Nullable per-frame override, applied in Sprite.UpdateToAnimationFrame |
RenderableSkiaObject (Gum/SkiaSharp text, SVG) |
Engines/SkiaGum/Renderables/RenderableSkiaObject.cs |
Hardcoded to Modulate, read-only — does not respect Add/Subtract/etc. |
Renderer global state |
Engines/FlatRedBallXNA/FlatRedBall/Graphics/Renderer.cs |
Static ColorOperation property drives shader technique selection via _effectManager.GetVertexColorTechniqueFromColorOperation |
Gotchas
- Skia-rendered objects (Text/Gum via
RenderableSkiaObject) silently ignore non-Modulate operations — theIRenderableIpso.ColorOperationgetter always returnsModulate, there's no setter. Don't expectAdd/Subtract/etc. to work outside the XNASpriterender path. - Debug-only platform restriction: on iOS/Android/WinRT, setting
Sprite.ColorOperationtoAdd,Subtract,InterpolateColor,InverseTexture,Modulate2X, orModulate4Xthrows in DEBUG builds (Sprite.cs~line 410) — these ops aren't guaranteed cross-platform. "AddSigned"is notColorOperation.AddSubtract. It's a separate legacy string op-code handled only inGraphicalEnumerations.SetColors(GraphicalEmumerations.cs~line 293), which biasesdesiredRed/Green/Blueby-127.5and remaps to plain"Add"— no enum value, noVertexColorPackerinvolvement.ColorOperation.Texturewith a nullTexturebehaves likeColorOperation.Color(seeRenderer.cs~line 3349,Sprite.cs~line 256) — a common "why does my untextured sprite still show color" trip-up.Sprite.Red/Green/Blueclamp to[-1, 1], not[0, 1](Sprite.cs~line 301-304), and nothing downstream re-clamps before GPU upload. Packing (nowVertexColorPacker.Pack, called fromSpriteManager.cs~line 2146) casts the float channel touintvia(uint)(255 * value). A negative float→uint cast here is undefined behavior per ECMA-335 (out-of-range float-to-unsigned conversion is unspecified) and is genuinely runtime-dependent, not just "clamps to 0": on .NET 8/x64 (this repo's actualnet8.0target) it wraps to byte129; on .NET 10 the same code gives0. Either way it is NOT a clean subtraction — never rely on negativeRed/Green/BlueunderColorOperation.Addto produce predictable output; useColorOperation.AddSubtractinstead (bias/scale-encodes the signed value so it survives the UNORM byte round-trip predictably).