Skip to content

Commit 928cd60

Browse files
authored
Merge pull request #496 from LLK/revert-493-revert-470-skin-alter-push
Revert "Revert "Skin alter push""
2 parents 2d4419c + 30ef2b1 commit 928cd60

File tree

8 files changed

+124
-46
lines changed

8 files changed

+124
-46
lines changed

src/BitmapSkin.js

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@ class BitmapSkin extends Skin {
2323

2424
/** @type {Array<int>} */
2525
this._textureSize = [0, 0];
26+
27+
/**
28+
* The "native" size, in texels, of this skin.
29+
* @type {Array<number>}
30+
*/
31+
this.size = [0, 0];
2632
}
2733

2834
/**
@@ -43,13 +49,6 @@ class BitmapSkin extends Skin {
4349
return true;
4450
}
4551

46-
/**
47-
* @return {Array<number>} the "native" size, in texels, of this skin.
48-
*/
49-
get size () {
50-
return [this._textureSize[0] / this._costumeResolution, this._textureSize[1] / this._costumeResolution];
51-
}
52-
5352
/**
5453
* @param {Array<number>} scale - The scaling factors to be used.
5554
* @return {WebGLTexture} The GL texture representation of this skin when drawing at the given scale.
@@ -110,6 +109,7 @@ class BitmapSkin extends Skin {
110109
// Do these last in case any of the above throws an exception
111110
this._costumeResolution = costumeResolution || 2;
112111
this._textureSize = BitmapSkin._getBitmapSize(bitmapData);
112+
this.size = [this._textureSize[0] / this._costumeResolution, this._textureSize[1] / this._costumeResolution];
113113

114114
if (typeof rotationCenter === 'undefined') rotationCenter = this.calculateRotationCenter();
115115
this.setRotationCenter.apply(this, rotationCenter);

src/Drawable.js

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ const twgl = require('twgl.js');
33
const Rectangle = require('./Rectangle');
44
const RenderConstants = require('./RenderConstants');
55
const ShaderManager = require('./ShaderManager');
6-
const Skin = require('./Skin');
76
const EffectTransform = require('./EffectTransform');
87

98
/**
@@ -101,8 +100,6 @@ class Drawable {
101100
/** @todo move convex hull functionality, maybe bounds functionality overall, to Skin classes */
102101
this._convexHullPoints = null;
103102
this._convexHullDirty = true;
104-
105-
this._skinWasAltered = this._skinWasAltered.bind(this);
106103
}
107104

108105
/**
@@ -141,13 +138,7 @@ class Drawable {
141138
*/
142139
set skin (newSkin) {
143140
if (this._skin !== newSkin) {
144-
if (this._skin) {
145-
this._skin.removeListener(Skin.Events.WasAltered, this._skinWasAltered);
146-
}
147141
this._skin = newSkin;
148-
if (this._skin) {
149-
this._skin.addListener(Skin.Events.WasAltered, this._skinWasAltered);
150-
}
151142
this._skinWasAltered();
152143
}
153144
}

