Skip to content

Commit 5ef3be1

Browse files
committed
blurhash の実装をしようとしました。
1 parent ce9abff commit 5ef3be1

4 files changed

Lines changed: 48 additions & 2 deletions

File tree

app/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
"@tauri-apps/plugin-barcode-scanner": "^2.4.4",
1919
"@tauri-apps/plugin-haptics": "^2.3.2",
2020
"@tauri-apps/plugin-opener": "^2",
21+
"blurhash": "^2.0.5",
2122
"boring-avatars": "^2.0.4",
2223
"motion": "^12.36.0",
2324
"react": "^19.2.4",

app/src/components/Composer.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { CssVar } from '../types/Theme'
1010
import { ComposerMode } from '../contexts/Composer'
1111
import { MdImage, MdClose } from 'react-icons/md'
1212
import { uploadImage } from '../utils/uploadImage'
13+
import { computeBlurhash } from '../utils/computeBlurhash'
1314
import { hapticSuccess } from '../utils/haptics'
1415
import { MdSend } from 'react-icons/md'
1516
import { MdEmojiEmotions } from 'react-icons/md'
@@ -224,10 +225,14 @@ export const Composer = (props: Props) => {
224225
// 画像をアップロード
225226
const uploadedMedias = await Promise.all(
226227
mediaDrafts.map(async (media) => {
227-
const [url, typ] = await uploadImage(client, media.file)
228+
const [[url, typ], blurhash] = await Promise.all([
229+
uploadImage(client, media.file),
230+
computeBlurhash(media.file)
231+
])
228232
return {
229233
mediaURL: url,
230-
mediaType: typ
234+
mediaType: typ,
235+
...(blurhash ? { blurhash } : {})
231236
}
232237
})
233238
)

app/src/utils/computeBlurhash.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { encode } from 'blurhash'
2+
3+
const RESIZE = 32
4+
const COMPONENT_X = 4
5+
const COMPONENT_Y = 3
6+
7+
/**
8+
* 画像ファイルからblurhashを計算する。
9+
* 画像以外のファイルが渡された場合はundefinedを返す。
10+
*/
11+
export const computeBlurhash = async (file: File): Promise<string | undefined> => {
12+
if (!file.type.startsWith('image/')) return undefined
13+
14+
const bitmap = await createImageBitmap(file)
15+
16+
const aspect = bitmap.width / bitmap.height
17+
const width = aspect >= 1 ? RESIZE : Math.round(RESIZE * aspect)
18+
const height = aspect >= 1 ? Math.round(RESIZE / aspect) : RESIZE
19+
20+
const canvas = new OffscreenCanvas(width, height)
21+
const ctx = canvas.getContext('2d')
22+
if (!ctx) return undefined
23+
24+
ctx.drawImage(bitmap, 0, 0, width, height)
25+
bitmap.close()
26+
27+
const imageData = ctx.getImageData(0, 0, width, height)
28+
return encode(imageData.data, width, height, COMPONENT_X, COMPONENT_Y)
29+
}

pnpm-lock.yaml

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)