From 4fbb42aa7350633c04272b093c4c582163a9a58d Mon Sep 17 00:00:00 2001 From: Chris Lorenzo Date: Wed, 17 Jun 2026 12:38:57 -0400 Subject: [PATCH] fix: respect createImageBitmap options on main thread when no crop Ensure options (premultiplyAlpha, colorSpaceConversion, imageOrientation) are passed to createImageBitmap on the main thread when no crop is requested. Previously, it incorrectly fell back to basic createImageBitmap since imageBitmapSupported.basic is true on all browsers supporting createImageBitmap. This aligns the main thread logic with the web worker's correct check. --- src/core/textures/ImageTexture.test.ts | 91 ++++++++++++++++++++++++++ src/core/textures/ImageTexture.ts | 5 +- 2 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 src/core/textures/ImageTexture.test.ts diff --git a/src/core/textures/ImageTexture.test.ts b/src/core/textures/ImageTexture.test.ts new file mode 100644 index 0000000..8517d1a --- /dev/null +++ b/src/core/textures/ImageTexture.test.ts @@ -0,0 +1,91 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { ImageTexture } from './ImageTexture.js'; +import type { CoreTextureManager } from '../CoreTextureManager.js'; + +describe('ImageTexture.createImageBitmap', () => { + beforeEach(() => { + vi.stubGlobal('ImageBitmap', class {}); + }); + + it('passes options to createImageBitmap when no crop is requested and options/full are supported', async () => { + const createImageBitmapMock = vi.fn(() => + Promise.resolve({ close: () => {} }), + ); + const txManager = { + imageBitmapSupported: { + basic: true, + options: true, + full: true, + premultiplyHonored: false, + }, + platform: { + createImageBitmap: createImageBitmapMock, + }, + } as unknown as CoreTextureManager; + + const props = ImageTexture.resolveDefaults({ + src: 'test.png', + premultiplyAlpha: true, + }); + const texture = new ImageTexture(txManager, props); + + const blob = new Blob([], { type: 'image/png' }); + const result = await texture.createImageBitmap( + blob, + true, + null, + null, + null, + null, + ); + + // It should have called createImageBitmap with the options object + expect(createImageBitmapMock).toHaveBeenCalledWith(blob, { + premultiplyAlpha: 'none', + colorSpaceConversion: 'none', + imageOrientation: 'none', + }); + + // Since premultiplyHonored is false, useGlPremultiply should be true + expect(result.premultiplyAlpha).toBe(true); + }); + + it('falls back to basic createImageBitmap when options are not supported', async () => { + const createImageBitmapMock = vi.fn(() => + Promise.resolve({ close: () => {} }), + ); + const txManager = { + imageBitmapSupported: { + basic: true, + options: false, + full: false, + premultiplyHonored: null, + }, + platform: { + createImageBitmap: createImageBitmapMock, + }, + } as unknown as CoreTextureManager; + + const props = ImageTexture.resolveDefaults({ + src: 'test.png', + premultiplyAlpha: true, + }); + const texture = new ImageTexture(txManager, props); + + const blob = new Blob([], { type: 'image/png' }); + const result = await texture.createImageBitmap( + blob, + true, + null, + null, + null, + null, + ); + + // It should have called basic createImageBitmap without options + expect(createImageBitmapMock).toHaveBeenCalledWith(blob); + + // And premultiplyAlpha should be false (browser default is assumed to premultiply) + expect(result.premultiplyAlpha).toBe(false); + }); +}); diff --git a/src/core/textures/ImageTexture.ts b/src/core/textures/ImageTexture.ts index a9651cc..a4e824b 100644 --- a/src/core/textures/ImageTexture.ts +++ b/src/core/textures/ImageTexture.ts @@ -219,7 +219,10 @@ export class ImageTexture extends Texture { }, ); return { data: bitmap, premultiplyAlpha: useGlPremultiply }; - } else if (imageBitmapSupported.basic === true) { + } else if ( + imageBitmapSupported.options === false && + imageBitmapSupported.full === false + ) { // basic createImageBitmap without options or crop // this is supported for Chrome v50 to v52/54 that doesn't support options. // The browser default premultiplies, so WebGL must not premultiply again.