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(x, y, color) {
92 wtu.checkCanvasRect(gl, x, y, 1, 1, color);
95 function checkPixelRange(x, y, color, allowedRange) {
96 var msg = "pixel " + x + ", " + y + " should be within " +
97 allowedRange + " units of " +
102 wtu.checkCanvasRect(gl, x, y, 1, 1, color, msg, allowedRange);
105 var tex = gl.createTexture();
106 gl.bindTexture(gl.TEXTURE_2D, tex);
107 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
108 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
109 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
110 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
112 var buf = new Uint8Array(width * height * 4);
115 debug("check pixels are NOT pre-multiplied");
116 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE,
117 imgs['../resources/zero-alpha.png']);
118 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup");
119 wtu.clearAndDrawUnitQuad(gl);
122 var middle = Math.floor(width / 2);
123 var right = width - 1;
125 var center = Math.floor(height / 2);
126 var top = height - 1;
127 checkPixel(left, top, [ 0, 0, 0, 255]);
128 checkPixel(middle, top, [255, 0, 255, 255]);
129 checkPixel(right, top, [ 0, 0, 255, 255]);
130 checkPixel(left, center, [128, 128, 128, 255]);
131 checkPixel(middle, center, [255, 255, 255, 255]);
132 checkPixel(right, center, [ 0, 255, 255, 255]);
133 checkPixel(left, bottom, [255, 0, 0, 255]);
134 checkPixel(middle, bottom, [255, 255, 0, 255]);
135 checkPixel(right, bottom, [ 0, 255, 0, 255]);
138 debug("check quantization");
140 {format: gl.RGBA, type: gl.UNSIGNED_BYTE, counts: [256, 256, 256, 256]},
141 {format: gl.RGBA, type: gl.UNSIGNED_SHORT_4_4_4_4, counts: [ 16, 16, 16, 16]},
142 {format: gl.RGB, type: gl.UNSIGNED_SHORT_5_6_5, counts: [ 32, 64, 32, 1]},
143 {format: gl.RGBA, type: gl.UNSIGNED_SHORT_5_5_5_1, counts: [ 32, 32, 32, 2]}];
144 for (var qq = 0; qq < quantInfo.length; ++qq) {
145 var info = quantInfo[qq];
147 gl.TEXTURE_2D, 0, info.format, info.format, info.type,
148 imgs['../resources/gray-ramp-256.png']);
149 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup.");
150 wtu.clearAndDrawUnitQuad(gl);
151 gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, buf);
152 var counts = [{ }, { }, { }, { }];
153 var numUniqueValues = [0, 0, 0, 0];
154 // Count the number of unique values in each channel.
155 for (var ii = 0; ii < width * height * 4; ii += 4) {
156 for (var jj = 0; jj < 4; ++jj) {
157 var v = buf[ii + jj];
158 if (!counts[jj][v]) {
160 ++numUniqueValues[jj];
166 for (var ii = 0; ii < 4; ++ii) {
167 assertMsg(numUniqueValues[ii] == info.counts[ii],
168 "There should be " + info.counts[ii] +
169 " unique values in channel " + ii + ". Found " +
170 numUniqueValues[ii]);
175 debug("Check that gamma settings don't effect 8bit pngs");
176 gl.pixelStorei(gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.NONE);
177 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE,
178 imgs['../resources/gray-ramp-default-gamma.png']);
179 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup.");
180 wtu.clearAndDrawUnitQuad(gl);
181 var ref = new Uint8Array(width * height * 4);
182 gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, ref);
185 '../resources/gray-ramp-gamma0.1.png',
186 '../resources/gray-ramp-gamma1.0.png',
187 '../resources/gray-ramp-gamma2.0.png',
188 '../resources/gray-ramp-gamma4.0.png',
189 '../resources/gray-ramp-gamma9.0.png'];
190 for (var ii = 0; ii < gammaImages.length; ++ii) {
191 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE,
192 imgs[gammaImages[ii]]);
193 wtu.clearAndDrawUnitQuad(gl);
194 gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, buf);
196 for (var jj = 0; jj < width * height * 4; ++jj) {
197 if (buf[jj] != ref[jj]) {
202 assertMsg(same, "pixels should be same regardless of gamma settings.");
206 debug("check pixels are UN pre-multiplied");
207 for (var ii = 0; ii < 2; ++ii) {
208 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
210 var canvas2d = document.createElement("canvas");
211 canvas2d.width = 256;
213 var ctx = canvas2d.getContext("2d");
214 ctx.drawImage(imgs['../resources/gray-ramp-256-with-128-alpha.png'], 0, 0);
215 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, canvas2d);
217 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE,
218 imgs['../resources/gray-ramp-256-with-128-alpha.png']);
220 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup.");
221 wtu.clearAndDrawUnitQuad(gl);
222 var buf = new Uint8Array(width * height * 4);
223 gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, buf);
224 var lt128Count = [0, 0, 0];
225 var ge128Count = [0, 0, 0];
226 for (var jj = 0; jj < width; ++jj) {
228 for (var cc = 0; cc < 3; ++cc) {
229 if (buf[off + cc] < 128) {
236 // Not sure the exact count here because gamma does effect drawing into the
237 // canvas but it should be close to 50% so I'll pass 45%
238 for (var jj = 0; jj < 3; ++jj) {
239 assertMsg(ge128Count[jj] > 256 * 0.45,
240 "Half the pixels in channel " + jj +
241 " should be >= 128,128,128. found " +
242 ((ge128Count[jj] / 256) * 100).toFixed() + "%");
243 assertMsg(lt128Count[jj] > 256 * 0.45,
244 "Half the pixels in channel " + jj +
245 " should be < 128,128,128. found " +
246 ((lt128Count[jj] / 256) * 100).toFixed() + "%");
251 debug("check canvas pixels are UN pre-multiplied");
252 var canvas2d = document.createElement("canvas");
255 var ctx = canvas2d.getContext("2d");
256 ctx.fillStyle ="rgba(255,255,255,0.5)";
257 ctx.fillRect(0, 0, 256, 1);
258 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, canvas2d);
259 wtu.clearAndDrawUnitQuad(gl);
260 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup.");
261 checkPixelRange(0, 0, [255, 255, 255, 127], 4);
264 debug("check canvas pixels are pre-multiplied");
265 gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);
266 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, canvas2d);
267 wtu.clearAndDrawUnitQuad(gl);
268 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup.");
269 checkPixelRange(0, 0, [127, 127, 127, 127], 4);
273 debug("check pixels are pre-multiplied");
274 gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);
275 // TODO(gman): use different texture that won't pass on failure
276 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE,
277 imgs['../resources/zero-alpha.png']);
278 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup");
279 wtu.clearAndDrawUnitQuad(gl);
280 gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, buf);
283 for (var jj = 0; jj < width * height * 4; ++jj) {
289 assertMsg(same, "pixels should all be 0.");
292 debug("check pixels are flipped");
293 gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
294 gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
295 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE,
296 imgs['../resources/3x3.png']);
297 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup");
298 wtu.clearAndDrawUnitQuad(gl);
300 checkPixel(left, top, [255, 0, 0, 255]);
301 checkPixel(middle, top, [255, 255, 0, 255]);
302 checkPixel(right, top, [255, 0, 0, 255]);
303 checkPixel(left, center, [255, 0, 255, 255]);
304 checkPixel(middle, center, [255, 0, 0, 255]);
305 checkPixel(right, center, [ 0, 255, 0, 255]);
306 checkPixel(left, bottom, [ 0, 0, 0, 255]);
307 checkPixel(middle, bottom, [ 0, 0, 255, 255]);
308 checkPixel(right, bottom, [255, 0, 0, 255]);
311 debug("check uploading of images with no alpha channel works");
312 gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
313 gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);
314 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE,
315 imgs['../resources/blue-1x1.jpg']);
316 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup");
317 wtu.clearAndDrawUnitQuad(gl);
318 checkPixelRange(middle, center, [ 0, 0, 255, 255], 10);
319 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors");
322 debug("check uploading of 16-bit images");
323 gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
324 gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);
325 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE,
326 imgs['../resources/green-2x2-16bit.png']);
327 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup");
328 wtu.clearAndDrawUnitQuad(gl);
329 checkPixelRange(middle, center, [ 15, 121, 0, 255], 10);
330 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors");
333 debug("check uploading of images with ICC profiles");
334 gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
335 gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);
336 gl.pixelStorei(gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.NONE);
338 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE,
339 imgs['../resources/small-square-with-colorspin-profile.jpg']);
340 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup");
341 wtu.clearAndDrawUnitQuad(gl);
342 // The image is red. However, if we ignore the color profile, it is blue.
343 checkPixelRange(middle, center, [ 0, 0, 255, 255], 10);
345 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE,
346 imgs['../resources/small-square-with-colorspin-profile.png']);
347 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup");
348 wtu.clearAndDrawUnitQuad(gl);
349 // The image is red. However, if we ignore the color profile, it is blue.
350 checkPixelRange(middle, center, [ 0, 0, 255, 255], 10);
353 '../resources/small-square-with-cie-rgb-profile.png',
354 '../resources/small-square-with-colormatch-profile.png',
355 '../resources/small-square-with-e-srgb-profile.png',
356 '../resources/small-square-with-smpte-c-profile.png',
357 '../resources/small-square-with-srgb-iec61966-2.1-profile.png'];
358 gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, buf);
359 for (var ii = 0; ii < iccPNGs.length; ++ii) {
360 var buf2 = new Uint8Array(width * height * 4);
361 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE,
363 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup");
364 wtu.clearAndDrawUnitQuad(gl);
365 gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, buf2);
366 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors");
368 for (var jj = 0; jj < buf.length; ++jj) {
369 if (buf[jj] != buf2[jj]) {
374 assertMsg(same, "uploading PNGs with same data but various ICC profiles should generate the same results");
378 debug("check uploading of indexed PNG images");
379 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE,
380 imgs['../resources/red-indexed.png']);
381 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup");
382 wtu.clearAndDrawUnitQuad(gl);
383 // The image should be red.
384 checkPixelRange(middle, center, [ 255, 0, 0, 255 ], 10);
386 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup");
387 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE,
388 imgs['../resources/transparent-on-left-indexed.png']);
389 wtu.clearAndDrawUnitQuad(gl);
390 wtu.checkCanvasRect(gl, 0, 0, 128, 16, [255, 0, 255, 0], "should be transparent purple");
391 wtu.checkCanvasRect(gl, 128, 0,128, 16, [255, 255, 0, 255], "should be yellow");
394 debug("check uploading of 1-channel JPG images");
395 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE,
396 imgs['../resources/1-channel.jpg']);
397 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup");
398 wtu.clearAndDrawUnitQuad(gl);
399 // The image should be gray.
400 checkPixelRange(middle, center, [ 128, 128, 128, 255 ], 28);
403 debug("check calling texImage2D with NULL clears the texture");
404 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB,
405 imgs['../resources/red-indexed.png'].width,
406 imgs['../resources/red-indexed.png'].height,
407 0, gl.RGB, gl.UNSIGNED_BYTE, null);
408 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup");
409 wtu.clearAndDrawUnitQuad(gl);
410 // The image should be white.
411 checkPixelRange(middle, center, [ 0, 0, 0, 255 ], 10);
414 successfullyParsed = true;
415 shouldBeTrue("successfullyParsed");
416 debug('<br /><span class="pass">TEST COMPLETE</span>');
417 notifyFinishedToHarness();