- Implemented ComfyGenerateRequest type and associated functions for generating images using various architectures and modes. - Added functions for listing generation options and handling image uploads. - Created workflows for different generation modes including SDXL, Z-Image, Z-Image Turbo, and Anima. - Introduced GenerationJobStatus component to display the status of ongoing generation jobs. - Developed MaskControls for managing mask operations and displaying mask analysis. - Created palette items for tool selection, layer management, and generation settings.
153 lines
6.3 KiB
TypeScript
153 lines
6.3 KiB
TypeScript
import { useEffect } from "react";
|
|
import { DropHalf } from "@phosphor-icons/react";
|
|
import { commandIds } from "@commands/ids";
|
|
import type { ImageDocument } from "@core/document";
|
|
import type { AppStore } from "@editor/store";
|
|
import type { ChromaKeySettings } from "@editor/tools";
|
|
import type { SelectionState } from "@editor/state";
|
|
import { applyChromaKeyMask, previewChromaKey, resolveChromaKeyTarget } from "@operations/masks/chromaKey";
|
|
import { BottomControlColorPicker } from "./ColorPicker";
|
|
import { BottomControlDivider } from "./Divider";
|
|
import { BottomControlSlider } from "./Slider";
|
|
import { bottomControlFieldClass, bottomControlIconSlotClass, bottomControlLabelClass, bottomControlMenuClass } from "./styles";
|
|
|
|
export type ChromaKeyControlsProps = {
|
|
document: ImageDocument;
|
|
selection: SelectionState;
|
|
settings: ChromaKeySettings;
|
|
dispatch: AppStore["dispatch"];
|
|
};
|
|
|
|
export function ChromaKeyControls({ document, selection, settings, dispatch }: ChromaKeyControlsProps) {
|
|
const target = resolveChromaKeyTarget(document, selection);
|
|
|
|
useEffect(() => {
|
|
return () => {
|
|
dispatch(commandIds.toolSetBrushStrokePreview, undefined);
|
|
};
|
|
}, [dispatch]);
|
|
|
|
useEffect(() => {
|
|
let cancelled = false;
|
|
|
|
if (!target) {
|
|
dispatch(commandIds.toolSetBrushStrokePreview, undefined);
|
|
return;
|
|
}
|
|
|
|
void previewChromaKey(target.asset.source, target.asset.intrinsicSize.w, target.asset.intrinsicSize.h, settings).then((source) => {
|
|
if (cancelled) return;
|
|
dispatch(commandIds.toolSetBrushStrokePreview, { layerId: target.layer.id, assetId: target.asset.id, source });
|
|
});
|
|
|
|
return () => {
|
|
cancelled = true;
|
|
};
|
|
}, [dispatch, settings.choke, settings.color, settings.despeckle, settings.feather, settings.softness, settings.spill, settings.tolerance, target?.asset.id, target?.asset.source, target?.asset.intrinsicSize.w, target?.asset.intrinsicSize.h, target?.layer.id]);
|
|
|
|
return (
|
|
<div className={bottomControlMenuClass()}>
|
|
<span className={bottomControlIconSlotClass()} title="Chroma key">
|
|
<DropHalf size={24} weight="regular" />
|
|
</span>
|
|
<BottomControlDivider />
|
|
<label className={bottomControlFieldClass()}>
|
|
<span className={bottomControlLabelClass()}>Key</span>
|
|
<BottomControlColorPicker
|
|
value={settings.color}
|
|
aria-label="Chroma key color"
|
|
onValueChange={(color) => dispatch(commandIds.toolSetChromaKeySettings, { color })}
|
|
/>
|
|
</label>
|
|
<BottomControlDivider />
|
|
<label className={bottomControlFieldClass()}>
|
|
<span className={bottomControlLabelClass()}>Tol</span>
|
|
<BottomControlSlider
|
|
min={0}
|
|
max={255}
|
|
value={settings.tolerance}
|
|
className="w-36"
|
|
aria-label="Chroma key tolerance"
|
|
onValueChange={(tolerance) => dispatch(commandIds.toolSetChromaKeySettings, { tolerance })}
|
|
/>
|
|
<span className="w-10 text-right text-base text-white">{Math.round(settings.tolerance)}</span>
|
|
</label>
|
|
<BottomControlDivider />
|
|
<label className={bottomControlFieldClass()}>
|
|
<span className={bottomControlLabelClass()}>Soft</span>
|
|
<BottomControlSlider
|
|
min={0}
|
|
max={255}
|
|
value={settings.softness}
|
|
className="w-36"
|
|
aria-label="Chroma key edge softness"
|
|
onValueChange={(softness) => dispatch(commandIds.toolSetChromaKeySettings, { softness })}
|
|
/>
|
|
<span className="w-10 text-right text-base text-white">{Math.round(settings.softness)}</span>
|
|
</label>
|
|
<BottomControlDivider />
|
|
<label className={bottomControlFieldClass()}>
|
|
<span className={bottomControlLabelClass()}>Feather</span>
|
|
<BottomControlSlider
|
|
min={0}
|
|
max={20}
|
|
value={settings.feather}
|
|
className="w-32"
|
|
aria-label="Chroma key mask feather"
|
|
onValueChange={(feather) => dispatch(commandIds.toolSetChromaKeySettings, { feather })}
|
|
/>
|
|
<span className="w-8 text-right text-base text-white">{Math.round(settings.feather)}</span>
|
|
</label>
|
|
<BottomControlDivider />
|
|
<label className={bottomControlFieldClass()}>
|
|
<span className={bottomControlLabelClass()}>Choke</span>
|
|
<BottomControlSlider
|
|
min={-20}
|
|
max={20}
|
|
value={settings.choke}
|
|
className="w-32"
|
|
aria-label="Chroma key mask choke or expand"
|
|
onValueChange={(choke) => dispatch(commandIds.toolSetChromaKeySettings, { choke })}
|
|
/>
|
|
<span className="w-8 text-right text-base text-white">{Math.round(settings.choke)}</span>
|
|
</label>
|
|
<BottomControlDivider />
|
|
<label className={bottomControlFieldClass()}>
|
|
<span className={bottomControlLabelClass()}>Clean</span>
|
|
<BottomControlSlider
|
|
min={0}
|
|
max={20}
|
|
value={settings.despeckle}
|
|
className="w-32"
|
|
aria-label="Chroma key mask despeckle"
|
|
onValueChange={(despeckle) => dispatch(commandIds.toolSetChromaKeySettings, { despeckle })}
|
|
/>
|
|
<span className="w-8 text-right text-base text-white">{Math.round(settings.despeckle)}</span>
|
|
</label>
|
|
<BottomControlDivider />
|
|
<label className={bottomControlFieldClass()}>
|
|
<span className={bottomControlLabelClass()}>Spill</span>
|
|
<BottomControlSlider
|
|
min={0}
|
|
max={100}
|
|
value={settings.spill}
|
|
className="w-36"
|
|
aria-label="Chroma key spill suppression"
|
|
onValueChange={(spill) => dispatch(commandIds.toolSetChromaKeySettings, { spill })}
|
|
/>
|
|
<span className="w-10 text-right text-base text-white">{Math.round(settings.spill)}</span>
|
|
</label>
|
|
<BottomControlDivider />
|
|
<button
|
|
type="button"
|
|
className="rounded-full bg-white px-5 py-2 text-base font-medium text-black transition hover:bg-white/90 disabled:pointer-events-none disabled:opacity-35"
|
|
disabled={!target}
|
|
title={target ? "Create or update a layer mask from the chroma key" : "Select one image or raster layer to create a chroma key mask"}
|
|
onClick={() => target && void applyChromaKeyMask(target, settings, dispatch)}
|
|
>
|
|
{target?.maskAsset ? "Update Mask" : "Create Mask"}
|
|
</button>
|
|
</div>
|
|
);
|
|
}
|