Skip to content

Commit 04e7be3

Browse files
committed
Heavily refactored. Vastly optimised generateTexture method. Canvas renderer now works correctly with animated sprites, texture atlases and trimmed sprites. Warning: Currently breaks WebGL rendering. Will fix soon.
1 parent cc46212 commit 04e7be3

1 file changed

Lines changed: 156 additions & 150 deletions

File tree

src/pixi/extras/TilingSprite.js

Lines changed: 156 additions & 150 deletions
Original file line numberDiff line numberDiff line change
@@ -83,31 +83,54 @@ PIXI.TilingSprite = function(texture, width, height)
8383
*/
8484
this.blendMode = PIXI.blendModes.NORMAL;
8585

86+
/**
87+
* The CanvasBuffer object that the tiled texture is drawn to.
88+
*
89+
* @property canvasBuffer
90+
* @type PIXI.CanvasBuffer
91+
*/
92+
this.canvasBuffer = null;
93+
94+
/**
95+
* An internal Texture object that holds the tiling texture that was generated from TilingSprite.texture.
96+
*
97+
* @property tilingTexture
98+
* @type PIXI.Texture
99+
*/
86100
this.tilingTexture = null;
87-
this.__tilePattern = null;
88-
this.refreshTexture = true;
89101

90-
this.originalTexture = texture;
102+
/**
103+
* The Context fill pattern that is used to draw the TilingSprite in Canvas mode only (will be null in WebGL).
104+
*
105+
* @property tilePattern
106+
* @type PIXI.Texture
107+
*/
108+
this.tilePattern = null;
91109

92-
this._cropX = 0;
93-
this._cropY = 0;
110+
/**
111+
* If true the TilingSprite will run generateTexture on its **next** render pass.
112+
* This is set by the likes of Phaser.LoadTexture.setFrame.
113+
*
114+
* @property refreshTexture
115+
* @type Boolean
116+
* @default true
117+
*/
118+
this.refreshTexture = true;
94119

95120
};
96121

97-
// constructor
98122
PIXI.TilingSprite.prototype = Object.create(PIXI.Sprite.prototype);
99123
PIXI.TilingSprite.prototype.constructor = PIXI.TilingSprite;
100124

101125
PIXI.TilingSprite.prototype.setTexture = function(texture)
102126
{
103-
this.texture = texture;
104-
this.originalTexture = texture;
105-
this.refreshTexture = true;
106-
107-
this._cropX = 0;
108-
this._cropY = 0;
127+
if (this.texture !== texture)
128+
{
129+
this.texture = texture;
130+
this.refreshTexture = true;
131+
this.cachedTint = 0xFFFFFF;
132+
}
109133

110-
this.cachedTint = 0xFFFFFF;
111134
};
112135

