1 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
2 "http://www.w3.org/TR/html4/loose.dtd">
5 <title>WebGL texImage2D conformance test.</title>
6 <script src="../../js/resources/js-test-pre.js"></script>
7 <script src="resources/webgl-test.js"> </script>
8 <script src="resources/webgl-test-utils.js"> </script>
11 <canvas id="example" width="256" height="16" style="width: 256px; height: 48px;"></canvas>
12 <div id="description"></div>
13 <div id="console"></div>
15 description("Test texImage2D conversions.");
16 var wtu = WebGLTestUtils;
17 var canvas = document.getElementById("example");
18 var gl = wtu.create3DContext(canvas);
19 var program = wtu.setupTexturedQuad(gl);
21 glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup.");
24 'resources/gray-ramp-256-with-128-alpha.png',
25 'resources/gray-ramp-256.png',
26 'resources/gray-ramp-default-gamma.png',
27 'resources/gray-ramp-gamma0.1.png',
28 'resources/gray-ramp-gamma1.0.png',
29 'resources/gray-ramp-gamma2.0.png',
30 'resources/gray-ramp-gamma4.0.png',
31 'resources/gray-ramp-gamma9.0.png',
32 'resources/gray-ramp.png',
33 'resources/zero-alpha.png',
35 'resources/blue-1x1.jpg',
36 'resources/red-indexed.png',
37 'resources/green-2x2-16bit.png',
38 'resources/small-square-with-colorspin-profile.jpg',
39 'resources/small-square-with-colorspin-profile.png',
40 'resources/small-square-with-cie-rgb-profile.png',
41 'resources/small-square-with-colormatch-profile.png',
42 'resources/small-square-with-e-srgb-profile.png',
43 'resources/small-square-with-smpte-c-profile.png',
44 'resources/small-square-with-srgb-iec61966-2.1-profile.png'];
47 wtu.loadImagesAsync(imgURLs, runTests);
49 function runTests(imgs) {
50 var loc = gl.getUniformLocation(program, "tex");
54 gl.disable(gl.DEPTH_TEST);
56 var width = canvas.width;
57 var height = canvas.height;
59 function checkPixel(buf, x, y, color) {
60 var off = (y * width + x) * 4;
61 var msg = "pixel " + x + ", " + y + " should be " +
71 for (var ii = 0; ii < 4; ++ii) {
72 if (buf[off + ii] != color[ii]) {
80 function checkPixelRange(buf, x, y, color, allowedRange) {
81 var off = (y * width + x) * 4;
82 var msg = "pixel " + x + ", " + y + " should be within " +
83 allowedRange + " units of " +
88 var subMsg = " was " +
93 // When running in WebKit's test harness, we don't want to print the
94 // pixel value when the test passes, because different machines might
95 // have different results and we record the text output.
96 var inDumpRenderTree = window.layoutTestController;
97 for (var ii = 0; ii < 4; ++ii) {
98 if (Math.abs(buf[off + ii] - color[ii]) > allowedRange) {
99 testFailed(msg + subMsg);
103 testPassed(msg + (inDumpRenderTree ? "" : subMsg));
106 var tex = gl.createTexture();
107 gl.bindTexture(gl.TEXTURE_2D, tex);
108 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
109 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
110 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
111 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
113 var buf = new Uint8Array(width * height * 4);
116 debug("check pixels are NOT pre-multiplied");
117 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE,
118 imgs['resources/zero-alpha.png']);
119 glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup");
121 gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, buf);
124 var middle = Math.floor(width / 2);
125 var right = width - 1;
127 var center = Math.floor(height / 2);
128 var top = height - 1;
129 checkPixel(buf, left, top, [ 0, 0, 0, 255]);
130 checkPixel(buf, middle, top, [255, 0, 255, 255]);
131 checkPixel(buf, right, top, [ 0, 0, 255, 255]);
132 checkPixel(buf, left, center, [128, 128, 128, 255]);
133 checkPixel(buf, middle, center, [255, 255, 255, 255]);
134 checkPixel(buf, right, center, [ 0, 255, 255, 255]);
135 checkPixel(buf, left, bottom, [255, 0, 0, 255]);
136 checkPixel(buf, middle, bottom, [255, 255, 0, 255]);
137 checkPixel(buf, right, bottom, [ 0, 255, 0, 255]);
140 debug("check quantization");
142 {format: gl.RGBA, type: gl.UNSIGNED_BYTE, counts: [256, 256, 256, 256]},
143 {format: gl.RGBA, type: gl.UNSIGNED_SHORT_4_4_4_4, counts: [ 16, 16, 16, 16]},
144 {format: gl.RGB, type: gl.UNSIGNED_SHORT_5_6_5, counts: [ 32, 64, 32, 1]},
145 {format: gl.RGBA, type: gl.UNSIGNED_SHORT_5_5_5_1, counts: [ 32, 32, 32, 2]}];
146 for (var qq = 0; qq < quantInfo.length; ++qq) {
147 var info = quantInfo[qq];
149 gl.TEXTURE_2D, 0, info.format, info.format, info.type,
150 imgs['resources/gray-ramp-256.png']);
151 glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup.");
153 gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, buf);
154 var counts = [{ }, { }, { }, { }];
155 var numUniqueValues = [0, 0, 0, 0];
156 // Count the number of unique values in each channel.
157 for (var ii = 0; ii < width * height * 4; ii += 4) {
158 for (var jj = 0; jj < 4; ++jj) {
159 var v = buf[ii + jj];
160 if (!counts[jj][v]) {
162 ++numUniqueValues[jj];
168 for (var ii = 0; ii < 4; ++ii) {
169 assertMsg(numUniqueValues[ii] == info.counts[ii],
170 "There should be " + info.counts[ii] +
171 " unique values in channel " + ii + ". Found " +
172 numUniqueValues[ii]);
177 debug("Check that gamma settings don't effect 8bit pngs");
178 gl.pixelStorei(gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.NONE);
179 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE,
180 imgs['resources/gray-ramp-default-gamma.png']);
181 glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup.");
183 var ref = new Uint8Array(width * height * 4);
184 gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, ref);
187 'resources/gray-ramp-gamma0.1.png',
188 'resources/gray-ramp-gamma1.0.png',
189 'resources/gray-ramp-gamma2.0.png',
190 'resources/gray-ramp-gamma4.0.png',
191 'resources/gray-ramp-gamma9.0.png'];
192 for (var ii = 0; ii < gammaImages.length; ++ii) {
193 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE,
194 imgs[gammaImages[ii]]);
196 gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, buf);
198 for (var jj = 0; jj < width * height * 4; ++jj) {
199 if (buf[jj] != ref[jj]) {
204 assertMsg(same, "pixels should be same regardless of gamma settings.");
208 debug("check pixels are UN pre-multiplied");
209 for (var ii = 0; ii < 2; ++ii) {
210 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
212 var canvas2d = document.createElement("canvas");
213 canvas2d.width = 256;
215 var ctx = canvas2d.getContext("2d");
216 ctx.drawImage(imgs['resources/gray-ramp-256-with-128-alpha.png'], 0, 0);
217 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, canvas2d);
219 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE,
220 imgs['resources/gray-ramp-256-with-128-alpha.png']);
222 glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup.");
224 var buf = new Uint8Array(width * height * 4);
225 gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, buf);
226 var lt128Count = [0, 0, 0];
227 var ge128Count = [0, 0, 0];
228 for (var jj = 0; jj < width; ++jj) {
230 for (var cc = 0; cc < 3; ++cc) {
231 if (buf[off + cc] < 128) {
238 // Not sure the exact count here because gamma does effect drawing into the
239 // canvas but it should be close to 50% so I'll pass 45%
240 for (var jj = 0; jj < 3; ++jj) {
241 assertMsg(ge128Count[jj] > 256 * 0.45,
242 "Half the pixels in channel " + jj +
243 " should be >= 128,128,128. found " +
244 ((ge128Count[jj] / 256) * 100).toFixed() + "%");
245 assertMsg(lt128Count[jj] > 256 * 0.45,
246 "Half the pixels in channel " + jj +
247 " should be < 128,128,128. found " +
248 ((lt128Count[jj] / 256) * 100).toFixed() + "%");
253 debug("check canvas pixels are UN pre-multiplied");
254 var canvas2d = document.createElement("canvas");
257 var ctx = canvas2d.getContext("2d");
258 ctx.fillStyle ="rgba(255,255,255,0.5)";
259 ctx.fillRect(0, 0, 256, 1);
260 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, canvas2d);
262 gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, buf);
263 glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup.");
264 checkPixelRange(buf, 0, 0, [255, 255, 255, 127], 4);
267 debug("check canvas pixels are pre-multiplied");
268 gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);
269 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, canvas2d);
271 gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, buf);
272 glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup.");
273 checkPixelRange(buf, 0, 0, [127, 127, 127, 127], 4);
277 debug("check pixels are pre-multiplied");
278 gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);
279 // TODO(gman): use different texture that won't pass on failure
280 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE,
281 imgs['resources/zero-alpha.png']);
282 glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup");
284 gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, buf);
287 for (var jj = 0; jj < width * height * 4; ++jj) {
293 assertMsg(same, "pixels should all be 0.");
296 debug("check pixels are flipped");
297 gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
298 gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
299 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE,
300 imgs['resources/3x3.png']);
301 glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup");
303 gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, buf);
305 checkPixel(buf, left, top, [255, 0, 0, 255]);
306 checkPixel(buf, middle, top, [255, 255, 0, 255]);
307 checkPixel(buf, right, top, [255, 0, 0, 255]);
308 checkPixel(buf, left, center, [255, 0, 255, 255]);
309 checkPixel(buf, middle, center, [255, 0, 0, 255]);
310 checkPixel(buf, right, center, [ 0, 255, 0, 255]);
311 checkPixel(buf, left, bottom, [ 0, 0, 0, 255]);
312 checkPixel(buf, middle, bottom, [ 0, 0, 255, 255]);
313 checkPixel(buf, right, bottom, [255, 0, 0, 255]);
316 debug("check uploading of images with no alpha channel works");
317 gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
318 gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);
319 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE,
320 imgs['resources/blue-1x1.jpg']);
321 glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup");
323 gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, buf);
324 checkPixelRange(buf, middle, center, [ 0, 0, 255, 255], 10);
325 glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors");
328 debug("check uploading of 16-bit images");
329 gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
330 gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);
331 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE,
332 imgs['resources/green-2x2-16bit.png']);
333 glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup");
335 gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, buf);
336 checkPixelRange(buf, middle, center, [ 15, 121, 0, 255], 10);
337 glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors");
340 debug("check uploading of images with ICC profiles");
341 gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
342 gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);
343 gl.pixelStorei(gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.NONE);
345 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE,
346 imgs['resources/small-square-with-colorspin-profile.jpg']);
347 glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup");
349 gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, buf);
350 // The image is red. However, if we ignore the color profile, it is blue.
351 checkPixelRange(buf, middle, center, [ 0, 0, 255, 255], 10);
353 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE,
354 imgs['resources/small-square-with-colorspin-profile.png']);
355 glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup");
357 gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, buf);
358 // The image is red. However, if we ignore the color profile, it is blue.
359 checkPixelRange(buf, middle, center, [ 0, 0, 255, 255], 10);
362 'resources/small-square-with-cie-rgb-profile.png',
363 'resources/small-square-with-colormatch-profile.png',
364 'resources/small-square-with-e-srgb-profile.png',
365 'resources/small-square-with-smpte-c-profile.png',
366 'resources/small-square-with-srgb-iec61966-2.1-profile.png'];
367 for (var ii = 0; ii < iccPNGs.length; ++ii) {
368 var buf2 = new Uint8Array(width * height * 4);
369 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE,
371 glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup");
373 gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, buf2);
374 glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors");
376 for (var jj = 0; jj < buf.length; ++jj) {
377 if (buf[jj] != buf2[jj]) {
382 assertMsg(same, "uploading PNGs with same data but various ICC profiles should generate the same results");
386 debug("check uploading of indexed PNG images");
387 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE,
388 imgs['resources/red-indexed.png']);
389 glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup");
391 gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, buf);
392 // The image should be red.
393 checkPixelRange(buf, middle, center, [ 255, 0, 0, 255 ], 10);
396 isSuccessfullyParsed();