Support rendering SVGs

This commit is contained in:
Christopher Willis-Ford 2016-05-18 11:48:57 -07:00
parent 06f7d46613
commit db69b23a0e
2 changed files with 102 additions and 27 deletions

View file

@ -13,11 +13,13 @@
"scripts": {
"test": "make test"
},
"devDependencies": {
"dependencies": {
"eslint": "2.7.0",
"json-loader": "0.5.4",
"svg-to-image": "1.1.3",
"tap": "5.7.1",
"twgl.js": "1.5.2",
"webpack": "1.13.0"
"webpack": "1.13.0",
"xhr": "2.2.0"
}
}

View file

@ -1,4 +1,6 @@
var twgl = require('twgl.js');
var svgToImage = require('svg-to-image');
var xhr = require('xhr');
/**
* An object which can be drawn by the renderer.
@ -73,8 +75,9 @@ Drawable.dirtyAllTransforms = function () {
// TODO: fall back on a built-in skin to protect against network problems
Drawable.prototype._DEFAULT_SKIN = {
squirrel: '7e24c99c1b853e52f8e7f9004416fa34.png',
bus: '66895930177178ea01d9e610917f8acf.png'
}.squirrel;
bus: '66895930177178ea01d9e610917f8acf.png',
scratch_cat: '09dc888b0b7df19f70d81588ae73420e.svg'
}.scratch_cat;
/**
* Dispose of this Drawable. Do not use it after calling this method.
@ -111,31 +114,18 @@ Drawable.prototype.setSkin = function (skin_md5ext) {
// TODO: cache Skins instead of loading each time. Ref count them?
// TODO: share Skins across Drawables - see also destroy()
if (this._uniforms.u_texture) {
this._gl.deleteTexture(this._uniforms);
this._gl.deleteTexture(this._uniforms.u_texture);
}
if (skin_md5ext) {
var url =
'https://cdn.assets.scratch.mit.edu/internalapi/asset/' +
skin_md5ext +
'/get/';
var options = {
auto: true,
src: url
};
var instance = this;
var callback = function (err, texture, source) {
if (!err) {
instance._uniforms.u_texture = texture;
instance._setDimensions(source.width, source.height);
}
};
// If we already have a texture, keep it until the new one loads.
if (this._uniforms.u_texture) {
twgl.createTexture(this._gl, options, callback);
}
else {
this._uniforms.u_texture =
twgl.createTexture(this._gl, options, callback);
var ext = skin_md5ext.substring(skin_md5ext.indexOf('.')+1);
switch (ext) {
case 'svg':
case 'svgz':
this._setSkinSVG(skin_md5ext);
break;
default:
this._setSkinBitmap(skin_md5ext);
break;
}
}
else {
@ -143,6 +133,89 @@ Drawable.prototype.setSkin = function (skin_md5ext) {
}
};
/**
* Load a bitmap skin. Supports the same formats as the Image element.
* @param {string} skin_md5ext The MD5 and file extension of the bitmap skin.
* @private
*/
Drawable.prototype._setSkinBitmap = function (skin_md5ext) {
var url =
'https://cdn.assets.scratch.mit.edu/internalapi/asset/' +
skin_md5ext +
'/get/';
this._setSkinCore(url, 2);
};
/**
* Load an SVG-based skin. This still needs quite a bit of work to match the
* level of quality found in Scratch 2.0:
* - We should detect when a skin is being scaled up and render the SVG at a
* higher resolution in those cases.
* - Colors seem a little off. This may be browser-specific.
* - This method works in Chrome, Firefox, Safari, and Edge but causes a
* security error in IE.
* @param {string} skin_md5ext The MD5 and file extension of the SVG skin.
* @private
*/
Drawable.prototype._setSkinSVG = function (skin_md5ext) {
var url =
'https://cdn.assets.scratch.mit.edu/internalapi/asset/' +
skin_md5ext +
'/get/';
var instance = this;
function gotSVG(err, response, body) {
if (!err) {
svgToImage(body, gotImage);
}
}
function gotImage(err, image) {
if (!err) {
instance._setSkinCore(image, 1);
}
}
xhr.get({
useXDR: true,
url: url
}, gotSVG);
};
/**
* Common code for setting all skin types.
* @param {string|Image} source The source of image data for the skin.
* @param costumeResolution {int} The resolution to use for this skin.
* @private
*/
Drawable.prototype._setSkinCore = function (source, costumeResolution) {
var instance = this;
var callback = function (err, texture, source) {
if (!err) {
instance._costumeResolution = costumeResolution || 1;
instance._uniforms.u_texture = texture;
instance._setDimensions(source.width, source.height);
}
};
var options = {
auto: true,
src: source
};
var willCallCallback = typeof source == 'string';
var texture = twgl.createTexture(
this._gl, options, willCallCallback ? callback : null);
// If we don't already have a texture, or if we won't get a callback when
// the new one loads, then just start using the texture immediately.
if (willCallCallback) {
if (!this._uniforms.u_texture) {
this._uniforms.u_texture = texture;
this._setDimensions(0, 0);
}
}
else {
callback(null, texture, source);
}
};
/**
* Retrieve the shader uniforms to be used when rendering this Drawable.
* @returns {Object.<string, *>}