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 Skin = require('./Skin');
|
||||
const SvgRenderer = require('scratch-svg-renderer').SVGRenderer;
|
||||
const {loadSvgString, serializeSvgToString} = require('scratch-svg-renderer');
|
||||
const ShaderManager = require('./ShaderManager');
|
||||
|
||||
const MAX_TEXTURE_DIMENSION = 2048;
|
||||
@ -28,8 +28,17 @@ class SVGSkin extends Skin {
|
||||
/** @type {RenderWebGL} */
|
||||
this._renderer = renderer;
|
||||
|
||||
/** @type {SvgRenderer} */
|
||||
this._svgRenderer = new SvgRenderer();
|
||||
/** @type {HTMLImageElement} */
|
||||
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>} */
|
||||
this._scaledMIPs = [];
|
||||
@ -56,7 +65,7 @@ class SVGSkin extends Skin {
|
||||
* @return {Array<number>} the natural size, in Scratch units, of this skin.
|
||||
*/
|
||||
get size () {
|
||||
return this._svgRenderer.size;
|
||||
return [this._size[0], this._size[1]];
|
||||
}
|
||||
|
||||
useNearest (scale, drawable) {
|
||||
@ -94,18 +103,27 @@ class SVGSkin extends Skin {
|
||||
* @return {SVGMIP} An object that handles creating and updating SVG textures.
|
||||
*/
|
||||
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
|
||||
// updating Silhouette and is better handled by more browsers in
|
||||
// regards to memory.
|
||||
const canvas = this._svgRenderer.canvas;
|
||||
// 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 textureData = this._context.getImageData(0, 0, this._canvas.width, this._canvas.height);
|
||||
|
||||
const textureOptions = {
|
||||
auto: false,
|
||||
@ -147,7 +165,7 @@ class SVGSkin extends Skin {
|
||||
// Can't use bitwise stuff here because we need to handle negative exponents
|
||||
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);
|
||||
}
|
||||
|
||||
@ -172,14 +190,21 @@ class SVGSkin extends Skin {
|
||||
* @param {Array<number>} [rotationCenter] - Optional rotation center for the SVG.
|
||||
*/
|
||||
setSVG (svgData, rotationCenter) {
|
||||
this._svgRenderer.loadSVG(svgData, false, () => {
|
||||
const svgSize = this._svgRenderer.size;
|
||||
if (svgSize[0] === 0 || svgSize[1] === 0) {
|
||||
const svgTag = loadSvgString(svgData);
|
||||
this._svgImage = document.createElement('img');
|
||||
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();
|
||||
return;
|
||||
}
|
||||
|
||||
const maxDimension = Math.ceil(Math.max(this.size[0], this.size[1]));
|
||||
const maxDimension = Math.ceil(Math.max(width, height));
|
||||
let testScale = 2;
|
||||
for (testScale; maxDimension * testScale <= MAX_TEXTURE_DIMENSION; testScale *= 2) {
|
||||
this._maxTextureScale = testScale;
|
||||
@ -188,12 +213,15 @@ class SVGSkin extends Skin {
|
||||
this.resetMIPs();
|
||||
|
||||
if (typeof rotationCenter === 'undefined') rotationCenter = this.calculateRotationCenter();
|
||||
const viewOffset = this._svgRenderer.viewOffset;
|
||||
this._rotationCenter[0] = rotationCenter[0] - viewOffset[0];
|
||||
this._rotationCenter[1] = rotationCenter[1] - viewOffset[1];
|
||||
// Compensate for viewbox offset.
|
||||
// See https://github.com/LLK/scratch-render/pull/90.
|
||||
this._rotationCenter[0] = rotationCenter[0] - x;
|
||||
this._rotationCenter[1] = rotationCenter[1] - y;
|
||||
|
||||
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) {
|
||||
if (skin.constructor.name !== 'SVGSkin') continue;
|
||||
numSVGSkins++;
|
||||
if (skin._svgRenderer.loaded) numLoadedSVGSkins++;
|
||||
if (skin._svgImage.complete) numLoadedSVGSkins++;
|
||||
}
|
||||
|
||||
if (numSVGSkins === numLoadedSVGSkins) {
|
||||
@ -47,7 +47,7 @@ window.initVM = render => {
|
||||
|
||||
vm.attachStorage(storage);
|
||||
vm.attachRenderer(render);
|
||||
vm.attachV2SVGAdapter(new ScratchSVGRenderer.SVGRenderer());
|
||||
vm.attachV2SVGAdapter(ScratchSVGRenderer.V2SVGAdapter);
|
||||
vm.attachV2BitmapAdapter(new ScratchSVGRenderer.BitmapAdapter());
|
||||
|
||||
return vm;
|
||||
|
Loading…
x
Reference in New Issue
Block a user