roshikhan301's picture
Upload 2113 files
8a37e0a verified
import { Mutex } from 'async-mutex';
import { deepClone } from 'common/util/deepClone';
import type { CanvasEntityBufferObjectRenderer } from 'features/controlLayers/konva/CanvasEntity/CanvasEntityBufferObjectRenderer';
import type { CanvasEntityFilterer } from 'features/controlLayers/konva/CanvasEntity/CanvasEntityFilterer';
import type { CanvasEntityObjectRenderer } from 'features/controlLayers/konva/CanvasEntity/CanvasEntityObjectRenderer';
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
import { CanvasModuleBase } from 'features/controlLayers/konva/CanvasModuleBase';
import type { CanvasSegmentAnythingModule } from 'features/controlLayers/konva/CanvasSegmentAnythingModule';
import type { CanvasStagingAreaModule } from 'features/controlLayers/konva/CanvasStagingAreaModule';
import { loadImage } from 'features/controlLayers/konva/util';
import type { CanvasImageState } from 'features/controlLayers/store/types';
import { t } from 'i18next';
import Konva from 'konva';
import type { Logger } from 'roarr';
import { getImageDTOSafe } from 'services/api/endpoints/images';
export class CanvasObjectImage extends CanvasModuleBase {
readonly type = 'object_image';
readonly id: string;
readonly path: string[];
readonly parent:
| CanvasEntityObjectRenderer
| CanvasEntityBufferObjectRenderer
| CanvasStagingAreaModule
| CanvasSegmentAnythingModule
| CanvasEntityFilterer;
readonly manager: CanvasManager;
readonly log: Logger;
state: CanvasImageState;
konva: {
group: Konva.Group;
placeholder: { group: Konva.Group; rect: Konva.Rect; text: Konva.Text };
image: Konva.Image | null; // The image is loaded asynchronously, so it may not be available immediately
};
imageElement: HTMLImageElement | null = null;
isLoading: boolean = false;
isError: boolean = false;
mutex = new Mutex();
constructor(
state: CanvasImageState,
parent:
| CanvasEntityObjectRenderer
| CanvasEntityBufferObjectRenderer
| CanvasStagingAreaModule
| CanvasSegmentAnythingModule
| CanvasEntityFilterer
) {
super();
this.id = state.id;
this.parent = parent;
this.manager = parent.manager;
this.path = this.manager.buildPath(this);
this.log = this.manager.buildLogger(this);
this.log.debug({ state }, 'Creating module');
const { width, height } = state.image;
this.konva = {
group: new Konva.Group({ name: `${this.type}:group`, listening: false }),
placeholder: {
group: new Konva.Group({ name: `${this.type}:placeholder_group`, listening: false }),
rect: new Konva.Rect({
name: `${this.type}:placeholder_rect`,
fill: 'hsl(220 12% 45% / 1)', // 'base.500'
width,
height,
listening: false,
perfectDrawEnabled: false,
}),
text: new Konva.Text({
name: `${this.type}:placeholder_text`,
fill: 'hsl(220 12% 10% / 1)', // 'base.900'
width,
height,
align: 'center',
verticalAlign: 'middle',
fontFamily: '"Inter Variable", sans-serif',
fontSize: width / 16,
fontStyle: '600',
text: t('common.loadingImage', 'Loading Image'),
listening: false,
perfectDrawEnabled: false,
}),
},
image: null,
};
this.konva.placeholder.group.add(this.konva.placeholder.rect);
this.konva.placeholder.group.add(this.konva.placeholder.text);
this.konva.group.add(this.konva.placeholder.group);
this.state = state;
}
updateImageSource = async (imageName: string) => {
try {
this.log.trace({ imageName }, 'Updating image source');
this.isLoading = true;
this.konva.group.visible(true);
if (!this.konva.image) {
this.konva.placeholder.group.visible(false);
this.konva.placeholder.text.text(t('common.loadingImage', 'Loading Image'));
}
const imageDTO = await getImageDTOSafe(imageName);
if (imageDTO === null) {
this.onFailedToLoadImage();
return;
}
this.imageElement = await loadImage(imageDTO.image_url);
await this.updateImageElement();
} catch {
this.onFailedToLoadImage();
}
};
onFailedToLoadImage = () => {
this.log({ image: this.state.image }, 'Failed to load image');
this.konva.image?.visible(false);
this.isLoading = false;
this.isError = true;
this.konva.placeholder.text.text(t('common.imageFailedToLoad', 'Image Failed to Load'));
this.konva.placeholder.group.visible(true);
};
updateImageElement = async () => {
const release = await this.mutex.acquire();
try {
if (this.imageElement) {
const { width, height } = this.state.image;
if (this.konva.image) {
this.log.trace('Updating Konva image attrs');
this.konva.image.setAttrs({
image: this.imageElement,
width,
height,
});
} else {
this.log.trace('Creating new Konva image');
this.konva.image = new Konva.Image({
name: `${this.type}:image`,
listening: false,
image: this.imageElement,
width,
height,
perfectDrawEnabled: false,
});
this.konva.group.add(this.konva.image);
}
this.konva.placeholder.rect.setAttrs({ width, height });
this.konva.placeholder.text.setAttrs({ width, height, fontSize: width / 16 });
this.isLoading = false;
this.isError = false;
this.konva.placeholder.group.visible(false);
}
} finally {
release();
}
};
update = async (state: CanvasImageState, force = false): Promise<boolean> => {
if (force || this.state !== state) {
this.log.trace({ state }, 'Updating image');
const { image } = state;
const { width, height, image_name } = image;
if (force || (this.state.image.image_name !== image_name && !this.isLoading)) {
await this.updateImageSource(image_name);
}
this.konva.image?.setAttrs({ width, height });
this.state = state;
return true;
}
return false;
};
destroy = () => {
this.log.debug('Destroying image renderer module');
this.konva.group.destroy();
};
setVisibility = (isVisible: boolean): void => {
this.log.trace({ isVisible }, 'Setting image visibility');
this.konva.group.visible(isVisible);
};
repr = () => {
return {
id: this.id,
type: this.type,
path: this.path,
parent: this.parent.id,
isLoading: this.isLoading,
isError: this.isError,
state: deepClone(this.state),
};
};
}