mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat(ui): render buffer separately from "real" objects
This commit is contained in:
parent
7f05af4a68
commit
5720ed4d64
@ -23,7 +23,7 @@ export class CanvasBrushLineRenderer {
|
|||||||
};
|
};
|
||||||
|
|
||||||
constructor(state: CanvasBrushLineState, parent: CanvasObjectRenderer) {
|
constructor(state: CanvasBrushLineState, parent: CanvasObjectRenderer) {
|
||||||
const { id, strokeWidth, clip, color, points } = state;
|
const { id, clip } = state;
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
this.manager = parent.manager;
|
this.manager = parent.manager;
|
||||||
@ -42,14 +42,10 @@ export class CanvasBrushLineRenderer {
|
|||||||
name: `${this.type}:line`,
|
name: `${this.type}:line`,
|
||||||
listening: false,
|
listening: false,
|
||||||
shadowForStrokeEnabled: false,
|
shadowForStrokeEnabled: false,
|
||||||
strokeWidth,
|
|
||||||
tension: 0.3,
|
tension: 0.3,
|
||||||
lineCap: 'round',
|
lineCap: 'round',
|
||||||
lineJoin: 'round',
|
lineJoin: 'round',
|
||||||
globalCompositeOperation: 'source-over',
|
globalCompositeOperation: 'source-over',
|
||||||
stroke: rgbaColorToString(color),
|
|
||||||
// A line with only one point will not be rendered, so we duplicate the points to make it visible
|
|
||||||
points: points.length === 2 ? [...points, ...points] : points,
|
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
this.konva.group.add(this.konva.line);
|
this.konva.group.add(this.konva.line);
|
||||||
@ -59,12 +55,11 @@ export class CanvasBrushLineRenderer {
|
|||||||
update(state: CanvasBrushLineState, force = false): boolean {
|
update(state: CanvasBrushLineState, force = false): boolean {
|
||||||
if (force || this.state !== state) {
|
if (force || this.state !== state) {
|
||||||
this.log.trace({ state }, 'Updating brush line');
|
this.log.trace({ state }, 'Updating brush line');
|
||||||
const { points, color, clip, strokeWidth } = state;
|
const { points, color, strokeWidth } = state;
|
||||||
this.konva.line.setAttrs({
|
this.konva.line.setAttrs({
|
||||||
// A line with only one point will not be rendered, so we duplicate the points to make it visible
|
// A line with only one point will not be rendered, so we duplicate the points to make it visible
|
||||||
points: points.length === 2 ? [...points, ...points] : points,
|
points: points.length === 2 ? [...points, ...points] : points,
|
||||||
stroke: rgbaColorToString(color),
|
stroke: rgbaColorToString(color),
|
||||||
clip,
|
|
||||||
strokeWidth,
|
strokeWidth,
|
||||||
});
|
});
|
||||||
this.state = state;
|
this.state = state;
|
||||||
|
@ -1,10 +1,8 @@
|
|||||||
import type { JSONObject } from 'common/types';
|
import type { JSONObject } from 'common/types';
|
||||||
import { rgbaColorToString } from 'common/util/colorCodeTransformers';
|
|
||||||
import { deepClone } from 'common/util/deepClone';
|
import { deepClone } from 'common/util/deepClone';
|
||||||
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
||||||
import type { CanvasObjectRenderer } from 'features/controlLayers/konva/CanvasObjectRenderer';
|
import type { CanvasObjectRenderer } from 'features/controlLayers/konva/CanvasObjectRenderer';
|
||||||
import type { CanvasEraserLineState } from 'features/controlLayers/store/types';
|
import type { CanvasEraserLineState } from 'features/controlLayers/store/types';
|
||||||
import { RGBA_RED } from 'features/controlLayers/store/types';
|
|
||||||
import Konva from 'konva';
|
import Konva from 'konva';
|
||||||
import type { Logger } from 'roarr';
|
import type { Logger } from 'roarr';
|
||||||
|
|
||||||
@ -24,7 +22,7 @@ export class CanvasEraserLineRenderer {
|
|||||||
};
|
};
|
||||||
|
|
||||||
constructor(state: CanvasEraserLineState, parent: CanvasObjectRenderer) {
|
constructor(state: CanvasEraserLineState, parent: CanvasObjectRenderer) {
|
||||||
const { id, strokeWidth, clip, points } = state;
|
const { id, clip } = state;
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
this.manager = parent.manager;
|
this.manager = parent.manager;
|
||||||
@ -43,14 +41,10 @@ export class CanvasEraserLineRenderer {
|
|||||||
name: `${this.type}:line`,
|
name: `${this.type}:line`,
|
||||||
listening: false,
|
listening: false,
|
||||||
shadowForStrokeEnabled: false,
|
shadowForStrokeEnabled: false,
|
||||||
strokeWidth,
|
|
||||||
tension: 0.3,
|
tension: 0.3,
|
||||||
lineCap: 'round',
|
lineCap: 'round',
|
||||||
lineJoin: 'round',
|
lineJoin: 'round',
|
||||||
globalCompositeOperation: 'destination-out',
|
globalCompositeOperation: 'destination-out',
|
||||||
stroke: rgbaColorToString(RGBA_RED),
|
|
||||||
// A line with only one point will not be rendered, so we duplicate the points to make it visible
|
|
||||||
points: points.length === 2 ? [...points, ...points] : points,
|
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
this.konva.group.add(this.konva.line);
|
this.konva.group.add(this.konva.line);
|
||||||
@ -60,11 +54,10 @@ export class CanvasEraserLineRenderer {
|
|||||||
update(state: CanvasEraserLineState, force = false): boolean {
|
update(state: CanvasEraserLineState, force = false): boolean {
|
||||||
if (force || this.state !== state) {
|
if (force || this.state !== state) {
|
||||||
this.log.trace({ state }, 'Updating eraser line');
|
this.log.trace({ state }, 'Updating eraser line');
|
||||||
const { points, clip, strokeWidth } = state;
|
const { points, strokeWidth } = state;
|
||||||
this.konva.line.setAttrs({
|
this.konva.line.setAttrs({
|
||||||
// A line with only one point will not be rendered, so we duplicate the points to make it visible
|
// A line with only one point will not be rendered, so we duplicate the points to make it visible
|
||||||
points: points.length === 2 ? [...points, ...points] : points,
|
points: points.length === 2 ? [...points, ...points] : points,
|
||||||
clip,
|
|
||||||
strokeWidth,
|
strokeWidth,
|
||||||
});
|
});
|
||||||
this.state = state;
|
this.state = state;
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import type { JSONObject } from 'common/types';
|
import type { JSONObject } from 'common/types';
|
||||||
import { rgbColorToString } from 'common/util/colorCodeTransformers';
|
import { rgbColorToString } from 'common/util/colorCodeTransformers';
|
||||||
import { deepClone } from 'common/util/deepClone';
|
|
||||||
import { CanvasBrushLineRenderer } from 'features/controlLayers/konva/CanvasBrushLine';
|
import { CanvasBrushLineRenderer } from 'features/controlLayers/konva/CanvasBrushLine';
|
||||||
import { CanvasEraserLineRenderer } from 'features/controlLayers/konva/CanvasEraserLine';
|
import { CanvasEraserLineRenderer } from 'features/controlLayers/konva/CanvasEraserLine';
|
||||||
import { CanvasImageRenderer } from 'features/controlLayers/konva/CanvasImage';
|
import { CanvasImageRenderer } from 'features/controlLayers/konva/CanvasImage';
|
||||||
@ -67,8 +66,14 @@ export class CanvasObjectRenderer {
|
|||||||
* drawn in real-time, such as brush lines. The buffer object state only exists in this renderer and is not part of
|
* drawn in real-time, such as brush lines. The buffer object state only exists in this renderer and is not part of
|
||||||
* the application state until it is committed.
|
* the application state until it is committed.
|
||||||
*/
|
*/
|
||||||
buffer: AnyObjectState | null = null;
|
bufferState: AnyObjectState | null = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The object renderer for the buffer object state. It is created when the buffer object state is set and destroyed
|
||||||
|
* when the buffer object state is cleared. This is separate from the other object renderers to allow the buffer to
|
||||||
|
* be rendered separately.
|
||||||
|
*/
|
||||||
|
bufferRenderer: AnyObjectRenderer | null = null;
|
||||||
/**
|
/**
|
||||||
* A map of object renderers, keyed by their ID.
|
* A map of object renderers, keyed by their ID.
|
||||||
*/
|
*/
|
||||||
@ -82,6 +87,10 @@ export class CanvasObjectRenderer {
|
|||||||
* A Konva Group that holds all the object renderers.
|
* A Konva Group that holds all the object renderers.
|
||||||
*/
|
*/
|
||||||
objectGroup: Konva.Group;
|
objectGroup: Konva.Group;
|
||||||
|
/**
|
||||||
|
* A Konva Group that holds the buffer object renderer.
|
||||||
|
*/
|
||||||
|
bufferGroup: Konva.Group;
|
||||||
/**
|
/**
|
||||||
* The compositing rect is used to draw the inpaint mask as a single shape with a given opacity.
|
* The compositing rect is used to draw the inpaint mask as a single shape with a given opacity.
|
||||||
*
|
*
|
||||||
@ -113,10 +122,12 @@ export class CanvasObjectRenderer {
|
|||||||
|
|
||||||
this.konva = {
|
this.konva = {
|
||||||
objectGroup: new Konva.Group({ name: `${this.type}:object_group`, listening: false }),
|
objectGroup: new Konva.Group({ name: `${this.type}:object_group`, listening: false }),
|
||||||
|
bufferGroup: new Konva.Group({ name: `${this.type}:buffer_group`, listening: false }),
|
||||||
compositing: null,
|
compositing: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.parent.konva.layer.add(this.konva.objectGroup);
|
this.parent.konva.layer.add(this.konva.objectGroup);
|
||||||
|
this.parent.konva.layer.add(this.konva.bufferGroup);
|
||||||
|
|
||||||
if (this.parent.state.type === 'inpaint_mask' || this.parent.state.type === 'regional_guidance') {
|
if (this.parent.state.type === 'inpaint_mask' || this.parent.state.type === 'regional_guidance') {
|
||||||
const rect = new Konva.Rect({
|
const rect = new Konva.Rect({
|
||||||
@ -148,7 +159,6 @@ export class CanvasObjectRenderer {
|
|||||||
this.subscriptions.add(
|
this.subscriptions.add(
|
||||||
this.manager.stateApi.$stageAttrs.listen(() => {
|
this.manager.stateApi.$stageAttrs.listen(() => {
|
||||||
if (this.konva.compositing && this.parent.type === 'mask_adapter') {
|
if (this.konva.compositing && this.parent.type === 'mask_adapter') {
|
||||||
this.updateCompositingRectFill(this.parent.state.fill);
|
|
||||||
this.updateCompositingRectSize();
|
this.updateCompositingRectSize();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -166,7 +176,7 @@ export class CanvasObjectRenderer {
|
|||||||
const objectIds = objectStates.map((objectState) => objectState.id);
|
const objectIds = objectStates.map((objectState) => objectState.id);
|
||||||
|
|
||||||
for (const renderer of this.renderers.values()) {
|
for (const renderer of this.renderers.values()) {
|
||||||
if (!objectIds.includes(renderer.id) && renderer.id !== this.buffer?.id) {
|
if (!objectIds.includes(renderer.id)) {
|
||||||
this.renderers.delete(renderer.id);
|
this.renderers.delete(renderer.id);
|
||||||
renderer.destroy();
|
renderer.destroy();
|
||||||
didRender = true;
|
didRender = true;
|
||||||
@ -177,10 +187,6 @@ export class CanvasObjectRenderer {
|
|||||||
didRender = (await this.renderObject(objectState)) || didRender;
|
didRender = (await this.renderObject(objectState)) || didRender;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.buffer) {
|
|
||||||
didRender = (await this.renderObject(this.buffer)) || didRender;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.syncCache(didRender);
|
this.syncCache(didRender);
|
||||||
|
|
||||||
return didRender;
|
return didRender;
|
||||||
@ -236,6 +242,7 @@ export class CanvasObjectRenderer {
|
|||||||
this.konva.compositing.group.opacity(opacity);
|
this.konva.compositing.group.opacity(opacity);
|
||||||
} else {
|
} else {
|
||||||
this.konva.objectGroup.opacity(opacity);
|
this.konva.objectGroup.opacity(opacity);
|
||||||
|
this.konva.bufferGroup.opacity(opacity);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -253,10 +260,10 @@ export class CanvasObjectRenderer {
|
|||||||
|
|
||||||
let renderer = this.renderers.get(objectState.id);
|
let renderer = this.renderers.get(objectState.id);
|
||||||
|
|
||||||
const isFirstRender = renderer === undefined;
|
const isFirstRender = !renderer;
|
||||||
|
|
||||||
if (objectState.type === 'brush_line') {
|
if (objectState.type === 'brush_line') {
|
||||||
assert(renderer instanceof CanvasBrushLineRenderer || renderer === undefined);
|
assert(renderer instanceof CanvasBrushLineRenderer || !renderer);
|
||||||
|
|
||||||
if (!renderer) {
|
if (!renderer) {
|
||||||
renderer = new CanvasBrushLineRenderer(objectState, this);
|
renderer = new CanvasBrushLineRenderer(objectState, this);
|
||||||
@ -266,7 +273,7 @@ export class CanvasObjectRenderer {
|
|||||||
|
|
||||||
didRender = renderer.update(objectState, force || isFirstRender);
|
didRender = renderer.update(objectState, force || isFirstRender);
|
||||||
} else if (objectState.type === 'eraser_line') {
|
} else if (objectState.type === 'eraser_line') {
|
||||||
assert(renderer instanceof CanvasEraserLineRenderer || renderer === undefined);
|
assert(renderer instanceof CanvasEraserLineRenderer || !renderer);
|
||||||
|
|
||||||
if (!renderer) {
|
if (!renderer) {
|
||||||
renderer = new CanvasEraserLineRenderer(objectState, this);
|
renderer = new CanvasEraserLineRenderer(objectState, this);
|
||||||
@ -276,7 +283,7 @@ export class CanvasObjectRenderer {
|
|||||||
|
|
||||||
didRender = renderer.update(objectState, force || isFirstRender);
|
didRender = renderer.update(objectState, force || isFirstRender);
|
||||||
} else if (objectState.type === 'rect') {
|
} else if (objectState.type === 'rect') {
|
||||||
assert(renderer instanceof CanvasRectRenderer || renderer === undefined);
|
assert(renderer instanceof CanvasRectRenderer || !renderer);
|
||||||
|
|
||||||
if (!renderer) {
|
if (!renderer) {
|
||||||
renderer = new CanvasRectRenderer(objectState, this);
|
renderer = new CanvasRectRenderer(objectState, this);
|
||||||
@ -286,7 +293,7 @@ export class CanvasObjectRenderer {
|
|||||||
|
|
||||||
didRender = renderer.update(objectState, force || isFirstRender);
|
didRender = renderer.update(objectState, force || isFirstRender);
|
||||||
} else if (objectState.type === 'image') {
|
} else if (objectState.type === 'image') {
|
||||||
assert(renderer instanceof CanvasImageRenderer || renderer === undefined);
|
assert(renderer instanceof CanvasImageRenderer || !renderer);
|
||||||
|
|
||||||
if (!renderer) {
|
if (!renderer) {
|
||||||
renderer = new CanvasImageRenderer(objectState, this);
|
renderer = new CanvasImageRenderer(objectState, this);
|
||||||
@ -296,6 +303,9 @@ export class CanvasObjectRenderer {
|
|||||||
didRender = await renderer.update(objectState, force || isFirstRender);
|
didRender = await renderer.update(objectState, force || isFirstRender);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We should always clear the buffer when rendering a "real" object
|
||||||
|
this.clearBuffer();
|
||||||
|
|
||||||
if (didRender && this.konva.objectGroup.isCached()) {
|
if (didRender && this.konva.objectGroup.isCached()) {
|
||||||
this.konva.objectGroup.clearCache();
|
this.konva.objectGroup.clearCache();
|
||||||
}
|
}
|
||||||
@ -303,12 +313,64 @@ export class CanvasObjectRenderer {
|
|||||||
return didRender;
|
return didRender;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the buffer object. If the buffer renderer does not exist, it will be created and its Konva group added to the
|
||||||
|
* parent entity's buffer object group.
|
||||||
|
* @returns A promise that resolves to a boolean, indicating if the object was rendered.
|
||||||
|
*/
|
||||||
|
renderBufferObject = async (): Promise<boolean> => {
|
||||||
|
let didRender = false;
|
||||||
|
|
||||||
|
if (!this.bufferState) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.bufferState.type === 'brush_line') {
|
||||||
|
assert(this.bufferRenderer instanceof CanvasBrushLineRenderer || !this.bufferRenderer);
|
||||||
|
|
||||||
|
if (!this.bufferRenderer) {
|
||||||
|
this.bufferRenderer = new CanvasBrushLineRenderer(this.bufferState, this);
|
||||||
|
this.konva.bufferGroup.add(this.bufferRenderer.konva.group);
|
||||||
|
}
|
||||||
|
|
||||||
|
didRender = this.bufferRenderer.update(this.bufferState, true);
|
||||||
|
} else if (this.bufferState.type === 'eraser_line') {
|
||||||
|
assert(this.bufferRenderer instanceof CanvasEraserLineRenderer || !this.bufferRenderer);
|
||||||
|
|
||||||
|
if (!this.bufferRenderer) {
|
||||||
|
this.bufferRenderer = new CanvasEraserLineRenderer(this.bufferState, this);
|
||||||
|
this.konva.bufferGroup.add(this.bufferRenderer.konva.group);
|
||||||
|
}
|
||||||
|
|
||||||
|
didRender = this.bufferRenderer.update(this.bufferState, true);
|
||||||
|
} else if (this.bufferState.type === 'rect') {
|
||||||
|
assert(this.bufferRenderer instanceof CanvasRectRenderer || !this.bufferRenderer);
|
||||||
|
|
||||||
|
if (!this.bufferRenderer) {
|
||||||
|
this.bufferRenderer = new CanvasRectRenderer(this.bufferState, this);
|
||||||
|
this.konva.bufferGroup.add(this.bufferRenderer.konva.group);
|
||||||
|
}
|
||||||
|
|
||||||
|
didRender = this.bufferRenderer.update(this.bufferState, true);
|
||||||
|
} else if (this.bufferState.type === 'image') {
|
||||||
|
assert(this.bufferRenderer instanceof CanvasImageRenderer || !this.bufferRenderer);
|
||||||
|
|
||||||
|
if (!this.bufferRenderer) {
|
||||||
|
this.bufferRenderer = new CanvasImageRenderer(this.bufferState, this);
|
||||||
|
this.konva.bufferGroup.add(this.bufferRenderer.konva.group);
|
||||||
|
}
|
||||||
|
didRender = await this.bufferRenderer.update(this.bufferState, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return didRender;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determines if the renderer has a buffer object to render.
|
* Determines if the renderer has a buffer object to render.
|
||||||
* @returns Whether the renderer has a buffer object to render.
|
* @returns Whether the renderer has a buffer object to render.
|
||||||
*/
|
*/
|
||||||
hasBuffer = (): boolean => {
|
hasBuffer = (): boolean => {
|
||||||
return this.buffer !== null;
|
return this.bufferState !== null || this.bufferRenderer !== null;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -319,33 +381,27 @@ export class CanvasObjectRenderer {
|
|||||||
setBuffer = async (objectState: AnyObjectState): Promise<boolean> => {
|
setBuffer = async (objectState: AnyObjectState): Promise<boolean> => {
|
||||||
this.log.trace('Setting buffer');
|
this.log.trace('Setting buffer');
|
||||||
|
|
||||||
this.buffer = objectState;
|
this.bufferState = objectState;
|
||||||
return await this.renderObject(this.buffer, true);
|
return await this.renderBufferObject();
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clears the buffer object state.
|
* Clears the buffer object state.
|
||||||
*/
|
*/
|
||||||
clearBuffer = () => {
|
clearBuffer = () => {
|
||||||
this.log.trace('Clearing buffer');
|
if (this.bufferState || this.bufferRenderer) {
|
||||||
|
this.log.trace('Clearing buffer');
|
||||||
if (this.buffer) {
|
this.bufferRenderer?.destroy();
|
||||||
const renderer = this.renderers.get(this.buffer.id);
|
this.bufferRenderer = null;
|
||||||
if (renderer) {
|
this.bufferState = null;
|
||||||
this.log.trace('Destroying buffer object renderer');
|
|
||||||
renderer.destroy();
|
|
||||||
this.renderers.delete(renderer.id);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.buffer = null;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Commits the current buffer object, pushing the buffer object state back to the application state.
|
* Commits the current buffer object, pushing the buffer object state back to the application state.
|
||||||
*/
|
*/
|
||||||
commitBuffer = () => {
|
commitBuffer = () => {
|
||||||
if (!this.buffer) {
|
if (!this.bufferState) {
|
||||||
this.log.trace('No buffer to commit');
|
this.log.trace('No buffer to commit');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -354,25 +410,23 @@ export class CanvasObjectRenderer {
|
|||||||
|
|
||||||
// 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
|
||||||
this.buffer.id = getPrefixedId(this.buffer.type);
|
this.bufferState.id = getPrefixedId(this.bufferState.type);
|
||||||
|
|
||||||
if (this.buffer.type === 'brush_line') {
|
if (this.bufferState.type === 'brush_line') {
|
||||||
this.manager.stateApi.addBrushLine({
|
this.manager.stateApi.addBrushLine({
|
||||||
entityIdentifier: this.parent.getEntityIdentifier(),
|
entityIdentifier: this.parent.getEntityIdentifier(),
|
||||||
brushLine: this.buffer,
|
brushLine: this.bufferState,
|
||||||
});
|
});
|
||||||
} else if (this.buffer.type === 'eraser_line') {
|
} else if (this.bufferState.type === 'eraser_line') {
|
||||||
this.manager.stateApi.addEraserLine({
|
this.manager.stateApi.addEraserLine({
|
||||||
entityIdentifier: this.parent.getEntityIdentifier(),
|
entityIdentifier: this.parent.getEntityIdentifier(),
|
||||||
eraserLine: this.buffer,
|
eraserLine: this.bufferState,
|
||||||
});
|
});
|
||||||
} else if (this.buffer.type === 'rect') {
|
} else if (this.bufferState.type === 'rect') {
|
||||||
this.manager.stateApi.addRect({ entityIdentifier: this.parent.getEntityIdentifier(), rect: this.buffer });
|
this.manager.stateApi.addRect({ entityIdentifier: this.parent.getEntityIdentifier(), rect: this.bufferState });
|
||||||
} else {
|
} else {
|
||||||
this.log.warn({ buffer: this.buffer }, 'Invalid buffer object type');
|
this.log.warn({ buffer: this.bufferState }, 'Invalid buffer object type');
|
||||||
}
|
}
|
||||||
|
|
||||||
this.buffer = null;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
hideObjects = (except: string[] = []) => {
|
hideObjects = (except: string[] = []) => {
|
||||||
@ -416,7 +470,7 @@ export class CanvasObjectRenderer {
|
|||||||
* @returns Whether the renderer has any objects to render.
|
* @returns Whether the renderer has any objects to render.
|
||||||
*/
|
*/
|
||||||
hasObjects = (): boolean => {
|
hasObjects = (): boolean => {
|
||||||
return this.renderers.size > 0 || this.buffer !== null;
|
return this.renderers.size > 0 || this.bufferState !== null || this.bufferRenderer !== null;
|
||||||
};
|
};
|
||||||
|
|
||||||
getRasterizedImageCache = (rect: Rect): ImageCache | null => {
|
getRasterizedImageCache = (rect: Rect): ImageCache | null => {
|
||||||
@ -455,7 +509,8 @@ export class CanvasObjectRenderer {
|
|||||||
imageDTO = await uploadImage(blob, `${this.id}_rasterized.png`, 'other', true);
|
imageDTO = await uploadImage(blob, `${this.id}_rasterized.png`, 'other', true);
|
||||||
const imageObject = imageDTOToImageObject(imageDTO);
|
const imageObject = imageDTOToImageObject(imageDTO);
|
||||||
if (replaceObjects) {
|
if (replaceObjects) {
|
||||||
await this.renderObject(imageObject, true);
|
this.bufferState = imageObject;
|
||||||
|
await this.renderBufferObject();
|
||||||
}
|
}
|
||||||
this.manager.stateApi.rasterizeEntity({
|
this.manager.stateApi.rasterizeEntity({
|
||||||
entityIdentifier: this.parent.getEntityIdentifier(),
|
entityIdentifier: this.parent.getEntityIdentifier(),
|
||||||
@ -500,7 +555,7 @@ export class CanvasObjectRenderer {
|
|||||||
type: this.type,
|
type: this.type,
|
||||||
parent: this.parent.id,
|
parent: this.parent.id,
|
||||||
renderers: Array.from(this.renderers.values()).map((renderer) => renderer.repr()),
|
renderers: Array.from(this.renderers.values()).map((renderer) => renderer.repr()),
|
||||||
buffer: deepClone(this.buffer),
|
buffer: this.bufferRenderer?.repr(),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ export class CanvasRectRenderer {
|
|||||||
isFirstRender: boolean = false;
|
isFirstRender: boolean = false;
|
||||||
|
|
||||||
constructor(state: CanvasRectState, parent: CanvasObjectRenderer) {
|
constructor(state: CanvasRectState, parent: CanvasObjectRenderer) {
|
||||||
const { id, rect, color } = state;
|
const { id } = state;
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
this.manager = parent.manager;
|
this.manager = parent.manager;
|
||||||
@ -34,12 +34,7 @@ export class CanvasRectRenderer {
|
|||||||
|
|
||||||
this.konva = {
|
this.konva = {
|
||||||
group: new Konva.Group({ name: `${this.type}:group`, listening: false }),
|
group: new Konva.Group({ name: `${this.type}:group`, listening: false }),
|
||||||
rect: new Konva.Rect({
|
rect: new Konva.Rect({ name: `${this.type}:rect`, listening: false }),
|
||||||
name: `${this.type}:rect`,
|
|
||||||
...rect,
|
|
||||||
listening: false,
|
|
||||||
fill: rgbaColorToString(color),
|
|
||||||
}),
|
|
||||||
};
|
};
|
||||||
this.konva.group.add(this.konva.rect);
|
this.konva.group.add(this.konva.rect);
|
||||||
this.state = state;
|
this.state = state;
|
||||||
@ -52,7 +47,10 @@ export class CanvasRectRenderer {
|
|||||||
this.log.trace({ state }, 'Updating rect');
|
this.log.trace({ state }, 'Updating rect');
|
||||||
const { rect, color } = state;
|
const { rect, color } = state;
|
||||||
this.konva.rect.setAttrs({
|
this.konva.rect.setAttrs({
|
||||||
...rect,
|
x: rect.x,
|
||||||
|
y: rect.y,
|
||||||
|
width: rect.width,
|
||||||
|
height: rect.height,
|
||||||
fill: rgbaColorToString(color),
|
fill: rgbaColorToString(color),
|
||||||
});
|
});
|
||||||
this.state = state;
|
this.state = state;
|
||||||
|
@ -5,6 +5,7 @@ import type { CanvasMaskAdapter } from 'features/controlLayers/konva/CanvasMaskA
|
|||||||
import { getEmptyRect, getPrefixedId } from 'features/controlLayers/konva/util';
|
import { getEmptyRect, getPrefixedId } from 'features/controlLayers/konva/util';
|
||||||
import type { Coordinate, Rect } from 'features/controlLayers/store/types';
|
import type { Coordinate, Rect } from 'features/controlLayers/store/types';
|
||||||
import Konva from 'konva';
|
import Konva from 'konva';
|
||||||
|
import type { GroupConfig } from 'konva/lib/Group';
|
||||||
import { debounce, get } from 'lodash-es';
|
import { debounce, get } from 'lodash-es';
|
||||||
import type { Logger } from 'roarr';
|
import type { Logger } from 'roarr';
|
||||||
|
|
||||||
@ -543,6 +544,7 @@ export class CanvasTransformer {
|
|||||||
rotation: 0,
|
rotation: 0,
|
||||||
};
|
};
|
||||||
this.parent.renderer.konva.objectGroup.setAttrs(attrs);
|
this.parent.renderer.konva.objectGroup.setAttrs(attrs);
|
||||||
|
this.parent.renderer.konva.bufferGroup.setAttrs(attrs);
|
||||||
this.konva.outlineRect.setAttrs(attrs);
|
this.konva.outlineRect.setAttrs(attrs);
|
||||||
this.konva.proxyRect.setAttrs(attrs);
|
this.konva.proxyRect.setAttrs(attrs);
|
||||||
};
|
};
|
||||||
@ -555,12 +557,14 @@ export class CanvasTransformer {
|
|||||||
this.log.trace('Updating position');
|
this.log.trace('Updating position');
|
||||||
const position = get(arg, 'position', this.parent.state.position);
|
const position = get(arg, 'position', this.parent.state.position);
|
||||||
|
|
||||||
this.parent.renderer.konva.objectGroup.setAttrs({
|
const groupAttrs: Partial<GroupConfig> = {
|
||||||
x: position.x + this.pixelRect.x,
|
x: position.x + this.pixelRect.x,
|
||||||
y: position.y + this.pixelRect.y,
|
y: position.y + this.pixelRect.y,
|
||||||
offsetX: this.pixelRect.x,
|
offsetX: this.pixelRect.x,
|
||||||
offsetY: this.pixelRect.y,
|
offsetY: this.pixelRect.y,
|
||||||
});
|
};
|
||||||
|
this.parent.renderer.konva.objectGroup.setAttrs(groupAttrs);
|
||||||
|
this.parent.renderer.konva.bufferGroup.setAttrs(groupAttrs);
|
||||||
|
|
||||||
this.update(position, this.pixelRect);
|
this.update(position, this.pixelRect);
|
||||||
};
|
};
|
||||||
@ -609,12 +613,14 @@ export class CanvasTransformer {
|
|||||||
|
|
||||||
this.syncInteractionState();
|
this.syncInteractionState();
|
||||||
this.update(this.parent.state.position, this.pixelRect);
|
this.update(this.parent.state.position, this.pixelRect);
|
||||||
this.parent.renderer.konva.objectGroup.setAttrs({
|
const groupAttrs: Partial<GroupConfig> = {
|
||||||
x: this.parent.state.position.x + this.pixelRect.x,
|
x: this.parent.state.position.x + this.pixelRect.x,
|
||||||
y: this.parent.state.position.y + this.pixelRect.y,
|
y: this.parent.state.position.y + this.pixelRect.y,
|
||||||
offsetX: this.pixelRect.x,
|
offsetX: this.pixelRect.x,
|
||||||
offsetY: this.pixelRect.y,
|
offsetY: this.pixelRect.y,
|
||||||
});
|
};
|
||||||
|
this.parent.renderer.konva.objectGroup.setAttrs(groupAttrs);
|
||||||
|
this.parent.renderer.konva.bufferGroup.setAttrs(groupAttrs);
|
||||||
};
|
};
|
||||||
|
|
||||||
calculateRect = debounce(() => {
|
calculateRect = debounce(() => {
|
||||||
|
@ -217,7 +217,7 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
|
|||||||
const alignedPoint = alignCoordForTool(normalizedPoint, toolState.brush.width);
|
const alignedPoint = alignCoordForTool(normalizedPoint, toolState.brush.width);
|
||||||
if (e.evt.shiftKey && lastLinePoint) {
|
if (e.evt.shiftKey && lastLinePoint) {
|
||||||
// Create a straight line from the last line point
|
// Create a straight line from the last line point
|
||||||
if (selectedEntity.adapter.renderer.buffer) {
|
if (selectedEntity.adapter.renderer.bufferState) {
|
||||||
selectedEntity.adapter.renderer.commitBuffer();
|
selectedEntity.adapter.renderer.commitBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -236,7 +236,7 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
|
|||||||
clip: getClip(selectedEntity.state),
|
clip: getClip(selectedEntity.state),
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
if (selectedEntity.adapter.renderer.buffer) {
|
if (selectedEntity.adapter.renderer.bufferState) {
|
||||||
selectedEntity.adapter.renderer.commitBuffer();
|
selectedEntity.adapter.renderer.commitBuffer();
|
||||||
}
|
}
|
||||||
await selectedEntity.adapter.renderer.setBuffer({
|
await selectedEntity.adapter.renderer.setBuffer({
|
||||||
@ -256,7 +256,7 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
|
|||||||
const alignedPoint = alignCoordForTool(normalizedPoint, toolState.eraser.width);
|
const alignedPoint = alignCoordForTool(normalizedPoint, toolState.eraser.width);
|
||||||
if (e.evt.shiftKey && lastLinePoint) {
|
if (e.evt.shiftKey && lastLinePoint) {
|
||||||
// Create a straight line from the last line point
|
// Create a straight line from the last line point
|
||||||
if (selectedEntity.adapter.renderer.buffer) {
|
if (selectedEntity.adapter.renderer.bufferState) {
|
||||||
selectedEntity.adapter.renderer.commitBuffer();
|
selectedEntity.adapter.renderer.commitBuffer();
|
||||||
}
|
}
|
||||||
await selectedEntity.adapter.renderer.setBuffer({
|
await selectedEntity.adapter.renderer.setBuffer({
|
||||||
@ -273,7 +273,7 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
|
|||||||
clip: getClip(selectedEntity.state),
|
clip: getClip(selectedEntity.state),
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
if (selectedEntity.adapter.renderer.buffer) {
|
if (selectedEntity.adapter.renderer.bufferState) {
|
||||||
selectedEntity.adapter.renderer.commitBuffer();
|
selectedEntity.adapter.renderer.commitBuffer();
|
||||||
}
|
}
|
||||||
await selectedEntity.adapter.renderer.setBuffer({
|
await selectedEntity.adapter.renderer.setBuffer({
|
||||||
@ -288,7 +288,7 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (toolState.selected === 'rect') {
|
if (toolState.selected === 'rect') {
|
||||||
if (selectedEntity.adapter.renderer.buffer) {
|
if (selectedEntity.adapter.renderer.bufferState) {
|
||||||
selectedEntity.adapter.renderer.commitBuffer();
|
selectedEntity.adapter.renderer.commitBuffer();
|
||||||
}
|
}
|
||||||
await selectedEntity.adapter.renderer.setBuffer({
|
await selectedEntity.adapter.renderer.setBuffer({
|
||||||
@ -312,7 +312,7 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
|
|||||||
const toolState = getToolState();
|
const toolState = getToolState();
|
||||||
|
|
||||||
if (toolState.selected === 'brush') {
|
if (toolState.selected === 'brush') {
|
||||||
const drawingBuffer = selectedEntity.adapter.renderer.buffer;
|
const drawingBuffer = selectedEntity.adapter.renderer.bufferState;
|
||||||
if (drawingBuffer?.type === 'brush_line') {
|
if (drawingBuffer?.type === 'brush_line') {
|
||||||
selectedEntity.adapter.renderer.commitBuffer();
|
selectedEntity.adapter.renderer.commitBuffer();
|
||||||
} else {
|
} else {
|
||||||
@ -321,7 +321,7 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (toolState.selected === 'eraser') {
|
if (toolState.selected === 'eraser') {
|
||||||
const drawingBuffer = selectedEntity.adapter.renderer.buffer;
|
const drawingBuffer = selectedEntity.adapter.renderer.bufferState;
|
||||||
if (drawingBuffer?.type === 'eraser_line') {
|
if (drawingBuffer?.type === 'eraser_line') {
|
||||||
selectedEntity.adapter.renderer.commitBuffer();
|
selectedEntity.adapter.renderer.commitBuffer();
|
||||||
} else {
|
} else {
|
||||||
@ -330,7 +330,7 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (toolState.selected === 'rect') {
|
if (toolState.selected === 'rect') {
|
||||||
const drawingBuffer = selectedEntity.adapter.renderer.buffer;
|
const drawingBuffer = selectedEntity.adapter.renderer.bufferState;
|
||||||
if (drawingBuffer?.type === 'rect') {
|
if (drawingBuffer?.type === 'rect') {
|
||||||
selectedEntity.adapter.renderer.commitBuffer();
|
selectedEntity.adapter.renderer.commitBuffer();
|
||||||
} else {
|
} else {
|
||||||
@ -365,7 +365,7 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
|
|||||||
getIsPrimaryMouseDown(e)
|
getIsPrimaryMouseDown(e)
|
||||||
) {
|
) {
|
||||||
if (toolState.selected === 'brush') {
|
if (toolState.selected === 'brush') {
|
||||||
const drawingBuffer = selectedEntity.adapter.renderer.buffer;
|
const drawingBuffer = selectedEntity.adapter.renderer.bufferState;
|
||||||
if (drawingBuffer) {
|
if (drawingBuffer) {
|
||||||
if (drawingBuffer.type === 'brush_line') {
|
if (drawingBuffer.type === 'brush_line') {
|
||||||
const lastPoint = getLastPointOfLine(drawingBuffer.points);
|
const lastPoint = getLastPointOfLine(drawingBuffer.points);
|
||||||
@ -384,7 +384,7 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
|
|||||||
selectedEntity.adapter.renderer.clearBuffer();
|
selectedEntity.adapter.renderer.clearBuffer();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (selectedEntity.adapter.renderer.buffer) {
|
if (selectedEntity.adapter.renderer.bufferState) {
|
||||||
selectedEntity.adapter.renderer.commitBuffer();
|
selectedEntity.adapter.renderer.commitBuffer();
|
||||||
}
|
}
|
||||||
const normalizedPoint = offsetCoord(pos, selectedEntity.state.position);
|
const normalizedPoint = offsetCoord(pos, selectedEntity.state.position);
|
||||||
@ -402,7 +402,7 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (toolState.selected === 'eraser') {
|
if (toolState.selected === 'eraser') {
|
||||||
const drawingBuffer = selectedEntity.adapter.renderer.buffer;
|
const drawingBuffer = selectedEntity.adapter.renderer.bufferState;
|
||||||
if (drawingBuffer) {
|
if (drawingBuffer) {
|
||||||
if (drawingBuffer.type === 'eraser_line') {
|
if (drawingBuffer.type === 'eraser_line') {
|
||||||
const lastPoint = getLastPointOfLine(drawingBuffer.points);
|
const lastPoint = getLastPointOfLine(drawingBuffer.points);
|
||||||
@ -421,7 +421,7 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
|
|||||||
selectedEntity.adapter.renderer.clearBuffer();
|
selectedEntity.adapter.renderer.clearBuffer();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (selectedEntity.adapter.renderer.buffer) {
|
if (selectedEntity.adapter.renderer.bufferState) {
|
||||||
selectedEntity.adapter.renderer.commitBuffer();
|
selectedEntity.adapter.renderer.commitBuffer();
|
||||||
}
|
}
|
||||||
const normalizedPoint = offsetCoord(pos, selectedEntity.state.position);
|
const normalizedPoint = offsetCoord(pos, selectedEntity.state.position);
|
||||||
@ -438,7 +438,7 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (toolState.selected === 'rect') {
|
if (toolState.selected === 'rect') {
|
||||||
const drawingBuffer = selectedEntity.adapter.renderer.buffer;
|
const drawingBuffer = selectedEntity.adapter.renderer.bufferState;
|
||||||
if (drawingBuffer) {
|
if (drawingBuffer) {
|
||||||
if (drawingBuffer.type === 'rect') {
|
if (drawingBuffer.type === 'rect') {
|
||||||
const normalizedPoint = offsetCoord(pos, selectedEntity.state.position);
|
const normalizedPoint = offsetCoord(pos, selectedEntity.state.position);
|
||||||
@ -469,7 +469,7 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
|
|||||||
!$spaceKey.get() &&
|
!$spaceKey.get() &&
|
||||||
getIsPrimaryMouseDown(e)
|
getIsPrimaryMouseDown(e)
|
||||||
) {
|
) {
|
||||||
const drawingBuffer = selectedEntity.adapter.renderer.buffer;
|
const drawingBuffer = selectedEntity.adapter.renderer.bufferState;
|
||||||
const normalizedPoint = offsetCoord(pos, selectedEntity.state.position);
|
const normalizedPoint = offsetCoord(pos, selectedEntity.state.position);
|
||||||
if (toolState.selected === 'brush' && drawingBuffer?.type === 'brush_line') {
|
if (toolState.selected === 'brush' && drawingBuffer?.type === 'brush_line') {
|
||||||
const alignedPoint = alignCoordForTool(normalizedPoint, toolState.brush.width);
|
const alignedPoint = alignCoordForTool(normalizedPoint, toolState.brush.width);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user