feat(ui): use black bg when rasterizing control images

This commit is contained in:
psychedelicious
2024-09-06 10:01:20 +10:00
parent ce4c79a8d9
commit 2b93dbd96a
8 changed files with 61 additions and 34 deletions

View File

@@ -63,7 +63,7 @@ export class CanvasEntityAdapterControlLayer extends CanvasEntityAdapterBase<Can
// The opacity may have been changed in response to user selecting a different entity category, so we must restore
// the original opacity before rendering the canvas
const attrs: GroupConfig = { opacity: this.state.opacity };
const canvas = this.renderer.getCanvas(rect, attrs);
const canvas = this.renderer.getCanvas({ rect, attrs });
return canvas;
};

View File

@@ -74,7 +74,7 @@ export class CanvasEntityAdapterInpaintMask extends CanvasEntityAdapterBase<Canv
// The opacity may have been changed in response to user selecting a different entity category, and the mask regions
// should be fully opaque - set opacity to 1 before rendering the canvas
const attrs: GroupConfig = { opacity: 1 };
const canvas = this.renderer.getCanvas(rect, attrs);
const canvas = this.renderer.getCanvas({ rect, attrs });
return canvas;
};
}

View File

@@ -56,7 +56,7 @@ export class CanvasEntityAdapterRasterLayer extends CanvasEntityAdapterBase<Canv
// The opacity may have been changed in response to user selecting a different entity category, so we must restore
// the original opacity before rendering the canvas
const attrs: GroupConfig = { opacity: this.state.opacity };
const canvas = this.renderer.getCanvas(rect, attrs);
const canvas = this.renderer.getCanvas({ rect, attrs });
return canvas;
};

View File

@@ -74,7 +74,7 @@ export class CanvasEntityAdapterRegionalGuidance extends CanvasEntityAdapterBase
// The opacity may have been changed in response to user selecting a different entity category, and the mask regions
// should be fully opaque - set opacity to 1 before rendering the canvas
const attrs: GroupConfig = { opacity: 1 };
const canvas = this.renderer.getCanvas(rect, attrs);
const canvas = this.renderer.getCanvas({ rect, attrs });
return canvas;
};
}

View File

