lotio 1.1.54 → 1.1.55
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/browser/index.js +147 -1
- package/browser/lotio.wasm +0 -0
- package/browser/wasm.js +183 -56
- package/package.json +1 -1
package/browser/index.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* @module lotio
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { initLotio, createAnimation, renderFrameToCanvas, renderFrameRGBA, cleanup, registerFont, getVersion } from './wasm.js';
|
|
6
|
+
import { initLotio, createAnimation, renderFrameToCanvas, renderFrameRGBA, cleanup, registerFont, getVersion, setDebugMode } from './wasm.js';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Animation states
|
|
@@ -75,6 +75,7 @@ export class Lotio extends EventEmitter {
|
|
|
75
75
|
* @param {number} options.textPadding - Text padding factor (0.0-1.0, default: 0.97)
|
|
76
76
|
* @param {string} options.textMeasurementMode - Text measurement mode: 'fast'|'accurate'|'pixel-perfect' (default: 'accurate')
|
|
77
77
|
* @param {string} options.wasmPath - Path to lotio.wasm file (default: './lotio.wasm')
|
|
78
|
+
* @param {boolean} options.debug - Enable debug logging (default: false)
|
|
78
79
|
*/
|
|
79
80
|
constructor(options = {}) {
|
|
80
81
|
super();
|
|
@@ -86,6 +87,7 @@ export class Lotio extends EventEmitter {
|
|
|
86
87
|
this._layerOverrides = options.layerOverrides || null;
|
|
87
88
|
this._textPadding = options.textPadding !== undefined ? options.textPadding : 0.97;
|
|
88
89
|
this._textMeasurementMode = options.textMeasurementMode || 'accurate';
|
|
90
|
+
this._debug = options.debug === true;
|
|
89
91
|
this._state = State.STOPPED;
|
|
90
92
|
this._wasmInitialized = false;
|
|
91
93
|
this._animationInfo = null;
|
|
@@ -96,6 +98,19 @@ export class Lotio extends EventEmitter {
|
|
|
96
98
|
this._startTime = 0;
|
|
97
99
|
this._registeredFonts = new Set();
|
|
98
100
|
|
|
101
|
+
if (this._debug) {
|
|
102
|
+
console.log('[Lotio] Constructor called with options:', {
|
|
103
|
+
wasmPath: this._wasmPath,
|
|
104
|
+
fps: this._fps,
|
|
105
|
+
hasAnimation: !!this._animation,
|
|
106
|
+
hasLayerOverrides: !!this._layerOverrides,
|
|
107
|
+
textPadding: this._textPadding,
|
|
108
|
+
textMeasurementMode: this._textMeasurementMode,
|
|
109
|
+
fontsCount: this._fonts.length,
|
|
110
|
+
debug: this._debug
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
99
114
|
// Auto-initialize if animation is provided
|
|
100
115
|
if (this._animation) {
|
|
101
116
|
this._init().catch(error => {
|
|
@@ -111,14 +126,34 @@ export class Lotio extends EventEmitter {
|
|
|
111
126
|
*/
|
|
112
127
|
async _init() {
|
|
113
128
|
if (this._wasmInitialized) {
|
|
129
|
+
if (this._debug) {
|
|
130
|
+
console.log('[Lotio] WASM already initialized, skipping');
|
|
131
|
+
}
|
|
114
132
|
return;
|
|
115
133
|
}
|
|
116
134
|
|
|
135
|
+
let initStartTime;
|
|
136
|
+
if (this._debug) {
|
|
137
|
+
console.log('[Lotio] Initializing WASM module from:', this._wasmPath);
|
|
138
|
+
initStartTime = performance.now();
|
|
139
|
+
setDebugMode(true);
|
|
140
|
+
} else {
|
|
141
|
+
setDebugMode(false);
|
|
142
|
+
}
|
|
143
|
+
|
|
117
144
|
try {
|
|
118
145
|
await initLotio(this._wasmPath);
|
|
119
146
|
this._wasmInitialized = true;
|
|
120
147
|
|
|
148
|
+
if (this._debug) {
|
|
149
|
+
const initEndTime = performance.now();
|
|
150
|
+
console.log(`[Lotio] WASM module initialized successfully in ${initStartTime ? (initEndTime - initStartTime).toFixed(2) : '?'}ms`);
|
|
151
|
+
}
|
|
152
|
+
|
|
121
153
|
// Register fonts
|
|
154
|
+
if (this._debug && this._fonts.length > 0) {
|
|
155
|
+
console.log(`[Lotio] Registering ${this._fonts.length} font(s)...`);
|
|
156
|
+
}
|
|
122
157
|
for (const font of this._fonts) {
|
|
123
158
|
await this._registerFont(font.name, font.data);
|
|
124
159
|
}
|
|
@@ -128,8 +163,14 @@ export class Lotio extends EventEmitter {
|
|
|
128
163
|
await this._loadAnimation();
|
|
129
164
|
} else {
|
|
130
165
|
this._setState(State.LOADED);
|
|
166
|
+
if (this._debug) {
|
|
167
|
+
console.log('[Lotio] No animation provided, state set to LOADED');
|
|
168
|
+
}
|
|
131
169
|
}
|
|
132
170
|
} catch (error) {
|
|
171
|
+
if (this._debug) {
|
|
172
|
+
console.error('[Lotio] Failed to initialize WASM:', error);
|
|
173
|
+
}
|
|
133
174
|
this._setState(State.ERROR);
|
|
134
175
|
this.emit('error', error, this);
|
|
135
176
|
throw error;
|
|
@@ -142,6 +183,9 @@ export class Lotio extends EventEmitter {
|
|
|
142
183
|
*/
|
|
143
184
|
async _registerFont(name, data) {
|
|
144
185
|
if (this._registeredFonts.has(name)) {
|
|
186
|
+
if (this._debug) {
|
|
187
|
+
console.log(`[Lotio] Font "${name}" already registered, skipping`);
|
|
188
|
+
}
|
|
145
189
|
return;
|
|
146
190
|
}
|
|
147
191
|
|
|
@@ -149,8 +193,16 @@ export class Lotio extends EventEmitter {
|
|
|
149
193
|
await this._init();
|
|
150
194
|
}
|
|
151
195
|
|
|
196
|
+
if (this._debug) {
|
|
197
|
+
console.log(`[Lotio] Registering font "${name}" (${data.length} bytes)`);
|
|
198
|
+
}
|
|
199
|
+
|
|
152
200
|
registerFont(name, data);
|
|
153
201
|
this._registeredFonts.add(name);
|
|
202
|
+
|
|
203
|
+
if (this._debug) {
|
|
204
|
+
console.log(`[Lotio] Font "${name}" registered successfully`);
|
|
205
|
+
}
|
|
154
206
|
}
|
|
155
207
|
|
|
156
208
|
/**
|
|
@@ -162,6 +214,12 @@ export class Lotio extends EventEmitter {
|
|
|
162
214
|
await this._init();
|
|
163
215
|
}
|
|
164
216
|
|
|
217
|
+
let loadStartTime;
|
|
218
|
+
if (this._debug) {
|
|
219
|
+
console.log('[Lotio] Loading animation...');
|
|
220
|
+
loadStartTime = performance.now();
|
|
221
|
+
}
|
|
222
|
+
|
|
165
223
|
try {
|
|
166
224
|
const animationJson = typeof this._animation === 'string'
|
|
167
225
|
? this._animation
|
|
@@ -173,6 +231,15 @@ export class Lotio extends EventEmitter {
|
|
|
173
231
|
: JSON.stringify(this._layerOverrides))
|
|
174
232
|
: null;
|
|
175
233
|
|
|
234
|
+
if (this._debug) {
|
|
235
|
+
const animSize = animationJson.length;
|
|
236
|
+
console.log(`[Lotio] Animation JSON size: ${animSize} bytes`);
|
|
237
|
+
if (layerOverridesJson) {
|
|
238
|
+
console.log(`[Lotio] Layer overrides JSON size: ${layerOverridesJson.length} bytes`);
|
|
239
|
+
}
|
|
240
|
+
console.log(`[Lotio] Text padding: ${this._textPadding}, measurement mode: ${this._textMeasurementMode}`);
|
|
241
|
+
}
|
|
242
|
+
|
|
176
243
|
this._animationInfo = createAnimation(
|
|
177
244
|
JSON.parse(animationJson),
|
|
178
245
|
layerOverridesJson ? JSON.parse(layerOverridesJson) : null,
|
|
@@ -180,11 +247,25 @@ export class Lotio extends EventEmitter {
|
|
|
180
247
|
this._textMeasurementMode
|
|
181
248
|
);
|
|
182
249
|
|
|
250
|
+
if (this._debug) {
|
|
251
|
+
const loadEndTime = performance.now();
|
|
252
|
+
console.log(`[Lotio] Animation loaded successfully in ${loadStartTime ? (loadEndTime - loadStartTime).toFixed(2) : '?'}ms`);
|
|
253
|
+
console.log('[Lotio] Animation info:', {
|
|
254
|
+
width: this._animationInfo.width,
|
|
255
|
+
height: this._animationInfo.height,
|
|
256
|
+
duration: this._animationInfo.duration,
|
|
257
|
+
fps: this._animationInfo.fps
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
|
|
183
261
|
this._currentTime = 0;
|
|
184
262
|
this._currentFrame = 0;
|
|
185
263
|
this._setState(State.LOADED);
|
|
186
264
|
this.emit('loaded', this);
|
|
187
265
|
} catch (error) {
|
|
266
|
+
if (this._debug) {
|
|
267
|
+
console.error('[Lotio] Failed to load animation:', error);
|
|
268
|
+
}
|
|
188
269
|
this._setState(State.ERROR);
|
|
189
270
|
this.emit('error', error, this);
|
|
190
271
|
throw error;
|
|
@@ -200,6 +281,9 @@ export class Lotio extends EventEmitter {
|
|
|
200
281
|
this._state = newState;
|
|
201
282
|
|
|
202
283
|
if (oldState !== newState) {
|
|
284
|
+
if (this._debug) {
|
|
285
|
+
console.log(`[Lotio] State changed: ${oldState} -> ${newState}`);
|
|
286
|
+
}
|
|
203
287
|
this.emit('statechange', newState, oldState, this);
|
|
204
288
|
}
|
|
205
289
|
}
|
|
@@ -210,6 +294,9 @@ export class Lotio extends EventEmitter {
|
|
|
210
294
|
*/
|
|
211
295
|
_animate() {
|
|
212
296
|
if (!this._isPlaying || !this._animationInfo) {
|
|
297
|
+
if (this._debug) {
|
|
298
|
+
console.warn('[Lotio] Animation loop called but not playing or no animation info');
|
|
299
|
+
}
|
|
213
300
|
return;
|
|
214
301
|
}
|
|
215
302
|
|
|
@@ -221,8 +308,15 @@ export class Lotio extends EventEmitter {
|
|
|
221
308
|
this._currentTime = (elapsed * speedMultiplier) % this._animationInfo.duration;
|
|
222
309
|
this._currentFrame = Math.floor(this._currentTime * animationFPS);
|
|
223
310
|
|
|
311
|
+
if (this._debug && this._currentFrame % Math.floor(animationFPS) === 0) {
|
|
312
|
+
console.log(`[Lotio] Frame ${this._currentFrame} at time ${this._currentTime.toFixed(3)}s (${(this._currentTime / this._animationInfo.duration * 100).toFixed(1)}%)`);
|
|
313
|
+
}
|
|
314
|
+
|
|
224
315
|
// Check if animation ended
|
|
225
316
|
if (this._currentTime >= this._animationInfo.duration) {
|
|
317
|
+
if (this._debug) {
|
|
318
|
+
console.log('[Lotio] Animation ended');
|
|
319
|
+
}
|
|
226
320
|
this.stop();
|
|
227
321
|
this.emit('end', this);
|
|
228
322
|
return;
|
|
@@ -344,6 +438,9 @@ export class Lotio extends EventEmitter {
|
|
|
344
438
|
throw new Error('Animation not loaded');
|
|
345
439
|
}
|
|
346
440
|
|
|
441
|
+
const oldTime = this._currentTime;
|
|
442
|
+
const oldFrame = this._currentFrame;
|
|
443
|
+
|
|
347
444
|
// Determine if input is frame number or time
|
|
348
445
|
if (frameOrTime < 1) {
|
|
349
446
|
// Likely a time value (0.0 - duration)
|
|
@@ -356,6 +453,10 @@ export class Lotio extends EventEmitter {
|
|
|
356
453
|
this._currentTime = this._currentFrame / animationFPS;
|
|
357
454
|
}
|
|
358
455
|
|
|
456
|
+
if (this._debug && (oldTime !== this._currentTime || oldFrame !== this._currentFrame)) {
|
|
457
|
+
console.log(`[Lotio] Seek: frame ${oldFrame} -> ${this._currentFrame}, time ${oldTime.toFixed(3)}s -> ${this._currentTime.toFixed(3)}s`);
|
|
458
|
+
}
|
|
459
|
+
|
|
359
460
|
if (this._isPlaying) {
|
|
360
461
|
this._startTime = performance.now() - (this._currentTime / (this._fps / (this._animationInfo.fps || 30)) * 1000);
|
|
361
462
|
}
|
|
@@ -370,9 +471,17 @@ export class Lotio extends EventEmitter {
|
|
|
370
471
|
}
|
|
371
472
|
|
|
372
473
|
if (this._isPlaying) {
|
|
474
|
+
if (this._debug) {
|
|
475
|
+
console.log('[Lotio] Already playing, ignoring start() call');
|
|
476
|
+
}
|
|
373
477
|
return this;
|
|
374
478
|
}
|
|
375
479
|
|
|
480
|
+
if (this._debug) {
|
|
481
|
+
console.log('[Lotio] Starting animation...');
|
|
482
|
+
console.log(`[Lotio] Starting from frame ${this._currentFrame}, time ${this._currentTime.toFixed(3)}s`);
|
|
483
|
+
}
|
|
484
|
+
|
|
376
485
|
this._isPlaying = true;
|
|
377
486
|
this._setState(State.PLAYING);
|
|
378
487
|
|
|
@@ -380,6 +489,10 @@ export class Lotio extends EventEmitter {
|
|
|
380
489
|
const speedMultiplier = this._fps / animationFPS;
|
|
381
490
|
this._startTime = performance.now() - (this._currentTime / speedMultiplier * 1000);
|
|
382
491
|
|
|
492
|
+
if (this._debug) {
|
|
493
|
+
console.log(`[Lotio] Animation FPS: ${animationFPS}, Target FPS: ${this._fps}, Speed multiplier: ${speedMultiplier.toFixed(3)}`);
|
|
494
|
+
}
|
|
495
|
+
|
|
383
496
|
this.emit('start', this);
|
|
384
497
|
this._animate();
|
|
385
498
|
|
|
@@ -388,9 +501,16 @@ export class Lotio extends EventEmitter {
|
|
|
388
501
|
|
|
389
502
|
pause() {
|
|
390
503
|
if (!this._isPlaying) {
|
|
504
|
+
if (this._debug) {
|
|
505
|
+
console.log('[Lotio] Not playing, ignoring pause() call');
|
|
506
|
+
}
|
|
391
507
|
return this;
|
|
392
508
|
}
|
|
393
509
|
|
|
510
|
+
if (this._debug) {
|
|
511
|
+
console.log(`[Lotio] Pausing at frame ${this._currentFrame}, time ${this._currentTime.toFixed(3)}s`);
|
|
512
|
+
}
|
|
513
|
+
|
|
394
514
|
this._isPlaying = false;
|
|
395
515
|
this._setState(State.PAUSED);
|
|
396
516
|
|
|
@@ -406,6 +526,10 @@ export class Lotio extends EventEmitter {
|
|
|
406
526
|
stop() {
|
|
407
527
|
const wasPlaying = this._isPlaying;
|
|
408
528
|
|
|
529
|
+
if (this._debug && wasPlaying) {
|
|
530
|
+
console.log(`[Lotio] Stopping animation (was at frame ${this._currentFrame}, time ${this._currentTime.toFixed(3)}s)`);
|
|
531
|
+
}
|
|
532
|
+
|
|
409
533
|
this.pause();
|
|
410
534
|
this.seek(0);
|
|
411
535
|
this._setState(State.STOPPED);
|
|
@@ -426,7 +550,20 @@ export class Lotio extends EventEmitter {
|
|
|
426
550
|
if (!this._animationInfo) {
|
|
427
551
|
throw new Error('Animation not loaded');
|
|
428
552
|
}
|
|
553
|
+
|
|
554
|
+
let renderStartTime;
|
|
555
|
+
if (this._debug) {
|
|
556
|
+
console.log(`[Lotio] Rendering frame ${this._currentFrame} (time ${this._currentTime.toFixed(3)}s) to canvas`);
|
|
557
|
+
renderStartTime = performance.now();
|
|
558
|
+
}
|
|
559
|
+
|
|
429
560
|
renderFrameToCanvas(canvas, this._currentTime, bgColor);
|
|
561
|
+
|
|
562
|
+
if (this._debug) {
|
|
563
|
+
const renderEndTime = performance.now();
|
|
564
|
+
console.log(`[Lotio] Canvas rendered in ${renderStartTime ? (renderEndTime - renderStartTime).toFixed(2) : '?'}ms`);
|
|
565
|
+
}
|
|
566
|
+
|
|
430
567
|
return this;
|
|
431
568
|
}
|
|
432
569
|
|
|
@@ -434,12 +571,21 @@ export class Lotio extends EventEmitter {
|
|
|
434
571
|
* Cleanup resources
|
|
435
572
|
*/
|
|
436
573
|
destroy() {
|
|
574
|
+
if (this._debug) {
|
|
575
|
+
console.log('[Lotio] Destroying instance...');
|
|
576
|
+
}
|
|
577
|
+
|
|
437
578
|
this.stop();
|
|
438
579
|
cleanup();
|
|
439
580
|
this._wasmInitialized = false;
|
|
440
581
|
this._animationInfo = null;
|
|
441
582
|
this._state = State.STOPPED;
|
|
442
583
|
this.emit('destroy', this);
|
|
584
|
+
|
|
585
|
+
if (this._debug) {
|
|
586
|
+
console.log('[Lotio] Instance destroyed');
|
|
587
|
+
}
|
|
588
|
+
|
|
443
589
|
return this;
|
|
444
590
|
}
|
|
445
591
|
}
|
package/browser/lotio.wasm
CHANGED
|
Binary file
|
package/browser/wasm.js
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
// Load WASM module
|
|
2
2
|
let Module = null;
|
|
3
|
+
let DebugMode = false;
|
|
4
|
+
|
|
5
|
+
export function setDebugMode(debug) {
|
|
6
|
+
DebugMode = debug === true;
|
|
7
|
+
}
|
|
3
8
|
|
|
4
9
|
export async function initLotio(wasmPath = './lotio.wasm', wasmBinary = null) {
|
|
5
10
|
// Load Emscripten module using ESM import
|
|
@@ -74,7 +79,9 @@ export async function initLotio(wasmPath = './lotio.wasm', wasmBinary = null) {
|
|
|
74
79
|
// HEAP arrays should be exported directly, but if not, try to create them
|
|
75
80
|
// First check if they're already available
|
|
76
81
|
if (Module.HEAP8 && Module.HEAP32 && Module.HEAPF32 && Module.HEAPU8) {
|
|
77
|
-
|
|
82
|
+
if (DebugMode) {
|
|
83
|
+
console.log('[WASM] HEAP arrays already available');
|
|
84
|
+
}
|
|
78
85
|
} else {
|
|
79
86
|
// Try to find the memory buffer
|
|
80
87
|
let memoryBuffer = null;
|
|
@@ -105,11 +112,15 @@ export async function initLotio(wasmPath = './lotio.wasm', wasmBinary = null) {
|
|
|
105
112
|
Module.HEAPU32 = new Uint32Array(memoryBuffer);
|
|
106
113
|
Module.HEAPF32 = new Float32Array(memoryBuffer);
|
|
107
114
|
Module.HEAPF64 = new Float64Array(memoryBuffer);
|
|
108
|
-
|
|
115
|
+
if (DebugMode) {
|
|
116
|
+
console.log('[WASM] Created HEAP arrays from memory buffer');
|
|
117
|
+
}
|
|
109
118
|
}
|
|
110
119
|
}
|
|
111
120
|
|
|
112
|
-
|
|
121
|
+
if (DebugMode) {
|
|
122
|
+
console.log('[WASM] Module initialized successfully');
|
|
123
|
+
}
|
|
113
124
|
return true;
|
|
114
125
|
} catch (error) {
|
|
115
126
|
throw new Error(`Failed to load WASM module: ${error.message}`);
|
|
@@ -121,10 +132,21 @@ export function createAnimation(jsonData, layerOverrides = null, textPadding = 0
|
|
|
121
132
|
throw new Error('WASM module not initialized. Call initLotio() first.');
|
|
122
133
|
}
|
|
123
134
|
|
|
135
|
+
if (DebugMode) {
|
|
136
|
+
console.log('[WASM] createAnimation called');
|
|
137
|
+
console.log('[WASM] Parameters:', {
|
|
138
|
+
hasJsonData: !!jsonData,
|
|
139
|
+
jsonDataType: typeof jsonData,
|
|
140
|
+
hasLayerOverrides: !!layerOverrides,
|
|
141
|
+
textPadding,
|
|
142
|
+
textMeasurementMode
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
124
146
|
// Check if Module has the required functions
|
|
125
147
|
if (!Module._malloc || !Module._free || !Module._lotio_init) {
|
|
126
|
-
console.error('Module object:', Module);
|
|
127
|
-
console.error('Available functions:', Object.keys(Module).filter(k => typeof Module[k] === 'function'));
|
|
148
|
+
console.error('[WASM] Module object:', Module);
|
|
149
|
+
console.error('[WASM] Available functions:', Object.keys(Module).filter(k => typeof Module[k] === 'function'));
|
|
128
150
|
throw new Error('Module is not properly initialized. Missing _malloc, _free, or _lotio_init functions.');
|
|
129
151
|
}
|
|
130
152
|
|
|
@@ -177,6 +199,13 @@ export function createAnimation(jsonData, layerOverrides = null, textPadding = 0
|
|
|
177
199
|
const jsonStr = typeof jsonData === 'string' ? jsonData : JSON.stringify(jsonData);
|
|
178
200
|
const layerOverridesStr = layerOverrides ? (typeof layerOverrides === 'string' ? layerOverrides : JSON.stringify(layerOverrides)) : null;
|
|
179
201
|
|
|
202
|
+
if (DebugMode) {
|
|
203
|
+
console.log(`[WASM] JSON string length: ${jsonStr.length} bytes`);
|
|
204
|
+
if (layerOverridesStr) {
|
|
205
|
+
console.log(`[WASM] Layer overrides string length: ${layerOverridesStr.length} bytes`);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
180
209
|
// Convert textMeasurementMode string to integer (0=FAST, 1=ACCURATE, 2=PIXEL_PERFECT)
|
|
181
210
|
const modeStr = String(textMeasurementMode).toLowerCase();
|
|
182
211
|
let modeInt = 1; // Default to ACCURATE
|
|
@@ -188,11 +217,21 @@ export function createAnimation(jsonData, layerOverrides = null, textPadding = 0
|
|
|
188
217
|
modeInt = 2;
|
|
189
218
|
}
|
|
190
219
|
|
|
220
|
+
if (DebugMode) {
|
|
221
|
+
console.log(`[WASM] Text measurement mode: "${textMeasurementMode}" -> ${modeInt} (0=FAST, 1=ACCURATE, 2=PIXEL_PERFECT)`);
|
|
222
|
+
console.log(`[WASM] Text padding: ${textPadding}`);
|
|
223
|
+
}
|
|
224
|
+
|
|
191
225
|
// Allocate memory using exported _malloc
|
|
192
226
|
const jsonPtr = Module._malloc(jsonStr.length + 1);
|
|
193
227
|
if (!jsonPtr) {
|
|
194
228
|
throw new Error('Failed to allocate memory for JSON data');
|
|
195
229
|
}
|
|
230
|
+
|
|
231
|
+
if (DebugMode) {
|
|
232
|
+
console.log(`[WASM] Allocated ${jsonStr.length + 1} bytes for JSON at pointer ${jsonPtr}`);
|
|
233
|
+
}
|
|
234
|
+
|
|
196
235
|
Module.stringToUTF8(jsonStr, jsonPtr, jsonStr.length + 1);
|
|
197
236
|
|
|
198
237
|
let layerOverridesPtr = 0;
|
|
@@ -204,17 +243,34 @@ export function createAnimation(jsonData, layerOverrides = null, textPadding = 0
|
|
|
204
243
|
throw new Error('Failed to allocate memory for layer overrides');
|
|
205
244
|
}
|
|
206
245
|
layerOverridesLen = layerOverridesStr.length;
|
|
246
|
+
if (DebugMode) {
|
|
247
|
+
console.log(`[WASM] Allocated ${layerOverridesStr.length + 1} bytes for layer overrides at pointer ${layerOverridesPtr}`);
|
|
248
|
+
}
|
|
207
249
|
Module.stringToUTF8(layerOverridesStr, layerOverridesPtr, layerOverridesStr.length + 1);
|
|
208
250
|
}
|
|
209
251
|
|
|
252
|
+
let initStartTime;
|
|
253
|
+
if (DebugMode) {
|
|
254
|
+
console.log('[WASM] Calling _lotio_init...');
|
|
255
|
+
initStartTime = performance.now();
|
|
256
|
+
}
|
|
257
|
+
|
|
210
258
|
const result = Module._lotio_init(jsonPtr, jsonStr.length, layerOverridesPtr, layerOverridesLen, textPadding, modeInt);
|
|
211
259
|
|
|
260
|
+
if (DebugMode) {
|
|
261
|
+
const initEndTime = performance.now();
|
|
262
|
+
console.log(`[WASM] _lotio_init returned: ${result} (${result === 0 ? 'success' : 'error'}) in ${initStartTime ? (initEndTime - initStartTime).toFixed(2) : '?'}ms`);
|
|
263
|
+
}
|
|
264
|
+
|
|
212
265
|
Module._free(jsonPtr);
|
|
213
266
|
if (layerOverridesPtr) {
|
|
214
267
|
Module._free(layerOverridesPtr);
|
|
215
268
|
}
|
|
216
269
|
|
|
217
270
|
if (result !== 0) {
|
|
271
|
+
if (DebugMode) {
|
|
272
|
+
console.error(`[WASM] Animation initialization failed with error code: ${result}`);
|
|
273
|
+
}
|
|
218
274
|
throw new Error('Failed to initialize animation');
|
|
219
275
|
}
|
|
220
276
|
|
|
@@ -224,6 +280,10 @@ export function createAnimation(jsonData, layerOverrides = null, textPadding = 0
|
|
|
224
280
|
const durationPtr = Module._malloc(4);
|
|
225
281
|
const fpsPtr = Module._malloc(4);
|
|
226
282
|
|
|
283
|
+
if (DebugMode) {
|
|
284
|
+
console.log('[WASM] Getting animation info...');
|
|
285
|
+
}
|
|
286
|
+
|
|
227
287
|
Module._lotio_get_info(widthPtr, heightPtr, durationPtr, fpsPtr);
|
|
228
288
|
|
|
229
289
|
const info = {
|
|
@@ -233,6 +293,10 @@ export function createAnimation(jsonData, layerOverrides = null, textPadding = 0
|
|
|
233
293
|
fps: Module.HEAPF32[fpsPtr / 4]
|
|
234
294
|
};
|
|
235
295
|
|
|
296
|
+
if (DebugMode) {
|
|
297
|
+
console.log('[WASM] Animation info retrieved:', info);
|
|
298
|
+
}
|
|
299
|
+
|
|
236
300
|
Module._free(widthPtr);
|
|
237
301
|
Module._free(heightPtr);
|
|
238
302
|
Module._free(durationPtr);
|
|
@@ -246,6 +310,10 @@ export function renderFrameRGBA(time) {
|
|
|
246
310
|
throw new Error('WASM module not initialized.');
|
|
247
311
|
}
|
|
248
312
|
|
|
313
|
+
if (DebugMode) {
|
|
314
|
+
console.log(`[WASM] renderFrameRGBA called for time: ${time.toFixed(3)}s`);
|
|
315
|
+
}
|
|
316
|
+
|
|
249
317
|
// Ensure HEAP arrays are available - use same logic as createAnimation
|
|
250
318
|
if (!Module.HEAP32 || !Module.HEAPU8) {
|
|
251
319
|
let memoryBuffer = null;
|
|
@@ -283,8 +351,20 @@ export function renderFrameRGBA(time) {
|
|
|
283
351
|
const bufferSize = width * height * 4; // RGBA
|
|
284
352
|
const bufferPtr = Module._malloc(bufferSize);
|
|
285
353
|
|
|
354
|
+
let renderStartTime;
|
|
355
|
+
if (DebugMode) {
|
|
356
|
+
console.log(`[WASM] Allocated ${bufferSize} bytes (${width}x${height} RGBA) at pointer ${bufferPtr}`);
|
|
357
|
+
console.log(`[WASM] Calling _lotio_render_frame...`);
|
|
358
|
+
renderStartTime = performance.now();
|
|
359
|
+
}
|
|
360
|
+
|
|
286
361
|
const result = Module._lotio_render_frame(time, bufferPtr, bufferSize);
|
|
287
362
|
|
|
363
|
+
if (DebugMode) {
|
|
364
|
+
const renderEndTime = performance.now();
|
|
365
|
+
console.log(`[WASM] _lotio_render_frame returned: ${result} (${result === 0 ? 'success' : 'error'}) in ${renderStartTime ? (renderEndTime - renderStartTime).toFixed(2) : '?'}ms`);
|
|
366
|
+
}
|
|
367
|
+
|
|
288
368
|
if (result !== 0) {
|
|
289
369
|
Module._free(bufferPtr);
|
|
290
370
|
const errorMsg = result === 1 ? 'Animation not initialized' :
|
|
@@ -295,6 +375,9 @@ export function renderFrameRGBA(time) {
|
|
|
295
375
|
result === 6 ? 'Failed to create conversion surface' :
|
|
296
376
|
result === 7 ? 'Failed to create converted image' :
|
|
297
377
|
`Unknown error: ${result}`;
|
|
378
|
+
if (DebugMode) {
|
|
379
|
+
console.error(`[WASM] Render error: ${errorMsg} (code ${result})`);
|
|
380
|
+
}
|
|
298
381
|
throw new Error(`Failed to render frame: ${errorMsg} (code ${result})`);
|
|
299
382
|
}
|
|
300
383
|
|
|
@@ -305,32 +388,64 @@ export function renderFrameRGBA(time) {
|
|
|
305
388
|
const rgba = new Uint8Array(bufferView);
|
|
306
389
|
|
|
307
390
|
// Debug: Check if we got any non-zero pixels
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
391
|
+
if (DebugMode) {
|
|
392
|
+
let nonZeroPixels = 0;
|
|
393
|
+
let maxValue = 0;
|
|
394
|
+
for (let i = 0; i < Math.min(rgba.length, 1000); i++) {
|
|
395
|
+
if (rgba[i] !== 0) {
|
|
396
|
+
nonZeroPixels++;
|
|
397
|
+
maxValue = Math.max(maxValue, rgba[i]);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// Sample a few pixels from the center
|
|
402
|
+
const centerX = Math.floor(width / 2);
|
|
403
|
+
const centerY = Math.floor(height / 2);
|
|
404
|
+
const centerIdx = (centerY * width + centerX) * 4;
|
|
405
|
+
const centerPixel = centerIdx < rgba.length ? [
|
|
406
|
+
rgba[centerIdx],
|
|
407
|
+
rgba[centerIdx + 1],
|
|
408
|
+
rgba[centerIdx + 2],
|
|
409
|
+
rgba[centerIdx + 3]
|
|
410
|
+
] : [0, 0, 0, 0];
|
|
411
|
+
|
|
412
|
+
// Sample corner pixels too
|
|
413
|
+
const topLeftPixel = [
|
|
414
|
+
rgba[0],
|
|
415
|
+
rgba[1],
|
|
416
|
+
rgba[2],
|
|
417
|
+
rgba[3]
|
|
418
|
+
];
|
|
419
|
+
const bottomRightIdx = (height - 1) * width * 4 + (width - 1) * 4;
|
|
420
|
+
const bottomRightPixel = bottomRightIdx < rgba.length ? [
|
|
421
|
+
rgba[bottomRightIdx],
|
|
422
|
+
rgba[bottomRightIdx + 1],
|
|
423
|
+
rgba[bottomRightIdx + 2],
|
|
424
|
+
rgba[bottomRightIdx + 3]
|
|
425
|
+
] : [0, 0, 0, 0];
|
|
426
|
+
|
|
427
|
+
if (nonZeroPixels === 0) {
|
|
428
|
+
console.warn(`[WASM] Warning: All pixels are zero! Frame at time ${time} may be empty.`);
|
|
429
|
+
console.warn(`[WASM] Center pixel (${centerX}, ${centerY}): [${centerPixel.join(', ')}]`);
|
|
430
|
+
console.warn(`[WASM] Top-left pixel: [${topLeftPixel.join(', ')}]`);
|
|
431
|
+
console.warn(`[WASM] Bottom-right pixel: [${bottomRightPixel.join(', ')}]`);
|
|
432
|
+
} else {
|
|
433
|
+
console.log(`[WASM] Frame rendered at time ${time.toFixed(3)}s: ${nonZeroPixels} non-zero pixels found, max value: ${maxValue}`);
|
|
434
|
+
console.log(`[WASM] Center pixel (${centerX}, ${centerY}): [${centerPixel.join(', ')}]`);
|
|
435
|
+
console.log(`[WASM] Top-left pixel: [${topLeftPixel.join(', ')}]`);
|
|
436
|
+
console.log(`[WASM] Bottom-right pixel: [${bottomRightPixel.join(', ')}]`);
|
|
437
|
+
|
|
438
|
+
// Check alpha channel distribution
|
|
439
|
+
let opaquePixels = 0;
|
|
440
|
+
let transparentPixels = 0;
|
|
441
|
+
let semiTransparentPixels = 0;
|
|
442
|
+
for (let i = 3; i < Math.min(rgba.length, 10000); i += 4) {
|
|
443
|
+
if (rgba[i] === 0) transparentPixels++;
|
|
444
|
+
else if (rgba[i] === 255) opaquePixels++;
|
|
445
|
+
else semiTransparentPixels++;
|
|
446
|
+
}
|
|
447
|
+
console.log(`[WASM] Alpha channel stats (first 10k pixels): opaque=${opaquePixels}, transparent=${transparentPixels}, semi-transparent=${semiTransparentPixels}`);
|
|
314
448
|
}
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
// Sample a few pixels from the center
|
|
318
|
-
const centerX = Math.floor(width / 2);
|
|
319
|
-
const centerY = Math.floor(height / 2);
|
|
320
|
-
const centerIdx = (centerY * width + centerX) * 4;
|
|
321
|
-
const centerPixel = centerIdx < rgba.length ? [
|
|
322
|
-
rgba[centerIdx],
|
|
323
|
-
rgba[centerIdx + 1],
|
|
324
|
-
rgba[centerIdx + 2],
|
|
325
|
-
rgba[centerIdx + 3]
|
|
326
|
-
] : [0, 0, 0, 0];
|
|
327
|
-
|
|
328
|
-
if (nonZeroPixels === 0) {
|
|
329
|
-
console.warn(`[WASM] Warning: All pixels are zero! Frame at time ${time} may be empty.`);
|
|
330
|
-
console.warn(`[WASM] Center pixel (${centerX}, ${centerY}): [${centerPixel.join(', ')}]`);
|
|
331
|
-
} else {
|
|
332
|
-
console.log(`[WASM] Frame rendered: ${nonZeroPixels} non-zero pixels found, max value: ${maxValue}`);
|
|
333
|
-
console.log(`[WASM] Center pixel (${centerX}, ${centerY}): [${centerPixel.join(', ')}]`);
|
|
334
449
|
}
|
|
335
450
|
|
|
336
451
|
Module._free(bufferPtr);
|
|
@@ -423,22 +538,27 @@ export function renderFrameToCanvas(canvas, time, bgColor = '#2a2a2a') {
|
|
|
423
538
|
}
|
|
424
539
|
|
|
425
540
|
// Debug: Check if imageData is being populated
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
imageData.data[
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
541
|
+
if (DebugMode) {
|
|
542
|
+
const beforeSet = imageData.data[0];
|
|
543
|
+
imageData.data.set(rgba);
|
|
544
|
+
const afterSet = imageData.data[0];
|
|
545
|
+
|
|
546
|
+
// Check a few sample pixels
|
|
547
|
+
const sampleIdx = Math.floor((height / 2) * width + width / 2) * 4;
|
|
548
|
+
const samplePixel = sampleIdx < imageData.data.length ? [
|
|
549
|
+
imageData.data[sampleIdx],
|
|
550
|
+
imageData.data[sampleIdx + 1],
|
|
551
|
+
imageData.data[sampleIdx + 2],
|
|
552
|
+
imageData.data[sampleIdx + 3]
|
|
553
|
+
] : [0, 0, 0, 0];
|
|
554
|
+
|
|
555
|
+
console.log(`[Canvas] Rendering frame at time ${time.toFixed(3)}s, size: ${width}x${height}`);
|
|
556
|
+
console.log(`[Canvas] Sample pixel at center: [${samplePixel.join(', ')}]`);
|
|
557
|
+
console.log(`[Canvas] ImageData first byte before set: ${beforeSet}, after set: ${afterSet}`);
|
|
558
|
+
console.log(`[Canvas] Background color: ${bgColor}`);
|
|
559
|
+
} else {
|
|
560
|
+
imageData.data.set(rgba);
|
|
561
|
+
}
|
|
442
562
|
|
|
443
563
|
// Create a temporary canvas to composite the image over the background
|
|
444
564
|
// This ensures the background color shows through transparent areas
|
|
@@ -455,21 +575,28 @@ export function renderFrameToCanvas(canvas, time, bgColor = '#2a2a2a') {
|
|
|
455
575
|
tempCtx.putImageData(imageData, 0, 0);
|
|
456
576
|
|
|
457
577
|
// Debug: Check what's actually on the temp canvas
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
tempImageData.
|
|
461
|
-
tempImageData.data
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
578
|
+
if (DebugMode) {
|
|
579
|
+
const sampleIdx = Math.floor((height / 2) * width + width / 2) * 4;
|
|
580
|
+
const tempImageData = tempCtx.getImageData(0, 0, width, height);
|
|
581
|
+
const tempSamplePixel = sampleIdx < tempImageData.data.length ? [
|
|
582
|
+
tempImageData.data[sampleIdx],
|
|
583
|
+
tempImageData.data[sampleIdx + 1],
|
|
584
|
+
tempImageData.data[sampleIdx + 2],
|
|
585
|
+
tempImageData.data[sampleIdx + 3]
|
|
586
|
+
] : [0, 0, 0, 0];
|
|
587
|
+
console.log(`[Canvas] Temp canvas sample pixel: [${tempSamplePixel.join(', ')}]`);
|
|
588
|
+
}
|
|
466
589
|
|
|
467
590
|
// Draw the composited result to the main canvas
|
|
468
591
|
ctx.drawImage(tempCanvas, 0, 0);
|
|
469
592
|
|
|
470
593
|
// Final check: verify what's on the main canvas
|
|
471
|
-
|
|
472
|
-
|
|
594
|
+
if (DebugMode) {
|
|
595
|
+
const finalImageData = ctx.getImageData(0, 0, Math.min(width, 10), Math.min(height, 10));
|
|
596
|
+
console.log(`[Canvas] Final canvas first 10x10 pixels sample: [${Array.from(finalImageData.data.slice(0, 16)).join(', ')}]`);
|
|
597
|
+
console.log(`[Canvas] Canvas dimensions: ${canvas.width}x${canvas.height}`);
|
|
598
|
+
console.log(`[Canvas] Canvas context type: ${ctx.getContext ? '2d' : 'unknown'}`);
|
|
599
|
+
}
|
|
473
600
|
}
|
|
474
601
|
|
|
475
602
|
// Helper to render frame as ImageData
|