fix(ui): clamp incoming w/h to ensure always a multiple of 8

When recalling metadata and/or using control image dimensions, it was possible to set a width or height that was not a multiple of 8, resulting in generation failures.

Added a `clamp` option to the w/h actions to fix this. The option is used for all untrusted sources - everything except for the w/h number inputs, which clamp the values themselves.
This commit is contained in:
psychedelicious 2024-05-02 18:28:46 +10:00 committed by Kent Keirsey
parent 1b13fee256
commit 474eab6f8a
6 changed files with 30 additions and 23 deletions

View File

@ -96,16 +96,16 @@ export const addSetDefaultSettingsListener = (startAppListening: AppStartListeni
dispatch(setScheduler(scheduler)); dispatch(setScheduler(scheduler));
} }
} }
const setSizeOptions = { updateAspectRatio: true, clamp: true };
if (width) { if (width) {
if (isParameterWidth(width)) { if (isParameterWidth(width)) {
dispatch(widthChanged({ width, updateAspectRatio: true })); dispatch(widthChanged({ width, ...setSizeOptions }));
} }
} }
if (height) { if (height) {
if (isParameterHeight(height)) { if (isParameterHeight(height)) {
dispatch(heightChanged({ height, updateAspectRatio: true })); dispatch(heightChanged({ height, ...setSizeOptions }));
} }
} }

View File

@ -96,12 +96,13 @@ const ControlAdapterImagePreview = ({ isSmall, id }: Props) => {
if (activeTabName === 'unifiedCanvas') { if (activeTabName === 'unifiedCanvas') {
dispatch(setBoundingBoxDimensions({ width: controlImage.width, height: controlImage.height }, optimalDimension)); dispatch(setBoundingBoxDimensions({ width: controlImage.width, height: controlImage.height }, optimalDimension));
} else { } else {
const options = { updateAspectRatio: true, clamp: true };
const { width, height } = calculateNewSize( const { width, height } = calculateNewSize(
controlImage.width / controlImage.height, controlImage.width / controlImage.height,
optimalDimension * optimalDimension optimalDimension * optimalDimension
); );
dispatch(widthChanged({ width, updateAspectRatio: true })); dispatch(widthChanged({ width, ...options }));
dispatch(heightChanged({ height, updateAspectRatio: true })); dispatch(heightChanged({ height, ...options }));
} }
}, [controlImage, activeTabName, dispatch, optimalDimension]); }, [controlImage, activeTabName, dispatch, optimalDimension]);

View File

@ -85,17 +85,19 @@ export const ControlAdapterImagePreview = memo(
setBoundingBoxDimensions({ width: controlImage.width, height: controlImage.height }, optimalDimension) setBoundingBoxDimensions({ width: controlImage.width, height: controlImage.height }, optimalDimension)
); );
} else { } else {
const options = { updateAspectRatio: true, clamp: true };
if (shift) { if (shift) {
const { width, height } = controlImage; const { width, height } = controlImage;
dispatch(widthChanged({ width, updateAspectRatio: true })); dispatch(widthChanged({ width, ...options }));
dispatch(heightChanged({ height, updateAspectRatio: true })); dispatch(heightChanged({ height, ...options }));
} else { } else {
const { width, height } = calculateNewSize( const { width, height } = calculateNewSize(
controlImage.width / controlImage.height, controlImage.width / controlImage.height,
optimalDimension * optimalDimension optimalDimension * optimalDimension
); );
dispatch(widthChanged({ width, updateAspectRatio: true })); dispatch(widthChanged({ width, ...options }));
dispatch(heightChanged({ height, updateAspectRatio: true })); dispatch(heightChanged({ height, ...options }));
} }
} }
}, [controlImage, activeTabName, dispatch, optimalDimension, shift]); }, [controlImage, activeTabName, dispatch, optimalDimension, shift]);

View File

@ -51,17 +51,18 @@ export const IPAdapterImagePreview = memo(
setBoundingBoxDimensions({ width: controlImage.width, height: controlImage.height }, optimalDimension) setBoundingBoxDimensions({ width: controlImage.width, height: controlImage.height }, optimalDimension)
); );
} else { } else {
const options = { updateAspectRatio: true, clamp: true };
if (shift) { if (shift) {
const { width, height } = controlImage; const { width, height } = controlImage;
dispatch(widthChanged({ width, updateAspectRatio: true })); dispatch(widthChanged({ width, ...options }));
dispatch(heightChanged({ height, updateAspectRatio: true })); dispatch(heightChanged({ height, ...options }));
} else { } else {
const { width, height } = calculateNewSize( const { width, height } = calculateNewSize(
controlImage.width / controlImage.height, controlImage.width / controlImage.height,
optimalDimension * optimalDimension optimalDimension * optimalDimension
); );
dispatch(widthChanged({ width, updateAspectRatio: true })); dispatch(widthChanged({ width, ...options }));
dispatch(heightChanged({ height, updateAspectRatio: true })); dispatch(heightChanged({ height, ...options }));
} }
} }
}, [controlImage, activeTabName, dispatch, optimalDimension, shift]); }, [controlImage, activeTabName, dispatch, optimalDimension, shift]);