113136
/**
@@ -137,7 +160,6 @@ PIXI.TilingSprite.prototype._renderWebGL = function(renderSession)
137160
renderSession.filterManager.pushFilter(this._filterBlock);
138161
}
139162

140-
// if (!this.tilingTexture || this.refreshTexture)
141163
if (this.refreshTexture)
142164
{
143165
this.generateTilingTexture(true);
@@ -189,32 +211,33 @@ PIXI.TilingSprite.prototype._renderCanvas = function(renderSession)
189211

190212
context.globalAlpha = this.worldAlpha;
191213

192-
var transform = this.worldTransform;
214+
var wt = this.worldTransform;
193215
var resolution = renderSession.resolution;
194216

195-
context.setTransform(transform.a * resolution,
196-
transform.b * resolution,
197-
transform.c * resolution,
198-
transform.d * resolution,
199-
transform.tx * resolution,
200-
transform.ty * resolution);
217+
context.setTransform(wt.a * resolution,
218+
wt.b * resolution,
219+
wt.c * resolution,
220+
wt.d * resolution,
221+
wt.tx * resolution,
222+
wt.ty * resolution);
201223

202-
// if (!this.__tilePattern || this.refreshTexture)
203224
if (this.refreshTexture)
204225
{
205226
this.generateTilingTexture(false);
206227

207228
if (this.tilingTexture)
208229
{
209-
this.__tilePattern = context.createPattern(this.tilingTexture.baseTexture.source, 'repeat');
230+
this.tilePattern = context.createPattern(this.tilingTexture.baseTexture.source, 'repeat');
210231
}
211232
else
212233
{
213234
return;
214235
}
215236
}
216237

217-
// check blend mode
238+
var sessionBlendMode = renderSession.currentBlendMode;
239+
240+
// Check blend mode
218241
if (this.blendMode !== renderSession.currentBlendMode)
219242
{
220243
renderSession.currentBlendMode = this.blendMode;
@@ -227,17 +250,29 @@ PIXI.TilingSprite.prototype._renderCanvas = function(renderSession)
227250
tilePosition.x %= this.tilingTexture.baseTexture.width;
228251
tilePosition.y %= this.tilingTexture.baseTexture.height;
229252

230-
// offset - make sure to account for the anchor point..
231-
context.scale(tileScale.x,tileScale.y);
253+
// Translate
254+
context.scale(tileScale.x, tileScale.y);
232255
context.translate(tilePosition.x + (this.anchor.x * -this._width), tilePosition.y + (this.anchor.y * -this._height));
233256

234-
context.fillStyle = this.__tilePattern;
257+
context.fillStyle = this.tilePattern;
235258

236-
context.fillRect(-tilePosition.x,
237-
-tilePosition.y,
238-
this._width / tileScale.x,
239-
this._height / tileScale.y);
259+
var tx = -tilePosition.x;
260+
var ty = -tilePosition.y;
261+
var tw = this._width / tileScale.x;
262+
var th = this._height / tileScale.y;
240263

264+
// Allow for pixel rounding
265+
if (renderSession.roundPixels)
266+
{
267+
tx | 0;
268+
ty | 0;
269+
tw | 0;
270+
th | 0;
271+
}
272+
273+
context.fillRect(tx, ty, tw, th);
274+
275+
// Translate back again
241276
context.scale(1 / tileScale.x, 1 / tileScale.y);
242277
context.translate(-tilePosition.x + (this.anchor.x * this._width), -tilePosition.y + (this.anchor.y * this._height));
243278

@@ -251,6 +286,96 @@ PIXI.TilingSprite.prototype._renderCanvas = function(renderSession)
251286
this.children[i]._renderCanvas(renderSession);
252287
}
253288

289+
// Reset blend mode
290+
if (sessionBlendMode !== this.blendMode)
291+
{
292+
renderSession.currentBlendMode = sessionBlendMode;
293+
context.globalCompositeOperation = PIXI.blendModesCanvas[sessionBlendMode];
294+
}
295+
296+
};
297+
298+
/**
299+
* When the texture is updated, this event will fire to update the scale and frame
300+
*
301+
* @method onTextureUpdate
302+
* @param event
303+
* @private
304+
*/
305+
PIXI.TilingSprite.prototype.onTextureUpdate = function()
306+
{
307+
// overriding the sprite version of this!
308+
};
309+
310+
/**
311+
*
312+
* @method generateTilingTexture
313+
*
314+
* @param forcePowerOfTwo {Boolean} Whether we want to force the texture to be a power of two
315+
*/
316+
PIXI.TilingSprite.prototype.generateTilingTexture = function(forcePowerOfTwo)
317+
{
318+
if (!this.texture.baseTexture.hasLoaded)
319+
{
320+
return;
321+
}
322+
323+
var texture = this.texture;
324+
var frame = texture.frame;
325+
326+
var targetWidth = this._frame.sourceSizeW;
327+
var targetHeight = this._frame.sourceSizeH;
328+
329+
var dx = 0;
330+
var dy = 0;
331+
332+
if (this._frame.trimmed)
333+
{
334+
dx = this._frame.spriteSourceSizeX;
335+
dy = this._frame.spriteSourceSizeY;
336+
}
337+
338+
if (forcePowerOfTwo)
339+
{
340+
targetWidth = PIXI.getNextPowerOfTwo(texture.crop.width);
341+
targetHeight = PIXI.getNextPowerOfTwo(texture.crop.height);
342+
}
343+
344+
if (this.canvasBuffer)
345+
{
346+
this.canvasBuffer.resize(targetWidth, targetHeight);
347+
this.tilingTexture.baseTexture.width = targetWidth;
348+
this.tilingTexture.baseTexture.height = targetHeight;
349+
this.tilingTexture.needsUpdate = true;
350+
}
351+
else
352+
{
353+
this.canvasBuffer = new PIXI.CanvasBuffer(targetWidth, targetHeight);
354+
this.tilingTexture = PIXI.Texture.fromCanvas(this.canvasBuffer.canvas);
355+
this.tilingTexture.isTiling = true;
356+
// Phaser.Canvas.addToDOM(this.canvasBuffer.canvas, document.body, false);
357+
}
358+
359+
// this.canvasBuffer.context.fillStyle = 'rgb(0,255,0)';
360+
// this.canvasBuffer.context.fillRect(0, 0, targetWidth, targetHeight);
361+
362+
this.canvasBuffer.context.drawImage(texture.baseTexture.source,
363+
texture.crop.x,
364+
texture.crop.y,
365+
texture.crop.width,
366+
texture.crop.height,
367+
dx,
368+
dy,
369+
texture.crop.width,
370+
texture.crop.height);
371+
372+
this.tileScaleOffset.x = frame.width / targetWidth;
373+
this.tileScaleOffset.y = frame.height / targetHeight;
374+
375+
this.refreshTexture = false;
376+
377+
this.tilingTexture.baseTexture._powerOf2 = true;
378+
254379
};
255380

