s2-tools
Version:
A collection of geospatial tools primarily designed for WGS84, Web Mercator, and S2.
126 lines • 4.9 kB
JavaScript
/* eslint-disable no-var */
import sharp from 'sharp';
/** Build a local ImageBitmap for reading the data */
class ImageBitmap {
data;
width;
height;
/**
* @param data - the image data
* @param width - the image width
* @param height - the image height
*/
constructor(data, width, height) {
this.data = data;
this.width = width;
this.height = height;
}
}
/**
* @param blob - the blob input data
* @returns an ImageBitmap
*/
async function createImageBitmap(blob) {
const imageBuffer = Buffer.from(await blob.arrayBuffer());
// Decode image using sharp
const decodedImage = await sharp(imageBuffer).raw().toBuffer({ resolveWithObject: true });
const { data, info: { width, height }, } = decodedImage;
return new ImageBitmap(new Uint8Array(data), width, height);
}
/** An offscreen canvas polyfill */
class OffscreenCanvasPolyfill {
width;
height;
/**
* @param width - the canvas width
* @param height - the canvas height
*/
constructor(width, height) {
this.width = width;
this.height = height;
}
/**
* @param type - expect '2d' or throw null
* @returns the OffscreenCanvasRenderingContext2D
*/
getContext(type) {
if (type !== '2d')
return null;
return new OffscreenCanvasRenderingContext2D(this.width, this.height);
}
}
/** An offscreen canvas rendering context polyfill */
class OffscreenCanvasRenderingContext2D {
width;
height;
data = new Uint8ClampedArray();
/**
* @param width - the canvas width
* @param height - the canvas height
*/
constructor(width, height) {
this.width = width;
this.height = height;
}
/**
* Draw an ImageBitmap onto the canvas at position (dx, dy).
* Copies the pixel data from the ImageBitmap to the canvas buffer.
* @param image - the image to draw
* @param dx - the x position on the canvas
* @param dy - the y position on the canvas
*/
drawImage(image, dx, dy) {
const { width: imgWidth, height: imgHeight, data: imgData } = image;
const channels = imgData.length / (imgWidth * imgHeight);
this.data = new Uint8ClampedArray(imgWidth * imgHeight * channels);
for (let y = 0; y < imgHeight; y++) {
for (let x = 0; x < imgWidth; x++) {
const imgIndex = (y * imgWidth + x) * channels; // Index in the image data (RGBA)
const canvasX = dx + x;
const canvasY = dy + y;
if (canvasX >= 0 && canvasX < this.width && canvasY >= 0 && canvasY < this.height) {
const canvasIndex = (canvasY * this.width + canvasX) * channels; // Index in the canvas data
// Copy RGBA values from the image to the canvas
this.data[canvasIndex] = imgData[imgIndex]; // R
this.data[canvasIndex + 1] = imgData[imgIndex + 1]; // G
this.data[canvasIndex + 2] = imgData[imgIndex + 2]; // B
if (channels === 4)
this.data[canvasIndex + 3] = imgData[imgIndex + 3]; // A
}
}
}
}
/**
* Get the ImageData for a specific region of the canvas.
* @param x - The x coordinate of the region's top-left corner
* @param y - The y coordinate of the region's top-left corner
* @param width - The width of the region
* @param height - The height of the region
* @returns the ImageData
*/
getImageData(x, y, width, height) {
const channels = this.data.length / (this.width * this.height);
const size = width * height * channels;
if (this.data.length === size)
return { data: this.data.slice(0, size), width, height };
const imageData = new Uint8ClampedArray(size);
for (let row = 0; row < height; row++) {
for (let col = 0; col < width; col++) {
const canvasX = x + col;
const canvasY = y + row;
const canvasIndex = (canvasY * this.width + canvasX) * channels;
const imageDataIndex = (row * width + col) * channels;
// Copy RGBA values from the canvas to the imageData
imageData[imageDataIndex] = this.data[canvasIndex]; // R
imageData[imageDataIndex + 1] = this.data[canvasIndex + 1]; // G
imageData[imageDataIndex + 2] = this.data[canvasIndex + 2]; // B
if (channels === 4)
imageData[imageDataIndex + 3] = this.data[canvasIndex + 3]; // A
}
}
return { data: imageData, width, height };
}
}
globalThis.createImageBitmap ??= createImageBitmap;
globalThis.OffscreenCanvas ??= OffscreenCanvasPolyfill;
//# sourceMappingURL=image.js.map