mirror of
https://github.com/scratchfoundation/scratch-vm.git
synced 2024-12-23 14:32:59 -05:00
Merge pull request #1082 from fsih/updateBitmap
Add updateBitmap function. getCostume can now serve bitmaps as well as svgs
This commit is contained in:
commit
5e6873628e
2 changed files with 66 additions and 4 deletions
|
@ -34,6 +34,7 @@
|
||||||
"babel-loader": "^7.0.0",
|
"babel-loader": "^7.0.0",
|
||||||
"babel-preset-es2015": "^6.24.1",
|
"babel-preset-es2015": "^6.24.1",
|
||||||
"buffer-loader": "0.0.1",
|
"buffer-loader": "0.0.1",
|
||||||
|
"canvas-toBlob": "1.0.0",
|
||||||
"copy-webpack-plugin": "4.2.1",
|
"copy-webpack-plugin": "4.2.1",
|
||||||
"decode-html": "2.0.0",
|
"decode-html": "2.0.0",
|
||||||
"escape-html": "1.0.3",
|
"escape-html": "1.0.3",
|
||||||
|
|
|
@ -2,6 +2,7 @@ const TextEncoder = require('text-encoding').TextEncoder;
|
||||||
const EventEmitter = require('events');
|
const EventEmitter = require('events');
|
||||||
const JSZip = require('jszip');
|
const JSZip = require('jszip');
|
||||||
|
|
||||||
|
const Buffer = require('buffer').Buffer;
|
||||||
const centralDispatch = require('./dispatch/central-dispatch');
|
const centralDispatch = require('./dispatch/central-dispatch');
|
||||||
const ExtensionManager = require('./extension-support/extension-manager');
|
const ExtensionManager = require('./extension-support/extension-manager');
|
||||||
const log = require('./util/log');
|
const log = require('./util/log');
|
||||||
|
@ -17,6 +18,7 @@ const Variable = require('./engine/variable');
|
||||||
const {loadCostume} = require('./import/load-costume.js');
|
const {loadCostume} = require('./import/load-costume.js');
|
||||||
const {loadSound} = require('./import/load-sound.js');
|
const {loadSound} = require('./import/load-sound.js');
|
||||||
const {serializeSounds, serializeCostumes} = require('./serialization/serialize-assets');
|
const {serializeSounds, serializeCostumes} = require('./serialization/serialize-assets');
|
||||||
|
require('canvas-toBlob');
|
||||||
|
|
||||||
const RESERVED_NAMES = ['_mouse_', '_stage_', '_edge_', '_myself_', '_random_'];
|
const RESERVED_NAMES = ['_mouse_', '_stage_', '_edge_', '_myself_', '_random_'];
|
||||||
|
|
||||||
|
@ -571,19 +573,77 @@ class VirtualMachine extends EventEmitter {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get an SVG string from storage.
|
* Get a string representation of the image from storage.
|
||||||
* @param {int} costumeIndex - the index of the costume to be got.
|
* @param {int} costumeIndex - the index of the costume to be got.
|
||||||
* @return {string} the costume's SVG string, or null if it's not an SVG costume.
|
* @return {string} the costume's SVG string if it's SVG,
|
||||||
|
* a dataURI if it's a PNG or JPG, or null if it couldn't be found or decoded.
|
||||||
*/
|
*/
|
||||||
getCostume (costumeIndex) {
|
getCostume (costumeIndex) {
|
||||||
const id = this.editingTarget.getCostumes()[costumeIndex].assetId;
|
const id = this.editingTarget.getCostumes()[costumeIndex].assetId;
|
||||||
if (id && this.runtime && this.runtime.storage &&
|
if (!id || !this.runtime || !this.runtime.storage) return null;
|
||||||
this.runtime.storage.get(id).dataFormat === 'svg') {
|
const format = this.runtime.storage.get(id).dataFormat;
|
||||||
|
if (format === this.runtime.storage.DataFormat.SVG) {
|
||||||
return this.runtime.storage.get(id).decodeText();
|
return this.runtime.storage.get(id).decodeText();
|
||||||
|
} else if (format === this.runtime.storage.DataFormat.PNG ||
|
||||||
|
format === this.runtime.storage.DataFormat.JPG) {
|
||||||
|
return this.runtime.storage.get(id).encodeDataURI();
|
||||||
}
|
}
|
||||||
|
log.error(`Unhandled format: ${this.runtime.storage.get(id).dataFormat}`);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update a costume with the given bitmap
|
||||||
|
* @param {!int} costumeIndex - the index of the costume to be updated.
|
||||||
|
* @param {!ImageData} bitmap - new bitmap for the renderer.
|
||||||
|
* @param {!number} rotationCenterX x of point about which the costume rotates, relative to its upper left corner
|
||||||
|
* @param {!number} rotationCenterY y of point about which the costume rotates, relative to its upper left corner
|
||||||
|
* @param {!number} bitmapResolution 1 for bitmaps that have 1 pixel per unit of stage,
|
||||||
|
* 2 for double-resolution bitmaps
|
||||||
|
*/
|
||||||
|
updateBitmap (costumeIndex, bitmap, rotationCenterX, rotationCenterY, bitmapResolution) {
|
||||||
|
const costume = this.editingTarget.getCostumes()[costumeIndex];
|
||||||
|
if (!(costume && this.runtime && this.runtime.renderer)) return;
|
||||||
|
|
||||||
|
costume.rotationCenterX = rotationCenterX;
|
||||||
|
costume.rotationCenterY = rotationCenterY;
|
||||||
|
|
||||||
|
// @todo: updateBitmapSkin does not take ImageData
|
||||||
|
const canvas = document.createElement('canvas');
|
||||||
|
canvas.width = bitmap.width;
|
||||||
|
canvas.height = bitmap.height;
|
||||||
|
const context = canvas.getContext('2d');
|
||||||
|
context.putImageData(bitmap, 0, 0);
|
||||||
|
|
||||||
|
// Divide by resolution because the renderer's definition of the rotation center
|
||||||
|
// is the rotation center divided by the bitmap resolution
|
||||||
|
this.runtime.renderer.updateBitmapSkin(
|
||||||
|
costume.skinId,
|
||||||
|
canvas,
|
||||||
|
bitmapResolution,
|
||||||
|
[rotationCenterX / bitmapResolution, rotationCenterY / bitmapResolution]
|
||||||
|
);
|
||||||
|
|
||||||
|
// @todo there should be a better way to get from ImageData to a decodable storage format
|
||||||
|
canvas.toBlob(blob => {
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.addEventListener('loadend', () => {
|
||||||
|
const storage = this.runtime.storage;
|
||||||
|
costume.assetId = storage.builtinHelper.cache(
|
||||||
|
storage.AssetType.ImageBitmap,
|
||||||
|
storage.DataFormat.PNG,
|
||||||
|
Buffer.from(reader.result)
|
||||||
|
);
|
||||||
|
costume.dataFormat = storage.DataFormat.PNG;
|
||||||
|
costume.bitmapResolution = bitmapResolution;
|
||||||
|
costume.size = [bitmap.width, bitmap.height];
|
||||||
|
costume.md5 = `${costume.assetId}.${costume.dataFormat}`;
|
||||||
|
this.emitTargetsUpdate();
|
||||||
|
});
|
||||||
|
reader.readAsArrayBuffer(blob);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update a costume with the given SVG
|
* Update a costume with the given SVG
|
||||||
* @param {int} costumeIndex - the index of the costume to be updated.
|
* @param {int} costumeIndex - the index of the costume to be updated.
|
||||||
|
@ -609,6 +669,7 @@ class VirtualMachine extends EventEmitter {
|
||||||
// so the dataFormat should be 'svg'
|
// so the dataFormat should be 'svg'
|
||||||
costume.dataFormat = storage.DataFormat.SVG;
|
costume.dataFormat = storage.DataFormat.SVG;
|
||||||
costume.md5 = `${costume.assetId}.${costume.dataFormat}`;
|
costume.md5 = `${costume.assetId}.${costume.dataFormat}`;
|
||||||
|
delete costume.bitmapResolution;
|
||||||
this.emitTargetsUpdate();
|
this.emitTargetsUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue