修复图片改造后二维清晰度降级问题 (#90)
前端按图层真实尺寸恢复图片改造比例与清晰度 后端在角色去背景后恢复生成原图像素尺寸 补充前后端二千像素尺寸回归测试 同步更新图片画布技术文档与项目决策记录 --------- Co-authored-by: MengHao <menghao@MengHaodeMacBook-Air.local> Reviewed-on: https://git.genarrative.world/git/GenarrativeAI/Genarrative/pulls/90 Reviewed-by: 段舒康 <kdletters@qq.com> Co-authored-by: 孟豪 <mh18530625731@163.com> Co-committed-by: 孟豪 <mh18530625731@163.com>
This commit was merged in pull request #90.
This commit is contained in:
@@ -1236,6 +1236,11 @@ describe('ImageCanvasEditorView generation integration', () => {
|
||||
{
|
||||
open: () => {
|
||||
fireEvent.click(screen.getByRole('button', { name: '生成图片' }));
|
||||
selectGenerationDimensions(
|
||||
screen.getByRole('dialog', { name: '生成图片' }),
|
||||
'1:1',
|
||||
'2K',
|
||||
);
|
||||
fireEvent.change(screen.getByLabelText('生成提示词'), {
|
||||
target: { value: '生成中的普通图片' },
|
||||
});
|
||||
@@ -1243,6 +1248,7 @@ describe('ImageCanvasEditorView generation integration', () => {
|
||||
},
|
||||
dialogName: '生成图片',
|
||||
frameLabel: '图像生成占位图',
|
||||
expectedFrameSize: { width: 2048, height: 2048 },
|
||||
},
|
||||
{
|
||||
open: () => {
|
||||
@@ -1268,10 +1274,16 @@ describe('ImageCanvasEditorView generation integration', () => {
|
||||
},
|
||||
dialogName: '生成规范',
|
||||
frameLabel: '规范生成占位图',
|
||||
expectedFrameSize: { width: 2048, height: 1152 },
|
||||
},
|
||||
{
|
||||
open: () => {
|
||||
fireEvent.click(screen.getByRole('button', { name: '生成角色形象' }));
|
||||
selectGenerationDimensions(
|
||||
screen.getByRole('dialog', { name: '生成角色形象' }),
|
||||
'2:3',
|
||||
'2K',
|
||||
);
|
||||
fireEvent.change(screen.getByLabelText('角色设定'), {
|
||||
target: { value: '生成中的角色形象' },
|
||||
});
|
||||
@@ -1279,6 +1291,7 @@ describe('ImageCanvasEditorView generation integration', () => {
|
||||
},
|
||||
dialogName: '生成角色形象',
|
||||
frameLabel: '角色生成占位图',
|
||||
expectedFrameSize: { width: 1365, height: 2048 },
|
||||
},
|
||||
] as const;
|
||||
|
||||
@@ -1298,6 +1311,12 @@ describe('ImageCanvasEditorView generation integration', () => {
|
||||
expect(frame.className).toContain(
|
||||
'image-canvas-editor__generation-frame--generating',
|
||||
);
|
||||
expect((frame as HTMLElement).style.width).toBe(
|
||||
`${testCase.expectedFrameSize.width}px`,
|
||||
);
|
||||
expect((frame as HTMLElement).style.height).toBe(
|
||||
`${testCase.expectedFrameSize.height}px`,
|
||||
);
|
||||
expect(within(frame).getByRole('status').textContent).toContain('生成中');
|
||||
|
||||
unmount();
|
||||
|
||||
@@ -144,7 +144,7 @@ describe('ImageCanvasGenerationDialogModel', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('creates character generation drafts with model dimensions and icon drafts with icon frame dimensions', () => {
|
||||
it('creates character and icon generation drafts with the selected model dimensions', () => {
|
||||
const canvasSize = { width: 960, height: 720 };
|
||||
const viewport = { x: 0, y: 0, scale: 1 };
|
||||
|
||||
@@ -183,12 +183,12 @@ describe('ImageCanvasGenerationDialogModel', () => {
|
||||
iconSpecReference: null,
|
||||
iconDescriptions: [],
|
||||
placeholder: {
|
||||
x: 300,
|
||||
y: 180,
|
||||
width: 360,
|
||||
height: 360,
|
||||
originalWidth: 512,
|
||||
originalHeight: 512,
|
||||
x: -32,
|
||||
y: -152,
|
||||
width: 1024,
|
||||
height: 1024,
|
||||
originalWidth: 1024,
|
||||
originalHeight: 1024,
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -233,10 +233,10 @@ describe('ImageCanvasGenerationDialogModel', () => {
|
||||
imageSize: '1K',
|
||||
uiDesignSpecReference: null,
|
||||
placeholder: {
|
||||
x: -32,
|
||||
y: 72,
|
||||
width: 1024,
|
||||
height: 576,
|
||||
x: -64,
|
||||
y: 56,
|
||||
width: 1088,
|
||||
height: 608,
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -538,6 +538,10 @@ describe('ImageCanvasGenerationDialogModel', () => {
|
||||
placeholder: {
|
||||
x: 472,
|
||||
y: 140,
|
||||
width: 1024,
|
||||
height: 768,
|
||||
originalWidth: 1024,
|
||||
originalHeight: 768,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -614,9 +618,58 @@ describe('ImageCanvasGenerationDialogModel', () => {
|
||||
imageModel: 'gpt-image-2',
|
||||
aspectRatio: '2:3',
|
||||
imageSize: '2K',
|
||||
placeholder: {
|
||||
x: 472,
|
||||
y: 140,
|
||||
width: 1376,
|
||||
height: 2048,
|
||||
originalWidth: 1376,
|
||||
originalHeight: 2048,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it.each([
|
||||
['character', 'character'],
|
||||
['icon', 'icon-spritesheet'],
|
||||
['ui-design', 'ui-design'],
|
||||
] as const)(
|
||||
'restores %s remodel placeholders to the selected 2K dimensions',
|
||||
(mode, assetKind) => {
|
||||
expect(
|
||||
createSameSourceGenerationDialogDraft({
|
||||
sourceLayer: createLayer({ assetKind }),
|
||||
canvasSize: { width: 960, height: 720 },
|
||||
viewport: { x: 0, y: 0, scale: 1 },
|
||||
sourceDialog: {
|
||||
id: `generation-dialog-${mode}`,
|
||||
mode,
|
||||
prompt: '来源生成器',
|
||||
status: 'idle',
|
||||
generatedLayerId: 'layer-source',
|
||||
imageModel: IMAGE_MODEL_GPT_IMAGE_2,
|
||||
aspectRatio: '2:3',
|
||||
imageSize: '2K',
|
||||
},
|
||||
mode: 'redraw',
|
||||
}),
|
||||
).toMatchObject({
|
||||
mode,
|
||||
imageModel: IMAGE_MODEL_GPT_IMAGE_2,
|
||||
aspectRatio: '2:3',
|
||||
imageSize: '2K',
|
||||
placeholder: {
|
||||
x: 472,
|
||||
y: 140,
|
||||
width: 1376,
|
||||
height: 2048,
|
||||
originalWidth: 1376,
|
||||
originalHeight: 2048,
|
||||
},
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
it('restores a plain generated layer from its own metadata', () => {
|
||||
const canvasSize = { width: 960, height: 720 };
|
||||
const viewport = { x: 0, y: 0, scale: 1 };
|
||||
@@ -672,6 +725,33 @@ describe('ImageCanvasGenerationDialogModel', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('preserves 2K dimensions when reopening a persisted character layer', () => {
|
||||
expect(
|
||||
createLayerGenerationDialogDraft({
|
||||
sourceLayer: createLayer({
|
||||
sourceType: 'generated',
|
||||
assetKind: 'character',
|
||||
model: IMAGE_MODEL_NANOBANANA2,
|
||||
originalWidth: 2048,
|
||||
originalHeight: 2048,
|
||||
generationInputs: {
|
||||
fields: [{ title: '角色设定', value: '香水师角色' }],
|
||||
references: [],
|
||||
},
|
||||
}),
|
||||
canvasSize: { width: 960, height: 720 },
|
||||
viewport: { x: 0, y: 0, scale: 1 },
|
||||
}),
|
||||
).toMatchObject({
|
||||
mode: 'character',
|
||||
prompt: '香水师角色',
|
||||
imageModel: IMAGE_MODEL_NANOBANANA2,
|
||||
aspectRatio: '1:1',
|
||||
imageSize: '2K',
|
||||
generatedLayerId: 'layer-source',
|
||||
});
|
||||
});
|
||||
|
||||
it('restores video and audio generators from media layer metadata', () => {
|
||||
const context = {
|
||||
canvasSize: { width: 960, height: 720 },
|
||||
|
||||
@@ -31,8 +31,6 @@ import {
|
||||
EDITOR_IMAGE_DIMENSION_OPTIONS,
|
||||
EDITOR_IMAGE_MODEL_OPTIONS,
|
||||
ICON_DESCRIPTION_LIMIT,
|
||||
ICON_FRAME_DISPLAY_SIZE,
|
||||
ICON_FRAME_ORIGINAL_SIZE,
|
||||
IMAGE_MODEL_GPT_IMAGE_2,
|
||||
inferEditorImageAspectRatio,
|
||||
inferEditorImageSizeLabel,
|
||||
@@ -40,6 +38,7 @@ import {
|
||||
PUBLICATION_FRAME_ORIGINAL_SIZE,
|
||||
resolveEditorImageGenerationPixelSize,
|
||||
resolveEditorVideoGenerationPixelSize,
|
||||
resizeGenerationPlaceholderToImageSelection,
|
||||
SPEC_FRAME_ORIGINAL_SIZE,
|
||||
} from './ImageCanvasGenerationModel';
|
||||
import { getPublicationMaterialsWorkflow } from './ImageCanvasPublicationMaterialsModel';
|
||||
@@ -298,6 +297,11 @@ export function createIconGenerationDialogDraft({
|
||||
const worldCenter = getViewportWorldCenter({ canvasSize, viewport });
|
||||
const normalizedImageModel = normalizeEditorImageModel(imageModel);
|
||||
const dimensionDefaults = resolveImageDimensionDefaults(normalizedImageModel);
|
||||
const placeholderSize = resolveEditorImageGenerationPixelSize({
|
||||
model: normalizedImageModel,
|
||||
aspectRatio: dimensionDefaults.aspectRatio,
|
||||
imageSize: dimensionDefaults.imageSize,
|
||||
});
|
||||
return {
|
||||
mode: 'icon',
|
||||
prompt: '',
|
||||
@@ -310,12 +314,12 @@ export function createIconGenerationDialogDraft({
|
||||
aspectRatio: dimensionDefaults.aspectRatio,
|
||||
imageSize: dimensionDefaults.imageSize,
|
||||
placeholder: {
|
||||
x: worldCenter.x - ICON_FRAME_DISPLAY_SIZE.width / 2,
|
||||
y: worldCenter.y - ICON_FRAME_DISPLAY_SIZE.height / 2,
|
||||
width: ICON_FRAME_DISPLAY_SIZE.width,
|
||||
height: ICON_FRAME_DISPLAY_SIZE.height,
|
||||
originalWidth: ICON_FRAME_ORIGINAL_SIZE.width,
|
||||
originalHeight: ICON_FRAME_ORIGINAL_SIZE.height,
|
||||
x: worldCenter.x - placeholderSize.width / 2,
|
||||
y: worldCenter.y - placeholderSize.height / 2,
|
||||
width: placeholderSize.width,
|
||||
height: placeholderSize.height,
|
||||
originalWidth: placeholderSize.width,
|
||||
originalHeight: placeholderSize.height,
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -706,8 +710,7 @@ export function createLayerGenerationDialogDraft({
|
||||
return {
|
||||
...draft,
|
||||
prompt: draft.prompt || sourceLayer.prompt?.trim() || '',
|
||||
imageModel:
|
||||
sourceDialog?.imageModel ?? sourceLayer.model ?? draft.imageModel,
|
||||
imageModel: sourceDialog?.imageModel ?? draft.imageModel,
|
||||
status: 'idle',
|
||||
composerOpen: true,
|
||||
generatedLayerId: sourceLayer.id,
|
||||
@@ -755,24 +758,47 @@ function restoreSharedImageOptions(
|
||||
sourceLayer: CanvasLayer,
|
||||
sourceDialog?: CanvasGenerationDialogState | null,
|
||||
): Omit<CanvasGenerationDialogState, 'id'> {
|
||||
return {
|
||||
const requestedModel = normalizeEditorImageModel(
|
||||
sourceDialog?.imageModel ?? sourceLayer.model ?? draft.imageModel,
|
||||
);
|
||||
const imageModel = EDITOR_IMAGE_MODEL_OPTIONS.some(
|
||||
(option) => option.value === requestedModel,
|
||||
)
|
||||
? requestedModel
|
||||
: DEFAULT_IMAGE_MODEL;
|
||||
const dimensionOptions =
|
||||
EDITOR_IMAGE_DIMENSION_OPTIONS[
|
||||
imageModel as keyof typeof EDITOR_IMAGE_DIMENSION_OPTIONS
|
||||
] ?? EDITOR_IMAGE_DIMENSION_OPTIONS[DEFAULT_IMAGE_MODEL];
|
||||
const supportedAspectRatios =
|
||||
dimensionOptions.aspectRatios as readonly string[];
|
||||
const supportedImageSizes = dimensionOptions.imageSizes as readonly string[];
|
||||
const inferredAspectRatio = inferEditorImageAspectRatio(
|
||||
sourceLayer.originalWidth,
|
||||
sourceLayer.originalHeight,
|
||||
);
|
||||
const inferredImageSize = inferEditorImageSizeLabel(
|
||||
sourceLayer.originalWidth,
|
||||
sourceLayer.originalHeight,
|
||||
);
|
||||
const preferredAspectRatio = sourceDialog?.aspectRatio ?? inferredAspectRatio;
|
||||
const preferredImageSize = sourceDialog?.imageSize ?? inferredImageSize;
|
||||
return resizeGenerationPlaceholderToImageSelection({
|
||||
...draft,
|
||||
imageModel: sourceDialog?.imageModel ?? draft.imageModel,
|
||||
aspectRatio:
|
||||
sourceDialog?.aspectRatio ??
|
||||
draft.aspectRatio ??
|
||||
inferEditorImageAspectRatio(
|
||||
sourceLayer.originalWidth,
|
||||
sourceLayer.originalHeight,
|
||||
),
|
||||
imageSize:
|
||||
sourceDialog?.imageSize ??
|
||||
draft.imageSize ??
|
||||
inferEditorImageSizeLabel(
|
||||
sourceLayer.originalWidth,
|
||||
sourceLayer.originalHeight,
|
||||
),
|
||||
};
|
||||
imageModel,
|
||||
aspectRatio: supportedAspectRatios.includes(preferredAspectRatio)
|
||||
? preferredAspectRatio
|
||||
: draft.aspectRatio && supportedAspectRatios.includes(draft.aspectRatio)
|
||||
? draft.aspectRatio
|
||||
: (dimensionOptions.aspectRatios[0] ?? '1:1'),
|
||||
imageSize: supportedImageSizes.includes(preferredImageSize)
|
||||
? preferredImageSize
|
||||
: draft.imageSize && supportedImageSizes.includes(draft.imageSize)
|
||||
? draft.imageSize
|
||||
: (dimensionOptions.imageSizes.find((size) => size === '1K') ??
|
||||
dimensionOptions.imageSizes[0] ??
|
||||
'1K'),
|
||||
});
|
||||
}
|
||||
|
||||
function resolveSpecTypeFromSourceLayer(
|
||||
|
||||
@@ -42,8 +42,10 @@ import {
|
||||
getGenerationFrameAriaLabel,
|
||||
getGenerationFrameLabel,
|
||||
IMAGE_MODEL_GPT_IMAGE_2,
|
||||
IMAGE_MODEL_NANOBANANA2,
|
||||
isQuickEditUnsupportedAssetKind,
|
||||
resolveCharacterAnimationSourceImageSrc,
|
||||
resolveEditorImageGenerationPixelSize,
|
||||
resolveImageGenerationErrorMessage,
|
||||
VIDEO_MODEL_KLING_3,
|
||||
VIDEO_MODEL_KLING_3_OMNI,
|
||||
@@ -101,6 +103,34 @@ describe('ImageCanvasGenerationModel', () => {
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
it.each([
|
||||
[IMAGE_MODEL_NANOBANANA2, '1K', '1:1', 1024, 1024],
|
||||
[IMAGE_MODEL_NANOBANANA2, '2K', '2:3', 1365, 2048],
|
||||
[IMAGE_MODEL_GPT_IMAGE_2, '1K', '1:1', 1024, 1024],
|
||||
[IMAGE_MODEL_GPT_IMAGE_2, '1K', '4:3', 1024, 768],
|
||||
[IMAGE_MODEL_GPT_IMAGE_2, '1K', '3:2', 1024, 688],
|
||||
[IMAGE_MODEL_GPT_IMAGE_2, '1K', '2:3', 688, 1024],
|
||||
[IMAGE_MODEL_GPT_IMAGE_2, '1K', '9:16', 608, 1088],
|
||||
[IMAGE_MODEL_GPT_IMAGE_2, '1K', '16:9', 1088, 608],
|
||||
[IMAGE_MODEL_GPT_IMAGE_2, '2K', '1:1', 2048, 2048],
|
||||
[IMAGE_MODEL_GPT_IMAGE_2, '2K', '4:3', 2048, 1536],
|
||||
[IMAGE_MODEL_GPT_IMAGE_2, '2K', '3:2', 2048, 1376],
|
||||
[IMAGE_MODEL_GPT_IMAGE_2, '2K', '2:3', 1376, 2048],
|
||||
[IMAGE_MODEL_GPT_IMAGE_2, '2K', '9:16', 1152, 2048],
|
||||
[IMAGE_MODEL_GPT_IMAGE_2, '2K', '16:9', 2048, 1152],
|
||||
] as const)(
|
||||
'%s 的 %s %s 占位尺寸与 provider 直接请求尺寸一致',
|
||||
(model, imageSize, aspectRatio, width, height) => {
|
||||
expect(
|
||||
resolveEditorImageGenerationPixelSize({
|
||||
model,
|
||||
aspectRatio,
|
||||
imageSize,
|
||||
}),
|
||||
).toEqual({ width, height });
|
||||
},
|
||||
);
|
||||
|
||||
it('所有生成按钮价格都从模型定价配置推导', () => {
|
||||
expect(calculateEditorImageModelPrice(DEFAULT_IMAGE_MODEL)).toBe(
|
||||
EDITOR_IMAGE_MODEL_MUD_POINT_CONFIG[DEFAULT_IMAGE_MODEL]['1K'],
|
||||
|
||||
@@ -173,7 +173,38 @@ function resolveImageFrameSizeFromRatio({
|
||||
};
|
||||
}
|
||||
|
||||
function alignImageDimensionUp(value: number, alignment = 16) {
|
||||
return Math.ceil(value / alignment) * alignment;
|
||||
}
|
||||
|
||||
function resolveGptImage2FrameSize({
|
||||
aspectRatio,
|
||||
imageSize,
|
||||
}: {
|
||||
aspectRatio: string | null | undefined;
|
||||
imageSize: string | null | undefined;
|
||||
}): EditorImageFrameSize {
|
||||
const requested = resolveImageFrameSizeFromRatio({ aspectRatio, imageSize });
|
||||
let width = requested.width;
|
||||
let height = requested.height;
|
||||
const pixels = width * height;
|
||||
|
||||
// 中文注释:gpt-image-2 要求总像素不少于 655360,且宽高均为 16 的倍数。
|
||||
// 这里只把用户选择的 K 档和比例转换为 provider 可直接生成的真实尺寸,不做生成后放大。
|
||||
if (pixels < 655_360) {
|
||||
const scale = Math.sqrt(655_360 / pixels);
|
||||
width *= scale;
|
||||
height *= scale;
|
||||
}
|
||||
|
||||
return {
|
||||
width: alignImageDimensionUp(width),
|
||||
height: alignImageDimensionUp(height),
|
||||
};
|
||||
}
|
||||
|
||||
export function resolveEditorImageGenerationPixelSize({
|
||||
model,
|
||||
aspectRatio,
|
||||
imageSize,
|
||||
}: {
|
||||
@@ -181,6 +212,9 @@ export function resolveEditorImageGenerationPixelSize({
|
||||
aspectRatio: string | null | undefined;
|
||||
imageSize: string | null | undefined;
|
||||
}): EditorImageFrameSize {
|
||||
if (model?.trim() === IMAGE_MODEL_GPT_IMAGE_2) {
|
||||
return resolveGptImage2FrameSize({ aspectRatio, imageSize });
|
||||
}
|
||||
return resolveImageFrameSizeFromRatio({ aspectRatio, imageSize });
|
||||
}
|
||||
|
||||
@@ -225,9 +259,9 @@ export function inferEditorImageSizeLabel(width: number, height: number) {
|
||||
return '1K';
|
||||
}
|
||||
|
||||
export function resizeGenerationPlaceholderToImageSelection(
|
||||
dialog: GenerateDialogState,
|
||||
): GenerateDialogState {
|
||||
export function resizeGenerationPlaceholderToImageSelection<
|
||||
T extends GenerateDialogState,
|
||||
>(dialog: T): T {
|
||||
if (!dialog.placeholder) {
|
||||
return dialog;
|
||||
}
|
||||
@@ -249,7 +283,7 @@ export function resizeGenerationPlaceholderToImageSelection(
|
||||
originalWidth: size.width,
|
||||
originalHeight: size.height,
|
||||
},
|
||||
};
|
||||
} as T;
|
||||
}
|
||||
|
||||
function resolveEvenVideoWidth(
|
||||
|
||||
@@ -1364,6 +1364,7 @@ describe('useImageCanvasGenerationSubmissionWorkflow', () => {
|
||||
);
|
||||
render(
|
||||
<SubmissionWorkflowHarness
|
||||
projectId="project-edit-size"
|
||||
initialDialog={{
|
||||
mode: 'edit',
|
||||
prompt: '',
|
||||
@@ -1384,6 +1385,16 @@ describe('useImageCanvasGenerationSubmissionWorkflow', () => {
|
||||
expect.objectContaining({
|
||||
prompt: '修改当前图片',
|
||||
sourceImageSrc: 'resource-source',
|
||||
canvasCompletion: expect.objectContaining({
|
||||
placeholder: {
|
||||
x: 472,
|
||||
y: 140,
|
||||
width: 1024,
|
||||
height: 768,
|
||||
originalWidth: 1024,
|
||||
originalHeight: 768,
|
||||
},
|
||||
}),
|
||||
}),
|
||||
);
|
||||
});
|
||||
@@ -2500,6 +2511,7 @@ describe('useImageCanvasGenerationSubmissionWorkflow', () => {
|
||||
});
|
||||
render(
|
||||
<SubmissionWorkflowHarness
|
||||
projectId="project-ui-extraction-size"
|
||||
initialLayers={[
|
||||
createLayer({
|
||||
assetKind: 'ui-design',
|
||||
@@ -2541,6 +2553,16 @@ describe('useImageCanvasGenerationSubmissionWorkflow', () => {
|
||||
segModel: 'birefnet',
|
||||
aspectRatio: '1:1',
|
||||
imageSize: '2K',
|
||||
canvasCompletion: expect.objectContaining({
|
||||
placeholder: {
|
||||
x: 472,
|
||||
y: 140,
|
||||
width: 2048,
|
||||
height: 2048,
|
||||
originalWidth: 2048,
|
||||
originalHeight: 2048,
|
||||
},
|
||||
}),
|
||||
generationInputs: expect.objectContaining({
|
||||
fields: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
|
||||
@@ -61,8 +61,6 @@ import {
|
||||
DEFAULT_EDITOR_GENERATION_BACKGROUND_COLOR,
|
||||
DEFAULT_IMAGE_MODEL,
|
||||
DEFAULT_VIDEO_MODEL,
|
||||
ICON_FRAME_DISPLAY_SIZE,
|
||||
ICON_FRAME_ORIGINAL_SIZE,
|
||||
inferEditorImageAspectRatio,
|
||||
inferEditorImageSizeLabel,
|
||||
isCanvasGenerationDialog,
|
||||
@@ -1085,13 +1083,18 @@ export function useImageCanvasGenerationSubmissionWorkflow({
|
||||
const extractionPlan = resolveUiAssetExtractionGenerationPlan(
|
||||
options.marks?.length ?? 0,
|
||||
);
|
||||
const extractionSize = resolveEditorImageGenerationPixelSize({
|
||||
model: options.model ?? DEFAULT_IMAGE_MODEL,
|
||||
aspectRatio: extractionPlan.aspectRatio,
|
||||
imageSize: extractionPlan.imageSize,
|
||||
});
|
||||
const extractionFrame = {
|
||||
x: sourceLayer.x + sourceLayer.width + 32,
|
||||
y: sourceLayer.y,
|
||||
width: ICON_FRAME_DISPLAY_SIZE.width,
|
||||
height: ICON_FRAME_DISPLAY_SIZE.height,
|
||||
originalWidth: ICON_FRAME_ORIGINAL_SIZE.width,
|
||||
originalHeight: ICON_FRAME_ORIGINAL_SIZE.height,
|
||||
width: extractionSize.width,
|
||||
height: extractionSize.height,
|
||||
originalWidth: extractionSize.width,
|
||||
originalHeight: extractionSize.height,
|
||||
};
|
||||
const generated = await runEditorGenerationWithWalletRefresh(
|
||||
extractEditorUiDesignAssets({
|
||||
@@ -1778,10 +1781,10 @@ export function useImageCanvasGenerationSubmissionWorkflow({
|
||||
buildRightSideCanvasCompletionPlaceholder(
|
||||
submissionPlan.sourceLayer,
|
||||
{
|
||||
width: 1024,
|
||||
height: 1024,
|
||||
originalWidth: 1024,
|
||||
originalHeight: 1024,
|
||||
width: submissionPlan.sourceLayer.originalWidth,
|
||||
height: submissionPlan.sourceLayer.originalHeight,
|
||||
originalWidth: submissionPlan.sourceLayer.originalWidth,
|
||||
originalHeight: submissionPlan.sourceLayer.originalHeight,
|
||||
},
|
||||
);
|
||||
const generated = await runEditorGenerationWithWalletRefresh(
|
||||
|
||||
Reference in New Issue
Block a user