mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
refactor(ui): move size state to regional
This commit is contained in:
committed by
Kent Keirsey
parent
b6a45e53f1
commit
cc4bef4859
@ -8,9 +8,10 @@ import {
|
|||||||
} from 'features/controlAdapters/store/controlAdaptersSlice';
|
} from 'features/controlAdapters/store/controlAdaptersSlice';
|
||||||
import { loraRemoved } from 'features/lora/store/loraSlice';
|
import { loraRemoved } from 'features/lora/store/loraSlice';
|
||||||
import { calculateNewSize } from 'features/parameters/components/ImageSize/calculateNewSize';
|
import { calculateNewSize } from 'features/parameters/components/ImageSize/calculateNewSize';
|
||||||
import { heightChanged, modelChanged, vaeSelected, widthChanged } from 'features/parameters/store/generationSlice';
|
import { modelChanged, vaeSelected } from 'features/parameters/store/generationSlice';
|
||||||
import { zParameterModel, zParameterVAEModel } from 'features/parameters/types/parameterSchemas';
|
import { zParameterModel, zParameterVAEModel } from 'features/parameters/types/parameterSchemas';
|
||||||
import { getIsSizeOptimal, getOptimalDimension } from 'features/parameters/util/optimalDimension';
|
import { getIsSizeOptimal, getOptimalDimension } from 'features/parameters/util/optimalDimension';
|
||||||
|
import { heightChanged, widthChanged } from 'features/regionalPrompts/store/regionalPromptsSlice';
|
||||||
import { refinerModelChanged } from 'features/sdxl/store/sdxlSlice';
|
import { refinerModelChanged } from 'features/sdxl/store/sdxlSlice';
|
||||||
import { forEach } from 'lodash-es';
|
import { forEach } from 'lodash-es';
|
||||||
import type { Logger } from 'roarr';
|
import type { Logger } from 'roarr';
|
||||||
@ -69,16 +70,22 @@ const handleMainModels: ModelHandler = (models, state, dispatch, log) => {
|
|||||||
dispatch(modelChanged(defaultModelInList, currentModel));
|
dispatch(modelChanged(defaultModelInList, currentModel));
|
||||||
|
|
||||||
const optimalDimension = getOptimalDimension(defaultModelInList);
|
const optimalDimension = getOptimalDimension(defaultModelInList);
|
||||||
if (getIsSizeOptimal(state.generation.width, state.generation.height, optimalDimension)) {
|
if (
|
||||||
|
getIsSizeOptimal(
|
||||||
|
state.regionalPrompts.present.size.width,
|
||||||
|
state.regionalPrompts.present.size.height,
|
||||||
|
optimalDimension
|
||||||
|
)
|
||||||
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const { width, height } = calculateNewSize(
|
const { width, height } = calculateNewSize(
|
||||||
state.generation.aspectRatio.value,
|
state.regionalPrompts.present.size.aspectRatio.value,
|
||||||
optimalDimension * optimalDimension
|
optimalDimension * optimalDimension
|
||||||
);
|
);
|
||||||
|
|
||||||
dispatch(widthChanged(width));
|
dispatch(widthChanged({ width }));
|
||||||
dispatch(heightChanged(height));
|
dispatch(heightChanged({ height }));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,12 @@
|
|||||||
import type { AppStartListening } from 'app/store/middleware/listenerMiddleware';
|
import type { AppStartListening } from 'app/store/middleware/listenerMiddleware';
|
||||||
import { setDefaultSettings } from 'features/parameters/store/actions';
|
import { setDefaultSettings } from 'features/parameters/store/actions';
|
||||||
import {
|
import {
|
||||||
heightRecalled,
|
|
||||||
setCfgRescaleMultiplier,
|
setCfgRescaleMultiplier,
|
||||||
setCfgScale,
|
setCfgScale,
|
||||||
setScheduler,
|
setScheduler,
|
||||||
setSteps,
|
setSteps,
|
||||||
vaePrecisionChanged,
|
vaePrecisionChanged,
|
||||||
vaeSelected,
|
vaeSelected,
|
||||||
widthRecalled,
|
|
||||||
} from 'features/parameters/store/generationSlice';
|
} from 'features/parameters/store/generationSlice';
|
||||||
import {
|
import {
|
||||||
isParameterCFGRescaleMultiplier,
|
isParameterCFGRescaleMultiplier,
|
||||||
@ -20,6 +18,7 @@ import {
|
|||||||
isParameterWidth,
|
isParameterWidth,
|
||||||
zParameterVAEModel,
|
zParameterVAEModel,
|
||||||
} from 'features/parameters/types/parameterSchemas';
|
} from 'features/parameters/types/parameterSchemas';
|
||||||
|
import { heightChanged, widthChanged } from 'features/regionalPrompts/store/regionalPromptsSlice';
|
||||||
import { addToast } from 'features/system/store/systemSlice';
|
import { addToast } from 'features/system/store/systemSlice';
|
||||||
import { makeToast } from 'features/system/util/makeToast';
|
import { makeToast } from 'features/system/util/makeToast';
|
||||||
import { t } from 'i18next';
|
import { t } from 'i18next';
|
||||||
@ -100,13 +99,13 @@ export const addSetDefaultSettingsListener = (startAppListening: AppStartListeni
|
|||||||
|
|
||||||
if (width) {
|
if (width) {
|
||||||
if (isParameterWidth(width)) {
|
if (isParameterWidth(width)) {
|
||||||
dispatch(widthRecalled(width));
|
dispatch(widthChanged({ width, updateAspectRatio: true }));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (height) {
|
if (height) {
|
||||||
if (isParameterHeight(height)) {
|
if (isParameterHeight(height)) {
|
||||||
dispatch(heightRecalled(height));
|
dispatch(heightChanged({ height, updateAspectRatio: true }));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,7 +15,8 @@ import {
|
|||||||
} from 'features/controlAdapters/store/controlAdaptersSlice';
|
} from 'features/controlAdapters/store/controlAdaptersSlice';
|
||||||
import type { TypesafeDraggableData, TypesafeDroppableData } from 'features/dnd/types';
|
import type { TypesafeDraggableData, TypesafeDroppableData } from 'features/dnd/types';
|
||||||
import { calculateNewSize } from 'features/parameters/components/ImageSize/calculateNewSize';
|
import { calculateNewSize } from 'features/parameters/components/ImageSize/calculateNewSize';
|
||||||
import { heightChanged, selectOptimalDimension, widthChanged } from 'features/parameters/store/generationSlice';
|
import { selectOptimalDimension } from 'features/parameters/store/generationSlice';
|
||||||
|
import { heightChanged, widthChanged } from 'features/regionalPrompts/store/regionalPromptsSlice';
|
||||||
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
||||||
import { memo, useCallback, useEffect, useMemo, useState } from 'react';
|
import { memo, useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
@ -99,8 +100,8 @@ const ControlAdapterImagePreview = ({ isSmall, id }: Props) => {
|
|||||||
controlImage.width / controlImage.height,
|
controlImage.width / controlImage.height,
|
||||||
optimalDimension * optimalDimension
|
optimalDimension * optimalDimension
|
||||||
);
|
);
|
||||||
dispatch(widthChanged(width));
|
dispatch(widthChanged({ width, updateAspectRatio: true }));
|
||||||
dispatch(heightChanged(height));
|
dispatch(heightChanged({ height, updateAspectRatio: true }));
|
||||||
}
|
}
|
||||||
}, [controlImage, activeTabName, dispatch, optimalDimension]);
|
}, [controlImage, activeTabName, dispatch, optimalDimension]);
|
||||||
|
|
||||||
|
@ -16,7 +16,6 @@ import type {
|
|||||||
} from 'features/metadata/types';
|
} from 'features/metadata/types';
|
||||||
import { modelSelected } from 'features/parameters/store/actions';
|
import { modelSelected } from 'features/parameters/store/actions';
|
||||||
import {
|
import {
|
||||||
heightRecalled,
|
|
||||||
initialImageChanged,
|
initialImageChanged,
|
||||||
setCfgRescaleMultiplier,
|
setCfgRescaleMultiplier,
|
||||||
setCfgScale,
|
setCfgScale,
|
||||||
@ -25,7 +24,6 @@ import {
|
|||||||
setSeed,
|
setSeed,
|
||||||
setSteps,
|
setSteps,
|
||||||
vaeSelected,
|
vaeSelected,
|
||||||
widthRecalled,
|
|
||||||
} from 'features/parameters/store/generationSlice';
|
} from 'features/parameters/store/generationSlice';
|
||||||
import type {
|
import type {
|
||||||
ParameterCFGRescaleMultiplier,
|
ParameterCFGRescaleMultiplier,
|
||||||
@ -50,10 +48,12 @@ import type {
|
|||||||
ParameterWidth,
|
ParameterWidth,
|
||||||
} from 'features/parameters/types/parameterSchemas';
|
} from 'features/parameters/types/parameterSchemas';
|
||||||
import {
|
import {
|
||||||
|
heightChanged,
|
||||||
negativePrompt2Changed,
|
negativePrompt2Changed,
|
||||||
negativePromptChanged,
|
negativePromptChanged,
|
||||||
positivePrompt2Changed,
|
positivePrompt2Changed,
|
||||||
positivePromptChanged,
|
positivePromptChanged,
|
||||||
|
widthChanged,
|
||||||
} from 'features/regionalPrompts/store/regionalPromptsSlice';
|
} from 'features/regionalPrompts/store/regionalPromptsSlice';
|
||||||
import {
|
import {
|
||||||
refinerModelChanged,
|
refinerModelChanged,
|
||||||
@ -103,11 +103,11 @@ const recallInitialImage: MetadataRecallFunc<ImageDTO> = async (imageDTO) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const recallWidth: MetadataRecallFunc<ParameterWidth> = (width) => {
|
const recallWidth: MetadataRecallFunc<ParameterWidth> = (width) => {
|
||||||
getStore().dispatch(widthRecalled(width));
|
getStore().dispatch(widthChanged({ width, updateAspectRatio: true }));
|
||||||
};
|
};
|
||||||
|
|
||||||
const recallHeight: MetadataRecallFunc<ParameterHeight> = (height) => {
|
const recallHeight: MetadataRecallFunc<ParameterHeight> = (height) => {
|
||||||
getStore().dispatch(heightRecalled(height));
|
getStore().dispatch(heightChanged({ height, updateAspectRatio: true }));
|
||||||
};
|
};
|
||||||
|
|
||||||
const recallSteps: MetadataRecallFunc<ParameterSteps> = (steps) => {
|
const recallSteps: MetadataRecallFunc<ParameterSteps> = (steps) => {
|
||||||
|
@ -110,10 +110,9 @@ export const addHrfToGraph = (state: RootState, graph: NonNullableGraph): void =
|
|||||||
|
|
||||||
const { vae, seamlessXAxis, seamlessYAxis } = state.generation;
|
const { vae, seamlessXAxis, seamlessYAxis } = state.generation;
|
||||||
const { hrfStrength, hrfEnabled, hrfMethod } = state.hrf;
|
const { hrfStrength, hrfEnabled, hrfMethod } = state.hrf;
|
||||||
|
const { width, height } = state.regionalPrompts.present.size;
|
||||||
const isAutoVae = !vae;
|
const isAutoVae = !vae;
|
||||||
const isSeamlessEnabled = seamlessXAxis || seamlessYAxis;
|
const isSeamlessEnabled = seamlessXAxis || seamlessYAxis;
|
||||||
const width = state.generation.width;
|
|
||||||
const height = state.generation.height;
|
|
||||||
const optimalDimension = selectOptimalDimension(state);
|
const optimalDimension = selectOptimalDimension(state);
|
||||||
const { newWidth: hrfWidth, newHeight: hrfHeight } = calculateHrfRes(optimalDimension, width, height);
|
const { newWidth: hrfWidth, newHeight: hrfHeight } = calculateHrfRes(optimalDimension, width, height);
|
||||||
|
|
||||||
|
@ -47,8 +47,6 @@ export const buildLinearImageToImageGraph = async (state: RootState): Promise<No
|
|||||||
initialImage,
|
initialImage,
|
||||||
img2imgStrength: strength,
|
img2imgStrength: strength,
|
||||||
shouldFitToWidthHeight,
|
shouldFitToWidthHeight,
|
||||||
width,
|
|
||||||
height,
|
|
||||||
clipSkip,
|
clipSkip,
|
||||||
shouldUseCpuNoise,
|
shouldUseCpuNoise,
|
||||||
vaePrecision,
|
vaePrecision,
|
||||||
@ -56,6 +54,7 @@ export const buildLinearImageToImageGraph = async (state: RootState): Promise<No
|
|||||||
seamlessYAxis,
|
seamlessYAxis,
|
||||||
} = state.generation;
|
} = state.generation;
|
||||||
const { positivePrompt, negativePrompt } = state.regionalPrompts.present.baseLayer;
|
const { positivePrompt, negativePrompt } = state.regionalPrompts.present.baseLayer;
|
||||||
|
const { width, height } = state.regionalPrompts.present.size;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The easiest way to build linear graphs is to do it in the node editor, then copy and paste the
|
* The easiest way to build linear graphs is to do it in the node editor, then copy and paste the
|
||||||
|
@ -47,8 +47,6 @@ export const buildLinearSDXLImageToImageGraph = async (state: RootState): Promis
|
|||||||
steps,
|
steps,
|
||||||
initialImage,
|
initialImage,
|
||||||
shouldFitToWidthHeight,
|
shouldFitToWidthHeight,
|
||||||
width,
|
|
||||||
height,
|
|
||||||
shouldUseCpuNoise,
|
shouldUseCpuNoise,
|
||||||
vaePrecision,
|
vaePrecision,
|
||||||
seamlessXAxis,
|
seamlessXAxis,
|
||||||
@ -56,6 +54,7 @@ export const buildLinearSDXLImageToImageGraph = async (state: RootState): Promis
|
|||||||
img2imgStrength: strength,
|
img2imgStrength: strength,
|
||||||
} = state.generation;
|
} = state.generation;
|
||||||
const { positivePrompt, negativePrompt } = state.regionalPrompts.present.baseLayer;
|
const { positivePrompt, negativePrompt } = state.regionalPrompts.present.baseLayer;
|
||||||
|
const { width, height } = state.regionalPrompts.present.size;
|
||||||
|
|
||||||
const { refinerModel, refinerStart } = state.sdxl;
|
const { refinerModel, refinerStart } = state.sdxl;
|
||||||
|
|
||||||
|
@ -36,14 +36,13 @@ export const buildLinearSDXLTextToImageGraph = async (state: RootState): Promise
|
|||||||
scheduler,
|
scheduler,
|
||||||
seed,
|
seed,
|
||||||
steps,
|
steps,
|
||||||
width,
|
|
||||||
height,
|
|
||||||
shouldUseCpuNoise,
|
shouldUseCpuNoise,
|
||||||
vaePrecision,
|
vaePrecision,
|
||||||
seamlessXAxis,
|
seamlessXAxis,
|
||||||
seamlessYAxis,
|
seamlessYAxis,
|
||||||
} = state.generation;
|
} = state.generation;
|
||||||
const { positivePrompt, negativePrompt } = state.regionalPrompts.present.baseLayer;
|
const { positivePrompt, negativePrompt } = state.regionalPrompts.present.baseLayer;
|
||||||
|
const { width, height } = state.regionalPrompts.present.size;
|
||||||
|
|
||||||
const { refinerModel, refinerStart } = state.sdxl;
|
const { refinerModel, refinerStart } = state.sdxl;
|
||||||
|
|
||||||
|
@ -35,8 +35,6 @@ export const buildLinearTextToImageGraph = async (state: RootState): Promise<Non
|
|||||||
cfgRescaleMultiplier: cfg_rescale_multiplier,
|
cfgRescaleMultiplier: cfg_rescale_multiplier,
|
||||||
scheduler,
|
scheduler,
|
||||||
steps,
|
steps,
|
||||||
width,
|
|
||||||
height,
|
|
||||||
clipSkip,
|
clipSkip,
|
||||||
shouldUseCpuNoise,
|
shouldUseCpuNoise,
|
||||||
vaePrecision,
|
vaePrecision,
|
||||||
@ -45,6 +43,7 @@ export const buildLinearTextToImageGraph = async (state: RootState): Promise<Non
|
|||||||
seed,
|
seed,
|
||||||
} = state.generation;
|
} = state.generation;
|
||||||
const { positivePrompt, negativePrompt } = state.regionalPrompts.present.baseLayer;
|
const { positivePrompt, negativePrompt } = state.regionalPrompts.present.baseLayer;
|
||||||
|
const { width, height } = state.regionalPrompts.present.size;
|
||||||
|
|
||||||
const use_cpu = shouldUseCpuNoise;
|
const use_cpu = shouldUseCpuNoise;
|
||||||
|
|
||||||
|
@ -1,11 +1,7 @@
|
|||||||
import type { PayloadAction } from '@reduxjs/toolkit';
|
import type { PayloadAction } from '@reduxjs/toolkit';
|
||||||
import { createSlice } from '@reduxjs/toolkit';
|
import { createSlice } from '@reduxjs/toolkit';
|
||||||
import type { PersistConfig, RootState } from 'app/store/store';
|
import type { PersistConfig, RootState } from 'app/store/store';
|
||||||
import { roundToMultiple } from 'common/util/roundDownToMultiple';
|
|
||||||
import { isAnyControlAdapterAdded } from 'features/controlAdapters/store/controlAdaptersSlice';
|
|
||||||
import { calculateNewSize } from 'features/parameters/components/ImageSize/calculateNewSize';
|
|
||||||
import { initialAspectRatioState } from 'features/parameters/components/ImageSize/constants';
|
import { initialAspectRatioState } from 'features/parameters/components/ImageSize/constants';
|
||||||
import type { AspectRatioState } from 'features/parameters/components/ImageSize/types';
|
|
||||||
import { CLIP_SKIP_MAP } from 'features/parameters/types/constants';
|
import { CLIP_SKIP_MAP } from 'features/parameters/types/constants';
|
||||||
import type {
|
import type {
|
||||||
ParameterCanvasCoherenceMode,
|
ParameterCanvasCoherenceMode,
|
||||||
@ -16,7 +12,7 @@ import type {
|
|||||||
ParameterScheduler,
|
ParameterScheduler,
|
||||||
ParameterVAEModel,
|
ParameterVAEModel,
|
||||||
} from 'features/parameters/types/parameterSchemas';
|
} from 'features/parameters/types/parameterSchemas';
|
||||||
import { getIsSizeOptimal, getOptimalDimension } from 'features/parameters/util/optimalDimension';
|
import { getOptimalDimension } from 'features/parameters/util/optimalDimension';
|
||||||
import { configChanged } from 'features/system/store/configSlice';
|
import { configChanged } from 'features/system/store/configSlice';
|
||||||
import { clamp } from 'lodash-es';
|
import { clamp } from 'lodash-es';
|
||||||
import type { RgbaColor } from 'react-colorful';
|
import type { RgbaColor } from 'react-colorful';
|
||||||
@ -28,7 +24,6 @@ const initialGenerationState: GenerationState = {
|
|||||||
_version: 2,
|
_version: 2,
|
||||||
cfgScale: 7.5,
|
cfgScale: 7.5,
|
||||||
cfgRescaleMultiplier: 0,
|
cfgRescaleMultiplier: 0,
|
||||||
height: 512,
|
|
||||||
img2imgStrength: 0.75,
|
img2imgStrength: 0.75,
|
||||||
infillMethod: 'patchmatch',
|
infillMethod: 'patchmatch',
|
||||||
iterations: 1,
|
iterations: 1,
|
||||||
@ -42,7 +37,6 @@ const initialGenerationState: GenerationState = {
|
|||||||
shouldFitToWidthHeight: true,
|
shouldFitToWidthHeight: true,
|
||||||
shouldRandomizeSeed: true,
|
shouldRandomizeSeed: true,
|
||||||
steps: 50,
|
steps: 50,
|
||||||
width: 512,
|
|
||||||
model: null,
|
model: null,
|
||||||
vae: null,
|
vae: null,
|
||||||
vaePrecision: 'fp32',
|
vaePrecision: 'fp32',
|
||||||
@ -51,7 +45,6 @@ const initialGenerationState: GenerationState = {
|
|||||||
clipSkip: 0,
|
clipSkip: 0,
|
||||||
shouldUseCpuNoise: true,
|
shouldUseCpuNoise: true,
|
||||||
shouldShowAdvancedOptions: false,
|
shouldShowAdvancedOptions: false,
|
||||||
aspectRatio: { ...initialAspectRatioState },
|
|
||||||
infillTileSize: 32,
|
infillTileSize: 32,
|
||||||
infillPatchmatchDownscaleSize: 1,
|
infillPatchmatchDownscaleSize: 1,
|
||||||
infillMosaicTileWidth: 64,
|
infillMosaicTileWidth: 64,
|
||||||
@ -140,19 +133,6 @@ export const generationSlice = createSlice({
|
|||||||
const { maxClip } = CLIP_SKIP_MAP[newModel.base];
|
const { maxClip } = CLIP_SKIP_MAP[newModel.base];
|
||||||
state.clipSkip = clamp(state.clipSkip, 0, maxClip);
|
state.clipSkip = clamp(state.clipSkip, 0, maxClip);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (action.meta.previousModel?.base === newModel.base) {
|
|
||||||
// The base model hasn't changed, we don't need to optimize the size
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const optimalDimension = getOptimalDimension(newModel);
|
|
||||||
if (getIsSizeOptimal(state.width, state.height, optimalDimension)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const { width, height } = calculateNewSize(state.aspectRatio.value, optimalDimension * optimalDimension);
|
|
||||||
state.width = width;
|
|
||||||
state.height = height;
|
|
||||||
},
|
},
|
||||||
prepare: (payload: ParameterModel | null, previousModel?: ParameterModel | null) => ({
|
prepare: (payload: ParameterModel | null, previousModel?: ParameterModel | null) => ({
|
||||||
payload,
|
payload,
|
||||||
@ -174,27 +154,6 @@ export const generationSlice = createSlice({
|
|||||||
shouldUseCpuNoiseChanged: (state, action: PayloadAction<boolean>) => {
|
shouldUseCpuNoiseChanged: (state, action: PayloadAction<boolean>) => {
|
||||||
state.shouldUseCpuNoise = action.payload;
|
state.shouldUseCpuNoise = action.payload;
|
||||||
},
|
},
|
||||||
widthChanged: (state, action: PayloadAction<number>) => {
|
|
||||||
state.width = action.payload;
|
|
||||||
},
|
|
||||||
heightChanged: (state, action: PayloadAction<number>) => {
|
|
||||||
state.height = action.payload;
|
|
||||||
},
|
|
||||||
widthRecalled: (state, action: PayloadAction<number>) => {
|
|
||||||
state.width = action.payload;
|
|
||||||
state.aspectRatio.value = action.payload / state.height;
|
|
||||||
state.aspectRatio.id = 'Free';
|
|
||||||
state.aspectRatio.isLocked = false;
|
|
||||||
},
|
|
||||||
heightRecalled: (state, action: PayloadAction<number>) => {
|
|
||||||
state.height = action.payload;
|
|
||||||
state.aspectRatio.value = state.width / action.payload;
|
|
||||||
state.aspectRatio.id = 'Free';
|
|
||||||
state.aspectRatio.isLocked = false;
|
|
||||||
},
|
|
||||||
aspectRatioChanged: (state, action: PayloadAction<AspectRatioState>) => {
|
|
||||||
state.aspectRatio = action.payload;
|
|
||||||
},
|
|
||||||
setInfillMethod: (state, action: PayloadAction<string>) => {
|
setInfillMethod: (state, action: PayloadAction<string>) => {
|
||||||
state.infillMethod = action.payload;
|
state.infillMethod = action.payload;
|
||||||
},
|
},
|
||||||
@ -229,15 +188,6 @@ export const generationSlice = createSlice({
|
|||||||
state.vaePrecision = action.payload.sd.vaePrecision;
|
state.vaePrecision = action.payload.sd.vaePrecision;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO: This is a temp fix to reduce issues with T2I adapter having a different downscaling
|
|
||||||
// factor than the UNet. Hopefully we get an upstream fix in diffusers.
|
|
||||||
builder.addMatcher(isAnyControlAdapterAdded, (state, action) => {
|
|
||||||
if (action.payload.type === 't2i_adapter') {
|
|
||||||
state.width = roundToMultiple(state.width, 64);
|
|
||||||
state.height = roundToMultiple(state.height, 64);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
selectors: {
|
selectors: {
|
||||||
selectOptimalDimension: (slice) => getOptimalDimension(slice.model),
|
selectOptimalDimension: (slice) => getOptimalDimension(slice.model),
|
||||||
@ -268,11 +218,6 @@ export const {
|
|||||||
setClipSkip,
|
setClipSkip,
|
||||||
shouldUseCpuNoiseChanged,
|
shouldUseCpuNoiseChanged,
|
||||||
vaePrecisionChanged,
|
vaePrecisionChanged,
|
||||||
aspectRatioChanged,
|
|
||||||
widthChanged,
|
|
||||||
heightChanged,
|
|
||||||
widthRecalled,
|
|
||||||
heightRecalled,
|
|
||||||
setInfillTileSize,
|
setInfillTileSize,
|
||||||
setInfillPatchmatchDownscaleSize,
|
setInfillPatchmatchDownscaleSize,
|
||||||
setInfillMosaicTileWidth,
|
setInfillMosaicTileWidth,
|
||||||
|
@ -1,10 +1,8 @@
|
|||||||
import type { PayloadAction } from '@reduxjs/toolkit';
|
import type { PayloadAction } from '@reduxjs/toolkit';
|
||||||
import type { AspectRatioState } from 'features/parameters/components/ImageSize/types';
|
|
||||||
import type {
|
import type {
|
||||||
ParameterCanvasCoherenceMode,
|
ParameterCanvasCoherenceMode,
|
||||||
ParameterCFGRescaleMultiplier,
|
ParameterCFGRescaleMultiplier,
|
||||||
ParameterCFGScale,
|
ParameterCFGScale,
|
||||||
ParameterHeight,
|
|
||||||
ParameterMaskBlurMethod,
|
ParameterMaskBlurMethod,
|
||||||
ParameterModel,
|
ParameterModel,
|
||||||
ParameterPrecision,
|
ParameterPrecision,
|
||||||
@ -13,7 +11,6 @@ import type {
|
|||||||
ParameterSteps,
|
ParameterSteps,
|
||||||
ParameterStrength,
|
ParameterStrength,
|
||||||
ParameterVAEModel,
|
ParameterVAEModel,
|
||||||
ParameterWidth,
|
|
||||||
} from 'features/parameters/types/parameterSchemas';
|
} from 'features/parameters/types/parameterSchemas';
|
||||||
import type { RgbaColor } from 'react-colorful';
|
import type { RgbaColor } from 'react-colorful';
|
||||||
|
|
||||||
@ -21,7 +18,6 @@ export interface GenerationState {
|
|||||||
_version: 2;
|
_version: 2;
|
||||||
cfgScale: ParameterCFGScale;
|
cfgScale: ParameterCFGScale;
|
||||||
cfgRescaleMultiplier: ParameterCFGRescaleMultiplier;
|
cfgRescaleMultiplier: ParameterCFGRescaleMultiplier;
|
||||||
height: ParameterHeight;
|
|
||||||
img2imgStrength: ParameterStrength;
|
img2imgStrength: ParameterStrength;
|
||||||
infillMethod: string;
|
infillMethod: string;
|
||||||
initialImage?: { imageName: string; width: number; height: number };
|
initialImage?: { imageName: string; width: number; height: number };
|
||||||
@ -36,7 +32,6 @@ export interface GenerationState {
|
|||||||
shouldFitToWidthHeight: boolean;
|
shouldFitToWidthHeight: boolean;
|
||||||
shouldRandomizeSeed: boolean;
|
shouldRandomizeSeed: boolean;
|
||||||
steps: ParameterSteps;
|
steps: ParameterSteps;
|
||||||
width: ParameterWidth;
|
|
||||||
model: ParameterModel | null;
|
model: ParameterModel | null;
|
||||||
vae: ParameterVAEModel | null;
|
vae: ParameterVAEModel | null;
|
||||||
vaePrecision: ParameterPrecision;
|
vaePrecision: ParameterPrecision;
|
||||||
@ -45,7 +40,6 @@ export interface GenerationState {
|
|||||||
clipSkip: number;
|
clipSkip: number;
|
||||||
shouldUseCpuNoise: boolean;
|
shouldUseCpuNoise: boolean;
|
||||||
shouldShowAdvancedOptions: boolean;
|
shouldShowAdvancedOptions: boolean;
|
||||||
aspectRatio: AspectRatioState;
|
|
||||||
infillTileSize: number;
|
infillTileSize: number;
|
||||||
infillPatchmatchDownscaleSize: number;
|
infillPatchmatchDownscaleSize: number;
|
||||||
infillMosaicTileWidth: number;
|
infillMosaicTileWidth: number;
|
||||||
|
@ -43,8 +43,6 @@ const useStageRenderer = (
|
|||||||
asPreview: boolean
|
asPreview: boolean
|
||||||
) => {
|
) => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const width = useAppSelector((s) => s.generation.width);
|
|
||||||
const height = useAppSelector((s) => s.generation.height);
|
|
||||||
const state = useAppSelector((s) => s.regionalPrompts.present);
|
const state = useAppSelector((s) => s.regionalPrompts.present);
|
||||||
const tool = useStore($tool);
|
const tool = useStore($tool);
|
||||||
const { onMouseDown, onMouseUp, onMouseMove, onMouseEnter, onMouseLeave, onMouseWheel } = useMouseEvents();
|
const { onMouseDown, onMouseUp, onMouseMove, onMouseEnter, onMouseLeave, onMouseWheel } = useMouseEvents();
|
||||||
@ -121,11 +119,11 @@ const useStageRenderer = (
|
|||||||
const stage = stageRef.current;
|
const stage = stageRef.current;
|
||||||
|
|
||||||
const fitStageToContainer = () => {
|
const fitStageToContainer = () => {
|
||||||
const newXScale = wrapper.offsetWidth / width;
|
const newXScale = wrapper.offsetWidth / state.size.width;
|
||||||
const newYScale = wrapper.offsetHeight / height;
|
const newYScale = wrapper.offsetHeight / state.size.height;
|
||||||
const newScale = Math.min(newXScale, newYScale, 1);
|
const newScale = Math.min(newXScale, newYScale, 1);
|
||||||
stage.width(width * newScale);
|
stage.width(state.size.width * newScale);
|
||||||
stage.height(height * newScale);
|
stage.height(state.size.height * newScale);
|
||||||
stage.scaleX(newScale);
|
stage.scaleX(newScale);
|
||||||
stage.scaleY(newScale);
|
stage.scaleY(newScale);
|
||||||
};
|
};
|
||||||
@ -137,7 +135,7 @@ const useStageRenderer = (
|
|||||||
return () => {
|
return () => {
|
||||||
resizeObserver.disconnect();
|
resizeObserver.disconnect();
|
||||||
};
|
};
|
||||||
}, [stageRef, width, height, wrapper]);
|
}, [stageRef, state.size.width, state.size.height, wrapper]);
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
log.trace('Rendering tool preview');
|
log.trace('Rendering tool preview');
|
||||||
@ -188,8 +186,8 @@ const useStageRenderer = (
|
|||||||
// The preview should not have a background
|
// The preview should not have a background
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
renderers.renderBackground(stageRef.current, width, height);
|
renderers.renderBackground(stageRef.current, state.size.width, state.size.height);
|
||||||
}, [stageRef, asPreview, width, height, renderers]);
|
}, [stageRef, asPreview, state.size.width, state.size.height, renderers]);
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
log.trace('Arranging layers');
|
log.trace('Arranging layers');
|
||||||
|
@ -2,15 +2,24 @@ import type { PayloadAction, UnknownAction } from '@reduxjs/toolkit';
|
|||||||
import { createSlice, isAnyOf } from '@reduxjs/toolkit';
|
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 { controlAdapterRemoved } from 'features/controlAdapters/store/controlAdaptersSlice';
|
import { deepClone } from 'common/util/deepClone';
|
||||||
|
import { roundToMultiple } from 'common/util/roundDownToMultiple';
|
||||||
|
import { controlAdapterRemoved, isAnyControlAdapterAdded } from 'features/controlAdapters/store/controlAdaptersSlice';
|
||||||
import type { ControlAdapterConfig } from 'features/controlAdapters/store/types';
|
import type { ControlAdapterConfig } from 'features/controlAdapters/store/types';
|
||||||
|
import { calculateNewSize } from 'features/parameters/components/ImageSize/calculateNewSize';
|
||||||
|
import { initialAspectRatioState } from 'features/parameters/components/ImageSize/constants';
|
||||||
|
import type { AspectRatioState } from 'features/parameters/components/ImageSize/types';
|
||||||
|
import { modelChanged } from 'features/parameters/store/generationSlice';
|
||||||
import type {
|
import type {
|
||||||
ParameterAutoNegative,
|
ParameterAutoNegative,
|
||||||
|
ParameterHeight,
|
||||||
ParameterNegativePrompt,
|
ParameterNegativePrompt,
|
||||||
ParameterNegativeStylePromptSDXL,
|
ParameterNegativeStylePromptSDXL,
|
||||||
ParameterPositivePrompt,
|
ParameterPositivePrompt,
|
||||||
ParameterPositiveStylePromptSDXL,
|
ParameterPositiveStylePromptSDXL,
|
||||||
|
ParameterWidth,
|
||||||
} from 'features/parameters/types/parameterSchemas';
|
} from 'features/parameters/types/parameterSchemas';
|
||||||
|
import { getIsSizeOptimal, getOptimalDimension } from 'features/parameters/util/optimalDimension';
|
||||||
import type { IRect, Vector2d } from 'konva/lib/types';
|
import type { IRect, Vector2d } from 'konva/lib/types';
|
||||||
import { isEqual } from 'lodash-es';
|
import { isEqual } from 'lodash-es';
|
||||||
import { atom } from 'nanostores';
|
import { atom } from 'nanostores';
|
||||||
@ -86,6 +95,11 @@ type RegionalPromptsState = {
|
|||||||
globalMaskLayerOpacity: number;
|
globalMaskLayerOpacity: number;
|
||||||
isEnabled: boolean;
|
isEnabled: boolean;
|
||||||
baseLayer: BaseLayerState;
|
baseLayer: BaseLayerState;
|
||||||
|
size: {
|
||||||
|
width: ParameterWidth;
|
||||||
|
height: ParameterHeight;
|
||||||
|
aspectRatio: AspectRatioState;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const initialRegionalPromptsState: RegionalPromptsState = {
|
export const initialRegionalPromptsState: RegionalPromptsState = {
|
||||||
@ -102,6 +116,11 @@ export const initialRegionalPromptsState: RegionalPromptsState = {
|
|||||||
negativePrompt2: '',
|
negativePrompt2: '',
|
||||||
shouldConcatPrompts: true,
|
shouldConcatPrompts: true,
|
||||||
},
|
},
|
||||||
|
size: {
|
||||||
|
width: 512,
|
||||||
|
height: 512,
|
||||||
|
aspectRatio: deepClone(initialAspectRatioState),
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const isLine = (obj: VectorMaskLine | VectorMaskRect): obj is VectorMaskLine => obj.type === 'vector_mask_line';
|
const isLine = (obj: VectorMaskLine | VectorMaskRect): obj is VectorMaskLine => obj.type === 'vector_mask_line';
|
||||||
@ -364,6 +383,27 @@ export const regionalPromptsSlice = createSlice({
|
|||||||
shouldConcatPromptsChanged: (state, action: PayloadAction<boolean>) => {
|
shouldConcatPromptsChanged: (state, action: PayloadAction<boolean>) => {
|
||||||
state.baseLayer.shouldConcatPrompts = action.payload;
|
state.baseLayer.shouldConcatPrompts = action.payload;
|
||||||
},
|
},
|
||||||
|
widthChanged: (state, action: PayloadAction<{ width: number; updateAspectRatio?: boolean }>) => {
|
||||||
|
const { width, updateAspectRatio } = action.payload;
|
||||||
|
state.size.width = width;
|
||||||
|
if (updateAspectRatio) {
|
||||||
|
state.size.aspectRatio.value = 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;
|
||||||
|
if (updateAspectRatio) {
|
||||||
|
state.size.aspectRatio.value = state.size.width / height;
|
||||||
|
state.size.aspectRatio.id = 'Free';
|
||||||
|
state.size.aspectRatio.isLocked = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
aspectRatioChanged: (state, action: PayloadAction<AspectRatioState>) => {
|
||||||
|
state.size.aspectRatio = action.payload;
|
||||||
|
},
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
//#region General
|
//#region General
|
||||||
@ -396,6 +436,30 @@ export const regionalPromptsSlice = createSlice({
|
|||||||
layer.ipAdapterIds = layer.ipAdapterIds.filter((id) => id !== action.payload.id);
|
layer.ipAdapterIds = layer.ipAdapterIds.filter((id) => id !== action.payload.id);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
builder.addCase(modelChanged, (state, action) => {
|
||||||
|
const newModel = action.payload;
|
||||||
|
if (!newModel || action.meta.previousModel?.base === newModel.base) {
|
||||||
|
// Model was cleared or the base didn't change
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const optimalDimension = getOptimalDimension(newModel);
|
||||||
|
if (getIsSizeOptimal(state.size.width, state.size.height, optimalDimension)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const { width, height } = calculateNewSize(state.size.aspectRatio.value, optimalDimension * optimalDimension);
|
||||||
|
state.size.width = width;
|
||||||
|
state.size.height = height;
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO: This is a temp fix to reduce issues with T2I adapter having a different downscaling
|
||||||
|
// factor than the UNet. Hopefully we get an upstream fix in diffusers.
|
||||||
|
builder.addMatcher(isAnyControlAdapterAdded, (state, action) => {
|
||||||
|
if (action.payload.type === 't2i_adapter') {
|
||||||
|
state.size.width = roundToMultiple(state.size.width, 64);
|
||||||
|
state.size.height = roundToMultiple(state.size.height, 64);
|
||||||
|
}
|
||||||
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -461,6 +525,9 @@ export const {
|
|||||||
positivePrompt2Changed,
|
positivePrompt2Changed,
|
||||||
negativePrompt2Changed,
|
negativePrompt2Changed,
|
||||||
shouldConcatPromptsChanged,
|
shouldConcatPromptsChanged,
|
||||||
|
widthChanged,
|
||||||
|
heightChanged,
|
||||||
|
aspectRatioChanged,
|
||||||
// General actions
|
// General actions
|
||||||
brushSizeChanged,
|
brushSizeChanged,
|
||||||
globalMaskLayerOpacityChanged,
|
globalMaskLayerOpacityChanged,
|
||||||
|
@ -17,9 +17,11 @@ export const getRegionalPromptLayerBlobs = async (
|
|||||||
preview: boolean = false
|
preview: boolean = false
|
||||||
): Promise<Record<string, Blob>> => {
|
): Promise<Record<string, Blob>> => {
|
||||||
const state = getStore().getState();
|
const state = getStore().getState();
|
||||||
const reduxLayers = state.regionalPrompts.present.layers.filter(isVectorMaskLayer);
|
const { layers } = state.regionalPrompts.present;
|
||||||
|
const { width, height } = state.regionalPrompts.present.size;
|
||||||
|
const reduxLayers = layers.filter(isVectorMaskLayer);
|
||||||
const container = document.createElement('div');
|
const container = document.createElement('div');
|
||||||
const stage = new Konva.Stage({ container, width: state.generation.width, height: state.generation.height });
|
const stage = new Konva.Stage({ container, width, height });
|
||||||
renderers.renderLayers(stage, reduxLayers, 1, 'brush');
|
renderers.renderLayers(stage, reduxLayers, 1, 'brush');
|
||||||
|
|
||||||
const konvaLayers = stage.find<Konva.Layer>(`.${VECTOR_MASK_LAYER_NAME}`);
|
const konvaLayers = stage.find<Konva.Layer>(`.${VECTOR_MASK_LAYER_NAME}`);
|
||||||
|
@ -14,6 +14,7 @@ import { ParamSeedNumberInput } from 'features/parameters/components/Seed/ParamS
|
|||||||
import { ParamSeedRandomize } from 'features/parameters/components/Seed/ParamSeedRandomize';
|
import { ParamSeedRandomize } from 'features/parameters/components/Seed/ParamSeedRandomize';
|
||||||
import { ParamSeedShuffle } from 'features/parameters/components/Seed/ParamSeedShuffle';
|
import { ParamSeedShuffle } from 'features/parameters/components/Seed/ParamSeedShuffle';
|
||||||
import { selectGenerationSlice } from 'features/parameters/store/generationSlice';
|
import { selectGenerationSlice } from 'features/parameters/store/generationSlice';
|
||||||
|
import { selectRegionalPromptsSlice } from 'features/regionalPrompts/store/regionalPromptsSlice';
|
||||||
import { useExpanderToggle } from 'features/settingsAccordions/hooks/useExpanderToggle';
|
import { useExpanderToggle } from 'features/settingsAccordions/hooks/useExpanderToggle';
|
||||||
import { useStandaloneAccordionToggle } from 'features/settingsAccordions/hooks/useStandaloneAccordionToggle';
|
import { useStandaloneAccordionToggle } from 'features/settingsAccordions/hooks/useStandaloneAccordionToggle';
|
||||||
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
||||||
@ -24,8 +25,8 @@ import { ImageSizeCanvas } from './ImageSizeCanvas';
|
|||||||
import { ImageSizeLinear } from './ImageSizeLinear';
|
import { ImageSizeLinear } from './ImageSizeLinear';
|
||||||
|
|
||||||
const selector = createMemoizedSelector(
|
const selector = createMemoizedSelector(
|
||||||
[selectGenerationSlice, selectCanvasSlice, selectHrfSlice, activeTabNameSelector],
|
[selectGenerationSlice, selectCanvasSlice, selectHrfSlice, selectRegionalPromptsSlice, activeTabNameSelector],
|
||||||
(generation, canvas, hrf, activeTabName) => {
|
(generation, canvas, hrf, regionalPrompts, activeTabName) => {
|
||||||
const { shouldRandomizeSeed, model } = generation;
|
const { shouldRandomizeSeed, model } = generation;
|
||||||
const { hrfEnabled } = hrf;
|
const { hrfEnabled } = hrf;
|
||||||
const badges: string[] = [];
|
const badges: string[] = [];
|
||||||
@ -42,7 +43,7 @@ const selector = createMemoizedSelector(
|
|||||||
badges.push('locked');
|
badges.push('locked');
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const { aspectRatio, width, height } = generation;
|
const { aspectRatio, width, height } = regionalPrompts.present.size;
|
||||||
badges.push(`${width}×${height}`);
|
badges.push(`${width}×${height}`);
|
||||||
badges.push(aspectRatio.id);
|
badges.push(aspectRatio.id);
|
||||||
if (aspectRatio.isLocked) {
|
if (aspectRatio.isLocked) {
|
||||||
|
@ -5,27 +5,27 @@ import { AspectRatioCanvasPreview } from 'features/parameters/components/ImageSi
|
|||||||
import { AspectRatioIconPreview } from 'features/parameters/components/ImageSize/AspectRatioIconPreview';
|
import { AspectRatioIconPreview } from 'features/parameters/components/ImageSize/AspectRatioIconPreview';
|
||||||
import { ImageSize } from 'features/parameters/components/ImageSize/ImageSize';
|
import { ImageSize } from 'features/parameters/components/ImageSize/ImageSize';
|
||||||
import type { AspectRatioState } from 'features/parameters/components/ImageSize/types';
|
import type { AspectRatioState } from 'features/parameters/components/ImageSize/types';
|
||||||
import { aspectRatioChanged, heightChanged, widthChanged } from 'features/parameters/store/generationSlice';
|
import { aspectRatioChanged, heightChanged, widthChanged } from 'features/regionalPrompts/store/regionalPromptsSlice';
|
||||||
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
||||||
import { memo, useCallback } from 'react';
|
import { memo, useCallback } from 'react';
|
||||||
|
|
||||||
export const ImageSizeLinear = memo(() => {
|
export const ImageSizeLinear = memo(() => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const tab = useAppSelector(activeTabNameSelector);
|
const tab = useAppSelector(activeTabNameSelector);
|
||||||
const width = useAppSelector((s) => s.generation.width);
|
const width = useAppSelector((s) => s.regionalPrompts.present.size.width);
|
||||||
const height = useAppSelector((s) => s.generation.height);
|
const height = useAppSelector((s) => s.regionalPrompts.present.size.height);
|
||||||
const aspectRatioState = useAppSelector((s) => s.generation.aspectRatio);
|
const aspectRatioState = useAppSelector((s) => s.regionalPrompts.present.size.aspectRatio);
|
||||||
|
|
||||||
const onChangeWidth = useCallback(
|
const onChangeWidth = useCallback(
|
||||||
(width: number) => {
|
(width: number) => {
|
||||||
dispatch(widthChanged(width));
|
dispatch(widthChanged({ width }));
|
||||||
},
|
},
|
||||||
[dispatch]
|
[dispatch]
|
||||||
);
|
);
|
||||||
|
|
||||||
const onChangeHeight = useCallback(
|
const onChangeHeight = useCallback(
|
||||||
(height: number) => {
|
(height: number) => {
|
||||||
dispatch(heightChanged(height));
|
dispatch(heightChanged({ height }));
|
||||||
},
|
},
|
||||||
[dispatch]
|
[dispatch]
|
||||||
);
|
);
|
||||||
|
Reference in New Issue
Block a user