Skip to content

Let a StyleImageInterface draw itself on the GPU for more performant animated icons - #7954

Merged
HarelM merged 43 commits into
maplibre:mainfrom
lucaswoj:bench-animated-icons
Aug 7, 2026
Merged

HarelM merged 43 commits into
maplibre:mainfrom
lucaswoj:bench-animated-icons

Conversation

@lucaswoj

@lucaswoj lucaswoj commented Jul 15, 2026 •

Copy link
Copy Markdown
Contributor

fix #7857

Today the recommended way to animate an icon is a StyleImageInterface#render callback that rewrites the icon's pixels every frame. MapLibre reprocesses those pixels on the CPU and re-uploads the whole icon to the GPU. That looks fine in a demo, but in a real app where many effects compete for frame time, a few animated icons cause real lag, especially on lower-powered phones.

This PR lets a StyleImageInterface hand over a {renderWithWebGL} callback as its data, in place of an array of pixels. Such an image renders straight into its slot of the shared icon atlas on the GPU:

interface StyleImageInterface {
    width: number;
    height: number;
    data: Uint8Array | Uint8ClampedArray | StyleImageWebGLData;
    render?: () => boolean;
    onAdd?: (map: Map, id: string) => void;
    onRemove?: () => void;
}

type StyleImageWebGLData = {
    renderWithWebGL: (target: StyleImageWebGLTarget) => void;
};

type StyleImageWebGLTarget = {
    gl: WebGL2RenderingContext;
    texture: WebGLTexture;
    x: number;
    y: number;
    width: number;
    height: number;
};

An image asks to be rendered again by returning true from render, the same signal the existing StyleImageInterface uses to commit new pixels. renderWithWebGL is the image's counterpart to CustomLayerInterface.render, and receives its GL context in the same state. It is also called before the first frame the image is used in, and again for each atlas holding a slot this image has never rendered into, so one change can mean several calls with different targets.

This can be used to show icons driven by animated images, videos, an external canvas, or shaders. The new animate-an-icon-on-the-gpu example renders a triangle whose corner hues cycle once a second, computed in a vertex shader instead of a JS pixel loop.

Performance

I'm working on a plugin that decodes a GIF, animated WebP, APNG or animated AVIF; uploads every frame to the GPU once packed into a grid; and advances a frame with a copyTexSubImage2D into the icon's atlas slot. No per-frame CPU work, no upload, no atlas repack. I'll publish it separately.

50 icons, 256px, 8 frames, one frame change per render, Apple M4 Max, headless Chrome:

how the icon animates cost per frame
StyleImageInterface#render rewriting pixels 18.0 ms
StyleImageWebGLData#renderWithWebGL 0.64 ms

About 28x here, and more on phones where the CPU work is relatively more expensive.

Launch Checklist

  • Confirm your changes do not include backports from Mapbox projects (unless with compliant license) - if you are not sure about this, please ask!
  • Briefly describe the changes in this PR.
  • Link to related issues.
  • Include before/after visuals or gifs if this PR includes visual changes.
  • Write tests for all new functionality.
  • Document any changes to public APIs.
  • Post benchmark scores.
  • Add an entry to CHANGELOG.md under the ## main section.
  • Confirm you have read our AI policy here.

Assisted-By: Claude Opus 5

@lucaswoj
lucaswoj marked this pull request as draft July 15, 2026 19:20
@lucaswoj
lucaswoj force-pushed the bench-animated-icons branch from cd5c010 to bcfe903 Compare July 15, 2026 19:22
@codecov

codecov Bot commented Jul 15, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 92.50000% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 93.59%. Comparing base (f950bf6) to head (bc6c02d).
⚠️ Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
src/ui/map.ts 72.72% 3 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #7954      +/-   ##
==========================================
+ Coverage   93.42%   93.59%   +0.16%     
==========================================
  Files         291      291              
  Lines       24631    24655      +24     
  Branches     6490     6498       +8     
==========================================
+ Hits        23012    23075      +63     
+ Misses       1619     1580      -39     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@lucaswoj
lucaswoj force-pushed the bench-animated-icons branch from 4f15f38 to 7e76f83 Compare July 18, 2026 00:23
@lucaswoj

lucaswoj commented Jul 18, 2026 •

