@loaders.gl/images
Version:
Framework-independent loaders and writers for images (PNG, JPG, ...)
613 lines (594 loc) • 18.8 kB
JavaScript
;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// dist/index.js
var dist_exports = {};
__export(dist_exports, {
ImageLoader: () => ImageLoader,
ImageWriter: () => ImageWriter,
getBinaryImageMetadata: () => getBinaryImageMetadata,
getDefaultImageType: () => getDefaultImageType,
getImageData: () => getImageData,
getImageSize: () => getImageSize,
getImageType: () => getImageType,
getSupportedImageFormats: () => getSupportedImageFormats,
isImage: () => isImage,
isImageFormatSupported: () => isImageFormatSupported,
isImageTypeSupported: () => isImageTypeSupported,
loadImage: () => loadImage
});
module.exports = __toCommonJS(dist_exports);
// dist/lib/utils/version.js
var VERSION = true ? "4.3.2" : "latest";
// dist/lib/parsers/parse-image.js
var import_loader_utils3 = require("@loaders.gl/loader-utils");
// dist/lib/category-api/image-type.js
var import_loader_utils = require("@loaders.gl/loader-utils");
var _a;
var parseImageNode = (_a = globalThis.loaders) == null ? void 0 : _a.parseImageNode;
var IMAGE_SUPPORTED = typeof Image !== "undefined";
var IMAGE_BITMAP_SUPPORTED = typeof ImageBitmap !== "undefined";
var NODE_IMAGE_SUPPORTED = Boolean(parseImageNode);
var DATA_SUPPORTED = import_loader_utils.isBrowser ? true : NODE_IMAGE_SUPPORTED;
function isImageTypeSupported(type) {
switch (type) {
case "auto":
return IMAGE_BITMAP_SUPPORTED || IMAGE_SUPPORTED || DATA_SUPPORTED;
case "imagebitmap":
return IMAGE_BITMAP_SUPPORTED;
case "image":
return IMAGE_SUPPORTED;
case "data":
return DATA_SUPPORTED;
default:
throw new Error(`@loaders.gl/images: image ${type} not supported in this environment`);
}
}
function getDefaultImageType() {
if (IMAGE_BITMAP_SUPPORTED) {
return "imagebitmap";
}
if (IMAGE_SUPPORTED) {
return "image";
}
if (DATA_SUPPORTED) {
return "data";
}
throw new Error("Install '@loaders.gl/polyfills' to parse images under Node.js");
}
// dist/lib/category-api/parsed-image-api.js
function isImage(image) {
return Boolean(getImageTypeOrNull(image));
}
function getImageType(image) {
const format = getImageTypeOrNull(image);
if (!format) {
throw new Error("Not an image");
}
return format;
}
function getImageSize(image) {
return getImageData(image);
}
function getImageData(image) {
switch (getImageType(image)) {
case "data":
return image;
case "image":
case "imagebitmap":
const canvas = document.createElement("canvas");
const context = canvas.getContext("2d");
if (!context) {
throw new Error("getImageData");
}
canvas.width = image.width;
canvas.height = image.height;
context.drawImage(image, 0, 0);
return context.getImageData(0, 0, image.width, image.height);
default:
throw new Error("getImageData");
}
}
function getImageTypeOrNull(image) {
if (typeof ImageBitmap !== "undefined" && image instanceof ImageBitmap) {
return "imagebitmap";
}
if (typeof Image !== "undefined" && image instanceof Image) {
return "image";
}
if (image && typeof image === "object" && image.data && image.width && image.height) {
return "data";
}
return null;
}
// dist/lib/parsers/svg-utils.js
var SVG_DATA_URL_PATTERN = /^data:image\/svg\+xml/;
var SVG_URL_PATTERN = /\.svg((\?|#).*)?$/;
function isSVG(url) {
return url && (SVG_DATA_URL_PATTERN.test(url) || SVG_URL_PATTERN.test(url));
}
function getBlobOrSVGDataUrl(arrayBuffer, url) {
if (isSVG(url)) {
const textDecoder = new TextDecoder();
let xmlText = textDecoder.decode(arrayBuffer);
try {
if (typeof unescape === "function" && typeof encodeURIComponent === "function") {
xmlText = unescape(encodeURIComponent(xmlText));
}
} catch (error) {
throw new Error(error.message);
}
const src = `data:image/svg+xml;base64,${btoa(xmlText)}`;
return src;
}
return getBlob(arrayBuffer, url);
}
function getBlob(arrayBuffer, url) {
if (isSVG(url)) {
throw new Error("SVG cannot be parsed directly to imagebitmap");
}
return new Blob([new Uint8Array(arrayBuffer)]);
}
// dist/lib/parsers/parse-to-image.js
async function parseToImage(arrayBuffer, options, url) {
const blobOrDataUrl = getBlobOrSVGDataUrl(arrayBuffer, url);
const URL = self.URL || self.webkitURL;
const objectUrl = typeof blobOrDataUrl !== "string" && URL.createObjectURL(blobOrDataUrl);
try {
return await loadToImage(objectUrl || blobOrDataUrl, options);
} finally {
if (objectUrl) {
URL.revokeObjectURL(objectUrl);
}
}
}
async function loadToImage(url, options) {
const image = new Image();
image.src = url;
if (options.image && options.image.decode && image.decode) {
await image.decode();
return image;
}
return await new Promise((resolve, reject) => {
try {
image.onload = () => resolve(image);
image.onerror = (error) => {
const message = error instanceof Error ? error.message : "error";
reject(new Error(message));
};
} catch (error) {
reject(error);
}
});
}
// dist/lib/parsers/parse-to-image-bitmap.js
var EMPTY_OBJECT = {};
var imagebitmapOptionsSupported = true;
async function parseToImageBitmap(arrayBuffer, options, url) {
let blob;
if (isSVG(url)) {
const image = await parseToImage(arrayBuffer, options, url);
blob = image;
} else {
blob = getBlob(arrayBuffer, url);
}
const imagebitmapOptions = options && options.imagebitmap;
return await safeCreateImageBitmap(blob, imagebitmapOptions);
}
async function safeCreateImageBitmap(blob, imagebitmapOptions = null) {
if (isEmptyObject(imagebitmapOptions) || !imagebitmapOptionsSupported) {
imagebitmapOptions = null;
}
if (imagebitmapOptions) {
try {
return await createImageBitmap(blob, imagebitmapOptions);
} catch (error) {
console.warn(error);
imagebitmapOptionsSupported = false;
}
}
return await createImageBitmap(blob);
}
function isEmptyObject(object) {
for (const key in object || EMPTY_OBJECT) {
return false;
}
return true;
}
// dist/lib/parsers/parse-to-node-image.js
var import_loader_utils2 = require("@loaders.gl/loader-utils");
// dist/lib/category-api/parse-isobmff-binary.js
function getISOBMFFMediaType(buffer) {
if (!checkString(buffer, "ftyp", 4)) {
return null;
}
if ((buffer[8] & 96) === 0) {
return null;
}
return decodeMajorBrand(buffer);
}
function decodeMajorBrand(buffer) {
const brandMajor = getUTF8String(buffer, 8, 12).replace("\0", " ").trim();
switch (brandMajor) {
case "avif":
case "avis":
return { extension: "avif", mimeType: "image/avif" };
default:
return null;
}
}
function getUTF8String(array, start, end) {
return String.fromCharCode(...array.slice(start, end));
}
function stringToBytes(string) {
return [...string].map((character) => character.charCodeAt(0));
}
function checkString(buffer, header, offset = 0) {
const headerBytes = stringToBytes(header);
for (let i = 0; i < headerBytes.length; ++i) {
if (headerBytes[i] !== buffer[i + offset]) {
return false;
}
}
return true;
}
// dist/lib/category-api/binary-image-api.js
var BIG_ENDIAN = false;
var LITTLE_ENDIAN = true;
function getBinaryImageMetadata(binaryData) {
const dataView = toDataView(binaryData);
return getPngMetadata(dataView) || getJpegMetadata(dataView) || getGifMetadata(dataView) || getBmpMetadata(dataView) || getISOBMFFMetadata(dataView);
}
function getISOBMFFMetadata(binaryData) {
const buffer = new Uint8Array(binaryData instanceof DataView ? binaryData.buffer : binaryData);
const mediaType = getISOBMFFMediaType(buffer);
if (!mediaType) {
return null;
}
return {
mimeType: mediaType.mimeType,
// TODO - decode width and height
width: 0,
height: 0
};
}
function getPngMetadata(binaryData) {
const dataView = toDataView(binaryData);
const isPng = dataView.byteLength >= 24 && dataView.getUint32(0, BIG_ENDIAN) === 2303741511;
if (!isPng) {
return null;
}
return {
mimeType: "image/png",
width: dataView.getUint32(16, BIG_ENDIAN),
height: dataView.getUint32(20, BIG_ENDIAN)
};
}
function getGifMetadata(binaryData) {
const dataView = toDataView(binaryData);
const isGif = dataView.byteLength >= 10 && dataView.getUint32(0, BIG_ENDIAN) === 1195984440;
if (!isGif) {
return null;
}
return {
mimeType: "image/gif",
width: dataView.getUint16(6, LITTLE_ENDIAN),
height: dataView.getUint16(8, LITTLE_ENDIAN)
};
}
function getBmpMetadata(binaryData) {
const dataView = toDataView(binaryData);
const isBmp = dataView.byteLength >= 14 && dataView.getUint16(0, BIG_ENDIAN) === 16973 && dataView.getUint32(2, LITTLE_ENDIAN) === dataView.byteLength;
if (!isBmp) {
return null;
}
return {
mimeType: "image/bmp",
width: dataView.getUint32(18, LITTLE_ENDIAN),
height: dataView.getUint32(22, LITTLE_ENDIAN)
};
}
function getJpegMetadata(binaryData) {
const dataView = toDataView(binaryData);
const isJpeg = dataView.byteLength >= 3 && dataView.getUint16(0, BIG_ENDIAN) === 65496 && dataView.getUint8(2) === 255;
if (!isJpeg) {
return null;
}
const { tableMarkers, sofMarkers } = getJpegMarkers();
let i = 2;
while (i + 9 < dataView.byteLength) {
const marker = dataView.getUint16(i, BIG_ENDIAN);
if (sofMarkers.has(marker)) {
return {
mimeType: "image/jpeg",
height: dataView.getUint16(i + 5, BIG_ENDIAN),
// Number of lines
width: dataView.getUint16(i + 7, BIG_ENDIAN)
// Number of pixels per line
};
}
if (!tableMarkers.has(marker)) {
return null;
}
i += 2;
i += dataView.getUint16(i, BIG_ENDIAN);
}
return null;
}
function getJpegMarkers() {
const tableMarkers = /* @__PURE__ */ new Set([65499, 65476, 65484, 65501, 65534]);
for (let i = 65504; i < 65520; ++i) {
tableMarkers.add(i);
}
const sofMarkers = /* @__PURE__ */ new Set([
65472,
65473,
65474,
65475,
65477,
65478,
65479,
65481,
65482,
65483,
65485,
65486,
65487,
65502
]);
return { tableMarkers, sofMarkers };
}
function toDataView(data) {
if (data instanceof DataView) {
return data;
}
if (ArrayBuffer.isView(data)) {
return new DataView(data.buffer);
}
if (data instanceof ArrayBuffer) {
return new DataView(data);
}
throw new Error("toDataView");
}
// dist/lib/parsers/parse-to-node-image.js
async function parseToNodeImage(arrayBuffer, options) {
var _a3;
const { mimeType } = getBinaryImageMetadata(arrayBuffer) || {};
const parseImageNode2 = (_a3 = globalThis.loaders) == null ? void 0 : _a3.parseImageNode;
(0, import_loader_utils2.assert)(parseImageNode2);
return await parseImageNode2(arrayBuffer, mimeType);
}
// dist/lib/parsers/parse-image.js
async function parseImage(arrayBuffer, options, context) {
options = options || {};
const imageOptions = options.image || {};
const imageType = imageOptions.type || "auto";
const { url } = context || {};
const loadType = getLoadableImageType(imageType);
let image;
switch (loadType) {
case "imagebitmap":
image = await parseToImageBitmap(arrayBuffer, options, url);
break;
case "image":
image = await parseToImage(arrayBuffer, options, url);
break;
case "data":
image = await parseToNodeImage(arrayBuffer, options);
break;
default:
(0, import_loader_utils3.assert)(false);
}
if (imageType === "data") {
image = getImageData(image);
}
return image;
}
function getLoadableImageType(type) {
switch (type) {
case "auto":
case "data":
return getDefaultImageType();
default:
isImageTypeSupported(type);
return type;
}
}
// dist/image-loader.js
var EXTENSIONS = ["png", "jpg", "jpeg", "gif", "webp", "bmp", "ico", "svg", "avif"];
var MIME_TYPES = [
"image/png",
"image/jpeg",
"image/gif",
"image/webp",
"image/avif",
"image/bmp",
"image/vnd.microsoft.icon",
"image/svg+xml"
];
var DEFAULT_IMAGE_LOADER_OPTIONS = {
image: {
type: "auto",
decode: true
// if format is HTML
}
// imagebitmap: {} - passes (platform dependent) parameters to ImageBitmap constructor
};
var ImageLoader = {
dataType: null,
batchType: null,
id: "image",
module: "images",
name: "Images",
version: VERSION,
mimeTypes: MIME_TYPES,
extensions: EXTENSIONS,
parse: parseImage,
// TODO: byteOffset, byteLength;
tests: [(arrayBuffer) => Boolean(getBinaryImageMetadata(new DataView(arrayBuffer)))],
options: DEFAULT_IMAGE_LOADER_OPTIONS
};
// dist/lib/encoders/encode-image.js
var _a2;
var encodeImageNode = (_a2 = globalThis.loaders) == null ? void 0 : _a2.encodeImageNode;
async function encodeImage(image, options) {
options = options || {};
options.image = options.image || {};
return encodeImageNode ? encodeImageNode(image, { type: options.image.mimeType }) : encodeImageInBrowser(image, options);
}
var qualityParamSupported = true;
async function encodeImageInBrowser(image, options) {
const { mimeType, jpegQuality } = options.image;
const { width, height } = getImageSize(image);
const canvas = document.createElement("canvas");
canvas.width = width;
canvas.height = height;
drawImageToCanvas(image, canvas);
const blob = await new Promise((resolve) => {
if (jpegQuality && qualityParamSupported) {
try {
canvas.toBlob(resolve, mimeType, jpegQuality);
return;
} catch (error) {
qualityParamSupported = false;
}
}
canvas.toBlob(resolve, mimeType);
});
if (!blob) {
throw new Error("image encoding failed");
}
return await blob.arrayBuffer();
}
function drawImageToCanvas(image, canvas, x = 0, y = 0) {
if (x === 0 && y === 0 && typeof ImageBitmap !== "undefined" && image instanceof ImageBitmap) {
const context2 = canvas.getContext("bitmaprenderer");
if (context2) {
context2.transferFromImageBitmap(image);
return canvas;
}
}
const context = canvas.getContext("2d");
if (image.data) {
const clampedArray = new Uint8ClampedArray(image.data);
const imageData = new ImageData(clampedArray, image.width, image.height);
context.putImageData(imageData, 0, 0);
return canvas;
}
context.drawImage(image, 0, 0);
return canvas;
}
// dist/image-writer.js
var ImageWriter = {
name: "Images",
id: "image",
module: "images",
version: VERSION,
extensions: ["jpeg"],
options: {
image: {
mimeType: "image/png",
jpegQuality: null
}
},
encode: encodeImage
};
// dist/lib/category-api/image-format.js
var import_loader_utils4 = require("@loaders.gl/loader-utils");
var MIME_TYPES2 = [
"image/png",
"image/jpeg",
"image/gif",
"image/webp",
"image/avif",
"image/tiff",
// TODO - what is the correct type for SVG
"image/svg",
"image/svg+xml",
"image/bmp",
"image/vnd.microsoft.icon"
];
var mimeTypeSupportedPromise = null;
async function getSupportedImageFormats() {
if (mimeTypeSupportedPromise) {
return await mimeTypeSupportedPromise;
}
const supportedMimeTypes = /* @__PURE__ */ new Set();
for (const mimeType of MIME_TYPES2) {
const supported = import_loader_utils4.isBrowser ? await checkBrowserImageFormatSupportAsync(mimeType) : checkNodeImageFormatSupport(mimeType);
if (supported) {
supportedMimeTypes.add(mimeType);
}
}
return supportedMimeTypes;
}
var mimeTypeSupportedSync = {};
function isImageFormatSupported(mimeType) {
if (mimeTypeSupportedSync[mimeType] === void 0) {
const supported = import_loader_utils4.isBrowser ? checkBrowserImageFormatSupport(mimeType) : checkNodeImageFormatSupport(mimeType);
mimeTypeSupportedSync[mimeType] = supported;
}
return mimeTypeSupportedSync[mimeType];
}
function checkNodeImageFormatSupport(mimeType) {
var _a3, _b;
const NODE_FORMAT_SUPPORT = ["image/png", "image/jpeg", "image/gif"];
const imageFormatsNode = ((_a3 = globalThis.loaders) == null ? void 0 : _a3.imageFormatsNode) || NODE_FORMAT_SUPPORT;
const parseImageNode2 = (_b = globalThis.loaders) == null ? void 0 : _b.parseImageNode;
return Boolean(parseImageNode2) && imageFormatsNode.includes(mimeType);
}
function checkBrowserImageFormatSupport(mimeType) {
switch (mimeType) {
case "image/avif":
case "image/webp":
return testBrowserImageFormatSupport(mimeType);
default:
return true;
}
}
var TEST_IMAGE = {
"image/avif": "data:image/avif;base64,AAAAIGZ0eXBhdmlmAAAAAGF2aWZtaWYxbWlhZk1BMUIAAADybWV0YQAAAAAAAAAoaGRscgAAAAAAAAAAcGljdAAAAAAAAAAAAAAAAGxpYmF2aWYAAAAADnBpdG0AAAAAAAEAAAAeaWxvYwAAAABEAAABAAEAAAABAAABGgAAAB0AAAAoaWluZgAAAAAAAQAAABppbmZlAgAAAAABAABhdjAxQ29sb3IAAAAAamlwcnAAAABLaXBjbwAAABRpc3BlAAAAAAAAAAIAAAACAAAAEHBpeGkAAAAAAwgICAAAAAxhdjFDgQ0MAAAAABNjb2xybmNseAACAAIAAYAAAAAXaXBtYQAAAAAAAAABAAEEAQKDBAAAACVtZGF0EgAKCBgANogQEAwgMg8f8D///8WfhwB8+ErK42A=",
// Lossy test image. Support for lossy images doesn't guarantee support for all WebP images.
"image/webp": "data:image/webp;base64,UklGRiIAAABXRUJQVlA4IBYAAAAwAQCdASoBAAEADsD+JaQAA3AAAAAA"
};
async function checkBrowserImageFormatSupportAsync(mimeType) {
const dataURL = TEST_IMAGE[mimeType];
return dataURL ? await testBrowserImageFormatSupportAsync(dataURL) : true;
}
function testBrowserImageFormatSupport(mimeType) {
try {
const element = document.createElement("canvas");
const dataURL = element.toDataURL(mimeType);
return dataURL.indexOf(`data:${mimeType}`) === 0;
} catch {
return false;
}
}
async function testBrowserImageFormatSupportAsync(testImageDataURL) {
return new Promise((resolve) => {
const image = new Image();
image.src = testImageDataURL;
image.onload = () => resolve(image.height > 0);
image.onerror = () => resolve(false);
});
}
// dist/index.js
function loadImage() {
throw new Error("loadImage has moved to @loaders.gl/textures");
}
//# sourceMappingURL=index.cjs.map