feat: support updating an ImageSource with a decoded image#7944
feat: support updating an ImageSource with a decoded image#7944mondsichtung wants to merge 9 commits into
Conversation
ImageSource.updateImage() now accepts an already-decoded image (HTMLImageElement, HTMLCanvasElement or ImageBitmap) via a new `image` option, as an alternative to `url`. When provided, the image is used directly without a network request. `url` is now optional and `image` takes precedence when both are given.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #7944 +/- ##
==========================================
+ Coverage 93.25% 93.26% +0.01%
==========================================
Files 290 290
Lines 24650 24655 +5
Branches 6471 6472 +1
==========================================
+ Hits 22987 22995 +8
+ Misses 1663 1660 -3 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
97bcb85 to
9e58afe
Compare
Express UpdateImageOptions as a discriminated union of `{url}` or `{image}`
instead of two optional fields, so the "provide exactly one" contract is
enforced at the type level. Passing both or neither is now a compile error;
the runtime guard is kept for untyped callers.
Move the duplicated source/map setup in the "updateImage with a decoded image" tests into a beforeEach. Stop relying on sleep(0): onAdd starts the initial load synchronously, so request cancellation is asserted on the AbortController directly instead of waiting for the network request to be dispatched.
CommanderStorm
left a comment
There was a problem hiding this comment.
Change LGTM from my side, I have a few minor nits left.
Co-authored-by: Frank Elsinga <frank@elsinga.de>
Co-authored-by: Frank Elsinga <frank@elsinga.de>
Give StubMap an injectable transformRequest and assert on that mock, so the ImageSource tests exercise the public request-transform path instead of reaching into the internal _requestManager.
The simplified UpdateImageOptions union (url | image, without never-guards) no longer permits reading url/image off the raw union, which broke the typecheck. Narrow with `'image' in options` and drop the now-impossible "neither provided" guard.
| * @param options - The options object. | ||
| */ | ||
| updateImage(options: UpdateImageOptions): this { | ||
| if (!options.url) { |
There was a problem hiding this comment.
I think this test still needs to be executed below the "if image" part, right?
| return new ImageSource('id', options, {} as any, options.eventedParent); | ||
| } | ||
|
|
||
| class StubMap extends Evented { |
There was a problem hiding this comment.
Can this use the util's createmap method and avoid creating another type of stub here?
| expect(source.serialize().coordinates).toEqual([[0, 0], [-1, 0], [-1, -1], [0, -1]]); | ||
| }); | ||
|
|
||
| test('fires a metadata data event', () => { |
There was a problem hiding this comment.
I think this test can be collapsed to a different test.
|
Thanks! Looks good overall. |
Summary
ImageSource.updateImage()can now take an already-decoded image directly, as an alternative to aurl. When you passimage, no network request is made and the image is used as-is.Motivation
Callers that already hold a decoded image (an
ImageBitmapfromOffscreenCanvas/a worker, anHTMLCanvasElementthey render into, procedurally generated or GPU read-back pixels asImageData, etc.) currently have to serialize it to a blob or data URL just soImageSourcecan fetch and decode it again. That round-trip is wasteful and, in practice, has pushed apps to reach intoImageSource's private fields (image,texture,_request) to inject the bitmap themselves. This makes that a supported, first-class operation.It also mirrors an API that already exists for sprite images:
Map.updateImage(id, image)acceptsHTMLImageElement | ImageData | ImageBitmap. This brings the same convenience, and the same set of decoded image types, to the geo-positionedImageSource.Changes
UpdateImageOptionsgains animage?: HTMLImageElement | HTMLCanvasElement | ImageBitmap | ImageDatafield;urlis now optional. If both are given,imagewins.updateImage()aborts any in-flight request, sets the image directly, updates coordinates if provided, resets the texture so it re-uploads on the nextprepare(), and fires the usualdata/metadataevent, reusing the exact texture lifecycle of the existing URL path.ImageSourceImage; the publicImageSource.imagefield type is widened to it (a superset, backward compatible).Public API changes
UpdateImageOptions.urlis now optional.UpdateImageOptions.imageadded (HTMLImageElement | HTMLCanvasElement | ImageBitmap | ImageData).ImageSourceImage.ImageSource.imagefield type widened.All are additive / widening, so existing
updateImage({ url })callers are unaffected. Documented via TSDoc (rendered by typedoc), including an updatedImageSourceexample.Tests
Added to
src/source/image_source.test.ts(updateImage with a decoded image): sets the image without a network request, resets the texture, updates coordinates alongside the image, fires the metadatadataevent, cancels a pending request, accepts anImageDatainstance, prefersimageoverurl, and no-ops on empty options. Full unit file: 27/27 passing;eslintandtypecheckclean.Launch Checklist
CHANGELOG.mdunder the## mainsection.Generated-By: Claude Opus 4.8 xhigh