@tldraw/utils
Version:
tldraw infinite canvas SDK (private utilities).
245 lines (244 loc) • 8.38 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);
var media_exports = {};
__export(media_exports, {
DEFAULT_SUPPORTED_ANIMATED_IMAGE_TYPES: () => DEFAULT_SUPPORTED_ANIMATED_IMAGE_TYPES,
DEFAULT_SUPPORTED_IMAGE_TYPES: () => DEFAULT_SUPPORTED_IMAGE_TYPES,
DEFAULT_SUPPORTED_MEDIA_TYPES: () => DEFAULT_SUPPORTED_MEDIA_TYPES,
DEFAULT_SUPPORTED_MEDIA_TYPE_LIST: () => DEFAULT_SUPPORTED_MEDIA_TYPE_LIST,
DEFAULT_SUPPORTED_STATIC_IMAGE_TYPES: () => DEFAULT_SUPPORTED_STATIC_IMAGE_TYPES,
DEFAULT_SUPPORTED_VECTOR_IMAGE_TYPES: () => DEFAULT_SUPPORTED_VECTOR_IMAGE_TYPES,
DEFAULT_SUPPORT_VIDEO_TYPES: () => DEFAULT_SUPPORT_VIDEO_TYPES,
MediaHelpers: () => MediaHelpers
});
module.exports = __toCommonJS(media_exports);
var import_control = require("../control");
var import_network = require("../network");
var import_apng = require("./apng");
var import_avif = require("./avif");
var import_gif = require("./gif");
var import_png = require("./png");
var import_webp = require("./webp");
const DEFAULT_SUPPORTED_VECTOR_IMAGE_TYPES = Object.freeze(["image/svg+xml"]);
const DEFAULT_SUPPORTED_STATIC_IMAGE_TYPES = Object.freeze([
"image/jpeg",
"image/png",
"image/webp"
]);
const DEFAULT_SUPPORTED_ANIMATED_IMAGE_TYPES = Object.freeze([
"image/gif",
"image/apng",
"image/avif"
]);
const DEFAULT_SUPPORTED_IMAGE_TYPES = Object.freeze([
...DEFAULT_SUPPORTED_STATIC_IMAGE_TYPES,
...DEFAULT_SUPPORTED_VECTOR_IMAGE_TYPES,
...DEFAULT_SUPPORTED_ANIMATED_IMAGE_TYPES
]);
const DEFAULT_SUPPORT_VIDEO_TYPES = Object.freeze([
"video/mp4",
"video/webm",
"video/quicktime"
]);
const DEFAULT_SUPPORTED_MEDIA_TYPES = Object.freeze([
...DEFAULT_SUPPORTED_IMAGE_TYPES,
...DEFAULT_SUPPORT_VIDEO_TYPES
]);
const DEFAULT_SUPPORTED_MEDIA_TYPE_LIST = DEFAULT_SUPPORTED_MEDIA_TYPES.join(",");
class MediaHelpers {
/**
* Load a video from a url.
* @public
*/
static loadVideo(src) {
return new Promise((resolve, reject) => {
const video = document.createElement("video");
video.onloadeddata = () => resolve(video);
video.onerror = (e) => {
console.error(e);
reject(new Error("Could not load video"));
};
video.crossOrigin = "anonymous";
video.src = src;
});
}
static async getVideoFrameAsDataUrl(video, time = 0) {
const promise = (0, import_control.promiseWithResolve)();
let didSetTime = false;
const onReadyStateChanged = () => {
if (!didSetTime) {
if (video.readyState >= video.HAVE_METADATA) {
didSetTime = true;
video.currentTime = time;
} else {
return;
}
}
if (video.readyState >= video.HAVE_CURRENT_DATA) {
const canvas = document.createElement("canvas");
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
const ctx = canvas.getContext("2d");
if (!ctx) {
throw new Error("Could not get 2d context");
}
ctx.drawImage(video, 0, 0);
promise.resolve(canvas.toDataURL());
}
};
const onError = (e) => {
console.error(e);
promise.reject(new Error("Could not get video frame"));
};
video.addEventListener("loadedmetadata", onReadyStateChanged);
video.addEventListener("loadeddata", onReadyStateChanged);
video.addEventListener("canplay", onReadyStateChanged);
video.addEventListener("seeked", onReadyStateChanged);
video.addEventListener("error", onError);
video.addEventListener("stalled", onError);
onReadyStateChanged();
try {
return await promise;
} finally {
video.removeEventListener("loadedmetadata", onReadyStateChanged);
video.removeEventListener("loadeddata", onReadyStateChanged);
video.removeEventListener("canplay", onReadyStateChanged);
video.removeEventListener("seeked", onReadyStateChanged);
video.removeEventListener("error", onError);
video.removeEventListener("stalled", onError);
}
}
/**
* Load an image from a url.
* @public
*/
static getImageAndDimensions(src) {
return new Promise((resolve, reject) => {
const img = (0, import_network.Image)();
img.onload = () => {
let dimensions;
if (img.naturalWidth) {
dimensions = {
w: img.naturalWidth,
h: img.naturalHeight
};
} else {
document.body.appendChild(img);
dimensions = {
w: img.clientWidth,
h: img.clientHeight
};
document.body.removeChild(img);
}
resolve({ ...dimensions, image: img });
};
img.onerror = (e) => {
console.error(e);
reject(new Error("Could not load image"));
};
img.crossOrigin = "anonymous";
img.referrerPolicy = "strict-origin-when-cross-origin";
img.style.visibility = "hidden";
img.style.position = "absolute";
img.style.opacity = "0";
img.style.zIndex = "-9999";
img.src = src;
});
}
/**
* Get the size of a video blob
*
* @param blob - A SharedBlob containing the video
* @public
*/
static async getVideoSize(blob) {
return MediaHelpers.usingObjectURL(blob, async (url) => {
const video = await MediaHelpers.loadVideo(url);
return { w: video.videoWidth, h: video.videoHeight };
});
}
/**
* Get the size of an image blob
*
* @param blob - A Blob containing the image.
* @public
*/
static async getImageSize(blob) {
const { w, h } = await MediaHelpers.usingObjectURL(blob, MediaHelpers.getImageAndDimensions);
try {
if (blob.type === "image/png") {
const view = new DataView(await blob.arrayBuffer());
if (import_png.PngHelpers.isPng(view, 0)) {
const physChunk = import_png.PngHelpers.findChunk(view, "pHYs");
if (physChunk) {
const physData = import_png.PngHelpers.parsePhys(view, physChunk.dataOffset);
if (physData.unit === 1 && physData.ppux === physData.ppuy) {
const pixelsPerMeter = 72 / 0.0254;
const pixelRatio = Math.max(physData.ppux / pixelsPerMeter, 1);
return {
w: Math.round(w / pixelRatio),
h: Math.round(h / pixelRatio)
};
}
}
}
}
} catch (err) {
console.error(err);
return { w, h };
}
return { w, h };
}
static async isAnimated(file) {
if (file.type === "image/gif") {
return (0, import_gif.isGifAnimated)(await file.arrayBuffer());
}
if (file.type === "image/avif") {
return (0, import_avif.isAvifAnimated)(await file.arrayBuffer());
}
if (file.type === "image/webp") {
return (0, import_webp.isWebpAnimated)(await file.arrayBuffer());
}
if (file.type === "image/apng") {
return (0, import_apng.isApngAnimated)(await file.arrayBuffer());
}
return false;
}
static isAnimatedImageType(mimeType) {
return DEFAULT_SUPPORTED_ANIMATED_IMAGE_TYPES.includes(mimeType || "");
}
static isStaticImageType(mimeType) {
return DEFAULT_SUPPORTED_STATIC_IMAGE_TYPES.includes(mimeType || "");
}
static isVectorImageType(mimeType) {
return DEFAULT_SUPPORTED_VECTOR_IMAGE_TYPES.includes(mimeType || "");
}
static isImageType(mimeType) {
return DEFAULT_SUPPORTED_IMAGE_TYPES.includes(mimeType || "");
}
static async usingObjectURL(blob, fn) {
const url = URL.createObjectURL(blob);
try {
return await fn(url);
} finally {
URL.revokeObjectURL(url);
}
}
}
//# sourceMappingURL=media.js.map