mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
fix(ui): prevent flash when applying transform
This commit is contained in:
parent
c9849a79ea
commit
a57e618d47
@ -7,7 +7,7 @@ import { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
|||||||
import { CanvasRect } from 'features/controlLayers/konva/CanvasRect';
|
import { CanvasRect } from 'features/controlLayers/konva/CanvasRect';
|
||||||
import { getBrushLineId, getEraserLineId, getRectShapeId } from 'features/controlLayers/konva/naming';
|
import { getBrushLineId, getEraserLineId, getRectShapeId } from 'features/controlLayers/konva/naming';
|
||||||
import { konvaNodeToBlob, mapId, previewBlob } from 'features/controlLayers/konva/util';
|
import { konvaNodeToBlob, mapId, previewBlob } from 'features/controlLayers/konva/util';
|
||||||
import { layerAllObjectsDeletedExceptOne, layerRasterized } from 'features/controlLayers/store/canvasV2Slice';
|
import { layerRasterized } from 'features/controlLayers/store/canvasV2Slice';
|
||||||
import {
|
import {
|
||||||
type BrushLine,
|
type BrushLine,
|
||||||
type CanvasV2State,
|
type CanvasV2State,
|
||||||
@ -55,7 +55,6 @@ export class CanvasLayer {
|
|||||||
|
|
||||||
isTransforming: boolean;
|
isTransforming: boolean;
|
||||||
isPendingBboxCalculation: boolean;
|
isPendingBboxCalculation: boolean;
|
||||||
rasterizedObjectId: string | null;
|
|
||||||
|
|
||||||
rect: Rect;
|
rect: Rect;
|
||||||
bbox: Rect;
|
bbox: Rect;
|
||||||
@ -191,7 +190,6 @@ export class CanvasLayer {
|
|||||||
this._bboxNeedsUpdate = true;
|
this._bboxNeedsUpdate = true;
|
||||||
this.isTransforming = false;
|
this.isTransforming = false;
|
||||||
this._isFirstRender = true;
|
this._isFirstRender = true;
|
||||||
this.rasterizedObjectId = null;
|
|
||||||
this.isPendingBboxCalculation = false;
|
this.isPendingBboxCalculation = false;
|
||||||
this._log = this.manager.getLogger(`layer_${this.id}`);
|
this._log = this.manager.getLogger(`layer_${this.id}`);
|
||||||
}
|
}
|
||||||
@ -218,7 +216,7 @@ export class CanvasLayer {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const drawingBuffer = this._drawingBuffer;
|
const drawingBuffer = this._drawingBuffer;
|
||||||
this.setDrawingBuffer(null);
|
await this.setDrawingBuffer(null);
|
||||||
|
|
||||||
// We need to give the objects a fresh ID else they will be considered the same object when they are re-rendered as
|
// We need to give the objects a fresh ID else they will be considered the same object when they are re-rendered as
|
||||||
// a non-buffer object, and we won't trigger things like bbox calculation
|
// a non-buffer object, and we won't trigger things like bbox calculation
|
||||||
@ -248,12 +246,12 @@ export class CanvasLayer {
|
|||||||
this._log.debug('Updating');
|
this._log.debug('Updating');
|
||||||
const { position, objects, opacity, isEnabled } = state;
|
const { position, objects, opacity, isEnabled } = state;
|
||||||
|
|
||||||
if (this._isFirstRender || position !== this._state.position) {
|
|
||||||
await this.updatePosition({ position });
|
|
||||||
}
|
|
||||||
if (this._isFirstRender || objects !== this._state.objects) {
|
if (this._isFirstRender || objects !== this._state.objects) {
|
||||||
await this.updateObjects({ objects });
|
await this.updateObjects({ objects });
|
||||||
}
|
}
|
||||||
|
if (this._isFirstRender || position !== this._state.position) {
|
||||||
|
await this.updatePosition({ position });
|
||||||
|
}
|
||||||
if (this._isFirstRender || opacity !== this._state.opacity) {
|
if (this._isFirstRender || opacity !== this._state.opacity) {
|
||||||
await this.updateOpacity({ opacity });
|
await this.updateOpacity({ opacity });
|
||||||
}
|
}
|
||||||
@ -270,14 +268,14 @@ export class CanvasLayer {
|
|||||||
this._isFirstRender = false;
|
this._isFirstRender = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateVisibility(arg?: { isEnabled: boolean }) {
|
updateVisibility(arg?: { isEnabled: boolean }) {
|
||||||
this._log.trace('Updating visibility');
|
this._log.trace('Updating visibility');
|
||||||
const isEnabled = get(arg, 'isEnabled', this._state.isEnabled);
|
const isEnabled = get(arg, 'isEnabled', this._state.isEnabled);
|
||||||
const hasObjects = this.objects.size > 0 || this._drawingBuffer !== null;
|
const hasObjects = this.objects.size > 0 || this._drawingBuffer !== null;
|
||||||
this.konva.layer.visible(isEnabled || hasObjects);
|
this.konva.layer.visible(isEnabled || hasObjects);
|
||||||
}
|
}
|
||||||
|
|
||||||
async updatePosition(arg?: { position: Coordinate }) {
|
updatePosition(arg?: { position: Coordinate }) {
|
||||||
this._log.trace('Updating position');
|
this._log.trace('Updating position');
|
||||||
const position = get(arg, 'position', this._state.position);
|
const position = get(arg, 'position', this._state.position);
|
||||||
const bboxPadding = this.manager.getScaledBboxPadding();
|
const bboxPadding = this.manager.getScaledBboxPadding();
|
||||||
@ -331,23 +329,15 @@ export class CanvasLayer {
|
|||||||
if (didUpdate) {
|
if (didUpdate) {
|
||||||
this.calculateBbox();
|
this.calculateBbox();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.isTransforming && this.rasterizedObjectId) {
|
|
||||||
this.manager._store.dispatch(layerAllObjectsDeletedExceptOne({ id: this.id, objectId: this.rasterizedObjectId }));
|
|
||||||
this.isTransforming = false;
|
|
||||||
this.rasterizedObjectId = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateOpacity(arg?: { opacity: number }) {
|
updateOpacity(arg?: { opacity: number }) {
|
||||||
this._log.trace('Updating opacity');
|
this._log.trace('Updating opacity');
|
||||||
|
|
||||||
const opacity = get(arg, 'opacity', this._state.opacity);
|
const opacity = get(arg, 'opacity', this._state.opacity);
|
||||||
|
|
||||||
this.konva.objectGroup.opacity(opacity);
|
this.konva.objectGroup.opacity(opacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateInteraction(arg?: { toolState: CanvasV2State['tool']; isSelected: boolean }) {
|
updateInteraction(arg?: { toolState: CanvasV2State['tool']; isSelected: boolean }) {
|
||||||
this._log.trace('Updating interaction');
|
this._log.trace('Updating interaction');
|
||||||
|
|
||||||
const toolState = get(arg, 'toolState', this.manager.stateApi.getToolState());
|
const toolState = get(arg, 'toolState', this.manager.stateApi.getToolState());
|
||||||
@ -396,7 +386,7 @@ export class CanvasLayer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateBbox() {
|
updateBbox() {
|
||||||
this._log.trace('Updating bbox');
|
this._log.trace('Updating bbox');
|
||||||
|
|
||||||
if (this.isPendingBboxCalculation) {
|
if (this.isPendingBboxCalculation) {
|
||||||
@ -442,7 +432,7 @@ export class CanvasLayer {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async syncStageScale() {
|
syncStageScale() {
|
||||||
this._log.trace('Syncing scale to stage');
|
this._log.trace('Syncing scale to stage');
|
||||||
|
|
||||||
const onePixel = this.manager.getScaledPixel();
|
const onePixel = this.manager.getScaledPixel();
|
||||||
@ -519,7 +509,7 @@ export class CanvasLayer {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
async startTransform() {
|
startTransform() {
|
||||||
this._log.debug('Starting transform');
|
this._log.debug('Starting transform');
|
||||||
this.isTransforming = true;
|
this.isTransforming = true;
|
||||||
|
|
||||||
@ -539,7 +529,7 @@ export class CanvasLayer {
|
|||||||
this.konva.bbox.visible(false);
|
this.konva.bbox.visible(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
async resetScale() {
|
resetScale() {
|
||||||
const attrs = {
|
const attrs = {
|
||||||
scaleX: 1,
|
scaleX: 1,
|
||||||
scaleY: 1,
|
scaleY: 1,
|
||||||
@ -550,39 +540,37 @@ export class CanvasLayer {
|
|||||||
this.konva.interactionRect.setAttrs(attrs);
|
this.konva.interactionRect.setAttrs(attrs);
|
||||||
}
|
}
|
||||||
|
|
||||||
async applyTransform() {
|
async rasterizeLayer() {
|
||||||
this._log.debug('Applying transform');
|
this._log.debug('Rasterizing layer');
|
||||||
|
|
||||||
const objectGroupClone = this.konva.objectGroup.clone();
|
const objectGroupClone = this.konva.objectGroup.clone();
|
||||||
const interactionRectClone = this.konva.interactionRect.clone();
|
const interactionRectClone = this.konva.interactionRect.clone();
|
||||||
const rect = interactionRectClone.getClientRect();
|
const rect = interactionRectClone.getClientRect();
|
||||||
const blob = await konvaNodeToBlob(objectGroupClone, rect);
|
const blob = await konvaNodeToBlob(objectGroupClone, rect);
|
||||||
if (this.manager._isDebugging) {
|
if (this.manager._isDebugging) {
|
||||||
previewBlob(blob, 'transformed layer');
|
previewBlob(blob, 'Rasterized layer');
|
||||||
}
|
}
|
||||||
const imageDTO = await uploadImage(blob, `${this.id}_transform.png`, 'other', true);
|
const imageDTO = await uploadImage(blob, `${this.id}_rasterized.png`, 'other', true);
|
||||||
const { dispatch } = getStore();
|
const { dispatch } = getStore();
|
||||||
const imageObject = imageDTOToImageObject(this.id, uuidv4(), imageDTO);
|
const imageObject = imageDTOToImageObject(this.id, uuidv4(), imageDTO);
|
||||||
dispatch(layerRasterized({ id: this.id, imageObject, position: { x: rect.x, y: rect.y } }));
|
await this._renderObject(imageObject, true);
|
||||||
this.rasterizedObjectId = imageObject.id;
|
for (const obj of this.objects.values()) {
|
||||||
|
if (obj.id !== imageObject.id) {
|
||||||
|
obj.konva.group.visible(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
this.resetScale();
|
this.resetScale();
|
||||||
|
dispatch(layerRasterized({ id: this.id, imageObject, position: { x: rect.x, y: rect.y } }));
|
||||||
}
|
}
|
||||||
|
|
||||||
async finalizeTransform() {
|
stopTransform() {
|
||||||
//
|
this._log.debug('Stopping transform');
|
||||||
}
|
|
||||||
|
|
||||||
async cancelTransform() {
|
|
||||||
this._log.debug('Canceling transform');
|
|
||||||
|
|
||||||
this.isTransforming = false;
|
this.isTransforming = false;
|
||||||
this.resetScale();
|
this.resetScale();
|
||||||
await this.updatePosition({ position: this._state.position });
|
this.updatePosition();
|
||||||
await this.updateBbox();
|
this.updateBbox();
|
||||||
await this.updateInteraction({
|
this.updateInteraction();
|
||||||
toolState: this.manager.stateApi.getToolState(),
|
|
||||||
isSelected: this.manager.stateApi.getIsSelected(this.id),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getDefaultRect(): Rect {
|
getDefaultRect(): Rect {
|
||||||
|
@ -299,10 +299,11 @@ export class CanvasManager {
|
|||||||
this.onTransform?.(true);
|
this.onTransform?.(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
applyTransform() {
|
async applyTransform() {
|
||||||
const layer = this.getTransformingLayer();
|
const layer = this.getTransformingLayer();
|
||||||
if (layer) {
|
if (layer) {
|
||||||
layer.applyTransform();
|
await layer.rasterizeLayer();
|
||||||
|
layer.stopTransform();
|
||||||
}
|
}
|
||||||
this.onTransform?.(false);
|
this.onTransform?.(false);
|
||||||
}
|
}
|
||||||
@ -310,7 +311,7 @@ export class CanvasManager {
|
|||||||
cancelTransform() {
|
cancelTransform() {
|
||||||
const layer = this.getTransformingLayer();
|
const layer = this.getTransformingLayer();
|
||||||
if (layer) {
|
if (layer) {
|
||||||
layer.cancelTransform();
|
layer.stopTransform();
|
||||||
}
|
}
|
||||||
this.onTransform?.(false);
|
this.onTransform?.(false);
|
||||||
}
|
}
|
||||||
|
@ -220,7 +220,6 @@ export const {
|
|||||||
layerTranslated,
|
layerTranslated,
|
||||||
layerBboxChanged,
|
layerBboxChanged,
|
||||||
layerImageAdded,
|
layerImageAdded,
|
||||||
layerAllObjectsDeletedExceptOne,
|
|
||||||
layerAllDeleted,
|
layerAllDeleted,
|
||||||
layerImageCacheChanged,
|
layerImageCacheChanged,
|
||||||
layerScaled,
|
layerScaled,
|
||||||
|
@ -259,19 +259,10 @@ export const layersReducers = {
|
|||||||
if (!layer) {
|
if (!layer) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
layer.objects.push(imageObject);
|
layer.objects = [imageObject];
|
||||||
layer.position = position;
|
layer.position = position;
|
||||||
state.layers.imageCache = null;
|
state.layers.imageCache = null;
|
||||||
},
|
},
|
||||||
layerAllObjectsDeletedExceptOne: (state, action: PayloadAction<{ id: string; objectId: string }>) => {
|
|
||||||
const { id, objectId } = action.payload;
|
|
||||||
const layer = selectLayer(state, id);
|
|
||||||
if (!layer) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
layer.objects = layer.objects.filter((obj) => obj.id === objectId);
|
|
||||||
state.layers.imageCache = null;
|
|
||||||
},
|
|
||||||
} satisfies SliceCaseReducers<CanvasV2State>;
|
} satisfies SliceCaseReducers<CanvasV2State>;
|
||||||
|
|
||||||
const scalePoints = (points: number[], scaleX: number, scaleY: number) => {
|
const scalePoints = (points: number[], scaleX: number, scaleY: number) => {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user