mirror of
https://github.com/Terncode/pixel.horse.git
synced 2026-09-24 21:55:52 +02:00
Archive commit
This commit is contained in:
@@ -0,0 +1,128 @@
|
||||
import { Rect, ExtCanvas } from './types';
|
||||
import { cropCanvas, createExtCanvas } from './canvas-utils';
|
||||
|
||||
const validPatternColors = [
|
||||
255, // red
|
||||
255 << 8, // green
|
||||
255 | (255 << 8), // yellow
|
||||
255 << 16, // blue
|
||||
255 | (255 << 16), // purple
|
||||
(255 << 8) | (255 << 16), // teal
|
||||
255 | (255 << 8) | (255 << 16), // white
|
||||
0, // black
|
||||
];
|
||||
|
||||
const getAlpha = (alpha: number) => alpha < 15 ? false : (alpha > 250 ? true : null);
|
||||
const isOutline = (shade: number) => shade <= 159;
|
||||
const round255 = (n: number) => n > 200 ? 255 : (n < 50 ? 0 : n);
|
||||
const rgbToColor = (r: number, g: number, b: number) => round255(r) | (round255(g) << 8) | (round255(b) << 16);
|
||||
const findValidPatternColor = (r: number, g: number, b: number) => validPatternColors.indexOf(rgbToColor(r, g, b));
|
||||
|
||||
function getShade(shade: number) {
|
||||
// outlines
|
||||
// 135 - dark outline
|
||||
// 159 - outline
|
||||
|
||||
// fills
|
||||
// 174 - dark shades
|
||||
// 204 - shade
|
||||
// 217 - dark fill
|
||||
// 255 - fill
|
||||
|
||||
if (shade > 130 && shade < 140) { // dark outline
|
||||
return 135;
|
||||
} else if (shade > 150 && shade < 165) { // outline
|
||||
return 159;
|
||||
} else if (shade > 165 && shade < 180) { // dark shade
|
||||
return 174;
|
||||
} else if (shade > 190 && shade < 210) { // shade
|
||||
return 204;
|
||||
} else if (shade > 210 && shade < 220) { // dark fill
|
||||
return 217;
|
||||
} else if (shade > 245) { // fill
|
||||
return 255;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
function getShadeForShading(shade: number) {
|
||||
if (shade === 135) { // dark outline
|
||||
return 204;
|
||||
} else if (shade === 159) { // outline
|
||||
return 255;
|
||||
} else {
|
||||
return shade;
|
||||
}
|
||||
}
|
||||
|
||||
function pixel(data: Uint8ClampedArray, index: number) {
|
||||
return data.slice(index, index + 4).join(', ');
|
||||
}
|
||||
|
||||
export interface ColorsOutput {
|
||||
colors?: number;
|
||||
forceWhite?: boolean;
|
||||
}
|
||||
|
||||
export function imageToPalette(
|
||||
rect: Rect, image: ExtCanvas, pattern: ExtCanvas, palette: number[] | undefined, config: ColorsOutput
|
||||
) {
|
||||
const pat = cropCanvas(pattern, 0, 0, image.width, image.height);
|
||||
const ctx = pat.getContext('2d')!;
|
||||
ctx.globalCompositeOperation = 'destination-in';
|
||||
ctx.drawImage(image, 0, 0);
|
||||
|
||||
const imageData = image.getContext('2d')!.getImageData(rect.x, rect.y, rect.w, rect.h);
|
||||
const pattData = ctx.getImageData(rect.x, rect.y, rect.w, rect.h);
|
||||
const length = imageData.width * imageData.height * 4;
|
||||
const data = imageData.data;
|
||||
const pdata = pattData.data;
|
||||
|
||||
let maxIndex = 0;
|
||||
|
||||
for (let i = 0; i < length; i += 4) {
|
||||
const x = ((i / 4) % rect.w) + rect.x;
|
||||
const y = Math.floor((i / 4) / rect.h) + rect.y;
|
||||
|
||||
if (palette) {
|
||||
const color = ((data[i] << 24) | (data[i + 1] << 16) | (data[i + 2] << 8) | data[i + 3]) >>> 0;
|
||||
const index = palette.indexOf(color);
|
||||
|
||||
if (index === -1) {
|
||||
throw new Error(`Invalid color (palette) [${pixel(data, i)}] [0x${color.toString(16)}] (${x} ${y}) (${image.info})`);
|
||||
}
|
||||
|
||||
data[i] = index;
|
||||
data[i + 1] = 255;
|
||||
} else {
|
||||
const alpha = getAlpha(data[i + 3]);
|
||||
const shade = getShade(data[i]);
|
||||
const index = findValidPatternColor(pdata[i], pdata[i + 1], pdata[i + 2]);
|
||||
|
||||
if (index === -1)
|
||||
throw new Error(`Invalid color (pattern) [${pixel(pdata, i)}] (${x} ${y}) (${pat.info})`);
|
||||
if (alpha == null)
|
||||
throw new Error(`Invalid color [${pixel(data, i)}] (${x} ${y}) (${image.info})`);
|
||||
|
||||
const actualIndex = (index * 2) + (isOutline(shade) ? 2 : 1);
|
||||
const shadeForShading = getShadeForShading(shade);
|
||||
|
||||
data[i] = alpha ? actualIndex : 0;
|
||||
data[i + 1] = (config.forceWhite && shade !== -1) ? 255 : (alpha ? shadeForShading : 255);
|
||||
|
||||
if (alpha) {
|
||||
maxIndex = Math.max(maxIndex, index);
|
||||
}
|
||||
}
|
||||
|
||||
data[i + 2] = 0;
|
||||
data[i + 3] = 255;
|
||||
}
|
||||
|
||||
config.colors = palette ? palette.length : ((maxIndex + 1) * 2 + 1);
|
||||
|
||||
const result = createExtCanvas(image.width, image.height, `${image.info} (image to palette)`);
|
||||
result.getContext('2d')!.putImageData(imageData, rect.x, rect.y);
|
||||
return result;
|
||||
}
|
||||
Reference in New Issue
Block a user