ttrpg-tools/src/components/utils/image-tracer.ts

98 lines
2.3 KiB
TypeScript
Raw Normal View History

2026-03-15 18:45:20 +08:00
import { ImageTracer, type TraceData, type OutlinedArea, type SvgLineAttributes, Options } from "@image-tracer-ts/core";
2026-03-16 09:29:32 +08:00
//@ts-ignore
import {SVGLoader, SVGResult} from "three/examples/jsm/loaders/SVGLoader";
import {Color, ShapePath, Shape} from "three";
2026-03-15 18:45:20 +08:00
export interface TracedLayer {
id: string;
name: string;
2026-03-16 09:29:32 +08:00
color: Color;
paths: Shape[];
2026-03-15 18:45:20 +08:00
}
export interface PathData {
points: Point[];
isClosed: boolean;
}
export interface Point {
x: number;
y: number;
}
export interface TraceResult {
width: number;
height: number;
layers: TracedLayer[];
}
interface SvgPath {
color: string;
d: string;
path: PathData;
}
/**
*
* @param image -
* @param options -
* @returns
*/
export async function traceImage(
image: HTMLImageElement,
options?: Partial<Options>
): Promise<TraceResult> {
// 创建 canvas 来获取图片数据
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
if (!ctx) {
throw new Error("无法创建 canvas 上下文");
}
// 设置 canvas 尺寸为图片原始尺寸
canvas.width = image.naturalWidth || image.width;
canvas.height = image.naturalHeight || image.height;
// 绘制图片到 canvas
ctx.drawImage(image, 0, 0);
// 获取图片数据
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
// 默认配置 - 使用 detailed preset 作为基础
const defaultOptions: Partial<Options> = {
2026-03-16 09:34:15 +08:00
...Options.Presets.default,
2026-03-15 18:45:20 +08:00
numberOfColors: 8, // 限制颜色数量以控制图层数
2026-03-16 09:02:35 +08:00
minColorQuota: 0.01, // 降低最小颜色占比阈值
2026-03-15 18:45:20 +08:00
strokeWidth: 0, // 不需要描边
2026-03-16 09:02:35 +08:00
lineFilter: true,
2026-03-15 18:45:20 +08:00
...options,
};
// 创建追踪器
const tracer = new ImageTracer(defaultOptions);
// 执行追踪,返回 SVG 字符串
const svgString = tracer.traceImage(imageData);
// 解析 SVG 字符串
2026-03-16 09:29:32 +08:00
const loader = new SVGLoader();
const result = loader.parse(svgString) as SVGResult;
const paths: ShapePath[] = result.paths;
const layers: TracedLayer[] = paths.map((path, i,) => {
return {
id: `layer-${i}`,
name: `颜色层 ${i + 1}`,
color: path.color,
paths: SVGLoader.createShapes(path),
};
});
2026-03-15 18:45:20 +08:00
return {
width: canvas.width,
height: canvas.height,
layers,
};
2026-03-16 09:29:32 +08:00
}