Compare commits

...

8 Commits

Author SHA1 Message Date
greenkeeper[bot]
8757e502bb chore(package): update docdash to version 1.1.0
Closes #335
2019-03-30 19:33:04 +00:00
Michael "Z" Goddard
9177705e04 Merge pull request #414 from mzgoddard/image-data-texture
ImageData WebGL Textures
2019-03-26 12:12:57 -04:00
Michael "Z" Goddard
e31934f6a9 update Skin textures with ImageData
When possible pass ImageData to texture creation and updating to help
remove chance of references that keep canvas and underlying data from
being garbage collected.
2019-03-19 17:52:21 -04:00
Paul Kaplan
3c79a5562e Merge pull request #421 from LLK/greenkeeper/scratch-svg-renderer-0.2.0-prerelease.20190304180800
Update scratch-svg-renderer to the latest version 🚀
2019-03-12 08:46:47 -04:00
greenkeeper[bot]
d59d45b6c8 fix(package): update scratch-svg-renderer to version 0.2.0-prerelease.20190304180800 2019-03-04 18:09:20 +00:00
Michael "Z" Goddard
19ee8e8eaa Merge pull request #415 from mzgoddard/drop-silhouette-alpha-buffer
Replace Silhouette._data with Silhouette._colorData
2019-03-04 12:56:58 -05:00
Michael "Z" Goddard
e022222365 replace Silhouette._data with Silhouette._colorData
_colorData holds the same (and more) data that _data holds. Dropping
the _data array saves a lot of memory for a tiny performance
degradation in regards to touching object.
2019-02-22 17:08:53 -05:00
Michael "Z" Goddard
be5ab2e689 receive ImageData directly in Silhouette.update
Given ImageData we can skip drawing the input and getting image data.
This can help if update's color can also use the ImageData directly.
2019-02-22 17:08:04 -05:00
4 changed files with 58 additions and 31 deletions

View File

@@ -31,7 +31,7 @@
"babel-preset-env": "^1.6.1",
"chromeless": "^1.5.1",
"copy-webpack-plugin": "^4.5.1",
"docdash": "^0.4.0",
"docdash": "^1.1.0",
"eslint": "^4.6.1",
"eslint-config-scratch": "^5.0.0",
"gh-pages": "^1.0.0",
@@ -53,7 +53,7 @@
"minilog": "3.1.0",
"raw-loader": "^0.5.1",
"scratch-storage": "^1.0.0",
"scratch-svg-renderer": "0.2.0-prerelease.20190125192231",
"scratch-svg-renderer": "0.2.0-prerelease.20190304180800",
"twgl.js": "4.4.0"
}
}

View File

