Improve perceptual MARD color matching
This commit is contained in:
+138
-31
@@ -8,6 +8,22 @@ type BeadColor = {
|
|||||||
name: string;
|
name: string;
|
||||||
hex: string;
|
hex: string;
|
||||||
rgb: [number, number, number];
|
rgb: [number, number, number];
|
||||||
|
lab: Oklab;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Oklab = [number, number, number];
|
||||||
|
|
||||||
|
type ColorBin = {
|
||||||
|
key: number;
|
||||||
|
count: number;
|
||||||
|
rgb: [number, number, number];
|
||||||
|
lab: Oklab;
|
||||||
|
};
|
||||||
|
|
||||||
|
type ColorCluster = {
|
||||||
|
count: number;
|
||||||
|
rgb: [number, number, number];
|
||||||
|
lab: Oklab;
|
||||||
};
|
};
|
||||||
|
|
||||||
type Pixel = {
|
type Pixel = {
|
||||||
@@ -37,12 +53,32 @@ const MARD_SERIES_NAMES: Record<string, string> = {
|
|||||||
F: "红色系", G: "肤棕系", H: "黑白灰系", M: "莫兰迪系",
|
F: "红色系", G: "肤棕系", H: "黑白灰系", M: "莫兰迪系",
|
||||||
};
|
};
|
||||||
|
|
||||||
const PALETTE: BeadColor[] = MARD_221.map(([code, hex]) => ({
|
function rgbToOklab([red, green, blue]: [number, number, number]): Oklab {
|
||||||
code,
|
const linear = (value: number) => {
|
||||||
name: `MARD ${MARD_SERIES_NAMES[code[0]]}`,
|
const channel = value / 255;
|
||||||
hex,
|
return channel <= 0.04045 ? channel / 12.92 : ((channel + 0.055) / 1.055) ** 2.4;
|
||||||
rgb: [parseInt(hex.slice(1, 3), 16), parseInt(hex.slice(3, 5), 16), parseInt(hex.slice(5, 7), 16)],
|
};
|
||||||
}));
|
const r = linear(red);
|
||||||
|
const g = linear(green);
|
||||||
|
const b = linear(blue);
|
||||||
|
const l = Math.cbrt(0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b);
|
||||||
|
const m = Math.cbrt(0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b);
|
||||||
|
const s = Math.cbrt(0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b);
|
||||||
|
return [
|
||||||
|
0.2104542553 * l + 0.793617785 * m - 0.0040720468 * s,
|
||||||
|
1.9779984951 * l - 2.428592205 * m + 0.4505937099 * s,
|
||||||
|
0.0259040371 * l + 0.7827717662 * m - 0.808675766 * s,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
function oklabDistance(a: Oklab, b: Oklab) {
|
||||||
|
return Math.hypot(a[0] - b[0], a[1] - b[1], a[2] - b[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const PALETTE: BeadColor[] = MARD_221.map(([code, hex]) => {
|
||||||
|
const rgb: [number, number, number] = [parseInt(hex.slice(1, 3), 16), parseInt(hex.slice(3, 5), 16), parseInt(hex.slice(5, 7), 16)];
|
||||||
|
return { code, name: `MARD ${MARD_SERIES_NAMES[code[0]]}`, hex, rgb, lab: rgbToOklab(rgb) };
|
||||||
|
});
|
||||||
|
|
||||||
const hexToRgb = (hex: string): [number, number, number] => [
|
const hexToRgb = (hex: string): [number, number, number] => [
|
||||||
parseInt(hex.slice(1, 3), 16),
|
parseInt(hex.slice(1, 3), 16),
|
||||||
@@ -50,19 +86,11 @@ const hexToRgb = (hex: string): [number, number, number] => [
|
|||||||
parseInt(hex.slice(5, 7), 16),
|
parseInt(hex.slice(5, 7), 16),
|
||||||
];
|
];
|
||||||
|
|
||||||
function colorDistance(a: [number, number, number], b: [number, number, number]) {
|
function nearestColor(lab: Oklab, palette: BeadColor[]) {
|
||||||
const meanR = (a[0] + b[0]) / 2;
|
|
||||||
const r = a[0] - b[0];
|
|
||||||
const g = a[1] - b[1];
|
|
||||||
const blue = a[2] - b[2];
|
|
||||||
return (2 + meanR / 256) * r * r + 4 * g * g + (2 + (255 - meanR) / 256) * blue * blue;
|
|
||||||
}
|
|
||||||
|
|
||||||
function nearestColor(rgb: [number, number, number], palette: BeadColor[]) {
|
|
||||||
let closest = palette[0];
|
let closest = palette[0];
|
||||||
let smallest = Number.POSITIVE_INFINITY;
|
let smallest = Number.POSITIVE_INFINITY;
|
||||||
for (const color of palette) {
|
for (const color of palette) {
|
||||||
const distance = colorDistance(rgb, color.rgb);
|
const distance = oklabDistance(lab, color.lab);
|
||||||
if (distance < smallest) {
|
if (distance < smallest) {
|
||||||
smallest = distance;
|
smallest = distance;
|
||||||
closest = color;
|
closest = color;
|
||||||
@@ -71,6 +99,95 @@ function nearestColor(rgb: [number, number, number], palette: BeadColor[]) {
|
|||||||
return closest;
|
return closest;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function mergePerceptualColors(data: Uint8ClampedArray) {
|
||||||
|
const pixelKeys: number[] = [];
|
||||||
|
const histogram = new Map<number, { count: number; red: number; green: number; blue: number }>();
|
||||||
|
for (let index = 0; index < data.length; index += 4) {
|
||||||
|
const red = data[index];
|
||||||
|
const green = data[index + 1];
|
||||||
|
const blue = data[index + 2];
|
||||||
|
const key = (red >> 3) << 10 | (green >> 3) << 5 | (blue >> 3);
|
||||||
|
pixelKeys.push(key);
|
||||||
|
const bin = histogram.get(key);
|
||||||
|
if (bin) {
|
||||||
|
bin.count += 1;
|
||||||
|
bin.red += red;
|
||||||
|
bin.green += green;
|
||||||
|
bin.blue += blue;
|
||||||
|
} else {
|
||||||
|
histogram.set(key, { count: 1, red, green, blue });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const bins: ColorBin[] = [...histogram.entries()].map(([key, value]) => {
|
||||||
|
const rgb: [number, number, number] = [value.red / value.count, value.green / value.count, value.blue / value.count];
|
||||||
|
return { key, count: value.count, rgb, lab: rgbToOklab(rgb) };
|
||||||
|
}).sort((a, b) => b.count - a.count);
|
||||||
|
|
||||||
|
const clusters: ColorCluster[] = [];
|
||||||
|
const mergeThreshold = 0.025;
|
||||||
|
for (const bin of bins) {
|
||||||
|
let nearestIndex = -1;
|
||||||
|
let nearestDistance = Number.POSITIVE_INFINITY;
|
||||||
|
for (let index = 0; index < clusters.length; index++) {
|
||||||
|
const distance = oklabDistance(bin.lab, clusters[index].lab);
|
||||||
|
if (distance < nearestDistance) {
|
||||||
|
nearestDistance = distance;
|
||||||
|
nearestIndex = index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (nearestIndex >= 0 && nearestDistance <= mergeThreshold) {
|
||||||
|
const cluster = clusters[nearestIndex];
|
||||||
|
const total = cluster.count + bin.count;
|
||||||
|
cluster.rgb = cluster.rgb.map((value, channel) => (value * cluster.count + bin.rgb[channel] * bin.count) / total) as [number, number, number];
|
||||||
|
cluster.lab = cluster.lab.map((value, channel) => (value * cluster.count + bin.lab[channel] * bin.count) / total) as Oklab;
|
||||||
|
cluster.count = total;
|
||||||
|
} else {
|
||||||
|
clusters.push({ count: bin.count, rgb: [...bin.rgb], lab: [...bin.lab] });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const binClusters = new Map<number, number>();
|
||||||
|
for (const bin of bins) {
|
||||||
|
let nearestIndex = 0;
|
||||||
|
let nearestDistance = Number.POSITIVE_INFINITY;
|
||||||
|
clusters.forEach((cluster, index) => {
|
||||||
|
const distance = oklabDistance(bin.lab, cluster.lab);
|
||||||
|
if (distance < nearestDistance) {
|
||||||
|
nearestDistance = distance;
|
||||||
|
nearestIndex = index;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
binClusters.set(bin.key, nearestIndex);
|
||||||
|
}
|
||||||
|
return { pixelKeys, clusters, binClusters };
|
||||||
|
}
|
||||||
|
|
||||||
|
function chooseDistinctMardColors(clusters: ColorCluster[], maximum: number) {
|
||||||
|
const candidates = new Map<string, { color: BeadColor; count: number; error: number; score: number }>();
|
||||||
|
for (const cluster of clusters) {
|
||||||
|
const color = nearestColor(cluster.lab, PALETTE);
|
||||||
|
const error = oklabDistance(cluster.lab, color.lab);
|
||||||
|
const chroma = Math.hypot(cluster.lab[1], cluster.lab[2]);
|
||||||
|
const detailBonus = 1 + Math.min(0.65, chroma * 2.2) + (cluster.lab[0] < 0.28 ? 0.35 : 0);
|
||||||
|
const current = candidates.get(color.code) ?? { color, count: 0, error: 0, score: 0 };
|
||||||
|
current.count += cluster.count;
|
||||||
|
current.error += error * cluster.count;
|
||||||
|
current.score += cluster.count * detailBonus / (1 + error * 5);
|
||||||
|
candidates.set(color.code, current);
|
||||||
|
}
|
||||||
|
|
||||||
|
const separated: BeadColor[] = [];
|
||||||
|
const minimumMardDistance = 0.035;
|
||||||
|
const ranked = [...candidates.values()].sort((a, b) => b.score - a.score || a.error / a.count - b.error / b.count);
|
||||||
|
for (const candidate of ranked) {
|
||||||
|
if (separated.every((selected) => oklabDistance(candidate.color.lab, selected.lab) >= minimumMardDistance)) {
|
||||||
|
separated.push(candidate.color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return separated.slice(0, Math.max(1, Math.min(maximum, separated.length)));
|
||||||
|
}
|
||||||
|
|
||||||
function drawFittedImage(
|
function drawFittedImage(
|
||||||
ctx: CanvasRenderingContext2D,
|
ctx: CanvasRenderingContext2D,
|
||||||
image: HTMLImageElement,
|
image: HTMLImageElement,
|
||||||
@@ -192,27 +309,17 @@ export default function Home() {
|
|||||||
ctx.fillRect(0, 0, width, height);
|
ctx.fillRect(0, 0, width, height);
|
||||||
drawFittedImage(ctx, image, width, height, fitMode, crop.zoom, crop.x, crop.y);
|
drawFittedImage(ctx, image, width, height, fitMode, crop.zoom, crop.x, crop.y);
|
||||||
const data = ctx.getImageData(0, 0, width, height).data;
|
const data = ctx.getImageData(0, 0, width, height).data;
|
||||||
const initialMatches: BeadColor[] = [];
|
const { pixelKeys, clusters, binClusters } = mergePerceptualColors(data);
|
||||||
const counts = new Map<string, number>();
|
const limitedPalette = chooseDistinctMardColors(clusters, Math.max(2, Math.min(colorLimit, PALETTE.length)));
|
||||||
for (let i = 0; i < data.length; i += 4) {
|
const clusterMatches = clusters.map((cluster) => nearestColor(cluster.lab, limitedPalette));
|
||||||
const match = nearestColor([data[i], data[i + 1], data[i + 2]], PALETTE);
|
const converted = pixelKeys.map((key) => ({ color: clusterMatches[binClusters.get(key) ?? 0] }));
|
||||||
initialMatches.push(match);
|
|
||||||
counts.set(match.code, (counts.get(match.code) ?? 0) + 1);
|
|
||||||
}
|
|
||||||
const limitedPalette = [...PALETTE]
|
|
||||||
.sort((a, b) => (counts.get(b.code) ?? 0) - (counts.get(a.code) ?? 0))
|
|
||||||
.slice(0, Math.max(2, Math.min(colorLimit, PALETTE.length)));
|
|
||||||
const converted = initialMatches.map((match, index) => {
|
|
||||||
const rgb: [number, number, number] = [data[index * 4], data[index * 4 + 1], data[index * 4 + 2]];
|
|
||||||
return { color: limitedPalette.some((c) => c.code === match.code) ? match : nearestColor(rgb, limitedPalette) };
|
|
||||||
});
|
|
||||||
setGridWidth(width);
|
setGridWidth(width);
|
||||||
setGridHeight(height);
|
setGridHeight(height);
|
||||||
setRequestedWidth(width);
|
setRequestedWidth(width);
|
||||||
setRequestedHeight(height);
|
setRequestedHeight(height);
|
||||||
setPixels(converted);
|
setPixels(converted);
|
||||||
setSelectedCodes(new Set());
|
setSelectedCodes(new Set());
|
||||||
setStatus(`已转换为 ${width} × ${height},共 ${width * height} 颗拼豆`);
|
setStatus(`已转换为 ${width} × ${height},共 ${width * height} 颗拼豆 · ${limitedPalette.length} 种差异色`);
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
"name": "rolldown-runtime"
|
"name": "rolldown-runtime"
|
||||||
},
|
},
|
||||||
"app/page.tsx": {
|
"app/page.tsx": {
|
||||||
"file": "_next/static/chunks/page-CXB_pn8J.js",
|
"file": "_next/static/chunks/page-CtIp9BI2.js",
|
||||||
"name": "page",
|
"name": "page",
|
||||||
"src": "app/page.tsx",
|
"src": "app/page.tsx",
|
||||||
"isDynamicEntry": true,
|
"isDynamicEntry": true,
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/vinext/dist/shims/layout-segment-context.js": {
|
"node_modules/vinext/dist/shims/layout-segment-context.js": {
|
||||||
"file": "_next/static/chunks/layout-segment-context-CaHt7o9U.js",
|
"file": "_next/static/chunks/layout-segment-context-B9Qg92Wu.js",
|
||||||
"name": "layout-segment-context",
|
"name": "layout-segment-context",
|
||||||
"src": "node_modules/vinext/dist/shims/layout-segment-context.js",
|
"src": "node_modules/vinext/dist/shims/layout-segment-context.js",
|
||||||
"isDynamicEntry": true,
|
"isDynamicEntry": true,
|
||||||
@@ -32,7 +32,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"virtual:vinext-app-browser-entry": {
|
"virtual:vinext-app-browser-entry": {
|
||||||
"file": "_next/static/chunks/index-Dctq4oWR.js",
|
"file": "_next/static/chunks/index-vlaCVOft.js",
|
||||||
"name": "index",
|
"name": "index",
|
||||||
"src": "virtual:vinext-app-browser-entry",
|
"src": "virtual:vinext-app-browser-entry",
|
||||||
"isEntry": true,
|
"isEntry": true,
|
||||||
|
|||||||
+2
-2
@@ -1,2 +1,2 @@
|
|||||||
<!DOCTYPE html><html lang="zh-CN"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="/_next/static/css/index.miWu1gnb.css" data-rsc-css-href="/_next/static/css/index.miWu1gnb.css" data-precedence="vite-rsc/importer-resources"/><link rel="modulepreload" fetchPriority="low" href="/_next/static/chunks/index-Dctq4oWR.js"/><script src="/_next/static/chunks/rolldown-runtime-C60lm6uB.js" type="module" async=""></script><script src="/_next/static/chunks/framework-BgSIrAUN.js" type="module" async=""></script><meta name="robots" content="noindex"/><title>豆格工坊|图片转拼豆图纸</title><meta name="description" content="在浏览器中把图片转换为拼豆像素图纸,按色号、名称或像素格筛选颜色。"/><title>404: This page could not be found.</title><script>Object.assign(((self[Symbol.for("vinext.navigationRuntime")]??={bootstrap:{routeManifest:null},functions:{}}).bootstrap.rsc??={rsc:[]}),{params:{},nav:{"pathname":"/__vinext_nonexistent_for_404__","searchParams":[]}})</script><link rel="modulepreload" href="/_next/static/chunks/index-Dctq4oWR.js" />
|
<!DOCTYPE html><html lang="zh-CN"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="/_next/static/css/index.miWu1gnb.css" data-rsc-css-href="/_next/static/css/index.miWu1gnb.css" data-precedence="vite-rsc/importer-resources"/><link rel="modulepreload" fetchPriority="low" href="/_next/static/chunks/index-vlaCVOft.js"/><script src="/_next/static/chunks/rolldown-runtime-C60lm6uB.js" type="module" async=""></script><script src="/_next/static/chunks/framework-BgSIrAUN.js" type="module" async=""></script><meta name="robots" content="noindex"/><title>豆格工坊|图片转拼豆图纸</title><meta name="description" content="在浏览器中把图片转换为拼豆像素图纸,按色号、名称或像素格筛选颜色。"/><title>404: This page could not be found.</title><script>Object.assign(((self[Symbol.for("vinext.navigationRuntime")]??={bootstrap:{routeManifest:null},functions:{}}).bootstrap.rsc??={rsc:[]}),{params:{},nav:{"pathname":"/__vinext_nonexistent_for_404__","searchParams":[]}})</script><link rel="modulepreload" href="/_next/static/chunks/index-vlaCVOft.js" />
|
||||||
</head><body><div style="font-family:system-ui,"Segoe UI",Roboto,Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji";height:100vh;text-align:center;display:flex;flex-direction:column;align-items:center;justify-content:center"><div><style>body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}</style><h1 class="next-error-h1" style="display:inline-block;margin:0 20px 0 0;padding:0 23px 0 0;font-size:24px;font-weight:500;vertical-align:top;line-height:49px">404</h1><div style="display:inline-block"><h2 style="font-size:14px;font-weight:400;line-height:49px;margin:0">This page could not be found.</h2></div></div></div><script type="module" src="/_next/static/chunks/index-Dctq4oWR.js" id="_R_" async=""></script><script>((self[Symbol.for("vinext.navigationRuntime")]??={bootstrap:{routeManifest:null},functions:{}}).bootstrap.rsc??={rsc:[]}).rsc.push("1:\"$Sreact.fragment\"\n")</script><script>((self[Symbol.for("vinext.navigationRuntime")]??={bootstrap:{routeManifest:null},functions:{}}).bootstrap.rsc??={rsc:[]}).rsc.push(":HL[\"/_next/static/css/index.miWu1gnb.css\",\"style\" ]\n")</script><script>((self[Symbol.for("vinext.navigationRuntime")]??={bootstrap:{routeManifest:null},functions:{}}).bootstrap.rsc??={rsc:[]}).rsc.push("0:{\"__route\":\"route:/__vinext_nonexistent_for_404__\",\"__interceptionContext\":null,\"__layoutIds\":[],\"__rootLayout\":null,\"route:/__vinext_nonexistent_for_404__\":[[[\"$\",\"link\",\"css:/_next/static/css/index.miWu1gnb.css\",{\"rel\":\"stylesheet\",\"precedence\":\"vite-rsc/importer-resources\",\"href\":\"/_next/static/css/index.miWu1gnb.css\",\"data-rsc-css-href\":\"/_next/static/css/index.miWu1gnb.css\"}],\"$undefined\"],[\"$\",\"html\",null,{\"lang\":\"zh-CN\",\"children\":[\"$\",\"body\",null,{\"children\":[[\"$\",\"meta\",\"charset\",{\"charSet\":\"utf-8\"}],[\"$\",\"meta\",\"robots\",{\"name\":\"robots\",\"content\":\"noindex\"}],[\"$\",\"$1\",\"metadata\",{\"children\":[[\"$\",\"title\",\"0\",{\"children\":\"豆格工坊|图片转拼豆图纸\"}],[\"$\",\"meta\",\"1\",{\"name\":\"description\",\"content\":\"在浏览器中把图片转换为拼豆像素图纸,按色号、名称或像素格筛选颜色。\"}]]}],[\"$\",\"$1\",\"viewport\",{\"children\":[[\"$\",\"meta\",\"0\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}]]}],[[\"$\",\"title\",null,{\"children\":\"404: This page could not be found.\"}],[\"$\",\"div\",null,{\"style\":{\"fontFamily\":\"system-ui,\\\"Segoe UI\\\",Roboto,Helvetica,Arial,sans-serif,\\\"Apple Color Emoji\\\",\\\"Segoe UI Emoji\\\"\",\"height\":\"100vh\",\"textAlign\":\"center\",\"display\":\"flex\",\"flexDirection\":\"column\",\"alignItems\":\"center\",\"justifyContent\":\"center\"},\"children\":[\"$\",\"div\",null,{\"children\":[[\"$\",\"style\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}\"}}],[\"$\",\"h1\",null,{\"className\":\"next-error-h1\",\"style\":{\"display\":\"inline-block\",\"margin\":\"0 20px 0 0\",\"padding\":\"0 23px 0 0\",\"fontSize\":24,\"fontWeight\":500,\"verticalAlign\":\"top\",\"lineHeight\":\"49px\"},\"children\":404}],[\"$\",\"div\",null,{\"style\":{\"display\":\"inline-block\"},\"children\":[\"$\",\"h2\",null,{\"style\":{\"fontSize\":14,\"fontWeight\":400,\"lineHeight\":\"49px\",\"margin\":0},\"children\":\"This page could not be found.\"}]}]]}]}]]]}]}]]}\n")</script><script>((self[Symbol.for("vinext.navigationRuntime")]??={bootstrap:{routeManifest:null},functions:{}}).bootstrap.rsc??={rsc:[]}).done=true</script></body></html>
|
</head><body><div style="font-family:system-ui,"Segoe UI",Roboto,Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji";height:100vh;text-align:center;display:flex;flex-direction:column;align-items:center;justify-content:center"><div><style>body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}</style><h1 class="next-error-h1" style="display:inline-block;margin:0 20px 0 0;padding:0 23px 0 0;font-size:24px;font-weight:500;vertical-align:top;line-height:49px">404</h1><div style="display:inline-block"><h2 style="font-size:14px;font-weight:400;line-height:49px;margin:0">This page could not be found.</h2></div></div></div><script type="module" src="/_next/static/chunks/index-vlaCVOft.js" id="_R_" async=""></script><script>((self[Symbol.for("vinext.navigationRuntime")]??={bootstrap:{routeManifest:null},functions:{}}).bootstrap.rsc??={rsc:[]}).rsc.push("1:\"$Sreact.fragment\"\n")</script><script>((self[Symbol.for("vinext.navigationRuntime")]??={bootstrap:{routeManifest:null},functions:{}}).bootstrap.rsc??={rsc:[]}).rsc.push(":HL[\"/_next/static/css/index.miWu1gnb.css\",\"style\" ]\n")</script><script>((self[Symbol.for("vinext.navigationRuntime")]??={bootstrap:{routeManifest:null},functions:{}}).bootstrap.rsc??={rsc:[]}).rsc.push("0:{\"__route\":\"route:/__vinext_nonexistent_for_404__\",\"__interceptionContext\":null,\"__layoutIds\":[],\"__rootLayout\":null,\"route:/__vinext_nonexistent_for_404__\":[[[\"$\",\"link\",\"css:/_next/static/css/index.miWu1gnb.css\",{\"rel\":\"stylesheet\",\"precedence\":\"vite-rsc/importer-resources\",\"href\":\"/_next/static/css/index.miWu1gnb.css\",\"data-rsc-css-href\":\"/_next/static/css/index.miWu1gnb.css\"}],\"$undefined\"],[\"$\",\"html\",null,{\"lang\":\"zh-CN\",\"children\":[\"$\",\"body\",null,{\"children\":[[\"$\",\"meta\",\"charset\",{\"charSet\":\"utf-8\"}],[\"$\",\"meta\",\"robots\",{\"name\":\"robots\",\"content\":\"noindex\"}],[\"$\",\"$1\",\"metadata\",{\"children\":[[\"$\",\"title\",\"0\",{\"children\":\"豆格工坊|图片转拼豆图纸\"}],[\"$\",\"meta\",\"1\",{\"name\":\"description\",\"content\":\"在浏览器中把图片转换为拼豆像素图纸,按色号、名称或像素格筛选颜色。\"}]]}],[\"$\",\"$1\",\"viewport\",{\"children\":[[\"$\",\"meta\",\"0\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}]]}],[[\"$\",\"title\",null,{\"children\":\"404: This page could not be found.\"}],[\"$\",\"div\",null,{\"style\":{\"fontFamily\":\"system-ui,\\\"Segoe UI\\\",Roboto,Helvetica,Arial,sans-serif,\\\"Apple Color Emoji\\\",\\\"Segoe UI Emoji\\\"\",\"height\":\"100vh\",\"textAlign\":\"center\",\"display\":\"flex\",\"flexDirection\":\"column\",\"alignItems\":\"center\",\"justifyContent\":\"center\"},\"children\":[\"$\",\"div\",null,{\"children\":[[\"$\",\"style\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}\"}}],[\"$\",\"h1\",null,{\"className\":\"next-error-h1\",\"style\":{\"display\":\"inline-block\",\"margin\":\"0 20px 0 0\",\"padding\":\"0 23px 0 0\",\"fontSize\":24,\"fontWeight\":500,\"verticalAlign\":\"top\",\"lineHeight\":\"49px\"},\"children\":404}],[\"$\",\"div\",null,{\"style\":{\"display\":\"inline-block\"},\"children\":[\"$\",\"h2\",null,{\"style\":{\"fontSize\":14,\"fontWeight\":400,\"lineHeight\":\"49px\",\"margin\":0},\"children\":\"This page could not be found.\"}]}]]}]}]]]}]}]]}\n")</script><script>((self[Symbol.for("vinext.navigationRuntime")]??={bootstrap:{routeManifest:null},functions:{}}).bootstrap.rsc??={rsc:[]}).done=true</script></body></html>
|
||||||
+3
-3
File diff suppressed because one or more lines are too long
+1
-1
@@ -1 +1 @@
|
|||||||
import{r as e}from"./rolldown-runtime-C60lm6uB.js";import{r as t}from"./framework-BgSIrAUN.js";import{t as n}from"./index-Dctq4oWR.js";var r=e(t(),1),i=new Map;function a(e,t){return e?{...e,...t}:t}function o({providerId:e,segmentMap:t,children:o}){let s=(0,r.useRef)(null),c=n(),l=a(s.current??(e?i.get(e)??null:null),t);return(0,r.useEffect)(()=>{s.current=l,e&&i.set(e,l)},[l,e]),c?(0,r.createElement)(c.Provider,{value:l},o):o}export{o as LayoutSegmentProvider,a as mergeLayoutSegmentMap};
|
import{r as e}from"./rolldown-runtime-C60lm6uB.js";import{r as t}from"./framework-BgSIrAUN.js";import{t as n}from"./index-vlaCVOft.js";var r=e(t(),1),i=new Map;function a(e,t){return e?{...e,...t}:t}function o({providerId:e,segmentMap:t,children:o}){let s=(0,r.useRef)(null),c=n(),l=a(s.current??(e?i.get(e)??null:null),t);return(0,r.useEffect)(()=>{s.current=l,e&&i.set(e,l)},[l,e]),c?(0,r.createElement)(c.Provider,{value:l},o):o}export{o as LayoutSegmentProvider,a as mergeLayoutSegmentMap};
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2
-2
File diff suppressed because one or more lines are too long
+1
-1
@@ -7,7 +7,7 @@
|
|||||||
8:I["9276801271d6",[],"AppRouterScrollTarget",1]
|
8:I["9276801271d6",[],"AppRouterScrollTarget",1]
|
||||||
9:I["593f344dc510",[],"RedirectBoundary",1]
|
9:I["593f344dc510",[],"RedirectBoundary",1]
|
||||||
:HL["/_next/static/css/index.miWu1gnb.css","style" ]
|
:HL["/_next/static/css/index.miWu1gnb.css","style" ]
|
||||||
0:{"__route":"route:/","__interceptionContext":null,"__layoutIds":["layout:/"],"__rootLayout":"/","__sourcePage":"/page","page:/":"$L1","layout:/":[[[["$","link","css:/_next/static/css/index.miWu1gnb.css",{"rel":"stylesheet","precedence":"vite-rsc/importer-resources","href":"/_next/static/css/index.miWu1gnb.css","data-rsc-css-href":"/_next/static/css/index.miWu1gnb.css"}],"$undefined"],["$","html",null,{"lang":"zh-CN","children":["$","body",null,{"children":["$","$L2",null,{}]}]}]],null],"route:/":[[["$","meta",null,{"charSet":"utf-8"}],[["$","title","0",{"children":"豆格工坊|图片转拼豆图纸"}],["$","meta","1",{"name":"description","content":"在浏览器中把图片转换为拼豆像素图纸,按色号、名称或像素格筛选颜色。"}]],[["$","meta","0",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]],["$","$L3",null,{"fallback":"$4","children":["$","$L5",null,{"fallback":"$4","children":["$","$L6",null,{"providerId":"layout:/","segmentMap":{"children":[]},"children":["$","$L7",null,{"id":"layout:/","parallelSlots":"$undefined","children":["$","$L8",null,{"children":["$","$L9",null,{"children":[["$","$L6",null,{"providerId":"page:/","segmentMap":{"children":["__PAGE__"]},"children":["$","$L7",null,{"id":"page:/"}]}],null]}]}]}]}]}]}],null,null],"__layoutFlags":{"layout:/":"s"},"__artifactCompatibility":{"schemaVersion":1,"graphVersion":"app-route-graph:1177fd80f83fa7b0","deploymentVersion":"80e16ac3-e7c1-4028-b059-ff488cc18f98","appElementsSchemaVersion":1,"rscPayloadSchemaVersion":1,"rootBoundaryId":"/","renderEpoch":null},"__renderObservation":{"schemaVersion":1,"output":{"kind":"app-rsc","mountedSlotsFingerprint":null,"renderEpoch":null,"rootBoundaryId":"/","routeId":"route:/"},"completeness":"partial","boundaryOutcome":{"kind":"unknown"},"requestApis":[{"kind":"connection","status":"unknown"},{"kind":"cookies","status":"unknown"},{"kind":"draftMode","status":"unknown"},{"kind":"headers","status":"unknown"},{"kind":"params","status":"unknown"},{"kind":"searchParams","status":"unknown"}],"dynamicFetches":[],"cacheTags":["/","_N_T_/","_N_T_/index","_N_T_/layout","_N_T_/page"],"pathTags":["/"],"cacheability":"unknown","downgrade":{"target":"freshRender","reasons":[{"code":"CP_DOWNGRADE_CACHEABILITY_UNKNOWN","target":"freshRender"},{"code":"CP_DOWNGRADE_INCOMPLETE_OBSERVATION","completeness":"partial","target":"freshRender"},{"code":"CP_DOWNGRADE_UNKNOWN_REQUEST_API","requestApi":"connection","target":"freshRender"},{"code":"CP_DOWNGRADE_UNKNOWN_REQUEST_API","requestApi":"cookies","target":"freshRender"},{"code":"CP_DOWNGRADE_UNKNOWN_REQUEST_API","requestApi":"draftMode","target":"freshRender"},{"code":"CP_DOWNGRADE_UNKNOWN_REQUEST_API","requestApi":"headers","target":"freshRender"},{"code":"CP_DOWNGRADE_UNKNOWN_REQUEST_API","requestApi":"params","target":"freshRender"},{"code":"CP_DOWNGRADE_UNKNOWN_REQUEST_API","requestApi":"searchParams","target":"freshRender"}],"fallback":{"kind":"breakerFallback","code":"CP_PRIVATE_DYNAMIC_DOWNGRADE","mode":"renderFresh","scope":"affectedOutput","fields":{"reasonCodes":["CP_DOWNGRADE_CACHEABILITY_UNKNOWN","CP_DOWNGRADE_INCOMPLETE_OBSERVATION","CP_DOWNGRADE_UNKNOWN_REQUEST_API","CP_DOWNGRADE_UNKNOWN_REQUEST_API","CP_DOWNGRADE_UNKNOWN_REQUEST_API","CP_DOWNGRADE_UNKNOWN_REQUEST_API","CP_DOWNGRADE_UNKNOWN_REQUEST_API","CP_DOWNGRADE_UNKNOWN_REQUEST_API"],"target":"freshRender"}},"isPublicCacheCandidate":false}}}
|
0:{"__route":"route:/","__interceptionContext":null,"__layoutIds":["layout:/"],"__rootLayout":"/","__sourcePage":"/page","page:/":"$L1","layout:/":[[[["$","link","css:/_next/static/css/index.miWu1gnb.css",{"rel":"stylesheet","precedence":"vite-rsc/importer-resources","href":"/_next/static/css/index.miWu1gnb.css","data-rsc-css-href":"/_next/static/css/index.miWu1gnb.css"}],"$undefined"],["$","html",null,{"lang":"zh-CN","children":["$","body",null,{"children":["$","$L2",null,{}]}]}]],null],"route:/":[[["$","meta",null,{"charSet":"utf-8"}],[["$","title","0",{"children":"豆格工坊|图片转拼豆图纸"}],["$","meta","1",{"name":"description","content":"在浏览器中把图片转换为拼豆像素图纸,按色号、名称或像素格筛选颜色。"}]],[["$","meta","0",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]],["$","$L3",null,{"fallback":"$4","children":["$","$L5",null,{"fallback":"$4","children":["$","$L6",null,{"providerId":"layout:/","segmentMap":{"children":[]},"children":["$","$L7",null,{"id":"layout:/","parallelSlots":"$undefined","children":["$","$L8",null,{"children":["$","$L9",null,{"children":[["$","$L6",null,{"providerId":"page:/","segmentMap":{"children":["__PAGE__"]},"children":["$","$L7",null,{"id":"page:/"}]}],null]}]}]}]}]}]}],null,null],"__layoutFlags":{"layout:/":"s"},"__artifactCompatibility":{"schemaVersion":1,"graphVersion":"app-route-graph:1177fd80f83fa7b0","deploymentVersion":"4bf90f03-9498-462d-abbc-bcd391bef9f2","appElementsSchemaVersion":1,"rscPayloadSchemaVersion":1,"rootBoundaryId":"/","renderEpoch":null},"__renderObservation":{"schemaVersion":1,"output":{"kind":"app-rsc","mountedSlotsFingerprint":null,"renderEpoch":null,"rootBoundaryId":"/","routeId":"route:/"},"completeness":"partial","boundaryOutcome":{"kind":"unknown"},"requestApis":[{"kind":"connection","status":"unknown"},{"kind":"cookies","status":"unknown"},{"kind":"draftMode","status":"unknown"},{"kind":"headers","status":"unknown"},{"kind":"params","status":"unknown"},{"kind":"searchParams","status":"unknown"}],"dynamicFetches":[],"cacheTags":["/","_N_T_/","_N_T_/index","_N_T_/layout","_N_T_/page"],"pathTags":["/"],"cacheability":"unknown","downgrade":{"target":"freshRender","reasons":[{"code":"CP_DOWNGRADE_CACHEABILITY_UNKNOWN","target":"freshRender"},{"code":"CP_DOWNGRADE_INCOMPLETE_OBSERVATION","completeness":"partial","target":"freshRender"},{"code":"CP_DOWNGRADE_UNKNOWN_REQUEST_API","requestApi":"connection","target":"freshRender"},{"code":"CP_DOWNGRADE_UNKNOWN_REQUEST_API","requestApi":"cookies","target":"freshRender"},{"code":"CP_DOWNGRADE_UNKNOWN_REQUEST_API","requestApi":"draftMode","target":"freshRender"},{"code":"CP_DOWNGRADE_UNKNOWN_REQUEST_API","requestApi":"headers","target":"freshRender"},{"code":"CP_DOWNGRADE_UNKNOWN_REQUEST_API","requestApi":"params","target":"freshRender"},{"code":"CP_DOWNGRADE_UNKNOWN_REQUEST_API","requestApi":"searchParams","target":"freshRender"}],"fallback":{"kind":"breakerFallback","code":"CP_PRIVATE_DYNAMIC_DOWNGRADE","mode":"renderFresh","scope":"affectedOutput","fields":{"reasonCodes":["CP_DOWNGRADE_CACHEABILITY_UNKNOWN","CP_DOWNGRADE_INCOMPLETE_OBSERVATION","CP_DOWNGRADE_UNKNOWN_REQUEST_API","CP_DOWNGRADE_UNKNOWN_REQUEST_API","CP_DOWNGRADE_UNKNOWN_REQUEST_API","CP_DOWNGRADE_UNKNOWN_REQUEST_API","CP_DOWNGRADE_UNKNOWN_REQUEST_API","CP_DOWNGRADE_UNKNOWN_REQUEST_API"],"target":"freshRender"}},"isPublicCacheCandidate":false}}}
|
||||||
a:I["6efdf509a785",[],"default",1]
|
a:I["6efdf509a785",[],"default",1]
|
||||||
1:["$","$La",null,{"params":"$@b","searchParams":"$@c"}]
|
1:["$","$La",null,{"params":"$@b","searchParams":"$@c"}]
|
||||||
b:{}
|
b:{}
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
{
|
{
|
||||||
"appBrowserEntry": "_next/static/chunks/index-Dctq4oWR.js"
|
"appBrowserEntry": "_next/static/chunks/index-vlaCVOft.js"
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user