mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat(ui): canvas layer preview, revised reactivity for adapters
This commit is contained in:
parent
7b54762b5e
commit
f76f1d89d7
@ -3,11 +3,12 @@ import { CanvasEntityContainer } from 'features/controlLayers/components/common/
|
|||||||
import { CanvasEntityDeleteButton } from 'features/controlLayers/components/common/CanvasEntityDeleteButton';
|
import { CanvasEntityDeleteButton } from 'features/controlLayers/components/common/CanvasEntityDeleteButton';
|
||||||
import { CanvasEntityEnabledToggle } from 'features/controlLayers/components/common/CanvasEntityEnabledToggle';
|
import { CanvasEntityEnabledToggle } from 'features/controlLayers/components/common/CanvasEntityEnabledToggle';
|
||||||
import { CanvasEntityHeader } from 'features/controlLayers/components/common/CanvasEntityHeader';
|
import { CanvasEntityHeader } from 'features/controlLayers/components/common/CanvasEntityHeader';
|
||||||
|
import { CanvasEntityPreviewImage } from 'features/controlLayers/components/common/CanvasEntityPreviewImage';
|
||||||
import { CanvasEntitySettingsWrapper } from 'features/controlLayers/components/common/CanvasEntitySettingsWrapper';
|
import { CanvasEntitySettingsWrapper } from 'features/controlLayers/components/common/CanvasEntitySettingsWrapper';
|
||||||
import { CanvasEntityEditableTitle } from 'features/controlLayers/components/common/CanvasEntityTitleEdit';
|
import { CanvasEntityEditableTitle } from 'features/controlLayers/components/common/CanvasEntityTitleEdit';
|
||||||
import { ControlLayerControlAdapter } from 'features/controlLayers/components/ControlLayer/ControlLayerControlAdapter';
|
import { ControlLayerControlAdapter } from 'features/controlLayers/components/ControlLayer/ControlLayerControlAdapter';
|
||||||
|
import { EntityLayerAdapterGate } from 'features/controlLayers/contexts/EntityAdapterContext';
|
||||||
import { EntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
import { EntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||||
import { EntityLayerAdapterProviderGate } from 'features/controlLayers/hooks/useEntityLayerAdapter';
|
|
||||||
import type { CanvasEntityIdentifier } from 'features/controlLayers/store/types';
|
import type { CanvasEntityIdentifier } from 'features/controlLayers/store/types';
|
||||||
import { memo, useMemo } from 'react';
|
import { memo, useMemo } from 'react';
|
||||||
|
|
||||||
@ -20,9 +21,10 @@ export const ControlLayer = memo(({ id }: Props) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<EntityIdentifierContext.Provider value={entityIdentifier}>
|
<EntityIdentifierContext.Provider value={entityIdentifier}>
|
||||||
<EntityLayerAdapterProviderGate>
|
<EntityLayerAdapterGate>
|
||||||
<CanvasEntityContainer>
|
<CanvasEntityContainer>
|
||||||
<CanvasEntityHeader>
|
<CanvasEntityHeader>
|
||||||
|
<CanvasEntityPreviewImage />
|
||||||
<CanvasEntityEnabledToggle />
|
<CanvasEntityEnabledToggle />
|
||||||
<CanvasEntityEditableTitle />
|
<CanvasEntityEditableTitle />
|
||||||
<Spacer />
|
<Spacer />
|
||||||
@ -32,7 +34,7 @@ export const ControlLayer = memo(({ id }: Props) => {
|
|||||||
<ControlLayerControlAdapter />
|
<ControlLayerControlAdapter />
|
||||||
</CanvasEntitySettingsWrapper>
|
</CanvasEntitySettingsWrapper>
|
||||||
</CanvasEntityContainer>
|
</CanvasEntityContainer>
|
||||||
</EntityLayerAdapterProviderGate>
|
</EntityLayerAdapterGate>
|
||||||
</EntityIdentifierContext.Provider>
|
</EntityIdentifierContext.Provider>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -10,16 +10,17 @@ import { ToolChooser } from 'features/controlLayers/components/Tool/ToolChooser'
|
|||||||
import { ToolEraserWidth } from 'features/controlLayers/components/Tool/ToolEraserWidth';
|
import { ToolEraserWidth } from 'features/controlLayers/components/Tool/ToolEraserWidth';
|
||||||
import { ToolFillColorPicker } from 'features/controlLayers/components/Tool/ToolFillColorPicker';
|
import { ToolFillColorPicker } from 'features/controlLayers/components/Tool/ToolFillColorPicker';
|
||||||
import { UndoRedoButtonGroup } from 'features/controlLayers/components/UndoRedoButtonGroup';
|
import { UndoRedoButtonGroup } from 'features/controlLayers/components/UndoRedoButtonGroup';
|
||||||
import { CanvasManagerProviderGate } from 'features/controlLayers/contexts/CanvasManagerProviderGate';
|
import { CanvasManagerProviderGate, useCanvasManager } from 'features/controlLayers/contexts/CanvasManagerProviderGate';
|
||||||
import { ToggleProgressButton } from 'features/gallery/components/ImageViewer/ToggleProgressButton';
|
import { ToggleProgressButton } from 'features/gallery/components/ImageViewer/ToggleProgressButton';
|
||||||
import { ViewerToggleMenu } from 'features/gallery/components/ImageViewer/ViewerToggleMenu';
|
import { ViewerToggleMenu } from 'features/gallery/components/ImageViewer/ViewerToggleMenu';
|
||||||
import { memo } from 'react';
|
import { memo, useSyncExternalStore } from 'react';
|
||||||
|
|
||||||
export const ControlLayersToolbar = memo(() => {
|
export const ControlLayersToolbar = memo(() => {
|
||||||
const tool = useAppSelector((s) => s.canvasV2.tool.selected);
|
const tool = useAppSelector((s) => s.canvasV2.tool.selected);
|
||||||
return (
|
return (
|
||||||
<CanvasManagerProviderGate>
|
<CanvasManagerProviderGate>
|
||||||
<Flex w="full" gap={2} alignItems="center">
|
<Flex w="full" gap={2} alignItems="center">
|
||||||
|
<ReactiveTest />
|
||||||
<ToggleProgressButton />
|
<ToggleProgressButton />
|
||||||
<ToolChooser />
|
<ToolChooser />
|
||||||
<Spacer />
|
<Spacer />
|
||||||
@ -40,3 +41,15 @@ export const ControlLayersToolbar = memo(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
ControlLayersToolbar.displayName = 'ControlLayersToolbar';
|
ControlLayersToolbar.displayName = 'ControlLayersToolbar';
|
||||||
|
|
||||||
|
const ReactiveTest = () => {
|
||||||
|
const canvasManager = useCanvasManager();
|
||||||
|
const adapters = useSyncExternalStore(
|
||||||
|
canvasManager.adapters.rasterLayers.subscribe,
|
||||||
|
canvasManager.adapters.rasterLayers.getSnapshot
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(adapters);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
@ -3,9 +3,10 @@ import { CanvasEntityContainer } from 'features/controlLayers/components/common/
|
|||||||
import { CanvasEntityDeleteButton } from 'features/controlLayers/components/common/CanvasEntityDeleteButton';
|
import { CanvasEntityDeleteButton } from 'features/controlLayers/components/common/CanvasEntityDeleteButton';
|
||||||
import { CanvasEntityEnabledToggle } from 'features/controlLayers/components/common/CanvasEntityEnabledToggle';
|
import { CanvasEntityEnabledToggle } from 'features/controlLayers/components/common/CanvasEntityEnabledToggle';
|
||||||
import { CanvasEntityHeader } from 'features/controlLayers/components/common/CanvasEntityHeader';
|
import { CanvasEntityHeader } from 'features/controlLayers/components/common/CanvasEntityHeader';
|
||||||
|
import { CanvasEntityPreviewImage } from 'features/controlLayers/components/common/CanvasEntityPreviewImage';
|
||||||
import { CanvasEntityEditableTitle } from 'features/controlLayers/components/common/CanvasEntityTitleEdit';
|
import { CanvasEntityEditableTitle } from 'features/controlLayers/components/common/CanvasEntityTitleEdit';
|
||||||
|
import { EntityMaskAdapterGate } from 'features/controlLayers/contexts/EntityAdapterContext';
|
||||||
import { EntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
import { EntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||||
import { EntityMaskAdapterProviderGate } from 'features/controlLayers/hooks/useEntityMaskAdapter';
|
|
||||||
import type { CanvasEntityIdentifier } from 'features/controlLayers/store/types';
|
import type { CanvasEntityIdentifier } from 'features/controlLayers/store/types';
|
||||||
import { memo, useMemo } from 'react';
|
import { memo, useMemo } from 'react';
|
||||||
|
|
||||||
@ -20,9 +21,10 @@ export const InpaintMask = memo(({ id }: Props) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<EntityIdentifierContext.Provider value={entityIdentifier}>
|
<EntityIdentifierContext.Provider value={entityIdentifier}>
|
||||||
<EntityMaskAdapterProviderGate>
|
<EntityMaskAdapterGate>
|
||||||
<CanvasEntityContainer>
|
<CanvasEntityContainer>
|
||||||
<CanvasEntityHeader>
|
<CanvasEntityHeader>
|
||||||
|
<CanvasEntityPreviewImage />
|
||||||
<CanvasEntityEnabledToggle />
|
<CanvasEntityEnabledToggle />
|
||||||
<CanvasEntityEditableTitle />
|
<CanvasEntityEditableTitle />
|
||||||
<Spacer />
|
<Spacer />
|
||||||
@ -30,7 +32,7 @@ export const InpaintMask = memo(({ id }: Props) => {
|
|||||||
<CanvasEntityDeleteButton />
|
<CanvasEntityDeleteButton />
|
||||||
</CanvasEntityHeader>
|
</CanvasEntityHeader>
|
||||||
</CanvasEntityContainer>
|
</CanvasEntityContainer>
|
||||||
</EntityMaskAdapterProviderGate>
|
</EntityMaskAdapterGate>
|
||||||
</EntityIdentifierContext.Provider>
|
</EntityIdentifierContext.Provider>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -3,9 +3,10 @@ import { CanvasEntityContainer } from 'features/controlLayers/components/common/
|
|||||||
import { CanvasEntityDeleteButton } from 'features/controlLayers/components/common/CanvasEntityDeleteButton';
|
import { CanvasEntityDeleteButton } from 'features/controlLayers/components/common/CanvasEntityDeleteButton';
|
||||||
import { CanvasEntityEnabledToggle } from 'features/controlLayers/components/common/CanvasEntityEnabledToggle';
|
import { CanvasEntityEnabledToggle } from 'features/controlLayers/components/common/CanvasEntityEnabledToggle';
|
||||||
import { CanvasEntityHeader } from 'features/controlLayers/components/common/CanvasEntityHeader';
|
import { CanvasEntityHeader } from 'features/controlLayers/components/common/CanvasEntityHeader';
|
||||||
|
import { CanvasEntityPreviewImage } from 'features/controlLayers/components/common/CanvasEntityPreviewImage';
|
||||||
import { CanvasEntityEditableTitle } from 'features/controlLayers/components/common/CanvasEntityTitleEdit';
|
import { CanvasEntityEditableTitle } from 'features/controlLayers/components/common/CanvasEntityTitleEdit';
|
||||||
|
import { EntityLayerAdapterGate } from 'features/controlLayers/contexts/EntityAdapterContext';
|
||||||
import { EntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
import { EntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||||
import { EntityLayerAdapterProviderGate } from 'features/controlLayers/hooks/useEntityLayerAdapter';
|
|
||||||
import type { CanvasEntityIdentifier } from 'features/controlLayers/store/types';
|
import type { CanvasEntityIdentifier } from 'features/controlLayers/store/types';
|
||||||
import { memo, useMemo } from 'react';
|
import { memo, useMemo } from 'react';
|
||||||
|
|
||||||
@ -18,16 +19,17 @@ export const RasterLayer = memo(({ id }: Props) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<EntityIdentifierContext.Provider value={entityIdentifier}>
|
<EntityIdentifierContext.Provider value={entityIdentifier}>
|
||||||
<EntityLayerAdapterProviderGate>
|
<EntityLayerAdapterGate>
|
||||||
<CanvasEntityContainer>
|
<CanvasEntityContainer>
|
||||||
<CanvasEntityHeader>
|
<CanvasEntityHeader>
|
||||||
|
<CanvasEntityPreviewImage />
|
||||||
<CanvasEntityEnabledToggle />
|
<CanvasEntityEnabledToggle />
|
||||||
<CanvasEntityEditableTitle />
|
<CanvasEntityEditableTitle />
|
||||||
<Spacer />
|
<Spacer />
|
||||||
<CanvasEntityDeleteButton />
|
<CanvasEntityDeleteButton />
|
||||||
</CanvasEntityHeader>
|
</CanvasEntityHeader>
|
||||||
</CanvasEntityContainer>
|
</CanvasEntityContainer>
|
||||||
</EntityLayerAdapterProviderGate>
|
</EntityLayerAdapterGate>
|
||||||
</EntityIdentifierContext.Provider>
|
</EntityIdentifierContext.Provider>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -3,11 +3,12 @@ import { CanvasEntityContainer } from 'features/controlLayers/components/common/
|
|||||||
import { CanvasEntityDeleteButton } from 'features/controlLayers/components/common/CanvasEntityDeleteButton';
|
import { CanvasEntityDeleteButton } from 'features/controlLayers/components/common/CanvasEntityDeleteButton';
|
||||||
import { CanvasEntityEnabledToggle } from 'features/controlLayers/components/common/CanvasEntityEnabledToggle';
|
import { CanvasEntityEnabledToggle } from 'features/controlLayers/components/common/CanvasEntityEnabledToggle';
|
||||||
import { CanvasEntityHeader } from 'features/controlLayers/components/common/CanvasEntityHeader';
|
import { CanvasEntityHeader } from 'features/controlLayers/components/common/CanvasEntityHeader';
|
||||||
|
import { CanvasEntityPreviewImage } from 'features/controlLayers/components/common/CanvasEntityPreviewImage';
|
||||||
import { CanvasEntityEditableTitle } from 'features/controlLayers/components/common/CanvasEntityTitleEdit';
|
import { CanvasEntityEditableTitle } from 'features/controlLayers/components/common/CanvasEntityTitleEdit';
|
||||||
import { RegionalGuidanceBadges } from 'features/controlLayers/components/RegionalGuidance/RegionalGuidanceBadges';
|
import { RegionalGuidanceBadges } from 'features/controlLayers/components/RegionalGuidance/RegionalGuidanceBadges';
|
||||||
import { RegionalGuidanceSettings } from 'features/controlLayers/components/RegionalGuidance/RegionalGuidanceSettings';
|
import { RegionalGuidanceSettings } from 'features/controlLayers/components/RegionalGuidance/RegionalGuidanceSettings';
|
||||||
|
import { EntityMaskAdapterGate } from 'features/controlLayers/contexts/EntityAdapterContext';
|
||||||
import { EntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
import { EntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||||
import { EntityMaskAdapterProviderGate } from 'features/controlLayers/hooks/useEntityMaskAdapter';
|
|
||||||
import type { CanvasEntityIdentifier } from 'features/controlLayers/store/types';
|
import type { CanvasEntityIdentifier } from 'features/controlLayers/store/types';
|
||||||
import { memo, useMemo } from 'react';
|
import { memo, useMemo } from 'react';
|
||||||
|
|
||||||
@ -23,9 +24,10 @@ export const RegionalGuidance = memo(({ id }: Props) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<EntityIdentifierContext.Provider value={entityIdentifier}>
|
<EntityIdentifierContext.Provider value={entityIdentifier}>
|
||||||
<EntityMaskAdapterProviderGate>
|
<EntityMaskAdapterGate>
|
||||||
<CanvasEntityContainer>
|
<CanvasEntityContainer>
|
||||||
<CanvasEntityHeader>
|
<CanvasEntityHeader>
|
||||||
|
<CanvasEntityPreviewImage />
|
||||||
<CanvasEntityEnabledToggle />
|
<CanvasEntityEnabledToggle />
|
||||||
<CanvasEntityEditableTitle />
|
<CanvasEntityEditableTitle />
|
||||||
<Spacer />
|
<Spacer />
|
||||||
@ -36,7 +38,7 @@ export const RegionalGuidance = memo(({ id }: Props) => {
|
|||||||
</CanvasEntityHeader>
|
</CanvasEntityHeader>
|
||||||
<RegionalGuidanceSettings />
|
<RegionalGuidanceSettings />
|
||||||
</CanvasEntityContainer>
|
</CanvasEntityContainer>
|
||||||
</EntityMaskAdapterProviderGate>
|
</EntityMaskAdapterGate>
|
||||||
</EntityIdentifierContext.Provider>
|
</EntityIdentifierContext.Provider>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -7,13 +7,7 @@ export const CanvasSettingsRecalculateRectsButton = memo(() => {
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const canvasManager = useCanvasManager();
|
const canvasManager = useCanvasManager();
|
||||||
const onClick = useCallback(() => {
|
const onClick = useCallback(() => {
|
||||||
const adapters = [
|
for (const adapter of canvasManager.adapters.getAll()) {
|
||||||
...canvasManager.rasterLayerAdapters.values(),
|
|
||||||
...canvasManager.controlLayerAdapters.values(),
|
|
||||||
...canvasManager.regionalGuidanceAdapters.values(),
|
|
||||||
...canvasManager.inpaintMaskAdapters.values(),
|
|
||||||
];
|
|
||||||
for (const adapter of adapters) {
|
|
||||||
adapter.transformer.requestRectCalculation();
|
adapter.transformer.requestRectCalculation();
|
||||||
}
|
}
|
||||||
}, [canvasManager]);
|
}, [canvasManager]);
|
||||||
|
@ -21,7 +21,6 @@ export const CanvasEntityContainer = memo((props: PropsWithChildren) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex
|
<Flex
|
||||||
position="relative" // necessary for drop overlay
|
|
||||||
flexDir="column"
|
flexDir="column"
|
||||||
w="full"
|
w="full"
|
||||||
bg={isSelected ? 'base.800' : 'base.850'}
|
bg={isSelected ? 'base.800' : 'base.850'}
|
||||||
|
@ -53,7 +53,7 @@ export const CanvasEntityHeader = memo(({ children, ...rest }: FlexProps) => {
|
|||||||
return (
|
return (
|
||||||
<ContextMenu renderMenu={renderMenu}>
|
<ContextMenu renderMenu={renderMenu}>
|
||||||
{(ref) => (
|
{(ref) => (
|
||||||
<Flex ref={ref} gap={2} alignItems="center" p={2} {...rest}>
|
<Flex ref={ref} h={16} gap={2} alignItems="center" p={2} {...rest}>
|
||||||
{children}
|
{children}
|
||||||
</Flex>
|
</Flex>
|
||||||
)}
|
)}
|
||||||
|
@ -0,0 +1,59 @@
|
|||||||
|
import { Box, chakra, Flex } from '@invoke-ai/ui-library';
|
||||||
|
import { useStore } from '@nanostores/react';
|
||||||
|
import { useEntityAdapter } from 'features/controlLayers/contexts/EntityAdapterContext';
|
||||||
|
import { TRANSPARENCY_CHECKER_PATTERN } from 'features/controlLayers/konva/constants';
|
||||||
|
import { memo, useEffect, useRef } from 'react';
|
||||||
|
|
||||||
|
const ChakraCanvas = chakra.canvas;
|
||||||
|
|
||||||
|
export const CanvasEntityPreviewImage = memo(() => {
|
||||||
|
const adapter = useEntityAdapter();
|
||||||
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
|
const canvasRef = useRef<HTMLCanvasElement>(null);
|
||||||
|
const cache = useStore(adapter.renderer.$canvasCache);
|
||||||
|
useEffect(() => {
|
||||||
|
if (!cache || !canvasRef.current || !containerRef.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const ctx = canvasRef.current.getContext('2d');
|
||||||
|
if (!ctx) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const { rect, canvas } = cache;
|
||||||
|
|
||||||
|
ctx.clearRect(0, 0, canvasRef.current.width, canvasRef.current.height);
|
||||||
|
|
||||||
|
canvasRef.current.width = rect.width;
|
||||||
|
canvasRef.current.height = rect.height;
|
||||||
|
|
||||||
|
ctx.drawImage(canvas, rect.x, rect.y, rect.width, rect.height, 0, 0, rect.width, rect.height);
|
||||||
|
}, [adapter.transformer, adapter.transformer.nodeRect, adapter.transformer.pixelRect, cache]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Flex
|
||||||
|
position="relative"
|
||||||
|
ref={containerRef}
|
||||||
|
alignItems="center"
|
||||||
|
justifyContent="center"
|
||||||
|
w={12}
|
||||||
|
h={12}
|
||||||
|
borderRadius="sm"
|
||||||
|
borderWidth={1}
|
||||||
|
bg="base.900"
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
position="absolute"
|
||||||
|
top={0}
|
||||||
|
right={0}
|
||||||
|
bottom={0}
|
||||||
|
left={0}
|
||||||
|
bgImage={TRANSPARENCY_CHECKER_PATTERN}
|
||||||
|
bgSize="5px"
|
||||||
|
opacity={0.1}
|
||||||
|
/>
|
||||||
|
<ChakraCanvas ref={canvasRef} objectFit="contain" maxW="full" maxH="full" />
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
CanvasEntityPreviewImage.displayName = 'CanvasEntityPreviewImage';
|
@ -0,0 +1,82 @@
|
|||||||
|
import type { SyncableMap } from 'common/util/SyncableMap/SyncableMap';
|
||||||
|
import { useCanvasManager } from 'features/controlLayers/contexts/CanvasManagerProviderGate';
|
||||||
|
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
||||||
|
import type { CanvasLayerAdapter } from 'features/controlLayers/konva/CanvasLayerAdapter';
|
||||||
|
import type { CanvasMaskAdapter } from 'features/controlLayers/konva/CanvasMaskAdapter';
|
||||||
|
import type { PropsWithChildren } from 'react';
|
||||||
|
import { createContext, memo, useContext, useMemo, useSyncExternalStore } from 'react';
|
||||||
|
import { assert } from 'tsafe';
|
||||||
|
|
||||||
|
const EntityAdapterContext = createContext<CanvasLayerAdapter | CanvasMaskAdapter | null>(null);
|
||||||
|
|
||||||
|
export const EntityLayerAdapterGate = memo(({ children }: PropsWithChildren) => {
|
||||||
|
const canvasManager = useCanvasManager();
|
||||||
|
const entityIdentifier = useEntityIdentifierContext();
|
||||||
|
const store = useMemo<SyncableMap<string, CanvasLayerAdapter>>(() => {
|
||||||
|
if (entityIdentifier.type === 'raster_layer') {
|
||||||
|
return canvasManager.adapters.rasterLayers;
|
||||||
|
}
|
||||||
|
if (entityIdentifier.type === 'control_layer') {
|
||||||
|
return canvasManager.adapters.controlLayers;
|
||||||
|
}
|
||||||
|
assert(false, 'Unknown entity type');
|
||||||
|
}, [canvasManager.adapters.controlLayers, canvasManager.adapters.rasterLayers, entityIdentifier.type]);
|
||||||
|
const adapters = useSyncExternalStore(store.subscribe, store.getSnapshot);
|
||||||
|
const adapter = useMemo(() => {
|
||||||
|
return adapters.get(entityIdentifier.id) ?? null;
|
||||||
|
}, [adapters, entityIdentifier.id]);
|
||||||
|
|
||||||
|
if (!adapter) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return <EntityAdapterContext.Provider value={adapter}>{children}</EntityAdapterContext.Provider>;
|
||||||
|
});
|
||||||
|
|
||||||
|
EntityLayerAdapterGate.displayName = 'EntityLayerAdapterGate';
|
||||||
|
|
||||||
|
export const useEntityLayerAdapter = (): CanvasLayerAdapter => {
|
||||||
|
const adapter = useContext(EntityAdapterContext);
|
||||||
|
assert(adapter, 'useEntityLayerAdapter must be used within a EntityLayerAdapterGate');
|
||||||
|
assert(adapter.type === 'layer_adapter', 'useEntityLayerAdapter must be used with a layer adapter');
|
||||||
|
return adapter;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const EntityMaskAdapterGate = memo(({ children }: PropsWithChildren) => {
|
||||||
|
const canvasManager = useCanvasManager();
|
||||||
|
const entityIdentifier = useEntityIdentifierContext();
|
||||||
|
const store = useMemo<SyncableMap<string, CanvasMaskAdapter>>(() => {
|
||||||
|
if (entityIdentifier.type === 'inpaint_mask') {
|
||||||
|
return canvasManager.adapters.inpaintMasks;
|
||||||
|
}
|
||||||
|
if (entityIdentifier.type === 'regional_guidance') {
|
||||||
|
return canvasManager.adapters.regionMasks;
|
||||||
|
}
|
||||||
|
assert(false, 'Unknown entity type');
|
||||||
|
}, [canvasManager.adapters.inpaintMasks, canvasManager.adapters.regionMasks, entityIdentifier.type]);
|
||||||
|
const adapters = useSyncExternalStore(store.subscribe, store.getSnapshot);
|
||||||
|
const adapter = useMemo(() => {
|
||||||
|
return adapters.get(entityIdentifier.id) ?? null;
|
||||||
|
}, [adapters, entityIdentifier.id]);
|
||||||
|
|
||||||
|
if (!adapter) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return <EntityAdapterContext.Provider value={adapter}>{children}</EntityAdapterContext.Provider>;
|
||||||
|
});
|
||||||
|
|
||||||
|
EntityMaskAdapterGate.displayName = 'EntityMaskAdapterGate';
|
||||||
|
|
||||||
|
export const useEntityMaskAdapter = (): CanvasMaskAdapter => {
|
||||||
|
const adapter = useContext(EntityAdapterContext);
|
||||||
|
assert(adapter, 'useEntityMaskAdapter must be used within a CanvasMaskAdapterGate');
|
||||||
|
assert(adapter.type === 'mask_adapter', 'useEntityMaskAdapter must be used with a mask adapter');
|
||||||
|
return adapter;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useEntityAdapter = (): CanvasLayerAdapter | CanvasMaskAdapter => {
|
||||||
|
const adapter = useContext(EntityAdapterContext);
|
||||||
|
assert(adapter, 'useEntityAdapter must be used within a CanvasRasterLayerAdapterGate');
|
||||||
|
return adapter;
|
||||||
|
};
|
@ -12,7 +12,7 @@ export const useEntityAdapter = (entityIdentifier: CanvasEntityIdentifier): Canv
|
|||||||
const entity = canvasManager.stateApi.getEntity(entityIdentifier);
|
const entity = canvasManager.stateApi.getEntity(entityIdentifier);
|
||||||
assert(entity, 'Entity adapter not found');
|
assert(entity, 'Entity adapter not found');
|
||||||
return entity.adapter;
|
return entity.adapter;
|
||||||
}, [canvasManager, entityIdentifier]);
|
}, [canvasManager.stateApi, entityIdentifier]);
|
||||||
|
|
||||||
return adapter;
|
return adapter;
|
||||||
};
|
};
|
||||||
|
@ -1,37 +0,0 @@
|
|||||||
import { useCanvasManager } from 'features/controlLayers/contexts/CanvasManagerProviderGate';
|
|
||||||
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
|
||||||
import type { CanvasLayerAdapter } from 'features/controlLayers/konva/CanvasLayerAdapter';
|
|
||||||
import type { PropsWithChildren } from 'react';
|
|
||||||
import { createContext, memo, useContext, useMemo } from 'react';
|
|
||||||
import { assert } from 'tsafe';
|
|
||||||
|
|
||||||
const EntityLayerAdapterContext = createContext<CanvasLayerAdapter | null>(null);
|
|
||||||
|
|
||||||
export const EntityLayerAdapterProviderGate = memo(({ children }: PropsWithChildren) => {
|
|
||||||
const entityIdentifier = useEntityIdentifierContext();
|
|
||||||
const canvasManager = useCanvasManager();
|
|
||||||
const adapter = useMemo(() => {
|
|
||||||
if (entityIdentifier.type === 'raster_layer') {
|
|
||||||
return canvasManager.rasterLayerAdapters.get(entityIdentifier.id) ?? null;
|
|
||||||
} else if (entityIdentifier.type === 'control_layer') {
|
|
||||||
return canvasManager.controlLayerAdapters.get(entityIdentifier.id) ?? null;
|
|
||||||
}
|
|
||||||
assert(false, 'EntityLayerAdapterProviderGate must be used with a valid EntityIdentifierContext');
|
|
||||||
}, [canvasManager, entityIdentifier]);
|
|
||||||
|
|
||||||
if (!canvasManager) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return <EntityLayerAdapterContext.Provider value={adapter}>{children}</EntityLayerAdapterContext.Provider>;
|
|
||||||
});
|
|
||||||
|
|
||||||
EntityLayerAdapterProviderGate.displayName = 'EntityLayerAdapterProviderGate';
|
|
||||||
|
|
||||||
export const useEntityLayerAdapter = (): CanvasLayerAdapter => {
|
|
||||||
const adapter = useContext(EntityLayerAdapterContext);
|
|
||||||
|
|
||||||
assert(adapter, 'useEntityLayerAdapter must be used within a EntityLayerAdapterProviderGate');
|
|
||||||
|
|
||||||
return adapter;
|
|
||||||
};
|
|
@ -1,37 +0,0 @@
|
|||||||
import { useCanvasManager } from 'features/controlLayers/contexts/CanvasManagerProviderGate';
|
|
||||||
import { useEntityIdentifierContext } from 'features/controlLayers/contexts/EntityIdentifierContext';
|
|
||||||
import type { CanvasMaskAdapter } from 'features/controlLayers/konva/CanvasMaskAdapter';
|
|
||||||
import type { PropsWithChildren } from 'react';
|
|
||||||
import { createContext, memo, useContext, useMemo } from 'react';
|
|
||||||
import { assert } from 'tsafe';
|
|
||||||
|
|
||||||
const EntityMaskAdapterContext = createContext<CanvasMaskAdapter | null>(null);
|
|
||||||
|
|
||||||
export const EntityMaskAdapterProviderGate = memo(({ children }: PropsWithChildren) => {
|
|
||||||
const entityIdentifier = useEntityIdentifierContext();
|
|
||||||
const canvasManager = useCanvasManager();
|
|
||||||
const adapter = useMemo(() => {
|
|
||||||
if (entityIdentifier.type === 'inpaint_mask') {
|
|
||||||
return canvasManager.inpaintMaskAdapters.get(entityIdentifier.id) ?? null;
|
|
||||||
} else if (entityIdentifier.type === 'regional_guidance') {
|
|
||||||
return canvasManager.regionalGuidanceAdapters.get(entityIdentifier.id) ?? null;
|
|
||||||
}
|
|
||||||
assert(false, 'EntityMaskAdapterProviderGate must be used with a valid EntityIdentifierContext');
|
|
||||||
}, [canvasManager, entityIdentifier]);
|
|
||||||
|
|
||||||
if (!canvasManager) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return <EntityMaskAdapterContext.Provider value={adapter}>{children}</EntityMaskAdapterContext.Provider>;
|
|
||||||
});
|
|
||||||
|
|
||||||
EntityMaskAdapterProviderGate.displayName = 'EntityMaskAdapterProviderGate';
|
|
||||||
|
|
||||||
export const useEntityMaskAdapter = (): CanvasMaskAdapter => {
|
|
||||||
const adapter = useContext(EntityMaskAdapterContext);
|
|
||||||
|
|
||||||
assert(adapter, 'useEntityMaskAdapter must be used within a EntityLayerAdapterProviderGate');
|
|
||||||
|
|
||||||
return adapter;
|
|
||||||
};
|
|
@ -30,7 +30,7 @@ export class CanvasCompositorModule {
|
|||||||
|
|
||||||
getCompositeRasterLayerEntityIds = (): string[] => {
|
getCompositeRasterLayerEntityIds = (): string[] => {
|
||||||
const ids = [];
|
const ids = [];
|
||||||
for (const adapter of this.manager.rasterLayerAdapters.values()) {
|
for (const adapter of this.manager.adapters.rasterLayers.values()) {
|
||||||
if (adapter.state.isEnabled && adapter.renderer.hasObjects()) {
|
if (adapter.state.isEnabled && adapter.renderer.hasObjects()) {
|
||||||
ids.push(adapter.id);
|
ids.push(adapter.id);
|
||||||
}
|
}
|
||||||
@ -40,7 +40,7 @@ export class CanvasCompositorModule {
|
|||||||
|
|
||||||
getCompositeInpaintMaskEntityIds = (): string[] => {
|
getCompositeInpaintMaskEntityIds = (): string[] => {
|
||||||
const ids = [];
|
const ids = [];
|
||||||
for (const adapter of this.manager.inpaintMaskAdapters.values()) {
|
for (const adapter of this.manager.adapters.inpaintMasks.values()) {
|
||||||
if (adapter.state.isEnabled && adapter.renderer.hasObjects()) {
|
if (adapter.state.isEnabled && adapter.renderer.hasObjects()) {
|
||||||
ids.push(adapter.id);
|
ids.push(adapter.id);
|
||||||
}
|
}
|
||||||
@ -67,7 +67,7 @@ export class CanvasCompositorModule {
|
|||||||
assert(ctx !== null, 'Canvas 2D context is null');
|
assert(ctx !== null, 'Canvas 2D context is null');
|
||||||
|
|
||||||
for (const id of this.getCompositeRasterLayerEntityIds()) {
|
for (const id of this.getCompositeRasterLayerEntityIds()) {
|
||||||
const adapter = this.manager.rasterLayerAdapters.get(id);
|
const adapter = this.manager.adapters.rasterLayers.get(id);
|
||||||
if (!adapter) {
|
if (!adapter) {
|
||||||
this.log.warn({ id }, 'Raster layer adapter not found');
|
this.log.warn({ id }, 'Raster layer adapter not found');
|
||||||
continue;
|
continue;
|
||||||
@ -99,7 +99,7 @@ export class CanvasCompositorModule {
|
|||||||
assert(ctx !== null);
|
assert(ctx !== null);
|
||||||
|
|
||||||
for (const id of this.getCompositeInpaintMaskEntityIds()) {
|
for (const id of this.getCompositeInpaintMaskEntityIds()) {
|
||||||
const adapter = this.manager.inpaintMaskAdapters.get(id);
|
const adapter = this.manager.adapters.inpaintMasks.get(id);
|
||||||
if (!adapter) {
|
if (!adapter) {
|
||||||
this.log.warn({ id }, 'Inpaint mask adapter not found');
|
this.log.warn({ id }, 'Inpaint mask adapter not found');
|
||||||
continue;
|
continue;
|
||||||
@ -117,7 +117,7 @@ export class CanvasCompositorModule {
|
|||||||
extra,
|
extra,
|
||||||
};
|
};
|
||||||
for (const id of this.getCompositeRasterLayerEntityIds()) {
|
for (const id of this.getCompositeRasterLayerEntityIds()) {
|
||||||
const adapter = this.manager.rasterLayerAdapters.get(id);
|
const adapter = this.manager.adapters.rasterLayers.get(id);
|
||||||
if (!adapter) {
|
if (!adapter) {
|
||||||
this.log.warn({ id }, 'Raster layer adapter not found');
|
this.log.warn({ id }, 'Raster layer adapter not found');
|
||||||
continue;
|
continue;
|
||||||
@ -132,7 +132,7 @@ export class CanvasCompositorModule {
|
|||||||
extra,
|
extra,
|
||||||
};
|
};
|
||||||
for (const id of this.getCompositeInpaintMaskEntityIds()) {
|
for (const id of this.getCompositeInpaintMaskEntityIds()) {
|
||||||
const adapter = this.manager.inpaintMaskAdapters.get(id);
|
const adapter = this.manager.adapters.inpaintMasks.get(id);
|
||||||
if (!adapter) {
|
if (!adapter) {
|
||||||
this.log.warn({ id }, 'Inpaint mask adapter not found');
|
this.log.warn({ id }, 'Inpaint mask adapter not found');
|
||||||
continue;
|
continue;
|
||||||
|
@ -9,6 +9,7 @@ import { CanvasRenderingModule } from 'features/controlLayers/konva/CanvasRender
|
|||||||
import { CanvasStageModule } from 'features/controlLayers/konva/CanvasStageModule';
|
import { CanvasStageModule } from 'features/controlLayers/konva/CanvasStageModule';
|
||||||
import { CanvasWorkerModule } from 'features/controlLayers/konva/CanvasWorkerModule.js';
|
import { CanvasWorkerModule } from 'features/controlLayers/konva/CanvasWorkerModule.js';
|
||||||
import { getPrefixedId } from 'features/controlLayers/konva/util';
|
import { getPrefixedId } from 'features/controlLayers/konva/util';
|
||||||
|
import { SyncableMap } from 'common/util/SyncableMap/SyncableMap';
|
||||||
import type Konva from 'konva';
|
import type Konva from 'konva';
|
||||||
import { atom } from 'nanostores';
|
import { atom } from 'nanostores';
|
||||||
import type { Logger } from 'roarr';
|
import type { Logger } from 'roarr';
|
||||||
@ -32,10 +33,20 @@ export class CanvasManager {
|
|||||||
store: AppStore;
|
store: AppStore;
|
||||||
socket: AppSocket;
|
socket: AppSocket;
|
||||||
|
|
||||||
rasterLayerAdapters: Map<string, CanvasLayerAdapter> = new Map();
|
adapters = {
|
||||||
controlLayerAdapters: Map<string, CanvasLayerAdapter> = new Map();
|
rasterLayers: new SyncableMap<string, CanvasLayerAdapter>(),
|
||||||
regionalGuidanceAdapters: Map<string, CanvasMaskAdapter> = new Map();
|
controlLayers: new SyncableMap<string, CanvasLayerAdapter>(),
|
||||||
inpaintMaskAdapters: Map<string, CanvasMaskAdapter> = new Map();
|
regionMasks: new SyncableMap<string, CanvasMaskAdapter>(),
|
||||||
|
inpaintMasks: new SyncableMap<string, CanvasMaskAdapter>(),
|
||||||
|
getAll: (): (CanvasLayerAdapter | CanvasMaskAdapter)[] => {
|
||||||
|
return [
|
||||||
|
...this.adapters.rasterLayers.values(),
|
||||||
|
...this.adapters.controlLayers.values(),
|
||||||
|
...this.adapters.regionMasks.values(),
|
||||||
|
...this.adapters.inpaintMasks.values(),
|
||||||
|
];
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
stateApi: CanvasStateApiModule;
|
stateApi: CanvasStateApiModule;
|
||||||
preview: CanvasPreviewModule;
|
preview: CanvasPreviewModule;
|
||||||
@ -105,13 +116,7 @@ export class CanvasManager {
|
|||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
this.log.debug('Cleaning up canvas manager');
|
this.log.debug('Cleaning up canvas manager');
|
||||||
const allAdapters = [
|
for (const adapter of this.adapters.getAll()) {
|
||||||
...this.rasterLayerAdapters.values(),
|
|
||||||
...this.controlLayerAdapters.values(),
|
|
||||||
...this.inpaintMaskAdapters.values(),
|
|
||||||
...this.regionalGuidanceAdapters.values(),
|
|
||||||
];
|
|
||||||
for (const adapter of allAdapters) {
|
|
||||||
adapter.destroy();
|
adapter.destroy();
|
||||||
}
|
}
|
||||||
this.background.destroy();
|
this.background.destroy();
|
||||||
@ -148,9 +153,9 @@ export class CanvasManager {
|
|||||||
logDebugInfo() {
|
logDebugInfo() {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.log(this);
|
console.log(this);
|
||||||
for (const layer of this.rasterLayerAdapters.values()) {
|
for (const adapter of this.adapters.getAll()) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.log(layer);
|
console.log(adapter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,6 +27,7 @@ import type {
|
|||||||
import { imageDTOToImageObject } from 'features/controlLayers/store/types';
|
import { imageDTOToImageObject } from 'features/controlLayers/store/types';
|
||||||
import Konva from 'konva';
|
import Konva from 'konva';
|
||||||
import type { GroupConfig } from 'konva/lib/Group';
|
import type { GroupConfig } from 'konva/lib/Group';
|
||||||
|
import { atom } from 'nanostores';
|
||||||
import type { Logger } from 'roarr';
|
import type { Logger } from 'roarr';
|
||||||
import { getImageDTO, uploadImage } from 'services/api/endpoints/images';
|
import { getImageDTO, uploadImage } from 'services/api/endpoints/images';
|
||||||
import type { ImageDTO } from 'services/api/types';
|
import type { ImageDTO } from 'services/api/types';
|
||||||
@ -118,6 +119,8 @@ export class CanvasObjectRenderer {
|
|||||||
} | null;
|
} | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$canvasCache = atom<{ canvas: HTMLCanvasElement; rect: Rect } | null>(null);
|
||||||
|
|
||||||
constructor(parent: CanvasLayerAdapter | CanvasMaskAdapter) {
|
constructor(parent: CanvasLayerAdapter | CanvasMaskAdapter) {
|
||||||
this.id = getPrefixedId(this.type);
|
this.id = getPrefixedId(this.type);
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
@ -205,7 +208,10 @@ export class CanvasObjectRenderer {
|
|||||||
} else if (force || !this.konva.objectGroup.isCached()) {
|
} else if (force || !this.konva.objectGroup.isCached()) {
|
||||||
this.log.trace('Caching object group');
|
this.log.trace('Caching object group');
|
||||||
this.konva.objectGroup.clearCache();
|
this.konva.objectGroup.clearCache();
|
||||||
this.konva.objectGroup.cache();
|
this.konva.objectGroup.cache({ pixelRatio: 1 });
|
||||||
|
if (!this.parent.transformer.isPendingRectCalculation) {
|
||||||
|
this.parent.renderer.updatePreviewCanvas();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -530,6 +536,24 @@ export class CanvasObjectRenderer {
|
|||||||
return imageDTO;
|
return imageDTO;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
updatePreviewCanvas = () => {
|
||||||
|
if (this.parent.transformer.pixelRect.width === 0 || this.parent.transformer.pixelRect.height === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const canvas = this.konva.objectGroup._getCachedSceneCanvas()._canvas as HTMLCanvasElement | undefined | null;
|
||||||
|
if (canvas) {
|
||||||
|
const nodeRect = this.parent.transformer.nodeRect;
|
||||||
|
const pixelRect = this.parent.transformer.pixelRect;
|
||||||
|
const rect = {
|
||||||
|
x: pixelRect.x - nodeRect.x,
|
||||||
|
y: pixelRect.y - nodeRect.y,
|
||||||
|
width: pixelRect.width,
|
||||||
|
height: pixelRect.height,
|
||||||
|
};
|
||||||
|
this.$canvasCache.set({ rect, canvas });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
cloneObjectGroup = (attrs?: GroupConfig): Konva.Group => {
|
cloneObjectGroup = (attrs?: GroupConfig): Konva.Group => {
|
||||||
const clone = this.konva.objectGroup.clone();
|
const clone = this.konva.objectGroup.clone();
|
||||||
clone.cache();
|
clone.cache();
|
||||||
|
@ -68,25 +68,27 @@ export class CanvasRenderingModule {
|
|||||||
};
|
};
|
||||||
|
|
||||||
renderRasterLayers = async (state: CanvasV2State, prevState: CanvasV2State | null) => {
|
renderRasterLayers = async (state: CanvasV2State, prevState: CanvasV2State | null) => {
|
||||||
|
const adapterMap = this.manager.adapters.rasterLayers;
|
||||||
|
|
||||||
if (!prevState || state.rasterLayers.isHidden !== prevState.rasterLayers.isHidden) {
|
if (!prevState || state.rasterLayers.isHidden !== prevState.rasterLayers.isHidden) {
|
||||||
for (const adapter of this.manager.rasterLayerAdapters.values()) {
|
for (const adapter of adapterMap.values()) {
|
||||||
adapter.renderer.updateOpacity(state.rasterLayers.isHidden ? 0 : adapter.state.opacity);
|
adapter.renderer.updateOpacity(state.rasterLayers.isHidden ? 0 : adapter.state.opacity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!prevState || state.rasterLayers.entities !== prevState.rasterLayers.entities) {
|
if (!prevState || state.rasterLayers.entities !== prevState.rasterLayers.entities) {
|
||||||
for (const entityAdapter of this.manager.rasterLayerAdapters.values()) {
|
for (const entityAdapter of adapterMap.values()) {
|
||||||
if (!state.rasterLayers.entities.find((l) => l.id === entityAdapter.id)) {
|
if (!state.rasterLayers.entities.find((l) => l.id === entityAdapter.id)) {
|
||||||
await entityAdapter.destroy();
|
await entityAdapter.destroy();
|
||||||
this.manager.rasterLayerAdapters.delete(entityAdapter.id);
|
adapterMap.delete(entityAdapter.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const entityState of state.rasterLayers.entities) {
|
for (const entityState of state.rasterLayers.entities) {
|
||||||
let adapter = this.manager.rasterLayerAdapters.get(entityState.id);
|
let adapter = adapterMap.get(entityState.id);
|
||||||
if (!adapter) {
|
if (!adapter) {
|
||||||
adapter = new CanvasLayerAdapter(entityState, this.manager);
|
adapter = new CanvasLayerAdapter(entityState, this.manager);
|
||||||
this.manager.rasterLayerAdapters.set(adapter.id, adapter);
|
adapterMap.set(adapter.id, adapter);
|
||||||
this.manager.stage.addLayer(adapter.konva.layer);
|
this.manager.stage.addLayer(adapter.konva.layer);
|
||||||
}
|
}
|
||||||
await adapter.update({
|
await adapter.update({
|
||||||
@ -99,25 +101,27 @@ export class CanvasRenderingModule {
|
|||||||
};
|
};
|
||||||
|
|
||||||
renderControlLayers = async (prevState: CanvasV2State | null, state: CanvasV2State) => {
|
renderControlLayers = async (prevState: CanvasV2State | null, state: CanvasV2State) => {
|
||||||
|
const adapterMap = this.manager.adapters.controlLayers;
|
||||||
|
|
||||||
if (!prevState || state.controlLayers.isHidden !== prevState.controlLayers.isHidden) {
|
if (!prevState || state.controlLayers.isHidden !== prevState.controlLayers.isHidden) {
|
||||||
for (const adapter of this.manager.controlLayerAdapters.values()) {
|
for (const adapter of adapterMap.values()) {
|
||||||
adapter.renderer.updateOpacity(state.controlLayers.isHidden ? 0 : adapter.state.opacity);
|
adapter.renderer.updateOpacity(state.controlLayers.isHidden ? 0 : adapter.state.opacity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!prevState || state.controlLayers.entities !== prevState.controlLayers.entities) {
|
if (!prevState || state.controlLayers.entities !== prevState.controlLayers.entities) {
|
||||||
for (const entityAdapter of this.manager.controlLayerAdapters.values()) {
|
for (const entityAdapter of adapterMap.values()) {
|
||||||
if (!state.controlLayers.entities.find((l) => l.id === entityAdapter.id)) {
|
if (!state.controlLayers.entities.find((l) => l.id === entityAdapter.id)) {
|
||||||
await entityAdapter.destroy();
|
await entityAdapter.destroy();
|
||||||
this.manager.controlLayerAdapters.delete(entityAdapter.id);
|
adapterMap.delete(entityAdapter.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const entityState of state.controlLayers.entities) {
|
for (const entityState of state.controlLayers.entities) {
|
||||||
let adapter = this.manager.controlLayerAdapters.get(entityState.id);
|
let adapter = adapterMap.get(entityState.id);
|
||||||
if (!adapter) {
|
if (!adapter) {
|
||||||
adapter = new CanvasLayerAdapter(entityState, this.manager);
|
adapter = new CanvasLayerAdapter(entityState, this.manager);
|
||||||
this.manager.controlLayerAdapters.set(adapter.id, adapter);
|
adapterMap.set(adapter.id, adapter);
|
||||||
this.manager.stage.addLayer(adapter.konva.layer);
|
this.manager.stage.addLayer(adapter.konva.layer);
|
||||||
}
|
}
|
||||||
await adapter.update({
|
await adapter.update({
|
||||||
@ -130,8 +134,10 @@ export class CanvasRenderingModule {
|
|||||||
};
|
};
|
||||||
|
|
||||||
renderRegionalGuidance = async (prevState: CanvasV2State | null, state: CanvasV2State) => {
|
renderRegionalGuidance = async (prevState: CanvasV2State | null, state: CanvasV2State) => {
|
||||||
|
const adapterMap = this.manager.adapters.regionMasks;
|
||||||
|
|
||||||
if (!prevState || state.regions.isHidden !== prevState.regions.isHidden) {
|
if (!prevState || state.regions.isHidden !== prevState.regions.isHidden) {
|
||||||
for (const adapter of this.manager.regionalGuidanceAdapters.values()) {
|
for (const adapter of adapterMap.values()) {
|
||||||
adapter.renderer.updateOpacity(state.regions.isHidden ? 0 : adapter.state.opacity);
|
adapter.renderer.updateOpacity(state.regions.isHidden ? 0 : adapter.state.opacity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -143,18 +149,18 @@ export class CanvasRenderingModule {
|
|||||||
state.selectedEntityIdentifier?.id !== prevState.selectedEntityIdentifier?.id
|
state.selectedEntityIdentifier?.id !== prevState.selectedEntityIdentifier?.id
|
||||||
) {
|
) {
|
||||||
// Destroy the konva nodes for nonexistent entities
|
// Destroy the konva nodes for nonexistent entities
|
||||||
for (const canvasRegion of this.manager.regionalGuidanceAdapters.values()) {
|
for (const canvasRegion of adapterMap.values()) {
|
||||||
if (!state.regions.entities.find((rg) => rg.id === canvasRegion.id)) {
|
if (!state.regions.entities.find((rg) => rg.id === canvasRegion.id)) {
|
||||||
canvasRegion.destroy();
|
canvasRegion.destroy();
|
||||||
this.manager.regionalGuidanceAdapters.delete(canvasRegion.id);
|
adapterMap.delete(canvasRegion.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const entityState of state.regions.entities) {
|
for (const entityState of state.regions.entities) {
|
||||||
let adapter = this.manager.regionalGuidanceAdapters.get(entityState.id);
|
let adapter = adapterMap.get(entityState.id);
|
||||||
if (!adapter) {
|
if (!adapter) {
|
||||||
adapter = new CanvasMaskAdapter(entityState, this.manager);
|
adapter = new CanvasMaskAdapter(entityState, this.manager);
|
||||||
this.manager.regionalGuidanceAdapters.set(adapter.id, adapter);
|
adapterMap.set(adapter.id, adapter);
|
||||||
this.manager.stage.addLayer(adapter.konva.layer);
|
this.manager.stage.addLayer(adapter.konva.layer);
|
||||||
}
|
}
|
||||||
await adapter.update({
|
await adapter.update({
|
||||||
@ -167,8 +173,10 @@ export class CanvasRenderingModule {
|
|||||||
};
|
};
|
||||||
|
|
||||||
renderInpaintMasks = async (state: CanvasV2State, prevState: CanvasV2State | null) => {
|
renderInpaintMasks = async (state: CanvasV2State, prevState: CanvasV2State | null) => {
|
||||||
|
const adapterMap = this.manager.adapters.inpaintMasks;
|
||||||
|
|
||||||
if (!prevState || state.inpaintMasks.isHidden !== prevState.inpaintMasks.isHidden) {
|
if (!prevState || state.inpaintMasks.isHidden !== prevState.inpaintMasks.isHidden) {
|
||||||
for (const adapter of this.manager.inpaintMaskAdapters.values()) {
|
for (const adapter of adapterMap.values()) {
|
||||||
adapter.renderer.updateOpacity(state.inpaintMasks.isHidden ? 0 : adapter.state.opacity);
|
adapter.renderer.updateOpacity(state.inpaintMasks.isHidden ? 0 : adapter.state.opacity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -180,18 +188,18 @@ export class CanvasRenderingModule {
|
|||||||
state.selectedEntityIdentifier?.id !== prevState.selectedEntityIdentifier?.id
|
state.selectedEntityIdentifier?.id !== prevState.selectedEntityIdentifier?.id
|
||||||
) {
|
) {
|
||||||
// Destroy the konva nodes for nonexistent entities
|
// Destroy the konva nodes for nonexistent entities
|
||||||
for (const adapter of this.manager.inpaintMaskAdapters.values()) {
|
for (const adapter of adapterMap.values()) {
|
||||||
if (!state.inpaintMasks.entities.find((rg) => rg.id === adapter.id)) {
|
if (!state.inpaintMasks.entities.find((rg) => rg.id === adapter.id)) {
|
||||||
adapter.destroy();
|
adapter.destroy();
|
||||||
this.manager.inpaintMaskAdapters.delete(adapter.id);
|
adapterMap.delete(adapter.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const entityState of state.inpaintMasks.entities) {
|
for (const entityState of state.inpaintMasks.entities) {
|
||||||
let adapter = this.manager.inpaintMaskAdapters.get(entityState.id);
|
let adapter = adapterMap.get(entityState.id);
|
||||||
if (!adapter) {
|
if (!adapter) {
|
||||||
adapter = new CanvasMaskAdapter(entityState, this.manager);
|
adapter = new CanvasMaskAdapter(entityState, this.manager);
|
||||||
this.manager.inpaintMaskAdapters.set(adapter.id, adapter);
|
adapterMap.set(adapter.id, adapter);
|
||||||
this.manager.stage.addLayer(adapter.konva.layer);
|
this.manager.stage.addLayer(adapter.konva.layer);
|
||||||
}
|
}
|
||||||
await adapter.update({
|
await adapter.update({
|
||||||
@ -239,19 +247,19 @@ export class CanvasRenderingModule {
|
|||||||
this.manager.background.konva.layer.zIndex(++zIndex);
|
this.manager.background.konva.layer.zIndex(++zIndex);
|
||||||
|
|
||||||
for (const { id } of this.manager.stateApi.getRasterLayersState().entities) {
|
for (const { id } of this.manager.stateApi.getRasterLayersState().entities) {
|
||||||
this.manager.rasterLayerAdapters.get(id)?.konva.layer.zIndex(++zIndex);
|
this.manager.adapters.rasterLayers.get(id)?.konva.layer.zIndex(++zIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const { id } of this.manager.stateApi.getControlLayersState().entities) {
|
for (const { id } of this.manager.stateApi.getControlLayersState().entities) {
|
||||||
this.manager.controlLayerAdapters.get(id)?.konva.layer.zIndex(++zIndex);
|
this.manager.adapters.controlLayers.get(id)?.konva.layer.zIndex(++zIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const { id } of this.manager.stateApi.getRegionsState().entities) {
|
for (const { id } of this.manager.stateApi.getRegionsState().entities) {
|
||||||
this.manager.regionalGuidanceAdapters.get(id)?.konva.layer.zIndex(++zIndex);
|
this.manager.adapters.regionMasks.get(id)?.konva.layer.zIndex(++zIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const { id } of this.manager.stateApi.getInpaintMasksState().entities) {
|
for (const { id } of this.manager.stateApi.getInpaintMasksState().entities) {
|
||||||
this.manager.inpaintMaskAdapters.get(id)?.konva.layer.zIndex(++zIndex);
|
this.manager.adapters.inpaintMasks.get(id)?.konva.layer.zIndex(++zIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.manager.preview.getLayer().zIndex(++zIndex);
|
this.manager.preview.getLayer().zIndex(++zIndex);
|
||||||
|
@ -58,25 +58,7 @@ export class CanvasStageModule {
|
|||||||
getVisibleRect = (): Rect => {
|
getVisibleRect = (): Rect => {
|
||||||
const rects = [];
|
const rects = [];
|
||||||
|
|
||||||
for (const adapter of this.manager.inpaintMaskAdapters.values()) {
|
for (const adapter of this.manager.adapters.getAll()) {
|
||||||
if (adapter.state.isEnabled) {
|
|
||||||
rects.push(adapter.transformer.getRelativeRect());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const adapter of this.manager.rasterLayerAdapters.values()) {
|
|
||||||
if (adapter.state.isEnabled) {
|
|
||||||
rects.push(adapter.transformer.getRelativeRect());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const adapter of this.manager.controlLayerAdapters.values()) {
|
|
||||||
if (adapter.state.isEnabled) {
|
|
||||||
rects.push(adapter.transformer.getRelativeRect());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const adapter of this.manager.regionalGuidanceAdapters.values()) {
|
|
||||||
if (adapter.state.isEnabled) {
|
if (adapter.state.isEnabled) {
|
||||||
rects.push(adapter.transformer.getRelativeRect());
|
rects.push(adapter.transformer.getRelativeRect());
|
||||||
}
|
}
|
||||||
|
@ -174,16 +174,16 @@ export class CanvasStateApiModule {
|
|||||||
|
|
||||||
if (identifier.type === 'raster_layer') {
|
if (identifier.type === 'raster_layer') {
|
||||||
entityState = state.rasterLayers.entities.find((i) => i.id === identifier.id) ?? null;
|
entityState = state.rasterLayers.entities.find((i) => i.id === identifier.id) ?? null;
|
||||||
entityAdapter = this.manager.rasterLayerAdapters.get(identifier.id) ?? null;
|
entityAdapter = this.manager.adapters.rasterLayers.get(identifier.id) ?? null;
|
||||||
} else if (identifier.type === 'control_layer') {
|
} else if (identifier.type === 'control_layer') {
|
||||||
entityState = state.controlLayers.entities.find((i) => i.id === identifier.id) ?? null;
|
entityState = state.controlLayers.entities.find((i) => i.id === identifier.id) ?? null;
|
||||||
entityAdapter = this.manager.controlLayerAdapters.get(identifier.id) ?? null;
|
entityAdapter = this.manager.adapters.controlLayers.get(identifier.id) ?? null;
|
||||||
} else if (identifier.type === 'regional_guidance') {
|
} else if (identifier.type === 'regional_guidance') {
|
||||||
entityState = state.regions.entities.find((i) => i.id === identifier.id) ?? null;
|
entityState = state.regions.entities.find((i) => i.id === identifier.id) ?? null;
|
||||||
entityAdapter = this.manager.regionalGuidanceAdapters.get(identifier.id) ?? null;
|
entityAdapter = this.manager.adapters.regionMasks.get(identifier.id) ?? null;
|
||||||
} else if (identifier.type === 'inpaint_mask') {
|
} else if (identifier.type === 'inpaint_mask') {
|
||||||
entityState = state.inpaintMasks.entities.find((i) => i.id === identifier.id) ?? null;
|
entityState = state.inpaintMasks.entities.find((i) => i.id === identifier.id) ?? null;
|
||||||
entityAdapter = this.manager.inpaintMaskAdapters.get(identifier.id) ?? null;
|
entityAdapter = this.manager.adapters.inpaintMasks.get(identifier.id) ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entityState && entityAdapter) {
|
if (entityState && entityAdapter) {
|
||||||
|
@ -496,7 +496,7 @@ export class CanvasTransformer {
|
|||||||
startTransform = () => {
|
startTransform = () => {
|
||||||
this.log.debug('Starting transform');
|
this.log.debug('Starting transform');
|
||||||
this.isTransforming = true;
|
this.isTransforming = true;
|
||||||
this.manager.stateApi.setTool('move')
|
this.manager.stateApi.setTool('move');
|
||||||
// When transforming, we want the stage to still be movable if the view tool is selected. If the transformer or
|
// When transforming, we want the stage to still be movable if the view tool is selected. If the transformer or
|
||||||
// interaction rect are listening, it will interrupt the stage's drag events. So we should disable listening
|
// interaction rect are listening, it will interrupt the stage's drag events. So we should disable listening
|
||||||
// when the view tool is selected
|
// when the view tool is selected
|
||||||
@ -605,6 +605,7 @@ export class CanvasTransformer {
|
|||||||
|
|
||||||
if (this.isPendingRectCalculation) {
|
if (this.isPendingRectCalculation) {
|
||||||
this.syncInteractionState();
|
this.syncInteractionState();
|
||||||
|
this.parent.renderer.updatePreviewCanvas();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -615,6 +616,7 @@ export class CanvasTransformer {
|
|||||||
// The layer is fully transparent but has objects - reset it
|
// The layer is fully transparent but has objects - reset it
|
||||||
this.manager.stateApi.resetEntity({ entityIdentifier: this.parent.getEntityIdentifier() });
|
this.manager.stateApi.resetEntity({ entityIdentifier: this.parent.getEntityIdentifier() });
|
||||||
this.syncInteractionState();
|
this.syncInteractionState();
|
||||||
|
this.parent.renderer.updatePreviewCanvas();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -628,6 +630,7 @@ export class CanvasTransformer {
|
|||||||
};
|
};
|
||||||
this.parent.renderer.konva.objectGroup.setAttrs(groupAttrs);
|
this.parent.renderer.konva.objectGroup.setAttrs(groupAttrs);
|
||||||
this.parent.renderer.konva.bufferGroup.setAttrs(groupAttrs);
|
this.parent.renderer.konva.bufferGroup.setAttrs(groupAttrs);
|
||||||
|
this.parent.renderer.updatePreviewCanvas();
|
||||||
};
|
};
|
||||||
|
|
||||||
calculateRect = debounce(() => {
|
calculateRect = debounce(() => {
|
||||||
|
@ -23,7 +23,7 @@ export const addControlAdapters = async (
|
|||||||
.filter((layer) => isValidControlAdapter(layer.controlAdapter, base));
|
.filter((layer) => isValidControlAdapter(layer.controlAdapter, base));
|
||||||
|
|
||||||
for (const layer of validControlLayers) {
|
for (const layer of validControlLayers) {
|
||||||
const adapter = manager.controlLayerAdapters.get(layer.id);
|
const adapter = manager.adapters.controlLayers.get(layer.id);
|
||||||
assert(adapter, 'Adapter not found');
|
assert(adapter, 'Adapter not found');
|
||||||
const imageDTO = await adapter.renderer.rasterize({ rect: bbox, attrs: { opacity: 1, filters: [] } });
|
const imageDTO = await adapter.renderer.rasterize({ rect: bbox, attrs: { opacity: 1, filters: [] } });
|
||||||
if (layer.controlAdapter.type === 'controlnet') {
|
if (layer.controlAdapter.type === 'controlnet') {
|
||||||
|
@ -47,7 +47,7 @@ export const addRegions = async (
|
|||||||
const validRegions = regions.filter((rg) => isValidRegion(rg, base));
|
const validRegions = regions.filter((rg) => isValidRegion(rg, base));
|
||||||
|
|
||||||
for (const region of validRegions) {
|
for (const region of validRegions) {
|
||||||
const adapter = manager.regionalGuidanceAdapters.get(region.id);
|
const adapter = manager.adapters.regionMasks.get(region.id);
|
||||||
assert(adapter, 'Adapter not found');
|
assert(adapter, 'Adapter not found');
|
||||||
const imageDTO = await adapter.renderer.rasterize({ rect: bbox });
|
const imageDTO = await adapter.renderer.rasterize({ rect: bbox });
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user