feat: more filters and gpu computed

This commit is contained in:
2025-12-20 23:35:29 +01:00
parent 030c2f0803
commit fc4b759051
+53 -39
View File
@@ -7,6 +7,11 @@ enum Filter {
Gray = "Gray",
Sepia = "Sepia",
Invert = "Invert",
Blur = "Blur",
Brightness = "Brightness",
Contrast = "Contrast",
HueShift = "Hue shift",
Saturate = "Saturate"
}
enum PAPosition {
None = "None",
@@ -29,6 +34,7 @@ const centerX = ref<number>(0.5);
const centerY = ref<number>(0.5);
const zoom = ref<number>(1);
const filter = ref<Filter>(Filter.None);
const filterValue = ref<number>(1);
const paPos = ref<PAPosition>(PAPosition.BR);
const paScale = ref<number>(0.2);
const paMargin = ref<number>(0.05);
@@ -65,6 +71,7 @@ function randomize() {
centerY.value = 0.25 + Math.random() * 0.5;
zoom.value = 1 + Math.random();
filter.value = randomElement(Object.values(Filter));
filterValue.value = 0.5 + Math.random() * 2;
paPos.value = randomElement(Object.values(PAPosition));
paScale.value = 0.1 + Math.random() * 0.2;
paMargin.value = Math.random() * 0.1;
@@ -76,6 +83,7 @@ function reset() {
centerY.value = 0.5;
zoom.value = 1;
filter.value = Filter.None;
filterValue.value = 1;
paPos.value = PAPosition.BR;
paScale.value = 0.2;
paMargin.value = 0.05;
@@ -97,28 +105,6 @@ function download() {
link.click();
}
function applyFilter(
f: Filter,
r: number,
g: number,
b: number,
): [number, number, number] {
switch (f) {
case Filter.Gray:
const gray = 0.21 * r + 0.72 * g + 0.07 * b;
return [gray, gray, gray];
case Filter.Invert:
return [255 - r, 255 - g, 255 - b];
case Filter.Sepia:
return [
Math.min(255, Math.round(r * 0.393 + g * 0.769 + b * 0.189)),
Math.min(255, Math.round(r * 0.349 + g * 0.686 + b * 0.168)),
Math.min(255, Math.round(r * 0.272 + g * 0.534 + b * 0.131)),
];
}
return [r, g, b];
}
const drawTimeout = ref<number | undefined>(undefined);
function asyncDraw() {
@@ -144,25 +130,38 @@ function draw(size = PREVIEW_SIZE) {
const imgHeight = size * (widthFirst ? imgRatio : 1) * zoom.value;
const dx = (size - imgWidth) * centerX.value;
const dy = (size - imgHeight) * centerY.value;
switch(filter.value) {
case Filter.Gray:
ctx.filter = `grayscale(${(filterValue.value * 100).toFixed(2)}%)`
break
case Filter.Sepia:
ctx.filter = `sepia(${(filterValue.value * 100).toFixed(2)}%)`
break
case Filter.Invert:
ctx.filter = `invert(${(filterValue.value * 100).toFixed(2)}%)`
break
case Filter.Blur:
ctx.filter = `blur(${(filterValue.value * 2 * size / PREVIEW_SIZE).toFixed(2)}px)`
break
case Filter.Brightness:
ctx.filter = `brightness(${(filterValue.value * 100).toFixed(2)}%)`
break
case Filter.Contrast:
ctx.filter = `contrast(${(filterValue.value * 100).toFixed(2)}%)`
break
case Filter.HueShift:
ctx.filter = `hue-rotate(${(filterValue.value * 360).toFixed(2)}deg)`
break
case Filter.Saturate:
ctx.filter = `saturate(${(filterValue.value * 100).toFixed(2)}%)`
break
}
ctx.drawImage(image.value, dx, dy, imgWidth, imgHeight);
if (filter.value !== Filter.None) {
const imageData = ctx.getImageData(0, 0, size, size);
const pixelData = imageData.data;
for (let i = 0; i < pixelData.length; i += 4) {
const r1 = pixelData[i] ?? 0;
const g1 = pixelData[i + 1] ?? 0;
const b1 = pixelData[i + 2] ?? 0;
const [r2, g2, b2] = applyFilter(filter.value, r1, g1, b1);
pixelData[i] = r2;
pixelData[i + 1] = g2;
pixelData[i + 2] = b2;
pixelData[i + 3] = 255;
}
ctx.putImageData(imageData, 0, 0);
}
ctx.filter = "none"
if (paPos.value !== PAPosition.None && parentalAdvisory.value) {
const paWidth = size * paScale.value;
@@ -313,6 +312,21 @@ onMounted(() => {
</td>
<td></td>
</tr>
<tr>
<td><label for="filter-value">Filter Value:</label></td>
<td>
<input
id="filter-value"
v-model="filterValue"
type="range"
min="0"
max="3"
step="0.01"
@input="asyncDraw"
/>
</td>
<td>{{ (filterValue * 100).toFixed(0) }}%</td>
</tr>
<tr>
<td><label for="pa-pos">Sticker Position:</label></td>
<td>