Copy link
Copy Markdown
Contributor Author
  • Fix the idle event
  • Ensure animations survive WebGL context loss
  • Validate all frame sizes match
  • Fix crash when passing addImage(id, [])
  • Guard against extrmely large aniation texture sizes
  • Add tests
  • Add docs

@lucaswoj

Copy link
Copy Markdown
Contributor Author

Status update: I'm heading into the backcountry and offline through Tuesday 7/28. I'll pick this back up Wednesday.

Comment thread src/render/image_atlas.ts Outdated
Comment thread src/render/image_atlas.ts
Comment thread src/style/style_image.ts Outdated
Comment thread CHANGELOG.md Outdated
@lucaswoj
lucaswoj force-pushed the bench-animated-icons branch 2 times, most recently from 7735ed0 to e9afc1c Compare August 3, 2026 02:17
@lucaswoj lucaswoj changed the title Improve support for animated icons Add StyleImageInterface#renderToTexture method to enable improved animated icon performance Aug 3, 2026
@lucaswoj

lucaswoj commented Aug 3, 2026

Copy link
Copy Markdown
Contributor Author

This is ready for review

Animating an icon today means a StyleImageInterface#render() callback
that rewrites its pixels every frame, which MapLibre reprocesses on the
CPU and re-uploads in full. Add an optional renderToTexture, so an image
can write straight into its slot in the icon atlas instead.
ImageAtlas#patchUpdatedImage calls it in place of the usual upload and
resets the WebGL state cache once per batch afterward, so the callback
is free to leave state behind.

Nothing about the hook is animation-specific, and it keeps ImageDecoder
out of core: animated icons can now live in a plugin.

Also adds Map#getMaxTextureSize(), so an image that manages its own
textures can tell what will fit.
@lucaswoj
lucaswoj force-pushed the bench-animated-icons branch from e9afc1c to 4966e3a Compare August 3, 2026 02:40
@HarelM

HarelM commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator

Can you add a short html example using this new API? Add @example maybe in the interface signature too?

This looks very specific and concise, which is good.

Can you share maybe what other alternatives you have considered and rejected to solve this?

I'm trying to get the bigger picture here.

Comment thread src/render/image_atlas.test.ts Outdated
Comment thread src/render/image_atlas.test.ts Outdated
Comment thread src/render/image_atlas.ts Outdated
Comment thread src/style/style_image.ts Outdated
Comment thread src/style/style_image.ts Outdated
@HarelM

HarelM commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator

Added a few commets, see if you can reduce some in method comments and move those to tsdocs, also reduce comments inside tests as much as possible.

@lucaswoj lucaswoj changed the title Let a StyleImageInterface draw itself on the GPU for cheaper animated icons Let a StyleImageInterface draw itself on the GPU for more performant animated icons Aug 7, 2026
lucaswoj and others added 5 commits August 7, 2026 07:57
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@lucaswoj
lucaswoj requested a review from HarelM August 7, 2026 15:48
@lucaswoj

lucaswoj commented Aug 7, 2026

Copy link
Copy Markdown
Contributor Author

This is ready for another round of review

Comment thread src/render/image_atlas.ts Outdated
@HarelM

HarelM commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator

Added a minor comment.
Looks good overall.
Let me know how you want to proceed.

@HarelM
HarelM enabled auto-merge (squash) August 7, 2026 21:30
auto-merge was automatically disabled August 7, 2026 21:39

Head branch was pushed to by a user without write access

@lucaswoj

lucaswoj commented Aug 7, 2026

Copy link
Copy Markdown
Contributor Author

Just did a final manual check on the example page and some of my prototype plugins. All looks good to me! I think this is ready to 🚢 . Thanks for the review.

@HarelM
HarelM enabled auto-merge (squash) August 7, 2026 21:40
@HarelM

HarelM commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator

Thanks!! It will probably take a week or two to release, I hope that's fine.

@HarelM
HarelM merged commit bc21e82 into maplibre:main Aug 7, 2026
22 checks passed
HarelM added a commit that referenced this pull request Aug 11, 2026
Reconciles the image manager rework with the WebGL style images of #7954:
the atlas now walks its own positions rather than a global dictionary of
updated images, and reaches a WebGL image's pending first render through
the same version bump that `addImage` already makes for it.
renovate Bot added a commit to cigaleapp/cigale that referenced this pull request Aug 24, 2026
##### [v6.4.0](https://github.com/maplibre/maplibre-gl-js/blob/HEAD/CHANGELOG.md#640)

