Files
easy-png-tools/web/src/lib/core/convolution.test.ts
T

155 lines
3.9 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import { convolve, gaussianBlur, sharpen } from './convolution';
import { makeImage } from './test-helpers';
const SHARPEN_KERNEL = [0, -1, 0, -1, 5, -1, 0, -1, 0];
describe('convolve', () => {
it('крестовое ядро резкости на полоске из трёх пикселей', () => {
const out = convolve(
makeImage(3, 1, [
[0, 0, 0, 255],
[100, 100, 100, 255],
[0, 0, 0, 255]
]),
SHARPEN_KERNEL,
3
);
expect([...out.data.slice(4, 8)]).toEqual([255, 255, 255, 255]);
expect([...out.data.slice(0, 4)]).toEqual([0, 0, 0, 255]);
expect([...out.data.slice(8, 12)]).toEqual([0, 0, 0, 255]);
});
it.each([
[2, 3],
[3.5, 3],
[0, 3]
])('бросает ошибку на некорректном размере ядра %i', (size) => {
expect(() => convolve(makeImage(1, 1, [[0, 0, 0, 255]]), [1], size as number)).toThrow();
});
});
describe('sharpen', () => {
it('сила 0 возвращает копию', () => {
const img = makeImage(2, 2, [
[10, 20, 30, 255],
[40, 50, 60, 128],
[70, 80, 90, 255],
[100, 110, 120, 200]
]);
expect([...sharpen(img, 0).data]).toEqual([...img.data]);
});
it('сила 100 применяет чистое ядро резкости', () => {
const out = sharpen(
makeImage(3, 1, [
[0, 0, 0, 255],
[100, 100, 100, 255],
[0, 0, 0, 255]
]),
100
);
expect(out.data[4]).toBe(255);
expect(out.data[0]).toBe(0);
});
});
describe('gaussianBlur', () => {
it('постоянное изображение не меняется ни в RGB, ни в альфе', () => {
const img = makeImage(3, 3, new Array(9).fill([40, 80, 120, 128]));
const out = gaussianBlur(img, 16);
for (let i = 0; i < out.data.length; i++) {
expect(out.data[i]).toBe(img.data[i]);
}
});
it('далёкие углы остаются прозрачными, цвет центра не искажается', () => {
const size = 61;
const pixels: number[][] = [];
for (let y = 0; y < size; y++) {
for (let x = 0; x < size; x++) {
pixels.push(
x >= 26 && x <= 34 && y >= 26 && y <= 34 ? [200, 50, 25, 255] : [0, 0, 0, 0]
);
}
}
const out = gaussianBlur(makeImage(size, size, pixels), 8);
const corner = 0;
expect(out.data[corner]).toBe(0);
expect(out.data[corner + 1]).toBe(0);
expect(out.data[corner + 2]).toBe(0);
expect(out.data[corner + 3]).toBe(0);
const center = (30 * size + 30) * 4;
expect(out.data[center]).toBe(200);
expect(out.data[center + 1]).toBe(50);
expect(out.data[center + 2]).toBe(25);
});
it('симметричный вход даёт симметричный результат', () => {
const leftByRow = [
[
[255, 0, 0, 255],
[10, 20, 30, 255],
[64, 64, 64, 64],
[5, 5, 5, 200]
],
[
[10, 20, 30, 255],
[200, 100, 50, 255],
[1, 2, 3, 4],
[90, 90, 90, 250]
],
[
[64, 64, 64, 64],
[1, 2, 3, 4],
[128, 128, 128, 128],
[40, 40, 40, 240]
],
[
[200, 100, 50, 255],
[90, 90, 90, 250],
[40, 40, 40, 240],
[7, 7, 7, 255]
],
[
[10, 20, 30, 255],
[1, 2, 3, 4],
[64, 64, 64, 64],
[90, 90, 90, 250]
],
[
[64, 64, 64, 64],
[200, 100, 50, 255],
[5, 5, 5, 200],
[1, 2, 3, 4]
],
[
[5, 5, 5, 200],
[40, 40, 40, 240],
[90, 90, 90, 250],
[128, 128, 128, 128]
]
];
const pixels: number[][] = [];
for (let y = 0; y < 7; y++) {
for (let x = 0; x < 7; x++) {
pixels.push(leftByRow[y][Math.min(x, 6 - x)]);
}
}
const img = makeImage(7, 7, pixels);
const blurred = gaussianBlur(img, 3);
for (let y = 0; y < 7; y++) {
for (let x = 0; x < 3; x++) {
const li = (y * 7 + x) * 4;
const ri = (y * 7 + (6 - x)) * 4;
expect([...blurred.data.slice(li, li + 4)]).toEqual([
blurred.data[ri],
blurred.data[ri + 1],
blurred.data[ri + 2],
blurred.data[ri + 3]
]);
}
}
});
});