| // META: script=/webcodecs/videoFrame-rgbx-utils.js | |
| // Uploads `frame` to a texture with texImage2D() and reads it back as RGBA. | |
| // `format` is the format of the texture, gl.RGBA or gl.RGB. The alpha of a | |
| // gl.RGB texture reads back as opaque. | |
| function readTexturePixels(frame, premultiplyAlpha, format = 'RGBA') { | |
| const canvas = document.createElement('canvas'); | |
| canvas.width = kRgbxWidth; | |
| canvas.height = kRgbxHeight; | |
| const gl = canvas.getContext( | |
| 'webgl', {alpha: true, premultipliedAlpha: false, antialias: false}); | |
| assert_not_equals(gl, null, 'WebGL is available'); | |
| const texture = gl.createTexture(); | |
| gl.bindTexture(gl.TEXTURE_2D, texture); | |
| gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); | |
| gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); | |
| gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); | |
| gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); | |
| gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, premultiplyAlpha); | |
| gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false); | |
| gl.texImage2D( | |
| gl.TEXTURE_2D, 0, gl[format], gl[format], gl.UNSIGNED_BYTE, frame); | |
| assert_equals(gl.getError(), gl.NO_ERROR, 'texImage2D'); | |
| // Read the texture through a framebuffer to see its exact RGBA content. | |
| const framebuffer = gl.createFramebuffer(); | |
| gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer); | |
| gl.framebufferTexture2D( | |
| gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0); | |
| assert_equals( | |
| gl.checkFramebufferStatus(gl.FRAMEBUFFER), gl.FRAMEBUFFER_COMPLETE, | |
| 'framebuffer'); | |
| const pixels = new Uint8Array(kRgbxWidth * kRgbxHeight * 4); | |
| gl.readPixels( | |
| 0, 0, kRgbxWidth, kRgbxHeight, gl.RGBA, gl.UNSIGNED_BYTE, pixels); | |
| assert_equals(gl.getError(), gl.NO_ERROR, 'readPixels'); | |
| gl.deleteFramebuffer(framebuffer); | |
| gl.deleteTexture(texture); | |
| return pixels; | |
| } | |
| for (const premultiplyAlpha of [false, true]) { | |
| runRgbxTests( | |
| `texImage2D() (premultiplyAlpha=${premultiplyAlpha})`, | |
| (frame, description) => { | |
| verifyOpaqueRgbxPixels( | |
| readTexturePixels(frame, premultiplyAlpha), description); | |
| }); | |
| } | |
| // With a gl.RGB texture the alpha of the frame is not uploaded at all: the RGB | |
| // values must be the same as with gl.RGBA, whatever the padding byte is. | |
| for (const premultiplyAlpha of [false, true]) { | |
| runRgbxTests( | |
| `texImage2D() as RGB (premultiplyAlpha=${premultiplyAlpha})`, | |
| (frame, description) => { | |
| verifyOpaqueRgbxPixels( | |
| readTexturePixels(frame, premultiplyAlpha, 'RGB'), description); | |
| }); | |
| } | |
| // A cloned frame is uploaded the same way. | |
| promise_test(async t => { | |
| const frame = makeRgbxFrame('RGBX', 0x00); | |
| t.add_cleanup(() => frame.close()); | |
| const clone = frame.clone(); | |
| t.add_cleanup(() => clone.close()); | |
| verifyOpaqueRgbxPixels(readTexturePixels(clone, false), 'cloned frame'); | |
| }, 'texImage2D() of a cloned RGBX VideoFrame is opaque'); |