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

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/content/canvas/test/webgl-conformance/conformance/resources/webgl-test-utils.js	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1408 @@
     1.4 +// Copyright (c) 2011 The Chromium Authors. All rights reserved.
     1.5 +// Use of this source code is governed by a BSD-style license that can be
     1.6 +// found in the LICENSE file.
     1.7 +
     1.8 +WebGLTestUtils = (function() {
     1.9 +
    1.10 +/**
    1.11 + * Wrapped logging function.
    1.12 + * @param {string} msg The message to log.
    1.13 + */
    1.14 +var log = function(msg) {
    1.15 +  if (window.console && window.console.log) {
    1.16 +    window.console.log(msg);
    1.17 +  }
    1.18 +};
    1.19 +
    1.20 +/**
    1.21 + * Wrapped logging function.
    1.22 + * @param {string} msg The message to log.
    1.23 + */
    1.24 +var error = function(msg) {
    1.25 +  if (window.console) {
    1.26 +    if (window.console.error) {
    1.27 +      window.console.error(msg);
    1.28 +    }
    1.29 +    else if (window.console.log) {
    1.30 +      window.console.log(msg);
    1.31 +    }
    1.32 +  }
    1.33 +};
    1.34 +
    1.35 +/**
    1.36 + * Turn off all logging.
    1.37 + */
    1.38 +var loggingOff = function() {
    1.39 +  log = function() {};
    1.40 +  error = function() {};
    1.41 +};
    1.42 +
    1.43 +/**
    1.44 + * Converts a WebGL enum to a string
    1.45 + * @param {!WebGLContext} gl The WebGLContext to use.
    1.46 + * @param {number} value The enum value.
    1.47 + * @return {string} The enum as a string.
    1.48 + */
    1.49 +var glEnumToString = function(gl, value) {
    1.50 +  for (var p in gl) {
    1.51 +    if (gl[p] == value) {
    1.52 +      return p;
    1.53 +    }
    1.54 +  }
    1.55 +  return "0x" + value.toString(16);
    1.56 +};
    1.57 +
    1.58 +var lastError = "";
    1.59 +
    1.60 +/**
    1.61 + * Returns the last compiler/linker error.
    1.62 + * @return {string} The last compiler/linker error.
    1.63 + */
    1.64 +var getLastError = function() {
    1.65 +  return lastError;
    1.66 +};
    1.67 +
    1.68 +/**
    1.69 + * Whether a haystack ends with a needle.
    1.70 + * @param {string} haystack String to search
    1.71 + * @param {string} needle String to search for.
    1.72 + * @param {boolean} True if haystack ends with needle.
    1.73 + */
    1.74 +var endsWith = function(haystack, needle) {
    1.75 +  return haystack.substr(haystack.length - needle.length) === needle;
    1.76 +};
    1.77 +
    1.78 +/**
    1.79 + * Whether a haystack starts with a needle.
    1.80 + * @param {string} haystack String to search
    1.81 + * @param {string} needle String to search for.
    1.82 + * @param {boolean} True if haystack starts with needle.
    1.83 + */
    1.84 +var startsWith = function(haystack, needle) {
    1.85 +  return haystack.substr(0, needle.length) === needle;
    1.86 +};
    1.87 +
    1.88 +/**
    1.89 + * A vertex shader for a single texture.
    1.90 + * @type {string}
    1.91 + */
    1.92 +var simpleTextureVertexShader = [
    1.93 +  'attribute vec4 vPosition;',
    1.94 +  'attribute vec2 texCoord0;',
    1.95 +  'varying vec2 texCoord;',
    1.96 +  'void main() {',
    1.97 +  '    gl_Position = vPosition;',
    1.98 +  '    texCoord = texCoord0;',
    1.99 +  '}'].join('\n');
   1.100 +
   1.101 +/**
   1.102 + * A fragment shader for a single texture.
   1.103 + * @type {string}
   1.104 + */
   1.105 +var simpleTextureFragmentShader = [
   1.106 +  'precision mediump float;',
   1.107 +  'uniform sampler2D tex;',
   1.108 +  'varying vec2 texCoord;',
   1.109 +  'void main() {',
   1.110 +  '    gl_FragData[0] = texture2D(tex, texCoord);',
   1.111 +  '}'].join('\n');
   1.112 +
   1.113 +/**
   1.114 + * Creates a simple texture vertex shader.
   1.115 + * @param {!WebGLContext} gl The WebGLContext to use.
   1.116 + * @return {!WebGLShader}
   1.117 + */
   1.118 +var setupSimpleTextureVertexShader = function(gl) {
   1.119 +    return loadShader(gl, simpleTextureVertexShader, gl.VERTEX_SHADER);
   1.120 +};
   1.121 +
   1.122 +/**
   1.123 + * Creates a simple texture fragment shader.
   1.124 + * @param {!WebGLContext} gl The WebGLContext to use.
   1.125 + * @return {!WebGLShader}
   1.126 + */
   1.127 +var setupSimpleTextureFragmentShader = function(gl) {
   1.128 +    return loadShader(
   1.129 +        gl, simpleTextureFragmentShader, gl.FRAGMENT_SHADER);
   1.130 +};
   1.131 +
   1.132 +/**
   1.133 + * Creates a program, attaches shaders, binds attrib locations, links the
   1.134 + * program and calls useProgram.
   1.135 + * @param {!Array.<!WebGLShader|string>} shaders The shaders to
   1.136 + *        attach, or the source, or the id of a script to get
   1.137 + *        the source from.
   1.138 + * @param {!Array.<string>} opt_attribs The attribs names.
   1.139 + * @param {!Array.<number>} opt_locations The locations for the attribs.
   1.140 + */
   1.141 +var setupProgram = function(gl, shaders, opt_attribs, opt_locations) {
   1.142 +  var realShaders = [];
   1.143 +  var program = gl.createProgram();
   1.144 +  for (var ii = 0; ii < shaders.length; ++ii) {
   1.145 +    var shader = shaders[ii];
   1.146 +    if (typeof shader == 'string') {
   1.147 +      var element = document.getElementById(shader);
   1.148 +      if (element) {
   1.149 +        shader = loadShaderFromScript(gl, shader);
   1.150 +      } else {
   1.151 +        shader = loadShader(gl, shader, ii ? gl.FRAGMENT_SHADER : gl.VERTEX_SHADER);
   1.152 +      }
   1.153 +    }
   1.154 +    gl.attachShader(program, shader);
   1.155 +  }
   1.156 +  if (opt_attribs) {
   1.157 +    for (var ii = 0; ii < opt_attribs.length; ++ii) {
   1.158 +      gl.bindAttribLocation(
   1.159 +          program,
   1.160 +          opt_locations ? opt_locations[ii] : ii,
   1.161 +          opt_attribs[ii]);
   1.162 +    }
   1.163 +  }
   1.164 +  gl.linkProgram(program);
   1.165 +
   1.166 +  // Check the link status
   1.167 +  var linked = gl.getProgramParameter(program, gl.LINK_STATUS);
   1.168 +  if (!linked) {
   1.169 +      // something went wrong with the link
   1.170 +      lastError = gl.getProgramInfoLog (program);
   1.171 +      error("Error in program linking:" + lastError);
   1.172 +
   1.173 +      gl.deleteProgram(program);
   1.174 +      return null;
   1.175 +  }
   1.176 +
   1.177 +  gl.useProgram(program);
   1.178 +  return program;
   1.179 +};
   1.180 +
   1.181 +/**
   1.182 + * Creates a simple texture program.
   1.183 + * @param {!WebGLContext} gl The WebGLContext to use.
   1.184 + * @param {number} opt_positionLocation The attrib location for position.
   1.185 + * @param {number} opt_texcoordLocation The attrib location for texture coords.
   1.186 + * @return {WebGLProgram}
   1.187 + */
   1.188 +var setupSimpleTextureProgram = function(
   1.189 +    gl, opt_positionLocation, opt_texcoordLocation) {
   1.190 +  opt_positionLocation = opt_positionLocation || 0;
   1.191 +  opt_texcoordLocation = opt_texcoordLocation || 1;
   1.192 +  var vs = setupSimpleTextureVertexShader(gl);
   1.193 +  var fs = setupSimpleTextureFragmentShader(gl);
   1.194 +  if (!vs || !fs) {
   1.195 +    return null;
   1.196 +  }
   1.197 +  var program = setupProgram(
   1.198 +      gl,
   1.199 +      [vs, fs],
   1.200 +      ['vPosition', 'texCoord0'],
   1.201 +      [opt_positionLocation, opt_texcoordLocation]);
   1.202 +  if (!program) {
   1.203 +    gl.deleteShader(fs);
   1.204 +    gl.deleteShader(vs);
   1.205 +  }
   1.206 +  gl.useProgram(program);
   1.207 +  return program;
   1.208 +};
   1.209 +
   1.210 +/**
   1.211 + * Creates buffers for a textured unit quad and attaches them to vertex attribs.
   1.212 + * @param {!WebGLContext} gl The WebGLContext to use.
   1.213 + * @param {number} opt_positionLocation The attrib location for position.
   1.214 + * @param {number} opt_texcoordLocation The attrib location for texture coords.
   1.215 + * @return {!Array.<WebGLBuffer>} The buffer objects that were
   1.216 + *      created.
   1.217 + */
   1.218 +var setupUnitQuad = function(gl, opt_positionLocation, opt_texcoordLocation) {
   1.219 +  return setupUnitQuadWithTexCoords(gl, [ 0.0, 0.0 ], [ 1.0, 1.0 ],
   1.220 +                                    opt_positionLocation, opt_texcoordLocation);
   1.221 +};
   1.222 +
   1.223 +/**
   1.224 + * Draws a previously setupUnitQuad.
   1.225 + * @param {!WebGLContext} gl The WebGLContext to use.
   1.226 + */
   1.227 +var drawUnitQuad = function(gl) {
   1.228 +  gl.drawArrays(gl.TRIANGLES, 0, 6);
   1.229 +};
   1.230 +
   1.231 +/**
   1.232 + * Clears then Draws a previously setupUnitQuad.
   1.233 + * @param {!WebGLContext} gl The WebGLContext to use.
   1.234 + * @param {!Array.<number>} opt_color The color to fill clear with before
   1.235 + * drawing. A 4 element array where each element is in the range 0 to
   1.236 + * 255. Default [255, 255, 255, 255]
   1.237 + */
   1.238 +var clearAndDrawUnitQuad = function(gl, opt_color) {
   1.239 +  opt_color = opt_color || [255, 255, 255, 255];
   1.240 +
   1.241 +  // Save and restore.
   1.242 +  var prevClearColor = gl.getParameter(gl.COLOR_CLEAR_VALUE);
   1.243 +
   1.244 +  gl.clearColor(opt_color[0] / 255,
   1.245 +                opt_color[1] / 255,
   1.246 +                opt_color[2] / 255,
   1.247 +                opt_color[3] / 255);
   1.248 +  gl.clear(gl.COLOR_BUFFER_BIT);
   1.249 +  drawUnitQuad(gl);
   1.250 +
   1.251 +  gl.clearColor(prevClearColor[0],
   1.252 +                prevClearColor[1],
   1.253 +                prevClearColor[2],
   1.254 +                prevClearColor[3]);
   1.255 +};
   1.256 +
   1.257 +/**
   1.258 + * Creates buffers for a textured unit quad with specified lower left
   1.259 + * and upper right texture coordinates, and attaches them to vertex
   1.260 + * attribs.
   1.261 + * @param {!WebGLContext} gl The WebGLContext to use.
   1.262 + * @param {!Array.<number>} lowerLeftTexCoords The texture coordinates for the lower left corner.
   1.263 + * @param {!Array.<number>} upperRightTexCoords The texture coordinates for the upper right corner.
   1.264 + * @param {number} opt_positionLocation The attrib location for position.
   1.265 + * @param {number} opt_texcoordLocation The attrib location for texture coords.
   1.266 + * @return {!Array.<WebGLBuffer>} The buffer objects that were
   1.267 + *      created.
   1.268 + */
   1.269 +var setupUnitQuadWithTexCoords = function(
   1.270 +    gl, lowerLeftTexCoords, upperRightTexCoords,
   1.271 +    opt_positionLocation, opt_texcoordLocation) {
   1.272 +  opt_positionLocation = opt_positionLocation || 0;
   1.273 +  opt_texcoordLocation = opt_texcoordLocation || 1;
   1.274 +  var objects = [];
   1.275 +
   1.276 +  var vertexObject = gl.createBuffer();
   1.277 +  gl.bindBuffer(gl.ARRAY_BUFFER, vertexObject);
   1.278 +  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
   1.279 +       1.0,  1.0, 0.0,
   1.280 +      -1.0,  1.0, 0.0,
   1.281 +      -1.0, -1.0, 0.0,
   1.282 +       1.0,  1.0, 0.0,
   1.283 +      -1.0, -1.0, 0.0,
   1.284 +       1.0, -1.0, 0.0]), gl.STATIC_DRAW);
   1.285 +  gl.enableVertexAttribArray(opt_positionLocation);
   1.286 +  gl.vertexAttribPointer(opt_positionLocation, 3, gl.FLOAT, false, 0, 0);
   1.287 +  objects.push(vertexObject);
   1.288 +
   1.289 +  var llx = lowerLeftTexCoords[0];
   1.290 +  var lly = lowerLeftTexCoords[1];
   1.291 +  var urx = upperRightTexCoords[0];
   1.292 +  var ury = upperRightTexCoords[1];
   1.293 +
   1.294 +  var vertexObject = gl.createBuffer();
   1.295 +  gl.bindBuffer(gl.ARRAY_BUFFER, vertexObject);
   1.296 +  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
   1.297 +      urx, ury,
   1.298 +      llx, ury,
   1.299 +      llx, lly,
   1.300 +      urx, ury,
   1.301 +      llx, lly,
   1.302 +      urx, lly]), gl.STATIC_DRAW);
   1.303 +  gl.enableVertexAttribArray(opt_texcoordLocation);
   1.304 +  gl.vertexAttribPointer(opt_texcoordLocation, 2, gl.FLOAT, false, 0, 0);
   1.305 +  objects.push(vertexObject);
   1.306 +  return objects;
   1.307 +};
   1.308 +
   1.309 +/**
   1.310 + * Creates a program and buffers for rendering a textured quad.
   1.311 + * @param {!WebGLContext} gl The WebGLContext to use.
   1.312 + * @param {number} opt_positionLocation The attrib location for position.
   1.313 + * @param {number} opt_texcoordLocation The attrib location for texture coords.
   1.314 + * @return {!WebGLProgram}
   1.315 + */
   1.316 +var setupTexturedQuad = function(
   1.317 +    gl, opt_positionLocation, opt_texcoordLocation) {
   1.318 +  var program = setupSimpleTextureProgram(
   1.319 +      gl, opt_positionLocation, opt_texcoordLocation);
   1.320 +  setupUnitQuad(gl, opt_positionLocation, opt_texcoordLocation);
   1.321 +  return program;
   1.322 +};
   1.323 +
   1.324 +/**
   1.325 + * Creates a program and buffers for rendering a textured quad with
   1.326 + * specified lower left and upper right texture coordinates.
   1.327 + * @param {!WebGLContext} gl The WebGLContext to use.
   1.328 + * @param {!Array.<number>} lowerLeftTexCoords The texture coordinates for the lower left corner.
   1.329 + * @param {!Array.<number>} upperRightTexCoords The texture coordinates for the upper right corner.
   1.330 + * @param {number} opt_positionLocation The attrib location for position.
   1.331 + * @param {number} opt_texcoordLocation The attrib location for texture coords.
   1.332 + * @return {!WebGLProgram}
   1.333 + */
   1.334 +var setupTexturedQuadWithTexCoords = function(
   1.335 +    gl, lowerLeftTexCoords, upperRightTexCoords,
   1.336 +    opt_positionLocation, opt_texcoordLocation) {
   1.337 +  var program = setupSimpleTextureProgram(
   1.338 +      gl, opt_positionLocation, opt_texcoordLocation);
   1.339 +  setupUnitQuadWithTexCoords(gl, lowerLeftTexCoords, upperRightTexCoords,
   1.340 +                             opt_positionLocation, opt_texcoordLocation);
   1.341 +  return program;
   1.342 +};
   1.343 +
   1.344 +/**
   1.345 + * Creates a unit quad with only positions of a given resolution.
   1.346 + * @param {!WebGLContext} gl The WebGLContext to use.
   1.347 + * @param {number} gridRes The resolution of the mesh grid, expressed in the number of triangles across and down.
   1.348 + * @param {number} opt_positionLocation The attrib location for position.
   1.349 + */
   1.350 +var setupQuad = function (
   1.351 +    gl, gridRes, opt_positionLocation, opt_flipOddTriangles) {
   1.352 +  var positionLocation = opt_positionLocation || 0;
   1.353 +  var objects = [];
   1.354 +
   1.355 +  var vertsAcross = gridRes + 1;
   1.356 +  var numVerts = vertsAcross * vertsAcross;
   1.357 +  var positions = new Float32Array(numVerts * 3);
   1.358 +  var indices = new Uint16Array(6 * gridRes * gridRes);
   1.359 +
   1.360 +  var poffset = 0;
   1.361 +
   1.362 +  for (var yy = 0; yy <= gridRes; ++yy) {
   1.363 +    for (var xx = 0; xx <= gridRes; ++xx) {
   1.364 +      positions[poffset + 0] = -1 + 2 * xx / gridRes;
   1.365 +      positions[poffset + 1] = -1 + 2 * yy / gridRes;
   1.366 +      positions[poffset + 2] = 0;
   1.367 +
   1.368 +      poffset += 3;
   1.369 +    }
   1.370 +  }
   1.371 +
   1.372 +  var tbase = 0;
   1.373 +  for (var yy = 0; yy < gridRes; ++yy) {
   1.374 +    var index = yy * vertsAcross;
   1.375 +    for (var xx = 0; xx < gridRes; ++xx) {
   1.376 +      indices[tbase + 0] = index + 0;
   1.377 +      indices[tbase + 1] = index + 1;
   1.378 +      indices[tbase + 2] = index + vertsAcross;
   1.379 +      indices[tbase + 3] = index + vertsAcross;
   1.380 +      indices[tbase + 4] = index + 1;
   1.381 +      indices[tbase + 5] = index + vertsAcross + 1;
   1.382 +
   1.383 +      if (opt_flipOddTriangles) {
   1.384 +        indices[tbase + 4] = index + vertsAcross + 1;
   1.385 +        indices[tbase + 5] = index + 1;
   1.386 +      }
   1.387 +
   1.388 +      index += 1;
   1.389 +      tbase += 6;
   1.390 +    }
   1.391 +  }
   1.392 +
   1.393 +  var buf = gl.createBuffer();
   1.394 +  gl.bindBuffer(gl.ARRAY_BUFFER, buf);
   1.395 +  gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW);
   1.396 +  gl.enableVertexAttribArray(positionLocation);
   1.397 +  gl.vertexAttribPointer(positionLocation, 3, gl.FLOAT, false, 0, 0);
   1.398 +  objects.push(buf);
   1.399 +
   1.400 +  var buf = gl.createBuffer();
   1.401 +  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buf);
   1.402 +  gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);
   1.403 +  objects.push(buf);
   1.404 +
   1.405 +  return objects;
   1.406 +};
   1.407 +
   1.408 +/**
   1.409 + * Fills the given texture with a solid color
   1.410 + * @param {!WebGLContext} gl The WebGLContext to use.
   1.411 + * @param {!WebGLTexture} tex The texture to fill.
   1.412 + * @param {number} width The width of the texture to create.
   1.413 + * @param {number} height The height of the texture to create.
   1.414 + * @param {!Array.<number>} color The color to fill with. A 4 element array
   1.415 + *        where each element is in the range 0 to 255.
   1.416 + * @param {number} opt_level The level of the texture to fill. Default = 0.
   1.417 + */
   1.418 +var fillTexture = function(gl, tex, width, height, color, opt_level) {
   1.419 +  opt_level = opt_level || 0;
   1.420 +  var numPixels = width * height;
   1.421 +  var size = numPixels * 4;
   1.422 +  var buf = new Uint8Array(size);
   1.423 +  for (var ii = 0; ii < numPixels; ++ii) {
   1.424 +    var off = ii * 4;
   1.425 +    buf[off + 0] = color[0];
   1.426 +    buf[off + 1] = color[1];
   1.427 +    buf[off + 2] = color[2];
   1.428 +    buf[off + 3] = color[3];
   1.429 +  }
   1.430 +  gl.bindTexture(gl.TEXTURE_2D, tex);
   1.431 +  gl.texImage2D(
   1.432 +      gl.TEXTURE_2D, opt_level, gl.RGBA, width, height, 0,
   1.433 +      gl.RGBA, gl.UNSIGNED_BYTE, buf);
   1.434 +  };
   1.435 +
   1.436 +/**
   1.437 + * Creates a textures and fills it with a solid color
   1.438 + * @param {!WebGLContext} gl The WebGLContext to use.
   1.439 + * @param {number} width The width of the texture to create.
   1.440 + * @param {number} height The height of the texture to create.
   1.441 + * @param {!Array.<number>} color The color to fill with. A 4 element array
   1.442 + *        where each element is in the range 0 to 255.
   1.443 + * @return {!WebGLTexture}
   1.444 + */
   1.445 +var createColoredTexture = function(gl, width, height, color) {
   1.446 +  var tex = gl.createTexture();
   1.447 +  fillTexture(gl, tex, width, height, color);
   1.448 +  return tex;
   1.449 +};
   1.450 +
   1.451 +/**
   1.452 + * Draws a previously setup quad.
   1.453 + * @param {!WebGLContext} gl The WebGLContext to use.
   1.454 + * @param {!Array.<number>} opt_color The color to fill clear with before
   1.455 + *        drawing. A 4 element array where each element is in the range 0 to
   1.456 + *        255. Default [255, 255, 255, 255]
   1.457 + */
   1.458 +var drawQuad = function(gl, opt_color) {
   1.459 +  opt_color = opt_color || [255, 255, 255, 255];
   1.460 +  gl.clearColor(
   1.461 +      opt_color[0] / 255,
   1.462 +      opt_color[1] / 255,
   1.463 +      opt_color[2] / 255,
   1.464 +      opt_color[3] / 255);
   1.465 +  gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
   1.466 +  gl.drawArrays(gl.TRIANGLES, 0, 6);
   1.467 +};
   1.468 +
   1.469 +/**
   1.470 + * Checks that a portion of a canvas is 1 color.
   1.471 + * @param {!WebGLContext} gl The WebGLContext to use.
   1.472 + * @param {number} x left corner of region to check.
   1.473 + * @param {number} y bottom corner of region to check.
   1.474 + * @param {number} width width of region to check.
   1.475 + * @param {number} height width of region to check.
   1.476 + * @param {!Array.<number>} color The color to fill clear with before drawing. A
   1.477 + *        4 element array where each element is in the range 0 to 255.
   1.478 + * @param {string} msg Message to associate with success. Eg ("should be red").
   1.479 + * @param {number} errorRange Optional. Acceptable error in
   1.480 + *        color checking. 0 by default.
   1.481 + */
   1.482 +var checkCanvasRect = function(gl, x, y, width, height, color, msg, errorRange) {
   1.483 +  errorRange = errorRange || 0;
   1.484 +  var buf = new Uint8Array(width * height * 4);
   1.485 +  gl.readPixels(x, y, width, height, gl.RGBA, gl.UNSIGNED_BYTE, buf);
   1.486 +  for (var i = 0; i < width * height; ++i) {
   1.487 +    var offset = i * 4;
   1.488 +    for (var j = 0; j < color.length; ++j) {
   1.489 +      if (Math.abs(buf[offset + j] - color[j]) > errorRange) {
   1.490 +        var was = buf[offset + 0].toString();
   1.491 +        for (j = 1; j < color.length; ++j) {
   1.492 +          was += "," + buf[offset + j];
   1.493 +        }
   1.494 +
   1.495 +        var cv = document.createElement('canvas');
   1.496 +        cv.height = height;
   1.497 +        cv.width = width;
   1.498 +        var ctx = cv.getContext('2d');
   1.499 +        ctx.fillStyle="rgba(" + color[0] + ", " + color[1] + ", " + color[2] + ", 255)";
   1.500 +        ctx.fillRect(0, 0, width, height);
   1.501 +        testFailedRender(msg, ctx, buf, width, height);
   1.502 +
   1.503 +        debug('at (' + (i % width) + ', ' + Math.floor(i / width) +
   1.504 +              ') expected: ' + color + ' was ' + was);
   1.505 +        return;
   1.506 +      }
   1.507 +    }
   1.508 +  }
   1.509 +  testPassed(msg);
   1.510 +};
   1.511 +
   1.512 +/**
   1.513 + * Checks that an entire canvas is 1 color.
   1.514 + * @param {!WebGLContext} gl The WebGLContext to use.
   1.515 + * @param {!Array.<number>} color The color to fill clear with before drawing. A
   1.516 + *        4 element array where each element is in the range 0 to 255.
   1.517 + * @param {string} msg Message to associate with success. Eg ("should be red").
   1.518 + * @param {number} errorRange Optional. Acceptable error in
   1.519 + *        color checking. 0 by default.
   1.520 + */
   1.521 +var checkCanvas = function(gl, color, msg, errorRange) {
   1.522 +  checkCanvasRect(gl, 0, 0, gl.canvas.width, gl.canvas.height, color, msg, errorRange);
   1.523 +};
   1.524 +
   1.525 +/**
   1.526 + * Loads a texture, calls callback when finished.
   1.527 + * @param {!WebGLContext} gl The WebGLContext to use.
   1.528 + * @param {string} url URL of image to load
   1.529 + * @param {function(!Image): void} callback Function that gets called after
   1.530 + *        image has loaded
   1.531 + * @return {!WebGLTexture} The created texture.
   1.532 + */
   1.533 +var loadTexture = function(gl, url, callback) {
   1.534 +    var texture = gl.createTexture();
   1.535 +    gl.bindTexture(gl.TEXTURE_2D, texture);
   1.536 +    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
   1.537 +    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
   1.538 +    var image = new Image();
   1.539 +    image.onload = function() {
   1.540 +        gl.bindTexture(gl.TEXTURE_2D, texture);
   1.541 +        gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
   1.542 +        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
   1.543 +        callback(image);
   1.544 +    };
   1.545 +    image.src = url;
   1.546 +    return texture;
   1.547 +};
   1.548 +
   1.549 +/**
   1.550 + * Creates a webgl context.
   1.551 + * @param {!Canvas|string} opt_canvas The canvas tag to get
   1.552 + *     context from. If one is not passed in one will be
   1.553 + *     created. If it's a string it's assumed to be the id of a
   1.554 + *     canvas.
   1.555 + * @return {!WebGLContext} The created context.
   1.556 + */
   1.557 +var create3DContext = function(opt_canvas, opt_attributes) {
   1.558 +  opt_canvas = opt_canvas || document.createElement("canvas");
   1.559 +  if (typeof opt_canvas == 'string') {
   1.560 +    opt_canvas = document.getElementById(opt_canvas);
   1.561 +  }
   1.562 +  var context = null;
   1.563 +  var names = ["webgl", "experimental-webgl"];
   1.564 +  for (var i = 0; i < names.length; ++i) {
   1.565 +    try {
   1.566 +      context = opt_canvas.getContext(names[i], opt_attributes);
   1.567 +    } catch (e) {
   1.568 +    }
   1.569 +    if (context) {
   1.570 +      break;
   1.571 +    }
   1.572 +  }
   1.573 +  if (!context) {
   1.574 +    testFailed("Unable to fetch WebGL rendering context for Canvas");
   1.575 +  }
   1.576 +  return context;
   1.577 +}
   1.578 +
   1.579 +/**
   1.580 + * Gets a GLError value as a string.
   1.581 + * @param {!WebGLContext} gl The WebGLContext to use.
   1.582 + * @param {number} err The webgl error as retrieved from gl.getError().
   1.583 + * @return {string} the error as a string.
   1.584 + */
   1.585 +var getGLErrorAsString = function(gl, err) {
   1.586 +  if (err === gl.NO_ERROR) {
   1.587 +    return "NO_ERROR";
   1.588 +  }
   1.589 +  for (var name in gl) {
   1.590 +    if (gl[name] === err) {
   1.591 +      return name;
   1.592 +    }
   1.593 +  }
   1.594 +  return err.toString();
   1.595 +};
   1.596 +
   1.597 +/**
   1.598 + * Wraps a WebGL function with a function that throws an exception if there is
   1.599 + * an error.
   1.600 + * @param {!WebGLContext} gl The WebGLContext to use.
   1.601 + * @param {string} fname Name of function to wrap.
   1.602 + * @return {function} The wrapped function.
   1.603 + */
   1.604 +var createGLErrorWrapper = function(context, fname) {
   1.605 +  return function() {
   1.606 +    var rv = context[fname].apply(context, arguments);
   1.607 +    var err = context.getError();
   1.608 +    if (err != 0)
   1.609 +      throw "GL error " + getGLErrorAsString(err) + " in " + fname;
   1.610 +    return rv;
   1.611 +  };
   1.612 +};
   1.613 +
   1.614 +/**
   1.615 + * Creates a WebGL context where all functions are wrapped to throw an exception
   1.616 + * if there is an error.
   1.617 + * @param {!Canvas} canvas The HTML canvas to get a context from.
   1.618 + * @return {!Object} The wrapped context.
   1.619 + */
   1.620 +function create3DContextWithWrapperThatThrowsOnGLError(canvas) {
   1.621 +  var context = create3DContext(canvas);
   1.622 +  var wrap = {};
   1.623 +  for (var i in context) {
   1.624 +    try {
   1.625 +      if (typeof context[i] == 'function') {
   1.626 +        wrap[i] = createGLErrorWrapper(context, i);
   1.627 +      } else {
   1.628 +        wrap[i] = context[i];
   1.629 +      }
   1.630 +    } catch (e) {
   1.631 +      error("createContextWrapperThatThrowsOnGLError: Error accessing " + i);
   1.632 +    }
   1.633 +  }
   1.634 +  wrap.getError = function() {
   1.635 +      return context.getError();
   1.636 +  };
   1.637 +  return wrap;
   1.638 +};
   1.639 +
   1.640 +/**
   1.641 + * Tests that an evaluated expression generates a specific GL error.
   1.642 + * @param {!WebGLContext} gl The WebGLContext to use.
   1.643 + * @param {number} glError The expected gl error.
   1.644 + * @param {string} evalSTr The string to evaluate.
   1.645 + */
   1.646 +var shouldGenerateGLError = function(gl, glError, evalStr) {
   1.647 +  var exception;
   1.648 +  try {
   1.649 +    eval(evalStr);
   1.650 +  } catch (e) {
   1.651 +    exception = e;
   1.652 +  }
   1.653 +  if (exception) {
   1.654 +    testFailed(evalStr + " threw exception " + exception);
   1.655 +  } else {
   1.656 +    var err = gl.getError();
   1.657 +    if (err != glError) {
   1.658 +      testFailed(evalStr + " expected: " + getGLErrorAsString(gl, glError) + ". Was " + getGLErrorAsString(gl, err) + ".");
   1.659 +    } else {
   1.660 +      testPassed(evalStr + " was expected value: " + getGLErrorAsString(gl, glError) + ".");
   1.661 +    }
   1.662 +  }
   1.663 +};
   1.664 +
   1.665 +/**
   1.666 + * Tests that the first error GL returns is the specified error.
   1.667 + * @param {!WebGLContext} gl The WebGLContext to use.
   1.668 + * @param {number} glError The expected gl error.
   1.669 + * @param {string} opt_msg
   1.670 + */
   1.671 +var glErrorShouldBe = function(gl, glError, opt_msg) {
   1.672 +  opt_msg = opt_msg || "";
   1.673 +  var err = gl.getError();
   1.674 +  if (err != glError) {
   1.675 +    testFailed("getError expected: " + getGLErrorAsString(gl, glError) +
   1.676 +               ". Was " + getGLErrorAsString(gl, err) + " : " + opt_msg);
   1.677 +  } else {
   1.678 +    testPassed("getError was expected value: " +
   1.679 +                getGLErrorAsString(gl, glError) + " : " + opt_msg);
   1.680 +  }
   1.681 +};
   1.682 +
   1.683 +/**
   1.684 + * Links a WebGL program, throws if there are errors.
   1.685 + * @param {!WebGLContext} gl The WebGLContext to use.
   1.686 + * @param {!WebGLProgram} program The WebGLProgram to link.
   1.687 + * @param {function(string): void) opt_errorCallback callback for errors.
   1.688 + */
   1.689 +var linkProgram = function(gl, program, opt_errorCallback) {
   1.690 +  errFn = opt_errorCallback || testFailed;
   1.691 +  // Link the program
   1.692 +  gl.linkProgram(program);
   1.693 +
   1.694 +  // Check the link status
   1.695 +  var linked = gl.getProgramParameter(program, gl.LINK_STATUS);
   1.696 +  if (!linked) {
   1.697 +    // something went wrong with the link
   1.698 +    var error = gl.getProgramInfoLog (program);
   1.699 +
   1.700 +    errFn("Error in program linking:" + error);
   1.701 +
   1.702 +    gl.deleteProgram(program);
   1.703 +  }
   1.704 +};
   1.705 +
   1.706 +/**
   1.707 + * Sets up WebGL with shaders.
   1.708 + * @param {string} canvasName The id of the canvas.
   1.709 + * @param {string} vshader The id of the script tag that contains the vertex
   1.710 + *     shader source.
   1.711 + * @param {string} fshader The id of the script tag that contains the fragment
   1.712 + *     shader source.
   1.713 + * @param {!Array.<string>} attribs An array of attrib names used to bind
   1.714 + *     attribs to the ordinal of the name in this array.
   1.715 + * @param {!Array.<number>} opt_clearColor The color to cla
   1.716 + * @return {!WebGLContext} The created WebGLContext.
   1.717 + */
   1.718 +var setupWebGLWithShaders = function(
   1.719 +   canvasName, vshader, fshader, attribs) {
   1.720 +  var canvas = document.getElementById(canvasName);
   1.721 +  var gl = create3DContext(canvas);
   1.722 +  if (!gl) {
   1.723 +    testFailed("No WebGL context found");
   1.724 +  }
   1.725 +
   1.726 +  // create our shaders
   1.727 +  var vertexShader = loadShaderFromScript(gl, vshader);
   1.728 +  var fragmentShader = loadShaderFromScript(gl, fshader);
   1.729 +
   1.730 +  if (!vertexShader || !fragmentShader) {
   1.731 +    return null;
   1.732 +  }
   1.733 +
   1.734 +  // Create the program object
   1.735 +  program = gl.createProgram();
   1.736 +
   1.737 +  if (!program) {
   1.738 +    return null;
   1.739 +  }
   1.740 +
   1.741 +  // Attach our two shaders to the program
   1.742 +  gl.attachShader (program, vertexShader);
   1.743 +  gl.attachShader (program, fragmentShader);
   1.744 +
   1.745 +  // Bind attributes
   1.746 +  for (var i in attribs) {
   1.747 +    gl.bindAttribLocation (program, i, attribs[i]);
   1.748 +  }
   1.749 +
   1.750 +  linkProgram(gl, program);
   1.751 +
   1.752 +  gl.useProgram(program);
   1.753 +
   1.754 +  gl.clearColor(0,0,0,1);
   1.755 +  gl.clearDepth(1);
   1.756 +
   1.757 +  gl.enable(gl.DEPTH_TEST);
   1.758 +  gl.enable(gl.BLEND);
   1.759 +  gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
   1.760 +
   1.761 +  gl.program = program;
   1.762 +  return gl;
   1.763 +};
   1.764 +
   1.765 +/**
   1.766 + * Loads text from an external file. This function is synchronous.
   1.767 + * @param {string} url The url of the external file.
   1.768 + * @param {!function(bool, string): void} callback that is sent a bool for
   1.769 + *     success and the string.
   1.770 + */
   1.771 +var loadTextFileAsync = function(url, callback) {
   1.772 +  log ("loading: " + url);
   1.773 +  var error = 'loadTextFileSynchronous failed to load url "' + url + '"';
   1.774 +  var request;
   1.775 +  if (window.XMLHttpRequest) {
   1.776 +    request = new XMLHttpRequest();
   1.777 +    if (request.overrideMimeType) {
   1.778 +      request.overrideMimeType('text/plain');
   1.779 +    }
   1.780 +  } else {
   1.781 +    throw 'XMLHttpRequest is disabled';
   1.782 +  }
   1.783 +  try {
   1.784 +    request.open('GET', url, true);
   1.785 +    request.onreadystatechange = function() {
   1.786 +      if (request.readyState == 4) {
   1.787 +        var text = '';
   1.788 +        // HTTP reports success with a 200 status. The file protocol reports
   1.789 +        // success with zero. HTTP does not use zero as a status code (they
   1.790 +        // start at 100).
   1.791 +        // https://developer.mozilla.org/En/Using_XMLHttpRequest
   1.792 +        var success = request.status == 200 || request.status == 0;
   1.793 +        if (success) {
   1.794 +          text = request.responseText;
   1.795 +        }
   1.796 +        log("loaded: " + url);
   1.797 +        callback(success, text);
   1.798 +      }
   1.799 +    };
   1.800 +    request.send(null);
   1.801 +  } catch (e) {
   1.802 +    log("failed to load: " + url);
   1.803 +    callback(false, '');
   1.804 +  }
   1.805 +};
   1.806 +
   1.807 +// Add your prefix here.
   1.808 +var browserPrefixes = [
   1.809 +  "",
   1.810 +  "MOZ_",
   1.811 +  "OP_",
   1.812 +  "WEBKIT_"
   1.813 +];
   1.814 +
   1.815 +/**
   1.816 + * Given an extension name like WEBGL_compressed_texture_s3tc
   1.817 + * returns the name of the supported version extension, like
   1.818 + * WEBKIT_WEBGL_compressed_teture_s3tc
   1.819 + * @param {string} name Name of extension to look for
   1.820 + * @return {string} name of extension found or undefined if not
   1.821 + *     found.
   1.822 + */
   1.823 +var getSupportedExtensionWithKnownPrefixes = function(gl, name) {
   1.824 +  var supported = gl.getSupportedExtensions();
   1.825 +  for (var ii = 0; ii < browserPrefixes.length; ++ii) {
   1.826 +    var prefixedName = browserPrefixes[ii] + name;
   1.827 +    if (supported.indexOf(prefixedName) >= 0) {
   1.828 +      return prefixedName;
   1.829 +    }
   1.830 +  }
   1.831 +};
   1.832 +
   1.833 +/**
   1.834 + * Given an extension name like WEBGL_compressed_texture_s3tc
   1.835 + * returns the supported version extension, like
   1.836 + * WEBKIT_WEBGL_compressed_teture_s3tc
   1.837 + * @param {string} name Name of extension to look for
   1.838 + * @return {WebGLExtension} The extension or undefined if not
   1.839 + *     found.
   1.840 + */
   1.841 +var getExtensionWithKnownPrefixes = function(gl, name) {
   1.842 +  for (var ii = 0; ii < browserPrefixes.length; ++ii) {
   1.843 +    var prefixedName = browserPrefixes[ii] + name;
   1.844 +    var ext = gl.getExtension(prefixedName);
   1.845 +    if (ext) {
   1.846 +      return ext;
   1.847 +    }
   1.848 +  }
   1.849 +};
   1.850 +
   1.851 +/**
   1.852 + * Recursively loads a file as a list. Each line is parsed for a relative
   1.853 + * path. If the file ends in .txt the contents of that file is inserted in
   1.854 + * the list.
   1.855 + *
   1.856 + * @param {string} url The url of the external file.
   1.857 + * @param {!function(bool, Array<string>): void} callback that is sent a bool
   1.858 + *     for success and the array of strings.
   1.859 + */
   1.860 +var getFileListAsync = function(url, callback) {
   1.861 +  var files = [];
   1.862 +
   1.863 +  var getFileListImpl = function(url, callback) {
   1.864 +    var files = [];
   1.865 +    if (url.substr(url.length - 4) == '.txt') {
   1.866 +      loadTextFileAsync(url, function() {
   1.867 +        return function(success, text) {
   1.868 +          if (!success) {
   1.869 +            callback(false, '');
   1.870 +            return;
   1.871 +          }
   1.872 +          var lines = text.split('\n');
   1.873 +          var prefix = '';
   1.874 +          var lastSlash = url.lastIndexOf('/');
   1.875 +          if (lastSlash >= 0) {
   1.876 +            prefix = url.substr(0, lastSlash + 1);
   1.877 +          }
   1.878 +          var fail = false;
   1.879 +          var count = 1;
   1.880 +          var index = 0;
   1.881 +          for (var ii = 0; ii < lines.length; ++ii) {
   1.882 +            var str = lines[ii].replace(/^\s\s*/, '').replace(/\s\s*$/, '');
   1.883 +            if (str.length > 4 &&
   1.884 +                str[0] != '#' &&
   1.885 +                str[0] != ";" &&
   1.886 +                str.substr(0, 2) != "//") {
   1.887 +              var names = str.split(/ +/);
   1.888 +              new_url = prefix + str;
   1.889 +              if (names.length == 1) {
   1.890 +                new_url = prefix + str;
   1.891 +                ++count;
   1.892 +                getFileListImpl(new_url, function(index) {
   1.893 +                  return function(success, new_files) {
   1.894 +                    log("got files: " + new_files.length);
   1.895 +                    if (success) {
   1.896 +                      files[index] = new_files;
   1.897 +                    }
   1.898 +                    finish(success);
   1.899 +                  };
   1.900 +                }(index++));
   1.901 +              } else {
   1.902 +                var s = "";
   1.903 +                var p = "";
   1.904 +                for (var jj = 0; jj < names.length; ++jj) {
   1.905 +                  s += p + prefix + names[jj];
   1.906 +                  p = " ";
   1.907 +                }
   1.908 +                files[index++] = s;
   1.909 +              }
   1.910 +            }
   1.911 +          }
   1.912 +          finish(true);
   1.913 +
   1.914 +          function finish(success) {
   1.915 +            if (!success) {
   1.916 +              fail = true;
   1.917 +            }
   1.918 +            --count;
   1.919 +            log("count: " + count);
   1.920 +            if (!count) {
   1.921 +              callback(!fail, files);
   1.922 +            }
   1.923 +          }
   1.924 +        }
   1.925 +      }());
   1.926 +
   1.927 +    } else {
   1.928 +      files.push(url);
   1.929 +      callback(true, files);
   1.930 +    }
   1.931 +  };
   1.932 +
   1.933 +  getFileListImpl(url, function(success, files) {
   1.934 +    // flatten
   1.935 +    var flat = [];
   1.936 +    flatten(files);
   1.937 +    function flatten(files) {
   1.938 +      for (var ii = 0; ii < files.length; ++ii) {
   1.939 +        var value = files[ii];
   1.940 +        if (typeof(value) == "string") {
   1.941 +          flat.push(value);
   1.942 +        } else {
   1.943 +          flatten(value);
   1.944 +        }
   1.945 +      }
   1.946 +    }
   1.947 +    callback(success, flat);
   1.948 +  });
   1.949 +};
   1.950 +
   1.951 +/**
   1.952 + * Gets a file from a file/URL
   1.953 + * @param {string} file the URL of the file to get.
   1.954 + * @return {string} The contents of the file.
   1.955 + */
   1.956 +var readFile = function(file) {
   1.957 +  var xhr = new XMLHttpRequest();
   1.958 +  xhr.open("GET", file, false);
   1.959 +  xhr.send();
   1.960 +  return xhr.responseText.replace(/\r/g, "");
   1.961 +};
   1.962 +
   1.963 +var readFileList = function(url) {
   1.964 +  var files = [];
   1.965 +  if (url.substr(url.length - 4) == '.txt') {
   1.966 +    var lines = readFile(url).split('\n');
   1.967 +    var prefix = '';
   1.968 +    var lastSlash = url.lastIndexOf('/');
   1.969 +    if (lastSlash >= 0) {
   1.970 +      prefix = url.substr(0, lastSlash + 1);
   1.971 +    }
   1.972 +    for (var ii = 0; ii < lines.length; ++ii) {
   1.973 +      var str = lines[ii].replace(/^\s\s*/, '').replace(/\s\s*$/, '');
   1.974 +      if (str.length > 4 &&
   1.975 +          str[0] != '#' &&
   1.976 +          str[0] != ";" &&
   1.977 +          str.substr(0, 2) != "//") {
   1.978 +        var names = str.split(/ +/);
   1.979 +        if (names.length == 1) {
   1.980 +          new_url = prefix + str;
   1.981 +          files = files.concat(readFileList(new_url));
   1.982 +        } else {
   1.983 +          var s = "";
   1.984 +          var p = "";
   1.985 +          for (var jj = 0; jj < names.length; ++jj) {
   1.986 +            s += p + prefix + names[jj];
   1.987 +            p = " ";
   1.988 +          }
   1.989 +          files.push(s);
   1.990 +        }
   1.991 +      }
   1.992 +    }
   1.993 +  } else {
   1.994 +    files.push(url);
   1.995 +  }
   1.996 +  return files;
   1.997 +};
   1.998 +
   1.999 +/**
  1.1000 + * Loads a shader.
  1.1001 + * @param {!WebGLContext} gl The WebGLContext to use.
  1.1002 + * @param {string} shaderSource The shader source.
  1.1003 + * @param {number} shaderType The type of shader.
  1.1004 + * @param {function(string): void) opt_errorCallback callback for errors.
  1.1005 + * @return {!WebGLShader} The created shader.
  1.1006 + */
  1.1007 +var loadShader = function(gl, shaderSource, shaderType, opt_errorCallback) {
  1.1008 +  var errFn = opt_errorCallback || error;
  1.1009 +  // Create the shader object
  1.1010 +  var shader = gl.createShader(shaderType);
  1.1011 +  if (shader == null) {
  1.1012 +    errFn("*** Error: unable to create shader '"+shaderSource+"'");
  1.1013 +    return null;
  1.1014 +  }
  1.1015 +
  1.1016 +  // Load the shader source
  1.1017 +  gl.shaderSource(shader, shaderSource);
  1.1018 +  var err = gl.getError();
  1.1019 +  if (err != gl.NO_ERROR) {
  1.1020 +    errFn("*** Error loading shader '" + shader + "':" + glEnumToString(gl, err));
  1.1021 +    return null;
  1.1022 +  }
  1.1023 +
  1.1024 +  // Compile the shader
  1.1025 +  gl.compileShader(shader);
  1.1026 +
  1.1027 +  // Check the compile status
  1.1028 +  var compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
  1.1029 +  if (!compiled) {
  1.1030 +    // Something went wrong during compilation; get the error
  1.1031 +    lastError = gl.getShaderInfoLog(shader);
  1.1032 +    errFn("*** Error compiling shader '" + shader + "':" + lastError);
  1.1033 +    gl.deleteShader(shader);
  1.1034 +    return null;
  1.1035 +  }
  1.1036 +
  1.1037 +  return shader;
  1.1038 +}
  1.1039 +
  1.1040 +/**
  1.1041 + * Loads a shader from a URL.
  1.1042 + * @param {!WebGLContext} gl The WebGLContext to use.
  1.1043 + * @param {file} file The URL of the shader source.
  1.1044 + * @param {number} type The type of shader.
  1.1045 + * @param {function(string): void) opt_errorCallback callback for errors.
  1.1046 + * @return {!WebGLShader} The created shader.
  1.1047 + */
  1.1048 +var loadShaderFromFile = function(gl, file, type, opt_errorCallback) {
  1.1049 +  var shaderSource = readFile(file);
  1.1050 +  return loadShader(gl, shaderSource, type, opt_errorCallback);
  1.1051 +};
  1.1052 +
  1.1053 +/**
  1.1054 + * Gets the content of script.
  1.1055 + */
  1.1056 +var getScript = function(scriptId) {
  1.1057 +  var shaderScript = document.getElementById(scriptId);
  1.1058 +  if (!shaderScript) {
  1.1059 +    throw("*** Error: unknown script element" + scriptId);
  1.1060 +  }
  1.1061 +  return shaderScript.text;
  1.1062 +};
  1.1063 +
  1.1064 +/**
  1.1065 + * Loads a shader from a script tag.
  1.1066 + * @param {!WebGLContext} gl The WebGLContext to use.
  1.1067 + * @param {string} scriptId The id of the script tag.
  1.1068 + * @param {number} opt_shaderType The type of shader. If not passed in it will
  1.1069 + *     be derived from the type of the script tag.
  1.1070 + * @param {function(string): void) opt_errorCallback callback for errors.
  1.1071 + * @return {!WebGLShader} The created shader.
  1.1072 + */
  1.1073 +var loadShaderFromScript = function(
  1.1074 +    gl, scriptId, opt_shaderType, opt_errorCallback) {
  1.1075 +  var shaderSource = "";
  1.1076 +  var shaderType;
  1.1077 +  var shaderScript = document.getElementById(scriptId);
  1.1078 +  if (!shaderScript) {
  1.1079 +    throw("*** Error: unknown script element " + scriptId);
  1.1080 +  }
  1.1081 +  shaderSource = shaderScript.text;
  1.1082 +
  1.1083 +  if (!opt_shaderType) {
  1.1084 +    if (shaderScript.type == "x-shader/x-vertex") {
  1.1085 +      shaderType = gl.VERTEX_SHADER;
  1.1086 +    } else if (shaderScript.type == "x-shader/x-fragment") {
  1.1087 +      shaderType = gl.FRAGMENT_SHADER;
  1.1088 +    } else if (shaderType != gl.VERTEX_SHADER && shaderType != gl.FRAGMENT_SHADER) {
  1.1089 +      throw("*** Error: unknown shader type");
  1.1090 +      return null;
  1.1091 +    }
  1.1092 +  }
  1.1093 +
  1.1094 +  return loadShader(
  1.1095 +      gl, shaderSource, opt_shaderType ? opt_shaderType : shaderType,
  1.1096 +      opt_errorCallback);
  1.1097 +};
  1.1098 +
  1.1099 +var loadStandardProgram = function(gl) {
  1.1100 +  var program = gl.createProgram();
  1.1101 +  gl.attachShader(program, loadStandardVertexShader(gl));
  1.1102 +  gl.attachShader(program, loadStandardFragmentShader(gl));
  1.1103 +  linkProgram(gl, program);
  1.1104 +  return program;
  1.1105 +};
  1.1106 +
  1.1107 +/**
  1.1108 + * Loads shaders from files, creates a program, attaches the shaders and links.
  1.1109 + * @param {!WebGLContext} gl The WebGLContext to use.
  1.1110 + * @param {string} vertexShaderPath The URL of the vertex shader.
  1.1111 + * @param {string} fragmentShaderPath The URL of the fragment shader.
  1.1112 + * @param {function(string): void) opt_errorCallback callback for errors.
  1.1113 + * @return {!WebGLProgram} The created program.
  1.1114 + */
  1.1115 +var loadProgramFromFile = function(
  1.1116 +    gl, vertexShaderPath, fragmentShaderPath, opt_errorCallback) {
  1.1117 +  var program = gl.createProgram();
  1.1118 +  gl.attachShader(
  1.1119 +      program,
  1.1120 +      loadShaderFromFile(
  1.1121 +          gl, vertexShaderPath, gl.VERTEX_SHADER, opt_errorCallback));
  1.1122 +  gl.attachShader(
  1.1123 +      program,
  1.1124 +      loadShaderFromFile(
  1.1125 +          gl, fragmentShaderPath, gl.FRAGMENT_SHADER, opt_errorCallback));
  1.1126 +  linkProgram(gl, program, opt_errorCallback);
  1.1127 +  return program;
  1.1128 +};
  1.1129 +
  1.1130 +/**
  1.1131 + * Loads shaders from script tags, creates a program, attaches the shaders and
  1.1132 + * links.
  1.1133 + * @param {!WebGLContext} gl The WebGLContext to use.
  1.1134 + * @param {string} vertexScriptId The id of the script tag that contains the
  1.1135 + *        vertex shader.
  1.1136 + * @param {string} fragmentScriptId The id of the script tag that contains the
  1.1137 + *        fragment shader.
  1.1138 + * @param {function(string): void) opt_errorCallback callback for errors.
  1.1139 + * @return {!WebGLProgram} The created program.
  1.1140 + */
  1.1141 +var loadProgramFromScript = function loadProgramFromScript(
  1.1142 +    gl, vertexScriptId, fragmentScriptId, opt_errorCallback) {
  1.1143 +  var program = gl.createProgram();
  1.1144 +  gl.attachShader(
  1.1145 +      program,
  1.1146 +      loadShaderFromScript(
  1.1147 +          gl, vertexScriptId, gl.VERTEX_SHADER, opt_errorCallback));
  1.1148 +  gl.attachShader(
  1.1149 +      program,
  1.1150 +      loadShaderFromScript(
  1.1151 +          gl, fragmentScriptId,  gl.FRAGMENT_SHADER, opt_errorCallback));
  1.1152 +  linkProgram(gl, program, opt_errorCallback);
  1.1153 +  return program;
  1.1154 +};
  1.1155 +
  1.1156 +/**
  1.1157 + * Loads shaders from source, creates a program, attaches the shaders and
  1.1158 + * links.
  1.1159 + * @param {!WebGLContext} gl The WebGLContext to use.
  1.1160 + * @param {string} vertexShader The vertex shader.
  1.1161 + * @param {string} fragmentShader The fragment shader.
  1.1162 + * @param {function(string): void) opt_errorCallback callback for errors.
  1.1163 + * @return {!WebGLProgram} The created program.
  1.1164 + */
  1.1165 +var loadProgram = function(
  1.1166 +    gl, vertexShader, fragmentShader, opt_errorCallback) {
  1.1167 +  var program = gl.createProgram();
  1.1168 +  gl.attachShader(
  1.1169 +      program,
  1.1170 +      loadShader(
  1.1171 +          gl, vertexShader, gl.VERTEX_SHADER, opt_errorCallback));
  1.1172 +  gl.attachShader(
  1.1173 +      program,
  1.1174 +      loadShader(
  1.1175 +          gl, fragmentShader, gl.FRAGMENT_SHADER, opt_errorCallback));
  1.1176 +  linkProgram(gl, program, opt_errorCallback);
  1.1177 +  return program;
  1.1178 +};
  1.1179 +
  1.1180 +/**
  1.1181 + * Loads shaders from source, creates a program, attaches the shaders and
  1.1182 + * links but expects error.
  1.1183 + *
  1.1184 + * GLSL 1.0.17 10.27 effectively says that compileShader can
  1.1185 + * always succeed as long as linkProgram fails so we can't
  1.1186 + * rely on compileShader failing. This function expects
  1.1187 + * one of the shader to fail OR linking to fail.
  1.1188 + *
  1.1189 + * @param {!WebGLContext} gl The WebGLContext to use.
  1.1190 + * @param {string} vertexShaderScriptId The vertex shader.
  1.1191 + * @param {string} fragmentShaderScriptId The fragment shader.
  1.1192 + * @return {WebGLProgram} The created program.
  1.1193 + */
  1.1194 +var loadProgramFromScriptExpectError = function(
  1.1195 +    gl, vertexShaderScriptId, fragmentShaderScriptId) {
  1.1196 +  var vertexShader = loadShaderFromScript(gl, vertexShaderScriptId);
  1.1197 +  if (!vertexShader) {
  1.1198 +    return null;
  1.1199 +  }
  1.1200 +  var fragmentShader = loadShaderFromScript(gl, fragmentShaderScriptId);
  1.1201 +  if (!fragmentShader) {
  1.1202 +    return null;
  1.1203 +  }
  1.1204 +  var linkSuccess = true;
  1.1205 +  var program = gl.createProgram();
  1.1206 +  gl.attachShader(program, vertexShader);
  1.1207 +  gl.attachShader(program, fragmentShader);
  1.1208 +  linkSuccess = true;
  1.1209 +  linkProgram(gl, program, function() {
  1.1210 +      linkSuccess = false;
  1.1211 +    });
  1.1212 +  return linkSuccess ? program : null;
  1.1213 +};
  1.1214 +
  1.1215 +var basePath;
  1.1216 +var getBasePath = function() {
  1.1217 +  if (!basePath) {
  1.1218 +    var expectedBase = "webgl-test-utils.js";
  1.1219 +    var scripts = document.getElementsByTagName('script');
  1.1220 +    for (var script, i = 0; script = scripts[i]; i++) {
  1.1221 +      var src = script.src;
  1.1222 +      var l = src.length;
  1.1223 +      if (src.substr(l - expectedBase.length) == expectedBase) {
  1.1224 +        basePath = src.substr(0, l - expectedBase.length);
  1.1225 +      }
  1.1226 +    }
  1.1227 +  }
  1.1228 +  return basePath;
  1.1229 +};
  1.1230 +
  1.1231 +var loadStandardVertexShader = function(gl) {
  1.1232 +  return loadShaderFromFile(
  1.1233 +      gl, getBasePath() + "vertexShader.vert", gl.VERTEX_SHADER);
  1.1234 +};
  1.1235 +
  1.1236 +var loadStandardFragmentShader = function(gl) {
  1.1237 +  return loadShaderFromFile(
  1.1238 +      gl, getBasePath() + "fragmentShader.frag", gl.FRAGMENT_SHADER);
  1.1239 +};
  1.1240 +
  1.1241 +/**
  1.1242 + * Loads an image asynchronously.
  1.1243 + * @param {string} url URL of image to load.
  1.1244 + * @param {!function(!Element): void} callback Function to call
  1.1245 + *     with loaded image.
  1.1246 + */
  1.1247 +var loadImageAsync = function(url, callback) {
  1.1248 +  var img = document.createElement('img');
  1.1249 +  img.onload = function() {
  1.1250 +    callback(img);
  1.1251 +  };
  1.1252 +  img.src = url;
  1.1253 +};
  1.1254 +
  1.1255 +/**
  1.1256 + * Loads an array of images.
  1.1257 + * @param {!Array.<string>} urls URLs of images to load.
  1.1258 + * @param {!function(!{string, img}): void} callback. Callback
  1.1259 + *     that gets passed map of urls to img tags.
  1.1260 + */
  1.1261 +var loadImagesAsync = function(urls, callback) {
  1.1262 +  var count = 1;
  1.1263 +  var images = { };
  1.1264 +  function countDown() {
  1.1265 +    --count;
  1.1266 +    if (count == 0) {
  1.1267 +      callback(images);
  1.1268 +    }
  1.1269 +  }
  1.1270 +  function imageLoaded(url) {
  1.1271 +    return function(img) {
  1.1272 +      images[url] = img;
  1.1273 +      countDown();
  1.1274 +    }
  1.1275 +  }
  1.1276 +  for (var ii = 0; ii < urls.length; ++ii) {
  1.1277 +    ++count;
  1.1278 +    loadImageAsync(urls[ii], imageLoaded(urls[ii]));
  1.1279 +  }
  1.1280 +  countDown();
  1.1281 +};
  1.1282 +
  1.1283 +var getUrlArguments = function() {
  1.1284 +  var args = {};
  1.1285 +  try {
  1.1286 +    var s = window.location.href;
  1.1287 +    var q = s.indexOf("?");
  1.1288 +    var e = s.indexOf("#");
  1.1289 +    if (e < 0) {
  1.1290 +      e = s.length;
  1.1291 +    }
  1.1292 +    var query = s.substring(q + 1, e);
  1.1293 +    var pairs = query.split("&");
  1.1294 +    for (var ii = 0; ii < pairs.length; ++ii) {
  1.1295 +      var keyValue = pairs[ii].split("=");
  1.1296 +      var key = keyValue[0];
  1.1297 +      var value = decodeURIComponent(keyValue[1]);
  1.1298 +      args[key] = value;
  1.1299 +    }
  1.1300 +  } catch (e) {
  1.1301 +    throw "could not parse url";
  1.1302 +  }
  1.1303 +  return args;
  1.1304 +};
  1.1305 +
  1.1306 +var makeImage = function(canvas) {
  1.1307 +  var img = document.createElement('img');
  1.1308 +  img.src = canvas.toDataURL();
  1.1309 +  return img;
  1.1310 +};
  1.1311 +
  1.1312 +var insertImage = function(element, caption, img) {
  1.1313 +  var div = document.createElement("div");
  1.1314 +  div.appendChild(img);
  1.1315 +  var label = document.createElement("div");
  1.1316 +  label.appendChild(document.createTextNode(caption));
  1.1317 +  div.appendChild(label);
  1.1318 +   element.appendChild(div);
  1.1319 +};
  1.1320 +
  1.1321 +var addShaderSource = function(element, label, source) {
  1.1322 +  var div = document.createElement("div");
  1.1323 +  var s = document.createElement("pre");
  1.1324 +  s.className = "shader-source";
  1.1325 +  s.style.display = "none";
  1.1326 +  var ol = document.createElement("ol");
  1.1327 +  //s.appendChild(document.createTextNode(source));
  1.1328 +  var lines = source.split("\n");
  1.1329 +  for (var ii = 0; ii < lines.length; ++ii) {
  1.1330 +    var line = lines[ii];
  1.1331 +    var li = document.createElement("li");
  1.1332 +    li.appendChild(document.createTextNode(line));
  1.1333 +    ol.appendChild(li);
  1.1334 +  }
  1.1335 +  s.appendChild(ol);
  1.1336 +  var l = document.createElement("a");
  1.1337 +  l.href = "show-shader-source";
  1.1338 +  l.appendChild(document.createTextNode(label));
  1.1339 +  l.addEventListener('click', function(event) {
  1.1340 +      if (event.preventDefault) {
  1.1341 +        event.preventDefault();
  1.1342 +      }
  1.1343 +      s.style.display = (s.style.display == 'none') ? 'block' : 'none';
  1.1344 +      return false;
  1.1345 +    }, false);
  1.1346 +  div.appendChild(l);
  1.1347 +  div.appendChild(s);
  1.1348 +  element.appendChild(div);
  1.1349 +}
  1.1350 +
  1.1351 +return {
  1.1352 +  addShaderSource: addShaderSource,
  1.1353 +  clearAndDrawUnitQuad : clearAndDrawUnitQuad,
  1.1354 +  create3DContext: create3DContext,
  1.1355 +  create3DContextWithWrapperThatThrowsOnGLError:
  1.1356 +    create3DContextWithWrapperThatThrowsOnGLError,
  1.1357 +  checkCanvas: checkCanvas,
  1.1358 +  checkCanvasRect: checkCanvasRect,
  1.1359 +  createColoredTexture: createColoredTexture,
  1.1360 +  drawQuad: drawQuad,
  1.1361 +  drawUnitQuad: drawUnitQuad,
  1.1362 +  endsWith: endsWith,
  1.1363 +  getExtensionWithKnownPrefixes: getExtensionWithKnownPrefixes,
  1.1364 +  getFileListAsync: getFileListAsync,
  1.1365 +  getLastError: getLastError,
  1.1366 +  getScript: getScript,
  1.1367 +  getSupportedExtensionWithKnownPrefixes: getSupportedExtensionWithKnownPrefixes,
  1.1368 +  getUrlArguments: getUrlArguments,
  1.1369 +  glEnumToString: glEnumToString,
  1.1370 +  glErrorShouldBe: glErrorShouldBe,
  1.1371 +  fillTexture: fillTexture,
  1.1372 +  insertImage: insertImage,
  1.1373 +  loadImageAsync: loadImageAsync,
  1.1374 +  loadImagesAsync: loadImagesAsync,
  1.1375 +  loadProgram: loadProgram,
  1.1376 +  loadProgramFromFile: loadProgramFromFile,
  1.1377 +  loadProgramFromScript: loadProgramFromScript,
  1.1378 +  loadProgramFromScriptExpectError: loadProgramFromScriptExpectError,
  1.1379 +  loadShader: loadShader,
  1.1380 +  loadShaderFromFile: loadShaderFromFile,
  1.1381 +  loadShaderFromScript: loadShaderFromScript,
  1.1382 +  loadStandardProgram: loadStandardProgram,
  1.1383 +  loadStandardVertexShader: loadStandardVertexShader,
  1.1384 +  loadStandardFragmentShader: loadStandardFragmentShader,
  1.1385 +  loadTextFileAsync: loadTextFileAsync,
  1.1386 +  loadTexture: loadTexture,
  1.1387 +  log: log,
  1.1388 +  loggingOff: loggingOff,
  1.1389 +  makeImage: makeImage,
  1.1390 +  error: error,
  1.1391 +  setupProgram: setupProgram,
  1.1392 +  setupQuad: setupQuad,
  1.1393 +  setupSimpleTextureFragmentShader: setupSimpleTextureFragmentShader,
  1.1394 +  setupSimpleTextureProgram: setupSimpleTextureProgram,
  1.1395 +  setupSimpleTextureVertexShader: setupSimpleTextureVertexShader,
  1.1396 +  setupTexturedQuad: setupTexturedQuad,
  1.1397 +  setupTexturedQuadWithTexCoords: setupTexturedQuadWithTexCoords,
  1.1398 +  setupUnitQuad: setupUnitQuad,
  1.1399 +  setupUnitQuadWithTexCoords: setupUnitQuadWithTexCoords,
  1.1400 +  setupWebGLWithShaders: setupWebGLWithShaders,
  1.1401 +  startsWith: startsWith,
  1.1402 +  shouldGenerateGLError: shouldGenerateGLError,
  1.1403 +  readFile: readFile,
  1.1404 +  readFileList: readFileList,
  1.1405 +
  1.1406 +  none: false
  1.1407 +};
  1.1408 +
  1.1409 +}());
  1.1410 +
  1.1411 +

mercurial