mapbox / mapbox/mapbox-gl-js

Option to disable premultiply on raster source fetch

Open
#13,634 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

auto-triaged feature :green_apple:
Dominant language
TypeScript
Stars
12.4k
Forks
2.4k
PR merge metrics
No merged PRs in 30d

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

1. Do nothing.
This keeps current behavior but leaves packed-data raster sources unsupported.

2. Custom source pipelines.
This works, but it forces applications to reimplement tile loading, caching, decoding, and texture management just to avoid premultiplication.

3. 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.

4. Add a per-layer option.
This is the wrong level. Premultiplication happens when the raster source is decoded and uploaded, before layers consume it.

5. 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 behavior
- `false`: decode and upload raster tiles without alpha premultiplication, so `RGBA` bytes 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:

```json
{
"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
- `A` can 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: `RGB` may be scaled by `A`
- non-premultiplied: `RGBA` channels 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

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by locating the raster source specification, current image-loading paths, and texture upload code described in the implementation notes. Trace how the default premultiplication behavior is applied, then verify that the new option preserves the existing path by default and keeps RGBA bytes unchanged when set to false.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, typescript
Domain
computer-graphics, frontend, web-dev
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.