Move SVG renderer logic back into SVGSkin
This will allow for fancier stuff to be done with the SVG viewbox in the future to avoid subpixel jitter.
This commit is contained in:
parent
9560de4d75
commit
801b0dab1f
@ -1,7 +1,7 @@
|
|||||||
const twgl = require('twgl.js');
|
const twgl = require('twgl.js');
|
||||||
|
|
||||||
const Skin = require('./Skin');
|
const Skin = require('./Skin');
|
||||||
const SvgRenderer = require('scratch-svg-renderer').SVGRenderer;
|
const {loadSvgString, serializeSvgToString} = require('scratch-svg-renderer');
|
||||||
const ShaderManager = require('./ShaderManager');
|
const ShaderManager = require('./ShaderManager');
|
||||||
|
|
||||||
const MAX_TEXTURE_DIMENSION = 2048;
|
const MAX_TEXTURE_DIMENSION = 2048;
|
||||||
@ -28,8 +28,17 @@ class SVGSkin extends Skin {
|
|||||||
/** @type {RenderWebGL} */
|
/** @type {RenderWebGL} */
|
||||||
this._renderer = renderer;
|
this._renderer = renderer;
|
||||||
|
|
||||||
/** @type {SvgRenderer} */
|
/** @type {HTMLImageElement} */
|
||||||
this._svgRenderer = new SvgRenderer();
|
this._svgImage = null;
|
||||||
|
|
||||||
|
/** @type {Array<number>} */
|
||||||
|
this._size = [0, 0];
|
||||||
|
|
||||||
|
/** @type {HTMLCanvasElement} */
|
||||||
|
this._canvas = document.createElement('canvas');
|
||||||
|
|
||||||
|
/** @type {CanvasRenderingContext2D} */
|
||||||
|
this._context = this._canvas.getContext('2d');
|
||||||
|
|
||||||
/** @type {Array<WebGLTexture>} */
|
/** @type {Array<WebGLTexture>} */
|
||||||
this._scaledMIPs = [];
|
this._scaledMIPs = [];
|
||||||
@ -56,7 +65,7 @@ class SVGSkin extends Skin {
|
|||||||
* @return {Array<number>} the natural size, in Scratch units, of this skin.
|
* @return {Array<number>} the natural size, in Scratch units, of this skin.
|
||||||
*/
|
*/
|
||||||
get size () {
|
get size () {
|
||||||
return this._svgRenderer.size;
|
return [this._size[0], this._size[1]];
|
||||||
}
|
}
|
||||||
|
|
||||||
useNearest (scale, drawable) {
|
useNearest (scale, drawable) {
|
||||||
@ -94,18 +103,27 @@ class SVGSkin extends Skin {
|
|||||||
* @return {SVGMIP} An object that handles creating and updating SVG textures.
|
* @return {SVGMIP} An object that handles creating and updating SVG textures.
|
||||||
*/
|
*/
|
||||||
createMIP (scale) {
|
createMIP (scale) {
|
||||||
this._svgRenderer.draw(scale);
|
const [width, height] = this._size;
|
||||||
|
this._canvas.width = width * scale;
|
||||||
|
this._canvas.height = height * scale;
|
||||||
|
if (
|
||||||
|
this._canvas.width <= 0 ||
|
||||||
|
this._canvas.height <= 0 ||
|
||||||
|
// Even if the canvas at the current scale has a nonzero size, the image's dimensions are floored
|
||||||
|
// pre-scaling; e.g. if an image has a width of 0.4 and is being rendered at 3x scale, the canvas will have
|
||||||
|
// a width of 1, but the image's width will be rounded down to 0 on some browsers (Firefox) prior to being
|
||||||
|
// drawn at that scale, resulting in an IndexSizeError if we attempt to draw it.
|
||||||
|
this._svgImage.naturalWidth <= 0 ||
|
||||||
|
this._svgImage.naturalHeight <= 0
|
||||||
|
) return super.getTexture();
|
||||||
|
this._context.clearRect(0, 0, this._canvas.width, this._canvas.height);
|
||||||
|
this._context.setTransform(scale, 0, 0, scale, 0, 0);
|
||||||
|
this._context.drawImage(this._svgImage, 0, 0);
|
||||||
|
|
||||||
// Pull out the ImageData from the canvas. ImageData speeds up
|
// Pull out the ImageData from the canvas. ImageData speeds up
|
||||||
// updating Silhouette and is better handled by more browsers in
|
// updating Silhouette and is better handled by more browsers in
|
||||||
// regards to memory.
|
// regards to memory.
|
||||||
const canvas = this._svgRenderer.canvas;
|
const textureData = this._context.getImageData(0, 0, this._canvas.width, this._canvas.height);
|
||||||
// If one of the canvas dimensions is 0, set this MIP to an empty image texture.
|
|
||||||
// This avoids an IndexSizeError from attempting to getImageData when one of the dimensions is 0.
|
|
||||||
if (canvas.width === 0 || canvas.height === 0) return super.getTexture();
|
|
||||||
|
|
||||||
const context = canvas.getContext('2d');
|
|
||||||
const textureData = context.getImageData(0, 0, canvas.width, canvas.height);
|
|
||||||
|
|
||||||
const textureOptions = {
|
const textureOptions = {
|
||||||
auto: false,
|
auto: false,
|
||||||
@ -147,7 +165,7 @@ class SVGSkin extends Skin {
|
|||||||
// Can't use bitwise stuff here because we need to handle negative exponents
|
// Can't use bitwise stuff here because we need to handle negative exponents
|
||||||
const mipScale = Math.pow(2, mipLevel - INDEX_OFFSET);
|
const mipScale = Math.pow(2, mipLevel - INDEX_OFFSET);
|
||||||
|
|
||||||
if (this._svgRenderer.loaded && !this._scaledMIPs[mipLevel]) {
|
if (this._svgImage.complete && !this._scaledMIPs[mipLevel]) {
|
||||||
this._scaledMIPs[mipLevel] = this.createMIP(mipScale);
|
this._scaledMIPs[mipLevel] = this.createMIP(mipScale);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -172,14 +190,21 @@ class SVGSkin extends Skin {
|
|||||||
* @param {Array<number>} [rotationCenter] - Optional rotation center for the SVG.
|
* @param {Array<number>} [rotationCenter] - Optional rotation center for the SVG.
|
||||||
*/
|
*/
|
||||||
setSVG (svgData, rotationCenter) {
|
setSVG (svgData, rotationCenter) {
|
||||||
this._svgRenderer.loadSVG(svgData, false, () => {
|
const svgTag = loadSvgString(svgData);
|
||||||
const svgSize = this._svgRenderer.size;
|
this._svgImage = document.createElement('img');
|
||||||
if (svgSize[0] === 0 || svgSize[1] === 0) {
|
const svgText = serializeSvgToString(svgTag, true /* shouldInjectFonts */);
|
||||||
|
|
||||||
|
this._svgImage.addEventListener('load', () => {
|
||||||
|
const {x, y, width, height} = svgTag.viewBox.baseVal;
|
||||||
|
this._size[0] = width;
|
||||||
|
this._size[1] = height;
|
||||||
|
|
||||||
|
if (width === 0 || height === 0) {
|
||||||
super.setEmptyImageData();
|
super.setEmptyImageData();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const maxDimension = Math.ceil(Math.max(this.size[0], this.size[1]));
|
const maxDimension = Math.ceil(Math.max(width, height));
|
||||||
let testScale = 2;
|
let testScale = 2;
|
||||||
for (testScale; maxDimension * testScale <= MAX_TEXTURE_DIMENSION; testScale *= 2) {
|
for (testScale; maxDimension * testScale <= MAX_TEXTURE_DIMENSION; testScale *= 2) {
|
||||||
this._maxTextureScale = testScale;
|
this._maxTextureScale = testScale;
|
||||||
@ -188,12 +213,15 @@ class SVGSkin extends Skin {
|
|||||||
this.resetMIPs();
|
this.resetMIPs();
|
||||||
|
|
||||||
if (typeof rotationCenter === 'undefined') rotationCenter = this.calculateRotationCenter();
|
if (typeof rotationCenter === 'undefined') rotationCenter = this.calculateRotationCenter();
|
||||||
const viewOffset = this._svgRenderer.viewOffset;
|
// Compensate for viewbox offset.
|
||||||
this._rotationCenter[0] = rotationCenter[0] - viewOffset[0];
|
// See https://github.com/LLK/scratch-render/pull/90.
|
||||||
this._rotationCenter[1] = rotationCenter[1] - viewOffset[1];
|
this._rotationCenter[0] = rotationCenter[0] - x;
|
||||||
|
this._rotationCenter[1] = rotationCenter[1] - y;
|
||||||
|
|
||||||
this.emit(Skin.Events.WasAltered);
|
this.emit(Skin.Events.WasAltered);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this._svgImage.src = `data:image/svg+xml;utf8,${encodeURIComponent(svgText)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ window.waitForSVGSkinLoad = renderer => new Promise(resolve => {
|
|||||||
for (const skin of renderer._allSkins) {
|
for (const skin of renderer._allSkins) {
|
||||||
if (skin.constructor.name !== 'SVGSkin') continue;
|
if (skin.constructor.name !== 'SVGSkin') continue;
|
||||||
numSVGSkins++;
|
numSVGSkins++;
|
||||||
if (skin._svgRenderer.loaded) numLoadedSVGSkins++;
|
if (skin._svgImage.complete) numLoadedSVGSkins++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (numSVGSkins === numLoadedSVGSkins) {
|
if (numSVGSkins === numLoadedSVGSkins) {
|
||||||
@ -47,7 +47,7 @@ window.initVM = render => {
|
|||||||
|
|
||||||
vm.attachStorage(storage);
|
vm.attachStorage(storage);
|
||||||
vm.attachRenderer(render);
|
vm.attachRenderer(render);
|
||||||
vm.attachV2SVGAdapter(new ScratchSVGRenderer.SVGRenderer());
|
vm.attachV2SVGAdapter(ScratchSVGRenderer.V2SVGAdapter);
|
||||||
vm.attachV2BitmapAdapter(new ScratchSVGRenderer.BitmapAdapter());
|
vm.attachV2BitmapAdapter(new ScratchSVGRenderer.BitmapAdapter());
|
||||||
|
|
||||||
return vm;
|
return vm;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user