feat: add text and generation tools
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
import { createPixelImage, type PixelImage } from './types';
|
||||
import { hslToRgb } from './palette';
|
||||
import { mulberry32 } from './pixel-fx';
|
||||
|
||||
/** Радужный спектр: оттенок 0..360 вдоль выбранной оси. */
|
||||
export function colorSpectrum(
|
||||
width: number,
|
||||
height: number,
|
||||
direction: 'horizontal' | 'vertical',
|
||||
saturationPercent: number,
|
||||
lightnessPercent: number
|
||||
): PixelImage {
|
||||
const out = createPixelImage(width, height);
|
||||
for (let y = 0; y < height; y++) {
|
||||
for (let x = 0; x < width; x++) {
|
||||
const t = direction === 'vertical' ? y / Math.max(1, height - 1) : x / Math.max(1, width - 1);
|
||||
const { r, g, b } = hslToRgb({
|
||||
h: t * 360,
|
||||
s: saturationPercent / 100,
|
||||
l: lightnessPercent / 100
|
||||
});
|
||||
const di = (y * width + x) * 4;
|
||||
out.data[di] = r;
|
||||
out.data[di + 1] = g;
|
||||
out.data[di + 2] = b;
|
||||
out.data[di + 3] = 255;
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/** Случайные яркие блоки: детерминировано по seed. */
|
||||
export function randomColorBlocks(
|
||||
width: number,
|
||||
height: number,
|
||||
blockSize: number,
|
||||
seed: number
|
||||
): PixelImage {
|
||||
const bs = Math.max(1, Math.round(blockSize));
|
||||
const out = createPixelImage(width, height);
|
||||
const rng = mulberry32(seed);
|
||||
for (let by = 0; by < height; by += bs) {
|
||||
for (let bx = 0; bx < width; bx += bs) {
|
||||
const { r, g, b } = hslToRgb({
|
||||
h: rng() * 360,
|
||||
s: 0.65 + rng() * 0.35,
|
||||
l: 0.45 + rng() * 0.25
|
||||
});
|
||||
const yMax = Math.min(by + bs, height);
|
||||
const xMax = Math.min(bx + bs, width);
|
||||
for (let y = by; y < yMax; y++) {
|
||||
for (let x = bx; x < xMax; x++) {
|
||||
const di = (y * width + x) * 4;
|
||||
out.data[di] = r;
|
||||
out.data[di + 1] = g;
|
||||
out.data[di + 2] = b;
|
||||
out.data[di + 3] = 255;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function lineMask(length: number, divisions: number, lineWidth: number): Uint8Array {
|
||||
const mask = new Uint8Array(length);
|
||||
const lw = Math.max(1, Math.round(lineWidth));
|
||||
for (let i = 0; i <= divisions; i++) {
|
||||
const start = Math.round((i * length) / divisions);
|
||||
for (let x = start; x < Math.min(start + lw, length); x++) mask[x] = 1;
|
||||
}
|
||||
return mask;
|
||||
}
|
||||
|
||||
/** Сетка линий на прозрачном или белом фоне. */
|
||||
export function drawGrid(
|
||||
width: number,
|
||||
height: number,
|
||||
cols: number,
|
||||
rows: number,
|
||||
lineWidth: number,
|
||||
colorHex: string,
|
||||
transparentBg: boolean
|
||||
): PixelImage {
|
||||
const out = createPixelImage(width, height);
|
||||
if (!transparentBg) out.data.fill(255);
|
||||
|
||||
const m = /^#([0-9a-f]{6})$/i.exec(colorHex.trim());
|
||||
const r = m ? parseInt(m[1].slice(0, 2), 16) : 0;
|
||||
const g = m ? parseInt(m[1].slice(2, 4), 16) : 0;
|
||||
const b = m ? parseInt(m[1].slice(4, 6), 16) : 0;
|
||||
|
||||
const colMask = lineMask(width, Math.max(1, cols), lineWidth);
|
||||
const rowMask = lineMask(height, Math.max(1, rows), lineWidth);
|
||||
for (let y = 0; y < height; y++) {
|
||||
for (let x = 0; x < width; x++) {
|
||||
if (!colMask[x] && !rowMask[y]) continue;
|
||||
const di = (y * width + x) * 4;
|
||||
out.data[di] = r;
|
||||
out.data[di + 1] = g;
|
||||
out.data[di + 2] = b;
|
||||
out.data[di + 3] = 255;
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
Reference in New Issue
Block a user