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.
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>
17 <canvas id=
"example" width=
"256" height=
"16" style=
"width: 256px; height: 48px;"></canvas>
18 <div id=
"description"></div>
19 <div id=
"console"></div>
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
;
30 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "Should be no errors from setup.");
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'];
58 wtu
.loadImagesAsync(imgURLs
, runTests
);
60 function runTests(imgs
) {
61 var loc
= gl
.getUniformLocation(program
, "tex");
65 gl
.disable(gl
.DEPTH_TEST
);
67 var width
= gl
.canvas
.width
;
68 var height
= gl
.canvas
.height
;
70 function checkPixel(x
, y
, color
) {
71 wtu
.checkCanvasRect(gl
, x
, y
, 1, 1, color
);
74 function checkPixelRange(x
, y
, color
, allowedRange
) {
75 var msg
= "pixel " + x
+ ", " + y
+ " should be within " +
76 allowedRange
+ " units of " +
81 wtu
.checkCanvasRect(gl
, x
, y
, 1, 1, color
, msg
, allowedRange
);
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
);
91 var buf
= new Uint8Array(width
* height
* 4);
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
);
101 var middle
= Math
.floor(width
/ 2);
102 var right
= width
- 1;
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]);
117 debug("check quantization");
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
];
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
]) {
139 ++numUniqueValues
[jj
];
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
]);
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
);
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
);
175 for (var jj
= 0; jj
< width
* height
* 4; ++jj
) {
176 if (buf
[jj
] != ref
[jj
]) {
181 assertMsg(same
, "pixels should be same regardless of gamma settings.");
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);
189 var canvas2d
= document
.createElement("canvas");
190 canvas2d
.width
= 256;
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
);
196 gl
.texImage2D(gl
.TEXTURE_2D
, 0, gl
.RGB
, gl
.RGB
, gl
.UNSIGNED_BYTE
,
197 imgs
['../../../resources/gray-ramp-256-with-128-alpha.png']);
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
) {
207 for (var cc
= 0; cc
< 3; ++cc
) {
208 if (buf
[off
+ cc
] < 128) {
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() + "%");
230 debug("check canvas pixels are UN pre-multiplied");
231 var canvas2d
= document
.createElement("canvas");
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);
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);
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
);
262 for (var jj
= 0; jj
< width
* height
* 4; ++jj
) {
268 assertMsg(same
, "pixels should all be 0.");
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
);
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]);
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");
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");
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);');
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);
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);
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
,
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");
347 for (var jj
= 0; jj
< buf
.length
; ++jj
) {
348 if (buf
[jj
] != buf2
[jj
]) {
353 assertMsg(same
, "uploading PNGs with same data but various ICC profiles should generate the same results");
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);
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");
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);
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);
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");
398 successfullyParsed
= true;
399 shouldBeTrue("successfullyParsed");
400 debug('<br /><span class="pass">TEST COMPLETE</span>');
401 notifyFinishedToHarness();