gl-teximage.html (16639B)
1 <!-- 2 Copyright (c) 2019 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 7 <!DOCTYPE html> 8 <html> 9 <head> 10 <meta charset="utf-8"> 11 <title>WebGL texImage2D conformance test.</title> 12 <link rel="stylesheet" href="../../../resources/js-test-style.css"/> 13 <script src="../../../js/js-test-pre.js"></script> 14 <script src="../../../js/webgl-test-utils.js"> </script> 15 </head> 16 <body> 17 <canvas id="example" width="256" height="16" style="width: 256px; height: 48px;"></canvas> 18 <div id="description"></div> 19 <div id="console"></div> 20 <script> 21 "use strict"; 22 enableJSTestPreVerboseLogging(); 23 description("Test texImage2D conversions."); 24 var wtu = WebGLTestUtils; 25 var gl = wtu.create3DContext("example"); 26 gl.disable(gl.DITHER); 27 var program = wtu.setupTexturedQuad(gl); 28 var successfullyParsed; 29 30 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup."); 31 32 var imgURLs = [ 33 '../../../resources/1-channel.jpg', 34 '../../../resources/gray-ramp-256-with-128-alpha.png', 35 '../../../resources/gray-ramp-256.png', 36 '../../../resources/gray-ramp-default-gamma.png', 37 '../../../resources/gray-ramp-gamma0.1.png', 38 '../../../resources/gray-ramp-gamma1.0.png', 39 '../../../resources/gray-ramp-gamma2.0.png', 40 '../../../resources/gray-ramp-gamma4.0.png', 41 '../../../resources/gray-ramp-gamma9.0.png', 42 '../../../resources/gray-ramp.png', 43 '../../../resources/zero-alpha.png', 44 '../../../resources/3x3.png', 45 '../../../resources/blue-1x1.jpg', 46 '../../../resources/red-indexed.png', 47 '../../../resources/transparent-on-left-indexed.png', 48 '../../../resources/green-2x2-16bit.png', 49 '../../../resources/small-square-with-colorspin-profile.jpg', 50 '../../../resources/small-square-with-colorspin-profile.png', 51 '../../../resources/small-square-with-cie-rgb-profile.png', 52 '../../../resources/small-square-with-colormatch-profile.png', 53 '../../../resources/small-square-with-e-srgb-profile.png', 54 '../../../resources/small-square-with-smpte-c-profile.png', 55 '../../../resources/small-square-with-srgb-iec61966-2.1-profile.png']; 56 57 58 wtu.loadImagesAsync(imgURLs, runTests); 59 60 function runTests(imgs) { 61 var loc = gl.getUniformLocation(program, "tex"); 62 gl.uniform1i(loc, 0); 63 64 gl.disable(gl.BLEND); 65 gl.disable(gl.DEPTH_TEST); 66 67 var width = gl.canvas.width; 68 var height = gl.canvas.height; 69 70 function checkPixel(x, y, color) { 71 wtu.checkCanvasRect(gl, x, y, 1, 1, color); 72 } 73 74 function checkPixelRange(x, y, color, allowedRange) { 75 var msg = "pixel " + x + ", " + y + " should be within " + 76 allowedRange + " units of " + 77 color[0] + ", " + 78 color[1] + ", " + 79 color[2] + ", " + 80 color[3]; 81 wtu.checkCanvasRect(gl, x, y, 1, 1, color, msg, allowedRange); 82 } 83 84 var tex = gl.createTexture(); 85 gl.bindTexture(gl.TEXTURE_2D, tex); 86 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); 87 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); 88 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); 89 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); 90 91 var buf = new Uint8Array(width * height * 4); 92 93 debug(""); 94 debug("check pixels are NOT pre-multiplied"); 95 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, 96 imgs['../../../resources/zero-alpha.png']); 97 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup"); 98 wtu.clearAndDrawUnitQuad(gl); 99 100 var left = 0; 101 var middle = Math.floor(width / 2); 102 var right = width - 1; 103 var bottom = 0; 104 var center = Math.floor(height / 2); 105 var top = height - 1; 106 checkPixel(left, top, [ 0, 0, 0, 255]); 107 checkPixel(middle, top, [255, 0, 255, 255]); 108 checkPixel(right, top, [ 0, 0, 255, 255]); 109 checkPixel(left, center, [128, 128, 128, 255]); 110 checkPixel(middle, center, [255, 255, 255, 255]); 111 checkPixel(right, center, [ 0, 255, 255, 255]); 112 checkPixel(left, bottom, [255, 0, 0, 255]); 113 checkPixel(middle, bottom, [255, 255, 0, 255]); 114 checkPixel(right, bottom, [ 0, 255, 0, 255]); 115 116 debug(""); 117 debug("check quantization"); 118 var quantInfo = [ 119 {format: gl.RGBA, type: gl.UNSIGNED_BYTE, counts: [256, 256, 256, 256]}, 120 {format: gl.RGBA, type: gl.UNSIGNED_SHORT_4_4_4_4, counts: [ 16, 16, 16, 16]}, 121 {format: gl.RGB, type: gl.UNSIGNED_SHORT_5_6_5, counts: [ 32, 64, 32, 1]}, 122 {format: gl.RGBA, type: gl.UNSIGNED_SHORT_5_5_5_1, counts: [ 32, 32, 32, 2]}]; 123 for (var qq = 0; qq < quantInfo.length; ++qq) { 124 var info = quantInfo[qq]; 125 gl.texImage2D( 126 gl.TEXTURE_2D, 0, info.format, info.format, info.type, 127 imgs['../../../resources/gray-ramp-256.png']); 128 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup."); 129 wtu.clearAndDrawUnitQuad(gl); 130 gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, buf); 131 var counts = [{ }, { }, { }, { }]; 132 var numUniqueValues = [0, 0, 0, 0]; 133 // Count the number of unique values in each channel. 134 for (var ii = 0; ii < width * height * 4; ii += 4) { 135 for (var jj = 0; jj < 4; ++jj) { 136 var v = buf[ii + jj]; 137 if (!counts[jj][v]) { 138 counts[jj][v] = 1; 139 ++numUniqueValues[jj]; 140 } else { 141 ++counts[jj][v]; 142 } 143 } 144 } 145 for (var ii = 0; ii < 4; ++ii) { 146 assertMsg(numUniqueValues[ii] == info.counts[ii], 147 "There should be " + info.counts[ii] + 148 " unique values in channel " + ii + ". Found " + 149 numUniqueValues[ii]); 150 } 151 } 152 153 debug(""); 154 debug("Check that gamma settings don't effect 8bit pngs"); 155 wtu.failIfGLError(gl, 'gl.pixelStorei(gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.NONE);'); 156 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, 157 imgs['../../../resources/gray-ramp-default-gamma.png']); 158 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup."); 159 wtu.clearAndDrawUnitQuad(gl); 160 var ref = new Uint8Array(width * height * 4); 161 gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, ref); 162 163 var gammaImages = [ 164 '../../../resources/gray-ramp-gamma0.1.png', 165 '../../../resources/gray-ramp-gamma1.0.png', 166 '../../../resources/gray-ramp-gamma2.0.png', 167 '../../../resources/gray-ramp-gamma4.0.png', 168 '../../../resources/gray-ramp-gamma9.0.png']; 169 for (var ii = 0; ii < gammaImages.length; ++ii) { 170 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, 171 imgs[gammaImages[ii]]); 172 wtu.clearAndDrawUnitQuad(gl); 173 gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, buf); 174 var same = true; 175 for (var jj = 0; jj < width * height * 4; ++jj) { 176 if (buf[jj] != ref[jj]) { 177 same = false; 178 break; 179 } 180 } 181 assertMsg(same, "pixels should be same regardless of gamma settings."); 182 } 183 184 debug(""); 185 debug("check pixels are UN pre-multiplied"); 186 for (var ii = 0; ii < 2; ++ii) { 187 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); 188 if (ii == 0) { 189 var canvas2d = document.createElement("canvas"); 190 canvas2d.width = 256; 191 canvas2d.height = 1; 192 var ctx = canvas2d.getContext("2d"); 193 ctx.drawImage(imgs['../../../resources/gray-ramp-256-with-128-alpha.png'], 0, 0); 194 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, canvas2d); 195 } else { 196 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, 197 imgs['../../../resources/gray-ramp-256-with-128-alpha.png']); 198 } 199 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup."); 200 wtu.clearAndDrawUnitQuad(gl); 201 var buf = new Uint8Array(width * height * 4); 202 gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, buf); 203 var lt128Count = [0, 0, 0]; 204 var ge128Count = [0, 0, 0]; 205 for (var jj = 0; jj < width; ++jj) { 206 var off = jj * 4; 207 for (var cc = 0; cc < 3; ++cc) { 208 if (buf[off + cc] < 128) { 209 ++lt128Count[cc]; 210 } else { 211 ++ge128Count[cc]; 212 } 213 } 214 } 215 // Not sure the exact count here because gamma does effect drawing into the 216 // canvas but it should be close to 50% so I'll pass 45% 217 for (var jj = 0; jj < 3; ++jj) { 218 assertMsg(ge128Count[jj] > 256 * 0.45, 219 "Half the pixels in channel " + jj + 220 " should be >= 128,128,128. found " + 221 ((ge128Count[jj] / 256) * 100).toFixed() + "%"); 222 assertMsg(lt128Count[jj] > 256 * 0.45, 223 "Half the pixels in channel " + jj + 224 " should be < 128,128,128. found " + 225 ((lt128Count[jj] / 256) * 100).toFixed() + "%"); 226 } 227 } 228 229 debug(""); 230 debug("check canvas pixels are UN pre-multiplied"); 231 var canvas2d = document.createElement("canvas"); 232 canvas2d.width = 1; 233 canvas2d.height = 1; 234 var ctx = canvas2d.getContext("2d"); 235 ctx.fillStyle ="rgba(255,255,255,0.5)"; 236 ctx.fillRect(0, 0, 256, 1); 237 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, canvas2d); 238 wtu.clearAndDrawUnitQuad(gl); 239 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup."); 240 checkPixelRange(0, 0, [255, 255, 255, 127], 4); 241 242 debug(""); 243 debug("check canvas pixels are pre-multiplied"); 244 gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true); 245 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, canvas2d); 246 wtu.clearAndDrawUnitQuad(gl); 247 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup."); 248 checkPixelRange(0, 0, [127, 127, 127, 127], 4); 249 250 251 debug(""); 252 debug("check pixels are pre-multiplied"); 253 gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true); 254 // TODO(gman): use different texture that won't pass on failure 255 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, 256 imgs['../../../resources/zero-alpha.png']); 257 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup"); 258 wtu.clearAndDrawUnitQuad(gl); 259 gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, buf); 260 261 var same = true; 262 for (var jj = 0; jj < width * height * 4; ++jj) { 263 if (buf[jj] != 0) { 264 same = false; 265 break; 266 } 267 } 268 assertMsg(same, "pixels should all be 0."); 269 270 debug(""); 271 debug("check pixels are flipped"); 272 gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false); 273 gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true); 274 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, 275 imgs['../../../resources/3x3.png']); 276 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup"); 277 wtu.clearAndDrawUnitQuad(gl); 278 279 checkPixel(left, top, [255, 0, 0, 255]); 280 checkPixel(middle, top, [255, 255, 0, 255]); 281 checkPixel(right, top, [255, 0, 0, 255]); 282 checkPixel(left, center, [255, 0, 255, 255]); 283 checkPixel(middle, center, [255, 0, 0, 255]); 284 checkPixel(right, center, [ 0, 255, 0, 255]); 285 checkPixel(left, bottom, [ 0, 0, 0, 255]); 286 checkPixel(middle, bottom, [ 0, 0, 255, 255]); 287 checkPixel(right, bottom, [255, 0, 0, 255]); 288 289 debug(""); 290 debug("check uploading of images with no alpha channel works"); 291 gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false); 292 gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false); 293 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, 294 imgs['../../../resources/blue-1x1.jpg']); 295 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup"); 296 wtu.clearAndDrawUnitQuad(gl); 297 checkPixelRange(middle, center, [ 0, 0, 255, 255], 10); 298 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors"); 299 300 debug(""); 301 debug("check uploading of 16-bit images"); 302 gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false); 303 gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false); 304 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, 305 imgs['../../../resources/green-2x2-16bit.png']); 306 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup"); 307 wtu.clearAndDrawUnitQuad(gl); 308 checkPixelRange(middle, center, [ 15, 121, 0, 255], 10); 309 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors"); 310 311 debug(""); 312 debug("check uploading of images with ICC profiles"); 313 gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false); 314 gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false); 315 wtu.failIfGLError(gl, 'gl.pixelStorei(gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.NONE);'); 316 317 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, 318 imgs['../../../resources/small-square-with-colorspin-profile.jpg']); 319 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup"); 320 wtu.clearAndDrawUnitQuad(gl); 321 // The image is red. However, if we ignore the color profile, it is blue. 322 checkPixelRange(middle, center, [ 0, 0, 255, 255], 10); 323 324 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, 325 imgs['../../../resources/small-square-with-colorspin-profile.png']); 326 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup"); 327 wtu.clearAndDrawUnitQuad(gl); 328 // The image is red. However, if we ignore the color profile, it is blue. 329 checkPixelRange(middle, center, [ 0, 0, 255, 255], 10); 330 331 var iccPNGs = [ 332 '../../../resources/small-square-with-cie-rgb-profile.png', 333 '../../../resources/small-square-with-colormatch-profile.png', 334 '../../../resources/small-square-with-e-srgb-profile.png', 335 '../../../resources/small-square-with-smpte-c-profile.png', 336 '../../../resources/small-square-with-srgb-iec61966-2.1-profile.png']; 337 gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, buf); 338 for (var ii = 0; ii < iccPNGs.length; ++ii) { 339 var buf2 = new Uint8Array(width * height * 4); 340 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, 341 imgs[iccPNGs[ii]]); 342 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup"); 343 wtu.clearAndDrawUnitQuad(gl); 344 gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, buf2); 345 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors"); 346 var same = true; 347 for (var jj = 0; jj < buf.length; ++jj) { 348 if (buf[jj] != buf2[jj]) { 349 same = false; 350 break; 351 } 352 } 353 assertMsg(same, "uploading PNGs with same data but various ICC profiles should generate the same results"); 354 } 355 356 debug(""); 357 debug("check uploading of indexed PNG images"); 358 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, 359 imgs['../../../resources/red-indexed.png']); 360 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup"); 361 wtu.clearAndDrawUnitQuad(gl); 362 // The image should be red. 363 checkPixelRange(middle, center, [ 255, 0, 0, 255 ], 10); 364 365 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup"); 366 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, 367 imgs['../../../resources/transparent-on-left-indexed.png']); 368 wtu.clearAndDrawUnitQuad(gl); 369 wtu.checkCanvasRect(gl, 0, 0, 128, 16, [255, 0, 255, 0], "should be transparent purple"); 370 wtu.checkCanvasRect(gl, 128, 0,128, 16, [255, 255, 0, 255], "should be yellow"); 371 372 debug(""); 373 debug("check uploading of 1-channel JPG images"); 374 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, 375 imgs['../../../resources/1-channel.jpg']); 376 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup"); 377 wtu.clearAndDrawUnitQuad(gl); 378 // The image should be gray. 379 checkPixelRange(middle, center, [ 128, 128, 128, 255 ], 28); 380 381 debug("") 382 debug("check calling texImage2D with NULL clears the texture"); 383 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, 384 imgs['../../../resources/red-indexed.png'].width, 385 imgs['../../../resources/red-indexed.png'].height, 386 0, gl.RGB, gl.UNSIGNED_BYTE, null); 387 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup"); 388 wtu.clearAndDrawUnitQuad(gl); 389 // The image should be white. 390 checkPixelRange(middle, center, [ 0, 0, 0, 255 ], 10); 391 392 debug(""); 393 debug("check zero size cases"); 394 gl.texImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE, 2, 0, 0, gl.LUMINANCE, gl.UNSIGNED_BYTE, new Uint8Array()); 395 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from zero sized textures"); 396 397 debug(""); 398 successfullyParsed = true; 399 shouldBeTrue("successfullyParsed"); 400 debug('<br /><span class="pass">TEST COMPLETE</span>'); 401 notifyFinishedToHarness(); 402 } 403 </script> 404 </body> 405 </html>