修复角色图去背景读取失败
去背景前统一解析私有图片资源 裁扩本地编辑复用图片引用解析 补充角色图去背景回归测试
This commit is contained in:
@@ -22,6 +22,8 @@ const generateEditorSoundEffectMock = vi.hoisted(() => vi.fn());
|
||||
const generateEditorBackgroundMusicMock = vi.hoisted(() => vi.fn());
|
||||
const editEditorImageMock = vi.hoisted(() => vi.fn());
|
||||
const renderCropExpandImageMock = vi.hoisted(() => vi.fn());
|
||||
const removeImageBackgroundMock = vi.hoisted(() => vi.fn());
|
||||
const resolveEditorImageReferenceDataUrlMock = vi.hoisted(() => vi.fn());
|
||||
|
||||
vi.mock('../../services/image-editor/editorImageReference', async () => {
|
||||
const actual = await vi.importActual<
|
||||
@@ -29,7 +31,7 @@ vi.mock('../../services/image-editor/editorImageReference', async () => {
|
||||
>('../../services/image-editor/editorImageReference');
|
||||
return {
|
||||
...actual,
|
||||
resolveEditorImageReferenceDataUrl: vi.fn(async (src: string) => src),
|
||||
resolveEditorImageReferenceDataUrl: resolveEditorImageReferenceDataUrlMock,
|
||||
resolveEditorImageReferenceDataUrlForGeneration: vi.fn(
|
||||
async (src: string) => src,
|
||||
),
|
||||
@@ -57,6 +59,7 @@ vi.mock('./ImageCanvasRasterEditModel', async () => {
|
||||
>('./ImageCanvasRasterEditModel');
|
||||
return {
|
||||
...actual,
|
||||
removeImageBackground: removeImageBackgroundMock,
|
||||
renderCropExpandImage: renderCropExpandImageMock,
|
||||
};
|
||||
});
|
||||
@@ -390,6 +393,12 @@ function GenerationWorkflowHarness({
|
||||
<button type="button" onClick={() => void workflow.submitCropExpand()}>
|
||||
完成裁扩
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => void workflow.removeSelectedLayerBackground(layers[0]!)}
|
||||
>
|
||||
去除背景
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() =>
|
||||
@@ -716,6 +725,9 @@ function GenerationWorkflowHarness({
|
||||
describe('useImageCanvasGenerationWorkflow', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
resolveEditorImageReferenceDataUrlMock.mockImplementation(
|
||||
async (src: string) => src,
|
||||
);
|
||||
window.localStorage.clear();
|
||||
});
|
||||
|
||||
@@ -1326,15 +1338,15 @@ describe('useImageCanvasGenerationWorkflow', () => {
|
||||
expect(screen.getByTestId('quick-edit-selection').textContent).toBe(
|
||||
'layer-source:none:0',
|
||||
);
|
||||
fireEvent.click(screen.getByRole('button', { name: '选择快速编辑矩形框选' }));
|
||||
fireEvent.click(
|
||||
screen.getByRole('button', { name: '选择快速编辑矩形框选' }),
|
||||
);
|
||||
fireEvent.click(screen.getByRole('button', { name: '快速编辑框选' }));
|
||||
|
||||
expect(screen.getByTestId('quick-edit-selection').textContent).toBe(
|
||||
'layer-source:rect:1',
|
||||
);
|
||||
expect(screen.getByTestId('quick-edit').textContent).toBe(
|
||||
'-',
|
||||
);
|
||||
expect(screen.getByTestId('quick-edit').textContent).toBe('-');
|
||||
|
||||
fireEvent.click(
|
||||
screen.getByRole('button', { name: '选择快速编辑椭圆框选' }),
|
||||
@@ -1344,9 +1356,7 @@ describe('useImageCanvasGenerationWorkflow', () => {
|
||||
expect(screen.getByTestId('quick-edit-selection').textContent).toBe(
|
||||
'layer-source:ellipse:2',
|
||||
);
|
||||
expect(screen.getByTestId('quick-edit').textContent).toBe(
|
||||
'-',
|
||||
);
|
||||
expect(screen.getByTestId('quick-edit').textContent).toBe('-');
|
||||
});
|
||||
|
||||
it('submits quick edits beside the source and fits source plus result', async () => {
|
||||
@@ -1429,6 +1439,44 @@ describe('useImageCanvasGenerationWorkflow', () => {
|
||||
expect(screen.getByTestId('fit-count').textContent).toBe('1');
|
||||
});
|
||||
|
||||
it('resolves private character image before removing background', async () => {
|
||||
resolveEditorImageReferenceDataUrlMock.mockResolvedValueOnce(
|
||||
'data:image/png;base64,resolved-character',
|
||||
);
|
||||
removeImageBackgroundMock.mockResolvedValueOnce({
|
||||
imageSrc: 'data:image/png;base64,no-background',
|
||||
width: 512,
|
||||
height: 768,
|
||||
});
|
||||
render(
|
||||
<GenerationWorkflowHarness
|
||||
initialLayers={[
|
||||
createLayer({
|
||||
assetKind: 'character',
|
||||
objectKey: 'generated-character-drafts/editor/private.png',
|
||||
src: '/api/assets/read-url?objectKey=generated-character-drafts/editor/private.png',
|
||||
}),
|
||||
]}
|
||||
/>,
|
||||
);
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: '去除背景' }));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(resolveEditorImageReferenceDataUrlMock).toHaveBeenCalledWith(
|
||||
'generated-character-drafts/editor/private.png',
|
||||
);
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(removeImageBackgroundMock).toHaveBeenCalledWith(
|
||||
'data:image/png;base64,resolved-character',
|
||||
);
|
||||
});
|
||||
expect(screen.getByTestId('layers').textContent).toContain(
|
||||
'layer-source:源图:-:character',
|
||||
);
|
||||
});
|
||||
|
||||
it('opens UI design extraction as a mark selection state before submitting', () => {
|
||||
render(<GenerationWorkflowHarness />);
|
||||
|
||||
@@ -1456,9 +1504,7 @@ describe('useImageCanvasGenerationWorkflow', () => {
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: '打开快速编辑' }));
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('quick-edit').textContent).toBe(
|
||||
'-',
|
||||
);
|
||||
expect(screen.getByTestId('quick-edit').textContent).toBe('-');
|
||||
});
|
||||
fireEvent.click(screen.getByRole('button', { name: '打开修改状态' }));
|
||||
fireEvent.click(screen.getByRole('button', { name: '打开UI素材框选' }));
|
||||
|
||||
@@ -14,6 +14,7 @@ import type {
|
||||
EditorAssetSnapshot,
|
||||
EditorProjectSnapshot,
|
||||
} from '../../services/image-editor/editorProjectClient';
|
||||
import { resolveEditorImageReferenceDataUrl } from '../../services/image-editor/editorImageReference';
|
||||
import { resizeCropExpandFrame } from './ImageCanvasCropExpandModel';
|
||||
import type {
|
||||
CanvasGenerationDialogState,
|
||||
@@ -468,8 +469,9 @@ export function useImageCanvasGenerationWorkflow({
|
||||
useState<UiAssetExtractionState | null>(null);
|
||||
const [quickEditSelectionState, setQuickEditSelectionState] =
|
||||
useState<UiAssetExtractionState | null>(null);
|
||||
const quickEditSelectionStateRef =
|
||||
useRef<UiAssetExtractionState | null>(null);
|
||||
const quickEditSelectionStateRef = useRef<UiAssetExtractionState | null>(
|
||||
null,
|
||||
);
|
||||
const cropExpandPanelRef = useRef<CropExpandPanelState | null>(null);
|
||||
const cropExpandResizeDragRef = useRef<CropExpandResizeDragState | null>(
|
||||
null,
|
||||
@@ -902,7 +904,8 @@ export function useImageCanvasGenerationWorkflow({
|
||||
.find(
|
||||
(dialog) =>
|
||||
dialog.generatedLayerId === sourceLayer.id ||
|
||||
(!dialog.generatedLayerId && dialog.sourceLayerId === sourceLayer.id),
|
||||
(!dialog.generatedLayerId &&
|
||||
dialog.sourceLayerId === sourceLayer.id),
|
||||
);
|
||||
return createQuickEditPanelDraft(sourceLayer, {
|
||||
imageModel: sourceDialog?.imageModel,
|
||||
@@ -1202,8 +1205,11 @@ export function useImageCanvasGenerationWorkflow({
|
||||
status: 'processing',
|
||||
errorMessage: undefined,
|
||||
});
|
||||
const sourceImageSrc = await resolveEditorImageReferenceDataUrl(
|
||||
cropExpandSourceLayer.objectKey?.trim() || cropExpandSourceLayer.src,
|
||||
);
|
||||
const result = await renderCropExpandImage({
|
||||
source: cropExpandSourceLayer.src,
|
||||
source: sourceImageSrc,
|
||||
insets,
|
||||
});
|
||||
layerCounterRef.current += 1;
|
||||
@@ -1263,7 +1269,10 @@ export function useImageCanvasGenerationWorkflow({
|
||||
setCropExpandPanel(null);
|
||||
setQuickEditPanel(null);
|
||||
try {
|
||||
const result = await removeImageBackground(sourceLayer.src);
|
||||
const sourceImageSrc = await resolveEditorImageReferenceDataUrl(
|
||||
sourceLayer.objectKey?.trim() || sourceLayer.src,
|
||||
);
|
||||
const result = await removeImageBackground(sourceImageSrc);
|
||||
updateSourceLayer(sourceLayer.id, (layer) => ({
|
||||
...layer,
|
||||
src: result.imageSrc,
|
||||
@@ -1285,6 +1294,7 @@ export function useImageCanvasGenerationWorkflow({
|
||||
}
|
||||
},
|
||||
[
|
||||
resolveEditorImageReferenceDataUrl,
|
||||
setActiveSidebarPanel,
|
||||
setImageContextMenu,
|
||||
setMetadataLayer,
|
||||
@@ -1533,7 +1543,8 @@ export function useImageCanvasGenerationWorkflow({
|
||||
...currentState,
|
||||
tool: currentState.tool === tool ? null : tool,
|
||||
draftMark: null,
|
||||
status: currentState.status === 'failed' ? 'idle' : currentState.status,
|
||||
status:
|
||||
currentState.status === 'failed' ? 'idle' : currentState.status,
|
||||
errorMessage:
|
||||
currentState.status === 'failed'
|
||||
? undefined
|
||||
|
||||
Reference in New Issue
Block a user