feat: more filters and gpu computed
This commit is contained in:
+53
-39
@@ -7,6 +7,11 @@ enum Filter {
|
|||||||
Gray = "Gray",
|
Gray = "Gray",
|
||||||
Sepia = "Sepia",
|
Sepia = "Sepia",
|
||||||
Invert = "Invert",
|
Invert = "Invert",
|
||||||
|
Blur = "Blur",
|
||||||
|
Brightness = "Brightness",
|
||||||
|
Contrast = "Contrast",
|
||||||
|
HueShift = "Hue shift",
|
||||||
|
Saturate = "Saturate"
|
||||||
}
|
}
|
||||||
enum PAPosition {
|
enum PAPosition {
|
||||||
None = "None",
|
None = "None",
|
||||||
@@ -29,6 +34,7 @@ const centerX = ref<number>(0.5);
|
|||||||
const centerY = ref<number>(0.5);
|
const centerY = ref<number>(0.5);
|
||||||
const zoom = ref<number>(1);
|
const zoom = ref<number>(1);
|
||||||
const filter = ref<Filter>(Filter.None);
|
const filter = ref<Filter>(Filter.None);
|
||||||
|
const filterValue = ref<number>(1);
|
||||||
const paPos = ref<PAPosition>(PAPosition.BR);
|
const paPos = ref<PAPosition>(PAPosition.BR);
|
||||||
const paScale = ref<number>(0.2);
|
const paScale = ref<number>(0.2);
|
||||||
const paMargin = ref<number>(0.05);
|
const paMargin = ref<number>(0.05);
|
||||||
@@ -65,6 +71,7 @@ function randomize() {
|
|||||||
centerY.value = 0.25 + Math.random() * 0.5;
|
centerY.value = 0.25 + Math.random() * 0.5;
|
||||||
zoom.value = 1 + Math.random();
|
zoom.value = 1 + Math.random();
|
||||||
filter.value = randomElement(Object.values(Filter));
|
filter.value = randomElement(Object.values(Filter));
|
||||||
|
filterValue.value = 0.5 + Math.random() * 2;
|
||||||
paPos.value = randomElement(Object.values(PAPosition));
|
paPos.value = randomElement(Object.values(PAPosition));
|
||||||
paScale.value = 0.1 + Math.random() * 0.2;
|
paScale.value = 0.1 + Math.random() * 0.2;
|
||||||
paMargin.value = Math.random() * 0.1;
|
paMargin.value = Math.random() * 0.1;
|
||||||
@@ -76,6 +83,7 @@ function reset() {
|
|||||||
centerY.value = 0.5;
|
centerY.value = 0.5;
|
||||||
zoom.value = 1;
|
zoom.value = 1;
|
||||||
filter.value = Filter.None;
|
filter.value = Filter.None;
|
||||||
|
filterValue.value = 1;
|
||||||
paPos.value = PAPosition.BR;
|
paPos.value = PAPosition.BR;
|
||||||
paScale.value = 0.2;
|
paScale.value = 0.2;
|
||||||
paMargin.value = 0.05;
|
paMargin.value = 0.05;
|
||||||
@@ -97,28 +105,6 @@ function download() {
|
|||||||
link.click();
|
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);
|
const drawTimeout = ref<number | undefined>(undefined);
|
||||||
|
|
||||||
function asyncDraw() {
|
function asyncDraw() {
|
||||||
@@ -144,25 +130,38 @@ function draw(size = PREVIEW_SIZE) {
|
|||||||
const imgHeight = size * (widthFirst ? imgRatio : 1) * zoom.value;
|
const imgHeight = size * (widthFirst ? imgRatio : 1) * zoom.value;
|
||||||
const dx = (size - imgWidth) * centerX.value;
|
const dx = (size - imgWidth) * centerX.value;
|
||||||
const dy = (size - imgHeight) * centerY.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);
|
ctx.drawImage(image.value, dx, dy, imgWidth, imgHeight);
|
||||||
|
|
||||||
if (filter.value !== Filter.None) {
|
ctx.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);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (paPos.value !== PAPosition.None && parentalAdvisory.value) {
|
if (paPos.value !== PAPosition.None && parentalAdvisory.value) {
|
||||||
const paWidth = size * paScale.value;
|
const paWidth = size * paScale.value;
|
||||||
@@ -313,6 +312,21 @@ onMounted(() => {
|
|||||||
</td>
|
</td>
|
||||||
<td></td>
|
<td></td>
|
||||||
</tr>
|
</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>
|
<tr>
|
||||||
<td><label for="pa-pos">Sticker Position:</label></td>
|
<td><label for="pa-pos">Sticker Position:</label></td>
|
||||||
<td>
|
<td>
|
||||||
|
|||||||
Reference in New Issue
Block a user