@@ -79,20 +79,31 @@ class BitmapSkin extends Skin {
setBitmap (bitmapData, costumeResolution, rotationCenter) {
const gl = this._renderer.gl;
// Preferably bitmapData is ImageData. ImageData speeds up updating
// Silhouette and is better handled by more browsers in regards to
// memory.
let textureData = bitmapData;
if (bitmapData instanceof HTMLCanvasElement) {
// Given a HTMLCanvasElement get the image data to pass to webgl and
// Silhouette.
const context = bitmapData.getContext('2d');
textureData = context.getImageData(0, 0, bitmapData.width, bitmapData.height);
}
if (this._texture) {
gl.bindTexture(gl.TEXTURE_2D, this._texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, bitmapData);
this._silhouette.update(bitmapData);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, textureData);
this._silhouette.update(textureData);
} else {
// TODO: mipmaps?
const textureOptions = {
auto: true,
wrap: gl.CLAMP_TO_EDGE,
src: bitmapData
src: textureData
};
this._texture = twgl.createTexture(gl, textureOptions);
this._silhouette.update(bitmapData);
this._silhouette.update(textureData);
}
// Do these last in case any of the above throws an exception

View File

@@ -77,10 +77,14 @@ class SVGSkin extends Skin {
this._textureScale = newScale;
this._svgRenderer._draw(this._textureScale, () => {
if (this._textureScale === newScale) {
const canvas = this._svgRenderer.canvas;
const context = canvas.getContext('2d');
const textureData = context.getImageData(0, 0, canvas.width, canvas.height);
const gl = this._renderer.gl;
gl.bindTexture(gl.TEXTURE_2D, this._texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this._svgRenderer.canvas);
this._silhouette.update(this._svgRenderer.canvas);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, textureData);
this._silhouette.update(textureData);
}
});
}
@@ -99,20 +103,28 @@ class SVGSkin extends Skin {
this._svgRenderer.fromString(svgData, 1, () => {
const gl = this._renderer.gl;
this._textureScale = this._maxTextureScale = 1;
// Pull out the ImageData from the canvas. ImageData speeds up
// updating Silhouette and is better handled by more browsers in
// regards to memory.
const canvas = this._svgRenderer.canvas;
const context = canvas.getContext('2d');
const textureData = context.getImageData(0, 0, canvas.width, canvas.height);
if (this._texture) {
gl.bindTexture(gl.TEXTURE_2D, this._texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this._svgRenderer.canvas);
this._silhouette.update(this._svgRenderer.canvas);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, textureData);
this._silhouette.update(textureData);
} else {
// TODO: mipmaps?
const textureOptions = {
auto: true,
wrap: gl.CLAMP_TO_EDGE,
src: this._svgRenderer.canvas
src: textureData
};
this._texture = twgl.createTexture(gl, textureOptions);
this._silhouette.update(this._svgRenderer.canvas);
this._silhouette.update(textureData);
}
const maxDimension = Math.max(this._svgRenderer.canvas.width, this._svgRenderer.canvas.height);

View File

@@ -19,12 +19,12 @@ let __SilhouetteUpdateCanvas;
* @param {number} y - y
* @return {number} Alpha value for x/y position
*/
const getPoint = ({_width: width, _height: height, _data: data}, x, y) => {
const getPoint = ({_width: width, _height: height, _colorData: data}, x, y) => {
// 0 if outside bouds, otherwise read from data.
if (x >= width || y >= height || x < 0 || y < 0) {
return 0;
}
return data[(y * width) + x];
return data[(((y * width) + x) * 4) + 3];
};
/**
@@ -76,7 +76,6 @@ class Silhouette {
* The data representing a skin's silhouette shape.
* @type {Uint8ClampedArray}
*/
this._data = null;
this._colorData = null;
this.colorAtNearest = this.colorAtLinear = (_, dst) => dst.fill(0);
@@ -88,28 +87,33 @@ class Silhouette {
* rendering can be queried from.
*/
update (bitmapData) {
const canvas = Silhouette._updateCanvas();
const width = this._width = canvas.width = bitmapData.width;
const height = this._height = canvas.height = bitmapData.height;
const ctx = canvas.getContext('2d');
let imageData;
if (bitmapData instanceof ImageData) {
// If handed ImageData directly, use it directly.
imageData = bitmapData;
this._width = bitmapData.width;
this._height = bitmapData.height;
} else {
// Draw about anything else to our update canvas and poll image data
// from that.
const canvas = Silhouette._updateCanvas();
const width = this._width = canvas.width = bitmapData.width;
const height = this._height = canvas.height = bitmapData.height;
const ctx = canvas.getContext('2d');
if (!(width && height)) {
return;
if (!(width && height)) {
return;
}
ctx.clearRect(0, 0, width, height);
ctx.drawImage(bitmapData, 0, 0, width, height);
imageData = ctx.getImageData(0, 0, width, height);
}
ctx.clearRect(0, 0, width, height);
ctx.drawImage(bitmapData, 0, 0, width, height);
const imageData = ctx.getImageData(0, 0, width, height);
this._data = new Uint8ClampedArray(imageData.data.length / 4);
this._colorData = imageData.data;
// delete our custom overriden "uninitalized" color functions
// let the prototype work for itself
delete this.colorAtNearest;
delete this.colorAtLinear;
for (let i = 0; i < imageData.data.length; i += 4) {
this._data[i / 4] = imageData.data[i + 3];
}
}
/**
@@ -166,7 +170,7 @@ class Silhouette {
* @return {boolean} If the nearest pixel has an alpha value.
*/
isTouchingNearest (vec) {
if (!this._data) return;
if (!this._colorData) return;
return getPoint(
this,
Math.floor(vec[0] * (this._width - 1)),
@@ -181,7 +185,7 @@ class Silhouette {
* @return {boolean} Any of the pixels have some alpha.
*/
isTouchingLinear (vec) {
if (!this._data) return;
if (!this._colorData) return;
const x = Math.floor(vec[0] * (this._width - 1));
const y = Math.floor(vec[1] * (this._height - 1));
return getPoint(this, x, y) > 0 ||