|
1 | 1 | // @see https://stackoverflow.com/questions/2541481/get-average-color-of-image-via-javascript |
2 | 2 |
|
3 | | -import { encode } from 'blurhash' |
| 3 | +import { rgbaToThumbHash } from 'thumbhash' |
4 | 4 |
|
5 | 5 | import { computeImageMeta } from '@haklex/rich-editor/renderers' |
6 | 6 |
|
@@ -28,92 +28,20 @@ export function rgbObjectToHex(rgb: { r: number; g: number; b: number }) { |
28 | 28 | return rgbToHex(rgb.r, rgb.g, rgb.b) |
29 | 29 | } |
30 | 30 |
|
31 | | -export function getBlurHash(imageObject: HTMLImageElement) { |
32 | | - const canvas = document.createElement('canvas'), |
33 | | - ctx = canvas.getContext('2d')! |
34 | | - |
35 | | - canvas.width = imageObject.naturalWidth |
36 | | - canvas.height = imageObject.naturalHeight |
37 | | - |
38 | | - ctx.drawImage(imageObject, 0, 0) |
39 | | - |
40 | | - const imageData = ctx.getImageData(0, 0, 32, 32) |
41 | | - const pixels = new Uint8ClampedArray(imageData.data) |
42 | | - const componentX = 4 |
43 | | - const componentY = 4 |
44 | | - |
45 | | - return encode(pixels, 32, 32, componentX, componentY) |
46 | | -} |
47 | | - |
48 | | -const getImageData = (image: HTMLImageElement) => { |
49 | | - const canvas = document.createElement('canvas') |
50 | | - canvas.width = image.width |
51 | | - canvas.height = image.height |
52 | | - const context = canvas.getContext('2d')! |
53 | | - context.drawImage(image, 0, 0) |
54 | | - return context.getImageData(0, 0, image.width, image.height) |
55 | | -} |
56 | | - |
57 | | -export const encodeImageToBlurhash = (image: HTMLImageElement) => { |
58 | | - const imageData = getImageData(image) |
59 | | - return encode(imageData.data, imageData.width, imageData.height, 4, 4) |
60 | | -} |
61 | | - |
62 | | -export const encodeImageToBlurhashWebgl = (image: HTMLImageElement) => { |
| 31 | +export function getThumbhash(img: HTMLImageElement): string { |
| 32 | + const scale = Math.min(100 / img.naturalWidth, 100 / img.naturalHeight, 1) |
| 33 | + const sw = Math.max(1, Math.round(img.naturalWidth * scale)) |
| 34 | + const sh = Math.max(1, Math.round(img.naturalHeight * scale)) |
63 | 35 | const canvas = document.createElement('canvas') |
64 | | - const gl = (canvas.getContext('webgl') || |
65 | | - canvas.getContext('experimental-webgl')) as WebGLRenderingContext |
66 | | - |
67 | | - if (!gl) { |
68 | | - throw new Error('WebGL not supported') |
69 | | - } |
70 | | - |
71 | | - canvas.width = image.naturalWidth |
72 | | - canvas.height = image.naturalHeight |
73 | | - |
74 | | - const texture = gl.createTexture() |
75 | | - gl.bindTexture(gl.TEXTURE_2D, texture) |
76 | | - gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image) |
77 | | - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE) |
78 | | - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE) |
79 | | - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR) |
80 | | - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR) |
81 | | - |
82 | | - const framebuffer = gl.createFramebuffer() |
83 | | - gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer) |
84 | | - gl.framebufferTexture2D( |
85 | | - gl.FRAMEBUFFER, |
86 | | - gl.COLOR_ATTACHMENT0, |
87 | | - gl.TEXTURE_2D, |
88 | | - texture, |
89 | | - 0, |
90 | | - ) |
91 | | - |
92 | | - const pixels = new Uint8Array(image.naturalWidth * image.naturalHeight * 4) |
93 | | - gl.readPixels( |
94 | | - 0, |
95 | | - 0, |
96 | | - image.naturalWidth, |
97 | | - image.naturalHeight, |
98 | | - gl.RGBA, |
99 | | - gl.UNSIGNED_BYTE, |
100 | | - pixels, |
101 | | - ) |
102 | | - |
103 | | - const resizedCanvas = document.createElement('canvas') |
104 | | - resizedCanvas.width = 32 |
105 | | - resizedCanvas.height = 32 |
106 | | - const resizedCtx = resizedCanvas.getContext('2d')! |
107 | | - const imageData = new ImageData( |
108 | | - new Uint8ClampedArray(pixels), |
109 | | - image.naturalWidth, |
110 | | - image.naturalHeight, |
111 | | - ) |
112 | | - resizedCtx.putImageData(imageData, 0, 0) |
113 | | - resizedCtx.drawImage(resizedCanvas, 0, 0, 32, 32) |
114 | | - const resizedImageData = resizedCtx.getImageData(0, 0, 32, 32) |
115 | | - |
116 | | - return encode(resizedImageData.data, 32, 32, 4, 4) |
| 36 | + canvas.width = sw |
| 37 | + canvas.height = sh |
| 38 | + const ctx = canvas.getContext('2d')! |
| 39 | + ctx.drawImage(img, 0, 0, sw, sh) |
| 40 | + const rgba = ctx.getImageData(0, 0, sw, sh).data |
| 41 | + const u8 = rgbaToThumbHash(sw, sh, rgba) |
| 42 | + let bin = '' |
| 43 | + for (let i = 0; i < u8.length; i++) bin += String.fromCharCode(u8[i]) |
| 44 | + return btoa(bin) |
117 | 45 | } |
118 | 46 |
|
119 | 47 | export const encodeImageToThumbhash = async (image: HTMLImageElement) => { |
|
0 commit comments