| // META: script=/webcodecs/videoFrame-utils.js |
| |
| promise_test(async t => { |
| const W = 5; |
| const H = 4; |
| |
| const canvas = document.createElement('canvas'); |
| canvas.width = W; |
| canvas.height = H; |
| const ctx = canvas.getContext('2d'); |
| |
| ctx.fillStyle = '#00ff00'; |
| ctx.fillRect(0, 0, W, H); |
| |
| const frame = new VideoFrame(canvas, {timestamp: 0}); |
| t.add_cleanup(() => frame.close()); |
| |
| const format = frame.format; |
| assert_true( |
| format === 'RGBA' || format === 'RGBX' || |
| format === 'BGRA' || format === 'BGRX', |
| `Expected an RGB(A/X) format, got ${format}`); |
| |
| const size = frame.allocationSize(); |
| const buf = new Uint8Array(size); |
| await frame.copyTo(buf); |
| |
| const bpp = 4; |
| |
| for (let row = 0; row < H; row++) { |
| for (let x = 0; x < W; x++) { |
| const i = (row * W + x) * bpp; |
| const pixel = [buf[i], buf[i + 1], buf[i + 2], buf[i + 3]]; |
| |
| // Green channel must be 255; red and blue must be 0. |
| // Alpha may be 255 for RGBA/BGRA or implementation-defined for X formats. |
| const rIdx = (format === 'BGRA' || format === 'BGRX') ? 2 : 0; |
| const gIdx = 1; |
| const bIdx = (format === 'BGRA' || format === 'BGRX') ? 0 : 2; |
| const aIdx = 3; |
| |
| assert_equals(pixel[rIdx], 0, |
| `row ${row} col ${x}: red channel should be 0, got ${pixel[rIdx]}`); |
| assert_equals(pixel[gIdx], 255, |
| `row ${row} col ${x}: green channel should be 255, got ${pixel[gIdx]}`); |
| assert_equals(pixel[bIdx], 0, |
| `row ${row} col ${x}: blue channel should be 0, got ${pixel[bIdx]}`); |
| if (format === 'RGBA' || format === 'BGRA') { |
| assert_equals(pixel[aIdx], 255, |
| `row ${row} col ${x}: alpha should be 255, got ${pixel[aIdx]}`); |
| } |
| } |
| } |
| }, 'copyTo from canvas-backed VideoFrame returns correct pixels for all rows'); |
| |
| promise_test(async t => { |
| // Use a different non-aligned width to ensure this isn't width-specific. |
| const W = 3; |
| const H = 8; |
| |
| const canvas = document.createElement('canvas'); |
| canvas.width = W; |
| canvas.height = H; |
| const ctx = canvas.getContext('2d'); |
| |
| // Draw a distinct color per row so stride errors are easily detected. |
| for (let row = 0; row < H; row++) { |
| ctx.fillStyle = `rgb(${row * 30}, ${100 + row * 10}, ${200 - row * 20})`; |
| ctx.fillRect(0, row, W, 1); |
| } |
| |
| const frame = new VideoFrame(canvas, {timestamp: 0}); |
| t.add_cleanup(() => frame.close()); |
| |
| const format = frame.format; |
| const size = frame.allocationSize(); |
| const buf = new Uint8Array(size); |
| await frame.copyTo(buf); |
| |
| const bpp = 4; |
| |
| // Read back canvas pixels for ground truth. |
| const imageData = ctx.getImageData(0, 0, W, H); |
| const canvasPixels = imageData.data; |
| |
| for (let row = 0; row < H; row++) { |
| for (let x = 0; x < W; x++) { |
| const frameIdx = (row * W + x) * bpp; |
| const canvasIdx = (row * W + x) * 4; |
| |
| const cR = canvasPixels[canvasIdx]; |
| const cG = canvasPixels[canvasIdx + 1]; |
| const cB = canvasPixels[canvasIdx + 2]; |
| |
| let fR, fG, fB; |
| if (format === 'BGRA' || format === 'BGRX') { |
| fB = buf[frameIdx]; |
| fG = buf[frameIdx + 1]; |
| fR = buf[frameIdx + 2]; |
| } else { |
| fR = buf[frameIdx]; |
| fG = buf[frameIdx + 1]; |
| fB = buf[frameIdx + 2]; |
| } |
| |
| assert_approx_equals(fR, cR, 1, |
| `row ${row} col ${x}: R mismatch`); |
| assert_approx_equals(fG, cG, 1, |
| `row ${row} col ${x}: G mismatch`); |
| assert_approx_equals(fB, cB, 1, |
| `row ${row} col ${x}: B mismatch`); |
| } |
| } |
| }, 'copyTo from canvas-backed VideoFrame matches canvas pixel data per row'); |