// META: global=window,dedicatedworker // META: script=/webcodecs/utils.js // META: script=/webcodecs/webgl-test-utils.js function testGLCanvas(gl, width, height, expectedPixel, assertCompares) { var colorData = new Uint8Array(gl.drawingBufferWidth * gl.drawingBufferHeight * 4); gl.readPixels( 0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight, gl.RGBA, gl.UNSIGNED_BYTE, colorData); assertCompares(gl.getError(), gl.NO_ERROR); const kMaxPixelToCheck = 128 * 96; let step = width * height / kMaxPixelToCheck; step = Math.round(step); step = (step < 1) ? 1 : step; for (let i = 0; i < 4 * width * height; i += (4 * step)) { assertCompares(colorData[i], expectedPixel[0]); assertCompares(colorData[i + 1], expectedPixel[1]); assertCompares(colorData[i + 2], expectedPixel[2]); assertCompares(colorData[i + 3], expectedPixel[3]); } } function testTexImage2DFromVideoFrame( width, height, useTexSubImage2D, expectedPixel) { let vfInit = {format: 'RGBA', timestamp: 0, codedWidth: width, codedHeight: height}; let argbData = new Uint32Array(vfInit.codedWidth * vfInit.codedHeight); argbData.fill(0xFF966432); // 'rgb(50, 100, 150)'; let frame = new VideoFrame(argbData, vfInit); let canvas; if (self.HTMLCanvasElement) { canvas = document.createElement("canvas"); canvas.width = width; canvas.height = height; } else canvas = new OffscreenCanvas(width, height); let gl = canvas.getContext('webgl'); let program = WebGLTestUtils.setupTexturedQuad(gl); gl.clearColor(0, 0, 0, 1); gl.clearDepth(1); gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); gl.colorMask(1, 1, 1, 0); // Disable any writes to the alpha channel. let textureLoc = gl.getUniformLocation(program, 'tex'); let texture = gl.createTexture(); // Bind the texture to texture unit 0. gl.bindTexture(gl.TEXTURE_2D, texture); // Set up texture parameters. 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); // Set up pixel store parameters. gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false); gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false); // Upload the videoElement into the texture if (useTexSubImage2D) { // Initialize the texture to black first gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, gl.RGBA, gl.UNSIGNED_BYTE, frame); } else { gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, frame); } frame.close(); assert_equals(gl.getError(), gl.NO_ERROR); // Point the uniform sampler to texture unit 0 gl.uniform1i(textureLoc, 0); // Draw the triangles WebGLTestUtils.drawQuad(gl, [0, 0, 0, 255]); // Wait for drawing to complete. gl.finish(); testGLCanvas(gl, width, height, expectedPixel, assert_equals); } function testTexImageWithClosedVideoFrame(useTexSubImage2D) { let width = 128; let height = 128; let vfInit = {format: 'RGBA', timestamp: 0, codedWidth: width, codedHeight: height}; let argbData = new Uint32Array(vfInit.codedWidth * vfInit.codedHeight); argbData.fill(0xFF966432); // 'rgb(50, 100, 150)'; let frame = new VideoFrame(argbData, vfInit); let canvas; if (self.HTMLCanvasElement) { canvas = document.createElement("canvas"); canvas.width = width; canvas.height = height; } else canvas = new OffscreenCanvas(width, height); let gl = canvas.getContext('webgl'); frame.close(); if (useTexSubImage2D) { gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, gl.RGBA, gl.UNSIGNED_BYTE, frame); } else { gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, frame); } assert_equals(gl.getError(), gl.INVALID_OPERATION); } test(_ => { testTexImage2DFromVideoFrame(48, 36, false, kSRGBPixel); }, 'texImage2D with 48x36 srgb VideoFrame.'); test(_ => { testTexImage2DFromVideoFrame(48, 36, true, kSRGBPixel); }, 'texSubImage2D with 48x36 srgb VideoFrame.'); test(_ => { testTexImage2DFromVideoFrame(480, 360, false, kSRGBPixel); }, 'texImage2D with 480x360 srgb VideoFrame.'); test(_ => { testTexImage2DFromVideoFrame(480, 360, true, kSRGBPixel); }, 'texSubImage2D with 480x360 srgb VideoFrame.'); test(_ => { testTexImageWithClosedVideoFrame(false); }, 'texImage2D with a closed VideoFrame.'); test(_ => { testTexImageWithClosedVideoFrame(true); }, 'texSubImage2D with a closed VideoFrame.');