@react-native-ohos/react-native-blurhash 2.0.4-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.OpenSource +11 -0
- package/README.md +13 -0
- package/harmony/blurhash/BuildProfile.ets +17 -0
- package/harmony/blurhash/build-profile.json5 +9 -0
- package/harmony/blurhash/hvigorfile.ts +2 -0
- package/harmony/blurhash/index.ets +2 -0
- package/harmony/blurhash/oh-package.json5 +13 -0
- package/harmony/blurhash/src/main/cpp/Blurhash.cpp +314 -0
- package/harmony/blurhash/src/main/cpp/Blurhash.hpp +27 -0
- package/harmony/blurhash/src/main/cpp/BlurhashNode.cpp +87 -0
- package/harmony/blurhash/src/main/cpp/BlurhashNode.h +35 -0
- package/harmony/blurhash/src/main/cpp/BlurhashPackage.h +53 -0
- package/harmony/blurhash/src/main/cpp/BlurhashViewComponentInstance.cpp +137 -0
- package/harmony/blurhash/src/main/cpp/BlurhashViewComponentInstance.h +34 -0
- package/harmony/blurhash/src/main/cpp/BlurhashViewJSIBinder.h +34 -0
- package/harmony/blurhash/src/main/cpp/CMakeLists.txt +13 -0
- package/harmony/blurhash/src/main/cpp/ComponentDescriptors.h +18 -0
- package/harmony/blurhash/src/main/cpp/EventEmitters.cpp +36 -0
- package/harmony/blurhash/src/main/cpp/EventEmitters.h +40 -0
- package/harmony/blurhash/src/main/cpp/Props.cpp +25 -0
- package/harmony/blurhash/src/main/cpp/Props.h +73 -0
- package/harmony/blurhash/src/main/cpp/RNBlurhashTurboModule.cpp +42 -0
- package/harmony/blurhash/src/main/cpp/RNBlurhashTurboModule.h +19 -0
- package/harmony/blurhash/src/main/cpp/ShadowNodes.cpp +15 -0
- package/harmony/blurhash/src/main/cpp/ShadowNodes.h +27 -0
- package/harmony/blurhash/src/main/cpp/SparseArray.cpp +28 -0
- package/harmony/blurhash/src/main/cpp/SparseArray.h +26 -0
- package/harmony/blurhash/src/main/cpp/napi_init.cpp +68 -0
- package/harmony/blurhash/src/main/cpp/stb_image.h +7656 -0
- package/harmony/blurhash/src/main/cpp/stb_image_write.h +1724 -0
- package/harmony/blurhash/src/main/cpp/types/libblurhash/Index.d.ts +1 -0
- package/harmony/blurhash/src/main/cpp/types/libblurhash/oh-package.json5 +6 -0
- package/harmony/blurhash/src/main/ets/BlurhashPackage.ts +58 -0
- package/harmony/blurhash/src/main/ets/BlurhashTurboModule.ts +49 -0
- package/harmony/blurhash/src/main/ets/BlurhashView.ets +107 -0
- package/harmony/blurhash/src/main/ets/Logger.ts +64 -0
- package/harmony/blurhash/src/main/ets/RNCBlurhashView.ts +157 -0
- package/harmony/blurhash/src/main/ets/RNCSpecs.ts +163 -0
- package/harmony/blurhash/src/main/ets/TMSpecs.ts +33 -0
- package/harmony/blurhash/src/main/module.json5 +7 -0
- package/harmony/blurhash/src/main/resources/base/element/string.json +8 -0
- package/harmony/blurhash/src/main/resources/en_US/element/string.json +8 -0
- package/harmony/blurhash/src/main/resources/zh_CN/element/string.json +8 -0
- package/harmony/blurhash/ts.ts +26 -0
- package/harmony/blurhash.har +0 -0
- package/lib/commonjs/index.js +95 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/commonjs/specs/NativeBlurhashModule.js +9 -0
- package/lib/commonjs/specs/NativeBlurhashModule.js.map +1 -0
- package/lib/commonjs/specs/NativeBlurhashView.js +10 -0
- package/lib/commonjs/specs/NativeBlurhashView.js.map +1 -0
- package/lib/commonjs/utils.js +56 -0
- package/lib/commonjs/utils.js.map +1 -0
- package/lib/module/index.js +85 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/specs/NativeBlurhashModule.js +3 -0
- package/lib/module/specs/NativeBlurhashModule.js.map +1 -0
- package/lib/module/specs/NativeBlurhashView.js +3 -0
- package/lib/module/specs/NativeBlurhashView.js.map +1 -0
- package/lib/module/utils.js +48 -0
- package/lib/module/utils.js.map +1 -0
- package/lib/typescript/index.d.ts +90 -0
- package/lib/typescript/specs/NativeBlurhashModule.d.ts +9 -0
- package/lib/typescript/specs/NativeBlurhashView.d.ts +19 -0
- package/lib/typescript/utils.d.ts +22 -0
- package/package.json +113 -0
- package/react-native-blurhash.podspec +34 -0
- package/src/index.tsx +131 -0
- package/src/specs/NativeBlurhashModule.ts +9 -0
- package/src/specs/NativeBlurhashView.ts +19 -0
- package/src/utils.ts +147 -0
package/package.json
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@react-native-ohos/react-native-blurhash",
|
|
3
|
+
"title": "React Native Blurhash",
|
|
4
|
+
"version": "2.0.4-rc.1",
|
|
5
|
+
"description": "🖼 Blurhash is a compact representation of a placeholder for an image. This is a Native UI Module for React Native to asynchronously wrap the Blurhash implementations and make them usable in React Native. Also supports encoding!",
|
|
6
|
+
"main": "lib/commonjs/index.js",
|
|
7
|
+
"types": "lib/typescript/index.d.ts",
|
|
8
|
+
"react-native": "src/index.tsx",
|
|
9
|
+
"module": "lib/module/index.js",
|
|
10
|
+
"files": [
|
|
11
|
+
"harmony",
|
|
12
|
+
"README.md",
|
|
13
|
+
"lib/commonjs",
|
|
14
|
+
"lib/module",
|
|
15
|
+
"lib/typescript",
|
|
16
|
+
"src",
|
|
17
|
+
"react-native-blurhash.podspec"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"lint": "eslint \"src/**/*.{js,ts,tsx}\" \"example/**/*.{js,ts,tsx}\"",
|
|
21
|
+
"build": "bob build",
|
|
22
|
+
"release": "release-it"
|
|
23
|
+
},
|
|
24
|
+
"repository": "https://github.com/mrousavy/react-native-blurhash",
|
|
25
|
+
"publishConfig": {
|
|
26
|
+
"registry": "https://registry.npmjs.org/"
|
|
27
|
+
},
|
|
28
|
+
"keywords": [
|
|
29
|
+
"react-native",
|
|
30
|
+
"blurhash",
|
|
31
|
+
"image",
|
|
32
|
+
"ui",
|
|
33
|
+
"ux",
|
|
34
|
+
"component",
|
|
35
|
+
"loading",
|
|
36
|
+
"placeholder",
|
|
37
|
+
"harmony"
|
|
38
|
+
],
|
|
39
|
+
"author": {
|
|
40
|
+
"name": "Marc Rousavy",
|
|
41
|
+
"email": "marcrousavy@hotmail.com"
|
|
42
|
+
},
|
|
43
|
+
"license": "MIT",
|
|
44
|
+
"licenseFilename": "LICENSE",
|
|
45
|
+
"readmeFilename": "README.md",
|
|
46
|
+
"peerDependencies": {
|
|
47
|
+
"react": ">=16.8.1",
|
|
48
|
+
"react-native": ">=0.60.0-rc.0 <1.0.x"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"@react-native-community/eslint-config": "^3.2.0",
|
|
52
|
+
"@react-native-community/eslint-plugin": "^1.3.0",
|
|
53
|
+
"@release-it/conventional-changelog": "^4.1.0",
|
|
54
|
+
"@types/react": "^18.2.6",
|
|
55
|
+
"eslint": "^8.56.0",
|
|
56
|
+
"eslint-config-prettier": "^9.1.0",
|
|
57
|
+
"eslint-plugin-prettier": "^5.1.3",
|
|
58
|
+
"eslint-plugin-react-native": "^4.1.0",
|
|
59
|
+
"prettier": "^3.2.4",
|
|
60
|
+
"react": "^18.2.0",
|
|
61
|
+
"react-native": "^0.73.2",
|
|
62
|
+
"react-native-builder-bob": "^0.18.2",
|
|
63
|
+
"release-it": "^14.12.4",
|
|
64
|
+
"typescript": "^5.0.4"
|
|
65
|
+
},
|
|
66
|
+
"react-native-builder-bob": {
|
|
67
|
+
"source": "harmony",
|
|
68
|
+
"output": "lib",
|
|
69
|
+
"targets": [
|
|
70
|
+
"commonjs",
|
|
71
|
+
"module",
|
|
72
|
+
[
|
|
73
|
+
"typescript",
|
|
74
|
+
{
|
|
75
|
+
"project": "tsconfig.build.json"
|
|
76
|
+
}
|
|
77
|
+
]
|
|
78
|
+
]
|
|
79
|
+
},
|
|
80
|
+
"release-it": {
|
|
81
|
+
"git": {
|
|
82
|
+
"commitMessage": "chore: release ${version}",
|
|
83
|
+
"tagName": "v${version}"
|
|
84
|
+
},
|
|
85
|
+
"npm": {
|
|
86
|
+
"publish": true
|
|
87
|
+
},
|
|
88
|
+
"github": {
|
|
89
|
+
"release": true
|
|
90
|
+
},
|
|
91
|
+
"plugins": {
|
|
92
|
+
"@release-it/conventional-changelog": {
|
|
93
|
+
"preset": "angular"
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
"eslintIgnore": [
|
|
98
|
+
"node_modules/",
|
|
99
|
+
"lib/"
|
|
100
|
+
],
|
|
101
|
+
"codegenConfig": {
|
|
102
|
+
"name": "blurhash_codegen",
|
|
103
|
+
"type": "all",
|
|
104
|
+
"jsSrcsDir": "./src/specs",
|
|
105
|
+
"android": {
|
|
106
|
+
"javaPackageName": "com.mrousavy.blurhash"
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
"harmony":{
|
|
110
|
+
"alias": "react-native-blurhash"
|
|
111
|
+
},
|
|
112
|
+
"packageManager": "yarn@1.22.19+sha1.4ba7fc5c6e704fce2066ecbfb0b0d8976fe62447"
|
|
113
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
require "json"
|
|
2
|
+
|
|
3
|
+
package = JSON.parse(File.read(File.join(__dir__, "package.json")))
|
|
4
|
+
|
|
5
|
+
Pod::Spec.new do |s|
|
|
6
|
+
s.name = "react-native-blurhash"
|
|
7
|
+
s.version = package["version"]
|
|
8
|
+
s.summary = package["description"]
|
|
9
|
+
s.description = <<-DESC
|
|
10
|
+
react-native-blurhash
|
|
11
|
+
DESC
|
|
12
|
+
s.homepage = "https://github.com/mrousavy/react-native-blurhash"
|
|
13
|
+
# brief license entry:
|
|
14
|
+
s.license = "MIT"
|
|
15
|
+
# optional - use expanded license entry instead:
|
|
16
|
+
# s.license = { :type => "MIT", :file => "LICENSE" }
|
|
17
|
+
s.authors = { "Marc Rousavy" => "marcrousavy@hotmail.com" }
|
|
18
|
+
s.ios.deployment_target = '9.0'
|
|
19
|
+
s.tvos.deployment_target = '9.0'
|
|
20
|
+
s.source = { :git => "https://github.com/mrousavy/react-native-blurhash.git", :tag => "#{s.version}" }
|
|
21
|
+
|
|
22
|
+
s.source_files = "ios/**/*.{h,c,m,mm,swift}"
|
|
23
|
+
|
|
24
|
+
s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' }
|
|
25
|
+
|
|
26
|
+
s.swift_version = "5.0"
|
|
27
|
+
|
|
28
|
+
if defined?(install_modules_dependencies()) != nil
|
|
29
|
+
install_modules_dependencies(s);
|
|
30
|
+
else
|
|
31
|
+
s.dependency "React-Core"
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
package/src/index.tsx
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { Platform, type NativeSyntheticEvent, type ViewProps } from 'react-native';
|
|
3
|
+
import NativeBlurhashModule from './specs/NativeBlurhashModule';
|
|
4
|
+
import NativeBlurhashView from './specs/NativeBlurhashView';
|
|
5
|
+
import { decode83, decodeDC, isBlurhashValid, type RGB } from './utils';
|
|
6
|
+
|
|
7
|
+
export interface BlurhashProps extends Omit<ViewProps, 'children'> {
|
|
8
|
+
/**
|
|
9
|
+
* The blurhash string to use. Example: `LGFFaXYk^6#M@-5c,1J5@[or[Q6`.
|
|
10
|
+
*/
|
|
11
|
+
blurhash: string;
|
|
12
|
+
/**
|
|
13
|
+
* The width (resolution) to decode to. Higher values decrease performance, use `16` for large lists, otherwise you can increase it to `32`.
|
|
14
|
+
* @default 32
|
|
15
|
+
*/
|
|
16
|
+
decodeWidth?: number;
|
|
17
|
+
/**
|
|
18
|
+
* The height (resolution) to decode to. Higher values decrease performance, use `16` for large lists, otherwise you can increase it to `32`.
|
|
19
|
+
* @default 32
|
|
20
|
+
*/
|
|
21
|
+
decodeHeight?: number;
|
|
22
|
+
/**
|
|
23
|
+
* Adjusts the contrast of the output image. Tweak it if you want a different look for your placeholders.
|
|
24
|
+
* @default 1.0
|
|
25
|
+
*/
|
|
26
|
+
decodePunch?: number;
|
|
27
|
+
/**
|
|
28
|
+
* Asynchronously decode the Blurhash on a background Thread instead of the UI-Thread.
|
|
29
|
+
* Read the [performance documentation](https://github.com/mrousavy/react-native-blurhash#performance)
|
|
30
|
+
* before enabling this.
|
|
31
|
+
* @default false
|
|
32
|
+
*/
|
|
33
|
+
decodeAsync?: boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Adjusts the resize mode of the image.
|
|
36
|
+
* @default 'cover'
|
|
37
|
+
*/
|
|
38
|
+
resizeMode?: 'cover' | 'contain' | 'stretch' | 'center';
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Emitted when the Blurhash received new parameters and started to decode the given `blurhash` string.
|
|
42
|
+
*/
|
|
43
|
+
onLoadStart?: () => void;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Emitted when the Blurhash successfully decoded the given `blurhash` string and rendered the image to the `<Blurhash>` view.
|
|
47
|
+
*/
|
|
48
|
+
onLoadEnd?: () => void;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Emitted when the Blurhash failed to decode/load.
|
|
52
|
+
*/
|
|
53
|
+
onLoadError?: (message?: string) => void;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export class Blurhash extends React.PureComponent<BlurhashProps> {
|
|
57
|
+
static displayName = 'Blurhash';
|
|
58
|
+
|
|
59
|
+
constructor(props: BlurhashProps) {
|
|
60
|
+
super(props);
|
|
61
|
+
this._onLoadStart = this._onLoadStart.bind(this);
|
|
62
|
+
this._onLoadEnd = this._onLoadEnd.bind(this);
|
|
63
|
+
this._onLoadError = this._onLoadError.bind(this);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Encodes the given image URI to a blurhash string
|
|
68
|
+
* @param imageUri An URI to an Image parseable by the react native image loader
|
|
69
|
+
* @param componentsX The number of X components
|
|
70
|
+
* @param componentsY The number of Y components
|
|
71
|
+
* @example
|
|
72
|
+
* const blurhash = await Blurhash.encode('https://blurha.sh/assets/images/img2.jpg')
|
|
73
|
+
*/
|
|
74
|
+
static encode(imageUri: string, componentsX: number, componentsY: number): Promise<string> {
|
|
75
|
+
if (typeof imageUri !== 'string') throw new Error('imageUri must be a non-empty string!');
|
|
76
|
+
if (typeof componentsX !== 'number') throw new Error('componentsX must be a valid positive number!');
|
|
77
|
+
if (typeof componentsY !== 'number') throw new Error('componentsY must be a valid positive number!');
|
|
78
|
+
|
|
79
|
+
return NativeBlurhashModule.createBlurhashFromImage(imageUri, componentsX, componentsY);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Gets the average color in a given blurhash string.
|
|
84
|
+
*
|
|
85
|
+
* This uses the JS blurhash decoder, so it might be slow.
|
|
86
|
+
* @param blurhash The blurhash string
|
|
87
|
+
* @example
|
|
88
|
+
* const averageColor = Blurhash.getAverageColor(`LGFFaXYk^6#M@-5c,1J5@[or[Q6.`)
|
|
89
|
+
*/
|
|
90
|
+
static getAverageColor(blurhash: string): RGB | undefined {
|
|
91
|
+
if (blurhash == null || blurhash.length < 7) return undefined;
|
|
92
|
+
|
|
93
|
+
const value = decode83(blurhash.substring(2, 6));
|
|
94
|
+
return decodeDC(value);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Clears the cosine cache and frees up memory.
|
|
99
|
+
*
|
|
100
|
+
* @platform Android
|
|
101
|
+
* @see https://github.com/mrousavy/react-native-blurhash#cosine-operations
|
|
102
|
+
*/
|
|
103
|
+
static clearCosineCache(): void {
|
|
104
|
+
if (Platform.OS === 'android' || Platform.OS === 'harmony') NativeBlurhashModule.clearCosineCache();
|
|
105
|
+
else console.warn('Blurhash.clearCosineCache is only available on Android.');
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Verifies if the given blurhash is valid by checking it's type, length and size flag.
|
|
110
|
+
*
|
|
111
|
+
* This uses the JS blurhash decoder, so it might be slow.
|
|
112
|
+
* @param blurhash The given blurhash string
|
|
113
|
+
*/
|
|
114
|
+
static isBlurhashValid(blurhash: string): ReturnType<typeof isBlurhashValid> {
|
|
115
|
+
return isBlurhashValid(blurhash);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
_onLoadStart() {
|
|
119
|
+
if (this.props.onLoadStart != null) this.props.onLoadStart();
|
|
120
|
+
}
|
|
121
|
+
_onLoadEnd() {
|
|
122
|
+
if (this.props.onLoadEnd != null) this.props.onLoadEnd();
|
|
123
|
+
}
|
|
124
|
+
_onLoadError(event?: NativeSyntheticEvent<{ message?: string }>) {
|
|
125
|
+
if (this.props.onLoadError != null) this.props.onLoadError(event?.nativeEvent?.message);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
render() {
|
|
129
|
+
return <NativeBlurhashView {...this.props} onLoadStart={this._onLoadStart} onLoadEnd={this._onLoadEnd} onLoadError={this._onLoadError} />;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type TurboModule, TurboModuleRegistry } from 'react-native';
|
|
2
|
+
import type { Int32 } from 'react-native/Libraries/Types/CodegenTypes';
|
|
3
|
+
|
|
4
|
+
export interface Spec extends TurboModule {
|
|
5
|
+
createBlurhashFromImage: (imageUri: string, componentsX: Int32, componentsY: Int32) => Promise<string>;
|
|
6
|
+
clearCosineCache: () => void;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export default TurboModuleRegistry.getEnforcing<Spec>('BlurhashModule');
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent';
|
|
2
|
+
import type { DirectEventHandler, Double, Int32, WithDefault } from 'react-native/Libraries/Types/CodegenTypes';
|
|
3
|
+
import type { ViewProps } from 'react-native';
|
|
4
|
+
|
|
5
|
+
type OnLoadErrorEvent = { message?: string };
|
|
6
|
+
|
|
7
|
+
interface NativeProps extends ViewProps {
|
|
8
|
+
blurhash: string;
|
|
9
|
+
decodeWidth?: WithDefault<Int32, 32>;
|
|
10
|
+
decodeHeight?: WithDefault<Int32, 32>;
|
|
11
|
+
decodePunch?: WithDefault<Double, 1>;
|
|
12
|
+
decodeAsync?: WithDefault<boolean, false>;
|
|
13
|
+
resizeMode?: WithDefault<'cover' | 'contain' | 'stretch' | 'center', 'cover'>;
|
|
14
|
+
onLoadStart?: DirectEventHandler<null>;
|
|
15
|
+
onLoadEnd?: DirectEventHandler<null>;
|
|
16
|
+
onLoadError?: DirectEventHandler<OnLoadErrorEvent>;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export default codegenNativeComponent<NativeProps>('BlurhashView');
|
package/src/utils.ts
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
// Some functions from the Blurhash JS implementation that are used for light tasks (such as getting the average color or validating if a blurhash string is valid)
|
|
2
|
+
|
|
3
|
+
export interface RGB {
|
|
4
|
+
/**
|
|
5
|
+
* The Red value component of this RGB instance. Ranges from 0 to 255.
|
|
6
|
+
*/
|
|
7
|
+
r: number;
|
|
8
|
+
/**
|
|
9
|
+
* The Green value component of this RGB instance. Ranges from 0 to 255.
|
|
10
|
+
*/
|
|
11
|
+
g: number;
|
|
12
|
+
/**
|
|
13
|
+
* The Blue value component of this RGB instance. Ranges from 0 to 255.
|
|
14
|
+
*/
|
|
15
|
+
b: number;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function sRGBToLinear(value: number): number {
|
|
19
|
+
const v = value / 255;
|
|
20
|
+
if (v <= 0.04045) return v / 12.92;
|
|
21
|
+
else return Math.pow((v + 0.055) / 1.055, 2.4);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function decodeDC(value: number): RGB {
|
|
25
|
+
const intR = value >> 16;
|
|
26
|
+
const intG = (value >> 8) & 255;
|
|
27
|
+
const intB = value & 255;
|
|
28
|
+
return { r: sRGBToLinear(intR) * 255, g: sRGBToLinear(intG) * 255, b: sRGBToLinear(intB) * 255 };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function decode83(str: string): number {
|
|
32
|
+
let value = 0;
|
|
33
|
+
for (let i = 0; i < str.length; i++) {
|
|
34
|
+
const c = str[i];
|
|
35
|
+
const digit = digitCharacters.indexOf(c);
|
|
36
|
+
value = value * 83 + digit;
|
|
37
|
+
}
|
|
38
|
+
return value;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function validateBlurhash(blurhash: string): void {
|
|
42
|
+
if (!blurhash || blurhash.length < 6) throw new Error('The blurhash string must be at least 6 characters');
|
|
43
|
+
|
|
44
|
+
const sizeFlag = decode83(blurhash[0]);
|
|
45
|
+
const numY = Math.floor(sizeFlag / 9) + 1;
|
|
46
|
+
const numX = (sizeFlag % 9) + 1;
|
|
47
|
+
|
|
48
|
+
if (blurhash.length !== 4 + 2 * numX * numY)
|
|
49
|
+
throw new Error(`blurhash length mismatch: length is ${blurhash.length} but it should be ${4 + 2 * numX * numY}`);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function isBlurhashValid(blurhash: string): { isValid: true } | { isValid: false; errorReason: string } {
|
|
53
|
+
try {
|
|
54
|
+
validateBlurhash(blurhash);
|
|
55
|
+
} catch (error) {
|
|
56
|
+
const message = error instanceof Error ? error.message : JSON.stringify(error);
|
|
57
|
+
return { isValid: false, errorReason: message };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return { isValid: true };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const digitCharacters = [
|
|
64
|
+
'0',
|
|
65
|
+
'1',
|
|
66
|
+
'2',
|
|
67
|
+
'3',
|
|
68
|
+
'4',
|
|
69
|
+
'5',
|
|
70
|
+
'6',
|
|
71
|
+
'7',
|
|
72
|
+
'8',
|
|
73
|
+
'9',
|
|
74
|
+
'A',
|
|
75
|
+
'B',
|
|
76
|
+
'C',
|
|
77
|
+
'D',
|
|
78
|
+
'E',
|
|
79
|
+
'F',
|
|
80
|
+
'G',
|
|
81
|
+
'H',
|
|
82
|
+
'I',
|
|
83
|
+
'J',
|
|
84
|
+
'K',
|
|
85
|
+
'L',
|
|
86
|
+
'M',
|
|
87
|
+
'N',
|
|
88
|
+
'O',
|
|
89
|
+
'P',
|
|
90
|
+
'Q',
|
|
91
|
+
'R',
|
|
92
|
+
'S',
|
|
93
|
+
'T',
|
|
94
|
+
'U',
|
|
95
|
+
'V',
|
|
96
|
+
'W',
|
|
97
|
+
'X',
|
|
98
|
+
'Y',
|
|
99
|
+
'Z',
|
|
100
|
+
'a',
|
|
101
|
+
'b',
|
|
102
|
+
'c',
|
|
103
|
+
'd',
|
|
104
|
+
'e',
|
|
105
|
+
'f',
|
|
106
|
+
'g',
|
|
107
|
+
'h',
|
|
108
|
+
'i',
|
|
109
|
+
'j',
|
|
110
|
+
'k',
|
|
111
|
+
'l',
|
|
112
|
+
'm',
|
|
113
|
+
'n',
|
|
114
|
+
'o',
|
|
115
|
+
'p',
|
|
116
|
+
'q',
|
|
117
|
+
'r',
|
|
118
|
+
's',
|
|
119
|
+
't',
|
|
120
|
+
'u',
|
|
121
|
+
'v',
|
|
122
|
+
'w',
|
|
123
|
+
'x',
|
|
124
|
+
'y',
|
|
125
|
+
'z',
|
|
126
|
+
'#',
|
|
127
|
+
'$',
|
|
128
|
+
'%',
|
|
129
|
+
'*',
|
|
130
|
+
'+',
|
|
131
|
+
',',
|
|
132
|
+
'-',
|
|
133
|
+
'.',
|
|
134
|
+
':',
|
|
135
|
+
';',
|
|
136
|
+
'=',
|
|
137
|
+
'?',
|
|
138
|
+
'@',
|
|
139
|
+
'[',
|
|
140
|
+
']',
|
|
141
|
+
'^',
|
|
142
|
+
'_',
|
|
143
|
+
'{',
|
|
144
|
+
'|',
|
|
145
|
+
'}',
|
|
146
|
+
'~',
|
|
147
|
+
];
|