Files
NianAIGC/lib/client/seedream-markup.ts
2026-08-25 14:06:04 +08:00

57 lines
2.1 KiB
TypeScript

import { materialContentUrl } from "@/lib/client/asset-urls";
import type { PromptMaterial } from "@/lib/prompt/assembler";
import type { SeedreamAnnotation } from "@/lib/seedream/creation";
const markerColor = "#2563eb";
export async function createSeedreamMarkedFile(
source: PromptMaterial,
annotations: SeedreamAnnotation[],
imageIndex: number
) {
const strokes = annotations.filter((annotation) => annotation.kind === "stroke" && annotation.points && annotation.points.length > 1);
if (!strokes.length) return null;
const response = await fetch(materialContentUrl(source), { credentials: "same-origin" });
if (!response.ok) throw new Error(`读取图${imageIndex}失败,无法合成蓝色标记`);
const bitmap = await createImageBitmap(await response.blob());
try {
const canvas = document.createElement("canvas");
canvas.width = bitmap.width;
canvas.height = bitmap.height;
const context = canvas.getContext("2d");
if (!context) throw new Error("当前浏览器无法创建标记画布");
context.drawImage(bitmap, 0, 0);
context.strokeStyle = markerColor;
context.fillStyle = markerColor;
context.lineCap = "round";
context.lineJoin = "round";
context.lineWidth = Math.max(5, Math.round(Math.min(bitmap.width, bitmap.height) * 0.012));
for (const stroke of strokes) {
const points = stroke.points || [];
if (!points.length) continue;
context.beginPath();
context.moveTo(points[0].x / 1000 * bitmap.width, points[0].y / 1000 * bitmap.height);
for (const point of points.slice(1)) {
context.lineTo(point.x / 1000 * bitmap.width, point.y / 1000 * bitmap.height);
}
context.stroke();
}
const blob = await canvasBlob(canvas, "image/png");
return new File([blob], `seedream-marked-image-${imageIndex}.png`, { type: "image/png" });
} finally {
bitmap.close();
}
}
function canvasBlob(canvas: HTMLCanvasElement, type: string) {
return new Promise<Blob>((resolve, reject) => {
canvas.toBlob((blob) => {
if (blob) resolve(blob);
else reject(new Error("生成标记图片失败"));
}, type);
});
}