256381
/**
@@ -331,125 +456,6 @@ PIXI.TilingSprite.prototype.getBounds = function()
331456
return bounds;
332457
};
333458

334-
/**
335-
* When the texture is updated, this event will fire to update the scale and frame
336-
*
337-
* @method onTextureUpdate
338-
* @param event
339-
* @private
340-
*/
341-
PIXI.TilingSprite.prototype.onTextureUpdate = function()
342-
{
343-
// overriding the sprite version of this!
344-
};
345-
346-
/**
347-
*
348-
* @method generateTilingTexture
349-
*
350-
* @param forcePowerOfTwo {Boolean} Whether we want to force the texture to be a power of two
351-
*/
352-
PIXI.TilingSprite.prototype.generateTilingTexture = function(forcePowerOfTwo)
353-
{
354-
if (!this.originalTexture.baseTexture.hasLoaded)
355-
{
356-
return;
357-
}
358-
359-
this.originalTexture = this.texture;
360-
361-
var texture = this.originalTexture;
362-
var frame = texture.frame;
363-
364-
var targetWidth;
365-
var targetHeight;
366-
367-
var newTextureRequired = false;
368-
369-
if (texture.crop.x !== this._cropX || texture.crop.y !== this._cropY || this.tilingTexture === null)
370-
{
371-
newTextureRequired = true;
372-
this._cropX = texture.crop.x;
373-
this._cropY = texture.crop.y;
374-
}
375-
376-
if (!forcePowerOfTwo)
377-
{
378-
targetWidth = texture.crop.width;
379-
targetHeight = texture.crop.height;
380-
newTextureRequired = true;
381-
}
382-
else
383-
{
384-
targetWidth = PIXI.getNextPowerOfTwo(texture.crop.width);
385-
targetHeight = PIXI.getNextPowerOfTwo(texture.crop.height);
386-
newTextureRequired = true;
387-
}
388-
389-
// console.log('generateTilingTexture', newTextureRequired, frame, 'pot', forcePowerOfTwo, 'bt', texture.baseTexture.width, texture.baseTexture.height, 'fr', frame.width, frame.height);
390-
// console.log('generateTilingTexture');
391-
// console.log(texture);
392-
393-
if (newTextureRequired)
394-
{
395-
var canvasBuffer;
396-
397-
if (this.tilingTexture && this.tilingTexture.isTiling)
398-
{
399-
canvasBuffer = this.tilingTexture.canvasBuffer;
400-
canvasBuffer.resize(targetWidth, targetHeight);
401-
this.tilingTexture.baseTexture.width = targetWidth;
402-
this.tilingTexture.baseTexture.height = targetHeight;
403-
this.tilingTexture.needsUpdate = true;
404-
}
405-
else
406-
{
407-
canvasBuffer = new PIXI.CanvasBuffer(targetWidth, targetHeight);
408-
this.tilingTexture = PIXI.Texture.fromCanvas(canvasBuffer.canvas);
409-
this.tilingTexture.canvasBuffer = canvasBuffer;
410-
this.tilingTexture.isTiling = true;
411-
}
412-
413-
canvasBuffer.context.drawImage(texture.baseTexture.source,
414-
texture.crop.x,
415-
texture.crop.y,
416-
texture.crop.width,
417-
texture.crop.height,
418-
0,
419-
0,
420-
targetWidth,
421-
targetHeight);
422-
423-
this.tileScaleOffset.x = frame.width / targetWidth;
424-
this.tileScaleOffset.y = frame.height / targetHeight;
425-
}
426-
else
427-
{
428-
/*
429-
// TODO - switching?
430-
if (this.tilingTexture && this.tilingTexture.isTiling)
431-
{
432-
// destroy the tiling texture!
433-
// TODO could store this somewhere?
434-
this.tilingTexture.destroy(true);
435-
}
436-
437-
this.tileScaleOffset.x = 1;
438-
this.tileScaleOffset.y = 1;
439-
this.tilingTexture = texture;
440-
*/
441-
}
442-
443-
this.refreshTexture = false;
444-
445-
this.texture = this.tilingTexture;
446-
447-
this.tilingTexture.baseTexture._powerOf2 = true;
448-
449-
// debugger;
450-
451-
};
452-
453459
PIXI.TilingSprite.prototype.destroy = function () {
454460

455461
PIXI.Sprite.prototype.destroy.call(this);

0 commit comments

Comments
 (0)