feat: more filters and gpu computed
This commit is contained in:
+53
-39
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user