This commit is contained in:
2026-04-19 20:33:18 +08:00
parent 692643136f
commit 67c584b4df
123 changed files with 11898 additions and 4082 deletions

View File

@@ -1,3 +1,4 @@
import { removeBackgroundFromRgba } from '../../../packages/shared/src/assets/chromaKey';
import {
AnimationState,
type Character,
@@ -718,71 +719,7 @@ function applyGreenScreenAlpha(
height: number,
) {
const imageData = context.getImageData(0, 0, width, height);
const pixels = imageData.data;
for (let index = 0; index < pixels.length; index += 4) {
const red = pixels[index] ?? 0;
const green = pixels[index + 1] ?? 0;
const blue = pixels[index + 2] ?? 0;
const alpha = pixels[index + 3] ?? 0;
const greenLead = green - Math.max(red, blue);
const greenRatio = green / Math.max(1, red + blue);
if (alpha === 0) {
continue;
}
if (green > 72 && greenLead > 20 && greenRatio > 0.72) {
let nextAlpha = Math.min(alpha, Math.max(0, 255 - greenLead * 6));
if (green > 120 && greenLead > 48 && greenRatio > 1.12) {
nextAlpha = 0;
}
pixels[index + 3] = nextAlpha;
if (nextAlpha > 0) {
pixels[index + 1] = Math.min(
green,
Math.max(red, blue) + Math.max(6, Math.round(greenLead * 0.18)),
);
}
}
}
for (let y = 0; y < height; y += 1) {
for (let x = 0; x < width; x += 1) {
const index = (y * width + x) * 4;
const alpha = pixels[index + 3] ?? 0;
if (alpha === 0) {
continue;
}
const red = pixels[index] ?? 0;
const green = pixels[index + 1] ?? 0;
const blue = pixels[index + 2] ?? 0;
const neighborAlphaValues = [
x > 0 ? (pixels[index - 1] ?? 255) : 255,
x + 1 < width ? (pixels[index + 7] ?? 255) : 255,
y > 0 ? (pixels[index - width * 4 + 3] ?? 255) : 255,
y + 1 < height ? (pixels[index + width * 4 + 3] ?? 255) : 255,
];
const touchesTransparentEdge = neighborAlphaValues.some(
(value) => value < 16,
);
if (!touchesTransparentEdge) {
continue;
}
if (green > Math.max(red, blue) + 4) {
pixels[index + 1] = Math.max(
Math.max(red, blue),
green - Math.round((green - Math.max(red, blue)) * 0.8),
);
}
}
}
removeBackgroundFromRgba(imageData.data, width, height);
context.putImageData(imageData, 0, 0);
}

View File

@@ -123,6 +123,7 @@ export type CharacterAnimationGenerationPayload = {
loop: boolean;
useChromaKey: boolean;
resolution: string;
ratio: string;
imageSequenceModel: string;
videoModel: string;
referenceVideoModel: string;

View File

@@ -0,0 +1,75 @@
const PROJECT_PIXEL_STYLE_REFERENCE_SOURCES = [
'/character/Sword Princess/Original/Hero/idle/Idle01.png',
'/character/Archer Hero/Original/Hero/idle/idle01.png',
'/character/Girl Hero 1/Original/Hero/Idle/Idle01.png',
'/character/Punch Hero 3/Original/Hero/Idle/Idle01.png',
'/character/Fighter 4/original/Hero/idle/idle01.png',
] as const;
function loadImageFromSource(source: string) {
return new Promise<HTMLImageElement>((resolve, reject) => {
const image = new Image();
image.crossOrigin = 'anonymous';
image.onload = () => resolve(image);
image.onerror = () => reject(new Error(`加载图片失败:${source}`));
image.src = source;
});
}
function drawContainedImage(
context: CanvasRenderingContext2D,
image: HTMLImageElement,
options: {
x: number;
y: number;
width: number;
height: number;
},
) {
const fitScale = Math.min(
options.width / image.width,
options.height / image.height,
);
const drawWidth = image.width * fitScale;
const drawHeight = image.height * fitScale;
const drawX = options.x + (options.width - drawWidth) / 2;
const drawY = options.y + (options.height - drawHeight) / 2;
context.drawImage(image, drawX, drawY, drawWidth, drawHeight);
}
export async function buildProjectPixelStyleReferenceBoard(
sources = PROJECT_PIXEL_STYLE_REFERENCE_SOURCES,
) {
const images = await Promise.all(
sources.map((source) => loadImageFromSource(source)),
);
const cols = 3;
const rows = 2;
const cellSize = 320;
const padding = 24;
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
if (!context) {
throw new Error('无法创建画布上下文');
}
canvas.width = cols * cellSize + padding * 2;
canvas.height = rows * cellSize + padding * 2;
context.fillStyle = '#f6f0dd';
context.fillRect(0, 0, canvas.width, canvas.height);
context.imageSmoothingEnabled = false;
images.forEach((image, index) => {
const colIndex = index % cols;
const rowIndex = Math.floor(index / cols);
drawContainedImage(context, image, {
x: padding + colIndex * cellSize,
y: padding + rowIndex * cellSize,
width: cellSize,
height: cellSize,
});
});
return canvas.toDataURL('image/png');
}