browser/devtools/tilt/tilt-gl.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 /* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* vim: set ts=2 et sw=2 tw=80: */
     3 /* This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 "use strict";
     8 const {Cc, Ci, Cu} = require("chrome");
    10 let TiltUtils = require("devtools/tilt/tilt-utils");
    11 let {TiltMath, mat4} = require("devtools/tilt/tilt-math");
    13 Cu.import("resource://gre/modules/Services.jsm");
    15 const WEBGL_CONTEXT_NAME = "experimental-webgl";
    18 /**
    19  * Module containing thin wrappers around low-level WebGL functions.
    20  */
    21 let TiltGL = {};
    22 module.exports = TiltGL;
    24 /**
    25  * Contains commonly used helper methods used in any 3D application.
    26  *
    27  * @param {HTMLCanvasElement} aCanvas
    28  *                            the canvas element used for rendering
    29  * @param {Function} onError
    30  *                   optional, function called if initialization failed
    31  * @param {Function} onLoad
    32  *                   optional, function called if initialization worked
    33  */
    34 TiltGL.Renderer = function TGL_Renderer(aCanvas, onError, onLoad)
    35 {
    36   /**
    37    * The WebGL context obtained from the canvas element, used for drawing.
    38    */
    39   this.context = TiltGL.create3DContext(aCanvas);
    41   // check if the context was created successfully
    42   if (!this.context) {
    43     TiltUtils.Output.alert("Firefox", TiltUtils.L10n.get("initTilt.error"));
    44     TiltUtils.Output.error(TiltUtils.L10n.get("initWebGL.error"));
    46     if ("function" === typeof onError) {
    47       onError();
    48     }
    49     return;
    50   }
    52   // set the default clear color and depth buffers
    53   this.context.clearColor(0, 0, 0, 0);
    54   this.context.clearDepth(1);
    56   /**
    57    * Variables representing the current framebuffer width and height.
    58    */
    59   this.width = aCanvas.width;
    60   this.height = aCanvas.height;
    61   this.initialWidth = this.width;
    62   this.initialHeight = this.height;
    64   /**
    65    * The current model view matrix.
    66    */
    67   this.mvMatrix = mat4.identity(mat4.create());
    69   /**
    70    * The current projection matrix.
    71    */
    72   this.projMatrix = mat4.identity(mat4.create());
    74   /**
    75    * The current fill color applied to any objects which can be filled.
    76    * These are rectangles, circles, boxes, 2d or 3d primitives in general.
    77    */
    78   this._fillColor = [];
    80   /**
    81    * The current stroke color applied to any objects which can be stroked.
    82    * This property mostly refers to lines.
    83    */
    84   this._strokeColor = [];
    86   /**
    87    * Variable representing the current stroke weight.
    88    */
    89   this._strokeWeightValue = 0;
    91   /**
    92    * A shader useful for drawing vertices with only a color component.
    93    */
    94   this._colorShader = new TiltGL.Program(this.context, {
    95     vs: TiltGL.ColorShader.vs,
    96     fs: TiltGL.ColorShader.fs,
    97     attributes: ["vertexPosition"],
    98     uniforms: ["mvMatrix", "projMatrix", "fill"]
    99   });
   101   // create helper functions to create shaders, meshes, buffers and textures
   102   this.Program =
   103     TiltGL.Program.bind(TiltGL.Program, this.context);
   104   this.VertexBuffer =
   105     TiltGL.VertexBuffer.bind(TiltGL.VertexBuffer, this.context);
   106   this.IndexBuffer =
   107     TiltGL.IndexBuffer.bind(TiltGL.IndexBuffer, this.context);
   108   this.Texture =
   109     TiltGL.Texture.bind(TiltGL.Texture, this.context);
   111   // set the default mvp matrices, tint, fill, stroke and other visual props.
   112   this.defaults();
   114   // the renderer was created successfully
   115   if ("function" === typeof onLoad) {
   116     onLoad();
   117   }
   118 };
   120 TiltGL.Renderer.prototype = {
   122   /**
   123    * Clears the color and depth buffers.
   124    */
   125   clear: function TGLR_clear()
   126   {
   127     let gl = this.context;
   128     gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
   129   },
   131   /**
   132    * Sets if depth testing should be enabled or not.
   133    * Disabling could be useful when handling transparency (for example).
   134    *
   135    * @param {Boolean} aEnabledFlag
   136    *                  true if depth testing should be enabled
   137    */
   138   depthTest: function TGLR_depthTest(aEnabledFlag)
   139   {
   140     let gl = this.context;
   142     if (aEnabledFlag) {
   143       gl.enable(gl.DEPTH_TEST);
   144     } else {
   145       gl.disable(gl.DEPTH_TEST);
   146     }
   147   },
   149   /**
   150    * Sets if stencil testing should be enabled or not.
   151    *
   152    * @param {Boolean} aEnabledFlag
   153    *                  true if stencil testing should be enabled
   154    */
   155   stencilTest: function TGLR_stencilTest(aEnabledFlag)
   156   {
   157     let gl = this.context;
   159     if (aEnabledFlag) {
   160       gl.enable(gl.STENCIL_TEST);
   161     } else {
   162       gl.disable(gl.STENCIL_TEST);
   163     }
   164   },
   166   /**
   167    * Sets cull face, either "front", "back" or disabled.
   168    *
   169    * @param {String} aModeFlag
   170    *                 blending mode, either "front", "back", "both" or falsy
   171    */
   172   cullFace: function TGLR_cullFace(aModeFlag)
   173   {
   174     let gl = this.context;
   176     switch (aModeFlag) {
   177       case "front":
   178         gl.enable(gl.CULL_FACE);
   179         gl.cullFace(gl.FRONT);
   180         break;
   181       case "back":
   182         gl.enable(gl.CULL_FACE);
   183         gl.cullFace(gl.BACK);
   184         break;
   185       case "both":
   186         gl.enable(gl.CULL_FACE);
   187         gl.cullFace(gl.FRONT_AND_BACK);
   188         break;
   189       default:
   190         gl.disable(gl.CULL_FACE);
   191     }
   192   },
   194   /**
   195    * Specifies the orientation of front-facing polygons.
   196    *
   197    * @param {String} aModeFlag
   198    *                 either "cw" or "ccw"
   199    */
   200   frontFace: function TGLR_frontFace(aModeFlag)
   201   {
   202     let gl = this.context;
   204     switch (aModeFlag) {
   205       case "cw":
   206         gl.frontFace(gl.CW);
   207         break;
   208       case "ccw":
   209         gl.frontFace(gl.CCW);
   210         break;
   211     }
   212   },
   214   /**
   215    * Sets blending, either "alpha" or "add" (additive blending).
   216    * Anything else disables blending.
   217    *
   218    * @param {String} aModeFlag
   219    *                 blending mode, either "alpha", "add" or falsy
   220    */
   221   blendMode: function TGLR_blendMode(aModeFlag)
   222   {
   223     let gl = this.context;
   225     switch (aModeFlag) {
   226       case "alpha":
   227         gl.enable(gl.BLEND);
   228         gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
   229         break;
   230       case "add":
   231         gl.enable(gl.BLEND);
   232         gl.blendFunc(gl.SRC_ALPHA, gl.ONE);
   233         break;
   234       default:
   235         gl.disable(gl.BLEND);
   236     }
   237   },
   239   /**
   240    * Helper function to activate the color shader.
   241    *
   242    * @param {TiltGL.VertexBuffer} aVerticesBuffer
   243    *                              a buffer of vertices positions
   244    * @param {Array} aColor
   245    *                the color fill to be used as [r, g, b, a] with 0..1 range
   246    * @param {Array} aMvMatrix
   247    *                the model view matrix
   248    * @param {Array} aProjMatrix
   249    *                the projection matrix
   250    */
   251   useColorShader: function TGLR_useColorShader(
   252     aVerticesBuffer, aColor, aMvMatrix, aProjMatrix)
   253   {
   254     let program = this._colorShader;
   256     // use this program
   257     program.use();
   259     // bind the attributes and uniforms as necessary
   260     program.bindVertexBuffer("vertexPosition", aVerticesBuffer);
   261     program.bindUniformMatrix("mvMatrix", aMvMatrix || this.mvMatrix);
   262     program.bindUniformMatrix("projMatrix", aProjMatrix || this.projMatrix);
   263     program.bindUniformVec4("fill", aColor || this._fillColor);
   264   },
   266   /**
   267    * Draws bound vertex buffers using the specified parameters.
   268    *
   269    * @param {Number} aDrawMode
   270    *                 WebGL enum, like TRIANGLES
   271    * @param {Number} aCount
   272    *                 the number of indices to be rendered
   273    */
   274   drawVertices: function TGLR_drawVertices(aDrawMode, aCount)
   275   {
   276     this.context.drawArrays(aDrawMode, 0, aCount);
   277   },
   279   /**
   280    * Draws bound vertex buffers using the specified parameters.
   281    * This function also makes use of an index buffer.
   282    *
   283    * @param {Number} aDrawMode
   284    *                 WebGL enum, like TRIANGLES
   285    * @param {TiltGL.IndexBuffer} aIndicesBuffer
   286    *                             indices for the vertices buffer
   287    */
   288   drawIndexedVertices: function TGLR_drawIndexedVertices(
   289     aDrawMode, aIndicesBuffer)
   290   {
   291     let gl = this.context;
   293     gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, aIndicesBuffer._ref);
   294     gl.drawElements(aDrawMode, aIndicesBuffer.numItems, gl.UNSIGNED_SHORT, 0);
   295   },
   297   /**
   298    * Sets the current fill color.
   299    *
   300    * @param {Array} aColor
   301    *                the color fill to be used as [r, g, b, a] with 0..1 range
   302    * @param {Number} aMultiplyAlpha
   303    *                 optional, scalar to multiply the alpha element with
   304    */
   305   fill: function TGLR_fill(aColor, aMultiplyAlpha)
   306   {
   307     let fill = this._fillColor;
   309     fill[0] = aColor[0];
   310     fill[1] = aColor[1];
   311     fill[2] = aColor[2];
   312     fill[3] = aColor[3] * (aMultiplyAlpha || 1);
   313   },
   315   /**
   316    * Sets the current stroke color.
   317    *
   318    * @param {Array} aColor
   319    *                the color stroke to be used as [r, g, b, a] with 0..1 range
   320    * @param {Number} aMultiplyAlpha
   321    *                 optional, scalar to multiply the alpha element with
   322    */
   323   stroke: function TGLR_stroke(aColor, aMultiplyAlpha)
   324   {
   325     let stroke = this._strokeColor;
   327     stroke[0] = aColor[0];
   328     stroke[1] = aColor[1];
   329     stroke[2] = aColor[2];
   330     stroke[3] = aColor[3] * (aMultiplyAlpha || 1);
   331   },
   333   /**
   334    * Sets the current stroke weight (line width).
   335    *
   336    * @param {Number} aWeight
   337    *                 the stroke weight
   338    */
   339   strokeWeight: function TGLR_strokeWeight(aWeight)
   340   {
   341     if (this._strokeWeightValue !== aWeight) {
   342       this._strokeWeightValue = aWeight;
   343       this.context.lineWidth(aWeight);
   344     }
   345   },
   347   /**
   348    * Sets a default perspective projection, with the near frustum rectangle
   349    * mapped to the canvas width and height bounds.
   350    */
   351   perspective: function TGLR_perspective()
   352   {
   353     let fov = 45;
   354     let w = this.width;
   355     let h = this.height;
   356     let x = w / 2;
   357     let y = h / 2;
   358     let z = y / Math.tan(TiltMath.radians(fov) / 2);
   359     let aspect = w / h;
   360     let znear = z / 10;
   361     let zfar = z * 10;
   363     mat4.perspective(fov, aspect, znear, zfar, this.projMatrix, -1);
   364     mat4.translate(this.projMatrix, [-x, -y, -z]);
   365     mat4.identity(this.mvMatrix);
   366   },
   368   /**
   369    * Sets a default orthographic projection (recommended for 2d rendering).
   370    */
   371   ortho: function TGLR_ortho()
   372   {
   373     mat4.ortho(0, this.width, this.height, 0, -1, 1, this.projMatrix);
   374     mat4.identity(this.mvMatrix);
   375   },
   377   /**
   378    * Sets a custom projection matrix.
   379    * @param {Array} matrix: the custom projection matrix to be used
   380    */
   381   projection: function TGLR_projection(aMatrix)
   382   {
   383     mat4.set(aMatrix, this.projMatrix);
   384     mat4.identity(this.mvMatrix);
   385   },
   387   /**
   388    * Resets the model view matrix to identity.
   389    * This is a default matrix with no rotation, no scaling, at (0, 0, 0);
   390    */
   391   origin: function TGLR_origin()
   392   {
   393     mat4.identity(this.mvMatrix);
   394   },
   396   /**
   397    * Transforms the model view matrix with a new matrix.
   398    * Useful for creating custom transformations.
   399    *
   400    * @param {Array} matrix: the matrix to be multiply the model view with
   401    */
   402   transform: function TGLR_transform(aMatrix)
   403   {
   404     mat4.multiply(this.mvMatrix, aMatrix);
   405   },
   407   /**
   408    * Translates the model view by the x, y and z coordinates.
   409    *
   410    * @param {Number} x
   411    *                 the x amount of translation
   412    * @param {Number} y
   413    *                 the y amount of translation
   414    * @param {Number} z
   415    *                 optional, the z amount of translation
   416    */
   417   translate: function TGLR_translate(x, y, z)
   418   {
   419     mat4.translate(this.mvMatrix, [x, y, z || 0]);
   420   },
   422   /**
   423    * Rotates the model view by a specified angle on the x, y and z axis.
   424    *
   425    * @param {Number} angle
   426    *                 the angle expressed in radians
   427    * @param {Number} x
   428    *                 the x axis of the rotation
   429    * @param {Number} y
   430    *                 the y axis of the rotation
   431    * @param {Number} z
   432    *                 the z axis of the rotation
   433    */
   434   rotate: function TGLR_rotate(angle, x, y, z)
   435   {
   436     mat4.rotate(this.mvMatrix, angle, [x, y, z]);
   437   },
   439   /**
   440    * Rotates the model view by a specified angle on the x axis.
   441    *
   442    * @param {Number} aAngle
   443    *                 the angle expressed in radians
   444    */
   445   rotateX: function TGLR_rotateX(aAngle)
   446   {
   447     mat4.rotateX(this.mvMatrix, aAngle);
   448   },
   450   /**
   451    * Rotates the model view by a specified angle on the y axis.
   452    *
   453    * @param {Number} aAngle
   454    *                 the angle expressed in radians
   455    */
   456   rotateY: function TGLR_rotateY(aAngle)
   457   {
   458     mat4.rotateY(this.mvMatrix, aAngle);
   459   },
   461   /**
   462    * Rotates the model view by a specified angle on the z axis.
   463    *
   464    * @param {Number} aAngle
   465    *                 the angle expressed in radians
   466    */
   467   rotateZ: function TGLR_rotateZ(aAngle)
   468   {
   469     mat4.rotateZ(this.mvMatrix, aAngle);
   470   },
   472   /**
   473    * Scales the model view by the x, y and z coordinates.
   474    *
   475    * @param {Number} x
   476    *                 the x amount of scaling
   477    * @param {Number} y
   478    *                 the y amount of scaling
   479    * @param {Number} z
   480    *                 optional, the z amount of scaling
   481    */
   482   scale: function TGLR_scale(x, y, z)
   483   {
   484     mat4.scale(this.mvMatrix, [x, y, z || 1]);
   485   },
   487   /**
   488    * Performs a custom interpolation between two matrices.
   489    * The result is saved in the first operand.
   490    *
   491    * @param {Array} aMat
   492    *                the first matrix
   493    * @param {Array} aMat2
   494    *                the second matrix
   495    * @param {Number} aLerp
   496    *                 interpolation amount between the two inputs
   497    * @param {Number} aDamping
   498    *                 optional, scalar adjusting the interpolation amortization
   499    * @param {Number} aBalance
   500    *                 optional, scalar adjusting the interpolation shift ammount
   501    */
   502   lerp: function TGLR_lerp(aMat, aMat2, aLerp, aDamping, aBalance)
   503   {
   504     if (aLerp < 0 || aLerp > 1) {
   505       return;
   506     }
   508     // calculate the interpolation factor based on the damping and step
   509     let f = Math.pow(1 - Math.pow(aLerp, aDamping || 1), 1 / aBalance || 1);
   511     // interpolate each element from the two matrices
   512     for (let i = 0, len = this.projMatrix.length; i < len; i++) {
   513       aMat[i] = aMat[i] + f * (aMat2[i] - aMat[i]);
   514     }
   515   },
   517   /**
   518    * Resets the drawing style to default.
   519    */
   520   defaults: function TGLR_defaults()
   521   {
   522     this.depthTest(true);
   523     this.stencilTest(false);
   524     this.cullFace(false);
   525     this.frontFace("ccw");
   526     this.blendMode("alpha");
   527     this.fill([1, 1, 1, 1]);
   528     this.stroke([0, 0, 0, 1]);
   529     this.strokeWeight(1);
   530     this.perspective();
   531     this.origin();
   532   },
   534   /**
   535    * Draws a quad composed of four vertices.
   536    * Vertices must be in clockwise order, or else drawing will be distorted.
   537    * Do not abuse this function, it is quite slow.
   538    *
   539    * @param {Array} aV0
   540    *                the [x, y, z] position of the first triangle point
   541    * @param {Array} aV1
   542    *                the [x, y, z] position of the second triangle point
   543    * @param {Array} aV2
   544    *                the [x, y, z] position of the third triangle point
   545    * @param {Array} aV3
   546    *                the [x, y, z] position of the fourth triangle point
   547    */
   548   quad: function TGLR_quad(aV0, aV1, aV2, aV3)
   549   {
   550     let gl = this.context;
   551     let fill = this._fillColor;
   552     let stroke = this._strokeColor;
   553     let vert = new TiltGL.VertexBuffer(gl, [aV0[0], aV0[1], aV0[2] || 0,
   554                                             aV1[0], aV1[1], aV1[2] || 0,
   555                                             aV2[0], aV2[1], aV2[2] || 0,
   556                                             aV3[0], aV3[1], aV3[2] || 0], 3);
   558     // use the necessary shader and draw the vertices
   559     this.useColorShader(vert, fill);
   560     this.drawVertices(gl.TRIANGLE_FAN, vert.numItems);
   562     this.useColorShader(vert, stroke);
   563     this.drawVertices(gl.LINE_LOOP, vert.numItems);
   565     TiltUtils.destroyObject(vert);
   566   },
   568   /**
   569    * Function called when this object is destroyed.
   570    */
   571   finalize: function TGLR_finalize()
   572   {
   573     if (this.context) {
   574       TiltUtils.destroyObject(this._colorShader);
   575     }
   576   }
   577 };
   579 /**
   580  * Creates a vertex buffer containing an array of elements.
   581  *
   582  * @param {Object} aContext
   583  *                 a WebGL context
   584  * @param {Array} aElementsArray
   585  *                an array of numbers (floats)
   586  * @param {Number} aItemSize
   587  *                 how many items create a block
   588  * @param {Number} aNumItems
   589  *                 optional, how many items to use from the array
   590  */
   591 TiltGL.VertexBuffer = function TGL_VertexBuffer(
   592   aContext, aElementsArray, aItemSize, aNumItems)
   593 {
   594   /**
   595    * The parent WebGL context.
   596    */
   597   this._context = aContext;
   599   /**
   600    * The array buffer.
   601    */
   602   this._ref = null;
   604   /**
   605    * Array of number components contained in the buffer.
   606    */
   607   this.components = null;
   609   /**
   610    * Variables defining the internal structure of the buffer.
   611    */
   612   this.itemSize = 0;
   613   this.numItems = 0;
   615   // if the array is specified in the constructor, initialize directly
   616   if (aElementsArray) {
   617     this.initBuffer(aElementsArray, aItemSize, aNumItems);
   618   }
   619 };
   621 TiltGL.VertexBuffer.prototype = {
   623   /**
   624    * Initializes buffer data to be used for drawing, using an array of floats.
   625    * The "aNumItems" param can be specified to use only a portion of the array.
   626    *
   627    * @param {Array} aElementsArray
   628    *                an array of floats
   629    * @param {Number} aItemSize
   630    *                 how many items create a block
   631    * @param {Number} aNumItems
   632    *                 optional, how many items to use from the array
   633    */
   634   initBuffer: function TGLVB_initBuffer(aElementsArray, aItemSize, aNumItems)
   635   {
   636     let gl = this._context;
   638     // the aNumItems parameter is optional, we can compute it if not specified
   639     aNumItems = aNumItems || aElementsArray.length / aItemSize;
   641     // create the Float32Array using the elements array
   642     this.components = new Float32Array(aElementsArray);
   644     // create an array buffer and bind the elements as a Float32Array
   645     this._ref = gl.createBuffer();
   646     gl.bindBuffer(gl.ARRAY_BUFFER, this._ref);
   647     gl.bufferData(gl.ARRAY_BUFFER, this.components, gl.STATIC_DRAW);
   649     // remember some properties, useful when binding the buffer to a shader
   650     this.itemSize = aItemSize;
   651     this.numItems = aNumItems;
   652   },
   654   /**
   655    * Function called when this object is destroyed.
   656    */
   657   finalize: function TGLVB_finalize()
   658   {
   659     if (this._context) {
   660       this._context.deleteBuffer(this._ref);
   661     }
   662   }
   663 };
   665 /**
   666  * Creates an index buffer containing an array of indices.
   667  *
   668  * @param {Object} aContext
   669  *                 a WebGL context
   670  * @param {Array} aElementsArray
   671  *                an array of unsigned integers
   672  * @param {Number} aNumItems
   673  *                 optional, how many items to use from the array
   674  */
   675 TiltGL.IndexBuffer = function TGL_IndexBuffer(
   676   aContext, aElementsArray, aNumItems)
   677 {
   678   /**
   679    * The parent WebGL context.
   680    */
   681   this._context = aContext;
   683   /**
   684    * The element array buffer.
   685    */
   686   this._ref = null;
   688   /**
   689    * Array of number components contained in the buffer.
   690    */
   691   this.components = null;
   693   /**
   694    * Variables defining the internal structure of the buffer.
   695    */
   696   this.itemSize = 0;
   697   this.numItems = 0;
   699   // if the array is specified in the constructor, initialize directly
   700   if (aElementsArray) {
   701     this.initBuffer(aElementsArray, aNumItems);
   702   }
   703 };
   705 TiltGL.IndexBuffer.prototype = {
   707   /**
   708    * Initializes a buffer of vertex indices, using an array of unsigned ints.
   709    * The item size will automatically default to 1, and the "numItems" will be
   710    * equal to the number of items in the array if not specified.
   711    *
   712    * @param {Array} aElementsArray
   713    *                an array of numbers (unsigned integers)
   714    * @param {Number} aNumItems
   715    *                 optional, how many items to use from the array
   716    */
   717   initBuffer: function TGLIB_initBuffer(aElementsArray, aNumItems)
   718   {
   719     let gl = this._context;
   721     // the aNumItems parameter is optional, we can compute it if not specified
   722     aNumItems = aNumItems || aElementsArray.length;
   724     // create the Uint16Array using the elements array
   725     this.components = new Uint16Array(aElementsArray);
   727     // create an array buffer and bind the elements as a Uint16Array
   728     this._ref = gl.createBuffer();
   729     gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this._ref);
   730     gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this.components, gl.STATIC_DRAW);
   732     // remember some properties, useful when binding the buffer to a shader
   733     this.itemSize = 1;
   734     this.numItems = aNumItems;
   735   },
   737   /**
   738    * Function called when this object is destroyed.
   739    */
   740   finalize: function TGLIB_finalize()
   741   {
   742     if (this._context) {
   743       this._context.deleteBuffer(this._ref);
   744     }
   745   }
   746 };
   748 /**
   749  * A program is composed of a vertex and a fragment shader.
   750  *
   751  * @param {Object} aProperties
   752  *                 optional, an object containing the following properties:
   753  *        {String} vs: the vertex shader source code
   754  *        {String} fs: the fragment shader source code
   755  *         {Array} attributes: an array of attributes as strings
   756  *         {Array} uniforms: an array of uniforms as strings
   757  */
   758 TiltGL.Program = function(aContext, aProperties)
   759 {
   760   // make sure the properties parameter is a valid object
   761   aProperties = aProperties || {};
   763   /**
   764    * The parent WebGL context.
   765    */
   766   this._context = aContext;
   768   /**
   769    * A reference to the actual GLSL program.
   770    */
   771   this._ref = null;
   773   /**
   774    * Each program has an unique id assigned.
   775    */
   776   this._id = -1;
   778   /**
   779    * Two arrays: an attributes array, containing all the cached attributes
   780    * and a uniforms array, containing all the cached uniforms.
   781    */
   782   this._attributes = null;
   783   this._uniforms = null;
   785   // if the sources are specified in the constructor, initialize directly
   786   if (aProperties.vs && aProperties.fs) {
   787     this.initProgram(aProperties);
   788   }
   789 };
   791 TiltGL.Program.prototype = {
   793   /**
   794    * Initializes a shader program, using specified source code as strings.
   795    *
   796    * @param {Object} aProperties
   797    *                 an object containing the following properties:
   798    *        {String} vs: the vertex shader source code
   799    *        {String} fs: the fragment shader source code
   800    *         {Array} attributes: an array of attributes as strings
   801    *         {Array} uniforms: an array of uniforms as strings
   802    */
   803   initProgram: function TGLP_initProgram(aProperties)
   804   {
   805     this._ref = TiltGL.ProgramUtils.create(this._context, aProperties);
   807     // cache for faster access
   808     this._id = this._ref.id;
   809     this._attributes = this._ref.attributes;
   810     this._uniforms = this._ref.uniforms;
   812     // cleanup
   813     delete this._ref.id;
   814     delete this._ref.attributes;
   815     delete this._ref.uniforms;
   816   },
   818   /**
   819    * Uses the shader program as current one for the WebGL context; it also
   820    * enables vertex attributes necessary to enable when using this program.
   821    * This method also does some useful caching, as the function "useProgram"
   822    * could take quite a lot of time.
   823    */
   824   use: function TGLP_use()
   825   {
   826     let id = this._id;
   827     let utils = TiltGL.ProgramUtils;
   829     // check if the program wasn't already active
   830     if (utils._activeProgram !== id) {
   831       utils._activeProgram = id;
   833       // use the the program if it wasn't already set
   834       this._context.useProgram(this._ref);
   835       this.cleanupVertexAttrib();
   837       // enable any necessary vertex attributes using the cache
   838       for each (let attribute in this._attributes) {
   839         this._context.enableVertexAttribArray(attribute);
   840         utils._enabledAttributes.push(attribute);
   841       }
   842     }
   843   },
   845   /**
   846    * Disables all currently enabled vertex attribute arrays.
   847    */
   848   cleanupVertexAttrib: function TGLP_cleanupVertexAttrib()
   849   {
   850     let utils = TiltGL.ProgramUtils;
   852     for each (let attribute in utils._enabledAttributes) {
   853       this._context.disableVertexAttribArray(attribute);
   854     }
   855     utils._enabledAttributes = [];
   856   },
   858   /**
   859    * Binds a vertex buffer as an array buffer for a specific shader attribute.
   860    *
   861    * @param {String} aAtribute
   862    *                 the attribute name obtained from the shader
   863    * @param {Float32Array} aBuffer
   864    *                       the buffer to be bound
   865    */
   866   bindVertexBuffer: function TGLP_bindVertexBuffer(aAtribute, aBuffer)
   867   {
   868     // get the cached attribute value from the shader
   869     let gl = this._context;
   870     let attr = this._attributes[aAtribute];
   871     let size = aBuffer.itemSize;
   873     gl.bindBuffer(gl.ARRAY_BUFFER, aBuffer._ref);
   874     gl.vertexAttribPointer(attr, size, gl.FLOAT, false, 0, 0);
   875   },
   877   /**
   878    * Binds a uniform matrix to the current shader.
   879    *
   880    * @param {String} aUniform
   881    *                 the uniform name to bind the variable to
   882    * @param {Float32Array} m
   883    *                       the matrix to be bound
   884    */
   885   bindUniformMatrix: function TGLP_bindUniformMatrix(aUniform, m)
   886   {
   887     this._context.uniformMatrix4fv(this._uniforms[aUniform], false, m);
   888   },
   890   /**
   891    * Binds a uniform vector of 4 elements to the current shader.
   892    *
   893    * @param {String} aUniform
   894    *                 the uniform name to bind the variable to
   895    * @param {Float32Array} v
   896    *                       the vector to be bound
   897    */
   898   bindUniformVec4: function TGLP_bindUniformVec4(aUniform, v)
   899   {
   900     this._context.uniform4fv(this._uniforms[aUniform], v);
   901   },
   903   /**
   904    * Binds a simple float element to the current shader.
   905    *
   906    * @param {String} aUniform
   907    *                 the uniform name to bind the variable to
   908    * @param {Number} v
   909    *                 the variable to be bound
   910    */
   911   bindUniformFloat: function TGLP_bindUniformFloat(aUniform, f)
   912   {
   913     this._context.uniform1f(this._uniforms[aUniform], f);
   914   },
   916   /**
   917    * Binds a uniform texture for a sampler to the current shader.
   918    *
   919    * @param {String} aSampler
   920    *                 the sampler name to bind the texture to
   921    * @param {TiltGL.Texture} aTexture
   922    *                       the texture to be bound
   923    */
   924   bindTexture: function TGLP_bindTexture(aSampler, aTexture)
   925   {
   926     let gl = this._context;
   928     gl.activeTexture(gl.TEXTURE0);
   929     gl.bindTexture(gl.TEXTURE_2D, aTexture._ref);
   930     gl.uniform1i(this._uniforms[aSampler], 0);
   931   },
   933   /**
   934    * Function called when this object is destroyed.
   935    */
   936   finalize: function TGLP_finalize()
   937   {
   938     if (this._context) {
   939       this._context.useProgram(null);
   940       this._context.deleteProgram(this._ref);
   941     }
   942   }
   943 };
   945 /**
   946  * Utility functions for handling GLSL shaders and programs.
   947  */
   948 TiltGL.ProgramUtils = {
   950   /**
   951    * Initializes a shader program, using specified source code as strings,
   952    * returning the newly created shader program, by compiling and linking the
   953    * vertex and fragment shader.
   954    *
   955    * @param {Object} aContext
   956    *                 a WebGL context
   957    * @param {Object} aProperties
   958    *                 an object containing the following properties:
   959    *        {String} vs: the vertex shader source code
   960    *        {String} fs: the fragment shader source code
   961    *         {Array} attributes: an array of attributes as strings
   962    *         {Array} uniforms: an array of uniforms as strings
   963    */
   964   create: function TGLPU_create(aContext, aProperties)
   965   {
   966     // make sure the properties parameter is a valid object
   967     aProperties = aProperties || {};
   969     // compile the two shaders
   970     let vertShader = this.compile(aContext, aProperties.vs, "vertex");
   971     let fragShader = this.compile(aContext, aProperties.fs, "fragment");
   972     let program = this.link(aContext, vertShader, fragShader);
   974     aContext.deleteShader(vertShader);
   975     aContext.deleteShader(fragShader);
   977     return this.cache(aContext, aProperties, program);
   978   },
   980   /**
   981    * Compiles a shader source of a specific type, either vertex or fragment.
   982    *
   983    * @param {Object} aContext
   984    *                 a WebGL context
   985    * @param {String} aShaderSource
   986    *                 the source code for the shader
   987    * @param {String} aShaderType
   988    *                 the shader type ("vertex" or "fragment")
   989    *
   990    * @return {WebGLShader} the compiled shader
   991    */
   992   compile: function TGLPU_compile(aContext, aShaderSource, aShaderType)
   993   {
   994     let gl = aContext, shader, status;
   996     // make sure the shader source is valid
   997     if ("string" !== typeof aShaderSource || aShaderSource.length < 1) {
   998       TiltUtils.Output.error(
   999         TiltUtils.L10n.get("compileShader.source.error"));
  1000       return null;
  1003     // also make sure the necessary shader mime type is valid
  1004     if (aShaderType === "vertex") {
  1005       shader = gl.createShader(gl.VERTEX_SHADER);
  1006     } else if (aShaderType === "fragment") {
  1007       shader = gl.createShader(gl.FRAGMENT_SHADER);
  1008     } else {
  1009       TiltUtils.Output.error(
  1010         TiltUtils.L10n.format("compileShader.type.error", [aShaderSource]));
  1011       return null;
  1014     // set the shader source and compile it
  1015     gl.shaderSource(shader, aShaderSource);
  1016     gl.compileShader(shader);
  1018     // remember the shader source (useful for debugging and caching)
  1019     shader.src = aShaderSource;
  1021     // verify the compile status; if something went wrong, log the error
  1022     if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
  1023       status = gl.getShaderInfoLog(shader);
  1025       TiltUtils.Output.error(
  1026         TiltUtils.L10n.format("compileShader.compile.error", [status]));
  1027       return null;
  1030     // return the newly compiled shader from the specified source
  1031     return shader;
  1032   },
  1034   /**
  1035    * Links two compiled vertex or fragment shaders together to form a program.
  1037    * @param {Object} aContext
  1038    *                 a WebGL context
  1039    * @param {WebGLShader} aVertShader
  1040    *                      the compiled vertex shader
  1041    * @param {WebGLShader} aFragShader
  1042    *                      the compiled fragment shader
  1044    * @return {WebGLProgram} the newly created and linked shader program
  1045    */
  1046   link: function TGLPU_link(aContext, aVertShader, aFragShader)
  1048     let gl = aContext, program, status;
  1050     // create a program and attach the compiled vertex and fragment shaders
  1051     program = gl.createProgram();
  1053     // attach the vertex and fragment shaders to the program
  1054     gl.attachShader(program, aVertShader);
  1055     gl.attachShader(program, aFragShader);
  1056     gl.linkProgram(program);
  1058     // verify the link status; if something went wrong, log the error
  1059     if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
  1060       status = gl.getProgramInfoLog(program);
  1062       TiltUtils.Output.error(
  1063         TiltUtils.L10n.format("linkProgram.error", [status]));
  1064       return null;
  1067     // generate an id for the program
  1068     program.id = this._count++;
  1070     return program;
  1071   },
  1073   /**
  1074    * Caches shader attributes and uniforms as properties for a program object.
  1076    * @param {Object} aContext
  1077    *                 a WebGL context
  1078    * @param {Object} aProperties
  1079    *                 an object containing the following properties:
  1080    *         {Array} attributes: optional, an array of attributes as strings
  1081    *         {Array} uniforms: optional, an array of uniforms as strings
  1082    * @param {WebGLProgram} aProgram
  1083    *                       the shader program used for caching
  1085    * @return {WebGLProgram} the same program
  1086    */
  1087   cache: function TGLPU_cache(aContext, aProperties, aProgram)
  1089     // make sure the properties parameter is a valid object
  1090     aProperties = aProperties || {};
  1092     // make sure the attributes and uniforms cache objects are created
  1093     aProgram.attributes = {};
  1094     aProgram.uniforms = {};
  1096     Object.defineProperty(aProgram.attributes, "length",
  1097       { value: 0, writable: true, enumerable: false, configurable: true });
  1099     Object.defineProperty(aProgram.uniforms, "length",
  1100       { value: 0, writable: true, enumerable: false, configurable: true });
  1103     let attr = aProperties.attributes;
  1104     let unif = aProperties.uniforms;
  1106     if (attr) {
  1107       for (let i = 0, len = attr.length; i < len; i++) {
  1108         // try to get a shader attribute from the program
  1109         let param = attr[i];
  1110         let loc = aContext.getAttribLocation(aProgram, param);
  1112         if ("number" === typeof loc && loc > -1) {
  1113           // if we get an attribute location, store it
  1114           // bind the new parameter only if it was not already defined
  1115           if (aProgram.attributes[param] === undefined) {
  1116             aProgram.attributes[param] = loc;
  1117             aProgram.attributes.length++;
  1123     if (unif) {
  1124       for (let i = 0, len = unif.length; i < len; i++) {
  1125         // try to get a shader uniform from the program
  1126         let param = unif[i];
  1127         let loc = aContext.getUniformLocation(aProgram, param);
  1129         if ("object" === typeof loc && loc) {
  1130           // if we get a uniform object, store it
  1131           // bind the new parameter only if it was not already defined
  1132           if (aProgram.uniforms[param] === undefined) {
  1133             aProgram.uniforms[param] = loc;
  1134             aProgram.uniforms.length++;
  1140     return aProgram;
  1141   },
  1143   /**
  1144    * The total number of programs created.
  1145    */
  1146   _count: 0,
  1148   /**
  1149    * Represents the current active shader, identified by an id.
  1150    */
  1151   _activeProgram: -1,
  1153   /**
  1154    * Represents the current enabled attributes.
  1155    */
  1156   _enabledAttributes: []
  1157 };
  1159 /**
  1160  * This constructor creates a texture from an Image.
  1162  * @param {Object} aContext
  1163  *                 a WebGL context
  1164  * @param {Object} aProperties
  1165  *                 optional, an object containing the following properties:
  1166  *         {Image} source: the source image for the texture
  1167  *        {String} format: the format of the texture ("RGB" or "RGBA")
  1168  *        {String} fill: optional, color to fill the transparent bits
  1169  *        {String} stroke: optional, color to draw an outline
  1170  *        {Number} strokeWeight: optional, the width of the outline
  1171  *        {String} minFilter: either "nearest" or "linear"
  1172  *        {String} magFilter: either "nearest" or "linear"
  1173  *        {String} wrapS: either "repeat" or "clamp"
  1174  *        {String} wrapT: either "repeat" or "clamp"
  1175  *       {Boolean} mipmap: true if should generate mipmap
  1176  */
  1177 TiltGL.Texture = function(aContext, aProperties)
  1179   // make sure the properties parameter is a valid object
  1180   aProperties = aProperties || {};
  1182   /**
  1183    * The parent WebGL context.
  1184    */
  1185   this._context = aContext;
  1187   /**
  1188    * A reference to the WebGL texture object.
  1189    */
  1190   this._ref = null;
  1192   /**
  1193    * Each texture has an unique id assigned.
  1194    */
  1195   this._id = -1;
  1197   /**
  1198    * Variables specifying the width and height of the texture.
  1199    * If these values are less than 0, the texture hasn't loaded yet.
  1200    */
  1201   this.width = -1;
  1202   this.height = -1;
  1204   /**
  1205    * Specifies if the texture has loaded or not.
  1206    */
  1207   this.loaded = false;
  1209   // if the image is specified in the constructor, initialize directly
  1210   if ("object" === typeof aProperties.source) {
  1211     this.initTexture(aProperties);
  1212   } else {
  1213     TiltUtils.Output.error(
  1214       TiltUtils.L10n.get("initTexture.source.error"));
  1216 };
  1218 TiltGL.Texture.prototype = {
  1220   /**
  1221    * Initializes a texture from a pre-existing image or canvas.
  1223    * @param {Image} aImage
  1224    *                the source image or canvas
  1225    * @param {Object} aProperties
  1226    *                 an object containing the following properties:
  1227    *         {Image} source: the source image for the texture
  1228    *        {String} format: the format of the texture ("RGB" or "RGBA")
  1229    *        {String} fill: optional, color to fill the transparent bits
  1230    *        {String} stroke: optional, color to draw an outline
  1231    *        {Number} strokeWeight: optional, the width of the outline
  1232    *        {String} minFilter: either "nearest" or "linear"
  1233    *        {String} magFilter: either "nearest" or "linear"
  1234    *        {String} wrapS: either "repeat" or "clamp"
  1235    *        {String} wrapT: either "repeat" or "clamp"
  1236    *       {Boolean} mipmap: true if should generate mipmap
  1237    */
  1238   initTexture: function TGLT_initTexture(aProperties)
  1240     this._ref = TiltGL.TextureUtils.create(this._context, aProperties);
  1242     // cache for faster access
  1243     this._id = this._ref.id;
  1244     this.width = this._ref.width;
  1245     this.height = this._ref.height;
  1246     this.loaded = true;
  1248     // cleanup
  1249     delete this._ref.id;
  1250     delete this._ref.width;
  1251     delete this._ref.height;
  1252     delete this.onload;
  1253   },
  1255   /**
  1256    * Function called when this object is destroyed.
  1257    */
  1258   finalize: function TGLT_finalize()
  1260     if (this._context) {
  1261       this._context.deleteTexture(this._ref);
  1264 };
  1266 /**
  1267  * Utility functions for creating and manipulating textures.
  1268  */
  1269 TiltGL.TextureUtils = {
  1271   /**
  1272    * Initializes a texture from a pre-existing image or canvas.
  1274    * @param {Object} aContext
  1275    *                 a WebGL context
  1276    * @param {Image} aImage
  1277    *                the source image or canvas
  1278    * @param {Object} aProperties
  1279    *                 an object containing some of the following properties:
  1280    *         {Image} source: the source image for the texture
  1281    *        {String} format: the format of the texture ("RGB" or "RGBA")
  1282    *        {String} fill: optional, color to fill the transparent bits
  1283    *        {String} stroke: optional, color to draw an outline
  1284    *        {Number} strokeWeight: optional, the width of the outline
  1285    *        {String} minFilter: either "nearest" or "linear"
  1286    *        {String} magFilter: either "nearest" or "linear"
  1287    *        {String} wrapS: either "repeat" or "clamp"
  1288    *        {String} wrapT: either "repeat" or "clamp"
  1289    *       {Boolean} mipmap: true if should generate mipmap
  1291    * @return {WebGLTexture} the created texture
  1292    */
  1293   create: function TGLTU_create(aContext, aProperties)
  1295     // make sure the properties argument is an object
  1296     aProperties = aProperties || {};
  1298     if (!aProperties.source) {
  1299       return null;
  1302     let gl = aContext;
  1303     let width = aProperties.source.width;
  1304     let height = aProperties.source.height;
  1305     let format = gl[aProperties.format || "RGB"];
  1307     // make sure the image is power of two before binding to a texture
  1308     let source = this.resizeImageToPowerOfTwo(aProperties);
  1310     // first, create the texture to hold the image data
  1311     let texture = gl.createTexture();
  1313     // attach the image data to the newly create texture
  1314     gl.bindTexture(gl.TEXTURE_2D, texture);
  1315     gl.texImage2D(gl.TEXTURE_2D, 0, format, format, gl.UNSIGNED_BYTE, source);
  1316     this.setTextureParams(gl, aProperties);
  1318     // do some cleanup
  1319     gl.bindTexture(gl.TEXTURE_2D, null);
  1321     // remember the width and the height
  1322     texture.width = width;
  1323     texture.height = height;
  1325     // generate an id for the texture
  1326     texture.id = this._count++;
  1328     return texture;
  1329   },
  1331   /**
  1332    * Sets texture parameters for the current texture binding.
  1333    * Optionally, you can also (re)set the current texture binding manually.
  1335    * @param {Object} aContext
  1336    *                 a WebGL context
  1337    * @param {Object} aProperties
  1338    *                 an object containing the texture properties
  1339    */
  1340   setTextureParams: function TGLTU_setTextureParams(aContext, aProperties)
  1342     // make sure the properties argument is an object
  1343     aProperties = aProperties || {};
  1345     let gl = aContext;
  1346     let minFilter = gl.TEXTURE_MIN_FILTER;
  1347     let magFilter = gl.TEXTURE_MAG_FILTER;
  1348     let wrapS = gl.TEXTURE_WRAP_S;
  1349     let wrapT = gl.TEXTURE_WRAP_T;
  1351     // bind a new texture if necessary
  1352     if (aProperties.texture) {
  1353       gl.bindTexture(gl.TEXTURE_2D, aProperties.texture.ref);
  1356     // set the minification filter
  1357     if ("nearest" === aProperties.minFilter) {
  1358       gl.texParameteri(gl.TEXTURE_2D, minFilter, gl.NEAREST);
  1359     } else if ("linear" === aProperties.minFilter && aProperties.mipmap) {
  1360       gl.texParameteri(gl.TEXTURE_2D, minFilter, gl.LINEAR_MIPMAP_LINEAR);
  1361     } else {
  1362       gl.texParameteri(gl.TEXTURE_2D, minFilter, gl.LINEAR);
  1365     // set the magnification filter
  1366     if ("nearest" === aProperties.magFilter) {
  1367       gl.texParameteri(gl.TEXTURE_2D, magFilter, gl.NEAREST);
  1368     } else {
  1369       gl.texParameteri(gl.TEXTURE_2D, magFilter, gl.LINEAR);
  1372     // set the wrapping on the x-axis for the texture
  1373     if ("repeat" === aProperties.wrapS) {
  1374       gl.texParameteri(gl.TEXTURE_2D, wrapS, gl.REPEAT);
  1375     } else {
  1376       gl.texParameteri(gl.TEXTURE_2D, wrapS, gl.CLAMP_TO_EDGE);
  1379     // set the wrapping on the y-axis for the texture
  1380     if ("repeat" === aProperties.wrapT) {
  1381       gl.texParameteri(gl.TEXTURE_2D, wrapT, gl.REPEAT);
  1382     } else {
  1383       gl.texParameteri(gl.TEXTURE_2D, wrapT, gl.CLAMP_TO_EDGE);
  1386     // generate mipmap if necessary
  1387     if (aProperties.mipmap) {
  1388       gl.generateMipmap(gl.TEXTURE_2D);
  1390   },
  1392   /**
  1393    * This shim renders a content window to a canvas element, but clamps the
  1394    * maximum width and height of the canvas to the WebGL MAX_TEXTURE_SIZE.
  1396    * @param {Window} aContentWindow
  1397    *                 the content window to get a texture from
  1398    * @param {Number} aMaxImageSize
  1399    *                 the maximum image size to be used
  1401    * @return {Image} the new content window image
  1402    */
  1403   createContentImage: function TGLTU_createContentImage(
  1404     aContentWindow, aMaxImageSize)
  1406     // calculate the total width and height of the content page
  1407     let size = TiltUtils.DOM.getContentWindowDimensions(aContentWindow);
  1409     // use a custom canvas element and a 2d context to draw the window
  1410     let canvas = TiltUtils.DOM.initCanvas(null);
  1411     canvas.width = TiltMath.clamp(size.width, 0, aMaxImageSize);
  1412     canvas.height = TiltMath.clamp(size.height, 0, aMaxImageSize);
  1414     // use the 2d context.drawWindow() magic
  1415     let ctx = canvas.getContext("2d");
  1416     ctx.drawWindow(aContentWindow, 0, 0, canvas.width, canvas.height, "#fff");
  1418     return canvas;
  1419   },
  1421   /**
  1422    * Scales an image's width and height to next power of two.
  1423    * If the image already has power of two sizes, it is immediately returned,
  1424    * otherwise, a new image is created.
  1426    * @param {Image} aImage
  1427    *                the image to be scaled
  1428    * @param {Object} aProperties
  1429    *                 an object containing the following properties:
  1430    *         {Image} source: the source image to resize
  1431    *       {Boolean} resize: true to resize the image if it has npot dimensions
  1432    *        {String} fill: optional, color to fill the transparent bits
  1433    *        {String} stroke: optional, color to draw an image outline
  1434    *        {Number} strokeWeight: optional, the width of the outline
  1436    * @return {Image} the resized image
  1437    */
  1438   resizeImageToPowerOfTwo: function TGLTU_resizeImageToPowerOfTwo(aProperties)
  1440     // make sure the properties argument is an object
  1441     aProperties = aProperties || {};
  1443     if (!aProperties.source) {
  1444       return null;
  1447     let isPowerOfTwoWidth = TiltMath.isPowerOfTwo(aProperties.source.width);
  1448     let isPowerOfTwoHeight = TiltMath.isPowerOfTwo(aProperties.source.height);
  1450     // first check if the image is not already power of two
  1451     if (!aProperties.resize || (isPowerOfTwoWidth && isPowerOfTwoHeight)) {
  1452       return aProperties.source;
  1455     // calculate the power of two dimensions for the npot image
  1456     let width = TiltMath.nextPowerOfTwo(aProperties.source.width);
  1457     let height = TiltMath.nextPowerOfTwo(aProperties.source.height);
  1459     // create a canvas, then we will use a 2d context to scale the image
  1460     let canvas = TiltUtils.DOM.initCanvas(null, {
  1461       width: width,
  1462       height: height
  1463     });
  1465     let ctx = canvas.getContext("2d");
  1467     // optional fill (useful when handling transparent images)
  1468     if (aProperties.fill) {
  1469       ctx.fillStyle = aProperties.fill;
  1470       ctx.fillRect(0, 0, width, height);
  1473     // draw the image with power of two dimensions
  1474     ctx.drawImage(aProperties.source, 0, 0, width, height);
  1476     // optional stroke (useful when creating textures for edges)
  1477     if (aProperties.stroke) {
  1478       ctx.strokeStyle = aProperties.stroke;
  1479       ctx.lineWidth = aProperties.strokeWeight;
  1480       ctx.strokeRect(0, 0, width, height);
  1483     return canvas;
  1484   },
  1486   /**
  1487    * The total number of textures created.
  1488    */
  1489   _count: 0
  1490 };
  1492 /**
  1493  * A color shader. The only useful thing it does is set the gl_FragColor.
  1495  * @param {Attribute} vertexPosition: the vertex position
  1496  * @param {Uniform} mvMatrix: the model view matrix
  1497  * @param {Uniform} projMatrix: the projection matrix
  1498  * @param {Uniform} color: the color to set the gl_FragColor to
  1499  */
  1500 TiltGL.ColorShader = {
  1502   /**
  1503    * Vertex shader.
  1504    */
  1505   vs: [
  1506     "attribute vec3 vertexPosition;",
  1508     "uniform mat4 mvMatrix;",
  1509     "uniform mat4 projMatrix;",
  1511     "void main() {",
  1512     "    gl_Position = projMatrix * mvMatrix * vec4(vertexPosition, 1.0);",
  1513     "}"
  1514   ].join("\n"),
  1516   /**
  1517    * Fragment shader.
  1518    */
  1519   fs: [
  1520     "#ifdef GL_ES",
  1521     "precision lowp float;",
  1522     "#endif",
  1524     "uniform vec4 fill;",
  1526     "void main() {",
  1527     "    gl_FragColor = fill;",
  1528     "}"
  1529   ].join("\n")
  1530 };
  1532 TiltGL.isWebGLForceEnabled = function TGL_isWebGLForceEnabled()
  1534   return Services.prefs.getBoolPref("webgl.force-enabled");
  1535 };
  1537 /**
  1538  * Tests if the WebGL OpenGL or Angle renderer is available using the
  1539  * GfxInfo service.
  1541  * @return {Boolean} true if WebGL is available
  1542  */
  1543 TiltGL.isWebGLSupported = function TGL_isWebGLSupported()
  1545   let supported = false;
  1547   try {
  1548     let gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo);
  1549     let angle = gfxInfo.FEATURE_WEBGL_ANGLE;
  1550     let opengl = gfxInfo.FEATURE_WEBGL_OPENGL;
  1552     // if either the Angle or OpenGL renderers are available, WebGL should work
  1553     supported = gfxInfo.getFeatureStatus(angle) === gfxInfo.FEATURE_NO_INFO ||
  1554                 gfxInfo.getFeatureStatus(opengl) === gfxInfo.FEATURE_NO_INFO;
  1555   } catch(e) {
  1556     if (e && e.message) { TiltUtils.Output.error(e.message); }
  1557     return false;
  1559   return supported;
  1560 };
  1562 /**
  1563  * Helper function to create a 3D context.
  1565  * @param {HTMLCanvasElement} aCanvas
  1566  *                            the canvas to get the WebGL context from
  1567  * @param {Object} aFlags
  1568  *                 optional, flags used for initialization
  1570  * @return {Object} the WebGL context, or null if anything failed
  1571  */
  1572 TiltGL.create3DContext = function TGL_create3DContext(aCanvas, aFlags)
  1574   TiltGL.clearCache();
  1576   // try to get a valid context from an existing canvas
  1577   let context = null;
  1579   try {
  1580     context = aCanvas.getContext(WEBGL_CONTEXT_NAME, aFlags);
  1581   } catch(e) {
  1582     if (e && e.message) { TiltUtils.Output.error(e.message); }
  1583     return null;
  1585   return context;
  1586 };
  1588 /**
  1589  * Clears the cache and sets all the variables to default.
  1590  */
  1591 TiltGL.clearCache = function TGL_clearCache()
  1593   TiltGL.ProgramUtils._activeProgram = -1;
  1594   TiltGL.ProgramUtils._enabledAttributes = [];
  1595 };

mercurial