deck.gl
deck.gl copied to clipboard
[Feat] Improving the getColor method in IconLayer
Target Use Case
Current getColor implementation with mask:true has critical limitations:
- Only works with binary masks - completely replaces all non-transparent pixels with solid color
- Destroys image details - cannot preserve:
- Multi-tone icons (e.g. logos with shadows)
- Built-in color schemes
- Performance overhead - requires generating separate atlases for each color variation
Proposal
Introduce alternative color blending via tint mode when mask:false:
new IconLayer({
getColor: d => [r, g, b, a], // Applies as tint color
iconMapping: {
customIcon: {
mask: false, // Enables tint blending
// Original colors are preserved but blended with tint
}
}
});
tint Mode vs Current mask:true
| Feature | mask:true (Current) |
Proposed tint Mode |
|---|---|---|
| Detail Preservation | ❌ Flat single-color | ✅ Original colors + tint blend |
| Dynamic Updates | ❌ Requires atlas rebuild | ✅ Instant color change |
| Performance | ⚠️ High memory usage | ✅ Low overhead |
| Supported Icons | Only monochrome masks | Any PNG/SVG (multi-color, gradients) |
Thanks for writing up this proposal. This sounds similar to how getColor works in ScenegraphLayer, where [255, 255, 255, 255] preserves the original colors and any other value is blended via multiply.
mask: false behavior is already defined and we wouldn't want to introduce a breaking change for this. From the docs:
If mask = false, only the alpha component will be used to control the opacity of the icon.
mask (boolean, optional): whether icon is treated as a transparency mask. If true, user defined color is applied. If false, pixel color from the image is applied. User still can specify the opacity through getColor. Default: false
So, I don't think mask is the appropriate config for this. Instead I'd suggest adding a new tint: boolean property to the iconMapping. It could be false by default. If true, it blends getColor with the icon's texture color.
Here's the relevant shader.
Backwards compatibility should be preserved, so if mask: true then I think the new tint setting should be ignored. Unless, there's a proposal for how these should interact with each other.
Wdyt @Pessimistress @felixpalmer @ibgreen ?
@mo3g666 would you be able to create a PR for this that includes a icon render test?
This is similar to the autoHighlight issue I raised in: https://github.com/visgl/deck.gl/issues/9291, at the end of which my recommendation is to allow passing in the standard blending factors (similar to how we do in Layer.parameters: https://github.com/visgl/deck.gl/blob/master/modules/mapbox/src/deck-utils.ts#L127-L130)
So the resulting API would be:
new IconLayer({
getColor: d => [r, g, b, a], // Applies as tint color
iconMapping: {
customIcon: {
mask: true | false, // Remains as before
blendColorSrcFactor: 'src-alpha',
blendColorDstFactor: 'one-minus-src-alpha',
blendAlphaSrcFactor: 'one',
blendAlphaDstFactor: 'one-minus-src-alpha',
...
}
}
});
The advantage is that we are using an existing set of defined constants and giving more flexibility for the user. The mask: "tint" is in effect a shorthand for one specific combination of the above factors. While it may be more verbose, in most cases it will not be necessary to specify all 4 factors