src/PenSkin.js

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,9 @@ class PenSkin extends Skin {
8888
/** @type {HTMLCanvasElement} */
8989
this._canvas = document.createElement('canvas');
9090

91+
/** @type {Array<number>} */
92+
this._canvasSize = twgl.v3.create();
93+
9194
/** @type {WebGLTexture} */
9295
this._texture = null;
9396

@@ -165,7 +168,7 @@ class PenSkin extends Skin {
165168
* @return {Array<number>} the "native" size, in texels, of this skin. [width, height]
166169
*/
167170
get size () {
168-
return [this._canvas.width, this._canvas.height];
171+
return this._canvasSize;
169172
}
170173

171174
/**
@@ -188,13 +191,13 @@ class PenSkin extends Skin {
188191
clear () {
189192
const gl = this._renderer.gl;
190193
twgl.bindFramebufferInfo(gl, this._framebuffer);
191-
194+
192195
/* Reset framebuffer to transparent black */
193196
gl.clearColor(0, 0, 0, 0);
194197
gl.clear(gl.COLOR_BUFFER_BIT);
195198

196199
const ctx = this._canvas.getContext('2d');
197-
ctx.clearRect(0, 0, this._canvas.width, this._canvas.height);
200+
ctx.clearRect(0, 0, this._canvasSize[0], this._canvasSize[1]);
198201

199202
this._silhouetteDirty = true;
200203
}
@@ -451,7 +454,7 @@ class PenSkin extends Skin {
451454
* @param {number} x - centered at x
452455
* @param {number} y - centered at y
453456
*/
454-
_drawRectangle (currentShader, texture, bounds, x = -this._canvas.width / 2, y = this._canvas.height / 2) {
457+
_drawRectangle (currentShader, texture, bounds, x = -this._canvasSize[0] / 2, y = this._canvasSize[1] / 2) {
455458
const gl = this._renderer.gl;
456459

457460
const projection = twgl.m4.ortho(
@@ -514,7 +517,7 @@ class PenSkin extends Skin {
514517
* @param {number} x - texture centered at x
515518
* @param {number} y - texture centered at y
516519
*/
517-
_drawToBuffer (texture = this._texture, x = -this._canvas.width / 2, y = this._canvas.height / 2) {
520+
_drawToBuffer (texture = this._texture, x = -this._canvasSize[0] / 2, y = this._canvasSize[1] / 2) {
518521
if (texture !== this._texture && this._canvasDirty) {
519522
this._drawToBuffer();
520523
}
@@ -528,7 +531,7 @@ class PenSkin extends Skin {
528531
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this._canvas);
529532

530533
const ctx = this._canvas.getContext('2d');
531-
ctx.clearRect(0, 0, this._canvas.width, this._canvas.height);
534+
ctx.clearRect(0, 0, this._canvasSize[0], this._canvasSize[1]);
532535

533536
this._canvasDirty = false;
534537
}
@@ -564,8 +567,8 @@ class PenSkin extends Skin {
564567
this._bounds = new Rectangle();
565568
this._bounds.initFromBounds(width / 2, width / -2, height / 2, height / -2);
566569

567-
this._canvas.width = width;
568-
this._canvas.height = height;
570+
this._canvas.width = this._canvasSize[0] = width;
571+
this._canvas.height = this._canvasSize[1] = height;
569572
this._rotationCenter[0] = width / 2;
570573
this._rotationCenter[1] = height / 2;
571574

@@ -651,8 +654,8 @@ class PenSkin extends Skin {
651654
this._renderer.enterDrawRegion(this._toBufferDrawRegionId);
652655

653656
// Sample the framebuffer's pixels into the silhouette instance
654-
const skinPixels = new Uint8Array(Math.floor(this._canvas.width * this._canvas.height * 4));
655-
gl.readPixels(0, 0, this._canvas.width, this._canvas.height, gl.RGBA, gl.UNSIGNED_BYTE, skinPixels);
657+
const skinPixels = new Uint8Array(Math.floor(this._canvasSize[0] * this._canvasSize[1] * 4));
658+
gl.readPixels(0, 0, this._canvasSize[0], this._canvasSize[1], gl.RGBA, gl.UNSIGNED_BYTE, skinPixels);
656659

657660
const skinCanvas = this._canvas;
658661
skinCanvas.width = bounds.width;

src/RenderWebGL.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ const EventEmitter = require('events');
33
const hull = require('hull.js');
44
const twgl = require('twgl.js');
55

6+
const Skin = require('./Skin');
67
const BitmapSkin = require('./BitmapSkin');
78
const Drawable = require('./Drawable');
89
const Rectangle = require('./Rectangle');
@@ -291,6 +292,20 @@ class RenderWebGL extends EventEmitter {
291292
this.emit(RenderConstants.Events.NativeSizeChanged, {newSize: this._nativeSize});
292293
}
293294

295+
/**
296+
* Notify Drawables whose skin is the skin that changed.
297+
* @param {Skin} skin - the skin that changed.
298+
* @private
299+
*/
300+
_skinWasAltered (skin) {
301+
for (let i = 0; i < this._allDrawables.length; i++) {
302+
const drawable = this._allDrawables[i];
303+
if (drawable && drawable._skin === skin) {
304+
drawable._skinWasAltered();
305+
}
306+
}
307+
}
308+
294309
/**
295310
* Create a new bitmap skin from a snapshot of the provided bitmap data.
296311
* @param {ImageData|HTMLImageElement|HTMLCanvasElement|HTMLVideoElement} bitmapData - new contents for this skin.
@@ -303,6 +318,7 @@ class RenderWebGL extends EventEmitter {
303318
const skinId = this._nextSkinId++;
304319
const newSkin = new BitmapSkin(skinId, this);
305320
newSkin.setBitmap(bitmapData, costumeResolution, rotationCenter);
321+
newSkin.addListener(Skin.Events.WasAltered, this._skinWasAltered.bind(this, newSkin));
306322
this._allSkins[skinId] = newSkin;
307323
return skinId;
308324
}
@@ -318,6 +334,7 @@ class RenderWebGL extends EventEmitter {
318334
const skinId = this._nextSkinId++;
319335
const newSkin = new SVGSkin(skinId, this);
320336
newSkin.setSVG(svgData, rotationCenter);
337+
newSkin.addListener(Skin.Events.WasAltered, this._skinWasAltered.bind(this, newSkin));
321338
this._allSkins[skinId] = newSkin;
322339
return skinId;
323340
}
@@ -329,6 +346,7 @@ class RenderWebGL extends EventEmitter {
329346
createPenSkin () {
330347
const skinId = this._nextSkinId++;
331348
const newSkin = new PenSkin(skinId, this);
349+
newSkin.addListener(Skin.Events.WasAltered, this._skinWasAltered.bind(this, newSkin));
332350
this._allSkins[skinId] = newSkin;
333351
return skinId;
334352
}
@@ -345,6 +363,7 @@ class RenderWebGL extends EventEmitter {
345363
const skinId = this._nextSkinId++;
346364
const newSkin = new TextBubbleSkin(skinId, this);
347365
newSkin.setTextBubble(type, text, pointsLeft);
366+
newSkin.addListener(Skin.Events.WasAltered, this._skinWasAltered.bind(this, newSkin));
348367
this._allSkins[skinId] = newSkin;
349368
return skinId;
350369
}

src/SVGSkin.js

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,24 @@ class SVGSkin extends Skin {
3030

3131
/** @type {Number} */
3232
this._maxTextureScale = 0;
33+
34+
/**
35+
* The natural size, in Scratch units, of this skin.
36+
* @type {Array<number>}
37+
*/
38+
this.size = [0, 0];
39+
40+
/**
41+
* The viewbox offset of the svg.
42+
* @type {Array<number>}
43+
*/
44+
this._viewOffset = [0, 0];
45+
46+
/**
47+
* The rotation center before offset by _viewOffset.
48+
* @type {Array<number>}
49+
*/
50+
this._rawRotationCenter = [NaN, NaN];
3351
}
3452

3553
/**
@@ -43,21 +61,17 @@ class SVGSkin extends Skin {
4361
super.dispose();
4462
}
4563

46-
/**
47-
* @return {Array<number>} the natural size, in Scratch units, of this skin.
48-
*/
49-
get size () {
50-
return this._svgRenderer.size;
51-
}
52-
5364
/**
5465
* Set the origin, in object space, about which this Skin should rotate.
5566
* @param {number} x - The x coordinate of the new rotation center.
5667
* @param {number} y - The y coordinate of the new rotation center.
5768
*/
5869
setRotationCenter (x, y) {
59-
const viewOffset = this._svgRenderer.viewOffset;
60-
super.setRotationCenter(x - viewOffset[0], y - viewOffset[1]);
70+
if (x !== this._rawRotationCenter[0] || y !== this._rawRotationCenter[1]) {
71+
this._rawRotationCenter[0] = x;
72+
this._rawRotationCenter[1] = y;
73+
super.setRotationCenter(x - this._viewOffset[0], y - this._viewOffset[1]);
74+
}
6175
}
6276

6377
/**
@@ -100,7 +114,19 @@ class SVGSkin extends Skin {
100114
* @fires Skin.event:WasAltered
101115
*/
102116
setSVG (svgData, rotationCenter) {
103-
this._svgRenderer.fromString(svgData, 1, () => {
117+
this._svgRenderer.loadString(svgData);
118+
119+
// Size must be updated synchronously because the VM sets the costume's `size` immediately after calling this.
120+
// TODO: add either a callback to this function so the costume size can be set after this is done,
121+
// or something in the VM to handle setting the costume size when Skin.Events.WasAltered is emitted.
122+
this.size = this._svgRenderer.size;
123+
if (typeof rotationCenter === 'undefined') rotationCenter = this.calculateRotationCenter();
124+
this._viewOffset = this._svgRenderer.viewOffset;
125+
// Reset rawRotationCenter when we update viewOffset.
126+
this._rawRotationCenter = [NaN, NaN];
127+
this.setRotationCenter(rotationCenter[0], rotationCenter[1]);
128+
129+
this._svgRenderer._draw(1, () => {
104130
const gl = this._renderer.gl;
105131
this._textureScale = this._maxTextureScale = 1;
106132

@@ -133,8 +159,6 @@ class SVGSkin extends Skin {
133159
this._maxTextureScale = testScale;
134160
}
135161

136-
if (typeof rotationCenter === 'undefined') rotationCenter = this.calculateRotationCenter();
137-
this.setRotationCenter.apply(this, rotationCenter);
138162
this.emit(Skin.Events.WasAltered);
139163
});
140164
}

src/Skin.js

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,13 @@ class Skin extends EventEmitter {
3333
/** @type {Vec3} */
3434
this._rotationCenter = twgl.v3.create(0, 0);
3535

36+
/**
37+
* The "native" size, in texels, of this skin.
38+
* @member size
39+
* @abstract
40+
* @type {Array<number>}
41+
*/
42+
3643
/**
3744
* The uniforms to be used by the vertex and pixel shaders.
3845
* Some of these are used by other parts of the renderer as well.
@@ -97,14 +104,6 @@ class Skin extends EventEmitter {
97104
return this._rotationCenter;
98105
}
99106

100-
/**
101-
* @abstract
102-
* @return {Array<number>} the "native" size, in texels, of this skin.
103-
*/
104-
get size () {
105-
return [0, 0];
106-
}
107-
108107
/**
109108
* Set the origin, in object space, about which this Skin should rotate.
110109
* @param {number} x - The x coordinate of the new rotation center.

test/fixtures/MockSkinPool.js

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
const Skin = require('../../src/Skin');
2+
3+
class MockSkinPool {
4+
constructor () {
5+
this._allDrawables = [];
6+
}
7+
8+
static forDrawableSkin (drawable) {
9+
const pool = new MockSkinPool();
10+
pool.addDrawable(drawable);
11+
pool.addSkin(drawable.skin);
12+
return pool;
13+
}
14+
15+
_skinWasAltered (skin) {
16+
for (let i = 0; i < this._allDrawables.length; i++) {
17+
const drawable = this._allDrawables[i];
18+
if (drawable && drawable._skin === skin) {
19+
drawable._skinWasAltered();
20+
}
21+
}
22+
}
23+
24+
addDrawable (drawable) {
25+
this._allDrawables.push(drawable);
26+
return drawable;
27+
}
28+
29+
addSkin (skin) {
30+
skin.addListener(Skin.Events.WasAltered, this._skinWasAltered.bind(this, skin));
31+
return skin;
32+
}
33+
}
34+
35+
module.exports = MockSkinPool;

0 commit comments

Comments
 (0)