mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
all files migrated; tweaks needed
This commit is contained in:
@ -0,0 +1,17 @@
|
||||
import { Vector2d } from 'konva/lib/types';
|
||||
|
||||
const calculateCoordinates = (
|
||||
containerWidth: number,
|
||||
containerHeight: number,
|
||||
containerX: number,
|
||||
containerY: number,
|
||||
contentWidth: number,
|
||||
contentHeight: number,
|
||||
scale: number
|
||||
): Vector2d => {
|
||||
const x = containerWidth / 2 - (containerX + contentWidth / 2) * scale;
|
||||
const y = containerHeight / 2 - (containerY + contentHeight / 2) * scale;
|
||||
return { x, y };
|
||||
};
|
||||
|
||||
export default calculateCoordinates;
|
@ -0,0 +1,14 @@
|
||||
const calculateScale = (
|
||||
containerWidth: number,
|
||||
containerHeight: number,
|
||||
contentWidth: number,
|
||||
contentHeight: number,
|
||||
padding = 0.95
|
||||
): number => {
|
||||
const scaleX = (containerWidth * padding) / contentWidth;
|
||||
const scaleY = (containerHeight * padding) / contentHeight;
|
||||
const scaleFit = Math.min(1, Math.min(scaleX, scaleY));
|
||||
return scaleFit;
|
||||
};
|
||||
|
||||
export default calculateScale;
|
@ -0,0 +1,16 @@
|
||||
import { RgbaColor, RgbColor } from 'react-colorful';
|
||||
|
||||
export const rgbaColorToString = (color: RgbaColor): string => {
|
||||
const { r, g, b, a } = color;
|
||||
return `rgba(${r}, ${g}, ${b}, ${a})`;
|
||||
};
|
||||
|
||||
export const rgbaColorToRgbString = (color: RgbaColor): string => {
|
||||
const { r, g, b } = color;
|
||||
return `rgba(${r}, ${g}, ${b})`;
|
||||
};
|
||||
|
||||
export const rgbColorToString = (color: RgbColor): string => {
|
||||
const { r, g, b } = color;
|
||||
return `rgba(${r}, ${g}, ${b})`;
|
||||
};
|
17
invokeai/frontend/web/src/features/canvas/util/constants.ts
Normal file
17
invokeai/frontend/web/src/features/canvas/util/constants.ts
Normal file
@ -0,0 +1,17 @@
|
||||
// bounding box anchor size
|
||||
export const TRANSFORMER_ANCHOR_SIZE = 15;
|
||||
|
||||
// canvas wheel zoom exponential scale factor
|
||||
export const CANVAS_SCALE_BY = 0.999;
|
||||
|
||||
// minimum (furthest-zoomed-out) scale
|
||||
export const MIN_CANVAS_SCALE = 0.1;
|
||||
|
||||
// maximum (furthest-zoomed-in) scale
|
||||
export const MAX_CANVAS_SCALE = 20;
|
||||
|
||||
// padding given to initial image/bounding box when stage view is reset
|
||||
export const STAGE_PADDING_PERCENTAGE = 0.95;
|
||||
|
||||
export const COLOR_PICKER_SIZE = 30;
|
||||
export const COLOR_PICKER_STROKE_RADIUS = 10;
|
34
invokeai/frontend/web/src/features/canvas/util/copyImage.ts
Normal file
34
invokeai/frontend/web/src/features/canvas/util/copyImage.ts
Normal file
@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Copies an image to the clipboard by drawing it to a canvas and then
|
||||
* calling toBlob() on the canvas.
|
||||
*/
|
||||
const copyImage = (url: string, width: number, height: number) => {
|
||||
const imageElement = document.createElement('img');
|
||||
|
||||
imageElement.addEventListener('load', () => {
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
const context = canvas.getContext('2d');
|
||||
|
||||
if (!context) return;
|
||||
|
||||
context.drawImage(imageElement, 0, 0);
|
||||
|
||||
canvas.toBlob((blob) => {
|
||||
blob &&
|
||||
navigator.clipboard.write([
|
||||
new ClipboardItem({
|
||||
[blob.type]: blob,
|
||||
}),
|
||||
]);
|
||||
});
|
||||
|
||||
canvas.remove();
|
||||
imageElement.remove();
|
||||
});
|
||||
|
||||
imageElement.src = url;
|
||||
};
|
||||
|
||||
export default copyImage;
|
@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Downloads a file, given its URL.
|
||||
*/
|
||||
const downloadFile = (url: string) => {
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = '';
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
a.remove();
|
||||
};
|
||||
|
||||
export default downloadFile;
|
@ -0,0 +1,10 @@
|
||||
import { Vector2d } from 'konva/lib/types';
|
||||
|
||||
const floorCoordinates = (coord: Vector2d): Vector2d => {
|
||||
return {
|
||||
x: Math.floor(coord.x),
|
||||
y: Math.floor(coord.y),
|
||||
};
|
||||
};
|
||||
|
||||
export default floorCoordinates;
|
@ -0,0 +1,64 @@
|
||||
import { CanvasMaskLine } from 'features/canvas/store/canvasTypes';
|
||||
import Konva from 'konva';
|
||||
import { IRect } from 'konva/lib/types';
|
||||
|
||||
/**
|
||||
* Generating a mask image from InpaintingCanvas.tsx is not as simple
|
||||
* as calling toDataURL() on the canvas, because the mask may be represented
|
||||
* by colored lines or transparency, or the user may have inverted the mask
|
||||
* display.
|
||||
*
|
||||
* So we need to regenerate the mask image by creating an offscreen canvas,
|
||||
* drawing the mask and compositing everything correctly to output a valid
|
||||
* mask image.
|
||||
*/
|
||||
const generateMask = (lines: CanvasMaskLine[], boundingBox: IRect): string => {
|
||||
// create an offscreen canvas and add the mask to it
|
||||
const { width, height } = boundingBox;
|
||||
|
||||
const offscreenContainer = document.createElement('div');
|
||||
|
||||
const stage = new Konva.Stage({
|
||||
container: offscreenContainer,
|
||||
width: width,
|
||||
height: height,
|
||||
});
|
||||
|
||||
const baseLayer = new Konva.Layer();
|
||||
const maskLayer = new Konva.Layer();
|
||||
|
||||
// composite the image onto the mask layer
|
||||
baseLayer.add(
|
||||
new Konva.Rect({
|
||||
...boundingBox,
|
||||
fill: 'white',
|
||||
})
|
||||
);
|
||||
|
||||
lines.forEach((line) =>
|
||||
maskLayer.add(
|
||||
new Konva.Line({
|
||||
points: line.points,
|
||||
stroke: 'black',
|
||||
strokeWidth: line.strokeWidth * 2,
|
||||
tension: 0,
|
||||
lineCap: 'round',
|
||||
lineJoin: 'round',
|
||||
shadowForStrokeEnabled: false,
|
||||
globalCompositeOperation:
|
||||
line.tool === 'brush' ? 'source-over' : 'destination-out',
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
stage.add(baseLayer);
|
||||
stage.add(maskLayer);
|
||||
|
||||
const dataURL = stage.toDataURL({ ...boundingBox });
|
||||
|
||||
offscreenContainer.remove();
|
||||
|
||||
return dataURL;
|
||||
};
|
||||
|
||||
export default generateMask;
|
@ -0,0 +1,39 @@
|
||||
import { roundToMultiple } from 'common/util/roundDownToMultiple';
|
||||
import { Dimensions } from '../store/canvasTypes';
|
||||
|
||||
const getScaledBoundingBoxDimensions = (dimensions: Dimensions) => {
|
||||
const { width, height } = dimensions;
|
||||
|
||||
const scaledDimensions = { width, height };
|
||||
const targetArea = 512 * 512;
|
||||
const aspectRatio = width / height;
|
||||
let currentArea = width * height;
|
||||
let maxDimension = 448;
|
||||
while (currentArea < targetArea) {
|
||||
maxDimension += 64;
|
||||
if (width === height) {
|
||||
scaledDimensions.width = 512;
|
||||
scaledDimensions.height = 512;
|
||||
break;
|
||||
} else {
|
||||
if (aspectRatio > 1) {
|
||||
scaledDimensions.width = maxDimension;
|
||||
scaledDimensions.height = roundToMultiple(
|
||||
maxDimension / aspectRatio,
|
||||
64
|
||||
);
|
||||
} else if (aspectRatio < 1) {
|
||||
scaledDimensions.height = maxDimension;
|
||||
scaledDimensions.width = roundToMultiple(
|
||||
maxDimension * aspectRatio,
|
||||
64
|
||||
);
|
||||
}
|
||||
currentArea = scaledDimensions.width * scaledDimensions.height;
|
||||
}
|
||||
}
|
||||
|
||||
return scaledDimensions;
|
||||
};
|
||||
|
||||
export default getScaledBoundingBoxDimensions;
|
@ -0,0 +1,18 @@
|
||||
import { Stage } from 'konva/lib/Stage';
|
||||
|
||||
const getScaledCursorPosition = (stage: Stage) => {
|
||||
const pointerPosition = stage.getPointerPosition();
|
||||
|
||||
const stageTransform = stage.getAbsoluteTransform().copy();
|
||||
|
||||
if (!pointerPosition || !stageTransform) return;
|
||||
|
||||
const scaledCursorPosition = stageTransform.invert().point(pointerPosition);
|
||||
|
||||
return {
|
||||
x: scaledCursorPosition.x,
|
||||
y: scaledCursorPosition.y,
|
||||
};
|
||||
};
|
||||
|
||||
export default getScaledCursorPosition;
|
@ -0,0 +1,16 @@
|
||||
import Konva from 'konva';
|
||||
|
||||
let canvasBaseLayer: Konva.Layer | null = null;
|
||||
let canvasStage: Konva.Stage | null = null;
|
||||
|
||||
export const setCanvasBaseLayer = (layer: Konva.Layer) => {
|
||||
canvasBaseLayer = layer;
|
||||
};
|
||||
|
||||
export const getCanvasBaseLayer = () => canvasBaseLayer;
|
||||
|
||||
export const setCanvasStage = (stage: Konva.Stage) => {
|
||||
canvasStage = stage;
|
||||
};
|
||||
|
||||
export const getCanvasStage = () => canvasStage;
|
@ -0,0 +1,53 @@
|
||||
import Konva from 'konva';
|
||||
import { IRect, Vector2d } from 'konva/lib/types';
|
||||
|
||||
const layerToDataURL = (
|
||||
layer: Konva.Layer,
|
||||
stageScale: number,
|
||||
stageCoordinates: Vector2d,
|
||||
boundingBox?: IRect
|
||||
) => {
|
||||
const tempScale = layer.scale();
|
||||
|
||||
const relativeClientRect = layer.getClientRect({
|
||||
relativeTo: layer.getParent(),
|
||||
});
|
||||
|
||||
// Scale the canvas before getting it as a Blob
|
||||
layer.scale({
|
||||
x: 1 / stageScale,
|
||||
y: 1 / stageScale,
|
||||
});
|
||||
|
||||
const { x, y, width, height } = layer.getClientRect();
|
||||
const dataURLBoundingBox = boundingBox
|
||||
? {
|
||||
x: boundingBox.x + stageCoordinates.x,
|
||||
y: boundingBox.y + stageCoordinates.y,
|
||||
width: boundingBox.width,
|
||||
height: boundingBox.height,
|
||||
}
|
||||
: {
|
||||
x: x,
|
||||
y: y,
|
||||
width: width,
|
||||
height: height,
|
||||
};
|
||||
|
||||
const dataURL = layer.toDataURL(dataURLBoundingBox);
|
||||
|
||||
// Unscale the canvas
|
||||
layer.scale(tempScale);
|
||||
|
||||
return {
|
||||
dataURL,
|
||||
boundingBox: {
|
||||
x: relativeClientRect.x,
|
||||
y: relativeClientRect.y,
|
||||
width: width,
|
||||
height: height,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export default layerToDataURL;
|
@ -0,0 +1,11 @@
|
||||
import { roundToMultiple } from 'common/util/roundDownToMultiple';
|
||||
import { Dimensions } from '../store/canvasTypes';
|
||||
|
||||
const roundDimensionsTo64 = (dimensions: Dimensions): Dimensions => {
|
||||
return {
|
||||
width: roundToMultiple(dimensions.width, 64),
|
||||
height: roundToMultiple(dimensions.height, 64),
|
||||
};
|
||||
};
|
||||
|
||||
export default roundDimensionsTo64;
|
@ -0,0 +1,5 @@
|
||||
const roundToHundreth = (val: number): number => {
|
||||
return Math.round(val * 100) / 100;
|
||||
};
|
||||
|
||||
export default roundToHundreth;
|
Reference in New Issue
Block a user