fabric
Version:
Object model for HTML5 canvas, and SVG-to-canvas parser. Backed by jsdom and node-canvas.
1 lines • 38.2 kB
Source Map (JSON)
{"version":3,"file":"Image.min.mjs","sources":["../../../src/shapes/Image.ts"],"sourcesContent":["import { getFabricDocument, getEnv } from '../env';\nimport type { BaseFilter } from '../filters/BaseFilter';\nimport { getFilterBackend } from '../filters/FilterBackend';\nimport { SHARED_ATTRIBUTES } from '../parser/attributes';\nimport { parseAttributes } from '../parser/parseAttributes';\nimport type {\n TClassProperties,\n TCrossOrigin,\n TSize,\n Abortable,\n TOptions,\n} from '../typedefs';\nimport { uid } from '../util/internals/uid';\nimport { createCanvasElementFor } from '../util/misc/dom';\nimport { findScaleToCover, findScaleToFit } from '../util/misc/findScaleTo';\nimport type { LoadImageOptions } from '../util/misc/objectEnlive';\nimport {\n enlivenObjectEnlivables,\n enlivenObjects,\n loadImage,\n} from '../util/misc/objectEnlive';\nimport { parsePreserveAspectRatioAttribute } from '../util/misc/svgParsing';\nimport { classRegistry } from '../ClassRegistry';\nimport { FabricObject, cacheProperties } from './Object/FabricObject';\nimport type { FabricObjectProps, SerializedObjectProps } from './Object/types';\nimport type { ObjectEvents } from '../EventTypeDefs';\nimport { WebGLFilterBackend } from '../filters/WebGLFilterBackend';\nimport { FILL, NONE } from '../constants';\nimport { getDocumentFromElement } from '../util/dom_misc';\nimport type { CSSRules } from '../parser/typedefs';\nimport type { Resize } from '../filters/Resize';\nimport type { TCachedFabricObject } from './Object/Object';\nimport { log } from '../util/internals/console';\n\n// @todo Would be nice to have filtering code not imported directly.\n\nexport type ImageSource =\n | HTMLImageElement\n | HTMLVideoElement\n | HTMLCanvasElement;\n\ninterface UniqueImageProps {\n srcFromAttribute: boolean;\n minimumScaleTrigger: number;\n cropX: number;\n cropY: number;\n imageSmoothing: boolean;\n filters: BaseFilter<string, Record<string, any>>[];\n resizeFilter?: Resize;\n}\n\nexport const imageDefaultValues: Partial<TClassProperties<FabricImage>> = {\n strokeWidth: 0,\n srcFromAttribute: false,\n minimumScaleTrigger: 0.5,\n cropX: 0,\n cropY: 0,\n imageSmoothing: true,\n};\n\nexport interface SerializedImageProps extends SerializedObjectProps {\n src: string;\n crossOrigin: TCrossOrigin;\n filters: any[];\n resizeFilter?: any;\n cropX: number;\n cropY: number;\n}\n\nexport interface ImageProps extends FabricObjectProps, UniqueImageProps {}\n\nconst IMAGE_PROPS = ['cropX', 'cropY'] as const;\n\n/**\n * @tutorial {@link http://fabricjs.com/fabric-intro-part-1#images}\n */\nexport class FabricImage<\n Props extends TOptions<ImageProps> = Partial<ImageProps>,\n SProps extends SerializedImageProps = SerializedImageProps,\n EventSpec extends ObjectEvents = ObjectEvents,\n >\n extends FabricObject<Props, SProps, EventSpec>\n implements ImageProps\n{\n /**\n * When calling {@link FabricImage.getSrc}, return value from element src with `element.getAttribute('src')`.\n * This allows for relative urls as image src.\n * @since 2.7.0\n * @type Boolean\n * @default false\n */\n declare srcFromAttribute: boolean;\n\n /**\n * private\n * contains last value of scaleX to detect\n * if the Image got resized after the last Render\n * @type Number\n */\n protected _lastScaleX = 1;\n\n /**\n * private\n * contains last value of scaleY to detect\n * if the Image got resized after the last Render\n * @type Number\n */\n protected _lastScaleY = 1;\n\n /**\n * private\n * contains last value of scaling applied by the apply filter chain\n * @type Number\n */\n protected _filterScalingX = 1;\n\n /**\n * private\n * contains last value of scaling applied by the apply filter chain\n * @type Number\n */\n protected _filterScalingY = 1;\n\n /**\n * minimum scale factor under which any resizeFilter is triggered to resize the image\n * 0 will disable the automatic resize. 1 will trigger automatically always.\n * number bigger than 1 are not implemented yet.\n * @type Number\n */\n declare minimumScaleTrigger: number;\n\n /**\n * key used to retrieve the texture representing this image\n * @since 2.0.0\n * @type String\n * @default\n */\n declare cacheKey: string;\n\n /**\n * Image crop in pixels from original image size.\n * @since 2.0.0\n * @type Number\n * @default\n */\n declare cropX: number;\n\n /**\n * Image crop in pixels from original image size.\n * @since 2.0.0\n * @type Number\n * @default\n */\n declare cropY: number;\n\n /**\n * Indicates whether this canvas will use image smoothing when painting this image.\n * Also influence if the cacheCanvas for this image uses imageSmoothing\n * @since 4.0.0-beta.11\n * @type Boolean\n * @default\n */\n declare imageSmoothing: boolean;\n\n declare preserveAspectRatio: string;\n\n protected declare src: string;\n\n declare filters: BaseFilter<string, Record<string, any>>[];\n declare resizeFilter: Resize;\n\n declare _element: ImageSource;\n declare _filteredEl?: HTMLCanvasElement;\n declare _originalElement: ImageSource;\n\n static type = 'Image';\n\n static cacheProperties = [...cacheProperties, ...IMAGE_PROPS];\n\n static ownDefaults = imageDefaultValues;\n\n static getDefaults(): Record<string, any> {\n return {\n ...super.getDefaults(),\n ...FabricImage.ownDefaults,\n };\n }\n /**\n * Constructor\n * Image can be initialized with any canvas drawable or a string.\n * The string should be a url and will be loaded as an image.\n * Canvas and Image element work out of the box, while videos require extra code to work.\n * Please check video element events for seeking.\n * @param {ImageSource | string} element Image element\n * @param {Object} [options] Options object\n */\n constructor(elementId: string, options?: Props);\n constructor(element: ImageSource, options?: Props);\n constructor(arg0: ImageSource | string, options?: Props) {\n super();\n this.filters = [];\n Object.assign(this, FabricImage.ownDefaults);\n this.setOptions(options);\n this.cacheKey = `texture${uid()}`;\n this.setElement(\n typeof arg0 === 'string'\n ? ((\n (this.canvas && getDocumentFromElement(this.canvas.getElement())) ||\n getFabricDocument()\n ).getElementById(arg0) as ImageSource)\n : arg0,\n options,\n );\n }\n\n /**\n * Returns image element which this instance if based on\n */\n getElement() {\n return this._element;\n }\n\n /**\n * Sets image element for this instance to a specified one.\n * If filters defined they are applied to new image.\n * You might need to call `canvas.renderAll` and `object.setCoords` after replacing, to render new image and update controls area.\n * @param {HTMLImageElement} element\n * @param {Partial<TSize>} [size] Options object\n */\n setElement(element: ImageSource, size: Partial<TSize> = {}) {\n this.removeTexture(this.cacheKey);\n this.removeTexture(`${this.cacheKey}_filtered`);\n this._element = element;\n this._originalElement = element;\n this._setWidthHeight(size);\n element.classList?.add(FabricImage.CSS_CANVAS);\n if (this.filters.length !== 0) {\n this.applyFilters();\n }\n // resizeFilters work on the already filtered copy.\n // we need to apply resizeFilters AFTER normal filters.\n // applyResizeFilters is run more often than normal filters\n // and is triggered by user interactions rather than dev code\n if (this.resizeFilter) {\n this.applyResizeFilters();\n }\n }\n\n /**\n * Delete a single texture if in webgl mode\n */\n removeTexture(key: string) {\n const backend = getFilterBackend(false);\n if (backend instanceof WebGLFilterBackend) {\n backend.evictCachesForKey(key);\n }\n }\n\n /**\n * Delete textures, reference to elements and eventually JSDOM cleanup\n */\n dispose() {\n super.dispose();\n this.removeTexture(this.cacheKey);\n this.removeTexture(`${this.cacheKey}_filtered`);\n this._cacheContext = null;\n (\n ['_originalElement', '_element', '_filteredEl', '_cacheCanvas'] as const\n ).forEach((elementKey) => {\n const el = this[elementKey];\n el && getEnv().dispose(el);\n // @ts-expect-error disposing\n this[elementKey] = undefined;\n });\n }\n\n /**\n * Get the crossOrigin value (of the corresponding image element)\n */\n getCrossOrigin(): string | null {\n return (\n this._originalElement &&\n ((this._originalElement as any).crossOrigin || null)\n );\n }\n\n /**\n * Returns original size of an image\n */\n getOriginalSize() {\n const element = this.getElement() as any;\n if (!element) {\n return {\n width: 0,\n height: 0,\n };\n }\n return {\n width: element.naturalWidth || element.width,\n height: element.naturalHeight || element.height,\n };\n }\n\n /**\n * @private\n * @param {CanvasRenderingContext2D} ctx Context to render on\n */\n _stroke(ctx: CanvasRenderingContext2D) {\n if (!this.stroke || this.strokeWidth === 0) {\n return;\n }\n const w = this.width / 2,\n h = this.height / 2;\n ctx.beginPath();\n ctx.moveTo(-w, -h);\n ctx.lineTo(w, -h);\n ctx.lineTo(w, h);\n ctx.lineTo(-w, h);\n ctx.lineTo(-w, -h);\n ctx.closePath();\n }\n\n /**\n * Returns object representation of an instance\n * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output\n * @return {Object} Object representation of an instance\n */\n toObject<\n T extends Omit<Props & TClassProperties<this>, keyof SProps>,\n K extends keyof T = never,\n >(propertiesToInclude: K[] = []): Pick<T, K> & SProps {\n const filters: Record<string, any>[] = [];\n this.filters.forEach((filterObj) => {\n filterObj && filters.push(filterObj.toObject());\n });\n return {\n ...super.toObject([...IMAGE_PROPS, ...propertiesToInclude]),\n src: this.getSrc(),\n crossOrigin: this.getCrossOrigin(),\n filters,\n ...(this.resizeFilter\n ? { resizeFilter: this.resizeFilter.toObject() }\n : {}),\n };\n }\n\n /**\n * Returns true if an image has crop applied, inspecting values of cropX,cropY,width,height.\n * @return {Boolean}\n */\n hasCrop() {\n return (\n !!this.cropX ||\n !!this.cropY ||\n this.width < this._element.width ||\n this.height < this._element.height\n );\n }\n\n /**\n * Returns svg representation of an instance\n * @return {string[]} an array of strings with the specific svg representation\n * of the instance\n */\n _toSVG() {\n const imageMarkup: string[] = [],\n element = this._element,\n x = -this.width / 2,\n y = -this.height / 2;\n let svgString: string[] = [],\n strokeSvg: string[] = [],\n clipPath = '',\n imageRendering = '';\n if (!element) {\n return [];\n }\n if (this.hasCrop()) {\n const clipPathId = uid();\n svgString.push(\n '<clipPath id=\"imageCrop_' + clipPathId + '\">\\n',\n '\\t<rect x=\"' +\n x +\n '\" y=\"' +\n y +\n '\" width=\"' +\n this.width +\n '\" height=\"' +\n this.height +\n '\" />\\n',\n '</clipPath>\\n',\n );\n clipPath = ' clip-path=\"url(#imageCrop_' + clipPathId + ')\" ';\n }\n if (!this.imageSmoothing) {\n imageRendering = ' image-rendering=\"optimizeSpeed\"';\n }\n imageMarkup.push(\n '\\t<image ',\n 'COMMON_PARTS',\n `xlink:href=\"${this.getSvgSrc(true)}\" x=\"${x - this.cropX}\" y=\"${\n y - this.cropY\n // we're essentially moving origin of transformation from top/left corner to the center of the shape\n // by wrapping it in container <g> element with actual transformation, then offsetting object to the top/left\n // so that object's center aligns with container's left/top\n }\" width=\"${\n element.width || (element as HTMLImageElement).naturalWidth\n }\" height=\"${\n element.height || (element as HTMLImageElement).naturalHeight\n }\"${imageRendering}${clipPath}></image>\\n`,\n );\n\n if (this.stroke || this.strokeDashArray) {\n const origFill = this.fill;\n this.fill = null;\n strokeSvg = [\n `\\t<rect x=\"${x}\" y=\"${y}\" width=\"${this.width}\" height=\"${\n this.height\n }\" style=\"${this.getSvgStyles()}\" />\\n`,\n ];\n this.fill = origFill;\n }\n if (this.paintFirst !== FILL) {\n svgString = svgString.concat(strokeSvg, imageMarkup);\n } else {\n svgString = svgString.concat(imageMarkup, strokeSvg);\n }\n return svgString;\n }\n\n /**\n * Returns source of an image\n * @param {Boolean} filtered indicates if the src is needed for svg\n * @return {String} Source of an image\n */\n getSrc(filtered?: boolean): string {\n const element = filtered ? this._element : this._originalElement;\n if (element) {\n if ((element as HTMLCanvasElement).toDataURL) {\n return (element as HTMLCanvasElement).toDataURL();\n }\n\n if (this.srcFromAttribute) {\n return element.getAttribute('src') || '';\n } else {\n return (element as HTMLImageElement).src;\n }\n } else {\n return this.src || '';\n }\n }\n\n /**\n * Alias for getSrc\n * @param filtered\n * @deprecated\n */\n getSvgSrc(filtered?: boolean) {\n return this.getSrc(filtered);\n }\n\n /**\n * Loads and sets source of an image\\\n * **IMPORTANT**: It is recommended to abort loading tasks before calling this method to prevent race conditions and unnecessary networking\n * @param {String} src Source string (URL)\n * @param {LoadImageOptions} [options] Options object\n */\n setSrc(src: string, { crossOrigin, signal }: LoadImageOptions = {}) {\n return loadImage(src, { crossOrigin, signal }).then((img) => {\n typeof crossOrigin !== 'undefined' && this.set({ crossOrigin });\n this.setElement(img);\n });\n }\n\n /**\n * Returns string representation of an instance\n * @return {String} String representation of an instance\n */\n toString() {\n return `#<Image: { src: \"${this.getSrc()}\" }>`;\n }\n\n applyResizeFilters() {\n const filter = this.resizeFilter,\n minimumScale = this.minimumScaleTrigger,\n objectScale = this.getTotalObjectScaling(),\n scaleX = objectScale.x,\n scaleY = objectScale.y,\n elementToFilter = this._filteredEl || this._originalElement;\n if (this.group) {\n this.set('dirty', true);\n }\n if (!filter || (scaleX > minimumScale && scaleY > minimumScale)) {\n this._element = elementToFilter;\n this._filterScalingX = 1;\n this._filterScalingY = 1;\n this._lastScaleX = scaleX;\n this._lastScaleY = scaleY;\n return;\n }\n const canvasEl = createCanvasElementFor(elementToFilter),\n { width, height } = elementToFilter;\n this._element = canvasEl;\n this._lastScaleX = filter.scaleX = scaleX;\n this._lastScaleY = filter.scaleY = scaleY;\n getFilterBackend().applyFilters(\n [filter],\n elementToFilter,\n width,\n height,\n this._element,\n );\n this._filterScalingX = canvasEl.width / this._originalElement.width;\n this._filterScalingY = canvasEl.height / this._originalElement.height;\n }\n\n /**\n * Applies filters assigned to this image (from \"filters\" array) or from filter param\n * @method applyFilters\n * @param {Array} filters to be applied\n * @param {Boolean} forResizing specify if the filter operation is a resize operation\n */\n applyFilters(\n filters: BaseFilter<string, Record<string, any>>[] = this.filters || [],\n ) {\n filters = filters.filter((filter) => filter && !filter.isNeutralState());\n this.set('dirty', true);\n\n // needs to clear out or WEBGL will not resize correctly\n this.removeTexture(`${this.cacheKey}_filtered`);\n\n if (filters.length === 0) {\n this._element = this._originalElement;\n // this is unsafe and needs to be rethinkend\n this._filteredEl = undefined;\n this._filterScalingX = 1;\n this._filterScalingY = 1;\n return;\n }\n\n const imgElement = this._originalElement,\n sourceWidth =\n (imgElement as HTMLImageElement).naturalWidth || imgElement.width,\n sourceHeight =\n (imgElement as HTMLImageElement).naturalHeight || imgElement.height;\n\n if (this._element === this._originalElement) {\n // if the _element a reference to _originalElement\n // we need to create a new element to host the filtered pixels\n const canvasEl = createCanvasElementFor({\n width: sourceWidth,\n height: sourceHeight,\n });\n this._element = canvasEl;\n this._filteredEl = canvasEl;\n } else if (this._filteredEl) {\n // if the _element is it own element,\n // and we also have a _filteredEl, then we clean up _filteredEl\n // and we assign it to _element.\n // in this way we invalidate the eventual old resize filtered element\n this._element = this._filteredEl;\n this._filteredEl\n .getContext('2d')!\n .clearRect(0, 0, sourceWidth, sourceHeight);\n // we also need to resize again at next renderAll, so remove saved _lastScaleX/Y\n this._lastScaleX = 1;\n this._lastScaleY = 1;\n }\n getFilterBackend().applyFilters(\n filters,\n this._originalElement,\n sourceWidth,\n sourceHeight,\n this._element as HTMLCanvasElement,\n this.cacheKey,\n );\n if (\n this._originalElement.width !== this._element.width ||\n this._originalElement.height !== this._element.height\n ) {\n this._filterScalingX = this._element.width / this._originalElement.width;\n this._filterScalingY =\n this._element.height / this._originalElement.height;\n }\n }\n\n /**\n * @private\n * @param {CanvasRenderingContext2D} ctx Context to render on\n */\n _render(ctx: CanvasRenderingContext2D) {\n ctx.imageSmoothingEnabled = this.imageSmoothing;\n if (this.isMoving !== true && this.resizeFilter && this._needsResize()) {\n this.applyResizeFilters();\n }\n this._stroke(ctx);\n this._renderPaintInOrder(ctx);\n }\n\n /**\n * Paint the cached copy of the object on the target context.\n * it will set the imageSmoothing for the draw operation\n * @param {CanvasRenderingContext2D} ctx Context to render on\n */\n drawCacheOnCanvas(\n this: TCachedFabricObject<FabricImage>,\n ctx: CanvasRenderingContext2D,\n ) {\n ctx.imageSmoothingEnabled = this.imageSmoothing;\n super.drawCacheOnCanvas(ctx);\n }\n\n /**\n * Decide if the FabricImage should cache or not. Create its own cache level\n * needsItsOwnCache should be used when the object drawing method requires\n * a cache step.\n * Generally you do not cache objects in groups because the group outside is cached.\n * This is the special Image version where we would like to avoid caching where possible.\n * Essentially images do not benefit from caching. They may require caching, and in that\n * case we do it. Also caching an image usually ends in a loss of details.\n * A full performance audit should be done.\n * @return {Boolean}\n */\n shouldCache() {\n return this.needsItsOwnCache();\n }\n\n _renderFill(ctx: CanvasRenderingContext2D) {\n const elementToDraw = this._element;\n if (!elementToDraw) {\n return;\n }\n const scaleX = this._filterScalingX,\n scaleY = this._filterScalingY,\n w = this.width,\n h = this.height,\n // crop values cannot be lesser than 0.\n cropX = Math.max(this.cropX, 0),\n cropY = Math.max(this.cropY, 0),\n elWidth =\n (elementToDraw as HTMLImageElement).naturalWidth || elementToDraw.width,\n elHeight =\n (elementToDraw as HTMLImageElement).naturalHeight ||\n elementToDraw.height,\n sX = cropX * scaleX,\n sY = cropY * scaleY,\n // the width height cannot exceed element width/height, starting from the crop offset.\n sW = Math.min(w * scaleX, elWidth - sX),\n sH = Math.min(h * scaleY, elHeight - sY),\n x = -w / 2,\n y = -h / 2,\n maxDestW = Math.min(w, elWidth / scaleX - cropX),\n maxDestH = Math.min(h, elHeight / scaleY - cropY);\n\n elementToDraw &&\n ctx.drawImage(elementToDraw, sX, sY, sW, sH, x, y, maxDestW, maxDestH);\n }\n\n /**\n * needed to check if image needs resize\n * @private\n */\n _needsResize() {\n const scale = this.getTotalObjectScaling();\n return scale.x !== this._lastScaleX || scale.y !== this._lastScaleY;\n }\n\n /**\n * @private\n * @deprecated unused\n */\n _resetWidthHeight() {\n this.set(this.getOriginalSize());\n }\n\n /**\n * @private\n * Set the width and the height of the image object, using the element or the\n * options.\n */\n _setWidthHeight({ width, height }: Partial<TSize> = {}) {\n const size = this.getOriginalSize();\n this.width = width || size.width;\n this.height = height || size.height;\n }\n\n /**\n * Calculate offset for center and scale factor for the image in order to respect\n * the preserveAspectRatio attribute\n * @private\n */\n parsePreserveAspectRatioAttribute() {\n const pAR = parsePreserveAspectRatioAttribute(\n this.preserveAspectRatio || '',\n ),\n pWidth = this.width,\n pHeight = this.height,\n parsedAttributes = { width: pWidth, height: pHeight };\n let rWidth = this._element.width,\n rHeight = this._element.height,\n scaleX = 1,\n scaleY = 1,\n offsetLeft = 0,\n offsetTop = 0,\n cropX = 0,\n cropY = 0,\n offset;\n\n if (pAR && (pAR.alignX !== NONE || pAR.alignY !== NONE)) {\n if (pAR.meetOrSlice === 'meet') {\n scaleX = scaleY = findScaleToFit(this._element, parsedAttributes);\n offset = (pWidth - rWidth * scaleX) / 2;\n if (pAR.alignX === 'Min') {\n offsetLeft = -offset;\n }\n if (pAR.alignX === 'Max') {\n offsetLeft = offset;\n }\n offset = (pHeight - rHeight * scaleY) / 2;\n if (pAR.alignY === 'Min') {\n offsetTop = -offset;\n }\n if (pAR.alignY === 'Max') {\n offsetTop = offset;\n }\n }\n if (pAR.meetOrSlice === 'slice') {\n scaleX = scaleY = findScaleToCover(this._element, parsedAttributes);\n offset = rWidth - pWidth / scaleX;\n if (pAR.alignX === 'Mid') {\n cropX = offset / 2;\n }\n if (pAR.alignX === 'Max') {\n cropX = offset;\n }\n offset = rHeight - pHeight / scaleY;\n if (pAR.alignY === 'Mid') {\n cropY = offset / 2;\n }\n if (pAR.alignY === 'Max') {\n cropY = offset;\n }\n rWidth = pWidth / scaleX;\n rHeight = pHeight / scaleY;\n }\n } else {\n scaleX = pWidth / rWidth;\n scaleY = pHeight / rHeight;\n }\n return {\n width: rWidth,\n height: rHeight,\n scaleX,\n scaleY,\n offsetLeft,\n offsetTop,\n cropX,\n cropY,\n };\n }\n\n /**\n * Default CSS class name for canvas\n * Will be removed from fabric 7\n * @static\n * @deprecated\n * @type String\n * @default\n */\n static CSS_CANVAS = 'canvas-img';\n\n /**\n * List of attribute names to account for when parsing SVG element (used by {@link FabricImage.fromElement})\n * @static\n * @see {@link http://www.w3.org/TR/SVG/struct.html#ImageElement}\n */\n static ATTRIBUTE_NAMES = [\n ...SHARED_ATTRIBUTES,\n 'x',\n 'y',\n 'width',\n 'height',\n 'preserveAspectRatio',\n 'xlink:href',\n 'href',\n 'crossOrigin',\n 'image-rendering',\n ];\n\n /**\n * Creates an instance of FabricImage from its object representation\n * @static\n * @param {Object} object Object to create an instance from\n * @param {object} [options] Options object\n * @param {AbortSignal} [options.signal] handle aborting, see https://developer.mozilla.org/en-US/docs/Web/API/AbortController/signal\n * @returns {Promise<FabricImage>}\n */\n static fromObject<T extends TOptions<SerializedImageProps>>(\n { filters: f, resizeFilter: rf, src, crossOrigin, type, ...object }: T,\n options?: Abortable,\n ) {\n return Promise.all([\n loadImage(src!, { ...options, crossOrigin }),\n f && enlivenObjects<BaseFilter<string>>(f, options),\n // TODO: redundant - handled by enlivenObjectEnlivables\n rf && enlivenObjects<BaseFilter<'Resize'>>([rf], options),\n enlivenObjectEnlivables(object, options),\n ]).then(([el, filters = [], [resizeFilter] = [], hydratedProps = {}]) => {\n return new this(el, {\n ...object,\n // TODO: this creates a difference between image creation and restoring from JSON\n src,\n filters,\n resizeFilter,\n ...hydratedProps,\n });\n });\n }\n\n /**\n * Creates an instance of Image from an URL string\n * @static\n * @param {String} url URL to create an image from\n * @param {LoadImageOptions} [options] Options object\n * @returns {Promise<FabricImage>}\n */\n static fromURL<T extends TOptions<ImageProps>>(\n url: string,\n { crossOrigin = null, signal }: LoadImageOptions = {},\n imageOptions?: T,\n ): Promise<FabricImage> {\n return loadImage(url, { crossOrigin, signal }).then(\n (img) => new this(img, imageOptions),\n );\n }\n\n /**\n * Returns {@link FabricImage} instance from an SVG element\n * @static\n * @param {HTMLElement} element Element to parse\n * @param {Object} [options] Options object\n * @param {AbortSignal} [options.signal] handle aborting, see https://developer.mozilla.org/en-US/docs/Web/API/AbortController/signal\n * @param {Function} callback Callback to execute when Image object is created\n */\n static async fromElement(\n element: HTMLElement,\n options: Abortable = {},\n cssRules?: CSSRules,\n ) {\n const parsedAttributes = parseAttributes(\n element,\n this.ATTRIBUTE_NAMES,\n cssRules,\n );\n return this.fromURL(\n parsedAttributes['xlink:href'] || parsedAttributes['href'],\n options,\n parsedAttributes,\n ).catch((err) => {\n log('log', 'Unable to parse Image', err);\n return null;\n });\n }\n}\n\nclassRegistry.setClass(FabricImage);\nclassRegistry.setSVGClass(FabricImage);\n"],"names":["imageDefaultValues","strokeWidth","srcFromAttribute","minimumScaleTrigger","cropX","cropY","imageSmoothing","IMAGE_PROPS","FabricImage","FabricObject","getDefaults","_objectSpread","super","ownDefaults","constructor","arg0","options","_defineProperty","this","filters","Object","assign","setOptions","cacheKey","concat","uid","setElement","canvas","getDocumentFromElement","getElement","getFabricDocument","getElementById","_element","element","_element$classList","size","arguments","length","undefined","removeTexture","_originalElement","_setWidthHeight","classList","add","CSS_CANVAS","applyFilters","resizeFilter","applyResizeFilters","key","backend","getFilterBackend","WebGLFilterBackend","evictCachesForKey","dispose","_cacheContext","forEach","elementKey","el","getEnv","getCrossOrigin","crossOrigin","getOriginalSize","width","naturalWidth","height","naturalHeight","_stroke","ctx","stroke","w","h","beginPath","moveTo","lineTo","closePath","toObject","propertiesToInclude","filterObj","push","src","getSrc","hasCrop","_toSVG","imageMarkup","x","y","svgString","strokeSvg","clipPath","imageRendering","clipPathId","getSvgSrc","strokeDashArray","origFill","fill","getSvgStyles","paintFirst","FILL","filtered","toDataURL","getAttribute","setSrc","signal","loadImage","then","img","set","toString","filter","minimumScale","objectScale","getTotalObjectScaling","scaleX","scaleY","elementToFilter","_filteredEl","group","_filterScalingX","_filterScalingY","_lastScaleX","_lastScaleY","canvasEl","createCanvasElementFor","isNeutralState","imgElement","sourceWidth","sourceHeight","getContext","clearRect","_render","imageSmoothingEnabled","isMoving","_needsResize","_renderPaintInOrder","drawCacheOnCanvas","shouldCache","needsItsOwnCache","_renderFill","elementToDraw","Math","max","elWidth","elHeight","sX","sY","sW","min","sH","maxDestW","maxDestH","drawImage","scale","_resetWidthHeight","parsePreserveAspectRatioAttribute","pAR","preserveAspectRatio","pWidth","pHeight","parsedAttributes","offset","rWidth","rHeight","offsetLeft","offsetTop","alignX","NONE","alignY","meetOrSlice","findScaleToFit","findScaleToCover","fromObject","_ref","f","rf","type","object","_objectWithoutProperties","_excluded","Promise","all","enlivenObjects","enlivenObjectEnlivables","_ref2","hydratedProps","fromURL","url","imageOptions","fromElement","cssRules","parseAttributes","ATTRIBUTE_NAMES","catch","err","log","cacheProperties","SHARED_ATTRIBUTES","classRegistry","setClass","setSVGClass"],"mappings":"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"}