191 lines
5.1 KiB
JavaScript
191 lines
5.1 KiB
JavaScript
// Function to apply filter to the block
|
|
export function applyFilter(block) {
|
|
let imageData = block.getContext("2d").getImageData(0, 0, block.width, block.height);
|
|
let data = imageData.data;
|
|
|
|
for (let i = 0; i < data.length; i += 4) {
|
|
let hsv = rgbToHsv(data[i], data[i + 1], data[i + 2]);
|
|
|
|
// Mainly adjust saturation and value channels
|
|
hsv[1] = Math.min(hsv[1] * 1.05 + 30 * Math.log(hsv[1] / 100 + 1), 100);
|
|
hsv[2] = Math.min(hsv[2] * 1.3 + 40 * Math.log(hsv[2] / 100 + 1), 100);
|
|
|
|
let rgb = hsvToRgb(hsv[0], hsv[1], hsv[2]);
|
|
|
|
data[i] = rgb[0];
|
|
data[i + 1] = rgb[1];
|
|
data[i + 2] = rgb[2];
|
|
}
|
|
|
|
block.getContext("2d").putImageData(imageData, 0, 0);
|
|
}
|
|
|
|
// Function to perform horizontal Gaussian blur
|
|
export function gaussianBlurHorizontal(imageData, radius) {
|
|
const width = imageData.width;
|
|
const height = imageData.height;
|
|
|
|
const newData = new Uint8ClampedArray(imageData.data);
|
|
|
|
const kernel = generateGaussianKernel(radius);
|
|
|
|
for (let y = 0; y < height; y++) {
|
|
for (let x = 0; x < width; x++) {
|
|
let r = 0,
|
|
g = 0,
|
|
b = 0,
|
|
a = 0;
|
|
|
|
for (let kx = -radius; kx <= radius; kx++) {
|
|
const pixelX = Math.min(width - 1, Math.max(0, x + kx));
|
|
|
|
const kernelValue = kernel[kx + radius];
|
|
|
|
const index = (y * width + pixelX) * 4;
|
|
|
|
r += imageData.data[index] * kernelValue;
|
|
g += imageData.data[index + 1] * kernelValue;
|
|
b += imageData.data[index + 2] * kernelValue;
|
|
a += imageData.data[index + 3] * kernelValue;
|
|
}
|
|
|
|
const dataIndex = (y * width + x) * 4;
|
|
newData[dataIndex] = r;
|
|
newData[dataIndex + 1] = g;
|
|
newData[dataIndex + 2] = b;
|
|
newData[dataIndex + 3] = a;
|
|
}
|
|
}
|
|
|
|
return new ImageData(newData, width, height);
|
|
}
|
|
|
|
// Function to perform vertical Gaussian blur
|
|
export function gaussianBlurVertical(imageData, radius) {
|
|
const width = imageData.width;
|
|
const height = imageData.height;
|
|
|
|
const newData = new Uint8ClampedArray(imageData.data);
|
|
|
|
const kernel = generateGaussianKernel(radius);
|
|
|
|
for (let y = 0; y < height; y++) {
|
|
for (let x = 0; x < width; x++) {
|
|
let r = 0,
|
|
g = 0,
|
|
b = 0,
|
|
a = 0;
|
|
|
|
for (let ky = -radius; ky <= radius; ky++) {
|
|
const pixelY = Math.min(height - 1, Math.max(0, y + ky));
|
|
|
|
const kernelValue = kernel[ky + radius];
|
|
|
|
const index = (pixelY * width + x) * 4;
|
|
|
|
r += imageData.data[index] * kernelValue;
|
|
g += imageData.data[index + 1] * kernelValue;
|
|
b += imageData.data[index + 2] * kernelValue;
|
|
a += imageData.data[index + 3] * kernelValue;
|
|
}
|
|
|
|
const dataIndex = (y * width + x) * 4;
|
|
newData[dataIndex] = r;
|
|
newData[dataIndex + 1] = g;
|
|
newData[dataIndex + 2] = b;
|
|
newData[dataIndex + 3] = a;
|
|
}
|
|
}
|
|
|
|
return new ImageData(newData, width, height);
|
|
}
|
|
|
|
// Function to generate Gaussian kernel
|
|
function generateGaussianKernel(radius) {
|
|
const size = radius * 2 + 1;
|
|
const kernel = [];
|
|
|
|
const sigma = 0.4 * ((radius - 1) * 0.5 - 1) + 0.8;
|
|
|
|
for (let i = 0; i < size; i++) {
|
|
const x = i - radius;
|
|
const exponent = Math.exp(-(x * x) / (2 * sigma * sigma));
|
|
kernel[i] = exponent / (Math.sqrt(2 * Math.PI) * sigma);
|
|
}
|
|
|
|
// Normalize the kernel
|
|
const sum = kernel.reduce((a, b) => a + b, 0);
|
|
return kernel.map((value) => value / sum);
|
|
}
|
|
|
|
// Function to convert RGB to HSV
|
|
function rgbToHsv(r, g, b) {
|
|
(r /= 255), (g /= 255), (b /= 255);
|
|
|
|
let max = Math.max(r, g, b),
|
|
min = Math.min(r, g, b),
|
|
h,
|
|
s,
|
|
v = max;
|
|
|
|
let d = max - min;
|
|
s = max === 0 ? 0 : d / max;
|
|
|
|
if (max === min) {
|
|
h = 0; // achromatic
|
|
} else {
|
|
switch (max) {
|
|
case r:
|
|
h = (g - b) / d + (g < b ? 6 : 0);
|
|
break;
|
|
case g:
|
|
h = (b - r) / d + 2;
|
|
break;
|
|
case b:
|
|
h = (r - g) / d + 4;
|
|
break;
|
|
}
|
|
h /= 6;
|
|
}
|
|
|
|
return [h * 360, s * 100, v * 100];
|
|
}
|
|
|
|
// Function to convert HSV to RGB
|
|
function hsvToRgb(h, s, v) {
|
|
h /= 360;
|
|
s /= 100;
|
|
v /= 100;
|
|
|
|
let i = Math.floor(h * 6),
|
|
f = h * 6 - i,
|
|
p = v * (1 - s),
|
|
q = v * (1 - f * s),
|
|
t = v * (1 - (1 - f) * s),
|
|
r,
|
|
g,
|
|
b;
|
|
|
|
switch (i % 6) {
|
|
case 0:
|
|
(r = v), (g = t), (b = p);
|
|
break;
|
|
case 1:
|
|
(r = q), (g = v), (b = p);
|
|
break;
|
|
case 2:
|
|
(r = p), (g = v), (b = t);
|
|
break;
|
|
case 3:
|
|
(r = p), (g = q), (b = v);
|
|
break;
|
|
case 4:
|
|
(r = t), (g = p), (b = v);
|
|
break;
|
|
case 5:
|
|
(r = v), (g = p), (b = q);
|
|
break;
|
|
}
|
|
|
|
return [r * 255, g * 255, b * 255];
|
|
} |