content/canvas/test/webgl-conformance/conformance/resources/webgl-test-utils.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
     2 // Use of this source code is governed by a BSD-style license that can be
     3 // found in the LICENSE file.
     5 WebGLTestUtils = (function() {
     7 /**
     8  * Wrapped logging function.
     9  * @param {string} msg The message to log.
    10  */
    11 var log = function(msg) {
    12   if (window.console && window.console.log) {
    13     window.console.log(msg);
    14   }
    15 };
    17 /**
    18  * Wrapped logging function.
    19  * @param {string} msg The message to log.
    20  */
    21 var error = function(msg) {
    22   if (window.console) {
    23     if (window.console.error) {
    24       window.console.error(msg);
    25     }
    26     else if (window.console.log) {
    27       window.console.log(msg);
    28     }
    29   }
    30 };
    32 /**
    33  * Turn off all logging.
    34  */
    35 var loggingOff = function() {
    36   log = function() {};
    37   error = function() {};
    38 };
    40 /**
    41  * Converts a WebGL enum to a string
    42  * @param {!WebGLContext} gl The WebGLContext to use.
    43  * @param {number} value The enum value.
    44  * @return {string} The enum as a string.
    45  */
    46 var glEnumToString = function(gl, value) {
    47   for (var p in gl) {
    48     if (gl[p] == value) {
    49       return p;
    50     }
    51   }
    52   return "0x" + value.toString(16);
    53 };
    55 var lastError = "";
    57 /**
    58  * Returns the last compiler/linker error.
    59  * @return {string} The last compiler/linker error.
    60  */
    61 var getLastError = function() {
    62   return lastError;
    63 };
    65 /**
    66  * Whether a haystack ends with a needle.
    67  * @param {string} haystack String to search
    68  * @param {string} needle String to search for.
    69  * @param {boolean} True if haystack ends with needle.
    70  */
    71 var endsWith = function(haystack, needle) {
    72   return haystack.substr(haystack.length - needle.length) === needle;
    73 };
    75 /**
    76  * Whether a haystack starts with a needle.
    77  * @param {string} haystack String to search
    78  * @param {string} needle String to search for.
    79  * @param {boolean} True if haystack starts with needle.
    80  */
    81 var startsWith = function(haystack, needle) {
    82   return haystack.substr(0, needle.length) === needle;
    83 };
    85 /**
    86  * A vertex shader for a single texture.
    87  * @type {string}
    88  */
    89 var simpleTextureVertexShader = [
    90   'attribute vec4 vPosition;',
    91   'attribute vec2 texCoord0;',
    92   'varying vec2 texCoord;',
    93   'void main() {',
    94   '    gl_Position = vPosition;',
    95   '    texCoord = texCoord0;',
    96   '}'].join('\n');
    98 /**
    99  * A fragment shader for a single texture.
   100  * @type {string}
   101  */
   102 var simpleTextureFragmentShader = [
   103   'precision mediump float;',
   104   'uniform sampler2D tex;',
   105   'varying vec2 texCoord;',
   106   'void main() {',
   107   '    gl_FragData[0] = texture2D(tex, texCoord);',
   108   '}'].join('\n');
   110 /**
   111  * Creates a simple texture vertex shader.
   112  * @param {!WebGLContext} gl The WebGLContext to use.
   113  * @return {!WebGLShader}
   114  */
   115 var setupSimpleTextureVertexShader = function(gl) {
   116     return loadShader(gl, simpleTextureVertexShader, gl.VERTEX_SHADER);
   117 };
   119 /**
   120  * Creates a simple texture fragment shader.
   121  * @param {!WebGLContext} gl The WebGLContext to use.
   122  * @return {!WebGLShader}
   123  */
   124 var setupSimpleTextureFragmentShader = function(gl) {
   125     return loadShader(
   126         gl, simpleTextureFragmentShader, gl.FRAGMENT_SHADER);
   127 };
   129 /**
   130  * Creates a program, attaches shaders, binds attrib locations, links the
   131  * program and calls useProgram.
   132  * @param {!Array.<!WebGLShader|string>} shaders The shaders to
   133  *        attach, or the source, or the id of a script to get
   134  *        the source from.
   135  * @param {!Array.<string>} opt_attribs The attribs names.
   136  * @param {!Array.<number>} opt_locations The locations for the attribs.
   137  */
   138 var setupProgram = function(gl, shaders, opt_attribs, opt_locations) {
   139   var realShaders = [];
   140   var program = gl.createProgram();
   141   for (var ii = 0; ii < shaders.length; ++ii) {
   142     var shader = shaders[ii];
   143     if (typeof shader == 'string') {
   144       var element = document.getElementById(shader);
   145       if (element) {
   146         shader = loadShaderFromScript(gl, shader);
   147       } else {
   148         shader = loadShader(gl, shader, ii ? gl.FRAGMENT_SHADER : gl.VERTEX_SHADER);
   149       }
   150     }
   151     gl.attachShader(program, shader);
   152   }
   153   if (opt_attribs) {
   154     for (var ii = 0; ii < opt_attribs.length; ++ii) {
   155       gl.bindAttribLocation(
   156           program,
   157           opt_locations ? opt_locations[ii] : ii,
   158           opt_attribs[ii]);
   159     }
   160   }
   161   gl.linkProgram(program);
   163   // Check the link status
   164   var linked = gl.getProgramParameter(program, gl.LINK_STATUS);
   165   if (!linked) {
   166       // something went wrong with the link
   167       lastError = gl.getProgramInfoLog (program);
   168       error("Error in program linking:" + lastError);
   170       gl.deleteProgram(program);
   171       return null;
   172   }
   174   gl.useProgram(program);
   175   return program;
   176 };
   178 /**
   179  * Creates a simple texture program.
   180  * @param {!WebGLContext} gl The WebGLContext to use.
   181  * @param {number} opt_positionLocation The attrib location for position.
   182  * @param {number} opt_texcoordLocation The attrib location for texture coords.
   183  * @return {WebGLProgram}
   184  */
   185 var setupSimpleTextureProgram = function(
   186     gl, opt_positionLocation, opt_texcoordLocation) {
   187   opt_positionLocation = opt_positionLocation || 0;
   188   opt_texcoordLocation = opt_texcoordLocation || 1;
   189   var vs = setupSimpleTextureVertexShader(gl);
   190   var fs = setupSimpleTextureFragmentShader(gl);
   191   if (!vs || !fs) {
   192     return null;
   193   }
   194   var program = setupProgram(
   195       gl,
   196       [vs, fs],
   197       ['vPosition', 'texCoord0'],
   198       [opt_positionLocation, opt_texcoordLocation]);
   199   if (!program) {
   200     gl.deleteShader(fs);
   201     gl.deleteShader(vs);
   202   }
   203   gl.useProgram(program);
   204   return program;
   205 };
   207 /**
   208  * Creates buffers for a textured unit quad and attaches them to vertex attribs.
   209  * @param {!WebGLContext} gl The WebGLContext to use.
   210  * @param {number} opt_positionLocation The attrib location for position.
   211  * @param {number} opt_texcoordLocation The attrib location for texture coords.
   212  * @return {!Array.<WebGLBuffer>} The buffer objects that were
   213  *      created.
   214  */
   215 var setupUnitQuad = function(gl, opt_positionLocation, opt_texcoordLocation) {
   216   return setupUnitQuadWithTexCoords(gl, [ 0.0, 0.0 ], [ 1.0, 1.0 ],
   217                                     opt_positionLocation, opt_texcoordLocation);
   218 };
   220 /**
   221  * Draws a previously setupUnitQuad.
   222  * @param {!WebGLContext} gl The WebGLContext to use.
   223  */
   224 var drawUnitQuad = function(gl) {
   225   gl.drawArrays(gl.TRIANGLES, 0, 6);
   226 };
   228 /**
   229  * Clears then Draws a previously setupUnitQuad.
   230  * @param {!WebGLContext} gl The WebGLContext to use.
   231  * @param {!Array.<number>} opt_color The color to fill clear with before
   232  * drawing. A 4 element array where each element is in the range 0 to
   233  * 255. Default [255, 255, 255, 255]
   234  */
   235 var clearAndDrawUnitQuad = function(gl, opt_color) {
   236   opt_color = opt_color || [255, 255, 255, 255];
   238   // Save and restore.
   239   var prevClearColor = gl.getParameter(gl.COLOR_CLEAR_VALUE);
   241   gl.clearColor(opt_color[0] / 255,
   242                 opt_color[1] / 255,
   243                 opt_color[2] / 255,
   244                 opt_color[3] / 255);
   245   gl.clear(gl.COLOR_BUFFER_BIT);
   246   drawUnitQuad(gl);
   248   gl.clearColor(prevClearColor[0],
   249                 prevClearColor[1],
   250                 prevClearColor[2],
   251                 prevClearColor[3]);
   252 };
   254 /**
   255  * Creates buffers for a textured unit quad with specified lower left
   256  * and upper right texture coordinates, and attaches them to vertex
   257  * attribs.
   258  * @param {!WebGLContext} gl The WebGLContext to use.
   259  * @param {!Array.<number>} lowerLeftTexCoords The texture coordinates for the lower left corner.
   260  * @param {!Array.<number>} upperRightTexCoords The texture coordinates for the upper right corner.
   261  * @param {number} opt_positionLocation The attrib location for position.
   262  * @param {number} opt_texcoordLocation The attrib location for texture coords.
   263  * @return {!Array.<WebGLBuffer>} The buffer objects that were
   264  *      created.
   265  */
   266 var setupUnitQuadWithTexCoords = function(
   267     gl, lowerLeftTexCoords, upperRightTexCoords,
   268     opt_positionLocation, opt_texcoordLocation) {
   269   opt_positionLocation = opt_positionLocation || 0;
   270   opt_texcoordLocation = opt_texcoordLocation || 1;
   271   var objects = [];
   273   var vertexObject = gl.createBuffer();
   274   gl.bindBuffer(gl.ARRAY_BUFFER, vertexObject);
   275   gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
   276        1.0,  1.0, 0.0,
   277       -1.0,  1.0, 0.0,
   278       -1.0, -1.0, 0.0,
   279        1.0,  1.0, 0.0,
   280       -1.0, -1.0, 0.0,
   281        1.0, -1.0, 0.0]), gl.STATIC_DRAW);
   282   gl.enableVertexAttribArray(opt_positionLocation);
   283   gl.vertexAttribPointer(opt_positionLocation, 3, gl.FLOAT, false, 0, 0);
   284   objects.push(vertexObject);
   286   var llx = lowerLeftTexCoords[0];
   287   var lly = lowerLeftTexCoords[1];
   288   var urx = upperRightTexCoords[0];
   289   var ury = upperRightTexCoords[1];
   291   var vertexObject = gl.createBuffer();
   292   gl.bindBuffer(gl.ARRAY_BUFFER, vertexObject);
   293   gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
   294       urx, ury,
   295       llx, ury,
   296       llx, lly,
   297       urx, ury,
   298       llx, lly,
   299       urx, lly]), gl.STATIC_DRAW);
   300   gl.enableVertexAttribArray(opt_texcoordLocation);
   301   gl.vertexAttribPointer(opt_texcoordLocation, 2, gl.FLOAT, false, 0, 0);
   302   objects.push(vertexObject);
   303   return objects;
   304 };
   306 /**
   307  * Creates a program and buffers for rendering a textured quad.
   308  * @param {!WebGLContext} gl The WebGLContext to use.
   309  * @param {number} opt_positionLocation The attrib location for position.
   310  * @param {number} opt_texcoordLocation The attrib location for texture coords.
   311  * @return {!WebGLProgram}
   312  */
   313 var setupTexturedQuad = function(
   314     gl, opt_positionLocation, opt_texcoordLocation) {
   315   var program = setupSimpleTextureProgram(
   316       gl, opt_positionLocation, opt_texcoordLocation);
   317   setupUnitQuad(gl, opt_positionLocation, opt_texcoordLocation);
   318   return program;
   319 };
   321 /**
   322  * Creates a program and buffers for rendering a textured quad with
   323  * specified lower left and upper right texture coordinates.
   324  * @param {!WebGLContext} gl The WebGLContext to use.
   325  * @param {!Array.<number>} lowerLeftTexCoords The texture coordinates for the lower left corner.
   326  * @param {!Array.<number>} upperRightTexCoords The texture coordinates for the upper right corner.
   327  * @param {number} opt_positionLocation The attrib location for position.
   328  * @param {number} opt_texcoordLocation The attrib location for texture coords.
   329  * @return {!WebGLProgram}
   330  */
   331 var setupTexturedQuadWithTexCoords = function(
   332     gl, lowerLeftTexCoords, upperRightTexCoords,
   333     opt_positionLocation, opt_texcoordLocation) {
   334   var program = setupSimpleTextureProgram(
   335       gl, opt_positionLocation, opt_texcoordLocation);
   336   setupUnitQuadWithTexCoords(gl, lowerLeftTexCoords, upperRightTexCoords,
   337                              opt_positionLocation, opt_texcoordLocation);
   338   return program;
   339 };
   341 /**
   342  * Creates a unit quad with only positions of a given resolution.
   343  * @param {!WebGLContext} gl The WebGLContext to use.
   344  * @param {number} gridRes The resolution of the mesh grid, expressed in the number of triangles across and down.
   345  * @param {number} opt_positionLocation The attrib location for position.
   346  */
   347 var setupQuad = function (
   348     gl, gridRes, opt_positionLocation, opt_flipOddTriangles) {
   349   var positionLocation = opt_positionLocation || 0;
   350   var objects = [];
   352   var vertsAcross = gridRes + 1;
   353   var numVerts = vertsAcross * vertsAcross;
   354   var positions = new Float32Array(numVerts * 3);
   355   var indices = new Uint16Array(6 * gridRes * gridRes);
   357   var poffset = 0;
   359   for (var yy = 0; yy <= gridRes; ++yy) {
   360     for (var xx = 0; xx <= gridRes; ++xx) {
   361       positions[poffset + 0] = -1 + 2 * xx / gridRes;
   362       positions[poffset + 1] = -1 + 2 * yy / gridRes;
   363       positions[poffset + 2] = 0;
   365       poffset += 3;
   366     }
   367   }
   369   var tbase = 0;
   370   for (var yy = 0; yy < gridRes; ++yy) {
   371     var index = yy * vertsAcross;
   372     for (var xx = 0; xx < gridRes; ++xx) {
   373       indices[tbase + 0] = index + 0;
   374       indices[tbase + 1] = index + 1;
   375       indices[tbase + 2] = index + vertsAcross;
   376       indices[tbase + 3] = index + vertsAcross;
   377       indices[tbase + 4] = index + 1;
   378       indices[tbase + 5] = index + vertsAcross + 1;
   380       if (opt_flipOddTriangles) {
   381         indices[tbase + 4] = index + vertsAcross + 1;
   382         indices[tbase + 5] = index + 1;
   383       }
   385       index += 1;
   386       tbase += 6;
   387     }
   388   }
   390   var buf = gl.createBuffer();
   391   gl.bindBuffer(gl.ARRAY_BUFFER, buf);
   392   gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW);
   393   gl.enableVertexAttribArray(positionLocation);
   394   gl.vertexAttribPointer(positionLocation, 3, gl.FLOAT, false, 0, 0);
   395   objects.push(buf);
   397   var buf = gl.createBuffer();
   398   gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buf);
   399   gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);
   400   objects.push(buf);
   402   return objects;
   403 };
   405 /**
   406  * Fills the given texture with a solid color
   407  * @param {!WebGLContext} gl The WebGLContext to use.
   408  * @param {!WebGLTexture} tex The texture to fill.
   409  * @param {number} width The width of the texture to create.
   410  * @param {number} height The height of the texture to create.
   411  * @param {!Array.<number>} color The color to fill with. A 4 element array
   412  *        where each element is in the range 0 to 255.
   413  * @param {number} opt_level The level of the texture to fill. Default = 0.
   414  */
   415 var fillTexture = function(gl, tex, width, height, color, opt_level) {
   416   opt_level = opt_level || 0;
   417   var numPixels = width * height;
   418   var size = numPixels * 4;
   419   var buf = new Uint8Array(size);
   420   for (var ii = 0; ii < numPixels; ++ii) {
   421     var off = ii * 4;
   422     buf[off + 0] = color[0];
   423     buf[off + 1] = color[1];
   424     buf[off + 2] = color[2];
   425     buf[off + 3] = color[3];
   426   }
   427   gl.bindTexture(gl.TEXTURE_2D, tex);
   428   gl.texImage2D(
   429       gl.TEXTURE_2D, opt_level, gl.RGBA, width, height, 0,
   430       gl.RGBA, gl.UNSIGNED_BYTE, buf);
   431   };
   433 /**
   434  * Creates a textures and fills it with a solid color
   435  * @param {!WebGLContext} gl The WebGLContext to use.
   436  * @param {number} width The width of the texture to create.
   437  * @param {number} height The height of the texture to create.
   438  * @param {!Array.<number>} color The color to fill with. A 4 element array
   439  *        where each element is in the range 0 to 255.
   440  * @return {!WebGLTexture}
   441  */
   442 var createColoredTexture = function(gl, width, height, color) {
   443   var tex = gl.createTexture();
   444   fillTexture(gl, tex, width, height, color);
   445   return tex;
   446 };
   448 /**
   449  * Draws a previously setup quad.
   450  * @param {!WebGLContext} gl The WebGLContext to use.
   451  * @param {!Array.<number>} opt_color The color to fill clear with before
   452  *        drawing. A 4 element array where each element is in the range 0 to
   453  *        255. Default [255, 255, 255, 255]
   454  */
   455 var drawQuad = function(gl, opt_color) {
   456   opt_color = opt_color || [255, 255, 255, 255];
   457   gl.clearColor(
   458       opt_color[0] / 255,
   459       opt_color[1] / 255,
   460       opt_color[2] / 255,
   461       opt_color[3] / 255);
   462   gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
   463   gl.drawArrays(gl.TRIANGLES, 0, 6);
   464 };
   466 /**
   467  * Checks that a portion of a canvas is 1 color.
   468  * @param {!WebGLContext} gl The WebGLContext to use.
   469  * @param {number} x left corner of region to check.
   470  * @param {number} y bottom corner of region to check.
   471  * @param {number} width width of region to check.
   472  * @param {number} height width of region to check.
   473  * @param {!Array.<number>} color The color to fill clear with before drawing. A
   474  *        4 element array where each element is in the range 0 to 255.
   475  * @param {string} msg Message to associate with success. Eg ("should be red").
   476  * @param {number} errorRange Optional. Acceptable error in
   477  *        color checking. 0 by default.
   478  */
   479 var checkCanvasRect = function(gl, x, y, width, height, color, msg, errorRange) {
   480   errorRange = errorRange || 0;
   481   var buf = new Uint8Array(width * height * 4);
   482   gl.readPixels(x, y, width, height, gl.RGBA, gl.UNSIGNED_BYTE, buf);
   483   for (var i = 0; i < width * height; ++i) {
   484     var offset = i * 4;
   485     for (var j = 0; j < color.length; ++j) {
   486       if (Math.abs(buf[offset + j] - color[j]) > errorRange) {
   487         var was = buf[offset + 0].toString();
   488         for (j = 1; j < color.length; ++j) {
   489           was += "," + buf[offset + j];
   490         }
   492         var cv = document.createElement('canvas');
   493         cv.height = height;
   494         cv.width = width;
   495         var ctx = cv.getContext('2d');
   496         ctx.fillStyle="rgba(" + color[0] + ", " + color[1] + ", " + color[2] + ", 255)";
   497         ctx.fillRect(0, 0, width, height);
   498         testFailedRender(msg, ctx, buf, width, height);
   500         debug('at (' + (i % width) + ', ' + Math.floor(i / width) +
   501               ') expected: ' + color + ' was ' + was);
   502         return;
   503       }
   504     }
   505   }
   506   testPassed(msg);
   507 };
   509 /**
   510  * Checks that an entire canvas is 1 color.
   511  * @param {!WebGLContext} gl The WebGLContext to use.
   512  * @param {!Array.<number>} color The color to fill clear with before drawing. A
   513  *        4 element array where each element is in the range 0 to 255.
   514  * @param {string} msg Message to associate with success. Eg ("should be red").
   515  * @param {number} errorRange Optional. Acceptable error in
   516  *        color checking. 0 by default.
   517  */
   518 var checkCanvas = function(gl, color, msg, errorRange) {
   519   checkCanvasRect(gl, 0, 0, gl.canvas.width, gl.canvas.height, color, msg, errorRange);
   520 };
   522 /**
   523  * Loads a texture, calls callback when finished.
   524  * @param {!WebGLContext} gl The WebGLContext to use.
   525  * @param {string} url URL of image to load
   526  * @param {function(!Image): void} callback Function that gets called after
   527  *        image has loaded
   528  * @return {!WebGLTexture} The created texture.
   529  */
   530 var loadTexture = function(gl, url, callback) {
   531     var texture = gl.createTexture();
   532     gl.bindTexture(gl.TEXTURE_2D, texture);
   533     gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
   534     gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
   535     var image = new Image();
   536     image.onload = function() {
   537         gl.bindTexture(gl.TEXTURE_2D, texture);
   538         gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
   539         gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
   540         callback(image);
   541     };
   542     image.src = url;
   543     return texture;
   544 };
   546 /**
   547  * Creates a webgl context.
   548  * @param {!Canvas|string} opt_canvas The canvas tag to get
   549  *     context from. If one is not passed in one will be
   550  *     created. If it's a string it's assumed to be the id of a
   551  *     canvas.
   552  * @return {!WebGLContext} The created context.
   553  */
   554 var create3DContext = function(opt_canvas, opt_attributes) {
   555   opt_canvas = opt_canvas || document.createElement("canvas");
   556   if (typeof opt_canvas == 'string') {
   557     opt_canvas = document.getElementById(opt_canvas);
   558   }
   559   var context = null;
   560   var names = ["webgl", "experimental-webgl"];
   561   for (var i = 0; i < names.length; ++i) {
   562     try {
   563       context = opt_canvas.getContext(names[i], opt_attributes);
   564     } catch (e) {
   565     }
   566     if (context) {
   567       break;
   568     }
   569   }
   570   if (!context) {
   571     testFailed("Unable to fetch WebGL rendering context for Canvas");
   572   }
   573   return context;
   574 }
   576 /**
   577  * Gets a GLError value as a string.
   578  * @param {!WebGLContext} gl The WebGLContext to use.
   579  * @param {number} err The webgl error as retrieved from gl.getError().
   580  * @return {string} the error as a string.
   581  */
   582 var getGLErrorAsString = function(gl, err) {
   583   if (err === gl.NO_ERROR) {
   584     return "NO_ERROR";
   585   }
   586   for (var name in gl) {
   587     if (gl[name] === err) {
   588       return name;
   589     }
   590   }
   591   return err.toString();
   592 };
   594 /**
   595  * Wraps a WebGL function with a function that throws an exception if there is
   596  * an error.
   597  * @param {!WebGLContext} gl The WebGLContext to use.
   598  * @param {string} fname Name of function to wrap.
   599  * @return {function} The wrapped function.
   600  */
   601 var createGLErrorWrapper = function(context, fname) {
   602   return function() {
   603     var rv = context[fname].apply(context, arguments);
   604     var err = context.getError();
   605     if (err != 0)
   606       throw "GL error " + getGLErrorAsString(err) + " in " + fname;
   607     return rv;
   608   };
   609 };
   611 /**
   612  * Creates a WebGL context where all functions are wrapped to throw an exception
   613  * if there is an error.
   614  * @param {!Canvas} canvas The HTML canvas to get a context from.
   615  * @return {!Object} The wrapped context.
   616  */
   617 function create3DContextWithWrapperThatThrowsOnGLError(canvas) {
   618   var context = create3DContext(canvas);
   619   var wrap = {};
   620   for (var i in context) {
   621     try {
   622       if (typeof context[i] == 'function') {
   623         wrap[i] = createGLErrorWrapper(context, i);
   624       } else {
   625         wrap[i] = context[i];
   626       }
   627     } catch (e) {
   628       error("createContextWrapperThatThrowsOnGLError: Error accessing " + i);
   629     }
   630   }
   631   wrap.getError = function() {
   632       return context.getError();
   633   };
   634   return wrap;
   635 };
   637 /**
   638  * Tests that an evaluated expression generates a specific GL error.
   639  * @param {!WebGLContext} gl The WebGLContext to use.
   640  * @param {number} glError The expected gl error.
   641  * @param {string} evalSTr The string to evaluate.
   642  */
   643 var shouldGenerateGLError = function(gl, glError, evalStr) {
   644   var exception;
   645   try {
   646     eval(evalStr);
   647   } catch (e) {
   648     exception = e;
   649   }
   650   if (exception) {
   651     testFailed(evalStr + " threw exception " + exception);
   652   } else {
   653     var err = gl.getError();
   654     if (err != glError) {
   655       testFailed(evalStr + " expected: " + getGLErrorAsString(gl, glError) + ". Was " + getGLErrorAsString(gl, err) + ".");
   656     } else {
   657       testPassed(evalStr + " was expected value: " + getGLErrorAsString(gl, glError) + ".");
   658     }
   659   }
   660 };
   662 /**
   663  * Tests that the first error GL returns is the specified error.
   664  * @param {!WebGLContext} gl The WebGLContext to use.
   665  * @param {number} glError The expected gl error.
   666  * @param {string} opt_msg
   667  */
   668 var glErrorShouldBe = function(gl, glError, opt_msg) {
   669   opt_msg = opt_msg || "";
   670   var err = gl.getError();
   671   if (err != glError) {
   672     testFailed("getError expected: " + getGLErrorAsString(gl, glError) +
   673                ". Was " + getGLErrorAsString(gl, err) + " : " + opt_msg);
   674   } else {
   675     testPassed("getError was expected value: " +
   676                 getGLErrorAsString(gl, glError) + " : " + opt_msg);
   677   }
   678 };
   680 /**
   681  * Links a WebGL program, throws if there are errors.
   682  * @param {!WebGLContext} gl The WebGLContext to use.
   683  * @param {!WebGLProgram} program The WebGLProgram to link.
   684  * @param {function(string): void) opt_errorCallback callback for errors.
   685  */
   686 var linkProgram = function(gl, program, opt_errorCallback) {
   687   errFn = opt_errorCallback || testFailed;
   688   // Link the program
   689   gl.linkProgram(program);
   691   // Check the link status
   692   var linked = gl.getProgramParameter(program, gl.LINK_STATUS);
   693   if (!linked) {
   694     // something went wrong with the link
   695     var error = gl.getProgramInfoLog (program);
   697     errFn("Error in program linking:" + error);
   699     gl.deleteProgram(program);
   700   }
   701 };
   703 /**
   704  * Sets up WebGL with shaders.
   705  * @param {string} canvasName The id of the canvas.
   706  * @param {string} vshader The id of the script tag that contains the vertex
   707  *     shader source.
   708  * @param {string} fshader The id of the script tag that contains the fragment
   709  *     shader source.
   710  * @param {!Array.<string>} attribs An array of attrib names used to bind
   711  *     attribs to the ordinal of the name in this array.
   712  * @param {!Array.<number>} opt_clearColor The color to cla
   713  * @return {!WebGLContext} The created WebGLContext.
   714  */
   715 var setupWebGLWithShaders = function(
   716    canvasName, vshader, fshader, attribs) {
   717   var canvas = document.getElementById(canvasName);
   718   var gl = create3DContext(canvas);
   719   if (!gl) {
   720     testFailed("No WebGL context found");
   721   }
   723   // create our shaders
   724   var vertexShader = loadShaderFromScript(gl, vshader);
   725   var fragmentShader = loadShaderFromScript(gl, fshader);
   727   if (!vertexShader || !fragmentShader) {
   728     return null;
   729   }
   731   // Create the program object
   732   program = gl.createProgram();
   734   if (!program) {
   735     return null;
   736   }
   738   // Attach our two shaders to the program
   739   gl.attachShader (program, vertexShader);
   740   gl.attachShader (program, fragmentShader);
   742   // Bind attributes
   743   for (var i in attribs) {
   744     gl.bindAttribLocation (program, i, attribs[i]);
   745   }
   747   linkProgram(gl, program);
   749   gl.useProgram(program);
   751   gl.clearColor(0,0,0,1);
   752   gl.clearDepth(1);
   754   gl.enable(gl.DEPTH_TEST);
   755   gl.enable(gl.BLEND);
   756   gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
   758   gl.program = program;
   759   return gl;
   760 };
   762 /**
   763  * Loads text from an external file. This function is synchronous.
   764  * @param {string} url The url of the external file.
   765  * @param {!function(bool, string): void} callback that is sent a bool for
   766  *     success and the string.
   767  */
   768 var loadTextFileAsync = function(url, callback) {
   769   log ("loading: " + url);
   770   var error = 'loadTextFileSynchronous failed to load url "' + url + '"';
   771   var request;
   772   if (window.XMLHttpRequest) {
   773     request = new XMLHttpRequest();
   774     if (request.overrideMimeType) {
   775       request.overrideMimeType('text/plain');
   776     }
   777   } else {
   778     throw 'XMLHttpRequest is disabled';
   779   }
   780   try {
   781     request.open('GET', url, true);
   782     request.onreadystatechange = function() {
   783       if (request.readyState == 4) {
   784         var text = '';
   785         // HTTP reports success with a 200 status. The file protocol reports
   786         // success with zero. HTTP does not use zero as a status code (they
   787         // start at 100).
   788         // https://developer.mozilla.org/En/Using_XMLHttpRequest
   789         var success = request.status == 200 || request.status == 0;
   790         if (success) {
   791           text = request.responseText;
   792         }
   793         log("loaded: " + url);
   794         callback(success, text);
   795       }
   796     };
   797     request.send(null);
   798   } catch (e) {
   799     log("failed to load: " + url);
   800     callback(false, '');
   801   }
   802 };
   804 // Add your prefix here.
   805 var browserPrefixes = [
   806   "",
   807   "MOZ_",
   808   "OP_",
   809   "WEBKIT_"
   810 ];
   812 /**
   813  * Given an extension name like WEBGL_compressed_texture_s3tc
   814  * returns the name of the supported version extension, like
   815  * WEBKIT_WEBGL_compressed_teture_s3tc
   816  * @param {string} name Name of extension to look for
   817  * @return {string} name of extension found or undefined if not
   818  *     found.
   819  */
   820 var getSupportedExtensionWithKnownPrefixes = function(gl, name) {
   821   var supported = gl.getSupportedExtensions();
   822   for (var ii = 0; ii < browserPrefixes.length; ++ii) {
   823     var prefixedName = browserPrefixes[ii] + name;
   824     if (supported.indexOf(prefixedName) >= 0) {
   825       return prefixedName;
   826     }
   827   }
   828 };
   830 /**
   831  * Given an extension name like WEBGL_compressed_texture_s3tc
   832  * returns the supported version extension, like
   833  * WEBKIT_WEBGL_compressed_teture_s3tc
   834  * @param {string} name Name of extension to look for
   835  * @return {WebGLExtension} The extension or undefined if not
   836  *     found.
   837  */
   838 var getExtensionWithKnownPrefixes = function(gl, name) {
   839   for (var ii = 0; ii < browserPrefixes.length; ++ii) {
   840     var prefixedName = browserPrefixes[ii] + name;
   841     var ext = gl.getExtension(prefixedName);
   842     if (ext) {
   843       return ext;
   844     }
   845   }
   846 };
   848 /**
   849  * Recursively loads a file as a list. Each line is parsed for a relative
   850  * path. If the file ends in .txt the contents of that file is inserted in
   851  * the list.
   852  *
   853  * @param {string} url The url of the external file.
   854  * @param {!function(bool, Array<string>): void} callback that is sent a bool
   855  *     for success and the array of strings.
   856  */
   857 var getFileListAsync = function(url, callback) {
   858   var files = [];
   860   var getFileListImpl = function(url, callback) {
   861     var files = [];
   862     if (url.substr(url.length - 4) == '.txt') {
   863       loadTextFileAsync(url, function() {
   864         return function(success, text) {
   865           if (!success) {
   866             callback(false, '');
   867             return;
   868           }
   869           var lines = text.split('\n');
   870           var prefix = '';
   871           var lastSlash = url.lastIndexOf('/');
   872           if (lastSlash >= 0) {
   873             prefix = url.substr(0, lastSlash + 1);
   874           }
   875           var fail = false;
   876           var count = 1;
   877           var index = 0;
   878           for (var ii = 0; ii < lines.length; ++ii) {
   879             var str = lines[ii].replace(/^\s\s*/, '').replace(/\s\s*$/, '');
   880             if (str.length > 4 &&
   881                 str[0] != '#' &&
   882                 str[0] != ";" &&
   883                 str.substr(0, 2) != "//") {
   884               var names = str.split(/ +/);
   885               new_url = prefix + str;
   886               if (names.length == 1) {
   887                 new_url = prefix + str;
   888                 ++count;
   889                 getFileListImpl(new_url, function(index) {
   890                   return function(success, new_files) {
   891                     log("got files: " + new_files.length);
   892                     if (success) {
   893                       files[index] = new_files;
   894                     }
   895                     finish(success);
   896                   };
   897                 }(index++));
   898               } else {
   899                 var s = "";
   900                 var p = "";
   901                 for (var jj = 0; jj < names.length; ++jj) {
   902                   s += p + prefix + names[jj];
   903                   p = " ";
   904                 }
   905                 files[index++] = s;
   906               }
   907             }
   908           }
   909           finish(true);
   911           function finish(success) {
   912             if (!success) {
   913               fail = true;
   914             }
   915             --count;
   916             log("count: " + count);
   917             if (!count) {
   918               callback(!fail, files);
   919             }
   920           }
   921         }
   922       }());
   924     } else {
   925       files.push(url);
   926       callback(true, files);
   927     }
   928   };
   930   getFileListImpl(url, function(success, files) {
   931     // flatten
   932     var flat = [];
   933     flatten(files);
   934     function flatten(files) {
   935       for (var ii = 0; ii < files.length; ++ii) {
   936         var value = files[ii];
   937         if (typeof(value) == "string") {
   938           flat.push(value);
   939         } else {
   940           flatten(value);
   941         }
   942       }
   943     }
   944     callback(success, flat);
   945   });
   946 };
   948 /**
   949  * Gets a file from a file/URL
   950  * @param {string} file the URL of the file to get.
   951  * @return {string} The contents of the file.
   952  */
   953 var readFile = function(file) {
   954   var xhr = new XMLHttpRequest();
   955   xhr.open("GET", file, false);
   956   xhr.send();
   957   return xhr.responseText.replace(/\r/g, "");
   958 };
   960 var readFileList = function(url) {
   961   var files = [];
   962   if (url.substr(url.length - 4) == '.txt') {
   963     var lines = readFile(url).split('\n');
   964     var prefix = '';
   965     var lastSlash = url.lastIndexOf('/');
   966     if (lastSlash >= 0) {
   967       prefix = url.substr(0, lastSlash + 1);
   968     }
   969     for (var ii = 0; ii < lines.length; ++ii) {
   970       var str = lines[ii].replace(/^\s\s*/, '').replace(/\s\s*$/, '');
   971       if (str.length > 4 &&
   972           str[0] != '#' &&
   973           str[0] != ";" &&
   974           str.substr(0, 2) != "//") {
   975         var names = str.split(/ +/);
   976         if (names.length == 1) {
   977           new_url = prefix + str;
   978           files = files.concat(readFileList(new_url));
   979         } else {
   980           var s = "";
   981           var p = "";
   982           for (var jj = 0; jj < names.length; ++jj) {
   983             s += p + prefix + names[jj];
   984             p = " ";
   985           }
   986           files.push(s);
   987         }
   988       }
   989     }
   990   } else {
   991     files.push(url);
   992   }
   993   return files;
   994 };
   996 /**
   997  * Loads a shader.
   998  * @param {!WebGLContext} gl The WebGLContext to use.
   999  * @param {string} shaderSource The shader source.
  1000  * @param {number} shaderType The type of shader.
  1001  * @param {function(string): void) opt_errorCallback callback for errors.
  1002  * @return {!WebGLShader} The created shader.
  1003  */
  1004 var loadShader = function(gl, shaderSource, shaderType, opt_errorCallback) {
  1005   var errFn = opt_errorCallback || error;
  1006   // Create the shader object
  1007   var shader = gl.createShader(shaderType);
  1008   if (shader == null) {
  1009     errFn("*** Error: unable to create shader '"+shaderSource+"'");
  1010     return null;
  1013   // Load the shader source
  1014   gl.shaderSource(shader, shaderSource);
  1015   var err = gl.getError();
  1016   if (err != gl.NO_ERROR) {
  1017     errFn("*** Error loading shader '" + shader + "':" + glEnumToString(gl, err));
  1018     return null;
  1021   // Compile the shader
  1022   gl.compileShader(shader);
  1024   // Check the compile status
  1025   var compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
  1026   if (!compiled) {
  1027     // Something went wrong during compilation; get the error
  1028     lastError = gl.getShaderInfoLog(shader);
  1029     errFn("*** Error compiling shader '" + shader + "':" + lastError);
  1030     gl.deleteShader(shader);
  1031     return null;
  1034   return shader;
  1037 /**
  1038  * Loads a shader from a URL.
  1039  * @param {!WebGLContext} gl The WebGLContext to use.
  1040  * @param {file} file The URL of the shader source.
  1041  * @param {number} type The type of shader.
  1042  * @param {function(string): void) opt_errorCallback callback for errors.
  1043  * @return {!WebGLShader} The created shader.
  1044  */
  1045 var loadShaderFromFile = function(gl, file, type, opt_errorCallback) {
  1046   var shaderSource = readFile(file);
  1047   return loadShader(gl, shaderSource, type, opt_errorCallback);
  1048 };
  1050 /**
  1051  * Gets the content of script.
  1052  */
  1053 var getScript = function(scriptId) {
  1054   var shaderScript = document.getElementById(scriptId);
  1055   if (!shaderScript) {
  1056     throw("*** Error: unknown script element" + scriptId);
  1058   return shaderScript.text;
  1059 };
  1061 /**
  1062  * Loads a shader from a script tag.
  1063  * @param {!WebGLContext} gl The WebGLContext to use.
  1064  * @param {string} scriptId The id of the script tag.
  1065  * @param {number} opt_shaderType The type of shader. If not passed in it will
  1066  *     be derived from the type of the script tag.
  1067  * @param {function(string): void) opt_errorCallback callback for errors.
  1068  * @return {!WebGLShader} The created shader.
  1069  */
  1070 var loadShaderFromScript = function(
  1071     gl, scriptId, opt_shaderType, opt_errorCallback) {
  1072   var shaderSource = "";
  1073   var shaderType;
  1074   var shaderScript = document.getElementById(scriptId);
  1075   if (!shaderScript) {
  1076     throw("*** Error: unknown script element " + scriptId);
  1078   shaderSource = shaderScript.text;
  1080   if (!opt_shaderType) {
  1081     if (shaderScript.type == "x-shader/x-vertex") {
  1082       shaderType = gl.VERTEX_SHADER;
  1083     } else if (shaderScript.type == "x-shader/x-fragment") {
  1084       shaderType = gl.FRAGMENT_SHADER;
  1085     } else if (shaderType != gl.VERTEX_SHADER && shaderType != gl.FRAGMENT_SHADER) {
  1086       throw("*** Error: unknown shader type");
  1087       return null;
  1091   return loadShader(
  1092       gl, shaderSource, opt_shaderType ? opt_shaderType : shaderType,
  1093       opt_errorCallback);
  1094 };
  1096 var loadStandardProgram = function(gl) {
  1097   var program = gl.createProgram();
  1098   gl.attachShader(program, loadStandardVertexShader(gl));
  1099   gl.attachShader(program, loadStandardFragmentShader(gl));
  1100   linkProgram(gl, program);
  1101   return program;
  1102 };
  1104 /**
  1105  * Loads shaders from files, creates a program, attaches the shaders and links.
  1106  * @param {!WebGLContext} gl The WebGLContext to use.
  1107  * @param {string} vertexShaderPath The URL of the vertex shader.
  1108  * @param {string} fragmentShaderPath The URL of the fragment shader.
  1109  * @param {function(string): void) opt_errorCallback callback for errors.
  1110  * @return {!WebGLProgram} The created program.
  1111  */
  1112 var loadProgramFromFile = function(
  1113     gl, vertexShaderPath, fragmentShaderPath, opt_errorCallback) {
  1114   var program = gl.createProgram();
  1115   gl.attachShader(
  1116       program,
  1117       loadShaderFromFile(
  1118           gl, vertexShaderPath, gl.VERTEX_SHADER, opt_errorCallback));
  1119   gl.attachShader(
  1120       program,
  1121       loadShaderFromFile(
  1122           gl, fragmentShaderPath, gl.FRAGMENT_SHADER, opt_errorCallback));
  1123   linkProgram(gl, program, opt_errorCallback);
  1124   return program;
  1125 };
  1127 /**
  1128  * Loads shaders from script tags, creates a program, attaches the shaders and
  1129  * links.
  1130  * @param {!WebGLContext} gl The WebGLContext to use.
  1131  * @param {string} vertexScriptId The id of the script tag that contains the
  1132  *        vertex shader.
  1133  * @param {string} fragmentScriptId The id of the script tag that contains the
  1134  *        fragment shader.
  1135  * @param {function(string): void) opt_errorCallback callback for errors.
  1136  * @return {!WebGLProgram} The created program.
  1137  */
  1138 var loadProgramFromScript = function loadProgramFromScript(
  1139     gl, vertexScriptId, fragmentScriptId, opt_errorCallback) {
  1140   var program = gl.createProgram();
  1141   gl.attachShader(
  1142       program,
  1143       loadShaderFromScript(
  1144           gl, vertexScriptId, gl.VERTEX_SHADER, opt_errorCallback));
  1145   gl.attachShader(
  1146       program,
  1147       loadShaderFromScript(
  1148           gl, fragmentScriptId,  gl.FRAGMENT_SHADER, opt_errorCallback));
  1149   linkProgram(gl, program, opt_errorCallback);
  1150   return program;
  1151 };
  1153 /**
  1154  * Loads shaders from source, creates a program, attaches the shaders and
  1155  * links.
  1156  * @param {!WebGLContext} gl The WebGLContext to use.
  1157  * @param {string} vertexShader The vertex shader.
  1158  * @param {string} fragmentShader The fragment shader.
  1159  * @param {function(string): void) opt_errorCallback callback for errors.
  1160  * @return {!WebGLProgram} The created program.
  1161  */
  1162 var loadProgram = function(
  1163     gl, vertexShader, fragmentShader, opt_errorCallback) {
  1164   var program = gl.createProgram();
  1165   gl.attachShader(
  1166       program,
  1167       loadShader(
  1168           gl, vertexShader, gl.VERTEX_SHADER, opt_errorCallback));
  1169   gl.attachShader(
  1170       program,
  1171       loadShader(
  1172           gl, fragmentShader, gl.FRAGMENT_SHADER, opt_errorCallback));
  1173   linkProgram(gl, program, opt_errorCallback);
  1174   return program;
  1175 };
  1177 /**
  1178  * Loads shaders from source, creates a program, attaches the shaders and
  1179  * links but expects error.
  1181  * GLSL 1.0.17 10.27 effectively says that compileShader can
  1182  * always succeed as long as linkProgram fails so we can't
  1183  * rely on compileShader failing. This function expects
  1184  * one of the shader to fail OR linking to fail.
  1186  * @param {!WebGLContext} gl The WebGLContext to use.
  1187  * @param {string} vertexShaderScriptId The vertex shader.
  1188  * @param {string} fragmentShaderScriptId The fragment shader.
  1189  * @return {WebGLProgram} The created program.
  1190  */
  1191 var loadProgramFromScriptExpectError = function(
  1192     gl, vertexShaderScriptId, fragmentShaderScriptId) {
  1193   var vertexShader = loadShaderFromScript(gl, vertexShaderScriptId);
  1194   if (!vertexShader) {
  1195     return null;
  1197   var fragmentShader = loadShaderFromScript(gl, fragmentShaderScriptId);
  1198   if (!fragmentShader) {
  1199     return null;
  1201   var linkSuccess = true;
  1202   var program = gl.createProgram();
  1203   gl.attachShader(program, vertexShader);
  1204   gl.attachShader(program, fragmentShader);
  1205   linkSuccess = true;
  1206   linkProgram(gl, program, function() {
  1207       linkSuccess = false;
  1208     });
  1209   return linkSuccess ? program : null;
  1210 };
  1212 var basePath;
  1213 var getBasePath = function() {
  1214   if (!basePath) {
  1215     var expectedBase = "webgl-test-utils.js";
  1216     var scripts = document.getElementsByTagName('script');
  1217     for (var script, i = 0; script = scripts[i]; i++) {
  1218       var src = script.src;
  1219       var l = src.length;
  1220       if (src.substr(l - expectedBase.length) == expectedBase) {
  1221         basePath = src.substr(0, l - expectedBase.length);
  1225   return basePath;
  1226 };
  1228 var loadStandardVertexShader = function(gl) {
  1229   return loadShaderFromFile(
  1230       gl, getBasePath() + "vertexShader.vert", gl.VERTEX_SHADER);
  1231 };
  1233 var loadStandardFragmentShader = function(gl) {
  1234   return loadShaderFromFile(
  1235       gl, getBasePath() + "fragmentShader.frag", gl.FRAGMENT_SHADER);
  1236 };
  1238 /**
  1239  * Loads an image asynchronously.
  1240  * @param {string} url URL of image to load.
  1241  * @param {!function(!Element): void} callback Function to call
  1242  *     with loaded image.
  1243  */
  1244 var loadImageAsync = function(url, callback) {
  1245   var img = document.createElement('img');
  1246   img.onload = function() {
  1247     callback(img);
  1248   };
  1249   img.src = url;
  1250 };
  1252 /**
  1253  * Loads an array of images.
  1254  * @param {!Array.<string>} urls URLs of images to load.
  1255  * @param {!function(!{string, img}): void} callback. Callback
  1256  *     that gets passed map of urls to img tags.
  1257  */
  1258 var loadImagesAsync = function(urls, callback) {
  1259   var count = 1;
  1260   var images = { };
  1261   function countDown() {
  1262     --count;
  1263     if (count == 0) {
  1264       callback(images);
  1267   function imageLoaded(url) {
  1268     return function(img) {
  1269       images[url] = img;
  1270       countDown();
  1273   for (var ii = 0; ii < urls.length; ++ii) {
  1274     ++count;
  1275     loadImageAsync(urls[ii], imageLoaded(urls[ii]));
  1277   countDown();
  1278 };
  1280 var getUrlArguments = function() {
  1281   var args = {};
  1282   try {
  1283     var s = window.location.href;
  1284     var q = s.indexOf("?");
  1285     var e = s.indexOf("#");
  1286     if (e < 0) {
  1287       e = s.length;
  1289     var query = s.substring(q + 1, e);
  1290     var pairs = query.split("&");
  1291     for (var ii = 0; ii < pairs.length; ++ii) {
  1292       var keyValue = pairs[ii].split("=");
  1293       var key = keyValue[0];
  1294       var value = decodeURIComponent(keyValue[1]);
  1295       args[key] = value;
  1297   } catch (e) {
  1298     throw "could not parse url";
  1300   return args;
  1301 };
  1303 var makeImage = function(canvas) {
  1304   var img = document.createElement('img');
  1305   img.src = canvas.toDataURL();
  1306   return img;
  1307 };
  1309 var insertImage = function(element, caption, img) {
  1310   var div = document.createElement("div");
  1311   div.appendChild(img);
  1312   var label = document.createElement("div");
  1313   label.appendChild(document.createTextNode(caption));
  1314   div.appendChild(label);
  1315    element.appendChild(div);
  1316 };
  1318 var addShaderSource = function(element, label, source) {
  1319   var div = document.createElement("div");
  1320   var s = document.createElement("pre");
  1321   s.className = "shader-source";
  1322   s.style.display = "none";
  1323   var ol = document.createElement("ol");
  1324   //s.appendChild(document.createTextNode(source));
  1325   var lines = source.split("\n");
  1326   for (var ii = 0; ii < lines.length; ++ii) {
  1327     var line = lines[ii];
  1328     var li = document.createElement("li");
  1329     li.appendChild(document.createTextNode(line));
  1330     ol.appendChild(li);
  1332   s.appendChild(ol);
  1333   var l = document.createElement("a");
  1334   l.href = "show-shader-source";
  1335   l.appendChild(document.createTextNode(label));
  1336   l.addEventListener('click', function(event) {
  1337       if (event.preventDefault) {
  1338         event.preventDefault();
  1340       s.style.display = (s.style.display == 'none') ? 'block' : 'none';
  1341       return false;
  1342     }, false);
  1343   div.appendChild(l);
  1344   div.appendChild(s);
  1345   element.appendChild(div);
  1348 return {
  1349   addShaderSource: addShaderSource,
  1350   clearAndDrawUnitQuad : clearAndDrawUnitQuad,
  1351   create3DContext: create3DContext,
  1352   create3DContextWithWrapperThatThrowsOnGLError:
  1353     create3DContextWithWrapperThatThrowsOnGLError,
  1354   checkCanvas: checkCanvas,
  1355   checkCanvasRect: checkCanvasRect,
  1356   createColoredTexture: createColoredTexture,
  1357   drawQuad: drawQuad,
  1358   drawUnitQuad: drawUnitQuad,
  1359   endsWith: endsWith,
  1360   getExtensionWithKnownPrefixes: getExtensionWithKnownPrefixes,
  1361   getFileListAsync: getFileListAsync,
  1362   getLastError: getLastError,
  1363   getScript: getScript,
  1364   getSupportedExtensionWithKnownPrefixes: getSupportedExtensionWithKnownPrefixes,
  1365   getUrlArguments: getUrlArguments,
  1366   glEnumToString: glEnumToString,
  1367   glErrorShouldBe: glErrorShouldBe,
  1368   fillTexture: fillTexture,
  1369   insertImage: insertImage,
  1370   loadImageAsync: loadImageAsync,
  1371   loadImagesAsync: loadImagesAsync,
  1372   loadProgram: loadProgram,
  1373   loadProgramFromFile: loadProgramFromFile,
  1374   loadProgramFromScript: loadProgramFromScript,
  1375   loadProgramFromScriptExpectError: loadProgramFromScriptExpectError,
  1376   loadShader: loadShader,
  1377   loadShaderFromFile: loadShaderFromFile,
  1378   loadShaderFromScript: loadShaderFromScript,
  1379   loadStandardProgram: loadStandardProgram,
  1380   loadStandardVertexShader: loadStandardVertexShader,
  1381   loadStandardFragmentShader: loadStandardFragmentShader,
  1382   loadTextFileAsync: loadTextFileAsync,
  1383   loadTexture: loadTexture,
  1384   log: log,
  1385   loggingOff: loggingOff,
  1386   makeImage: makeImage,
  1387   error: error,
  1388   setupProgram: setupProgram,
  1389   setupQuad: setupQuad,
  1390   setupSimpleTextureFragmentShader: setupSimpleTextureFragmentShader,
  1391   setupSimpleTextureProgram: setupSimpleTextureProgram,
  1392   setupSimpleTextureVertexShader: setupSimpleTextureVertexShader,
  1393   setupTexturedQuad: setupTexturedQuad,
  1394   setupTexturedQuadWithTexCoords: setupTexturedQuadWithTexCoords,
  1395   setupUnitQuad: setupUnitQuad,
  1396   setupUnitQuadWithTexCoords: setupUnitQuadWithTexCoords,
  1397   setupWebGLWithShaders: setupWebGLWithShaders,
  1398   startsWith: startsWith,
  1399   shouldGenerateGLError: shouldGenerateGLError,
  1400   readFile: readFile,
  1401   readFileList: readFileList,
  1403   none: false
  1404 };
  1406 }());

mercurial