-
Notifications
You must be signed in to change notification settings - Fork 2.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ImageBitmap and OffscreenCanvas for DEM tiles #8845
Changes from 10 commits
33942bb
58756d7
4c8bae2
0509d5c
1fdf49e
5ad0c74
5089802
8568876
89e33da
4664f65
84119b9
84b6983
453389e
19f4ab7
6d42581
199c2d8
91789df
d8bc399
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
// @flow strict | ||
|
||
declare class OffscreenCanvas { | ||
width: number; | ||
height: number; | ||
|
||
constructor(width: number, height: number): OffscreenCanvas; | ||
getContext(contextType: '2d' ): CanvasRenderingContext2D; | ||
} | ||
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,31 +1,52 @@ | ||
// @flow | ||
|
||
import DEMData from '../data/dem_data'; | ||
import {RGBAImage} from '../util/image'; | ||
import window from '../util/window'; | ||
|
||
import type Actor from '../util/actor'; | ||
import type { | ||
WorkerDEMTileParameters, | ||
WorkerDEMTileCallback, | ||
TileParameters | ||
} from './worker_source'; | ||
const {ImageBitmap} = window; | ||
|
||
class RasterDEMTileWorkerSource { | ||
actor: Actor; | ||
loaded: {[string]: DEMData}; | ||
offcreenCanvas: OffscreenCanvas; | ||
offcreenCanvasContext: CanvasRenderingContext2D; | ||
|
||
constructor() { | ||
this.loaded = {}; | ||
} | ||
|
||
loadTile(params: WorkerDEMTileParameters, callback: WorkerDEMTileCallback) { | ||
const {uid, encoding, rawImageData} = params; | ||
const dem = new DEMData(uid, rawImageData, encoding); | ||
|
||
const imagePixels = (ImageBitmap && rawImageData instanceof ImageBitmap) ? this.getImageData(rawImageData) : rawImageData; | ||
const dem = new DEMData(uid, imagePixels, encoding); | ||
this.loaded = this.loaded || {}; | ||
this.loaded[uid] = dem; | ||
callback(null, dem); | ||
} | ||
|
||
getImageData(imgBitmap: ImageBitmap): RGBAImage { | ||
// Lazily initialize OffscreenCanvas | ||
if (!this.offcreenCanvas || !this.offcreenCanvasContext) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. typo in variable name |
||
this.offcreenCanvas = new OffscreenCanvas(512, 512); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why does 512×512 suffice? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 256x256 is probably better I had this initially because thats what i though our tile resolution was. |
||
this.offcreenCanvasContext = this.offcreenCanvas.getContext('2d'); | ||
} | ||
|
||
this.offcreenCanvas.width = imgBitmap.width; | ||
this.offcreenCanvas.height = imgBitmap.height; | ||
this.offcreenCanvasContext.drawImage(imgBitmap, 0, 0, imgBitmap.width, imgBitmap.height); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When reusing the OffscreenCanvas for decoding images that are smaller, the right/bottom backfill border might contain leftovers from the previously decoded image. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh good catch! Didn't think about this! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I added a clearRect call and re-ran the benchmarks, doesn't seem to have a performance impact. |
||
// Insert an additional 1px padding around the image to allow backfilling for neighboring data. | ||
const imgData = this.offcreenCanvasContext.getImageData(-1, -1, imgBitmap.width + 2, imgBitmap.height + 2); | ||
return new RGBAImage({width: imgData.width, height: imgData.height}, imgData.data); | ||
} | ||
|
||
removeTile(params: TileParameters) { | ||
const loaded = this.loaded, | ||
uid = params.uid; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,6 +7,7 @@ import config from './config'; | |
import assert from 'assert'; | ||
import {cacheGet, cachePut} from './tile_request_cache'; | ||
import webpSupported from './webp_supported'; | ||
import offscreenCanvasSupported from './offscreen_canvas_supported'; | ||
|
||
import type {Callback} from '../types/callback'; | ||
import type {Cancelable} from '../types/cancelable'; | ||
|
@@ -257,14 +258,38 @@ function sameOrigin(url) { | |
|
||
const transparentPngUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAC0lEQVQYV2NgAAIAAAUAAarVyFEAAAAASUVORK5CYII='; | ||
|
||
function arrayBufferToImage(data: ArrayBuffer, callback: (err: ?Error, image: ?HTMLImageElement) => void, cacheControl: ?string, expires: ?string) { | ||
const img: HTMLImageElement = new window.Image(); | ||
const URL = window.URL || window.webkitURL; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need the |
||
img.onload = () => { | ||
callback(null, img); | ||
URL.revokeObjectURL(img.src); | ||
}; | ||
img.onerror = () => callback(new Error('Could not load image. Please make sure to use a supported image type such as PNG or JPEG. Note that SVGs are not supported.')); | ||
const blob: Blob = new window.Blob([new Uint8Array(data)], {type: 'image/png'}); | ||
(img: any).cacheControl = cacheControl; | ||
(img: any).expires = expires; | ||
img.src = data.byteLength ? URL.createObjectURL(blob) : transparentPngUrl; | ||
} | ||
|
||
function arrayBufferToImageBitmap(data: ArrayBuffer, callback: (err: ?Error, image: ?ImageBitmap) => void) { | ||
const blob: Blob = new window.Blob([new Uint8Array(data)], {type: 'image/png'}); | ||
window.createImageBitmap(blob).then((imgBitmap) => { | ||
callback(null, imgBitmap); | ||
}).catch((e) => { | ||
console.log(e); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Did you mean to leave this in? |
||
callback(new Error('Could not load image. Please make sure to use a supported image type such as PNG or JPEG. Note that SVGs are not supported.')); | ||
}); | ||
} | ||
|
||
let imageQueue, numImageRequests; | ||
export const resetImageRequestQueue = () => { | ||
imageQueue = []; | ||
numImageRequests = 0; | ||
}; | ||
resetImageRequestQueue(); | ||
|
||
export const getImage = function(requestParameters: RequestParameters, callback: Callback<HTMLImageElement>): Cancelable { | ||
export const getImage = function(requestParameters: RequestParameters, callback: Callback<HTMLImageElement | ImageBitmap>): Cancelable { | ||
if (webpSupported.supported) { | ||
if (!requestParameters.headers) { | ||
requestParameters.headers = {}; | ||
|
@@ -309,16 +334,11 @@ export const getImage = function(requestParameters: RequestParameters, callback: | |
if (err) { | ||
callback(err); | ||
} else if (data) { | ||
const img: HTMLImageElement = new window.Image(); | ||
img.onload = () => { | ||
callback(null, img); | ||
window.URL.revokeObjectURL(img.src); | ||
}; | ||
img.onerror = () => callback(new Error('Could not load image. Please make sure to use a supported image type such as PNG or JPEG. Note that SVGs are not supported.')); | ||
const blob: Blob = new window.Blob([new Uint8Array(data)], {type: 'image/png'}); | ||
(img: any).cacheControl = cacheControl; | ||
(img: any).expires = expires; | ||
img.src = data.byteLength ? window.URL.createObjectURL(blob) : transparentPngUrl; | ||
if (offscreenCanvasSupported()) { | ||
arrayBufferToImageBitmap(data, callback); | ||
} else { | ||
arrayBufferToImage(data, callback, cacheControl, expires); | ||
} | ||
} | ||
}); | ||
|
||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,14 @@ | ||||||
// @flow | ||||||
import window from './window'; | ||||||
|
||||||
let supportsOffscreenCanvas = null; | ||||||
|
||||||
export default function offscreenCanvasSupported(): boolean { | ||||||
if (supportsOffscreenCanvas == null) { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Intentional as a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Then let's use
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But then that would run when There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In what situations would There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It really wound't ever be I added the |
||||||
supportsOffscreenCanvas = window.OffscreenCanvas && | ||||||
new window.OffscreenCanvas(1, 1).getContext('2d') && | ||||||
typeof window.createImageBitmap === 'function'; | ||||||
} | ||||||
|
||||||
return supportsOffscreenCanvas; | ||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: newline