Spaces:
Runtime error
Runtime error
| const xmlEscape = require('../../util/xml-escape'); | |
| const uid = require('../../util/uid'); | |
| const StageLayering = require('../../engine/stage-layering'); | |
| class CanvasVar { | |
| static customId = 'canvasData' | |
| /** | |
| * initiats the variable | |
| * @param {Runtime} runtime the runtime this canvas exists inside | |
| * @param {string} id this canvas's id | |
| * @param {string} name the name of this canvas | |
| * @param {[number,number]|string|Image} [img=[1, 1]] optionally the image to be loaded into this canvas | |
| */ | |
| constructor (runtime, id, name, img = [1, 1]) { | |
| this.id = id ?? uid(); | |
| this.name = name; | |
| this.type = 'canvas'; | |
| this.customId = CanvasVar.customId; | |
| this.runtime = runtime; | |
| this.renderer = runtime.renderer; | |
| this.canvas = document.createElement('canvas'); | |
| this._costumeDrawer = this.renderer.createDrawable(StageLayering.SPRITE_LAYER); | |
| this._skinId = this.renderer.createBitmapSkin(this.canvas, 1); | |
| this._monitorUpToDate = false; | |
| this._cachedMonContent = [null, 0]; | |
| this._cameraStuff = { | |
| x: 0, | |
| y: 0, | |
| rotation: 0, | |
| scaleX: 1, | |
| scaleY: 1 | |
| }; | |
| // img is just a size to be given to the canvas | |
| if (Array.isArray(img)) { | |
| this.size = img; | |
| return; | |
| } | |
| if (img) this.loadImage(img); | |
| } | |
| serialize(canvas) { | |
| const instance = canvas ?? this; | |
| return [instance.id, instance.name, instance.canvas.toDataURL()]; | |
| } | |
| getSnapshot() { | |
| const snap = new Image(); | |
| snap.src = this.canvas.toDataURL(); | |
| return snap; | |
| } | |
| toReporterContent() { | |
| return this.canvas; | |
| } | |
| toMonitorContent() { | |
| if (!this._monitorUpToDate) { | |
| this._cachedMonContent = this.getSnapshot(); | |
| this._monitorUpToDate = true; | |
| } | |
| return this._cachedMonContent; | |
| } | |
| toListEditor() { | |
| return this.toString(); | |
| } | |
| fromListEditor(edit) { | |
| if (this.toString() !== edit) { | |
| this.loadImage(edit); | |
| } | |
| return this; | |
| } | |
| toString() { | |
| return this.canvas.toDataURL(); | |
| } | |
| toXML(isLocal) { | |
| return `<variable type="canvas" id="${this.id}" islocal="${isLocal | |
| }" iscloud="false">${xmlEscape(this.name)}</variable>`; | |
| } | |
| toToolboxDefault(fieldName) { | |
| return `<field name="${fieldName}" id="${this.id}" variabletype="canvas">${xmlEscape(this.name)}</field>`; | |
| } | |
| get size() { | |
| return [this.canvas.width, this.canvas.height]; | |
| } | |
| set size(size) { | |
| this.canvas.width = size[0]; | |
| this.canvas.height = size[1]; | |
| } | |
| /** | |
| * load an image onto the 2d canvas | |
| * @param {Image} img the image to load onto the 2d canvas | |
| */ | |
| async loadImage(img) { | |
| // we where not given something we can use imediatly :( | |
| if (img instanceof Image && !img.complete) { | |
| await new Promise(resolve => { | |
| img.onload = resolve; | |
| img.onerror = resolve; | |
| }); | |
| } | |
| if (typeof img === 'string') { | |
| await new Promise(resolve => { | |
| const src = img; | |
| img = new Image(); | |
| img.onload = resolve; | |
| img.onerror = resolve; | |
| img.src = src; | |
| }); | |
| } | |
| this.canvas.width = img.width; | |
| this.canvas.height = img.height; | |
| const ctx = this.canvas.getContext('2d'); | |
| ctx.drawImage(img, 0, 0); | |
| // do this cause we just added new content | |
| this.updateCanvasContentRenders(); | |
| } | |
| stampDrawable(id, x, y) { | |
| // drawable doesnt exist, we will get an error if we try to access this drawable | |
| if (!this.renderer._allDrawables[id]) return; | |
| const drawable = this.renderer.extractDrawableScreenSpace(id); | |
| // never got any data, ignore request | |
| if (!drawable) return; | |
| const ctx = this.canvas.getContext('2d'); | |
| ctx.putImageData(drawable.imageData, x, y); | |
| } | |
| stampCostume(target, costumeName, x, y) { | |
| const skin = costumeName !== '__current__' | |
| ? (() => { | |
| const costumeIdx = target.getCostumeIndexByName(costumeName); | |
| const costumeList = target.getCostumes(); | |
| const costume = costumeList[costumeIdx]; | |
| return this.renderer._allSkins[costume.skinId]; | |
| })() | |
| : this.renderer._allDrawables[target.drawableID].skin; | |
| const ctx = this.canvas.getContext('2d'); | |
| // draw svg skins loaded image element | |
| if (skin._svgImage) { | |
| ctx.drawImage(skin._svgImage, x, y); | |
| return; | |
| } | |
| // draw the generated content of TextCostumeSkin or TextBubbleSkin directly | |
| if (skin._canvas) { | |
| ctx.drawImage(skin._canvas, x, y); | |
| return; | |
| } | |
| // shit, alright we cant just goofy ahh our way through this | |
| // we need to somehow request some form of image that we can just draw to the canvas | |
| // from either the webgl texture that the skin gives us or the sprite | |
| /** | |
| * TODO: please if someone could make this not shitty ass and make it just draw a | |
| * fucking webgl texture directly that would be amazing | |
| */ | |
| this.renderer.updateDrawableSkinId(this._costumeDrawer, skin.id); | |
| this.stampDrawable(this._costumeDrawer); | |
| } | |
| updateCanvasContentRenders() { | |
| this._monitorUpToDate = false; | |
| // if width or height are smaller then one, replace them with one | |
| const width = Math.max(this.canvas.width, 1); | |
| const height = Math.max(this.canvas.height, 1); | |
| const ctx = this.canvas.getContext('2d'); | |
| const printSkin = this.renderer._allSkins[this._skinId]; | |
| const imageData = ctx.getImageData(0, 0, width, height); | |
| printSkin._setTexture(imageData); | |
| } | |
| applyCanvasToTarget(target) { | |
| this.renderer.updateDrawableSkinId(target.drawableID, this._skinId); | |
| this.runtime.requestRedraw(); | |
| } | |
| } | |
| module.exports = CanvasVar; | |