diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/setDefaultSettings.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/setDefaultSettings.ts index 6f3aa9756a..61a978d576 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/setDefaultSettings.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/setDefaultSettings.ts @@ -96,16 +96,16 @@ export const addSetDefaultSettingsListener = (startAppListening: AppStartListeni dispatch(setScheduler(scheduler)); } } - + const setSizeOptions = { updateAspectRatio: true, clamp: true }; if (width) { if (isParameterWidth(width)) { - dispatch(widthChanged({ width, updateAspectRatio: true })); + dispatch(widthChanged({ width, ...setSizeOptions })); } } if (height) { if (isParameterHeight(height)) { - dispatch(heightChanged({ height, updateAspectRatio: true })); + dispatch(heightChanged({ height, ...setSizeOptions })); } } diff --git a/invokeai/frontend/web/src/features/controlAdapters/components/ControlAdapterImagePreview.tsx b/invokeai/frontend/web/src/features/controlAdapters/components/ControlAdapterImagePreview.tsx index 56589fe613..1360c76240 100644 --- a/invokeai/frontend/web/src/features/controlAdapters/components/ControlAdapterImagePreview.tsx +++ b/invokeai/frontend/web/src/features/controlAdapters/components/ControlAdapterImagePreview.tsx @@ -96,12 +96,13 @@ const ControlAdapterImagePreview = ({ isSmall, id }: Props) => { if (activeTabName === 'unifiedCanvas') { dispatch(setBoundingBoxDimensions({ width: controlImage.width, height: controlImage.height }, optimalDimension)); } else { + const options = { updateAspectRatio: true, clamp: true }; const { width, height } = calculateNewSize( controlImage.width / controlImage.height, optimalDimension * optimalDimension ); - dispatch(widthChanged({ width, updateAspectRatio: true })); - dispatch(heightChanged({ height, updateAspectRatio: true })); + dispatch(widthChanged({ width, ...options })); + dispatch(heightChanged({ height, ...options })); } }, [controlImage, activeTabName, dispatch, optimalDimension]); diff --git a/invokeai/frontend/web/src/features/controlLayers/components/ControlAndIPAdapter/ControlAdapterImagePreview.tsx b/invokeai/frontend/web/src/features/controlLayers/components/ControlAndIPAdapter/ControlAdapterImagePreview.tsx index 675118c534..81bd7b14f2 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/ControlAndIPAdapter/ControlAdapterImagePreview.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/ControlAndIPAdapter/ControlAdapterImagePreview.tsx @@ -85,17 +85,19 @@ export const ControlAdapterImagePreview = memo( setBoundingBoxDimensions({ width: controlImage.width, height: controlImage.height }, optimalDimension) ); } else { + const options = { updateAspectRatio: true, clamp: true }; + if (shift) { const { width, height } = controlImage; - dispatch(widthChanged({ width, updateAspectRatio: true })); - dispatch(heightChanged({ height, updateAspectRatio: true })); + dispatch(widthChanged({ width, ...options })); + dispatch(heightChanged({ height, ...options })); } else { const { width, height } = calculateNewSize( controlImage.width / controlImage.height, optimalDimension * optimalDimension ); - dispatch(widthChanged({ width, updateAspectRatio: true })); - dispatch(heightChanged({ height, updateAspectRatio: true })); + dispatch(widthChanged({ width, ...options })); + dispatch(heightChanged({ height, ...options })); } } }, [controlImage, activeTabName, dispatch, optimalDimension, shift]); diff --git a/invokeai/frontend/web/src/features/controlLayers/components/ControlAndIPAdapter/IPAdapterImagePreview.tsx b/invokeai/frontend/web/src/features/controlLayers/components/ControlAndIPAdapter/IPAdapterImagePreview.tsx index 7de726cda5..f73f61cbbd 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/ControlAndIPAdapter/IPAdapterImagePreview.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/ControlAndIPAdapter/IPAdapterImagePreview.tsx @@ -51,17 +51,18 @@ export const IPAdapterImagePreview = memo( setBoundingBoxDimensions({ width: controlImage.width, height: controlImage.height }, optimalDimension) ); } else { + const options = { updateAspectRatio: true, clamp: true }; if (shift) { const { width, height } = controlImage; - dispatch(widthChanged({ width, updateAspectRatio: true })); - dispatch(heightChanged({ height, updateAspectRatio: true })); + dispatch(widthChanged({ width, ...options })); + dispatch(heightChanged({ height, ...options })); } else { const { width, height } = calculateNewSize( controlImage.width / controlImage.height, optimalDimension * optimalDimension ); - dispatch(widthChanged({ width, updateAspectRatio: true })); - dispatch(heightChanged({ height, updateAspectRatio: true })); + dispatch(widthChanged({ width, ...options })); + dispatch(heightChanged({ height, ...options })); } } }, [controlImage, activeTabName, dispatch, optimalDimension, shift]); diff --git a/invokeai/frontend/web/src/features/controlLayers/store/controlLayersSlice.ts b/invokeai/frontend/web/src/features/controlLayers/store/controlLayersSlice.ts index 0b17b6bc9c..9f36401e83 100644 --- a/invokeai/frontend/web/src/features/controlLayers/store/controlLayersSlice.ts +++ b/invokeai/frontend/web/src/features/controlLayers/store/controlLayersSlice.ts @@ -3,6 +3,7 @@ import { createSlice, isAnyOf } from '@reduxjs/toolkit'; import type { PersistConfig, RootState } from 'app/store/store'; import { moveBackward, moveForward, moveToBack, moveToFront } from 'common/util/arrayUtils'; import { deepClone } from 'common/util/deepClone'; +import { roundDownToMultiple } from 'common/util/roundDownToMultiple'; import type { CLIPVisionModelV2, ControlModeV2, @@ -626,20 +627,20 @@ export const controlLayersSlice = createSlice({ shouldConcatPromptsChanged: (state, action: PayloadAction) => { state.shouldConcatPrompts = action.payload; }, - widthChanged: (state, action: PayloadAction<{ width: number; updateAspectRatio?: boolean }>) => { - const { width, updateAspectRatio } = action.payload; - state.size.width = width; + widthChanged: (state, action: PayloadAction<{ width: number; updateAspectRatio?: boolean; clamp?: boolean }>) => { + const { width, updateAspectRatio, clamp } = action.payload; + state.size.width = clamp ? Math.max(roundDownToMultiple(width, 8), 64) : width; 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.isLocked = false; } }, - heightChanged: (state, action: PayloadAction<{ height: number; updateAspectRatio?: boolean }>) => { - const { height, updateAspectRatio } = action.payload; - state.size.height = height; + heightChanged: (state, action: PayloadAction<{ height: number; updateAspectRatio?: boolean; clamp?: boolean }>) => { + const { height, updateAspectRatio, clamp } = action.payload; + state.size.height = clamp ? Math.max(roundDownToMultiple(height, 8), 64) : height; 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.isLocked = false; } diff --git a/invokeai/frontend/web/src/features/metadata/util/recallers.ts b/invokeai/frontend/web/src/features/metadata/util/recallers.ts index a7b0cd5b2e..c04259ac62 100644 --- a/invokeai/frontend/web/src/features/metadata/util/recallers.ts +++ b/invokeai/frontend/web/src/features/metadata/util/recallers.ts @@ -110,12 +110,14 @@ const recallInitialImage: MetadataRecallFunc = async (imageDTO) => { getStore().dispatch(initialImageChanged(imageDTO)); }; +const setSizeOptions = { updateAspectRatio: true, clamp: true }; + const recallWidth: MetadataRecallFunc = (width) => { - getStore().dispatch(widthChanged({ width, updateAspectRatio: true })); + getStore().dispatch(widthChanged({ width, ...setSizeOptions })); }; const recallHeight: MetadataRecallFunc = (height) => { - getStore().dispatch(heightChanged({ height, updateAspectRatio: true })); + getStore().dispatch(heightChanged({ height, ...setSizeOptions })); }; const recallSteps: MetadataRecallFunc = (steps) => {