@onekeyfe/hd-core 1.2.0-alpha.167 → 1.2.0-alpha.169
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/__tests__/firmware-update/firmware-update-v4-install-poll.test.ts +6 -6
- package/__tests__/pro2Wallpaper.test.ts +66 -0
- package/__tests__/protocol-v2.test.ts +309 -19
- package/dist/api/FirmwareUpdateV4.d.ts +2 -0
- package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
- package/dist/api/protocol-v2/DeviceUploadWallpaper.d.ts +2 -1
- package/dist/api/protocol-v2/DeviceUploadWallpaper.d.ts.map +1 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/device/DeviceConnector.d.ts +1 -1
- package/dist/device/DeviceConnector.d.ts.map +1 -1
- package/dist/index.d.ts +5 -2
- package/dist/index.js +277 -72
- package/dist/utils/pro2Wallpaper.d.ts +3 -1
- package/dist/utils/pro2Wallpaper.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/api/FirmwareUpdateV4.ts +127 -17
- package/src/api/protocol-v2/DeviceUploadWallpaper.ts +12 -2
- package/src/core/index.ts +6 -100
- package/src/device/DeviceConnector.ts +8 -3
- package/src/utils/pro2Wallpaper.ts +197 -1
|
@@ -6,12 +6,26 @@ import { ERRORS, HardwareErrorCode } from '@onekeyfe/hd-shared';
|
|
|
6
6
|
export const PRO2_WALLPAPER_WIDTH = 604;
|
|
7
7
|
export const PRO2_WALLPAPER_HEIGHT = 1024;
|
|
8
8
|
|
|
9
|
-
export type Pro2WallpaperColorFormat = 'RGB565' | 'RGB565A8';
|
|
9
|
+
export type Pro2WallpaperColorFormat = 'RGB565' | 'RGB565A8' | 'I8';
|
|
10
|
+
|
|
11
|
+
export type Pro2WallpaperEncoding = 'rgb565' | 'i8-lz4';
|
|
10
12
|
|
|
11
13
|
export type Pro2ImageAlphaMode = 'preserve' | 'black-background';
|
|
12
14
|
|
|
13
15
|
const COLOR_FORMAT_RGB565 = 0x12;
|
|
14
16
|
const COLOR_FORMAT_RGB565A8 = 0x14;
|
|
17
|
+
const COLOR_FORMAT_I8 = 0x0a;
|
|
18
|
+
const IMAGE_FLAG_COMPRESSED = 0x0008;
|
|
19
|
+
const IMAGE_COMPRESSION_LZ4 = 0x00000002;
|
|
20
|
+
const I8_PALETTE_SIZE = 256 * 4;
|
|
21
|
+
const I8_RED_LEVELS = 6;
|
|
22
|
+
const I8_GREEN_LEVELS = 7;
|
|
23
|
+
const I8_BLUE_LEVELS = 6;
|
|
24
|
+
const LZ4_MIN_MATCH = 4;
|
|
25
|
+
const LZ4_LAST_LITERALS = 5;
|
|
26
|
+
const LZ4_MATCH_FIND_LIMIT = 12;
|
|
27
|
+
const LZ4_HASH_BITS = 16;
|
|
28
|
+
const LZ4_HASH_MULTIPLIER = -1640531535;
|
|
15
29
|
|
|
16
30
|
const RED_THRESHOLD = [
|
|
17
31
|
1, 7, 3, 5, 0, 8, 2, 6, 7, 1, 5, 3, 8, 0, 6, 2, 3, 5, 0, 8, 2, 6, 1, 7, 5, 3, 8, 0, 6, 2, 7, 1, 0,
|
|
@@ -38,6 +52,184 @@ function align(value: number, boundary: number): number {
|
|
|
38
52
|
return Math.ceil(value / boundary) * boundary;
|
|
39
53
|
}
|
|
40
54
|
|
|
55
|
+
function writeLz4Length(output: Uint8Array, offset: number, length: number) {
|
|
56
|
+
let cursor = offset;
|
|
57
|
+
let remaining = length;
|
|
58
|
+
while (remaining >= 0xff) {
|
|
59
|
+
output[cursor] = 0xff;
|
|
60
|
+
cursor += 1;
|
|
61
|
+
remaining -= 0xff;
|
|
62
|
+
}
|
|
63
|
+
output[cursor] = remaining;
|
|
64
|
+
return cursor + 1;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function readUint32LittleEndian(data: Uint8Array, offset: number) {
|
|
68
|
+
return (
|
|
69
|
+
data[offset] | (data[offset + 1] << 8) | (data[offset + 2] << 16) | (data[offset + 3] << 24)
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function getLz4Hash(sequence: number) {
|
|
74
|
+
return (Math.imul(sequence, LZ4_HASH_MULTIPLIER) >>> (32 - LZ4_HASH_BITS)) & 0xffff;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function compressLz4Block(input: Uint8Array) {
|
|
78
|
+
const output = new Uint8Array(input.byteLength + Math.floor(input.byteLength / 0xff) + 16);
|
|
79
|
+
const hashTable = new Int32Array(1 << LZ4_HASH_BITS);
|
|
80
|
+
hashTable.fill(-1);
|
|
81
|
+
|
|
82
|
+
let anchor = 0;
|
|
83
|
+
let inputOffset = 0;
|
|
84
|
+
let outputOffset = 0;
|
|
85
|
+
const matchFindEnd = input.byteLength - LZ4_MATCH_FIND_LIMIT;
|
|
86
|
+
const matchCopyEnd = input.byteLength - LZ4_LAST_LITERALS;
|
|
87
|
+
|
|
88
|
+
while (inputOffset <= matchFindEnd) {
|
|
89
|
+
const sequence = readUint32LittleEndian(input, inputOffset);
|
|
90
|
+
const hash = getLz4Hash(sequence);
|
|
91
|
+
const reference = hashTable[hash];
|
|
92
|
+
hashTable[hash] = inputOffset;
|
|
93
|
+
|
|
94
|
+
const matchOffset = inputOffset - reference;
|
|
95
|
+
const hasMatch =
|
|
96
|
+
reference >= 0 &&
|
|
97
|
+
matchOffset <= 0xffff &&
|
|
98
|
+
readUint32LittleEndian(input, reference) === sequence;
|
|
99
|
+
if (hasMatch) {
|
|
100
|
+
let matchLength = LZ4_MIN_MATCH;
|
|
101
|
+
while (
|
|
102
|
+
inputOffset + matchLength < matchCopyEnd &&
|
|
103
|
+
input[reference + matchLength] === input[inputOffset + matchLength]
|
|
104
|
+
) {
|
|
105
|
+
matchLength += 1;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const literalLength = inputOffset - anchor;
|
|
109
|
+
const encodedMatchLength = matchLength - LZ4_MIN_MATCH;
|
|
110
|
+
const tokenOffset = outputOffset;
|
|
111
|
+
outputOffset += 1;
|
|
112
|
+
output[tokenOffset] =
|
|
113
|
+
(Math.min(literalLength, 0x0f) << 4) | Math.min(encodedMatchLength, 0x0f);
|
|
114
|
+
|
|
115
|
+
if (literalLength >= 0x0f) {
|
|
116
|
+
outputOffset = writeLz4Length(output, outputOffset, literalLength - 0x0f);
|
|
117
|
+
}
|
|
118
|
+
output.set(input.subarray(anchor, inputOffset), outputOffset);
|
|
119
|
+
outputOffset += literalLength;
|
|
120
|
+
|
|
121
|
+
output[outputOffset] = matchOffset & 0xff;
|
|
122
|
+
output[outputOffset + 1] = matchOffset >> 8;
|
|
123
|
+
outputOffset += 2;
|
|
124
|
+
if (encodedMatchLength >= 0x0f) {
|
|
125
|
+
outputOffset = writeLz4Length(output, outputOffset, encodedMatchLength - 0x0f);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const matchStart = inputOffset;
|
|
129
|
+
inputOffset += matchLength;
|
|
130
|
+
anchor = inputOffset;
|
|
131
|
+
for (
|
|
132
|
+
let cursor = Math.max(matchStart + 1, inputOffset - 2);
|
|
133
|
+
cursor < inputOffset;
|
|
134
|
+
cursor += 1
|
|
135
|
+
) {
|
|
136
|
+
if (cursor <= matchFindEnd) {
|
|
137
|
+
hashTable[getLz4Hash(readUint32LittleEndian(input, cursor))] = cursor;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
} else {
|
|
141
|
+
inputOffset += 1;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const literalLength = input.byteLength - anchor;
|
|
146
|
+
const tokenOffset = outputOffset;
|
|
147
|
+
outputOffset += 1;
|
|
148
|
+
output[tokenOffset] = Math.min(literalLength, 0x0f) << 4;
|
|
149
|
+
if (literalLength >= 0x0f) {
|
|
150
|
+
outputOffset = writeLz4Length(output, outputOffset, literalLength - 0x0f);
|
|
151
|
+
}
|
|
152
|
+
output.set(input.subarray(anchor), outputOffset);
|
|
153
|
+
outputOffset += literalLength;
|
|
154
|
+
|
|
155
|
+
return output.slice(0, outputOffset);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function quantizeChannel(value: number, levels: number) {
|
|
159
|
+
return Math.floor((value * (levels - 1) + 0x7f) / 0xff);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function expandChannel(value: number, levels: number) {
|
|
163
|
+
return Math.floor((value * 0xff + Math.floor((levels - 1) / 2)) / (levels - 1));
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function encodePro2I8Lz4(options: {
|
|
167
|
+
width: number;
|
|
168
|
+
height: number;
|
|
169
|
+
rgba: Uint8Array | ArrayBuffer;
|
|
170
|
+
}): { data: Uint8Array; colorFormat: Pro2WallpaperColorFormat } {
|
|
171
|
+
const { width, height } = options;
|
|
172
|
+
if (!Number.isInteger(width) || width <= 0 || width > 0xffff) {
|
|
173
|
+
throw invalidParameter('Wallpaper width must be an integer between 1 and 65535.');
|
|
174
|
+
}
|
|
175
|
+
if (!Number.isInteger(height) || height <= 0 || height > 0xffff) {
|
|
176
|
+
throw invalidParameter('Wallpaper height must be an integer between 1 and 65535.');
|
|
177
|
+
}
|
|
178
|
+
const rgba = asBytes(options.rgba);
|
|
179
|
+
const expectedLength = width * height * 4;
|
|
180
|
+
if (rgba.byteLength !== expectedLength) {
|
|
181
|
+
throw invalidParameter(
|
|
182
|
+
`Wallpaper RGBA data length must be ${expectedLength} bytes, received ${rgba.byteLength}.`
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
const stride = width;
|
|
186
|
+
const rawData = new Uint8Array(I8_PALETTE_SIZE + stride * height);
|
|
187
|
+
|
|
188
|
+
for (let red = 0; red < I8_RED_LEVELS; red += 1) {
|
|
189
|
+
for (let green = 0; green < I8_GREEN_LEVELS; green += 1) {
|
|
190
|
+
for (let blue = 0; blue < I8_BLUE_LEVELS; blue += 1) {
|
|
191
|
+
const paletteIndex = (red * I8_GREEN_LEVELS + green) * I8_BLUE_LEVELS + blue;
|
|
192
|
+
const paletteOffset = paletteIndex * 4;
|
|
193
|
+
// LVGL stores its ARGB8888 palette as B, G, R, A bytes on little-endian devices.
|
|
194
|
+
rawData[paletteOffset] = expandChannel(blue, I8_BLUE_LEVELS);
|
|
195
|
+
rawData[paletteOffset + 1] = expandChannel(green, I8_GREEN_LEVELS);
|
|
196
|
+
rawData[paletteOffset + 2] = expandChannel(red, I8_RED_LEVELS);
|
|
197
|
+
rawData[paletteOffset + 3] = 0xff;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
for (let pixel = 0; pixel < width * height; pixel += 1) {
|
|
203
|
+
const sourceOffset = pixel * 4;
|
|
204
|
+
const alpha = rgba[sourceOffset + 3];
|
|
205
|
+
const red = Math.round((rgba[sourceOffset] * alpha) / 0xff);
|
|
206
|
+
const green = Math.round((rgba[sourceOffset + 1] * alpha) / 0xff);
|
|
207
|
+
const blue = Math.round((rgba[sourceOffset + 2] * alpha) / 0xff);
|
|
208
|
+
const paletteIndex =
|
|
209
|
+
(quantizeChannel(red, I8_RED_LEVELS) * I8_GREEN_LEVELS +
|
|
210
|
+
quantizeChannel(green, I8_GREEN_LEVELS)) *
|
|
211
|
+
I8_BLUE_LEVELS +
|
|
212
|
+
quantizeChannel(blue, I8_BLUE_LEVELS);
|
|
213
|
+
rawData[I8_PALETTE_SIZE + pixel] = paletteIndex;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const compressed = compressLz4Block(rawData);
|
|
217
|
+
const data = new Uint8Array(24 + compressed.byteLength);
|
|
218
|
+
const view = new DataView(data.buffer);
|
|
219
|
+
data[0] = 0x19;
|
|
220
|
+
data[1] = COLOR_FORMAT_I8;
|
|
221
|
+
view.setUint16(2, IMAGE_FLAG_COMPRESSED, true);
|
|
222
|
+
view.setUint16(4, width, true);
|
|
223
|
+
view.setUint16(6, height, true);
|
|
224
|
+
view.setUint16(8, stride, true);
|
|
225
|
+
view.setUint16(10, 0, true);
|
|
226
|
+
view.setUint32(12, IMAGE_COMPRESSION_LZ4, true);
|
|
227
|
+
view.setUint32(16, compressed.byteLength, true);
|
|
228
|
+
view.setUint32(20, rawData.byteLength, true);
|
|
229
|
+
data.set(compressed, 24);
|
|
230
|
+
return { data, colorFormat: 'I8' };
|
|
231
|
+
}
|
|
232
|
+
|
|
41
233
|
export function encodePro2Image(options: {
|
|
42
234
|
width: number;
|
|
43
235
|
height: number;
|
|
@@ -125,6 +317,10 @@ export function encodePro2Wallpaper(options: {
|
|
|
125
317
|
width: number;
|
|
126
318
|
height: number;
|
|
127
319
|
rgba: Uint8Array | ArrayBuffer;
|
|
320
|
+
encoding?: Pro2WallpaperEncoding;
|
|
128
321
|
}): { data: Uint8Array; colorFormat: Pro2WallpaperColorFormat } {
|
|
322
|
+
if (options.encoding === 'i8-lz4') {
|
|
323
|
+
return encodePro2I8Lz4(options);
|
|
324
|
+
}
|
|
129
325
|
return encodePro2Image(options);
|
|
130
326
|
}
|