@mentra/sdk 2.1.27 → 2.1.28

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.
Files changed (90) hide show
  1. package/dist/app/session/api-client.d.ts.map +1 -1
  2. package/dist/app/session/dashboard.d.ts +5 -8
  3. package/dist/app/session/dashboard.d.ts.map +1 -1
  4. package/dist/app/session/events.d.ts +2 -1
  5. package/dist/app/session/events.d.ts.map +1 -1
  6. package/dist/app/session/index.d.ts +62 -3
  7. package/dist/app/session/index.d.ts.map +1 -1
  8. package/dist/app/session/modules/audio.d.ts +33 -4
  9. package/dist/app/session/modules/audio.d.ts.map +1 -1
  10. package/dist/app/session/modules/camera-managed-extension.d.ts +2 -3
  11. package/dist/app/session/modules/camera-managed-extension.d.ts.map +1 -1
  12. package/dist/app/session/modules/camera.d.ts +5 -5
  13. package/dist/app/session/modules/camera.d.ts.map +1 -1
  14. package/dist/app/session/modules/led.d.ts +141 -0
  15. package/dist/app/session/modules/led.d.ts.map +1 -0
  16. package/dist/app/session/modules/location.d.ts +1 -2
  17. package/dist/app/session/modules/location.d.ts.map +1 -1
  18. package/dist/app/session/modules/simple-storage.d.ts.map +1 -1
  19. package/dist/index.d.ts +7 -7
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +5347 -112
  22. package/dist/index.js.map +45 -0
  23. package/dist/logging/logger.d.ts +1 -1
  24. package/dist/logging/logger.d.ts.map +1 -1
  25. package/dist/types/capabilities.d.ts +3 -0
  26. package/dist/types/capabilities.d.ts.map +1 -1
  27. package/dist/types/index.d.ts +4 -14
  28. package/dist/types/index.d.ts.map +1 -1
  29. package/dist/types/message-types.d.ts +8 -1
  30. package/dist/types/message-types.d.ts.map +1 -1
  31. package/dist/types/messages/app-to-cloud.d.ts +48 -2
  32. package/dist/types/messages/app-to-cloud.d.ts.map +1 -1
  33. package/dist/types/messages/cloud-to-app.d.ts +16 -6
  34. package/dist/types/messages/cloud-to-app.d.ts.map +1 -1
  35. package/dist/types/messages/cloud-to-glasses.d.ts +29 -1
  36. package/dist/types/messages/cloud-to-glasses.d.ts.map +1 -1
  37. package/dist/types/messages/glasses-to-cloud.d.ts +24 -1
  38. package/dist/types/messages/glasses-to-cloud.d.ts.map +1 -1
  39. package/dist/types/rtmp-stream.d.ts +1 -1
  40. package/dist/types/rtmp-stream.d.ts.map +1 -1
  41. package/dist/types/streams.d.ts +28 -1
  42. package/dist/types/streams.d.ts.map +1 -1
  43. package/package.json +9 -3
  44. package/dist/app/index.js +0 -24
  45. package/dist/app/server/index.js +0 -658
  46. package/dist/app/session/api-client.js +0 -101
  47. package/dist/app/session/dashboard.js +0 -149
  48. package/dist/app/session/events.js +0 -315
  49. package/dist/app/session/index.js +0 -1573
  50. package/dist/app/session/layouts.js +0 -372
  51. package/dist/app/session/modules/audio.js +0 -321
  52. package/dist/app/session/modules/camera-managed-extension.js +0 -310
  53. package/dist/app/session/modules/camera.js +0 -607
  54. package/dist/app/session/modules/index.js +0 -19
  55. package/dist/app/session/modules/location.js +0 -61
  56. package/dist/app/session/modules/simple-storage.js +0 -232
  57. package/dist/app/session/settings.js +0 -358
  58. package/dist/app/token/index.js +0 -22
  59. package/dist/app/token/utils.js +0 -144
  60. package/dist/app/webview/index.js +0 -382
  61. package/dist/constants/index.js +0 -16
  62. package/dist/constants/log-messages/color.js +0 -14
  63. package/dist/constants/log-messages/logos.js +0 -48
  64. package/dist/constants/log-messages/updates.js +0 -55
  65. package/dist/constants/log-messages/warning.js +0 -89
  66. package/dist/examples/managed-rtmp-streaming-example.js +0 -158
  67. package/dist/examples/managed-rtmp-streaming-with-restream-example.js +0 -124
  68. package/dist/examples/rtmp-streaming-example.js +0 -102
  69. package/dist/logging/logger.js +0 -79
  70. package/dist/types/capabilities.js +0 -9
  71. package/dist/types/dashboard/index.js +0 -12
  72. package/dist/types/enums.js +0 -75
  73. package/dist/types/index.js +0 -101
  74. package/dist/types/layouts.js +0 -3
  75. package/dist/types/message-types.js +0 -212
  76. package/dist/types/messages/app-to-cloud.js +0 -95
  77. package/dist/types/messages/base.js +0 -3
  78. package/dist/types/messages/cloud-to-app.js +0 -78
  79. package/dist/types/messages/cloud-to-glasses.js +0 -68
  80. package/dist/types/messages/glasses-to-cloud.js +0 -140
  81. package/dist/types/models.js +0 -101
  82. package/dist/types/photo-data.js +0 -2
  83. package/dist/types/rtmp-stream.js +0 -3
  84. package/dist/types/streams.js +0 -306
  85. package/dist/types/token.js +0 -7
  86. package/dist/types/webhooks.js +0 -28
  87. package/dist/utils/animation-utils.js +0 -340
  88. package/dist/utils/bitmap-utils.js +0 -475
  89. package/dist/utils/permissions-utils.js +0 -263
  90. package/dist/utils/resource-tracker.js +0 -153
