texture-srgb-upload.html (8736B)
1 <!-- 2 Copyright (c) 2022 The Khronos Group Inc. 3 Use of this source code is governed by an MIT-style license that can be 4 found in the LICENSE.txt file. 5 --> 6 <!DOCTYPE html> 7 <html> 8 <head> 9 <meta charset=utf-8> 10 <title>Upload texture from video into srgb internalformats</title> 11 <link rel="stylesheet" href="../../../resources/js-test-style.css"/> 12 <script src="../../../js/js-test-pre.js"></script> 13 <script src="../../../js/webgl-test-utils.js"></script> 14 </head> 15 <body> 16 <div id="description"></div> 17 <div id="console"></div> 18 <div> 19 Video: 20 <canvas id="e_rgba" width="300" height="200"></canvas> 21 0x7f: <canvas id="e_rgba_color" width="30" height="200"></canvas> 22 <div>GL.RGBA</div> 23 </div> 24 <hr> 25 <div> 26 Video: 27 <canvas id="e_srgb8" width="300" height="200"></canvas> 28 0x7f: <canvas id="e_srgb8_color" width="30" height="200"></canvas> 29 <div>GL.SRGB8</div> 30 </div> 31 <hr> 32 <div> 33 Video: 34 <canvas id="e_srgb8_alpha8" width="300" height="200"></canvas> 35 0x7f: <canvas id="e_srgb8_alpha8_color" width="30" height="200"></canvas> 36 <div>GL.SRGB8_ALPHA8</div> 37 </div> 38 <script> 39 "use strict"; 40 const wtu = WebGLTestUtils; 41 description(); 42 43 const DATA_URL_FOR_720p_png_bt709_bt709_tv_yuv420p_vp9_webm = '\ 44 data:video/webm;base64,GkXfo59ChoEBQveBAULygQRC84EIQoKEd2VibUKHgQJChYECGFOAZwEA\ 45 AAAAAAMBEU2bdLpNu4tTq4QVSalmU6yBoU27i1OrhBZUrmtTrIHGTbuMU6uEElTDZ1OsggElTbuMU6u\ 46 EHFO7a1OsggLr7AEAAAAAAABZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\ 47 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVSalmoCrXsYMPQ\ 48 kBNgIRMYXZmV0GETGF2ZkSJiEBEAAAAAAAAFlSua9quAQAAAAAAAFHXgQFzxYgAAAAAAAAAAZyBACK1\ 49 nIN1bmSGhVZfVlA5g4EBI+ODhAJiWgDgAQAAAAAAAB6wggUAuoIC0JqBAlWwkFW6gQFVsYEBVbuBAVW\ 50 5gQESVMNn43NzAQAAAAAAAFljwItjxYgAAAAAAAAAAWfIAQAAAAAAABxFo4dFTkNPREVSRIePTGF2Yy\ 51 BsaWJ2cHgtdnA5Z8iiRaOIRFVSQVRJT05Eh5QwMDowMDowMC4wNDAwMDAwMDAAAB9DtnVBWOeBAKNBU\ 52 oEAAICCSYNCQE/wLPYAOCQcGAAYAFB/N9H/HZUjnnscu9GvIJt3936AAAAAACh4E4g/fJ8GmILlgmQ6\ 53 iUMwWlrCvdZpJAjY24ONeWCZEIrug5k4YTeAAAAAaXgTiD98nwaYguWCZDq6Zy9PLtRqFgTRRWpDzEC\ 54 RrKr8wtgzCibnQJwWtOOaHH9ZRjl4+aOQHHoHk/YUdplRSYiwuJO6LIyUXumq92uzm/wLAqBN0N9kRR\ 55 evcxyTv6VcsFqLJ5W5INE4AAAAAGN4E3vgaWsaGceNeWlTmlA/W7BnrSNUEx9X/o/hlK8PPDCgN5Kpw\ 56 0gRJkKtiMQMtYO7DQAUWLnf3+GjIUUj4hiAGdY+FNLJIdswhZLCeSDQfqV1btKL/ns57OfXQc0R3HFz\ 57 YyB4E3vgaWsaGceNeWjppQzBaWtIcWVNbYO5ARh7kHkq6WBosnlbkfoAHFO7a5G7j7OBALeK94EB8YI\ 58 BjfCBAw=='; 59 60 function invoke(fn) { return fn(); } 61 62 invoke(async () => { 63 const video = document.createElement("video"); 64 video.src = DATA_URL_FOR_720p_png_bt709_bt709_tv_yuv420p_vp9_webm; 65 if (!video.canPlayType('video/webm')) { 66 debug('Browser can not play webm videos. Skipping test.'); 67 finishTest(); 68 return; 69 } 70 71 video.muted = true; 72 video.loop = true; 73 video.crossOrigin = "anonymous"; 74 try { 75 await video.play(); 76 } catch (e) { 77 debug('Browser could not play this specific video. Skipping test.'); 78 finishTest(); 79 return; 80 } 81 82 function renderTex(canvas, fn_tex_image) { 83 const gl = canvas.gl = wtu.create3DContext(canvas); 84 85 const vs = ` 86 attribute float a_VertexID; 87 varying vec2 v_uv; 88 void main() { 89 float id = a_VertexID; 90 v_uv.x = mod(id, 2.0); 91 id -= v_uv.x; 92 id /= 2.0; 93 v_uv.y = mod(id, 2.0); 94 gl_Position = vec4(2.0 * v_uv - 1.0, 0, 1); 95 }`; 96 97 const fs = ` 98 precision mediump float; 99 uniform sampler2D tex; 100 varying vec2 v_uv; 101 void main() { 102 gl_FragColor = texture2D(tex, v_uv); 103 }`; 104 105 const program = gl.createProgram(); 106 let shader = gl.createShader(gl.VERTEX_SHADER); 107 gl.shaderSource(shader, vs); 108 gl.compileShader(shader); 109 gl.attachShader(program, shader); 110 shader = gl.createShader(gl.FRAGMENT_SHADER); 111 gl.shaderSource(shader, fs); 112 gl.compileShader(shader); 113 gl.attachShader(program, shader); 114 gl.bindAttribLocation(program, 0, 'a_VertexID'); 115 gl.linkProgram(program); 116 gl.useProgram(program); 117 if (gl.getError()) throw 'Error during linking'; 118 119 const vbuf = gl.createBuffer(); 120 gl.bindBuffer(gl.ARRAY_BUFFER, vbuf); 121 gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([0,1,2,3]), gl.STATIC_DRAW); 122 gl.enableVertexAttribArray(0); 123 gl.vertexAttribPointer(0, 1, gl.FLOAT, false, 0, 0); 124 125 const texture = gl.createTexture(); 126 gl.bindTexture(gl.TEXTURE_2D, texture); 127 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); 128 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); 129 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); 130 131 const draw = function() { 132 //requestAnimationFrame(draw); 133 fn_tex_image(gl); 134 gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); 135 }; 136 draw(); 137 138 if (gl.getError()) throw 'Error during drawing'; 139 } 140 141 const GL = WebGL2RenderingContext; 142 const COLOR_DATA = new Uint8Array([127, 127, 127, 255]); 143 144 function internalformat_webgl1or2(gl, internalformat_name) { 145 let internalformat = gl[internalformat_name]; 146 if (!internalformat) { 147 const ext = gl.getExtension('EXT_srgb'); 148 if (!ext) { 149 testPassed('EXT_srgb not supported. (ok!)'); 150 return; 151 } 152 switch (internalformat_name) { 153 case 'SRGB8': 154 internalformat = ext.SRGB_EXT; 155 break; 156 case 'SRGB8_ALPHA8': 157 internalformat = ext.SRGB_ALPHA_EXT; 158 break; 159 default: 160 throw internalformat_name; 161 } 162 } 163 return internalformat; 164 } 165 166 function begin(e_video, e_color, internalformat_name, unpackformat) { 167 renderTex(e_video, gl => { 168 const internalformat = internalformat_webgl1or2(gl, internalformat_name); 169 if (!gl.SRGB8) { 170 unpackformat = internalformat; // Must match in webgl1. 171 } 172 gl.texImage2D(GL.TEXTURE_2D, 0, internalformat, 173 unpackformat, GL.UNSIGNED_BYTE, video); 174 }); 175 renderTex(e_color, gl => { 176 const internalformat = internalformat_webgl1or2(gl, internalformat_name); 177 if (!gl.SRGB8) { 178 unpackformat = internalformat; // Must match in webgl1. 179 } 180 gl.texImage2D(GL.TEXTURE_2D, 0, internalformat, 1, 1, 0, 181 unpackformat, GL.UNSIGNED_BYTE, COLOR_DATA); 182 }); 183 } 184 185 begin(e_rgba, e_rgba_color, 'RGBA', GL.RGBA); 186 begin(e_srgb8, e_srgb8_color, 'SRGB8', GL.RGB); 187 begin(e_srgb8_alpha8, e_srgb8_alpha8_color, 'SRGB8_ALPHA8', GL.RGBA); 188 189 // - 190 191 const GREY50_COLOR_COORD = { 192 x: 0, 193 y: 0, 194 }; 195 const GREY50_TEX_COORD = { 196 x: e_rgba.width/2 + 1, 197 y: e_rgba.height/2 + 1, 198 }; 199 const fn_test = (canvas, coord, data) => { 200 wtu.checkCanvasRect(canvas.gl, coord.x, coord.y, 1, 1, data, 201 `${canvas.id} @${JSON.stringify(coord)}`); 202 } 203 204 debug(''); 205 debug('e_rgba'); 206 fn_test(e_rgba_color, GREY50_COLOR_COORD, [0x7f, 0x7f, 0x7f, 0xff]); 207 fn_test(e_rgba, GREY50_TEX_COORD, [0x7f, 0x7f, 0x7f, 0xff]); 208 209 debug(''); 210 debug('e_srgb8'); 211 fn_test(e_srgb8_color, GREY50_COLOR_COORD, [0x36, 0x36, 0x36, 0xff]); 212 fn_test(e_srgb8, GREY50_TEX_COORD, [0x36, 0x36, 0x36, 0xff]); 213 214 debug(''); 215 debug('e_srgb8_alpha8'); 216 fn_test(e_srgb8_alpha8_color, GREY50_COLOR_COORD, [0x36, 0x36, 0x36, 0xff]); 217 fn_test(e_srgb8_alpha8, GREY50_TEX_COORD, [0x36, 0x36, 0x36, 0xff]); 218 219 finishTest(); 220 }); 221 222 /* 223 async function blobToDataURL(blob) { 224 const fr = new FileReader(); 225 return await new Promise((yes, no) => { 226 fr.addEventListener('loadend', ev => { 227 if (fr.result) { 228 return yes(fr.result); 229 } 230 return no(fr.error); 231 }); 232 fr.readAsDataURL(blob); 233 }); 234 } 235 236 async function fetchDataUrl(url, wrapAt) { 237 const r = await fetch(url); 238 const b = await r.blob(); 239 const durl = await blobToDataURL(b); 240 return durl; 241 } 242 243 function wrapLines(str, wrapAt) { 244 const lines = []; 245 let remaining = str; 246 while (remaining) { 247 lines.push(remaining.slice(0, wrapAt)); 248 remaining = remaining.slice(wrapAt); 249 } 250 return lines; 251 } 252 253 (async () => { 254 const url = '720p.png.bt709.bt709.tv.yuv420p.vp9.webm'; 255 const ident = 'DATA_URL_FOR_' + url.replaceAll('.', '_'); 256 const durl = await fetchDataUrl(url); 257 const lines = wrapLines(durl, 79); 258 console.log(ident, '= \'\\\n' + lines.join('\\\n') + '\';'); 259 })(); 260 */ 261 </script> 262 </body> 263 </html>