##### ✨ Features and improvements

- Avoid a per-query `Array.sort()` in cross-tile symbol matching (`TileLayerIndex.findMatches`), claiming the lowest-index unclaimed candidate in a single pass instead; reduces main-thread symbol-placement cost on dense/coincident symbol layers ([#7797](maplibre/maplibre-gl-js#7797)) (by [@pholmstr](https://github.com/pholmstr))
- Use `texelFetch` for exact DEM and color-relief elevation stop lookups instead of normalized texture coordinate arithmetic ([#7640](maplibre/maplibre-gl-js#7640)) (by [@johncarmack1984](https://github.com/johncarmack1984))
- Make default draggable markers keyboard-focusable and movable with the arrow keys (1 px per press, 10 px with Shift); custom marker elements stay application-owned ([#8020](maplibre/maplibre-gl-js#8020)) (by [@smmariquit](https://github.com/smmariquit))

##### 🐞 Bug fixes

- Fix a permanent frame rate degradation after switching styles: every sprite reload marked its images as updated forever, making every in-view tile re-check and re-upload them on every frame. Also stop leaking the images of a replaced sprite, which were never removed from the image manager ([#8052](maplibre/maplibre-gl-js#8052)) (by [@HarelM](https://github.com/HarelM))
- Prevent a rejected missing style image resolver from blocking successfully resolved images in the same batch ([#8146](maplibre/maplibre-gl-js#8146)) (by [@birkskyum](https://github.com/birkskyum))
- Explicitly request no browser color management when decoding raster-DEM tiles so their RGB-encoded elevation values are not changed (what would otherwise happen with `gfx.color_management.mode = 1` in Firefox) ([#8125](maplibre/maplibre-gl-js#8125)) (by [@tnikkel](https://github.com/tnikkel))
- Let an abort reach an image or raster tile load that is still awaiting its `transformRequest`, so `ImageSource.updateImage` no longer loses the image it was just handed and an aborted tile is no longer fetched ([#8071](maplibre/maplibre-gl-js#8071)) (by [@mondsichtung](https://github.com/mondsichtung))
- Fix raster tiles fading in again when they are reloaded, briefly flashing the map background, most visibly when switching projection ([#8106](maplibre/maplibre-gl-js#8106)) (by [@mondsichtung](https://github.com/mondsichtung))
- Fix globe panning inverting and stalling near and across the poles by rotating the globe with a versor, keeping the drag direction consistent at every latitude. Panning also eases off as the cursor approaches the edge of the globe and continues past it, instead of stopping. The bearing is preserved while panning, as before ([#5296](maplibre/maplibre-gl-js#5296)) (by [@jcolot](https://github.com/jcolot))
- Fix `fill-extrusion-rounded-corner-distance` producing spikes: corner arcs now land on the integer tile grid, and corners created by tile clipping are left sharp ([#8153](maplibre/maplibre-gl-js#8153)) (by [@HarelM](https://github.com/HarelM))
- Fix a gesture which was held still before being released still flinging the map ([#1303](maplibre/maplibre-gl-js#1303)) (by [@zdila](https://github.com/zdila))
##### [v6.3.0](https://github.com/maplibre/maplibre-gl-js/blob/HEAD/CHANGELOG.md#630)

##### ✨ Features and improvements

- Make fired/listened map events typed. This means that `map.on("something", ...)` (and `once`, `listens`) will now give you an typescript error and better autocomplete. If you relied on firing/listening custom events via the map, this still works via the escape hatches `map.fire("something" as any)` -> `map.on("something" as any, ...)` ([#8072](maplibre/maplibre-gl-js#8072)) (by [@CommanderStorm](https://github.com/CommanderStorm))
- Let a `StyleImageInterface` give a `{renderWithWebGL}` callback as its `data`, an escape hatch for plugin developers and advanced users that renders a style image on the GPU instead of moving its pixels through the CPU. Nothing new is possible that pixels could not already express, but an image that changes often, such as an animated icon, gets more performant ([#7954](maplibre/maplibre-gl-js#7954)) (by [@lucaswoj](https://github.com/lucaswoj))
- Use integer vertex attributes for packed line data instead of float conversion ([#7640](maplibre/maplibre-gl-js#7640)) (by [@johncarmack1984](https://github.com/johncarmack1984))
- Use integer vertex attributes for packed circle, heatmap, symbol, and fill-extrusion data instead of float conversion ([#7640](maplibre/maplibre-gl-js#7640), [#8143](maplibre/maplibre-gl-js#8143)) (by [@johncarmack1984](https://github.com/johncarmack1984))
- Redesign benchmarks to use vitest bench capabilities and remove custom build for benchmarks code ([#982](maplibre/maplibre-gl-js#982)) (by [@johncarmack1984](https://github.com/johncarmack1984))

##### 🐞 Bug fixes

- Fix terrain pan/zoom gestures losing the grabbed terrain point: gestures are now solved against the elevation of the terrain under the gesture instead of the frozen center elevation, so terrain under the pointer/fingers no longer slips during moving-centroid pinches and drags ([#8067](maplibre/maplibre-gl-js#8067)) (by [@StrawberryJam22](https://github.com/StrawberryJam22))
- Fix `ImageSource`, `VideoSource` and `CanvasSource` leaking a GPU texture on every image update and on removal, and a resized texture losing its wrap and filter settings ([#8094](maplibre/maplibre-gl-js#8094)) (by [@mondsichtung](https://github.com/mondsichtung))
- Fix `map.queryRenderedFeatures()` sometimes causing "Out of bounds" error due to race condition while loading tile data ([#8064](maplibre/maplibre-gl-js#8064)) (by [@smvjohansenbouvet](https://github.com/smvjohansenbouvet))
- Fix zooming the globe with the scroll wheel or a two-finger pinch drifting away from the pointer while the globe is small on screen, instead of keeping the location under the pointer as it does when zoomed in ([#8095](maplibre/maplibre-gl-js#8095)) (by [@mondsichtung](https://github.com/mondsichtung))
- Fix projective rendering for non-parallelogram image source quads ([#7887](maplibre/maplibre-gl-js#7887)) (by [@i4innovationnet](https://github.com/i4innovationnet))
renovate Bot added a commit to cigaleapp/cigale that referenced this pull request Aug 25, 2026
##### [v6.4.1](https://github.com/maplibre/maplibre-gl-js/blob/HEAD/CHANGELOG.md#641)

##### 🐞 Bug fixes

- Fix `DOM.sanitize` leaving dangerous attributes behind when multiple consecutive attributes are present. Iterating the live `NamedNodeMap` from `elem.attributes` while calling `removeAttribute` skipped the attribute directly after a removed one, so a second dangerous attribute (for example an `ontoggle` on a `<details open>` element) could survive sanitisation and later execute ([#8189](maplibre/maplibre-gl-js#8189)) (by [@0xKirisame](https://github.com/0xKirisame))
- Give custom layers the live globe transition in `CustomRenderMethodInput.defaultProjectionData.projectionTransition`, which was hardcoded to 1 for the whole globe/mercator transition, so a custom layer jumped straight to the fully bent globe while every other layer eased ([#8169](maplibre/maplibre-gl-js#8169)) (by [@mondsichtung](https://github.com/mondsichtung))
##### [v6.4.0](https://github.com/maplibre/maplibre-gl-js/blob/HEAD/CHANGELOG.md#640)

##### ✨ Features and improvements

- Avoid a per-query `Array.sort()` in cross-tile symbol matching (`TileLayerIndex.findMatches`), claiming the lowest-index unclaimed candidate in a single pass instead; reduces main-thread symbol-placement cost on dense/coincident symbol layers ([#7797](maplibre/maplibre-gl-js#7797)) (by [@pholmstr](https://github.com/pholmstr))
- Use `texelFetch` for exact DEM and color-relief elevation stop lookups instead of normalized texture coordinate arithmetic ([#7640](maplibre/maplibre-gl-js#7640)) (by [@johncarmack1984](https://github.com/johncarmack1984))
- Make default draggable markers keyboard-focusable and movable with the arrow keys (1 px per press, 10 px with Shift); custom marker elements stay application-owned ([#8020](maplibre/maplibre-gl-js#8020)) (by [@smmariquit](https://github.com/smmariquit))

##### 🐞 Bug fixes

- Fix a permanent frame rate degradation after switching styles: every sprite reload marked its images as updated forever, making every in-view tile re-check and re-upload them on every frame. Also stop leaking the images of a replaced sprite, which were never removed from the image manager ([#8052](maplibre/maplibre-gl-js#8052)) (by [@HarelM](https://github.com/HarelM))
- Prevent a rejected missing style image resolver from blocking successfully resolved images in the same batch ([#8146](maplibre/maplibre-gl-js#8146)) (by [@birkskyum](https://github.com/birkskyum))
- Explicitly request no browser color management when decoding raster-DEM tiles so their RGB-encoded elevation values are not changed (what would otherwise happen with `gfx.color_management.mode = 1` in Firefox) ([#8125](maplibre/maplibre-gl-js#8125)) (by [@tnikkel](https://github.com/tnikkel))
- Let an abort reach an image or raster tile load that is still awaiting its `transformRequest`, so `ImageSource.updateImage` no longer loses the image it was just handed and an aborted tile is no longer fetched ([#8071](maplibre/maplibre-gl-js#8071)) (by [@mondsichtung](https://github.com/mondsichtung))
- Fix raster tiles fading in again when they are reloaded, briefly flashing the map background, most visibly when switching projection ([#8106](maplibre/maplibre-gl-js#8106)) (by [@mondsichtung](https://github.com/mondsichtung))
- Fix globe panning inverting and stalling near and across the poles by rotating the globe with a versor, keeping the drag direction consistent at every latitude. Panning also eases off as the cursor approaches the edge of the globe and continues past it, instead of stopping. The bearing is preserved while panning, as before ([#5296](maplibre/maplibre-gl-js#5296)) (by [@jcolot](https://github.com/jcolot))
- Fix `fill-extrusion-rounded-corner-distance` producing spikes: corner arcs now land on the integer tile grid, and corners created by tile clipping are left sharp ([#8153](maplibre/maplibre-gl-js#8153)) (by [@HarelM](https://github.com/HarelM))
- Fix a gesture which was held still before being released still flinging the map ([#1303](maplibre/maplibre-gl-js#1303)) (by [@zdila](https://github.com/zdila))
##### [v6.3.0](https://github.com/maplibre/maplibre-gl-js/blob/HEAD/CHANGELOG.md#630)

##### ✨ Features and improvements

- Make fired/listened map events typed. This means that `map.on("something", ...)` (and `once`, `listens`) will now give you an typescript error and better autocomplete. If you relied on firing/listening custom events via the map, this still works via the escape hatches `map.fire("something" as any)` -> `map.on("something" as any, ...)` ([#8072](maplibre/maplibre-gl-js#8072)) (by [@CommanderStorm](https://github.com/CommanderStorm))
- Let a `StyleImageInterface` give a `{renderWithWebGL}` callback as its `data`, an escape hatch for plugin developers and advanced users that renders a style image on the GPU instead of moving its pixels through the CPU. Nothing new is possible that pixels could not already express, but an image that changes often, such as an animated icon, gets more performant ([#7954](maplibre/maplibre-gl-js#7954)) (by [@lucaswoj](https://github.com/lucaswoj))
- Use integer vertex attributes for packed line data instead of float conversion ([#7640](maplibre/maplibre-gl-js#7640)) (by [@johncarmack1984](https://github.com/johncarmack1984))
- Use integer vertex attributes for packed circle, heatmap, symbol, and fill-extrusion data instead of float conversion ([#7640](maplibre/maplibre-gl-js#7640), [#8143](maplibre/maplibre-gl-js#8143)) (by [@johncarmack1984](https://github.com/johncarmack1984))
- Redesign benchmarks to use vitest bench capabilities and remove custom build for benchmarks code ([#982](maplibre/maplibre-gl-js#982)) (by [@johncarmack1984](https://github.com/johncarmack1984))

##### 🐞 Bug fixes

- Fix terrain pan/zoom gestures losing the grabbed terrain point: gestures are now solved against the elevation of the terrain under the gesture instead of the frozen center elevation, so terrain under the pointer/fingers no longer slips during moving-centroid pinches and drags ([#8067](maplibre/maplibre-gl-js#8067)) (by [@StrawberryJam22](https://github.com/StrawberryJam22))
- Fix `ImageSource`, `VideoSource` and `CanvasSource` leaking a GPU texture on every image update and on removal, and a resized texture losing its wrap and filter settings ([#8094](maplibre/maplibre-gl-js#8094)) (by [@mondsichtung](https://github.com/mondsichtung))
- Fix `map.queryRenderedFeatures()` sometimes causing "Out of bounds" error due to race condition while loading tile data ([#8064](maplibre/maplibre-gl-js#8064)) (by [@smvjohansenbouvet](https://github.com/smvjohansenbouvet))
- Fix zooming the globe with the scroll wheel or a two-finger pinch drifting away from the pointer while the globe is small on screen, instead of keeping the location under the pointer as it does when zoomed in ([#8095](maplibre/maplibre-gl-js#8095)) (by [@mondsichtung](https://github.com/mondsichtung))
- Fix projective rendering for non-parallelogram image source quads ([#7887](maplibre/maplibre-gl-js#7887)) (by [@i4innovationnet](https://github.com/i4innovationnet))
gwennlbh pushed a commit to cigaleapp/cigale that referenced this pull request Aug 25, 2026
##### [v6.4.1](https://github.com/maplibre/maplibre-gl-js/blob/HEAD/CHANGELOG.md#641)

##### 🐞 Bug fixes

- Fix `DOM.sanitize` leaving dangerous attributes behind when multiple consecutive attributes are present. Iterating the live `NamedNodeMap` from `elem.attributes` while calling `removeAttribute` skipped the attribute directly after a removed one, so a second dangerous attribute (for example an `ontoggle` on a `<details open>` element) could survive sanitisation and later execute ([#8189](maplibre/maplibre-gl-js#8189)) (by [@0xKirisame](https://github.com/0xKirisame))
- Give custom layers the live globe transition in `CustomRenderMethodInput.defaultProjectionData.projectionTransition`, which was hardcoded to 1 for the whole globe/mercator transition, so a custom layer jumped straight to the fully bent globe while every other layer eased ([#8169](maplibre/maplibre-gl-js#8169)) (by [@mondsichtung](https://github.com/mondsichtung))
##### [v6.4.0](https://github.com/maplibre/maplibre-gl-js/blob/HEAD/CHANGELOG.md#640)

##### ✨ Features and improvements

- Avoid a per-query `Array.sort()` in cross-tile symbol matching (`TileLayerIndex.findMatches`), claiming the lowest-index unclaimed candidate in a single pass instead; reduces main-thread symbol-placement cost on dense/coincident symbol layers ([#7797](maplibre/maplibre-gl-js#7797)) (by [@pholmstr](https://github.com/pholmstr))
- Use `texelFetch` for exact DEM and color-relief elevation stop lookups instead of normalized texture coordinate arithmetic ([#7640](maplibre/maplibre-gl-js#7640)) (by [@johncarmack1984](https://github.com/johncarmack1984))
- Make default draggable markers keyboard-focusable and movable with the arrow keys (1 px per press, 10 px with Shift); custom marker elements stay application-owned ([#8020](maplibre/maplibre-gl-js#8020)) (by [@smmariquit](https://github.com/smmariquit))

##### 🐞 Bug fixes

- Fix a permanent frame rate degradation after switching styles: every sprite reload marked its images as updated forever, making every in-view tile re-check and re-upload them on every frame. Also stop leaking the images of a replaced sprite, which were never removed from the image manager ([#8052](maplibre/maplibre-gl-js#8052)) (by [@HarelM](https://github.com/HarelM))
- Prevent a rejected missing style image resolver from blocking successfully resolved images in the same batch ([#8146](maplibre/maplibre-gl-js#8146)) (by [@birkskyum](https://github.com/birkskyum))
- Explicitly request no browser color management when decoding raster-DEM tiles so their RGB-encoded elevation values are not changed (what would otherwise happen with `gfx.color_management.mode = 1` in Firefox) ([#8125](maplibre/maplibre-gl-js#8125)) (by [@tnikkel](https://github.com/tnikkel))
- Let an abort reach an image or raster tile load that is still awaiting its `transformRequest`, so `ImageSource.updateImage` no longer loses the image it was just handed and an aborted tile is no longer fetched ([#8071](maplibre/maplibre-gl-js#8071)) (by [@mondsichtung](https://github.com/mondsichtung))
- Fix raster tiles fading in again when they are reloaded, briefly flashing the map background, most visibly when switching projection ([#8106](maplibre/maplibre-gl-js#8106)) (by [@mondsichtung](https://github.com/mondsichtung))
- Fix globe panning inverting and stalling near and across the poles by rotating the globe with a versor, keeping the drag direction consistent at every latitude. Panning also eases off as the cursor approaches the edge of the globe and continues past it, instead of stopping. The bearing is preserved while panning, as before ([#5296](maplibre/maplibre-gl-js#5296)) (by [@jcolot](https://github.com/jcolot))
- Fix `fill-extrusion-rounded-corner-distance` producing spikes: corner arcs now land on the integer tile grid, and corners created by tile clipping are left sharp ([#8153](maplibre/maplibre-gl-js#8153)) (by [@HarelM](https://github.com/HarelM))
- Fix a gesture which was held still before being released still flinging the map ([#1303](maplibre/maplibre-gl-js#1303)) (by [@zdila](https://github.com/zdila))
##### [v6.3.0](https://github.com/maplibre/maplibre-gl-js/blob/HEAD/CHANGELOG.md#630)

##### ✨ Features and improvements

- Make fired/listened map events typed. This means that `map.on("something", ...)` (and `once`, `listens`) will now give you an typescript error and better autocomplete. If you relied on firing/listening custom events via the map, this still works via the escape hatches `map.fire("something" as any)` -> `map.on("something" as any, ...)` ([#8072](maplibre/maplibre-gl-js#8072)) (by [@CommanderStorm](https://github.com/CommanderStorm))
- Let a `StyleImageInterface` give a `{renderWithWebGL}` callback as its `data`, an escape hatch for plugin developers and advanced users that renders a style image on the GPU instead of moving its pixels through the CPU. Nothing new is possible that pixels could not already express, but an image that changes often, such as an animated icon, gets more performant ([#7954](maplibre/maplibre-gl-js#7954)) (by [@lucaswoj](https://github.com/lucaswoj))
- Use integer vertex attributes for packed line data instead of float conversion ([#7640](maplibre/maplibre-gl-js#7640)) (by [@johncarmack1984](https://github.com/johncarmack1984))
- Use integer vertex attributes for packed circle, heatmap, symbol, and fill-extrusion data instead of float conversion ([#7640](maplibre/maplibre-gl-js#7640), [#8143](maplibre/maplibre-gl-js#8143)) (by [@johncarmack1984](https://github.com/johncarmack1984))
- Redesign benchmarks to use vitest bench capabilities and remove custom build for benchmarks code ([#982](maplibre/maplibre-gl-js#982)) (by [@johncarmack1984](https://github.com/johncarmack1984))

##### 🐞 Bug fixes

- Fix terrain pan/zoom gestures losing the grabbed terrain point: gestures are now solved against the elevation of the terrain under the gesture instead of the frozen center elevation, so terrain under the pointer/fingers no longer slips during moving-centroid pinches and drags ([#8067](maplibre/maplibre-gl-js#8067)) (by [@StrawberryJam22](https://github.com/StrawberryJam22))
- Fix `ImageSource`, `VideoSource` and `CanvasSource` leaking a GPU texture on every image update and on removal, and a resized texture losing its wrap and filter settings ([#8094](maplibre/maplibre-gl-js#8094)) (by [@mondsichtung](https://github.com/mondsichtung))
- Fix `map.queryRenderedFeatures()` sometimes causing "Out of bounds" error due to race condition while loading tile data ([#8064](maplibre/maplibre-gl-js#8064)) (by [@smvjohansenbouvet](https://github.com/smvjohansenbouvet))
- Fix zooming the globe with the scroll wheel or a two-finger pinch drifting away from the pointer while the globe is small on screen, instead of keeping the location under the pointer as it does when zoomed in ([#8095](maplibre/maplibre-gl-js#8095)) (by [@mondsichtung](https://github.com/mondsichtung))
- Fix projective rendering for non-parallelogram image source quads ([#7887](maplibre/maplibre-gl-js#7887)) (by [@i4innovationnet](https://github.com/i4innovationnet))

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add support for animated icons

2 participants