import { types, getParent } from "mobx-state-tree"; import { FileLoader } from "../../../utils/FileLoader"; import { clamp } from "../../../utils/utilities"; import { FF_IMAGE_MEMORY_USAGE, isFF } from "../../../utils/feature-flags"; const fileLoader = new FileLoader(); export const ImageEntity = types .model("ImageEntity", { id: types.identifier, src: types.string, index: types.number, rotation: types.optional(types.number, 0), /** * Natural sizes of Image * Constants */ naturalWidth: types.optional(types.integer, 1), naturalHeight: types.optional(types.integer, 1), stageWidth: types.optional(types.number, 1), stageHeight: types.optional(types.number, 1), /** * Zoom Scale */ zoomScale: types.optional(types.number, 1), /** * Coordinates of left top corner * Default: { x: 0, y: 0 } */ zoomingPositionX: types.optional(types.number, 0), zoomingPositionY: types.optional(types.number, 0), /** * Brightness of Canvas */ brightnessGrade: types.optional(types.number, 100), contrastGrade: types.optional(types.number, 100), }) .volatile(() => ({ stageRatio: 1, // Container's sizes causing limits to calculate a scale factor containerWidth: 1, containerHeight: 1, stageZoom: 1, stageZoomX: 1, stageZoomY: 1, currentZoom: 1, /** Is image downloaded to local cache */ downloaded: false, /** Is image being downloaded */ downloading: false, /** If error happened during download */ error: false, /** Download progress 0..1 */ progress: 0, /** Local image src created with URL.createURLObject */ currentSrc: undefined, /** Is image loaded using `` tag and cached by the browser */ imageLoaded: false, })) .views((self) => ({ get parent() { // Get the ImageEntityMixin return getParent(self, 2); }, get imageCrossOrigin() { return self.parent?.imageCrossOrigin ?? "anonymous"; }, })) .actions((self) => ({ preload() { if (self.ensurePreloaded() || !self.src) return; if (isFF(FF_IMAGE_MEMORY_USAGE)) { self.setDownloading(true); new Promise((resolve) => { const img = new Image(); // Get from the image tag const crossOrigin = self.imageCrossOrigin; if (crossOrigin) img.crossOrigin = crossOrigin; img.onload = () => { self.setCurrentSrc(self.src); self.setDownloaded(true); self.setProgress(1); self.setDownloading(false); self.setImageLoaded(true); resolve(); }; img.onerror = () => { self.setError(true); self.setDownloading(false); resolve(); }; img.src = self.src; }); return; } self.setDownloading(true); fileLoader .download(self.src, (_t, _l, progress) => { self.setProgress(progress); }) .then((url) => { self.setDownloaded(true); self.setDownloading(false); self.setCurrentSrc(url); }) .catch(() => { self.setDownloading(false); self.setError(true); }); }, ensurePreloaded() { if (isFF(FF_IMAGE_MEMORY_USAGE)) return self.currentSrc !== undefined; if (fileLoader.isError(self.src)) { self.setDownloading(false); self.setError(true); return true; } if (fileLoader.isPreloaded(self.src)) { self.setDownloading(false); self.setDownloaded(true); self.setProgress(1); self.setCurrentSrc(fileLoader.getPreloadedURL(self.src)); return true; } return false; }, setImageLoaded(value) { self.imageLoaded = value; }, setProgress(progress) { self.progress = clamp(progress, 0, 100); }, setDownloading(downloading) { self.downloading = downloading; }, setDownloaded(downloaded) { self.downloaded = downloaded; }, setCurrentSrc(src) { self.currentSrc = src; }, setError() { self.error = true; }, })) .actions((self) => ({ setRotation(angle) { self.rotation = angle; }, setNaturalWidth(width) { self.naturalWidth = width; }, setNaturalHeight(height) { self.naturalHeight = height; }, setStageWidth(width) { self.stageWidth = width; }, setStageHeight(height) { self.stageHeight = height; }, setStageRatio(ratio) { self.stageRatio = ratio; }, setContainerWidth(width) { self.containerWidth = width; }, setContainerHeight(height) { self.containerHeight = height; }, setStageZoom(zoom) { self.stageZoom = zoom; }, setStageZoomX(zoom) { self.stageZoomX = zoom; }, setStageZoomY(zoom) { self.stageZoomY = zoom; }, setCurrentZoom(zoom) { self.currentZoom = zoom; }, setZoomScale(zoomScale) { self.zoomScale = zoomScale; }, setZoomingPositionX(x) { self.zoomingPositionX = x; }, setZoomingPositionY(y) { self.zoomingPositionY = y; }, setBrightnessGrade(grade) { self.brightnessGrade = grade; }, setContrastGrade(grade) { self.contrastGrade = grade; }, }));