3 <script src="../../js/resources/js-test-pre.js"></script>
4 <script src="resources/pnglib.js"></script>
5 <script src="resources/webgl-test.js"></script>
6 <script src="resources/webgl-test-utils.js"></script>
9 var wtu = WebGLTestUtils;
11 var textureLoc = null;
13 //----------------------------------------------------------------------
26 // This must remain the last mode.
32 if (window.initNonKhronosFramework) {
33 window.initNonKhronosFramework(true);
36 description('Verify texImage2D and texSubImage2D code paths taking both HTML and user-specified data with all format/type combinations');
38 var canvas = document.getElementById("example");
39 gl = wtu.create3DContext(canvas);
40 var program = wtu.setupTexturedQuad(gl);
42 gl.clearColor(0,0,0,1);
46 textureLoc = gl.getUniformLocation(program, "tex");
51 function initializeTests()
53 // Verify that uploading to packed pixel formats performs the
54 // required conversion and associated loss of precision.
55 for (var dataMode = 0; dataMode < DataMode.NUM_HTML_MODES; ++dataMode) {
56 for (var useTexSubImage2D = 0; useTexSubImage2D < 2; ++useTexSubImage2D) {
59 useTexSubImage2D: !!useTexSubImage2D,
62 generator: generateOpaqueGrayscaleRamp,
63 premultiplyAlpha: false,
65 type: gl.UNSIGNED_BYTE,
66 verifier: allChannelsIncreaseByNoMoreThan,
69 description: "RGBA/UNSIGNED_BYTE should maintain full precision of data"
73 useTexSubImage2D: !!useTexSubImage2D,
76 generator: generateOpaqueGrayscaleRamp,
77 premultiplyAlpha: false,
79 type: gl.UNSIGNED_SHORT_4_4_4_4,
80 verifier: allChannelsIncreaseByAtLeast,
83 description: "RGBA/UNSIGNED_SHORT_4_4_4_4 must drop low four bits of precision"
87 useTexSubImage2D: !!useTexSubImage2D,
90 generator: generateOpaqueGrayscaleRamp,
91 premultiplyAlpha: false,
93 type: gl.UNSIGNED_SHORT_5_5_5_1,
94 verifier: allChannelsIncreaseByAtLeast,
97 description: "RGBA/UNSIGNED_SHORT_5_5_5_1 must drop low three bits of precision"
101 useTexSubImage2D: !!useTexSubImage2D,
104 generator: generateOpaqueGrayscaleRamp,
105 premultiplyAlpha: false,
107 type: gl.UNSIGNED_BYTE,
108 verifier: allChannelsIncreaseByNoMoreThan,
111 description: "RGB/UNSIGNED_BYTE should maintain full precision of data"
115 useTexSubImage2D: !!useTexSubImage2D,
118 generator: generateOpaqueGrayscaleRamp,
119 premultiplyAlpha: false,
121 type: gl.UNSIGNED_SHORT_5_6_5,
122 verifier: allChannelsIncreaseByAtLeast,
125 description: "RGB/UNSIGNED_SHORT_5_6_5 must drop low two or three bits of precision"
129 useTexSubImage2D: !!useTexSubImage2D,
132 generator: generateTranslucentGrayscaleRamp,
133 premultiplyAlpha: false,
135 type: gl.UNSIGNED_BYTE,
136 verifier: alphaChannelIncreasesByNoMoreThan,
139 description: "ALPHA/UNSIGNED_BYTE should maintain full precision of data"
143 useTexSubImage2D: !!useTexSubImage2D,
146 generator: generateOpaqueGrayscaleRamp,
147 premultiplyAlpha: false,
148 format: gl.LUMINANCE,
149 type: gl.UNSIGNED_BYTE,
150 verifier: allChannelsIncreaseByNoMoreThan,
153 description: "LUMINANCE/UNSIGNED_BYTE should maintain full precision of data"
157 useTexSubImage2D: !!useTexSubImage2D,
160 generator: generateOpaqueGrayscaleRamp,
161 premultiplyAlpha: false,
162 format: gl.LUMINANCE_ALPHA,
163 type: gl.UNSIGNED_BYTE,
164 verifier: allChannelsIncreaseByNoMoreThan,
167 description: "LUMINANCE_ALPHA/UNSIGNED_BYTE should maintain full precision of data"
172 // Verify that setting the UNPACK_PREMULTIPLY_ALPHA_WEBGL pixel
173 // store parameter and sending down a zero alpha causes the color
174 // channels to go to zero.
175 for (var dataMode = 0; dataMode < DataMode.NUM_MODES; ++dataMode) {
176 for (var useTexSubImage2D = 0; useTexSubImage2D < 2; ++useTexSubImage2D) {
179 useTexSubImage2D: !!useTexSubImage2D,
182 generator: generateTransparentGrayscaleRamp,
183 premultiplyAlpha: true,
185 type: gl.UNSIGNED_BYTE,
186 verifier: colorChannelsAreZero,
187 description: "UNPACK_PREMULTIPLY_ALPHA_WEBGL with RGBA/UNSIGNED_BYTE"
191 useTexSubImage2D: !!useTexSubImage2D,
194 generator: generateTransparentGrayscaleRamp,
195 premultiplyAlpha: true,
197 type: gl.UNSIGNED_SHORT_4_4_4_4,
198 verifier: colorChannelsAreZero,
199 description: "UNPACK_PREMULTIPLY_ALPHA_WEBGL with RGBA/UNSIGNED_SHORT_4_4_4_4"
203 useTexSubImage2D: !!useTexSubImage2D,
206 generator: generateTransparentGrayscaleRamp,
207 premultiplyAlpha: true,
209 type: gl.UNSIGNED_SHORT_5_5_5_1,
210 verifier: colorChannelsAreZero,
211 description: "UNPACK_PREMULTIPLY_ALPHA_WEBGL with RGBA/UNSIGNED_SHORT_5_5_5_1"
213 // The following few tests are invalid for the raw data
214 // mode because there is either no alpha channel or no
215 // separate alpha channel.
216 if (dataMode != DataMode.RAW_DATA) {
219 useTexSubImage2D: !!useTexSubImage2D,
222 generator: generateTransparentGrayscaleRamp,
223 premultiplyAlpha: true,
225 type: gl.UNSIGNED_BYTE,
226 verifier: colorChannelsAreZero,
227 description: "UNPACK_PREMULTIPLY_ALPHA_WEBGL with RGB/UNSIGNED_BYTE"
231 useTexSubImage2D: !!useTexSubImage2D,
234 generator: generateTransparentGrayscaleRamp,
235 premultiplyAlpha: true,
237 type: gl.UNSIGNED_SHORT_5_6_5,
238 verifier: colorChannelsAreZero,
239 description: "UNPACK_PREMULTIPLY_ALPHA_WEBGL with RGB/UNSIGNED_SHORT_5_6_5"
243 useTexSubImage2D: !!useTexSubImage2D,
246 generator: generateTransparentGrayscaleRamp,
247 premultiplyAlpha: true,
249 type: gl.UNSIGNED_BYTE,
250 verifier: colorChannelsAreZero,
251 description: "UNPACK_PREMULTIPLY_ALPHA_WEBGL with ALPHA/UNSIGNED_BYTE"
255 useTexSubImage2D: !!useTexSubImage2D,
258 generator: generateTransparentGrayscaleRamp,
259 premultiplyAlpha: true,
260 format: gl.LUMINANCE,
261 type: gl.UNSIGNED_BYTE,
262 verifier: colorChannelsAreZero,
263 description: "UNPACK_PREMULTIPLY_ALPHA_WEBGL with LUMINANCE/UNSIGNED_BYTE"
268 useTexSubImage2D: !!useTexSubImage2D,
271 generator: generateTransparentGrayscaleRamp,
272 premultiplyAlpha: true,
273 format: gl.LUMINANCE_ALPHA,
274 type: gl.UNSIGNED_BYTE,
275 verifier: colorChannelsAreZero,
276 description: "UNPACK_PREMULTIPLY_ALPHA_WEBGL with LUMINANCE_ALPHA/UNSIGNED_BYTE"
281 // Produce data for all testcases. Because we load images, some of
282 // these may generate their data asynchronously.
286 function generateTestData()
288 for (var i = 0; i < testCases.length; i++) {
289 var testCase = testCases[i];
291 switch (testCase.dataMode) {
293 wrapper = new ImageWrapper(testCase.width, testCase.height);
295 case DataMode.IMAGE_DATA:
296 wrapper = new ImageDataWrapper(testCase.width, testCase.height);
298 case DataMode.RAW_DATA:
299 switch (testCase.type) {
300 case gl.UNSIGNED_BYTE:
301 switch (testCase.format) {
303 wrapper = new RGBA8DataWrapper(testCase.width, testCase.height);
306 wrapper = new RGB8DataWrapper(testCase.width, testCase.height);
309 wrapper = new A8DataWrapper(testCase.width, testCase.height);
312 wrapper = new L8DataWrapper(testCase.width, testCase.height);
314 case gl.LUMINANCE_ALPHA:
315 wrapper = new LA8DataWrapper(testCase.width, testCase.height);
319 case gl.UNSIGNED_SHORT_4_4_4_4:
320 wrapper = new RGBA4444DataWrapper(testCase.width, testCase.height);
322 case gl.UNSIGNED_SHORT_5_5_5_1:
323 wrapper = new RGBA5551DataWrapper(testCase.width, testCase.height);
325 case gl.UNSIGNED_SHORT_5_6_5:
326 wrapper = new RGB565DataWrapper(testCase.width, testCase.height);
330 testCase.wrapper = wrapper;
331 testCase.generator(wrapper);
332 testCase.wrapper.generateData();
335 // See whether we need to run the tests, in case all of them
336 // generated their results synchronously.
340 var ranTests = false;
342 function maybeRunTests()
345 for (var i = 0; i < testCases.length; ++i)
346 if (!testCases[i].wrapper || !testCases[i].wrapper.data)
351 for (var i = 0; i < testCases.length; ++i)
352 runOneTest(testCases[i]);
353 var epilogue = document.createElement("script");
354 epilogue.onload = finish;
355 epilogue.src = "../../js/resources/js-test-post.js";
356 document.body.appendChild(epilogue);
359 function testCaseToString(testCase)
362 switch (testCase.dataMode) {
366 case DataMode.IMAGE_DATA:
369 case DataMode.RAW_DATA:
373 return (testCase.useTexSubImage2D ? "texSubImage2D" : "texImage2D") +
374 " with " + mode + " at " + testCase.width + "x" + testCase.height;
377 function runOneTest(testCase)
379 debug("Testing " + testCaseToString(testCase));
380 var data = testCase.wrapper.data;
381 gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
382 var texture = gl.createTexture();
383 // Bind the texture to texture unit 0.
384 gl.bindTexture(gl.TEXTURE_2D, texture);
385 // Set up texture parameters.
386 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
387 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
388 // Set up pixel store parameters.
389 gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, testCase.premultiplyAlpha);
390 // Upload the image into the texture.
391 if (testCase.useTexSubImage2D) {
392 // Initialize the texture to black first.
393 gl.texImage2D(gl.TEXTURE_2D, 0, testCase.format, testCase.width, testCase.height, 0,
394 testCase.format, testCase.type, null);
396 switch (testCase.dataMode) {
398 case DataMode.IMAGE_DATA:
399 if (testCase.useTexSubImage2D)
400 gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, testCase.format, testCase.type, data);
402 gl.texImage2D(gl.TEXTURE_2D, 0, testCase.format, testCase.format, testCase.type, data);
404 case DataMode.RAW_DATA:
405 if (testCase.useTexSubImage2D)
406 gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, testCase.width, testCase.height, testCase.format, testCase.type, data);
408 gl.texImage2D(gl.TEXTURE_2D, 0, testCase.format, testCase.width, testCase.height, 0, testCase.format, testCase.type, data);
411 // Point the uniform sampler to texture unit 0.
412 gl.uniform1i(textureLoc, 0);
413 // Draw the triangles.
414 gl.drawArrays(gl.TRIANGLES, 0, 6);
415 // Clean up the texture.
416 gl.deleteTexture(texture);
418 // Read back the rendering results.
419 buf = new Uint8Array(testCase.width * testCase.height * 4);
420 gl.readPixels(0, 0, testCase.width, testCase.height, gl.RGBA, gl.UNSIGNED_BYTE, buf);
421 // Run the verification routine.
422 if (testCase.verifier(buf, testCase.threshold, testCase.numOccurrences))
423 testPassed(testCase.description);
425 testFailed(testCase.description);
429 if (window.nonKhronosFrameworkNotifyDone) {
430 window.nonKhronosFrameworkNotifyDone();
434 //----------------------------------------------------------------------
435 // Wrappers for programmatic construction of Image, ImageData and raw texture data
438 function ImageWrapper(width, height)
440 this.pngBuilder_ = new PNGlib(width, height, 256);
443 ImageWrapper.prototype.getWidth = function() {
444 return this.pngBuilder_.width;
447 ImageWrapper.prototype.getHeight = function() {
448 return this.pngBuilder_.height;
451 ImageWrapper.prototype.setPixel = function(x, y, r, g, b, a) {
452 this.pngBuilder_.buffer[this.pngBuilder_.index(x, y)] = this.pngBuilder_.color(r, g, b, a);
455 // Generates data into "data" property, possibly asynchronously.
456 ImageWrapper.prototype.generateData = function() {
458 var img = new Image();
459 img.onload = function() {
463 img.src = "data:image/png;base64," + this.pngBuilder_.getBase64();
466 function ImageDataWrapper(width, height)
468 if (!ImageDataWrapper.tempCanvas) {
469 ImageDataWrapper.tempCanvas = document.createElement("canvas");
471 this.imageData_ = ImageDataWrapper.tempCanvas.getContext("2d").createImageData(width, height);
474 ImageDataWrapper.tempCanvas = null;
476 ImageDataWrapper.prototype.getWidth = function() {
477 return this.imageData_.width;
480 ImageDataWrapper.prototype.getHeight = function() {
481 return this.imageData_.height;
484 ImageDataWrapper.prototype.setPixel = function(x, y, r, g, b, a) {
485 var index = 4 * (this.imageData_.width * y + x);
486 this.imageData_.data[index] = r;
487 this.imageData_.data[index + 1] = g;
488 this.imageData_.data[index + 2] = b;
489 this.imageData_.data[index + 3] = a;
492 ImageDataWrapper.prototype.generateData = function() {
493 this.data = this.imageData_;
497 function TextureDataWrapper(width, height)
500 this.height_ = height;
503 TextureDataWrapper.prototype.getWidth = function() {
507 TextureDataWrapper.prototype.getHeight = function() {
511 TextureDataWrapper.prototype.generateData = function() {
512 this.data = this.data_;
516 function RGBA8DataWrapper(width, height)
518 TextureDataWrapper.call(this, width, height);
519 this.data_ = new Uint8Array(4 * width * height);
522 RGBA8DataWrapper.prototype = new TextureDataWrapper;
524 RGBA8DataWrapper.prototype.setPixel = function(x, y, r, g, b, a) {
525 var index = 4 * (this.width_ * y + x);
526 this.data_[index] = r;
527 this.data_[index + 1] = g;
528 this.data_[index + 2] = b;
529 this.data_[index + 3] = a;
532 function RGBA5551DataWrapper(width, height)
534 TextureDataWrapper.call(this, width, height);
535 this.data_ = new Uint16Array(width * height);
538 RGBA5551DataWrapper.prototype = new TextureDataWrapper;
540 RGBA5551DataWrapper.prototype.setPixel = function(x, y, r, g, b, a) {
541 var value = (((r & 0xF8) << 8)
545 this.data_[this.width_ * y + x] = value;
548 function RGBA4444DataWrapper(width, height)
550 TextureDataWrapper.call(this, width, height);
551 this.data_ = new Uint16Array(width * height);
554 RGBA4444DataWrapper.prototype = new TextureDataWrapper;
556 RGBA4444DataWrapper.prototype.setPixel = function(x, y, r, g, b, a) {
557 var value = (((r & 0xF0) << 8)
561 this.data_[this.width_ * y + x] = value;
564 function RGB8DataWrapper(width, height)
566 TextureDataWrapper.call(this, width, height);
567 this.data_ = new Uint8Array(3 * width * height);
570 RGB8DataWrapper.prototype = new TextureDataWrapper;
572 RGB8DataWrapper.prototype.setPixel = function(x, y, r, g, b, a) {
573 var index = 3 * (this.width_ * y + x);
574 this.data_[index] = r;
575 this.data_[index + 1] = g;
576 this.data_[index + 2] = b;
579 function RGB565DataWrapper(width, height)
581 TextureDataWrapper.call(this, width, height);
582 this.data_ = new Uint16Array(width * height);
585 RGB565DataWrapper.prototype = new TextureDataWrapper;
587 RGB565DataWrapper.prototype.setPixel = function(x, y, r, g, b, a) {
588 var value = (((r & 0xF8) << 8)
590 | ((b & 0xF8) >> 3));
591 this.data_[this.width_ * y + x] = value;
594 function A8DataWrapper(width, height)
596 TextureDataWrapper.call(this, width, height);
597 this.data_ = new Uint8Array(width * height);
600 A8DataWrapper.prototype = new TextureDataWrapper;
602 A8DataWrapper.prototype.setPixel = function(x, y, r, g, b, a) {
603 this.data_[this.width_ * y + x] = a;
606 function L8DataWrapper(width, height)
608 TextureDataWrapper.call(this, width, height);
609 this.data_ = new Uint8Array(width * height);
612 L8DataWrapper.prototype = new TextureDataWrapper;
614 L8DataWrapper.prototype.setPixel = function(x, y, r, g, b, a) {
615 this.data_[this.width_ * y + x] = r;
618 function LA8DataWrapper(width, height)
620 TextureDataWrapper.call(this, width, height);
621 this.data_ = new Uint8Array(2 * width * height);
624 LA8DataWrapper.prototype = new TextureDataWrapper;
626 LA8DataWrapper.prototype.setPixel = function(x, y, r, g, b, a) {
627 var index = 2 * (this.width_ * y + x);
628 this.data_[index] = r;
629 this.data_[index + 1] = a;
632 //----------------------------------------------------------------------
633 // Color ramp generation functions
636 function generateOpaqueGrayscaleRamp(wrapper)
638 var width = wrapper.getWidth();
639 var height = wrapper.getHeight();
640 for (var x = 0; x < width; ++x) {
641 var value = Math.round(255.0 * x / width);
642 for (var y = 0; y < height; ++y)
643 wrapper.setPixel(x, y, value, value, value, 255);
647 function generateTranslucentGrayscaleRamp(wrapper)
649 var width = wrapper.getWidth();
650 var height = wrapper.getHeight();
651 for (var x = 0; x < width; ++x) {
652 var value = Math.round(255.0 * x / width);
653 for (var y = 0; y < height; ++y)
654 wrapper.setPixel(x, y, value, value, value, value);
658 function generateTransparentGrayscaleRamp(wrapper)
660 var width = wrapper.getWidth();
661 var height = wrapper.getHeight();
662 for (var x = 0; x < width; ++x) {
663 var value = Math.round(255.0 * x / width);
664 for (var y = 0; y < height; ++y)
665 wrapper.setPixel(x, y, value, value, value, 0);
669 //----------------------------------------------------------------------
670 // Verification routines
673 function allChannelsIncreaseByNoMoreThan(array, threshold, numOccurrences) {
675 for (var i = 4; i < array.length; i += 4)
676 for (var j = 0; j < 4; j++)
677 if (array[i + j] - array[i + j - 4] > threshold)
680 return numFound < numOccurrences;
683 function alphaChannelIncreasesByNoMoreThan(array, threshold, numOccurrences) {
685 for (var i = 7; i < array.length; i += 4)
686 if (array[i] - array[i - 4] > threshold)
689 return numFound < numOccurrences;
692 function allChannelsIncreaseByAtLeast(array, threshold, numOccurrences) {
694 for (var i = 4; i < array.length; i += 4)
695 for (var j = 0; j < 4; ++j)
696 if (array[i + j] - array[i + j - 4] > threshold)
699 return numFound > numOccurrences;
702 function colorChannelsAreZero(array, threshold, numOccurrences) {
706 for (var i = 4; i < array.length; i += 4)
707 for (var j = 0; j < 3; ++j)
708 if (array[i + j] != 0) {
710 if (++numFailures <= 5)
711 debug(" array[" + (i + j) + "] should have been 0, was " + array[i + j]);
719 <body onload="init()">
720 <canvas id="example" width="256px" height="1px"></canvas>
721 <div id="description"></div>
722 <div id="console"></div>