diff --git a/src/__test__/setup.ts b/src/__test__/setup.ts index e98cd28..f11babb 100644 --- a/src/__test__/setup.ts +++ b/src/__test__/setup.ts @@ -34,6 +34,10 @@ if (typeof GPUTextureUsage === 'undefined') { }; } +// The fallback OffscreenCanvas returns zero-filled RGBA bytes from getImageData(). +// Zero-alpha pixels map to the transparent sentinel (index 0) in SpriteSheet.indexize(). +// Tests that need specific opaque pixel colors must stub OffscreenCanvas themselves +// (e.g. via vi.stubGlobal('OffscreenCanvas', ...)) before calling indexize(). if (typeof OffscreenCanvas === 'undefined') { (globalThis as unknown as Record).OffscreenCanvas = class { public readonly width: number; diff --git a/src/core/BTAPI.test.ts b/src/core/BTAPI.test.ts index 7c21638..3285979 100644 --- a/src/core/BTAPI.test.ts +++ b/src/core/BTAPI.test.ts @@ -207,7 +207,8 @@ describe('BTAPI', () => { }); it('drawBitmapText should not throw before init', () => { - const mockFont = {} as unknown as BitmapFont; + const mockSheet = { isIndexized: () => true } as unknown as SpriteSheet; + const mockFont = { getSpriteSheet: () => mockSheet } as unknown as BitmapFont; expect(() => BTAPI.instance.drawBitmapText(mockFont, new Vector2i(0, 0), 'hi')).not.toThrow(); }); diff --git a/src/core/BTAPI.ts b/src/core/BTAPI.ts index 4618a5f..29131e5 100644 --- a/src/core/BTAPI.ts +++ b/src/core/BTAPI.ts @@ -415,6 +415,8 @@ export class BTAPI { * @throws If the sprite sheet has not been indexized. */ public drawSprite(spriteSheet: SpriteSheet, srcRect: Rect2i, destPos: Vector2i, paletteOffset: number = 0): void { + this.assertPaletteIndex(paletteOffset); + if (!spriteSheet.isIndexized()) { throw new Error( '[BT] drawSprite: sprite sheet has not been indexized.' + @@ -434,8 +436,21 @@ export class BTAPI { * @param pos - Text position (top-left corner). * @param text - String to render. * @param paletteOffset - Palette index offset applied to all glyphs (default 0). + * @throws If the font's sprite sheet has not been indexized. */ public drawBitmapText(font: BitmapFont, pos: Vector2i, text: string, paletteOffset: number = 0): void { + this.assertPaletteIndex(paletteOffset); + + const sheet = font.getSpriteSheet(); + + if (!sheet.isIndexized()) { + throw new Error( + '[BT] drawBitmapText: font sprite sheet has not been indexized.' + + ' Call spriteSheet.indexize(palette) after setting a palette.', + ); + } + + this.spriteSheets.add(sheet); this.renderer?.drawBitmapText(font, pos, text, paletteOffset); } @@ -452,11 +467,23 @@ export class BTAPI { throw new Error('[BT] spritesRefresh: no active palette. Call BT.paletteSet() first.'); } + let refreshed = 0; + for (const sheet of this.spriteSheets) { - sheet.reindexize(this.palette); + if (!sheet.isIndexized()) { + this.spriteSheets.delete(sheet); + continue; + } + + try { + sheet.reindexize(this.palette); + refreshed++; + } catch { + this.spriteSheets.delete(sheet); + } } - console.log(`[BT] Refreshed ${this.spriteSheets.size} sprite sheet(s) against current palette`); + console.log(`[BT] Refreshed ${refreshed} sprite sheet(s) against current palette`); } // #endregion