tldraw
Version:
A tiny little drawing editor.
122 lines (121 loc) • 4.18 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 ImageAlphaCache_exports = {};
__export(ImageAlphaCache_exports, {
TRANSPARENT_IMAGE_MIMETYPES: () => TRANSPARENT_IMAGE_MIMETYPES,
getAlphaData: () => getAlphaData,
isImagePointTransparent: () => isImagePointTransparent,
isPointTransparent: () => isPointTransparent,
preloadAlphaData: () => preloadAlphaData
});
module.exports = __toCommonJS(ImageAlphaCache_exports);
var import_editor = require("@tldraw/editor");
const TRANSPARENT_IMAGE_MIMETYPES = [
"image/png",
"image/webp",
"image/gif",
"image/avif"
];
function mapToImageCoords(config, point, bounds) {
let nx = Math.max(0, Math.min(1, (point.x - bounds.minX) / bounds.w));
let ny = Math.max(0, Math.min(1, (point.y - bounds.minY) / bounds.h));
if (config.crop) {
const { topLeft, bottomRight } = config.crop;
nx = topLeft.x + nx * (bottomRight.x - topLeft.x);
ny = topLeft.y + ny * (bottomRight.y - topLeft.y);
}
if (config.flipX) nx = 1 - nx;
if (config.flipY) ny = 1 - ny;
return { nx, ny };
}
function isImagePointTransparent(config, point, bounds) {
const data = config.alphaDataGetter();
if (!data) return false;
const { nx, ny } = mapToImageCoords(config, point, bounds);
return isPointTransparent(data, nx, ny);
}
const MAX_SIZE = 256;
const alphaCache = /* @__PURE__ */ new Map();
const pending = /* @__PURE__ */ new Set();
let offscreenCanvas = null;
function getOffscreenCanvas(w, h) {
if (!offscreenCanvas) {
offscreenCanvas = new OffscreenCanvas(w, h);
} else {
offscreenCanvas.width = w;
offscreenCanvas.height = h;
}
return offscreenCanvas;
}
function extractAlphas(ctx, w, h) {
const imageData = ctx.getImageData(0, 0, w, h);
const pixels = new Uint32Array(imageData.data.buffer);
const alphas = new Uint8Array(w * h);
for (let i = 0; i < alphas.length; i++) {
alphas[i] = pixels[i] >>> 24;
}
return alphas;
}
function preloadAlphaData(url, cacheKey) {
const key = cacheKey ?? url;
if (alphaCache.has(key) || pending.has(key)) return;
pending.add(key);
const img = (0, import_editor.Image)();
img.crossOrigin = "anonymous";
img.onload = async () => {
pending.delete(key);
const { width: origW, height: origH } = img;
if (origW === 0 || origH === 0) return;
const scale = Math.min(1, MAX_SIZE / Math.max(origW, origH));
const w = Math.max(1, Math.round(origW * scale));
const h = Math.max(1, Math.round(origH * scale));
let bitmap = null;
try {
bitmap = await createImageBitmap(img, {
resizeWidth: w,
resizeHeight: h,
resizeQuality: "low"
});
} catch {
}
const canvas = getOffscreenCanvas(w, h);
const ctx = canvas.getContext("2d");
if (!ctx) return;
if (bitmap) {
ctx.drawImage(bitmap, 0, 0);
bitmap.close();
} else {
ctx.drawImage(img, 0, 0, w, h);
}
alphaCache.set(key, { width: w, height: h, alphas: extractAlphas(ctx, w, h) });
};
img.onerror = () => {
pending.delete(key);
};
img.src = url;
}
function getAlphaData(src) {
return alphaCache.get(src) ?? null;
}
function isPointTransparent(data, nx, ny, threshold = 10) {
const ix = Math.min(Math.floor(nx * data.width), data.width - 1);
const iy = Math.min(Math.floor(ny * data.height), data.height - 1);
return data.alphas[iy * data.width + ix] < threshold;
}
//# sourceMappingURL=ImageAlphaCache.js.map