@@ -539,10 +539,16 @@ export class CanvasEntityObjectRenderer extends CanvasModuleBase {
* @param rect The rect to rasterize. If omitted, the entity's full rect will be used.
* @returns A promise that resolves to the rasterized image DTO.
*/
rasterize = async (options: { rect: Rect; replaceObjects?: boolean; attrs?: GroupConfig }): Promise<ImageDTO> => {
const { rect, replaceObjects, attrs } = { replaceObjects: false, attrs: {}, ...options };
rasterize = async (options: {
rect: Rect;
replaceObjects?: boolean;
attrs?: GroupConfig;
bg?: string;
}): Promise<ImageDTO> => {
const { rect, replaceObjects, attrs, bg } = { replaceObjects: false, attrs: {}, ...options };
let imageDTO: ImageDTO | null = null;
const hash = this.parent.hash({ rect, attrs });
const rasterizeArgs = { rect, attrs, bg };
const hash = this.parent.hash(rasterizeArgs);
const cachedImageName = this.manager.cache.imageNameCache.get(hash);
if (cachedImageName) {
@@ -553,9 +559,9 @@ export class CanvasEntityObjectRenderer extends CanvasModuleBase {
}
}
this.log.trace({ rect }, 'Rasterizing entity');
this.log.trace({ rasterizeArgs }, 'Rasterizing entity');
const blob = await this.getBlob(rect, attrs);
const blob = await this.getBlob(rasterizeArgs);
if (this.manager._isDebugging) {
previewBlob(blob, 'Rasterized entity');
}
@@ -608,32 +614,35 @@ export class CanvasEntityObjectRenderer extends CanvasModuleBase {
}
}, 300);
cloneObjectGroup = (attrs?: GroupConfig): Konva.Group => {
cloneObjectGroup = (arg: { attrs?: GroupConfig } = {}): Konva.Group => {
const { attrs } = arg;
const clone = this.konva.objectGroup.clone();
clone.cache();
if (attrs) {
clone.setAttrs(attrs);
}
clone.cache();
return clone;
};
getCanvas = (rect?: Rect, attrs?: GroupConfig): HTMLCanvasElement => {
const clone = this.cloneObjectGroup(attrs);
const canvas = konvaNodeToCanvas(clone, rect);
getCanvas = (arg: { rect?: Rect; attrs?: GroupConfig; bg?: string } = {}): HTMLCanvasElement => {
const { rect, attrs, bg } = arg;
const clone = this.cloneObjectGroup({ attrs });
const canvas = konvaNodeToCanvas({ node: clone, rect, bg });
clone.destroy();
return canvas;
};
getBlob = async (rect?: Rect, attrs?: GroupConfig): Promise<Blob> => {
const clone = this.cloneObjectGroup(attrs);
const blob = await konvaNodeToBlob(clone, rect);
clone.destroy();
getBlob = async (arg: { rect?: Rect; attrs?: GroupConfig; bg?: string } = {}): Promise<Blob> => {
const { rect, attrs, bg } = arg;
const clone = this.cloneObjectGroup({ attrs });
const blob = await konvaNodeToBlob({ node: clone, rect, bg });
return blob;
};
getImageData = (rect?: Rect, attrs?: GroupConfig): ImageData => {
const clone = this.cloneObjectGroup(attrs);
const imageData = konvaNodeToImageData(clone, rect);
getImageData = (arg: { rect?: Rect; attrs?: GroupConfig; bg?: string } = {}): ImageData => {
const { rect, attrs, bg } = arg;
const clone = this.cloneObjectGroup({ attrs });
const imageData = konvaNodeToImageData({ node: clone, rect, bg });
clone.destroy();
return imageData;
};

View File

@@ -705,7 +705,7 @@ export class CanvasEntityTransformer extends CanvasModuleBase {
}
// We have eraser strokes - we must calculate the bbox using pixel data
const canvas = this.parent.renderer.getCanvas(undefined, { opacity: 1 });
const canvas = this.parent.renderer.getCanvas({ attrs: { opacity: 1 } });
const imageData = canvasToImageData(canvas);
this.manager.worker.requestBbox(
{ buffer: imageData.data.buffer, width: imageData.width, height: imageData.height },

View File

@@ -304,8 +304,24 @@ export const dataURLToImageData = (dataURL: string, width: number, height: numbe
});
};
export const konvaNodeToCanvas = (node: Konva.Node, bbox?: Rect): HTMLCanvasElement => {
return node.toCanvas({ ...(bbox ?? {}) });
export const konvaNodeToCanvas = (arg: { node: Konva.Node; rect?: Rect; bg?: string }): HTMLCanvasElement => {
const { node, rect, bg } = arg;
const canvas = node.toCanvas({ ...(rect ?? {}) });
if (!bg) {
return canvas;
}
// We need to draw the canvas onto a new canvas with the specified background color
const bgCanvas = document.createElement('canvas');
bgCanvas.width = canvas.width;
bgCanvas.height = canvas.height;
const bgCtx = bgCanvas.getContext('2d');
assert(bgCtx !== null, 'bgCtx is null');
bgCtx.fillStyle = bg;
bgCtx.fillRect(0, 0, bgCanvas.width, bgCanvas.height);
bgCtx.drawImage(canvas, 0, 0);
return bgCanvas;
};
/**
@@ -335,22 +351,24 @@ export const canvasToImageData = (canvas: HTMLCanvasElement): ImageData => {
/**
* Converts a Konva node to an ImageData object
* @param node - The Konva node to convert to an ImageData object
* @param bbox - The bounding box to crop to
* @param rect - The bounding box to crop to
* @returns A Promise that resolves with ImageData object of the node cropped to the bounding box
*/
export const konvaNodeToImageData = (node: Konva.Node, bbox?: Rect): ImageData => {
const canvas = konvaNodeToCanvas(node, bbox);
export const konvaNodeToImageData = (arg: { node: Konva.Node; rect?: Rect; bg?: string }): ImageData => {
const { node, rect, bg } = arg;
const canvas = konvaNodeToCanvas({ node, rect, bg });
return canvasToImageData(canvas);
};
/**
* Converts a Konva node to a Blob
* @param node - The Konva node to convert to a Blob
* @param bbox - The bounding box to crop to
* @param rect - The bounding box to crop to
* @returns A Promise that resolves to the Blob or null,
*/
export const konvaNodeToBlob = (node: Konva.Node, bbox?: Rect): Promise<Blob> => {
const canvas = konvaNodeToCanvas(node, bbox);
export const konvaNodeToBlob = (arg: { node: Konva.Node; rect?: Rect; bg?: string }): Promise<Blob> => {
const { node, rect, bg } = arg;
const canvas = konvaNodeToCanvas({ node, rect, bg });
return canvasToBlob(canvas);
};

View File

@@ -17,7 +17,7 @@ export const addControlNets = async (
manager: CanvasManager,
layers: CanvasControlLayerState[],
g: Graph,
bbox: Rect,
rect: Rect,
collector: Invocation<'collect'>,
base: BaseModelType
): Promise<AddControlNetsResult> => {
@@ -35,7 +35,7 @@ export const addControlNets = async (
const adapter = manager.adapters.controlLayers.get(layer.id);
assert(adapter, 'Adapter not found');
const imageDTO = await adapter.renderer.rasterize({ rect: bbox, attrs: { opacity: 1, filters: [] } });
const imageDTO = await adapter.renderer.rasterize({ rect, attrs: { opacity: 1, filters: [] }, bg: 'black' });
addControlNetToGraph(g, layer, imageDTO, collector);
}
@@ -50,7 +50,7 @@ export const addT2IAdapters = async (
manager: CanvasManager,
layers: CanvasControlLayerState[],
g: Graph,
bbox: Rect,
rect: Rect,
collector: Invocation<'collect'>,
base: BaseModelType
): Promise<AddT2IAdaptersResult> => {
@@ -68,7 +68,7 @@ export const addT2IAdapters = async (
const adapter = manager.adapters.controlLayers.get(layer.id);
assert(adapter, 'Adapter not found');
const imageDTO = await adapter.renderer.rasterize({ rect: bbox, attrs: { opacity: 1, filters: [] } });
const imageDTO = await adapter.renderer.rasterize({ rect, attrs: { opacity: 1, filters: [], bg: 'black' } });
addT2IAdapterToGraph(g, layer, imageDTO, collector);
}