4 ** Copyright (c) 2012 The Khronos Group Inc.
6 ** Permission is hereby granted, free of charge, to any person obtaining a
7 ** copy of this software and/or associated documentation files (the
8 ** "Materials"), to deal in the Materials without restriction, including
9 ** without limitation the rights to use, copy, modify, merge, publish,
10 ** distribute, sublicense, and/or sell copies of the Materials, and to
11 ** permit persons to whom the Materials are furnished to do so, subject to
12 ** the following conditions:
14 ** The above copyright notice and this permission notice shall be included
15 ** in all copies or substantial portions of the Materials.
17 ** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20 ** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
21 ** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
22 ** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
23 ** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
31 <meta charset="utf-8">
32 <title>WebGL texImage2D conformance test.</title>
33 <link rel="stylesheet" href="../../resources/js-test-style.css"/>
34 <script src="../../resources/js-test-pre.js"></script>
35 <script src="../resources/webgl-test-utils.js"> </script>
38 <canvas id="example" width="256" height="16" style="width: 256px; height: 48px;"></canvas>
39 <div id="description"></div>
40 <div id="console"></div>
43 enableJSTestPreVerboseLogging();
44 description("Test texImage2D conversions.");
45 var wtu = WebGLTestUtils;
46 var gl = wtu.create3DContext("example");
47 gl.disable(gl.DITHER);
48 var program = wtu.setupTexturedQuad(gl);
49 var successfullyParsed;
51 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup.");
54 '../resources/1-channel.jpg',
55 '../resources/gray-ramp-256-with-128-alpha.png',
56 '../resources/gray-ramp-256.png',
57 '../resources/gray-ramp-default-gamma.png',
58 '../resources/gray-ramp-gamma0.1.png',
59 '../resources/gray-ramp-gamma1.0.png',
60 '../resources/gray-ramp-gamma2.0.png',
61 '../resources/gray-ramp-gamma4.0.png',
62 '../resources/gray-ramp-gamma9.0.png',
63 '../resources/gray-ramp.png',
64 '../resources/zero-alpha.png',
65 '../resources/3x3.png',
66 '../resources/blue-1x1.jpg',
67 '../resources/red-indexed.png',
68 '../resources/transparent-on-left-indexed.png',
69 '../resources/green-2x2-16bit.png',
70 '../resources/small-square-with-colorspin-profile.jpg',
71 '../resources/small-square-with-colorspin-profile.png',
72 '../resources/small-square-with-cie-rgb-profile.png',
73 '../resources/small-square-with-colormatch-profile.png',
74 '../resources/small-square-with-e-srgb-profile.png',
75 '../resources/small-square-with-smpte-c-profile.png',
76 '../resources/small-square-with-srgb-iec61966-2.1-profile.png'];
79 wtu.loadImagesAsync(imgURLs, runTests);
81 function runTests(imgs) {
82 var loc = gl.getUniformLocation(program, "tex");
86 gl.disable(gl.DEPTH_TEST);
88 var width = gl.canvas.width;
89 var height = gl.canvas.height;
91 function checkPixel(buf, x, y, color) {
92 var off = (y * width + x) * 4;
93 var msg = "pixel " + x + ", " + y + " should be " +
100 buf[off + 2] + ", " +
103 for (var ii = 0; ii < 4; ++ii) {
104 if (buf[off + ii] != color[ii]) {
112 function checkPixelRange(buf, x, y, color, allowedRange) {
113 var off = (y * width + x) * 4;
114 var msg = "pixel " + x + ", " + y + " should be within " +
115 allowedRange + " units of " +
120 var subMsg = " was " +
121 buf[off + 0] + ", " +
122 buf[off + 1] + ", " +
123 buf[off + 2] + ", " +
125 // When running in WebKit's test harness, we don't want to print the
126 // pixel value when the test passes, because different machines might
127 // have different results and we record the text output.
128 var inDumpRenderTree = window.layoutTestController;
129 for (var ii = 0; ii < 4; ++ii) {
130 if (Math.abs(buf[off + ii] - color[ii]) > allowedRange) {
131 testFailed(msg + subMsg);
135 testPassed(msg + (inDumpRenderTree ? "" : subMsg));
138 var tex = gl.createTexture();
139 gl.bindTexture(gl.TEXTURE_2D, tex);
140 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
141 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
142 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
143 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
145 var buf = new Uint8Array(width * height * 4);
148 debug("check pixels are NOT pre-multiplied");
149 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE,
150 imgs['../resources/zero-alpha.png']);
151 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup");
152 wtu.clearAndDrawUnitQuad(gl);
153 gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, buf);
156 var middle = Math.floor(width / 2);
157 var right = width - 1;
159 var center = Math.floor(height / 2);
160 var top = height - 1;
161 checkPixel(buf, left, top, [ 0, 0, 0, 255]);
162 checkPixel(buf, middle, top, [255, 0, 255, 255]);
163 checkPixel(buf, right, top, [ 0, 0, 255, 255]);
164 checkPixel(buf, left, center, [128, 128, 128, 255]);
165 checkPixel(buf, middle, center, [255, 255, 255, 255]);
166 checkPixel(buf, right, center, [ 0, 255, 255, 255]);
167 checkPixel(buf, left, bottom, [255, 0, 0, 255]);
168 checkPixel(buf, middle, bottom, [255, 255, 0, 255]);
169 checkPixel(buf, right, bottom, [ 0, 255, 0, 255]);
172 debug("check quantization");
174 {format: gl.RGBA, type: gl.UNSIGNED_BYTE, counts: [256, 256, 256, 256]},
175 {format: gl.RGBA, type: gl.UNSIGNED_SHORT_4_4_4_4, counts: [ 16, 16, 16, 16]},
176 {format: gl.RGB, type: gl.UNSIGNED_SHORT_5_6_5, counts: [ 32, 64, 32, 1]},
177 {format: gl.RGBA, type: gl.UNSIGNED_SHORT_5_5_5_1, counts: [ 32, 32, 32, 2]}];
178 for (var qq = 0; qq < quantInfo.length; ++qq) {
179 var info = quantInfo[qq];
181 gl.TEXTURE_2D, 0, info.format, info.format, info.type,
182 imgs['../resources/gray-ramp-256.png']);
183 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup.");
184 wtu.clearAndDrawUnitQuad(gl);
185 gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, buf);
186 var counts = [{ }, { }, { }, { }];
187 var numUniqueValues = [0, 0, 0, 0];
188 // Count the number of unique values in each channel.
189 for (var ii = 0; ii < width * height * 4; ii += 4) {
190 for (var jj = 0; jj < 4; ++jj) {
191 var v = buf[ii + jj];
192 if (!counts[jj][v]) {
194 ++numUniqueValues[jj];
200 for (var ii = 0; ii < 4; ++ii) {
201 assertMsg(numUniqueValues[ii] == info.counts[ii],
202 "There should be " + info.counts[ii] +
203 " unique values in channel " + ii + ". Found " +
204 numUniqueValues[ii]);
209 debug("Check that gamma settings don't effect 8bit pngs");
210 gl.pixelStorei(gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.NONE);
211 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE,
212 imgs['../resources/gray-ramp-default-gamma.png']);
213 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup.");
214 wtu.clearAndDrawUnitQuad(gl);
215 var ref = new Uint8Array(width * height * 4);
216 gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, ref);
219 '../resources/gray-ramp-gamma0.1.png',
220 '../resources/gray-ramp-gamma1.0.png',
221 '../resources/gray-ramp-gamma2.0.png',
222 '../resources/gray-ramp-gamma4.0.png',
223 '../resources/gray-ramp-gamma9.0.png'];
224 for (var ii = 0; ii < gammaImages.length; ++ii) {
225 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE,
226 imgs[gammaImages[ii]]);
227 wtu.clearAndDrawUnitQuad(gl);
228 gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, buf);
230 for (var jj = 0; jj < width * height * 4; ++jj) {
231 if (buf[jj] != ref[jj]) {
236 assertMsg(same, "pixels should be same regardless of gamma settings.");
240 debug("check pixels are UN pre-multiplied");
241 for (var ii = 0; ii < 2; ++ii) {
242 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
244 var canvas2d = document.createElement("canvas");
245 canvas2d.width = 256;
247 var ctx = canvas2d.getContext("2d");
248 ctx.drawImage(imgs['../resources/gray-ramp-256-with-128-alpha.png'], 0, 0);
249 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, canvas2d);
251 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE,
252 imgs['../resources/gray-ramp-256-with-128-alpha.png']);
254 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup.");
255 wtu.clearAndDrawUnitQuad(gl);
256 var buf = new Uint8Array(width * height * 4);
257 gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, buf);
258 var lt128Count = [0, 0, 0];
259 var ge128Count = [0, 0, 0];
260 for (var jj = 0; jj < width; ++jj) {
262 for (var cc = 0; cc < 3; ++cc) {
263 if (buf[off + cc] < 128) {
270 // Not sure the exact count here because gamma does effect drawing into the
271 // canvas but it should be close to 50% so I'll pass 45%
272 for (var jj = 0; jj < 3; ++jj) {
273 assertMsg(ge128Count[jj] > 256 * 0.45,
274 "Half the pixels in channel " + jj +
275 " should be >= 128,128,128. found " +
276 ((ge128Count[jj] / 256) * 100).toFixed() + "%");
277 assertMsg(lt128Count[jj] > 256 * 0.45,
278 "Half the pixels in channel " + jj +
279 " should be < 128,128,128. found " +
280 ((lt128Count[jj] / 256) * 100).toFixed() + "%");
285 debug("check canvas pixels are UN pre-multiplied");
286 var canvas2d = document.createElement("canvas");
289 var ctx = canvas2d.getContext("2d");
290 ctx.fillStyle ="rgba(255,255,255,0.5)";
291 ctx.fillRect(0, 0, 256, 1);
292 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, canvas2d);
293 wtu.clearAndDrawUnitQuad(gl);
294 gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, buf);
295 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup.");
296 checkPixelRange(buf, 0, 0, [255, 255, 255, 127], 4);
299 debug("check canvas pixels are pre-multiplied");
300 gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);
301 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, canvas2d);
302 wtu.clearAndDrawUnitQuad(gl);
303 gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, buf);
304 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup.");
305 checkPixelRange(buf, 0, 0, [127, 127, 127, 127], 4);
309 debug("check pixels are pre-multiplied");
310 gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);
311 // TODO(gman): use different texture that won't pass on failure
312 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE,
313 imgs['../resources/zero-alpha.png']);
314 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup");
315 wtu.clearAndDrawUnitQuad(gl);
316 gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, buf);
319 for (var jj = 0; jj < width * height * 4; ++jj) {
325 assertMsg(same, "pixels should all be 0.");
328 debug("check pixels are flipped");
329 gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
330 gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
331 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE,
332 imgs['../resources/3x3.png']);
333 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup");
334 wtu.clearAndDrawUnitQuad(gl);
335 gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, buf);
337 checkPixel(buf, left, top, [255, 0, 0, 255]);
338 checkPixel(buf, middle, top, [255, 255, 0, 255]);
339 checkPixel(buf, right, top, [255, 0, 0, 255]);
340 checkPixel(buf, left, center, [255, 0, 255, 255]);
341 checkPixel(buf, middle, center, [255, 0, 0, 255]);
342 checkPixel(buf, right, center, [ 0, 255, 0, 255]);
343 checkPixel(buf, left, bottom, [ 0, 0, 0, 255]);
344 checkPixel(buf, middle, bottom, [ 0, 0, 255, 255]);
345 checkPixel(buf, right, bottom, [255, 0, 0, 255]);
348 debug("check uploading of images with no alpha channel works");
349 gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
350 gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);
351 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE,
352 imgs['../resources/blue-1x1.jpg']);
353 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup");
354 wtu.clearAndDrawUnitQuad(gl);
355 gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, buf);
356 checkPixelRange(buf, middle, center, [ 0, 0, 255, 255], 10);
357 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors");
360 debug("check uploading of 16-bit images");
361 gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
362 gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);
363 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE,
364 imgs['../resources/green-2x2-16bit.png']);
365 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup");
366 wtu.clearAndDrawUnitQuad(gl);
367 gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, buf);
368 checkPixelRange(buf, middle, center, [ 15, 121, 0, 255], 10);
369 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors");
372 debug("check uploading of images with ICC profiles");
373 gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
374 gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);
375 gl.pixelStorei(gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.NONE);
377 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE,
378 imgs['../resources/small-square-with-colorspin-profile.jpg']);
379 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup");
380 wtu.clearAndDrawUnitQuad(gl);
381 gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, buf);
382 // The image is red. However, if we ignore the color profile, it is blue.
383 checkPixelRange(buf, middle, center, [ 0, 0, 255, 255], 10);
385 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE,
386 imgs['../resources/small-square-with-colorspin-profile.png']);
387 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup");
388 wtu.clearAndDrawUnitQuad(gl);
389 gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, buf);
390 // The image is red. However, if we ignore the color profile, it is blue.
391 checkPixelRange(buf, middle, center, [ 0, 0, 255, 255], 10);
394 '../resources/small-square-with-cie-rgb-profile.png',
395 '../resources/small-square-with-colormatch-profile.png',
396 '../resources/small-square-with-e-srgb-profile.png',
397 '../resources/small-square-with-smpte-c-profile.png',
398 '../resources/small-square-with-srgb-iec61966-2.1-profile.png'];
399 for (var ii = 0; ii < iccPNGs.length; ++ii) {
400 var buf2 = new Uint8Array(width * height * 4);
401 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE,
403 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup");
404 wtu.clearAndDrawUnitQuad(gl);
405 gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, buf2);
406 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors");
408 for (var jj = 0; jj < buf.length; ++jj) {
409 if (buf[jj] != buf2[jj]) {
414 assertMsg(same, "uploading PNGs with same data but various ICC profiles should generate the same results");
418 debug("check uploading of indexed PNG images");
419 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE,
420 imgs['../resources/red-indexed.png']);
421 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup");
422 wtu.clearAndDrawUnitQuad(gl);
423 gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, buf);
424 // The image should be red.
425 checkPixelRange(buf, middle, center, [ 255, 0, 0, 255 ], 10);
427 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup");
428 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE,
429 imgs['../resources/transparent-on-left-indexed.png']);
430 wtu.clearAndDrawUnitQuad(gl);
431 wtu.checkCanvasRect(gl, 0, 0, 128, 16, [255, 0, 255, 0], "should be transparent purple");
432 wtu.checkCanvasRect(gl, 128, 0,128, 16, [255, 255, 0, 255], "should be yellow");
435 debug("check uploading of 1-channel JPG images");
436 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE,
437 imgs['../resources/1-channel.jpg']);
438 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup");
439 wtu.clearAndDrawUnitQuad(gl);
440 gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, buf);
441 // The image should be gray.
442 checkPixelRange(buf, middle, center, [ 128, 128, 128, 255 ], 28);
445 debug("check calling texImage2D with NULL clears the texture");
446 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB,
447 imgs['../resources/red-indexed.png'].width,
448 imgs['../resources/red-indexed.png'].height,
449 0, gl.RGB, gl.UNSIGNED_BYTE, null);
450 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup");
451 wtu.clearAndDrawUnitQuad(gl);
452 gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, buf);
453 // The image should be white.
454 checkPixelRange(buf, middle, center, [ 0, 0, 0, 255 ], 10);
457 successfullyParsed = true;
458 shouldBeTrue("successfullyParsed");
459 debug('<br /><span class="pass">TEST COMPLETE</span>');
460 notifyFinishedToHarness();