mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
59 lines
1.9 KiB
TypeScript
59 lines
1.9 KiB
TypeScript
import type { FastifyInstance } from "fastify";
|
|
import sharp from "sharp";
|
|
import { z } from "zod";
|
|
import { resolveOutputFormat } from "../../lib/output-format.js";
|
|
import { createToolRoute } from "../tool-factory.js";
|
|
|
|
const hexColor = z.string().regex(/^#[0-9a-fA-F]{6}$/);
|
|
|
|
const settingsSchema = z.object({
|
|
shadow: hexColor.default("#1e3a8a"),
|
|
highlight: hexColor.default("#fbbf24"),
|
|
});
|
|
|
|
function parseHex(hex: string) {
|
|
return {
|
|
r: parseInt(hex.slice(1, 3), 16),
|
|
g: parseInt(hex.slice(3, 5), 16),
|
|
b: parseInt(hex.slice(5, 7), 16),
|
|
};
|
|
}
|
|
|
|
export function registerDuotone(app: FastifyInstance) {
|
|
createToolRoute(app, {
|
|
toolId: "duotone",
|
|
settingsSchema,
|
|
process: async (inputBuffer, settings, filename) => {
|
|
const a = parseHex(settings.shadow);
|
|
const b = parseHex(settings.highlight);
|
|
|
|
// Duotone math: output = shadow + (highlight - shadow) * luminance
|
|
// .linear(multipliers, offsets) with per-channel arrays
|
|
const multipliers = [(b.r - a.r) / 255, (b.g - a.g) / 255, (b.b - a.b) / 255];
|
|
const offsets = [a.r, a.g, a.b];
|
|
|
|
// Grayscale to single channel, then expand back to 3-channel sRGB
|
|
// so that .linear() can apply per-channel multipliers/offsets
|
|
const grayBuf = await sharp(inputBuffer)
|
|
.removeAlpha()
|
|
.grayscale()
|
|
.toColourspace("srgb")
|
|
.toBuffer();
|
|
|
|
const buf = await sharp(grayBuf).linear(multipliers, offsets).toBuffer();
|
|
|
|
const outputFormat = await resolveOutputFormat(inputBuffer, filename);
|
|
const buffer = await sharp(buf)
|
|
.toFormat(outputFormat.format, { quality: outputFormat.quality })
|
|
.toBuffer();
|
|
const base = filename.replace(/\.[^.]+$/, "");
|
|
const ext = outputFormat.extension;
|
|
return {
|
|
buffer,
|
|
filename: `${base}_duotone.${ext}`,
|
|
contentType: outputFormat.contentType,
|
|
};
|
|
},
|
|
});
|
|
}
|