Add blurhash support
This commit is contained in:
parent
2ea987b766
commit
1edc1a2ec7
10 changed files with 100 additions and 3 deletions
|
@ -25,6 +25,7 @@
|
||||||
"@floatingghost/pinch-zoom-element": "^1.3.1",
|
"@floatingghost/pinch-zoom-element": "^1.3.1",
|
||||||
"@vuelidate/core": "^2.0.0",
|
"@vuelidate/core": "^2.0.0",
|
||||||
"@vuelidate/validators": "^2.0.0",
|
"@vuelidate/validators": "^2.0.0",
|
||||||
|
"blurhash": "^2.0.4",
|
||||||
"body-scroll-lock": "2.7.1",
|
"body-scroll-lock": "2.7.1",
|
||||||
"chromatism": "3.0.0",
|
"chromatism": "3.0.0",
|
||||||
"click-outside-vue3": "4.0.1",
|
"click-outside-vue3": "4.0.1",
|
||||||
|
|
|
@ -18,6 +18,7 @@ import {
|
||||||
faPencilAlt,
|
faPencilAlt,
|
||||||
faAlignRight
|
faAlignRight
|
||||||
} from '@fortawesome/free-solid-svg-icons'
|
} from '@fortawesome/free-solid-svg-icons'
|
||||||
|
import Blurhash from '../blurhash/Blurhash.vue'
|
||||||
|
|
||||||
library.add(
|
library.add(
|
||||||
faFile,
|
faFile,
|
||||||
|
@ -63,7 +64,8 @@ const Attachment = {
|
||||||
components: {
|
components: {
|
||||||
Flash,
|
Flash,
|
||||||
StillImage,
|
StillImage,
|
||||||
VideoAttachment
|
VideoAttachment,
|
||||||
|
Blurhash
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
classNames () {
|
classNames () {
|
||||||
|
@ -84,6 +86,9 @@ const Attachment = {
|
||||||
useContainFit () {
|
useContainFit () {
|
||||||
return this.$store.getters.mergedConfig.useContainFit
|
return this.$store.getters.mergedConfig.useContainFit
|
||||||
},
|
},
|
||||||
|
useBlurhash () {
|
||||||
|
return this.$store.getters.mergedConfig.useBlurhash
|
||||||
|
},
|
||||||
placeholderName () {
|
placeholderName () {
|
||||||
if (this.attachment.description === '' || !this.attachment.description) {
|
if (this.attachment.description === '' || !this.attachment.description) {
|
||||||
return this.type.toUpperCase()
|
return this.type.toUpperCase()
|
||||||
|
|
|
@ -64,7 +64,15 @@
|
||||||
:title="attachment.description"
|
:title="attachment.description"
|
||||||
@click.prevent.stop="toggleHidden"
|
@click.prevent.stop="toggleHidden"
|
||||||
>
|
>
|
||||||
|
<Blurhash
|
||||||
|
v-if="useBlurhash"
|
||||||
|
:height="512"
|
||||||
|
:width="1024"
|
||||||
|
:hash="attachment.blurhash"
|
||||||
|
:punch="1"
|
||||||
|
/>
|
||||||
<img
|
<img
|
||||||
|
v-else
|
||||||
:key="nsfwImage"
|
:key="nsfwImage"
|
||||||
class="nsfw"
|
class="nsfw"
|
||||||
:src="nsfwImage"
|
:src="nsfwImage"
|
||||||
|
|
66
src/components/blurhash/Blurhash.vue
Normal file
66
src/components/blurhash/Blurhash.vue
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
<template>
|
||||||
|
<canvas
|
||||||
|
ref="canvas"
|
||||||
|
class="blurhash"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { decode } from "blurhash";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'Blurhash',
|
||||||
|
props: {
|
||||||
|
hash: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
width: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
height: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
punch: {
|
||||||
|
type: Number,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
canvas: null,
|
||||||
|
ctx: null,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.canvas = this.$refs.canvas;
|
||||||
|
this.ctx = this.canvas.getContext('2d');
|
||||||
|
this.canvas.width = 1024;
|
||||||
|
this.canvas.height = 512;
|
||||||
|
this.draw();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
draw() {
|
||||||
|
const pixels = decode(this.hash, this.width, this.height, this.punch);
|
||||||
|
const imageData = this.ctx.createImageData(this.width, this.height);
|
||||||
|
imageData.data.set(pixels);
|
||||||
|
this.ctx.putImageData(imageData, 0, 0);
|
||||||
|
fetch("/static/blurhash-overlay.png")
|
||||||
|
.then((response) => response.blob())
|
||||||
|
.then((blob) => {
|
||||||
|
const img = new Image();
|
||||||
|
img.src = URL.createObjectURL(blob);
|
||||||
|
img.onload = () => {
|
||||||
|
this.ctx.drawImage(img, 0, 0, this.width, this.height);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
|
@ -407,6 +407,15 @@
|
||||||
{{ $t('settings.preload_images') }}
|
{{ $t('settings.preload_images') }}
|
||||||
</BooleanSetting>
|
</BooleanSetting>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<BooleanSetting
|
||||||
|
path="useBlurhash"
|
||||||
|
expert="1"
|
||||||
|
:disabled="!hideNsfw"
|
||||||
|
>
|
||||||
|
{{ $t('settings.use_blurhash') }}
|
||||||
|
</BooleanSetting>
|
||||||
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<BooleanSetting
|
<BooleanSetting
|
||||||
path="useOneClickNsfw"
|
path="useOneClickNsfw"
|
||||||
|
|
|
@ -939,6 +939,7 @@
|
||||||
"title": "Version"
|
"title": "Version"
|
||||||
},
|
},
|
||||||
"virtual_scrolling": "Optimize timeline rendering",
|
"virtual_scrolling": "Optimize timeline rendering",
|
||||||
|
"use_blurhash": "Use blurhashes for NSFW thumbnails",
|
||||||
"word_filter": "Word filter",
|
"word_filter": "Word filter",
|
||||||
"wordfilter": "Wordfilter"
|
"wordfilter": "Wordfilter"
|
||||||
},
|
},
|
||||||
|
|
|
@ -117,7 +117,8 @@ export const defaultState = {
|
||||||
maxDepthInThread: undefined, // instance default
|
maxDepthInThread: undefined, // instance default
|
||||||
translationLanguage: undefined, // instance default,
|
translationLanguage: undefined, // instance default,
|
||||||
supportedTranslationLanguages: {}, // instance default
|
supportedTranslationLanguages: {}, // instance default
|
||||||
userProfileDefaultTab: 'statuses'
|
userProfileDefaultTab: 'statuses',
|
||||||
|
useBlurhash: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
// caching the instance default properties
|
// caching the instance default properties
|
||||||
|
|
|
@ -234,13 +234,14 @@ export const parseAttachment = (data) => {
|
||||||
if (masto) {
|
if (masto) {
|
||||||
// Not exactly same...
|
// Not exactly same...
|
||||||
output.mimetype = data.pleroma ? data.pleroma.mime_type : data.type
|
output.mimetype = data.pleroma ? data.pleroma.mime_type : data.type
|
||||||
output.meta = data.meta // not present in BE yet
|
output.meta = data.meta
|
||||||
output.id = data.id
|
output.id = data.id
|
||||||
} else {
|
} else {
|
||||||
output.mimetype = data.mimetype
|
output.mimetype = data.mimetype
|
||||||
// output.meta = ??? missing
|
// output.meta = ??? missing
|
||||||
}
|
}
|
||||||
|
|
||||||
|
output.blurhash = data.blurhash
|
||||||
output.url = data.url
|
output.url = data.url
|
||||||
output.large_thumb_url = data.preview_url
|
output.large_thumb_url = data.preview_url
|
||||||
output.description = data.description
|
output.description = data.description
|
||||||
|
|
BIN
static/blurhash-overlay.png
Executable file
BIN
static/blurhash-overlay.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 27 KiB |
|
@ -2494,6 +2494,11 @@ binary-extensions@^2.0.0:
|
||||||
resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz"
|
resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz"
|
||||||
integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==
|
integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==
|
||||||
|
|
||||||
|
blurhash@^2.0.4:
|
||||||
|
version "2.0.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/blurhash/-/blurhash-2.0.4.tgz#60642a823b50acaaf3732ddb6c7dfd721bdfef2a"
|
||||||
|
integrity sha512-r/As72u2FbucLoK5NTegM/GucxJc3d8GvHc4ngo13IO/nt2HU4gONxNLq1XPN6EM/V8Y9URIa7PcSz2RZu553A==
|
||||||
|
|
||||||
body-parser@1.19.2:
|
body-parser@1.19.2:
|
||||||
version "1.19.2"
|
version "1.19.2"
|
||||||
resolved "https://registry.npmjs.org/body-parser/-/body-parser-1.19.2.tgz"
|
resolved "https://registry.npmjs.org/body-parser/-/body-parser-1.19.2.tgz"
|
||||||
|
|
Loading…
Reference in a new issue