-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Description
Motivation
This is a backport/upstream of functionality we use internally to ship multi-band weather rasters to a custom layer, and I thought it might be useful to others too.
In those cases, premultiplication silently corrupts the red, green, and blue channels before application code can read them. The result is incorrect colors, wrong decoded values, and invalid downstream rendering.
Allowing premultiplication to be disabled would let custom layers get the exact byte values of all channels of image data.
Design Alternatives
-
Do nothing.
This keeps current behavior but leaves packed-data raster sources unsupported. -
Custom source pipelines.
This works, but it forces applications to reimplement tile loading, caching, decoding, and texture management just to avoid premultiplication. -
Add a new source type for raw raster data.
This is more explicit, but it adds a lot of API and implementation surface for what is fundamentally one decode/upload behavior switch. -
Add a per-layer option.
This is the wrong level. Premultiplication happens when the raster source is decoded and uploaded, before layers consume it. -
Add a per-source raster option.
This is the smallest API that matches where the problem occurs.
Design
Add a new raster source option:
premultiply: boolean
Default:
true
Behavior:
true: preserve current behaviorfalse: decode and upload raster tiles without alpha premultiplication, soRGBAbytes remain unchanged
Advantages:
- minimal API change
- backward compatible by default
- supports packed-data raster workflows without requiring a custom loader
Potential drawbacks:
- adds one more source option to the style spec
Mock-Up
Developer-facing style:
{
"sources": {
"packed-raster": {
"type": "raster",
"tiles": ["https://example.com/tiles/{z}/{x}/{y}.webp"],
"tileSize": 256,
"premultiply": false
}
}
}Developer-facing effect:
- when sampling the source, channel values are the original tile bytes
Acan safely be used as a data channel
End-user effect:
- raster-derived rendering is correct for packed RGBA data
- no visible change for ordinary raster imagery, since the default remains
true
Concepts
The main concept is “alpha premultiplication”:
- premultiplied:
RGBmay be scaled byA - non-premultiplied:
RGBAchannels are preserved as authored
This does not introduce a new rendering model. It only exposes control over an existing decode/upload behavior that is currently implicit.
Implementation
JavaScript:
- extend raster source spec with
premultiply?: boolean - default to
true - when
premultiply === false:- avoid image-loading paths that force premultiplication
- use a fetch/ArrayBuffer/ImageBitmap path where possible
- call
createImageBitmap(blob, { premultiplyAlpha: 'none' }) - upload textures with unpack premultiplication disabled
- when
premultiply !== false, keep the current path unchanged