feat: add channel tools

This commit is contained in:
2026-08-26 08:38:30 +05:00
parent db02cbd165
commit 5c37eb2e7b
6 changed files with 364 additions and 4 deletions
+65
View File
@@ -0,0 +1,65 @@
import { describe, expect, it } from 'vitest';
import { hexToRgb } from './palette';
import { renderSpace, SPACES } from './channels';
import { makeImage } from './test-helpers';
describe('преобразования пространств', () => {
it('hsl красного: h=0, s=1, l=0.5', () => {
const [h, s, l] = SPACES.hsl.convert(hexToRgb('#ff0000'));
expect(h).toBeCloseTo(0);
expect(s).toBeCloseTo(1);
expect(l).toBeCloseTo(0.5);
});
it('hsv белого: v=1, чёрного: v=0', () => {
expect(SPACES.hsv.convert({ r: 255, g: 255, b: 255 })[2]).toBeCloseTo(1);
expect(SPACES.hsv.convert({ r: 0, g: 0, b: 0 })[2]).toBe(0);
});
it('hsi серого: s=0', () => {
const [, s] = SPACES.hsi.convert({ r: 128, g: 128, b: 128 });
expect(s).toBeCloseTo(0);
});
it('cmyk: белый — 0,0,0,0; чёрный — 0,0,0,1', () => {
const w = SPACES.cmyk.convert({ r: 255, g: 255, b: 255 });
const b = SPACES.cmyk.convert({ r: 0, g: 0, b: 0 });
expect(w.every((v) => Math.abs(v) < 1e-9)).toBe(true);
expect(b[3]).toBeCloseTo(1);
});
it('ycbcr белого: y≈1, cb≈cr≈0.5', () => {
const [y, cb, cr] = SPACES.ycbcr.convert({ r: 255, g: 255, b: 255 });
expect(y).toBeCloseTo(1);
expect(cb).toBeCloseTo(0.5, 2);
expect(cr).toBeCloseTo(0.5, 2);
});
it('lab белого: l≈1, a≈b≈0.5 (центр)', () => {
const [l, a, bb] = SPACES.lab.convert({ r: 255, g: 255, b: 255 });
expect(l).toBeCloseTo(1, 2);
expect(a).toBeCloseTo(0.5, 2);
expect(bb).toBeCloseTo(0.5, 2);
});
});
describe('renderSpace', () => {
const red = makeImage(1, 1, [[255, 0, 0, 255]]);
it('gray-режим: компонент y (жёлтый) чистого красного = белый', () => {
const out = renderSpace(red, 'cmyk', 'y', 'gray');
expect([...out.data]).toEqual([255, 255, 255, 255]);
});
it('color-режим hsl красного: каналы = тон/насыщенность/светлота', () => {
const out = renderSpace(red, 'hsl', 's', 'color');
expect(out.data[0]).toBe(0);
expect(out.data[1]).toBe(255);
expect(out.data[2]).toBeGreaterThanOrEqual(127);
});
it('неизвестное пространство или компонент дают прозрачную заглушку', () => {
const junk = makeImage(1, 1, [[10, 20, 30, 255]]);
expect(renderSpace(junk, 'cmyk' as never, 'zz' as never, 'gray').data[3]).toBe(0);
});
});