diff --git a/README.md b/README.md index 1bd2045..edc54ab 100644 --- a/README.md +++ b/README.md @@ -109,10 +109,13 @@ // You need to create a root container that will hold the scene you want to draw. var stage = new PIXI.Container(); +// Declare a global variable for our sprite so that the animate function can access it. +var bunny = null; + // load the texture we need PIXI.loader.add('bunny', 'bunny.png').load(function (loader, resources) { // This creates a texture from a 'bunny.png' image. - var bunny = new PIXI.Sprite(resources.bunny.texture); + bunny = new PIXI.Sprite(resources.bunny.texture); // Setup the position and scale of the bunny bunny.position.x = 400; diff --git a/README.md b/README.md index 1bd2045..edc54ab 100644 --- a/README.md +++ b/README.md @@ -109,10 +109,13 @@ // You need to create a root container that will hold the scene you want to draw. var stage = new PIXI.Container(); +// Declare a global variable for our sprite so that the animate function can access it. +var bunny = null; + // load the texture we need PIXI.loader.add('bunny', 'bunny.png').load(function (loader, resources) { // This creates a texture from a 'bunny.png' image. - var bunny = new PIXI.Sprite(resources.bunny.texture); + bunny = new PIXI.Sprite(resources.bunny.texture); // Setup the position and scale of the bunny bunny.position.x = 400; diff --git a/package.json b/package.json index 66f3a58..d38da2b 100644 --- a/package.json +++ b/package.json @@ -33,38 +33,38 @@ "async": "^1.5.0", "brfs": "^1.4.1", "browserify-versionify": "^1.0.6", - "earcut": "^2.0.7", + "earcut": "^2.0.8", "eventemitter3": "^1.1.1", "object-assign": "^4.0.1", "resource-loader": "^1.6.4" }, "devDependencies": { - "browserify": "^11.1.0", + "browserify": "^13.0.0", "chai": "^3.2.0", - "del": "^2.0.2", + "del": "^2.2.0", "gulp": "^3.9.0", "gulp-cached": "^1.1.0", "gulp-concat": "^2.6.0", "gulp-debug": "^2.1.0", "gulp-header": "^1.7.1", - "gulp-jshint": "^1.11.2", - "gulp-mirror": "^0.4.0", + "gulp-jshint": "^2.0.0", + "gulp-mirror": "^1.0.0", "gulp-plumber": "^1.0.1", "gulp-rename": "^1.2.2", "gulp-sourcemaps": "^1.5.2", - "gulp-uglify": "^1.4.1", + "gulp-uglify": "^1.5.1", "gulp-util": "^3.0.6", "jaguarjs-jsdoc": "git+https://github.com/davidshimjs/jaguarjs-jsdoc.git", - "jsdoc": "^3.3.2", + "jsdoc": "^3.4.0", "jshint-summary": "^0.4.0", "minimist": "^1.2.0", - "mocha": "^2.3.2", + "mocha": "^2.4.5", "require-dir": "^0.3.0", "run-sequence": "^1.1.2", "testem": "^0.9.4", "vinyl-buffer": "^1.0.0", "vinyl-source-stream": "^1.1.0", - "watchify": "^3.4.0" + "watchify": "^3.7.0" }, "browserify": { "transform": [ diff --git a/README.md b/README.md index 1bd2045..edc54ab 100644 --- a/README.md +++ b/README.md @@ -109,10 +109,13 @@ // You need to create a root container that will hold the scene you want to draw. var stage = new PIXI.Container(); +// Declare a global variable for our sprite so that the animate function can access it. +var bunny = null; + // load the texture we need PIXI.loader.add('bunny', 'bunny.png').load(function (loader, resources) { // This creates a texture from a 'bunny.png' image. - var bunny = new PIXI.Sprite(resources.bunny.texture); + bunny = new PIXI.Sprite(resources.bunny.texture); // Setup the position and scale of the bunny bunny.position.x = 400; diff --git a/package.json b/package.json index 66f3a58..d38da2b 100644 --- a/package.json +++ b/package.json @@ -33,38 +33,38 @@ "async": "^1.5.0", "brfs": "^1.4.1", "browserify-versionify": "^1.0.6", - "earcut": "^2.0.7", + "earcut": "^2.0.8", "eventemitter3": "^1.1.1", "object-assign": "^4.0.1", "resource-loader": "^1.6.4" }, "devDependencies": { - "browserify": "^11.1.0", + "browserify": "^13.0.0", "chai": "^3.2.0", - "del": "^2.0.2", + "del": "^2.2.0", "gulp": "^3.9.0", "gulp-cached": "^1.1.0", "gulp-concat": "^2.6.0", "gulp-debug": "^2.1.0", "gulp-header": "^1.7.1", - "gulp-jshint": "^1.11.2", - "gulp-mirror": "^0.4.0", + "gulp-jshint": "^2.0.0", + "gulp-mirror": "^1.0.0", "gulp-plumber": "^1.0.1", "gulp-rename": "^1.2.2", "gulp-sourcemaps": "^1.5.2", - "gulp-uglify": "^1.4.1", + "gulp-uglify": "^1.5.1", "gulp-util": "^3.0.6", "jaguarjs-jsdoc": "git+https://github.com/davidshimjs/jaguarjs-jsdoc.git", - "jsdoc": "^3.3.2", + "jsdoc": "^3.4.0", "jshint-summary": "^0.4.0", "minimist": "^1.2.0", - "mocha": "^2.3.2", + "mocha": "^2.4.5", "require-dir": "^0.3.0", "run-sequence": "^1.1.2", "testem": "^0.9.4", "vinyl-buffer": "^1.0.0", "vinyl-source-stream": "^1.1.0", - "watchify": "^3.4.0" + "watchify": "^3.7.0" }, "browserify": { "transform": [ diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index fdd49f7..3989125 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -536,6 +536,8 @@ this.gl.useProgram(null); + this.gl.flush(); + this.gl = null; }; diff --git a/README.md b/README.md index 1bd2045..edc54ab 100644 --- a/README.md +++ b/README.md @@ -109,10 +109,13 @@ // You need to create a root container that will hold the scene you want to draw. var stage = new PIXI.Container(); +// Declare a global variable for our sprite so that the animate function can access it. +var bunny = null; + // load the texture we need PIXI.loader.add('bunny', 'bunny.png').load(function (loader, resources) { // This creates a texture from a 'bunny.png' image. - var bunny = new PIXI.Sprite(resources.bunny.texture); + bunny = new PIXI.Sprite(resources.bunny.texture); // Setup the position and scale of the bunny bunny.position.x = 400; diff --git a/package.json b/package.json index 66f3a58..d38da2b 100644 --- a/package.json +++ b/package.json @@ -33,38 +33,38 @@ "async": "^1.5.0", "brfs": "^1.4.1", "browserify-versionify": "^1.0.6", - "earcut": "^2.0.7", + "earcut": "^2.0.8", "eventemitter3": "^1.1.1", "object-assign": "^4.0.1", "resource-loader": "^1.6.4" }, "devDependencies": { - "browserify": "^11.1.0", + "browserify": "^13.0.0", "chai": "^3.2.0", - "del": "^2.0.2", + "del": "^2.2.0", "gulp": "^3.9.0", "gulp-cached": "^1.1.0", "gulp-concat": "^2.6.0", "gulp-debug": "^2.1.0", "gulp-header": "^1.7.1", - "gulp-jshint": "^1.11.2", - "gulp-mirror": "^0.4.0", + "gulp-jshint": "^2.0.0", + "gulp-mirror": "^1.0.0", "gulp-plumber": "^1.0.1", "gulp-rename": "^1.2.2", "gulp-sourcemaps": "^1.5.2", - "gulp-uglify": "^1.4.1", + "gulp-uglify": "^1.5.1", "gulp-util": "^3.0.6", "jaguarjs-jsdoc": "git+https://github.com/davidshimjs/jaguarjs-jsdoc.git", - "jsdoc": "^3.3.2", + "jsdoc": "^3.4.0", "jshint-summary": "^0.4.0", "minimist": "^1.2.0", - "mocha": "^2.3.2", + "mocha": "^2.4.5", "require-dir": "^0.3.0", "run-sequence": "^1.1.2", "testem": "^0.9.4", "vinyl-buffer": "^1.0.0", "vinyl-source-stream": "^1.1.0", - "watchify": "^3.4.0" + "watchify": "^3.7.0" }, "browserify": { "transform": [ diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index fdd49f7..3989125 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -536,6 +536,8 @@ this.gl.useProgram(null); + this.gl.flush(); + this.gl = null; }; diff --git a/src/core/text/Text.js b/src/core/text/Text.js index 2af93f9..3e33926 100644 --- a/src/core/text/Text.js +++ b/src/core/text/Text.js @@ -26,6 +26,8 @@ * @param [style.strokeThickness=0] {number} A number that represents the thickness of the stroke. Default is 0 (no stroke) * @param [style.wordWrap=false] {boolean} Indicates if word wrap should be used * @param [style.wordWrapWidth=100] {number} The width at which text will wrap, it needs wordWrap to be set to true + * @param [style.letterSpacing=0] {number} The amount of spacing between letters, default is 0 + * @param [style.breakWords=false] {boolean} Indicates if lines can be wrapped within words, it needs wordWrap to be set to true * @param [style.lineHeight] {number} The line height, a number that represents the vertical space that a letter uses * @param [style.dropShadow=false] {boolean} Set a drop shadow for the text * @param [style.dropShadowColor='#000000'] {string} A fill style to be used on the dropshadow e.g 'red', '#00FF00' @@ -195,6 +197,8 @@ style.strokeThickness = style.strokeThickness || 0; style.wordWrap = style.wordWrap || false; style.wordWrapWidth = style.wordWrapWidth || 100; + style.breakWords = style.breakWords || false; + style.letterSpacing = style.letterSpacing || 0; style.dropShadow = style.dropShadow || false; style.dropShadowColor = style.dropShadowColor || '#000000'; @@ -260,7 +264,7 @@ var fontProperties = this.determineFontProperties(style.font); for (var i = 0; i < lines.length; i++) { - var lineWidth = this.context.measureText(lines[i]).width; + var lineWidth = this.context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing); lineWidths[i] = lineWidth; maxLineWidth = Math.max(maxLineWidth, lineWidth); } @@ -333,7 +337,7 @@ if (style.fill) { - this.context.fillText(lines[i], linePositionX + xShadowOffset, linePositionY + yShadowOffset + this._style.padding); + this.drawLetterSpacing(lines[i], linePositionX + xShadowOffset, linePositionY + yShadowOffset + style.padding); } } } @@ -358,12 +362,12 @@ if (style.stroke && style.strokeThickness) { - this.context.strokeText(lines[i], linePositionX, linePositionY + this._style.padding); + this.drawLetterSpacing(lines[i], linePositionX, linePositionY + style.padding, true); } if (style.fill) { - this.context.fillText(lines[i], linePositionX, linePositionY + this._style.padding); + this.drawLetterSpacing(lines[i], linePositionX, linePositionY + style.padding); } } @@ -371,6 +375,51 @@ }; /** + * Render the text with letter-spacing. + * + * @private + */ +Text.prototype.drawLetterSpacing = function(text, x, y, isStroke) +{ + var style = this._style; + + // letterSpacing of 0 means normal + var letterSpacing = style.letterSpacing; + + if (letterSpacing === 0) + { + if (isStroke) + { + this.context.strokeText(text, x, y); + } + else + { + this.context.fillText(text, x, y); + } + return; + } + + var characters = String.prototype.split.call(text, ''), + index = 0, + current, + currentPosition = x; + + while (index < text.length) + { + current = characters[index++]; + if (isStroke) + { + this.context.strokeText(current, currentPosition, y); + } + else + { + this.context.fillText(current, currentPosition, y); + } + currentPosition += this.context.measureText(current).width + letterSpacing; + } +}; + +/** * Updates texture size based on canvas size * * @private @@ -378,6 +427,7 @@ Text.prototype.updateTexture = function () { var texture = this._texture; + var style = this._style; texture.baseTexture.hasLoaded = true; texture.baseTexture.resolution = this.resolution; @@ -388,10 +438,10 @@ texture.crop.height = texture._frame.height = this.canvas.height / this.resolution; texture.trim.x = 0; - texture.trim.y = -this._style.padding; + texture.trim.y = -style.padding; texture.trim.width = texture._frame.width; - texture.trim.height = texture._frame.height - this._style.padding*2; + texture.trim.height = texture._frame.height - style.padding*2; this._width = this.canvas.width / this.resolution; this._height = this.canvas.height / this.resolution; @@ -559,22 +609,48 @@ for (var j = 0; j < words.length; j++) { var wordWidth = this.context.measureText(words[j]).width; - var wordWidthWithSpace = wordWidth + this.context.measureText(' ').width; - if (j === 0 || wordWidthWithSpace > spaceLeft) + if (this._style.breakWords && wordWidth > wordWrapWidth) { - // Skip printing the newline if it's the first word of the line that is - // greater than the word wrap width. - if (j > 0) + // Word should be split in the middle + var characters = words[j].split(''); + for (var c = 0; c < characters.length; c++) { - result += '\n'; + var characterWidth = this.context.measureText(characters[c]).width; + if (characterWidth > spaceLeft) + { + result += '\n' + characters[c]; + spaceLeft = wordWrapWidth - characterWidth; + } + else + { + if (c === 0) + { + result += ' '; + } + result += characters[c]; + spaceLeft -= characterWidth; + } } - result += words[j]; - spaceLeft = wordWrapWidth - wordWidth; } - else + else { - spaceLeft -= wordWidthWithSpace; - result += ' ' + words[j]; + var wordWidthWithSpace = wordWidth + this.context.measureText(' ').width; + if (j === 0 || wordWidthWithSpace > spaceLeft) + { + // Skip printing the newline if it's the first word of the line that is + // greater than the word wrap width. + if (j > 0) + { + result += '\n'; + } + result += words[j]; + spaceLeft = wordWrapWidth - wordWidth; + } + else + { + spaceLeft -= wordWidthWithSpace; + result += ' ' + words[j]; + } } } diff --git a/README.md b/README.md index 1bd2045..edc54ab 100644 --- a/README.md +++ b/README.md @@ -109,10 +109,13 @@ // You need to create a root container that will hold the scene you want to draw. var stage = new PIXI.Container(); +// Declare a global variable for our sprite so that the animate function can access it. +var bunny = null; + // load the texture we need PIXI.loader.add('bunny', 'bunny.png').load(function (loader, resources) { // This creates a texture from a 'bunny.png' image. - var bunny = new PIXI.Sprite(resources.bunny.texture); + bunny = new PIXI.Sprite(resources.bunny.texture); // Setup the position and scale of the bunny bunny.position.x = 400; diff --git a/package.json b/package.json index 66f3a58..d38da2b 100644 --- a/package.json +++ b/package.json @@ -33,38 +33,38 @@ "async": "^1.5.0", "brfs": "^1.4.1", "browserify-versionify": "^1.0.6", - "earcut": "^2.0.7", + "earcut": "^2.0.8", "eventemitter3": "^1.1.1", "object-assign": "^4.0.1", "resource-loader": "^1.6.4" }, "devDependencies": { - "browserify": "^11.1.0", + "browserify": "^13.0.0", "chai": "^3.2.0", - "del": "^2.0.2", + "del": "^2.2.0", "gulp": "^3.9.0", "gulp-cached": "^1.1.0", "gulp-concat": "^2.6.0", "gulp-debug": "^2.1.0", "gulp-header": "^1.7.1", - "gulp-jshint": "^1.11.2", - "gulp-mirror": "^0.4.0", + "gulp-jshint": "^2.0.0", + "gulp-mirror": "^1.0.0", "gulp-plumber": "^1.0.1", "gulp-rename": "^1.2.2", "gulp-sourcemaps": "^1.5.2", - "gulp-uglify": "^1.4.1", + "gulp-uglify": "^1.5.1", "gulp-util": "^3.0.6", "jaguarjs-jsdoc": "git+https://github.com/davidshimjs/jaguarjs-jsdoc.git", - "jsdoc": "^3.3.2", + "jsdoc": "^3.4.0", "jshint-summary": "^0.4.0", "minimist": "^1.2.0", - "mocha": "^2.3.2", + "mocha": "^2.4.5", "require-dir": "^0.3.0", "run-sequence": "^1.1.2", "testem": "^0.9.4", "vinyl-buffer": "^1.0.0", "vinyl-source-stream": "^1.1.0", - "watchify": "^3.4.0" + "watchify": "^3.7.0" }, "browserify": { "transform": [ diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index fdd49f7..3989125 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -536,6 +536,8 @@ this.gl.useProgram(null); + this.gl.flush(); + this.gl = null; }; diff --git a/src/core/text/Text.js b/src/core/text/Text.js index 2af93f9..3e33926 100644 --- a/src/core/text/Text.js +++ b/src/core/text/Text.js @@ -26,6 +26,8 @@ * @param [style.strokeThickness=0] {number} A number that represents the thickness of the stroke. Default is 0 (no stroke) * @param [style.wordWrap=false] {boolean} Indicates if word wrap should be used * @param [style.wordWrapWidth=100] {number} The width at which text will wrap, it needs wordWrap to be set to true + * @param [style.letterSpacing=0] {number} The amount of spacing between letters, default is 0 + * @param [style.breakWords=false] {boolean} Indicates if lines can be wrapped within words, it needs wordWrap to be set to true * @param [style.lineHeight] {number} The line height, a number that represents the vertical space that a letter uses * @param [style.dropShadow=false] {boolean} Set a drop shadow for the text * @param [style.dropShadowColor='#000000'] {string} A fill style to be used on the dropshadow e.g 'red', '#00FF00' @@ -195,6 +197,8 @@ style.strokeThickness = style.strokeThickness || 0; style.wordWrap = style.wordWrap || false; style.wordWrapWidth = style.wordWrapWidth || 100; + style.breakWords = style.breakWords || false; + style.letterSpacing = style.letterSpacing || 0; style.dropShadow = style.dropShadow || false; style.dropShadowColor = style.dropShadowColor || '#000000'; @@ -260,7 +264,7 @@ var fontProperties = this.determineFontProperties(style.font); for (var i = 0; i < lines.length; i++) { - var lineWidth = this.context.measureText(lines[i]).width; + var lineWidth = this.context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing); lineWidths[i] = lineWidth; maxLineWidth = Math.max(maxLineWidth, lineWidth); } @@ -333,7 +337,7 @@ if (style.fill) { - this.context.fillText(lines[i], linePositionX + xShadowOffset, linePositionY + yShadowOffset + this._style.padding); + this.drawLetterSpacing(lines[i], linePositionX + xShadowOffset, linePositionY + yShadowOffset + style.padding); } } } @@ -358,12 +362,12 @@ if (style.stroke && style.strokeThickness) { - this.context.strokeText(lines[i], linePositionX, linePositionY + this._style.padding); + this.drawLetterSpacing(lines[i], linePositionX, linePositionY + style.padding, true); } if (style.fill) { - this.context.fillText(lines[i], linePositionX, linePositionY + this._style.padding); + this.drawLetterSpacing(lines[i], linePositionX, linePositionY + style.padding); } } @@ -371,6 +375,51 @@ }; /** + * Render the text with letter-spacing. + * + * @private + */ +Text.prototype.drawLetterSpacing = function(text, x, y, isStroke) +{ + var style = this._style; + + // letterSpacing of 0 means normal + var letterSpacing = style.letterSpacing; + + if (letterSpacing === 0) + { + if (isStroke) + { + this.context.strokeText(text, x, y); + } + else + { + this.context.fillText(text, x, y); + } + return; + } + + var characters = String.prototype.split.call(text, ''), + index = 0, + current, + currentPosition = x; + + while (index < text.length) + { + current = characters[index++]; + if (isStroke) + { + this.context.strokeText(current, currentPosition, y); + } + else + { + this.context.fillText(current, currentPosition, y); + } + currentPosition += this.context.measureText(current).width + letterSpacing; + } +}; + +/** * Updates texture size based on canvas size * * @private @@ -378,6 +427,7 @@ Text.prototype.updateTexture = function () { var texture = this._texture; + var style = this._style; texture.baseTexture.hasLoaded = true; texture.baseTexture.resolution = this.resolution; @@ -388,10 +438,10 @@ texture.crop.height = texture._frame.height = this.canvas.height / this.resolution; texture.trim.x = 0; - texture.trim.y = -this._style.padding; + texture.trim.y = -style.padding; texture.trim.width = texture._frame.width; - texture.trim.height = texture._frame.height - this._style.padding*2; + texture.trim.height = texture._frame.height - style.padding*2; this._width = this.canvas.width / this.resolution; this._height = this.canvas.height / this.resolution; @@ -559,22 +609,48 @@ for (var j = 0; j < words.length; j++) { var wordWidth = this.context.measureText(words[j]).width; - var wordWidthWithSpace = wordWidth + this.context.measureText(' ').width; - if (j === 0 || wordWidthWithSpace > spaceLeft) + if (this._style.breakWords && wordWidth > wordWrapWidth) { - // Skip printing the newline if it's the first word of the line that is - // greater than the word wrap width. - if (j > 0) + // Word should be split in the middle + var characters = words[j].split(''); + for (var c = 0; c < characters.length; c++) { - result += '\n'; + var characterWidth = this.context.measureText(characters[c]).width; + if (characterWidth > spaceLeft) + { + result += '\n' + characters[c]; + spaceLeft = wordWrapWidth - characterWidth; + } + else + { + if (c === 0) + { + result += ' '; + } + result += characters[c]; + spaceLeft -= characterWidth; + } } - result += words[j]; - spaceLeft = wordWrapWidth - wordWidth; } - else + else { - spaceLeft -= wordWidthWithSpace; - result += ' ' + words[j]; + var wordWidthWithSpace = wordWidth + this.context.measureText(' ').width; + if (j === 0 || wordWidthWithSpace > spaceLeft) + { + // Skip printing the newline if it's the first word of the line that is + // greater than the word wrap width. + if (j > 0) + { + result += '\n'; + } + result += words[j]; + spaceLeft = wordWrapWidth - wordWidth; + } + else + { + spaceLeft -= wordWidthWithSpace; + result += ' ' + words[j]; + } } } diff --git a/src/filters/color/colorMatrix.frag b/src/filters/color/colorMatrix.frag index 5de20fc..041f345 100644 --- a/src/filters/color/colorMatrix.frag +++ b/src/filters/color/colorMatrix.frag @@ -13,24 +13,24 @@ gl_FragColor.r += (m[1] * c.g); gl_FragColor.r += (m[2] * c.b); gl_FragColor.r += (m[3] * c.a); - gl_FragColor.r += m[4]; + gl_FragColor.r += m[4] * c.a; gl_FragColor.g = (m[5] * c.r); gl_FragColor.g += (m[6] * c.g); gl_FragColor.g += (m[7] * c.b); gl_FragColor.g += (m[8] * c.a); - gl_FragColor.g += m[9]; + gl_FragColor.g += m[9] * c.a; gl_FragColor.b = (m[10] * c.r); gl_FragColor.b += (m[11] * c.g); gl_FragColor.b += (m[12] * c.b); gl_FragColor.b += (m[13] * c.a); - gl_FragColor.b += m[14]; + gl_FragColor.b += m[14] * c.a; gl_FragColor.a = (m[15] * c.r); gl_FragColor.a += (m[16] * c.g); gl_FragColor.a += (m[17] * c.b); gl_FragColor.a += (m[18] * c.a); - gl_FragColor.a += m[19]; + gl_FragColor.a += m[19] * c.a; } diff --git a/README.md b/README.md index 1bd2045..edc54ab 100644 --- a/README.md +++ b/README.md @@ -109,10 +109,13 @@ // You need to create a root container that will hold the scene you want to draw. var stage = new PIXI.Container(); +// Declare a global variable for our sprite so that the animate function can access it. +var bunny = null; + // load the texture we need PIXI.loader.add('bunny', 'bunny.png').load(function (loader, resources) { // This creates a texture from a 'bunny.png' image. - var bunny = new PIXI.Sprite(resources.bunny.texture); + bunny = new PIXI.Sprite(resources.bunny.texture); // Setup the position and scale of the bunny bunny.position.x = 400; diff --git a/package.json b/package.json index 66f3a58..d38da2b 100644 --- a/package.json +++ b/package.json @@ -33,38 +33,38 @@ "async": "^1.5.0", "brfs": "^1.4.1", "browserify-versionify": "^1.0.6", - "earcut": "^2.0.7", + "earcut": "^2.0.8", "eventemitter3": "^1.1.1", "object-assign": "^4.0.1", "resource-loader": "^1.6.4" }, "devDependencies": { - "browserify": "^11.1.0", + "browserify": "^13.0.0", "chai": "^3.2.0", - "del": "^2.0.2", + "del": "^2.2.0", "gulp": "^3.9.0", "gulp-cached": "^1.1.0", "gulp-concat": "^2.6.0", "gulp-debug": "^2.1.0", "gulp-header": "^1.7.1", - "gulp-jshint": "^1.11.2", - "gulp-mirror": "^0.4.0", + "gulp-jshint": "^2.0.0", + "gulp-mirror": "^1.0.0", "gulp-plumber": "^1.0.1", "gulp-rename": "^1.2.2", "gulp-sourcemaps": "^1.5.2", - "gulp-uglify": "^1.4.1", + "gulp-uglify": "^1.5.1", "gulp-util": "^3.0.6", "jaguarjs-jsdoc": "git+https://github.com/davidshimjs/jaguarjs-jsdoc.git", - "jsdoc": "^3.3.2", + "jsdoc": "^3.4.0", "jshint-summary": "^0.4.0", "minimist": "^1.2.0", - "mocha": "^2.3.2", + "mocha": "^2.4.5", "require-dir": "^0.3.0", "run-sequence": "^1.1.2", "testem": "^0.9.4", "vinyl-buffer": "^1.0.0", "vinyl-source-stream": "^1.1.0", - "watchify": "^3.4.0" + "watchify": "^3.7.0" }, "browserify": { "transform": [ diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index fdd49f7..3989125 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -536,6 +536,8 @@ this.gl.useProgram(null); + this.gl.flush(); + this.gl = null; }; diff --git a/src/core/text/Text.js b/src/core/text/Text.js index 2af93f9..3e33926 100644 --- a/src/core/text/Text.js +++ b/src/core/text/Text.js @@ -26,6 +26,8 @@ * @param [style.strokeThickness=0] {number} A number that represents the thickness of the stroke. Default is 0 (no stroke) * @param [style.wordWrap=false] {boolean} Indicates if word wrap should be used * @param [style.wordWrapWidth=100] {number} The width at which text will wrap, it needs wordWrap to be set to true + * @param [style.letterSpacing=0] {number} The amount of spacing between letters, default is 0 + * @param [style.breakWords=false] {boolean} Indicates if lines can be wrapped within words, it needs wordWrap to be set to true * @param [style.lineHeight] {number} The line height, a number that represents the vertical space that a letter uses * @param [style.dropShadow=false] {boolean} Set a drop shadow for the text * @param [style.dropShadowColor='#000000'] {string} A fill style to be used on the dropshadow e.g 'red', '#00FF00' @@ -195,6 +197,8 @@ style.strokeThickness = style.strokeThickness || 0; style.wordWrap = style.wordWrap || false; style.wordWrapWidth = style.wordWrapWidth || 100; + style.breakWords = style.breakWords || false; + style.letterSpacing = style.letterSpacing || 0; style.dropShadow = style.dropShadow || false; style.dropShadowColor = style.dropShadowColor || '#000000'; @@ -260,7 +264,7 @@ var fontProperties = this.determineFontProperties(style.font); for (var i = 0; i < lines.length; i++) { - var lineWidth = this.context.measureText(lines[i]).width; + var lineWidth = this.context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing); lineWidths[i] = lineWidth; maxLineWidth = Math.max(maxLineWidth, lineWidth); } @@ -333,7 +337,7 @@ if (style.fill) { - this.context.fillText(lines[i], linePositionX + xShadowOffset, linePositionY + yShadowOffset + this._style.padding); + this.drawLetterSpacing(lines[i], linePositionX + xShadowOffset, linePositionY + yShadowOffset + style.padding); } } } @@ -358,12 +362,12 @@ if (style.stroke && style.strokeThickness) { - this.context.strokeText(lines[i], linePositionX, linePositionY + this._style.padding); + this.drawLetterSpacing(lines[i], linePositionX, linePositionY + style.padding, true); } if (style.fill) { - this.context.fillText(lines[i], linePositionX, linePositionY + this._style.padding); + this.drawLetterSpacing(lines[i], linePositionX, linePositionY + style.padding); } } @@ -371,6 +375,51 @@ }; /** + * Render the text with letter-spacing. + * + * @private + */ +Text.prototype.drawLetterSpacing = function(text, x, y, isStroke) +{ + var style = this._style; + + // letterSpacing of 0 means normal + var letterSpacing = style.letterSpacing; + + if (letterSpacing === 0) + { + if (isStroke) + { + this.context.strokeText(text, x, y); + } + else + { + this.context.fillText(text, x, y); + } + return; + } + + var characters = String.prototype.split.call(text, ''), + index = 0, + current, + currentPosition = x; + + while (index < text.length) + { + current = characters[index++]; + if (isStroke) + { + this.context.strokeText(current, currentPosition, y); + } + else + { + this.context.fillText(current, currentPosition, y); + } + currentPosition += this.context.measureText(current).width + letterSpacing; + } +}; + +/** * Updates texture size based on canvas size * * @private @@ -378,6 +427,7 @@ Text.prototype.updateTexture = function () { var texture = this._texture; + var style = this._style; texture.baseTexture.hasLoaded = true; texture.baseTexture.resolution = this.resolution; @@ -388,10 +438,10 @@ texture.crop.height = texture._frame.height = this.canvas.height / this.resolution; texture.trim.x = 0; - texture.trim.y = -this._style.padding; + texture.trim.y = -style.padding; texture.trim.width = texture._frame.width; - texture.trim.height = texture._frame.height - this._style.padding*2; + texture.trim.height = texture._frame.height - style.padding*2; this._width = this.canvas.width / this.resolution; this._height = this.canvas.height / this.resolution; @@ -559,22 +609,48 @@ for (var j = 0; j < words.length; j++) { var wordWidth = this.context.measureText(words[j]).width; - var wordWidthWithSpace = wordWidth + this.context.measureText(' ').width; - if (j === 0 || wordWidthWithSpace > spaceLeft) + if (this._style.breakWords && wordWidth > wordWrapWidth) { - // Skip printing the newline if it's the first word of the line that is - // greater than the word wrap width. - if (j > 0) + // Word should be split in the middle + var characters = words[j].split(''); + for (var c = 0; c < characters.length; c++) { - result += '\n'; + var characterWidth = this.context.measureText(characters[c]).width; + if (characterWidth > spaceLeft) + { + result += '\n' + characters[c]; + spaceLeft = wordWrapWidth - characterWidth; + } + else + { + if (c === 0) + { + result += ' '; + } + result += characters[c]; + spaceLeft -= characterWidth; + } } - result += words[j]; - spaceLeft = wordWrapWidth - wordWidth; } - else + else { - spaceLeft -= wordWidthWithSpace; - result += ' ' + words[j]; + var wordWidthWithSpace = wordWidth + this.context.measureText(' ').width; + if (j === 0 || wordWidthWithSpace > spaceLeft) + { + // Skip printing the newline if it's the first word of the line that is + // greater than the word wrap width. + if (j > 0) + { + result += '\n'; + } + result += words[j]; + spaceLeft = wordWrapWidth - wordWidth; + } + else + { + spaceLeft -= wordWidthWithSpace; + result += ' ' + words[j]; + } } } diff --git a/src/filters/color/colorMatrix.frag b/src/filters/color/colorMatrix.frag index 5de20fc..041f345 100644 --- a/src/filters/color/colorMatrix.frag +++ b/src/filters/color/colorMatrix.frag @@ -13,24 +13,24 @@ gl_FragColor.r += (m[1] * c.g); gl_FragColor.r += (m[2] * c.b); gl_FragColor.r += (m[3] * c.a); - gl_FragColor.r += m[4]; + gl_FragColor.r += m[4] * c.a; gl_FragColor.g = (m[5] * c.r); gl_FragColor.g += (m[6] * c.g); gl_FragColor.g += (m[7] * c.b); gl_FragColor.g += (m[8] * c.a); - gl_FragColor.g += m[9]; + gl_FragColor.g += m[9] * c.a; gl_FragColor.b = (m[10] * c.r); gl_FragColor.b += (m[11] * c.g); gl_FragColor.b += (m[12] * c.b); gl_FragColor.b += (m[13] * c.a); - gl_FragColor.b += m[14]; + gl_FragColor.b += m[14] * c.a; gl_FragColor.a = (m[15] * c.r); gl_FragColor.a += (m[16] * c.g); gl_FragColor.a += (m[17] * c.b); gl_FragColor.a += (m[18] * c.a); - gl_FragColor.a += m[19]; + gl_FragColor.a += m[19] * c.a; } diff --git a/src/interaction/InteractionManager.js b/src/interaction/InteractionManager.js index 05759d4..5c3bdc7 100644 --- a/src/interaction/InteractionManager.js +++ b/src/interaction/InteractionManager.js @@ -88,7 +88,7 @@ * Setting to true will make things work more in line with how the DOM verison works. * Setting to false can make things easier for things like dragging * It is currently set to false as this is how pixi used to work. This will be set to true in future versions of pixi. - * @member {HTMLElement} + * @member {boolean} * @private */ this.moveWhenInside = false; diff --git a/README.md b/README.md index 1bd2045..edc54ab 100644 --- a/README.md +++ b/README.md @@ -109,10 +109,13 @@ // You need to create a root container that will hold the scene you want to draw. var stage = new PIXI.Container(); +// Declare a global variable for our sprite so that the animate function can access it. +var bunny = null; + // load the texture we need PIXI.loader.add('bunny', 'bunny.png').load(function (loader, resources) { // This creates a texture from a 'bunny.png' image. - var bunny = new PIXI.Sprite(resources.bunny.texture); + bunny = new PIXI.Sprite(resources.bunny.texture); // Setup the position and scale of the bunny bunny.position.x = 400; diff --git a/package.json b/package.json index 66f3a58..d38da2b 100644 --- a/package.json +++ b/package.json @@ -33,38 +33,38 @@ "async": "^1.5.0", "brfs": "^1.4.1", "browserify-versionify": "^1.0.6", - "earcut": "^2.0.7", + "earcut": "^2.0.8", "eventemitter3": "^1.1.1", "object-assign": "^4.0.1", "resource-loader": "^1.6.4" }, "devDependencies": { - "browserify": "^11.1.0", + "browserify": "^13.0.0", "chai": "^3.2.0", - "del": "^2.0.2", + "del": "^2.2.0", "gulp": "^3.9.0", "gulp-cached": "^1.1.0", "gulp-concat": "^2.6.0", "gulp-debug": "^2.1.0", "gulp-header": "^1.7.1", - "gulp-jshint": "^1.11.2", - "gulp-mirror": "^0.4.0", + "gulp-jshint": "^2.0.0", + "gulp-mirror": "^1.0.0", "gulp-plumber": "^1.0.1", "gulp-rename": "^1.2.2", "gulp-sourcemaps": "^1.5.2", - "gulp-uglify": "^1.4.1", + "gulp-uglify": "^1.5.1", "gulp-util": "^3.0.6", "jaguarjs-jsdoc": "git+https://github.com/davidshimjs/jaguarjs-jsdoc.git", - "jsdoc": "^3.3.2", + "jsdoc": "^3.4.0", "jshint-summary": "^0.4.0", "minimist": "^1.2.0", - "mocha": "^2.3.2", + "mocha": "^2.4.5", "require-dir": "^0.3.0", "run-sequence": "^1.1.2", "testem": "^0.9.4", "vinyl-buffer": "^1.0.0", "vinyl-source-stream": "^1.1.0", - "watchify": "^3.4.0" + "watchify": "^3.7.0" }, "browserify": { "transform": [ diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index fdd49f7..3989125 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -536,6 +536,8 @@ this.gl.useProgram(null); + this.gl.flush(); + this.gl = null; }; diff --git a/src/core/text/Text.js b/src/core/text/Text.js index 2af93f9..3e33926 100644 --- a/src/core/text/Text.js +++ b/src/core/text/Text.js @@ -26,6 +26,8 @@ * @param [style.strokeThickness=0] {number} A number that represents the thickness of the stroke. Default is 0 (no stroke) * @param [style.wordWrap=false] {boolean} Indicates if word wrap should be used * @param [style.wordWrapWidth=100] {number} The width at which text will wrap, it needs wordWrap to be set to true + * @param [style.letterSpacing=0] {number} The amount of spacing between letters, default is 0 + * @param [style.breakWords=false] {boolean} Indicates if lines can be wrapped within words, it needs wordWrap to be set to true * @param [style.lineHeight] {number} The line height, a number that represents the vertical space that a letter uses * @param [style.dropShadow=false] {boolean} Set a drop shadow for the text * @param [style.dropShadowColor='#000000'] {string} A fill style to be used on the dropshadow e.g 'red', '#00FF00' @@ -195,6 +197,8 @@ style.strokeThickness = style.strokeThickness || 0; style.wordWrap = style.wordWrap || false; style.wordWrapWidth = style.wordWrapWidth || 100; + style.breakWords = style.breakWords || false; + style.letterSpacing = style.letterSpacing || 0; style.dropShadow = style.dropShadow || false; style.dropShadowColor = style.dropShadowColor || '#000000'; @@ -260,7 +264,7 @@ var fontProperties = this.determineFontProperties(style.font); for (var i = 0; i < lines.length; i++) { - var lineWidth = this.context.measureText(lines[i]).width; + var lineWidth = this.context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing); lineWidths[i] = lineWidth; maxLineWidth = Math.max(maxLineWidth, lineWidth); } @@ -333,7 +337,7 @@ if (style.fill) { - this.context.fillText(lines[i], linePositionX + xShadowOffset, linePositionY + yShadowOffset + this._style.padding); + this.drawLetterSpacing(lines[i], linePositionX + xShadowOffset, linePositionY + yShadowOffset + style.padding); } } } @@ -358,12 +362,12 @@ if (style.stroke && style.strokeThickness) { - this.context.strokeText(lines[i], linePositionX, linePositionY + this._style.padding); + this.drawLetterSpacing(lines[i], linePositionX, linePositionY + style.padding, true); } if (style.fill) { - this.context.fillText(lines[i], linePositionX, linePositionY + this._style.padding); + this.drawLetterSpacing(lines[i], linePositionX, linePositionY + style.padding); } } @@ -371,6 +375,51 @@ }; /** + * Render the text with letter-spacing. + * + * @private + */ +Text.prototype.drawLetterSpacing = function(text, x, y, isStroke) +{ + var style = this._style; + + // letterSpacing of 0 means normal + var letterSpacing = style.letterSpacing; + + if (letterSpacing === 0) + { + if (isStroke) + { + this.context.strokeText(text, x, y); + } + else + { + this.context.fillText(text, x, y); + } + return; + } + + var characters = String.prototype.split.call(text, ''), + index = 0, + current, + currentPosition = x; + + while (index < text.length) + { + current = characters[index++]; + if (isStroke) + { + this.context.strokeText(current, currentPosition, y); + } + else + { + this.context.fillText(current, currentPosition, y); + } + currentPosition += this.context.measureText(current).width + letterSpacing; + } +}; + +/** * Updates texture size based on canvas size * * @private @@ -378,6 +427,7 @@ Text.prototype.updateTexture = function () { var texture = this._texture; + var style = this._style; texture.baseTexture.hasLoaded = true; texture.baseTexture.resolution = this.resolution; @@ -388,10 +438,10 @@ texture.crop.height = texture._frame.height = this.canvas.height / this.resolution; texture.trim.x = 0; - texture.trim.y = -this._style.padding; + texture.trim.y = -style.padding; texture.trim.width = texture._frame.width; - texture.trim.height = texture._frame.height - this._style.padding*2; + texture.trim.height = texture._frame.height - style.padding*2; this._width = this.canvas.width / this.resolution; this._height = this.canvas.height / this.resolution; @@ -559,22 +609,48 @@ for (var j = 0; j < words.length; j++) { var wordWidth = this.context.measureText(words[j]).width; - var wordWidthWithSpace = wordWidth + this.context.measureText(' ').width; - if (j === 0 || wordWidthWithSpace > spaceLeft) + if (this._style.breakWords && wordWidth > wordWrapWidth) { - // Skip printing the newline if it's the first word of the line that is - // greater than the word wrap width. - if (j > 0) + // Word should be split in the middle + var characters = words[j].split(''); + for (var c = 0; c < characters.length; c++) { - result += '\n'; + var characterWidth = this.context.measureText(characters[c]).width; + if (characterWidth > spaceLeft) + { + result += '\n' + characters[c]; + spaceLeft = wordWrapWidth - characterWidth; + } + else + { + if (c === 0) + { + result += ' '; + } + result += characters[c]; + spaceLeft -= characterWidth; + } } - result += words[j]; - spaceLeft = wordWrapWidth - wordWidth; } - else + else { - spaceLeft -= wordWidthWithSpace; - result += ' ' + words[j]; + var wordWidthWithSpace = wordWidth + this.context.measureText(' ').width; + if (j === 0 || wordWidthWithSpace > spaceLeft) + { + // Skip printing the newline if it's the first word of the line that is + // greater than the word wrap width. + if (j > 0) + { + result += '\n'; + } + result += words[j]; + spaceLeft = wordWrapWidth - wordWidth; + } + else + { + spaceLeft -= wordWidthWithSpace; + result += ' ' + words[j]; + } } } diff --git a/src/filters/color/colorMatrix.frag b/src/filters/color/colorMatrix.frag index 5de20fc..041f345 100644 --- a/src/filters/color/colorMatrix.frag +++ b/src/filters/color/colorMatrix.frag @@ -13,24 +13,24 @@ gl_FragColor.r += (m[1] * c.g); gl_FragColor.r += (m[2] * c.b); gl_FragColor.r += (m[3] * c.a); - gl_FragColor.r += m[4]; + gl_FragColor.r += m[4] * c.a; gl_FragColor.g = (m[5] * c.r); gl_FragColor.g += (m[6] * c.g); gl_FragColor.g += (m[7] * c.b); gl_FragColor.g += (m[8] * c.a); - gl_FragColor.g += m[9]; + gl_FragColor.g += m[9] * c.a; gl_FragColor.b = (m[10] * c.r); gl_FragColor.b += (m[11] * c.g); gl_FragColor.b += (m[12] * c.b); gl_FragColor.b += (m[13] * c.a); - gl_FragColor.b += m[14]; + gl_FragColor.b += m[14] * c.a; gl_FragColor.a = (m[15] * c.r); gl_FragColor.a += (m[16] * c.g); gl_FragColor.a += (m[17] * c.b); gl_FragColor.a += (m[18] * c.a); - gl_FragColor.a += m[19]; + gl_FragColor.a += m[19] * c.a; } diff --git a/src/interaction/InteractionManager.js b/src/interaction/InteractionManager.js index 05759d4..5c3bdc7 100644 --- a/src/interaction/InteractionManager.js +++ b/src/interaction/InteractionManager.js @@ -88,7 +88,7 @@ * Setting to true will make things work more in line with how the DOM verison works. * Setting to false can make things easier for things like dragging * It is currently set to false as this is how pixi used to work. This will be set to true in future versions of pixi. - * @member {HTMLElement} + * @member {boolean} * @private */ this.moveWhenInside = false; diff --git a/src/loaders/spritesheetParser.js b/src/loaders/spritesheetParser.js index 8de8b68..c748ba9 100644 --- a/src/loaders/spritesheetParser.js +++ b/src/loaders/spritesheetParser.js @@ -1,13 +1,18 @@ var Resource = require('resource-loader').Resource, path = require('path'), - core = require('../core'); + core = require('../core'), + async = require('async'); + +var BATCH_SIZE = 1000; module.exports = function () { return function (resource, next) { - // skip if no data, its not json, or it isn't spritesheet data - if (!resource.data || !resource.isJson || !resource.data.frames) + var imageResourceName = resource.name + '_image'; + + // skip if no data, its not json, it isn't spritesheet data, or the image resource already exists + if (!resource.data || !resource.isJson || !resource.data.frames || this.resources[imageResourceName]) { return next(); } @@ -20,63 +25,93 @@ var route = path.dirname(resource.url.replace(this.baseUrl, '')); - var resolution = core.utils.getResolutionOfUrl( resource.url ); - // load the image for this sheet - this.add(resource.name + '_image', route + '/' + resource.data.meta.image, loadOptions, function (res) + this.add(imageResourceName, route + '/' + resource.data.meta.image, loadOptions, function (res) { resource.textures = {}; var frames = resource.data.frames; + var frameKeys = Object.keys(frames); + var resolution = core.utils.getResolutionOfUrl(resource.url); + var batchIndex = 0; - for (var i in frames) + function processFrames(initialFrameIndex, maxFrames) { - var rect = frames[i].frame; + var frameIndex = initialFrameIndex; - if (rect) + while (frameIndex - initialFrameIndex < maxFrames && frameIndex < frameKeys.length) { - var size = null; - var trim = null; + var frame = frames[frameKeys[frameIndex]]; + var rect = frame.frame; - if (frames[i].rotated) { - size = new core.Rectangle(rect.x, rect.y, rect.h, rect.w); - } - else { - size = new core.Rectangle(rect.x, rect.y, rect.w, rect.h); - } - - // Check to see if the sprite is trimmed - if (frames[i].trimmed) + if (rect) { - trim = new core.Rectangle( - frames[i].spriteSourceSize.x / resolution, - frames[i].spriteSourceSize.y / resolution, - frames[i].sourceSize.w / resolution, - frames[i].sourceSize.h / resolution - ); + var size = null; + var trim = null; + + if (frame.rotated) + { + size = new core.Rectangle(rect.x, rect.y, rect.h, rect.w); + } + else + { + size = new core.Rectangle(rect.x, rect.y, rect.w, rect.h); + } + + // Check to see if the sprite is trimmed + if (frame.trimmed) + { + trim = new core.Rectangle( + frame.spriteSourceSize.x / resolution, + frame.spriteSourceSize.y / resolution, + frame.sourceSize.w / resolution, + frame.sourceSize.h / resolution + ); + } + + // flip the width and height! + if (frame.rotated) + { + var temp = size.width; + size.width = size.height; + size.height = temp; + } + + size.x /= resolution; + size.y /= resolution; + size.width /= resolution; + size.height /= resolution; + + resource.textures[frameKeys[frameIndex]] = new core.Texture(res.texture.baseTexture, size, size.clone(), trim, frame.rotated); + + // lets also add the frame to pixi's global cache for fromFrame and fromImage functions + core.utils.TextureCache[frameKeys[frameIndex]] = resource.textures[frameKeys[frameIndex]]; } - - // flip the width and height! - if (frames[i].rotated) - { - var temp = size.width; - size.width = size.height; - size.height = temp; - } - - size.x /= resolution; - size.y /= resolution; - size.width /= resolution; - size.height /= resolution; - - resource.textures[i] = new core.Texture(res.texture.baseTexture, size, size.clone(), trim, frames[i].rotated ? 2 : 0); - - // lets also add the frame to pixi's global cache for fromFrame and fromImage functions - core.utils.TextureCache[i] = resource.textures[i]; + frameIndex++; } } - next(); + function shouldProcessNextBatch() + { + return batchIndex * BATCH_SIZE < frameKeys.length; + } + + function processNextBatch(done) + { + processFrames(batchIndex * BATCH_SIZE, BATCH_SIZE); + batchIndex++; + setTimeout(done, 0); + } + + if (frameKeys.length <= BATCH_SIZE) + { + processFrames(0, BATCH_SIZE); + next(); + } + else + { + async.whilst(shouldProcessNextBatch, processNextBatch, next); + } }); }; };