UNPKG

js-draw

Version:

Draw pictures using a pen, touchscreen, or mouse! JS-draw is a drawing library for JavaScript and TypeScript.

163 lines (162 loc) 5.78 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const math_1 = require("@js-draw/math"); const assertions_1 = require("../util/assertions"); const AbstractComponent_1 = __importDefault(require("./AbstractComponent")); const waitForImageLoaded_1 = __importDefault(require("../util/waitForImageLoaded")); /** * Represents a raster image. * * **Example: Adding images**: * [[include:doc-pages/inline-examples/adding-an-image-and-data-urls.md]] */ class ImageComponent extends AbstractComponent_1.default { constructor(image) { super('image-component'); this.image = { ...image, label: image.label ?? image.image.getAttribute('alt') ?? image.image.getAttribute('aria-label') ?? undefined, }; const isHTMLImageElem = (elem) => { return elem.getAttribute('src') !== undefined; }; if (isHTMLImageElem(image.image) && !image.image.complete) { image.image.onload = () => this.recomputeBBox(); } this.recomputeBBox(); } getImageRect() { return new math_1.Rect2(0, 0, this.image.image.width, this.image.image.height); } recomputeBBox() { this.contentBBox = this.getImageRect(); this.contentBBox = this.contentBBox.transformedBoundingBox(this.image.transform); } /** * Load from an image. Waits for the image to load if incomplete. * * The image, `elem`, must not [taint](https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image#security_and_tainted_canvases) * an HTMLCanvasElement when rendered. */ static async fromImage(elem, transform) { await (0, waitForImageLoaded_1.default)(elem); let width, height; if (typeof elem.width === 'number' && typeof elem.height === 'number' && elem.width !== 0 && elem.height !== 0) { width = elem.width; height = elem.height; } else { width = elem.clientWidth; height = elem.clientHeight; } let image; let url = elem.src ?? ''; if (!url.startsWith('data:image/')) { // Convert to a data URL: const canvas = document.createElement('canvas'); canvas.width = width; canvas.height = height; const ctx = canvas.getContext('2d'); ctx.drawImage(elem, 0, 0, canvas.width, canvas.height); url = canvas.toDataURL(); image = canvas; } else { image = new Image(); image.src = url; image.width = width; image.height = height; } image.setAttribute('alt', elem.getAttribute('alt') ?? ''); image.setAttribute('aria-label', elem.getAttribute('aria-label') ?? ''); return new ImageComponent({ image, base64Url: url, transform: transform, }); } render(canvas, _visibleRect) { canvas.startObject(this.contentBBox); canvas.drawImage(this.image); canvas.endObject(this.getLoadSaveData()); } // A *very* rough estimate of how long it takes to render this component getProportionalRenderingTime() { // Estimate: Equivalent to a stroke with 10 segments. return 10; } intersects(lineSegment) { const rect = this.getImageRect(); const edges = rect.getEdges().map((edge) => edge.transformedBy(this.image.transform)); for (const edge of edges) { if (edge.intersects(lineSegment)) { return true; } } return false; } applyTransformation(affineTransfm) { this.image.transform = affineTransfm.rightMul(this.image.transform); this.recomputeBBox(); } description(localizationTable) { return this.image.label ? localizationTable.imageNode(this.image.label) : localizationTable.unlabeledImageNode; } getAltText() { return this.image.label; } // The base64 image URL of this image. getURL() { return this.image.base64Url; } getTransformation() { return this.image.transform; } createClone() { return new ImageComponent({ ...this.image, }); } serializeToJSON() { return { src: this.image.base64Url, label: this.image.label, // Store the width and height for bounding box computations while the image is loading. width: this.image.image.width, height: this.image.image.height, transform: this.image.transform.toArray(), }; } static deserializeFromJSON(data) { if (!(typeof data.src === 'string')) { throw new Error(`${data} has invalid format! Expected src property.`); } (0, assertions_1.assertIsNumberArray)(data.transform); (0, assertions_1.assertIsNumber)(data.width); (0, assertions_1.assertIsNumber)(data.height); const image = new Image(); image.src = data.src; image.width = data.width; image.height = data.height; const transform = new math_1.Mat33(...data.transform); return new ImageComponent({ image: image, base64Url: data.src, label: data.label, transform, }); } } exports.default = ImageComponent; AbstractComponent_1.default.registerComponent('image-component', ImageComponent.deserializeFromJSON);