Add blurhash support

This commit is contained in:
FloatingGhost 2022-12-30 04:57:23 +00:00
parent 236b19e854
commit 313ddcebcb
10 changed files with 100 additions and 3 deletions

View file

@ -25,6 +25,7 @@
"@kazvmoe-infra/pinch-zoom-element": "1.2.0", "@kazvmoe-infra/pinch-zoom-element": "1.2.0",
"@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",

View file

@ -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()

View file

@ -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"

View 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>

View file

@ -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"

View file

@ -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"
}, },

View file

@ -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

View file

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

View file

@ -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"