mirror of
https://github.com/piskelapp/piskel.git
synced 2023-08-10 21:12:52 +03:00
deploy dev version
This commit is contained in:
111
dev/js/utils/serialization/Deserializer.js
Normal file
111
dev/js/utils/serialization/Deserializer.js
Normal file
@@ -0,0 +1,111 @@
|
||||
(function () {
|
||||
var ns = $.namespace('pskl.utils.serialization');
|
||||
|
||||
ns.Deserializer = function (data, callback) {
|
||||
this.layersToLoad_ = 0;
|
||||
this.data_ = data;
|
||||
this.callback_ = callback;
|
||||
this.piskel_ = null;
|
||||
this.layers_ = [];
|
||||
};
|
||||
|
||||
ns.Deserializer.deserialize = function (data, onSuccess, onError) {
|
||||
try {
|
||||
var deserializer;
|
||||
if (data.modelVersion == Constants.MODEL_VERSION) {
|
||||
deserializer = new ns.Deserializer(data, onSuccess);
|
||||
} else if (data.modelVersion == 1) {
|
||||
deserializer = new ns.backward.Deserializer_v1(data, onSuccess);
|
||||
} else {
|
||||
deserializer = new ns.backward.Deserializer_v0(data, onSuccess);
|
||||
}
|
||||
deserializer.deserialize();
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
if (typeof onError === 'function') {
|
||||
onError(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ns.Deserializer.prototype.deserialize = function () {
|
||||
var data = this.data_;
|
||||
var piskelData = data.piskel;
|
||||
var name = piskelData.name || 'Deserialized piskel';
|
||||
var description = piskelData.description || '';
|
||||
|
||||
var descriptor = new pskl.model.piskel.Descriptor(name, description);
|
||||
this.piskel_ = new pskl.model.Piskel(piskelData.width, piskelData.height, piskelData.fps, descriptor);
|
||||
|
||||
this.layersToLoad_ = piskelData.layers.length;
|
||||
piskelData.layers.forEach(this.deserializeLayer.bind(this));
|
||||
};
|
||||
|
||||
ns.Deserializer.prototype.deserializeLayer = function (layerString, index) {
|
||||
var layerData = JSON.parse(layerString);
|
||||
var layer = new pskl.model.Layer(layerData.name);
|
||||
layer.setOpacity(layerData.opacity);
|
||||
|
||||
// Backward compatibility: if the layerData is not chunked but contains a single base64PNG,
|
||||
// create a fake chunk, expected to represent all frames side-by-side.
|
||||
if (typeof layerData.chunks === 'undefined' && layerData.base64PNG) {
|
||||
this.normalizeLayerData_(layerData);
|
||||
}
|
||||
|
||||
var chunks = layerData.chunks;
|
||||
|
||||
// Prepare a frames array to store frame objects extracted from the chunks.
|
||||
var frames = [];
|
||||
Q.all(chunks.map(function (chunk) {
|
||||
// Create a promise for each chunk.
|
||||
var deferred = Q.defer();
|
||||
var image = new Image();
|
||||
// Load the chunk image in an Image object.
|
||||
image.onload = function () {
|
||||
// extract the chunkFrames from the chunk image
|
||||
var chunkFrames = pskl.utils.FrameUtils.createFramesFromChunk(image, chunk.layout);
|
||||
// add each image to the frames array, at the extracted index
|
||||
chunkFrames.forEach(function (chunkFrame) {
|
||||
frames[chunkFrame.index] = chunkFrame.frame;
|
||||
});
|
||||
deferred.resolve();
|
||||
};
|
||||
image.src = chunk.base64PNG;
|
||||
return deferred.promise;
|
||||
})).then(function () {
|
||||
frames.forEach(layer.addFrame.bind(layer));
|
||||
this.layers_[index] = layer;
|
||||
this.onLayerLoaded_();
|
||||
}.bind(this)).catch(function (error) {
|
||||
console.error('Failed to deserialize layer');
|
||||
console.error(error);
|
||||
});
|
||||
|
||||
return layer;
|
||||
};
|
||||
|
||||
ns.Deserializer.prototype.onLayerLoaded_ = function () {
|
||||
this.layersToLoad_ = this.layersToLoad_ - 1;
|
||||
if (this.layersToLoad_ === 0) {
|
||||
this.layers_.forEach(function (layer) {
|
||||
this.piskel_.addLayer(layer);
|
||||
}.bind(this));
|
||||
this.callback_(this.piskel_);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Backward comptibility only. Create a chunk for layerData objects that only contain
|
||||
* an single base64PNG without chunk/layout information.
|
||||
*/
|
||||
ns.Deserializer.prototype.normalizeLayerData_ = function (layerData) {
|
||||
var layout = [];
|
||||
for (var i = 0 ; i < layerData.frameCount ; i++) {
|
||||
layout.push([i]);
|
||||
}
|
||||
layerData.chunks = [{
|
||||
base64PNG : layerData.base64PNG,
|
||||
layout : layout
|
||||
}];
|
||||
};
|
||||
})();
|
||||
91
dev/js/utils/serialization/Serializer.js
Normal file
91
dev/js/utils/serialization/Serializer.js
Normal file
@@ -0,0 +1,91 @@
|
||||
(function () {
|
||||
var ns = $.namespace('pskl.utils.serialization');
|
||||
|
||||
var areChunksValid = function (chunks) {
|
||||
return chunks.length && chunks.every(function (chunk) {
|
||||
return chunk.base64PNG && chunk.base64PNG !== 'data:,';
|
||||
});
|
||||
};
|
||||
|
||||
var createLineLayout = function (size, offset) {
|
||||
var layout = [];
|
||||
for (var i = 0 ; i < size ; i++) {
|
||||
layout.push([i + offset]);
|
||||
}
|
||||
|
||||
return layout;
|
||||
};
|
||||
|
||||
ns.Serializer = {
|
||||
serialize : function (piskel) {
|
||||
var serializedLayers = piskel.getLayers().map(function (l) {
|
||||
return pskl.utils.serialization.Serializer.serializeLayer(l);
|
||||
});
|
||||
return JSON.stringify({
|
||||
modelVersion : Constants.MODEL_VERSION,
|
||||
piskel : {
|
||||
name : piskel.getDescriptor().name,
|
||||
description : piskel.getDescriptor().description,
|
||||
fps : pskl.app.piskelController.getFPS(),
|
||||
height : piskel.getHeight(),
|
||||
width : piskel.getWidth(),
|
||||
layers : serializedLayers
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
serializeLayer : function (layer) {
|
||||
var frames = layer.getFrames();
|
||||
var layerToSerialize = {
|
||||
name : layer.getName(),
|
||||
opacity : layer.getOpacity(),
|
||||
frameCount : frames.length
|
||||
};
|
||||
|
||||
// A layer spritesheet data can be chunked in case the spritesheet PNG is to big to be
|
||||
// converted to a dataURL.
|
||||
// Frames are divided equally amongst chunks and each chunk is converted to a spritesheet
|
||||
// PNG. If any chunk contains an invalid base64 PNG, we increase the number of chunks and
|
||||
// retry.
|
||||
var chunks = [];
|
||||
while (!areChunksValid(chunks)) {
|
||||
if (chunks.length >= frames.length) {
|
||||
// Something went horribly wrong.
|
||||
chunks = [];
|
||||
break;
|
||||
}
|
||||
|
||||
// Chunks are invalid, increase the number of chunks by one, and chunk the frames array.
|
||||
var frameChunks = pskl.utils.Array.chunk(frames, chunks.length + 1);
|
||||
|
||||
// Reset chunks array.
|
||||
chunks = [];
|
||||
|
||||
// After each chunk update the offset by te number of frames that have been processed.
|
||||
var offset = 0;
|
||||
for (var i = 0 ; i < frameChunks.length ; i++) {
|
||||
var chunkFrames = frameChunks[i];
|
||||
chunks.push({
|
||||
// create a layout array, containing the indices of the frames extracted in this chunk
|
||||
layout : createLineLayout(chunkFrames.length, offset),
|
||||
base64PNG : ns.Serializer.serializeFramesToBase64(chunkFrames),
|
||||
});
|
||||
|
||||
offset += chunkFrames.length;
|
||||
}
|
||||
}
|
||||
|
||||
layerToSerialize.chunks = chunks;
|
||||
return JSON.stringify(layerToSerialize);
|
||||
},
|
||||
|
||||
serializeFramesToBase64 : function (frames) {
|
||||
try {
|
||||
var renderer = new pskl.rendering.FramesheetRenderer(frames);
|
||||
return renderer.renderAsCanvas().toDataURL();
|
||||
} catch (e) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
};
|
||||
})();
|
||||
@@ -0,0 +1,123 @@
|
||||
(function () {
|
||||
var ns = $.namespace('pskl.utils.serialization.arraybuffer');
|
||||
|
||||
/**
|
||||
* The array buffer serialization-deserialization should only be used when when backing
|
||||
* up the animation in memory. If you actually need to dump the animation to a string
|
||||
* use the regular serialization helpers.
|
||||
*
|
||||
* This is due to the lacking support on TypedArray::toString on some browsers.
|
||||
* Will revisit the option of using this across the whole project when the APIs are less
|
||||
* green.
|
||||
*
|
||||
*/
|
||||
ns.ArrayBufferDeserializer = {
|
||||
deserialize : function (data, callback) {
|
||||
var i;
|
||||
var j;
|
||||
var buffer = data;
|
||||
var arr8 = new Uint8Array(buffer);
|
||||
var arr16 = new Uint16Array(arr8.buffer);
|
||||
var sub;
|
||||
|
||||
/********/
|
||||
/* META */
|
||||
/********/
|
||||
// Piskel meta
|
||||
var modelVersion = arr16[0];
|
||||
var width = arr16[1];
|
||||
var height = arr16[2];
|
||||
var fps = arr16[3];
|
||||
|
||||
// Descriptor meta
|
||||
var descriptorNameLength = arr16[4];
|
||||
var descriptorDescriptionLength = arr16[5];
|
||||
|
||||
// Layers meta
|
||||
var layerCount = arr16[6];
|
||||
|
||||
/********/
|
||||
/* DATA */
|
||||
/********/
|
||||
// Descriptor name
|
||||
var descriptorName = '';
|
||||
for (i = 0; i < descriptorNameLength; i++) {
|
||||
descriptorName += String.fromCharCode(arr16[7 + i]);
|
||||
}
|
||||
|
||||
// Descriptor description
|
||||
var descriptorDescription = '';
|
||||
for (i = 0; i < descriptorDescriptionLength; i++) {
|
||||
descriptorDescription = String.fromCharCode(arr16[7 + descriptorNameLength + i]);
|
||||
}
|
||||
|
||||
// Layers
|
||||
var layerStartIndex = 7 + descriptorNameLength + descriptorDescriptionLength;
|
||||
var layers = [];
|
||||
var layer;
|
||||
for (i = 0; i < layerCount; i++) {
|
||||
layer = {};
|
||||
var frames = [];
|
||||
|
||||
// Meta
|
||||
var layerNameLength = arr16[layerStartIndex];
|
||||
var opacity = arr16[layerStartIndex + 1] / 65535;
|
||||
var frameCount = arr16[layerStartIndex + 2];
|
||||
var dataUriLengthFirstHalf = arr16[layerStartIndex + 3];
|
||||
var dataUriLengthSecondHalf = arr16[layerStartIndex + 4];
|
||||
var dataUriLength = (dataUriLengthSecondHalf >>> 0) | (dataUriLengthFirstHalf << 16 >>> 0);
|
||||
|
||||
// Name
|
||||
var layerName = '';
|
||||
for (j = 0; j < layerNameLength; j++) {
|
||||
layerName += String.fromCharCode(arr16[layerStartIndex + 5 + j]);
|
||||
}
|
||||
|
||||
// Data URI
|
||||
var dataUri = '';
|
||||
for (j = 0; j < dataUriLength; j++) {
|
||||
dataUri += String.fromCharCode(arr8[(layerStartIndex + 5 + layerNameLength) * 2 + j]);
|
||||
}
|
||||
dataUri = 'data:image/png;base64,' + dataUri;
|
||||
|
||||
layerStartIndex += Math.ceil(5 + layerNameLength + (dataUriLength / 2));
|
||||
|
||||
layer.name = layerName;
|
||||
layer.opacity = opacity;
|
||||
layer.frameCount = frameCount;
|
||||
layer.dataUri = dataUri;
|
||||
layers.push(layer);
|
||||
}
|
||||
|
||||
var descriptor = new pskl.model.piskel.Descriptor(descriptorName, descriptorDescription);
|
||||
var piskel = new pskl.model.Piskel(width, height, fps, descriptor);
|
||||
var loadedLayers = 0;
|
||||
|
||||
var loadLayerImage = function(layer, cb) {
|
||||
var image = new Image();
|
||||
image.onload = function() {
|
||||
var frames = pskl.utils.FrameUtils.createFramesFromSpritesheet(this, layer.frameCount);
|
||||
frames.forEach(function (frame) {
|
||||
layer.model.addFrame(frame);
|
||||
});
|
||||
|
||||
loadedLayers++;
|
||||
if (loadedLayers == layerCount) {
|
||||
cb(piskel);
|
||||
}
|
||||
};
|
||||
image.src = layer.dataUri;
|
||||
};
|
||||
|
||||
for (i = 0; i < layerCount; i++) {
|
||||
layer = layers[i];
|
||||
var nlayer = new pskl.model.Layer(layer.name);
|
||||
layer.model = nlayer;
|
||||
nlayer.setOpacity(layer.opacity);
|
||||
piskel.addLayer(nlayer);
|
||||
|
||||
loadLayerImage.bind(this, layer, callback)();
|
||||
}
|
||||
}
|
||||
};
|
||||
})();
|
||||
183
dev/js/utils/serialization/arraybuffer/ArrayBufferSerializer.js
Normal file
183
dev/js/utils/serialization/arraybuffer/ArrayBufferSerializer.js
Normal file
@@ -0,0 +1,183 @@
|
||||
(function () {
|
||||
var ns = $.namespace('pskl.utils.serialization.arraybuffer');
|
||||
|
||||
/**
|
||||
* The array buffer serialization-deserialization should only be used when when backing
|
||||
* up the animation in memory. If you actually need to dump the animation to a string
|
||||
* use the regular serialization helpers.
|
||||
*
|
||||
* This is due to the lacking support on TypedArray::toString on some browsers.
|
||||
* Will revisit the option of using this across the whole project when the APIs are less
|
||||
* green.
|
||||
*
|
||||
*********
|
||||
* META *
|
||||
*********
|
||||
* // Piskel
|
||||
* [0] = model version
|
||||
* [1] = width
|
||||
* [2] = height
|
||||
* [3] = fps
|
||||
*
|
||||
* // Descriptor
|
||||
* [4] = name length
|
||||
* [5] = description length
|
||||
*
|
||||
* // Layers
|
||||
* [6] = layers count
|
||||
* [layer data index start] = layer name length
|
||||
* [layer data index start + 1] = opacity
|
||||
* [layer data index start + 2] = frame count
|
||||
* [layer data index start + 3] = base 64 png data url length (upper 16 bits)
|
||||
* [layer data index start + 4] = base 64 png data url length (lower 16 bits)
|
||||
*
|
||||
*********
|
||||
* DATA *
|
||||
*********
|
||||
* [7..name length-1] = name
|
||||
* [name length..description length-1] = description
|
||||
* [layer data index start + 4..layer name length-1] = layer name
|
||||
* [layer name length..base 64 png data url length-1] = base 64 png data url
|
||||
*
|
||||
*/
|
||||
|
||||
ns.ArrayBufferSerializer = {
|
||||
calculateRequiredBytes : function(piskel, framesData) {
|
||||
var width = piskel.getWidth();
|
||||
var height = piskel.getHeight();
|
||||
var descriptorNameLength = piskel.getDescriptor().name.length;
|
||||
var descriptorDescriptionLength = piskel.getDescriptor().description.length;
|
||||
var layersLength = piskel.getLayers().length;
|
||||
|
||||
var bytes = 0;
|
||||
|
||||
/********/
|
||||
/* META */
|
||||
/********/
|
||||
// Piskel meta
|
||||
bytes += 4 * 2;
|
||||
|
||||
// Descriptor meta
|
||||
bytes += 2 * 2;
|
||||
|
||||
// Layers meta
|
||||
bytes += 1 * 2;
|
||||
|
||||
/********/
|
||||
/* DATA */
|
||||
/********/
|
||||
// Descriptor name
|
||||
bytes += descriptorNameLength * 2;
|
||||
|
||||
// Descriptor description
|
||||
bytes += descriptorDescriptionLength * 2;
|
||||
|
||||
// Layers
|
||||
for (var i = 0, layers = piskel.getLayers(); i < layers.length; i++) {
|
||||
bytes += 5 * 2;
|
||||
bytes += layers[i].name.length * 2;
|
||||
bytes += framesData[i].length;
|
||||
if (bytes % 2 == 1) {
|
||||
bytes++;
|
||||
}
|
||||
}
|
||||
|
||||
return bytes;
|
||||
},
|
||||
|
||||
serialize : function (piskel) {
|
||||
var i;
|
||||
var j;
|
||||
var layers;
|
||||
var dataUri;
|
||||
var dataUriLength;
|
||||
|
||||
// Render frames
|
||||
var framesData = [];
|
||||
for (i = 0, layers = piskel.getLayers(); i < layers.length; i++) {
|
||||
var renderer = new pskl.rendering.FramesheetRenderer(layers[i].getFrames());
|
||||
dataUri = renderer.renderAsCanvas().toDataURL().split(',')[1];
|
||||
dataUriLength = dataUri.length;
|
||||
framesData.push({uri: dataUri, length: dataUriLength});
|
||||
}
|
||||
|
||||
var bytes = ns.ArrayBufferSerializer.calculateRequiredBytes(piskel, framesData);
|
||||
|
||||
var buffer = new ArrayBuffer(bytes);
|
||||
var arr8 = new Uint8Array(buffer);
|
||||
var arr16 = new Uint16Array(buffer);
|
||||
|
||||
var width = piskel.getWidth();
|
||||
var height = piskel.getHeight();
|
||||
var descriptorName = piskel.getDescriptor().name;
|
||||
var descriptorNameLength = descriptorName.length;
|
||||
var descriptorDescription = piskel.getDescriptor().description;
|
||||
var descriptorDescriptionLength = descriptorDescription.length;
|
||||
|
||||
/********/
|
||||
/* META */
|
||||
/********/
|
||||
// Piskel meta
|
||||
arr16[0] = Constants.MODEL_VERSION;
|
||||
arr16[1] = width;
|
||||
arr16[2] = height;
|
||||
arr16[3] = pskl.app.piskelController.getFPS();
|
||||
|
||||
// Descriptor meta
|
||||
arr16[4] = descriptorNameLength;
|
||||
arr16[5] = descriptorDescriptionLength;
|
||||
|
||||
// Layers meta
|
||||
arr16[6] = piskel.getLayers().length;
|
||||
|
||||
/********/
|
||||
/* DATA */
|
||||
/********/
|
||||
// Descriptor name
|
||||
for (i = 0; i < descriptorNameLength; i++) {
|
||||
arr16[7 + i] = descriptorName.charCodeAt(i);
|
||||
}
|
||||
|
||||
// Descriptor description
|
||||
for (i = 0; i < descriptorDescriptionLength; i++) {
|
||||
arr16[7 + descriptorNameLength + i] = descriptorDescription.charCodeAt(i);
|
||||
}
|
||||
|
||||
// Layers
|
||||
var layerStartIndex = 7 + descriptorNameLength + descriptorDescriptionLength;
|
||||
for (i = 0, layers = piskel.getLayers(); i < layers.length; i++) {
|
||||
var layer = layers[i];
|
||||
var frames = layer.getFrames();
|
||||
|
||||
var layerName = layer.getName();
|
||||
var layerNameLength = layerName.length;
|
||||
var opacity = layer.getOpacity();
|
||||
var frameCount = frames.length;
|
||||
|
||||
dataUri = framesData[i].uri;
|
||||
dataUriLength = framesData[i].length;
|
||||
|
||||
// Meta
|
||||
arr16[layerStartIndex] = layerNameLength;
|
||||
arr16[layerStartIndex + 1] = Math.floor(opacity * 65535);
|
||||
arr16[layerStartIndex + 2] = frameCount;
|
||||
arr16[layerStartIndex + 3] = ((dataUriLength & 0xffff0000) >> 16) >>> 0; // Upper 16
|
||||
arr16[layerStartIndex + 4] = ((dataUriLength & 0x0000ffff)) >>> 0; // Lower 16
|
||||
|
||||
// Name
|
||||
for (j = 0; j < layerNameLength; j++) {
|
||||
arr16[layerStartIndex + 5 + j] = layerName.charCodeAt(j);
|
||||
}
|
||||
|
||||
// Data URI
|
||||
for (j = 0; j < dataUriLength; j++) {
|
||||
arr8[(layerStartIndex + 5 + layerNameLength) * 2 + j] = dataUri.charCodeAt(j);
|
||||
}
|
||||
|
||||
layerStartIndex += Math.ceil(5 + layerNameLength + (dataUriLength / 2));
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
};
|
||||
})();
|
||||
19
dev/js/utils/serialization/backward/Deserializer_v0.js
Normal file
19
dev/js/utils/serialization/backward/Deserializer_v0.js
Normal file
@@ -0,0 +1,19 @@
|
||||
(function () {
|
||||
var ns = $.namespace('pskl.utils.serialization.backward');
|
||||
|
||||
ns.Deserializer_v0 = function (data, callback) {
|
||||
this.data_ = data;
|
||||
this.callback_ = callback;
|
||||
};
|
||||
|
||||
ns.Deserializer_v0.prototype.deserialize = function () {
|
||||
var pixelGrids = this.data_;
|
||||
var frames = pixelGrids.map(function (grid) {
|
||||
return pskl.model.Frame.fromPixelGrid(grid);
|
||||
});
|
||||
var descriptor = new pskl.model.piskel.Descriptor('Deserialized piskel', '');
|
||||
var layer = pskl.model.Layer.fromFrames('Layer 1', frames);
|
||||
|
||||
this.callback_(pskl.model.Piskel.fromLayers([layer], Constants.DEFAULT.FPS, descriptor));
|
||||
};
|
||||
})();
|
||||
37
dev/js/utils/serialization/backward/Deserializer_v1.js
Normal file
37
dev/js/utils/serialization/backward/Deserializer_v1.js
Normal file
@@ -0,0 +1,37 @@
|
||||
(function () {
|
||||
var ns = $.namespace('pskl.utils.serialization.backward');
|
||||
|
||||
ns.Deserializer_v1 = function (data, callback) {
|
||||
this.callback_ = callback;
|
||||
this.data_ = data;
|
||||
};
|
||||
|
||||
ns.Deserializer_v1.prototype.deserialize = function () {
|
||||
var piskelData = this.data_.piskel;
|
||||
var descriptor = new pskl.model.piskel.Descriptor('Deserialized piskel', '');
|
||||
var piskel = new pskl.model.Piskel(piskelData.width, piskelData.height, Constants.DEFAULT.FPS, descriptor);
|
||||
|
||||
piskelData.layers.forEach(function (serializedLayer) {
|
||||
var layer = this.deserializeLayer(serializedLayer);
|
||||
piskel.addLayer(layer);
|
||||
}.bind(this));
|
||||
|
||||
this.callback_(piskel);
|
||||
};
|
||||
|
||||
ns.Deserializer_v1.prototype.deserializeLayer = function (layerString) {
|
||||
var layerData = JSON.parse(layerString);
|
||||
var layer = new pskl.model.Layer(layerData.name);
|
||||
layerData.frames.forEach(function (serializedFrame) {
|
||||
var frame = this.deserializeFrame(serializedFrame);
|
||||
layer.addFrame(frame);
|
||||
}.bind(this));
|
||||
|
||||
return layer;
|
||||
};
|
||||
|
||||
ns.Deserializer_v1.prototype.deserializeFrame = function (frameString) {
|
||||
var framePixelGrid = JSON.parse(frameString);
|
||||
return pskl.model.Frame.fromPixelGrid(framePixelGrid);
|
||||
};
|
||||
})();
|
||||
Reference in New Issue
Block a user