@@ -1,475 +0,0 @@
1
- "use strict";
2
- /**
3
- * 🎨 Bitmap Utilities Module
4
- *
5
- * Provides helper functions for working with bitmap images in MentraOS applications.
6
- * Includes file loading, data validation, and format conversion utilities.
7
- *
8
- * @example
9
- * ```typescript
10
- * import { BitmapUtils } from '@mentra/sdk';
11
- *
12
- * // Load a single BMP file
13
- * const bmpHex = await BitmapUtils.loadBmpAsHex('./my-image.bmp');
14
- * session.layouts.showBitmapView(bmpHex);
15
- *
16
- * // Load multiple animation frames
17
- * const frames = await BitmapUtils.loadBmpFrames('./animations', 10);
18
- * session.layouts.showBitmapAnimation(frames, 1500, true);
19
- * ```
20
- */
21
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
22
- if (k2 === undefined) k2 = k;
23
- var desc = Object.getOwnPropertyDescriptor(m, k);
24
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
25
- desc = { enumerable: true, get: function() { return m[k]; } };
26
- }
27
- Object.defineProperty(o, k2, desc);
28
- }) : (function(o, m, k, k2) {
29
- if (k2 === undefined) k2 = k;
30
- o[k2] = m[k];
31
- }));
32
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
33
- Object.defineProperty(o, "default", { enumerable: true, value: v });
34
- }) : function(o, v) {
35
- o["default"] = v;
36
- });
37
- var __importStar = (this && this.__importStar) || (function () {
38
- var ownKeys = function(o) {
39
- ownKeys = Object.getOwnPropertyNames || function (o) {
40
- var ar = [];
41
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
42
- return ar;
43
- };
44
- return ownKeys(o);
45
- };
46
- return function (mod) {
47
- if (mod && mod.__esModule) return mod;
48
- var result = {};
49
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
50
- __setModuleDefault(result, mod);
51
- return result;
52
- };
53
- })();
54
- Object.defineProperty(exports, "__esModule", { value: true });
55
- exports.BitmapUtils = void 0;
56
- const fs = __importStar(require("fs/promises"));
57
- const path = __importStar(require("path"));
58
- const jimp_1 = require("jimp");
59
- /**
60
- * Utility class for working with bitmap images in MentraOS applications
61
- */
62
- class BitmapUtils {
63
- static async convert24BitTo1BitBMP(input24BitBmp) {
64
- // Read header information from 24-bit BMP
65
- const width = input24BitBmp.readUInt32LE(18);
66
- const height = Math.abs(input24BitBmp.readInt32LE(22)); // Height can be negative (top-down BMP)
67
- const isTopDown = input24BitBmp.readInt32LE(22) < 0;
68
- const bitsPerPixel = input24BitBmp.readUInt16LE(28);
69
- if (bitsPerPixel !== 24) {
70
- throw new Error("Input must be a 24-bit BMP");
71
- }
72
- // Calculate row sizes (both must be 4-byte aligned)
73
- const rowSize24 = Math.ceil((width * 3) / 4) * 4;
74
- const rowSize1 = Math.ceil(width / 32) * 4; // 32 pixels per 4 bytes
75
- // Calculate sizes for 1-bit BMP
76
- const colorTableSize = 8; // 2 colors * 4 bytes each
77
- const headerSize = 54 + colorTableSize;
78
- const pixelDataSize = rowSize1 * height;
79
- const fileSize = headerSize + pixelDataSize;
80
- // Create new buffer for 1-bit BMP
81
- const output1BitBmp = Buffer.alloc(fileSize);
82
- let offset = 0;
83
- // Write BMP file header (14 bytes)
84
- output1BitBmp.write("BM", offset);
85
- offset += 2; // Signature
86
- output1BitBmp.writeUInt32LE(fileSize, offset);
87
- offset += 4; // File size
88
- output1BitBmp.writeUInt16LE(0, offset);
89
- offset += 2; // Reserved 1
90
- output1BitBmp.writeUInt16LE(0, offset);
91
- offset += 2; // Reserved 2
92
- output1BitBmp.writeUInt32LE(headerSize, offset);
93
- offset += 4; // Pixel data offset
94
- // Write DIB header (40 bytes)
95
- output1BitBmp.writeUInt32LE(40, offset);
96
- offset += 4; // DIB header size
97
- output1BitBmp.writeInt32LE(width, offset);
98
- offset += 4; // Width
99
- output1BitBmp.writeInt32LE(height, offset);
100
- offset += 4; // Height (positive for bottom-up)
101
- output1BitBmp.writeUInt16LE(1, offset);
102
- offset += 2; // Planes
103
- output1BitBmp.writeUInt16LE(1, offset);
104
- offset += 2; // Bits per pixel (1-bit)
105
- output1BitBmp.writeUInt32LE(0, offset);
106
- offset += 4; // Compression (none)
107
- output1BitBmp.writeUInt32LE(pixelDataSize, offset);
108
- offset += 4; // Image size
109
- output1BitBmp.writeInt32LE(2835, offset);
110
- offset += 4; // X pixels per meter (72 DPI)
111
- output1BitBmp.writeInt32LE(2835, offset);
112
- offset += 4; // Y pixels per meter (72 DPI)
113
- output1BitBmp.writeUInt32LE(2, offset);
114
- offset += 4; // Colors used
115
- output1BitBmp.writeUInt32LE(2, offset);
116
- offset += 4; // Important colors
117
- // Write color table (8 bytes)
118
- // Black (index 0): B=0, G=0, R=0, Reserved=0
119
- output1BitBmp.writeUInt32LE(0x00000000, offset);
120
- offset += 4;
121
- // White (index 1): B=255, G=255, R=255, Reserved=0
122
- output1BitBmp.writeUInt8(255, offset++); // Blue
123
- output1BitBmp.writeUInt8(255, offset++); // Green
124
- output1BitBmp.writeUInt8(255, offset++); // Red
125
- output1BitBmp.writeUInt8(0, offset++); // Reserved
126
- // Convert pixel data from 24-bit to 1-bit
127
- const pixelDataStart24 = 54; // 24-bit BMP has no color table
128
- for (let y = 0; y < height; y++) {
129
- // BMP files are usually stored bottom-up
130
- const sourceY = isTopDown ? y : height - 1 - y;
131
- const destY = height - 1 - y; // Always write bottom-up for compatibility
132
- // Initialize the row with zeros
133
- const rowData = Buffer.alloc(rowSize1);
134
- for (let x = 0; x < width; x++) {
135
- // Get pixel from 24-bit BMP
136
- const offset24 = pixelDataStart24 + sourceY * rowSize24 + x * 3;
137
- const blue = input24BitBmp[offset24];
138
- const green = input24BitBmp[offset24 + 1];
139
- const red = input24BitBmp[offset24 + 2];
140
- // Determine if pixel is white (assuming pure black or white)
141
- // White = 1, Black = 0
142
- const isWhite = red > 128 || green > 128 || blue > 128 ? 1 : 0;
143
- // Calculate bit position
144
- const byteIndex = Math.floor(x / 8);
145
- const bitPosition = 7 - (x % 8); // MSB first
146
- // Set bit if white
147
- if (isWhite) {
148
- rowData[byteIndex] |= 1 << bitPosition;
149
- }
150
- }
151
- // Write row to output buffer
152
- const destOffset = offset + destY * rowSize1;
153
- rowData.copy(output1BitBmp, destOffset);
154
- }
155
- return output1BitBmp;
156
- }
157
- /**
158
- * Load a BMP file as hex string from filesystem
159
- *
160
- * @param filePath - Path to the BMP file
161
- * @returns Promise resolving to hex-encoded bitmap data
162
- * @throws Error if file cannot be read or is not a valid BMP
163
- *
164
- * @example
165
- * ```typescript
166
- * const bmpBase64 = await BitmapUtils.loadBmpFromFileAsBase64('./assets/icon.bmp');
167
- * session.layouts.showBitmapView(bmpBase64);
168
- * ```
169
- */
170
- static async fileToBase64(filePath) {
171
- try {
172
- const bmpData = await fs.readFile(filePath);
173
- return this.bufferToBase64(bmpData);
174
- }
175
- catch (error) {
176
- if (error instanceof Error) {
177
- throw new Error(`Failed to load BMP file ${filePath}: ${error.message}`);
178
- }
179
- throw new Error(`Failed to load BMP file ${filePath}: Unknown error`);
180
- }
181
- }
182
- static async bufferToBase64(bmpData) {
183
- return bmpData.toString("base64");
184
- }
185
- static async padBase64Bitmap(bmpBase64, padding) {
186
- const buffer = Buffer.from(bmpBase64, "base64");
187
- const paddedBuffer = await this.padBmpForGlasses(buffer, padding?.left, padding?.top);
188
- return paddedBuffer.toString("base64");
189
- }
190
- static async padBmpForGlasses(bmpData, leftPadding = 50, topPadding = 35) {
191
- try {
192
- // Basic BMP validation - check for BMP signature
193
- if (bmpData.length < 14 || bmpData[0] !== 0x42 || bmpData[1] !== 0x4d) {
194
- throw new Error(`Bmp data is not a valid BMP file (missing BM signature)`);
195
- }
196
- let finalBmpData = bmpData;
197
- // Load the image with Jimp
198
- const image = await jimp_1.Jimp.read(bmpData);
199
- // Check if we need to add padding
200
- if (image.width !== 576 || image.height !== 135) {
201
- console.log(`Adding padding to BMP since it isn't 576x135 (assuming it's 526x100!)(current: ${image.width}x${image.height})`);
202
- // Create a new 576x135 white canvas
203
- const paddedImage = new jimp_1.Jimp({
204
- width: 576,
205
- height: 135,
206
- color: 0x00000000,
207
- });
208
- // // Calculate position to place the original image (with padding)
209
- const leftPadding = 50; // 45px padding on left
210
- const topPadding = 35; // 35px padding on top
211
- // Composite the original image onto the white canvas
212
- // paddedImage.composite(image, leftPadding, topPadding);
213
- paddedImage.composite(image, leftPadding, topPadding);
214
- finalBmpData = await this.convert24BitTo1BitBMP(await paddedImage.getBuffer("image/bmp"));
215
- }
216
- // No padding needed, just return as hex
217
- console.log(`finalBmpData: ${finalBmpData.length} bytes`);
218
- return finalBmpData;
219
- }
220
- catch (error) {
221
- if (error instanceof Error) {
222
- throw new Error(`Failed to load BMP data: ${error.message}`);
223
- }
224
- throw new Error(`Failed to load BMP data: Unknown error`);
225
- }
226
- }
227
- /**
228
- * Load multiple BMP frames as hex array for animations
229
- *
230
- * @param basePath - Directory containing the frame files
231
- * @param frameCount - Number of frames to load
232
- * @param options - Loading options and configuration
233
- * @returns Promise resolving to array of hex-encoded bitmap data
234
- *
235
- * @example
236
- * ```typescript
237
- * // Load 10 frames with default pattern
238
- * const frames = await BitmapUtils.loadBmpFrames('./animations', 10);
239
- *
240
- * // Load with custom pattern
241
- * const customFrames = await BitmapUtils.loadBmpFrames('./sprites', 8, {
242
- * filePattern: 'sprite_{i}.bmp',
243
- * startFrame: 0
244
- * });
245
- * ```
246
- */
247
- static async loadBmpFrames(basePath, frameCount, options = {}) {
248
- const { filePattern = "animation_10_frame_{i}.bmp", startFrame = 1, validateFrames = true, skipMissingFrames = false, } = options;
249
- const frames = [];
250
- const errors = [];
251
- for (let i = 0; i < frameCount; i++) {
252
- const frameNumber = startFrame + i;
253
- const fileName = filePattern.replace("{i}", frameNumber.toString());
254
- const filePath = path.join(basePath, fileName);
255
- try {
256
- const frameBase64 = await this.fileToBase64(filePath);
257
- if (validateFrames) {
258
- const validation = this.validateBase64Bitmap(frameBase64);
259
- if (!validation.isValid) {
260
- const errorMsg = `Frame ${frameNumber} validation failed: ${validation.errors.join(", ")}`;
261
- if (skipMissingFrames) {
262
- console.warn(`⚠️ ${errorMsg} - skipping`);
263
- continue;
264
- }
265
- else {
266
- throw new Error(errorMsg);
267
- }
268
- }
269
- console.log(`✅ Frame ${frameNumber} validated (${validation.blackPixels} black pixels)`);
270
- }
271
- frames.push(frameBase64);
272
- }
273
- catch (error) {
274
- const errorMsg = `Failed to load frame ${frameNumber} (${fileName}): ${error instanceof Error ? error.message : "Unknown error"}`;
275
- if (skipMissingFrames) {
276
- console.warn(`⚠️ ${errorMsg} - skipping`);
277
- continue;
278
- }
279
- else {
280
- errors.push(errorMsg);
281
- }
282
- }
283
- }
284
- if (errors.length > 0) {
285
- throw new Error(`Failed to load frames:\n${errors.join("\n")}`);
286
- }
287
- if (frames.length === 0) {
288
- throw new Error(`No valid frames loaded from ${basePath}`);
289
- }
290
- console.log(`📚 Loaded ${frames.length} animation frames from ${basePath}`);
291
- return frames;
292
- }
293
- /**
294
- * Validate BMP hex data integrity and extract metadata
295
- *
296
- * @param hexString - Hex-encoded bitmap data
297
- * @returns Validation result with detailed information
298
- *
299
- * @example
300
- * ```typescript
301
- * const validation = BitmapUtils.validateBmpHex(bmpHex);
302
- * if (!validation.isValid) {
303
- * console.error('Invalid bitmap:', validation.errors);
304
- * } else {
305
- * console.log(`Valid bitmap: ${validation.blackPixels} black pixels`);
306
- * }
307
- * ```
308
- */
309
- static validateBase64Bitmap(bmpFrame) {
310
- const errors = [];
311
- let byteCount = 0;
312
- let blackPixels = 0;
313
- const metadata = {};
314
- try {
315
- const hexString = Buffer.from(bmpFrame, "base64").toString("hex");
316
- // Basic hex validation
317
- if (typeof hexString !== "string" || hexString.length === 0) {
318
- errors.push("Hex string is empty or invalid");
319
- return { isValid: false, byteCount: 0, blackPixels: 0, errors };
320
- }
321
- if (hexString.length % 2 !== 0) {
322
- errors.push("Hex string length must be even");
323
- return { isValid: false, byteCount: 0, blackPixels: 0, errors };
324
- }
325
- // Convert to buffer
326
- const buffer = Buffer.from(hexString, "hex");
327
- byteCount = buffer.length;
328
- // BMP signature validation
329
- if (buffer.length < 14) {
330
- errors.push("File too small to be a valid BMP (minimum 14 bytes for header)");
331
- }
332
- else {
333
- if (buffer[0] !== 0x42 || buffer[1] !== 0x4d) {
334
- errors.push('Invalid BMP signature (should start with "BM")');
335
- }
336
- }
337
- // Size validation for MentraOS (576x135 = ~9782 bytes expected)
338
- const expectedSize = 9782;
339
- if (buffer.length < expectedSize - 100) {
340
- // Allow some tolerance
341
- errors.push(`BMP too small (${buffer.length} bytes, expected ~${expectedSize})`);
342
- }
343
- else if (buffer.length > expectedSize + 1000) {
344
- // Allow some tolerance
345
- errors.push(`BMP too large (${buffer.length} bytes, expected ~${expectedSize})`);
346
- }
347
- // Extract BMP metadata if header is valid
348
- if (buffer.length >= 54) {
349
- try {
350
- // BMP width and height are at offsets 18 and 22 (little-endian)
351
- const width = buffer.readUInt32LE(18);
352
- const height = buffer.readUInt32LE(22);
353
- metadata.dimensions = { width, height };
354
- metadata.format = "BMP";
355
- // Validate dimensions for MentraOS glasses
356
- if (width !== 576 || height !== 135) {
357
- errors.push(`Invalid dimensions (${width}x${height}, expected 576x135 for MentraOS)`);
358
- }
359
- }
360
- catch (e) {
361
- errors.push("Failed to parse BMP header metadata");
362
- }
363
- }
364
- // Pixel data validation (assumes 54-byte header + pixel data)
365
- if (buffer.length > 62) {
366
- const pixelData = buffer.slice(62); // Skip BMP header
367
- blackPixels = Array.from(pixelData).filter((b) => b !== 0xff).length;
368
- if (blackPixels === 0) {
369
- errors.push("No black pixels found (image appears to be all white)");
370
- }
371
- }
372
- else {
373
- errors.push("File too small to contain pixel data");
374
- }
375
- }
376
- catch (error) {
377
- errors.push(`Failed to parse hex data: ${error instanceof Error ? error.message : "Unknown error"}`);
378
- }
379
- return {
380
- isValid: errors.length === 0,
381
- byteCount,
382
- blackPixels,
383
- errors,
384
- metadata: Object.keys(metadata).length > 0 ? metadata : undefined,
385
- };
386
- }
387
- /**
388
- * Convert bitmap data between different formats
389
- *
390
- * @param data - Input bitmap data
391
- * @param fromFormat - Source format ('hex' | 'base64' | 'buffer')
392
- * @param toFormat - Target format ('hex' | 'base64' | 'buffer')
393
- * @returns Converted bitmap data
394
- *
395
- * @example
396
- * ```typescript
397
- * const base64Data = BitmapUtils.convertFormat(hexData, 'hex', 'base64');
398
- * const bufferData = BitmapUtils.convertFormat(base64Data, 'base64', 'buffer');
399
- * ```
400
- */
401
- static convertFormat(data, fromFormat, toFormat) {
402
- let buffer;
403
- // Convert input to buffer
404
- switch (fromFormat) {
405
- case "hex":
406
- buffer = Buffer.from(data, "hex");
407
- break;
408
- case "base64":
409
- buffer = Buffer.from(data, "base64");
410
- break;
411
- case "buffer":
412
- buffer = data;
413
- break;
414
- default:
415
- throw new Error(`Unsupported source format: ${fromFormat}`);
416
- }
417
- // Convert buffer to target format
418
- switch (toFormat) {
419
- case "hex":
420
- return buffer.toString("hex");
421
- case "base64":
422
- return buffer.toString("base64");
423
- case "buffer":
424
- return buffer;
425
- default:
426
- throw new Error(`Unsupported target format: ${toFormat}`);
427
- }
428
- }
429
- /**
430
- * Get bitmap information without full validation
431
- *
432
- * @param hexString - Hex-encoded bitmap data
433
- * @returns Basic bitmap information
434
- *
435
- * @example
436
- * ```typescript
437
- * const info = BitmapUtils.getBitmapInfo(bmpHex);
438
- * console.log(`Bitmap: ${info.width}x${info.height}, ${info.blackPixels} black pixels`);
439
- * ```
440
- */
441
- static getBitmapInfo(hexString) {
442
- try {
443
- const buffer = Buffer.from(hexString, "hex");
444
- const isValidBmp = buffer.length >= 14 && buffer[0] === 0x42 && buffer[1] === 0x4d;
445
- let width;
446
- let height;
447
- if (isValidBmp && buffer.length >= 54) {
448
- try {
449
- width = buffer.readUInt32LE(18);
450
- height = buffer.readUInt32LE(22);
451
- }
452
- catch (e) {
453
- // Ignore metadata parsing errors
454
- }
455
- }
456
- const pixelData = buffer.slice(62);
457
- const blackPixels = Array.from(pixelData).filter((b) => b !== 0xff).length;
458
- return {
459
- byteCount: buffer.length,
460
- blackPixels,
461
- width,
462
- height,
463
- isValidBmp,
464
- };
465
- }
466
- catch (error) {
467
- return {
468
- byteCount: 0,
469
- blackPixels: 0,
470
- isValidBmp: false,
471
- };
472
- }
473
- }
474
- }
475
- exports.BitmapUtils = BitmapUtils;