View File

@ -3,6 +3,7 @@ import { createSlice, isAnyOf } from '@reduxjs/toolkit';
import type { PersistConfig, RootState } from 'app/store/store'; import type { PersistConfig, RootState } from 'app/store/store';
import { moveBackward, moveForward, moveToBack, moveToFront } from 'common/util/arrayUtils'; import { moveBackward, moveForward, moveToBack, moveToFront } from 'common/util/arrayUtils';
import { deepClone } from 'common/util/deepClone'; import { deepClone } from 'common/util/deepClone';
import { roundDownToMultiple } from 'common/util/roundDownToMultiple';
import type { import type {
CLIPVisionModelV2, CLIPVisionModelV2,
ControlModeV2, ControlModeV2,
@ -626,20 +627,20 @@ export const controlLayersSlice = createSlice({
shouldConcatPromptsChanged: (state, action: PayloadAction<boolean>) => { shouldConcatPromptsChanged: (state, action: PayloadAction<boolean>) => {
state.shouldConcatPrompts = action.payload; state.shouldConcatPrompts = action.payload;
}, },
widthChanged: (state, action: PayloadAction<{ width: number; updateAspectRatio?: boolean }>) => { widthChanged: (state, action: PayloadAction<{ width: number; updateAspectRatio?: boolean; clamp?: boolean }>) => {
const { width, updateAspectRatio } = action.payload; const { width, updateAspectRatio, clamp } = action.payload;
state.size.width = width; state.size.width = clamp ? Math.max(roundDownToMultiple(width, 8), 64) : width;
if (updateAspectRatio) { if (updateAspectRatio) {
state.size.aspectRatio.value = width / state.size.height; state.size.aspectRatio.value = state.size.width / state.size.height;
state.size.aspectRatio.id = 'Free'; state.size.aspectRatio.id = 'Free';
state.size.aspectRatio.isLocked = false; state.size.aspectRatio.isLocked = false;
} }
}, },
heightChanged: (state, action: PayloadAction<{ height: number; updateAspectRatio?: boolean }>) => { heightChanged: (state, action: PayloadAction<{ height: number; updateAspectRatio?: boolean; clamp?: boolean }>) => {
const { height, updateAspectRatio } = action.payload; const { height, updateAspectRatio, clamp } = action.payload;
state.size.height = height; state.size.height = clamp ? Math.max(roundDownToMultiple(height, 8), 64) : height;
if (updateAspectRatio) { if (updateAspectRatio) {
state.size.aspectRatio.value = state.size.width / height; state.size.aspectRatio.value = state.size.width / state.size.height;
state.size.aspectRatio.id = 'Free'; state.size.aspectRatio.id = 'Free';
state.size.aspectRatio.isLocked = false; state.size.aspectRatio.isLocked = false;
} }

View File

@ -110,12 +110,14 @@ const recallInitialImage: MetadataRecallFunc<ImageDTO> = async (imageDTO) => {
getStore().dispatch(initialImageChanged(imageDTO)); getStore().dispatch(initialImageChanged(imageDTO));
}; };
const setSizeOptions = { updateAspectRatio: true, clamp: true };
const recallWidth: MetadataRecallFunc<ParameterWidth> = (width) => { const recallWidth: MetadataRecallFunc<ParameterWidth> = (width) => {
getStore().dispatch(widthChanged({ width, updateAspectRatio: true })); getStore().dispatch(widthChanged({ width, ...setSizeOptions }));
}; };
const recallHeight: MetadataRecallFunc<ParameterHeight> = (height) => { const recallHeight: MetadataRecallFunc<ParameterHeight> = (height) => {
getStore().dispatch(heightChanged({ height, updateAspectRatio: true })); getStore().dispatch(heightChanged({ height, ...setSizeOptions }));
}; };
const recallSteps: MetadataRecallFunc<ParameterSteps> = (steps) => { const recallSteps: MetadataRecallFunc<ParameterSteps> = (steps) => {