From 0cecdc74eb31ec03442e54615b95d8ff6fe71581 Mon Sep 17 00:00:00 2001 From: jdescottes Date: Thu, 17 Apr 2014 01:27:49 +0200 Subject: [PATCH] Temp commit --- src/js/Events.js | 1 + src/js/controller/DrawingController.js | 1 + src/js/controller/LayersListController.js | 12 +- src/js/controller/PiskelController.js | 121 ++++++++++++++--- src/js/drawingtools/BaseTool.js | 19 ++- src/js/drawingtools/Move.js | 14 +- src/js/drawingtools/PaintBucket.js | 15 ++- src/js/drawingtools/ShapeTool.js | 22 +++ src/js/drawingtools/SimplePen.js | 30 ++++- src/js/drawingtools/Stroke.js | 33 +++-- src/js/drawingtools/VerticalMirrorPen.js | 4 +- .../drawingtools/selectiontools/BaseSelect.js | 2 - src/js/model/Frame.js | 11 +- src/js/rendering/frame/CachedFrameRenderer.js | 2 +- src/js/selection/BaseSelection.js | 9 +- src/js/selection/SelectionManager.js | 43 ++++-- src/js/service/HistoryService.js | 127 ++++++++++++++++-- src/js/utils/serialization/Deserializer.js | 46 ++++--- src/js/utils/serialization/Serializer.js | 23 ++-- 19 files changed, 423 insertions(+), 112 deletions(-) diff --git a/src/js/Events.js b/src/js/Events.js index 07d75b09..97d84028 100644 --- a/src/js/Events.js +++ b/src/js/Events.js @@ -32,6 +32,7 @@ var Events = { * Number of frames, content of frames, color used for the palette may have changed. */ PISKEL_RESET: "PISKEL_RESET", + PISKEL_SAVE_STATE: "PISKEL_SAVE_STATE", PISKEL_SAVED: "PISKEL_SAVED", diff --git a/src/js/controller/DrawingController.js b/src/js/controller/DrawingController.js index 1cb9dd7f..cdce5a08 100644 --- a/src/js/controller/DrawingController.js +++ b/src/js/controller/DrawingController.js @@ -175,6 +175,7 @@ event ); } + $.publish(Events.CURSOR_MOVED, [coords.x, coords.y]); this.previousMousemoveTime = currentTime; } }; diff --git a/src/js/controller/LayersListController.js b/src/js/controller/LayersListController.js index b3e2a76f..27246926 100644 --- a/src/js/controller/LayersListController.js +++ b/src/js/controller/LayersListController.js @@ -44,15 +44,15 @@ this.piskelController.setCurrentLayerIndex(parseInt(index, 10)); } else if (el.classList.contains('edit-icon')) { index = el.parentNode.dataset.layerIndex; - var layer = this.piskelController.getLayerByIndex(parseInt(index, 10)); - this.renameLayer_(layer); + this.renameLayerAt_(index); } }; - ns.LayersListController.prototype.renameLayer_ = function (layer) { - var newName = window.prompt("Please enter the layer name", layer.getName()); - if (newName) { - layer.setName(newName); + ns.LayersListController.prototype.renameLayerAt_ = function (index) { + var layer = this.piskelController.getLayerAt(index); + var name = window.prompt("Please enter the layer name", layer.getName()); + if (name) { + this.piskelController.renameLayerAt(index, name); this.renderLayerList_(); } }; diff --git a/src/js/controller/PiskelController.js b/src/js/controller/PiskelController.js index 776f220a..b1d56273 100644 --- a/src/js/controller/PiskelController.js +++ b/src/js/controller/PiskelController.js @@ -4,6 +4,7 @@ ns.PiskelController = function (piskel) { if (piskel) { this.setPiskel(piskel); + this.silenced = false; } else { throw 'A piskel instance is mandatory for instanciating PiskelController'; } @@ -16,8 +17,13 @@ this.layerIdCounter = 1; - $.publish(Events.FRAME_SIZE_CHANGED); - $.publish(Events.PISKEL_RESET); + if (!this.silenced) { + $.publish(Events.FRAME_SIZE_CHANGED); + $.publish(Events.PISKEL_RESET); + $.publish(Events.PISKEL_SAVE_STATE, { + type : 'FULL' + }); + } }; ns.PiskelController.prototype.init = function () { @@ -51,7 +57,11 @@ }; ns.PiskelController.prototype.getCurrentLayer = function () { - return this.piskel.getLayerAt(this.currentLayerIndex); + return this.getLayerAt(this.currentLayerIndex); + }; + + ns.PiskelController.prototype.getLayerAt = function (index) { + return this.piskel.getLayerAt(index); }; ns.PiskelController.prototype.getCurrentFrame = function () { @@ -79,12 +89,18 @@ }; ns.PiskelController.prototype.addFrameAt = function (index) { - var layers = this.getLayers(); - layers.forEach(function (l) { + this.getLayers().forEach(function (l) { l.addFrameAt(this.createEmptyFrame_(), index); }.bind(this)); - $.publish(Events.PISKEL_RESET); + if (!this.silenced) { + $.publish(Events.PISKEL_SAVE_STATE, { + type : 'ADD_FRAME', + index : index + }); + + $.publish(Events.PISKEL_RESET); + } }; ns.PiskelController.prototype.createEmptyFrame_ = function () { @@ -93,8 +109,7 @@ }; ns.PiskelController.prototype.removeFrameAt = function (index) { - var layers = this.getLayers(); - layers.forEach(function (l) { + this.getLayers().forEach(function (l) { l.removeFrameAt(index); }); // Current frame index is impacted if the removed frame was before the current frame @@ -102,7 +117,13 @@ this.setCurrentFrameIndex(this.currentFrameIndex - 1); } - $.publish(Events.PISKEL_RESET); + if (!this.silenced) { + $.publish(Events.PISKEL_SAVE_STATE, { + type : 'DELETE_FRAME', + index : index + }); + $.publish(Events.PISKEL_RESET); + } }; ns.PiskelController.prototype.duplicateCurrentFrame = function () { @@ -110,19 +131,34 @@ }; ns.PiskelController.prototype.duplicateFrameAt = function (index) { - var layers = this.getLayers(); - layers.forEach(function (l) { + this.getLayers().forEach(function (l) { l.duplicateFrameAt(index); }); - $.publish(Events.PISKEL_RESET); + if (!this.silenced) { + $.publish(Events.PISKEL_SAVE_STATE, { + type : 'DUPLICATE_FRAME', + index : index + }); + + $.publish(Events.PISKEL_RESET); + } }; ns.PiskelController.prototype.moveFrame = function (fromIndex, toIndex) { - var layers = this.getLayers(); - layers.forEach(function (l) { + this.getLayers().forEach(function (l) { l.moveFrame(fromIndex, toIndex); }); + + if (!this.silenced) { + $.publish(Events.PISKEL_SAVE_STATE, { + type : 'MOVE_FRAME', + from : fromIndex, + to : toIndex + }); + + $.publish(Events.PISKEL_RESET); + } }; ns.PiskelController.prototype.getFrameCount = function () { @@ -132,7 +168,9 @@ ns.PiskelController.prototype.setCurrentFrameIndex = function (index) { this.currentFrameIndex = index; - $.publish(Events.PISKEL_RESET); + if (!this.silenced) { + $.publish(Events.PISKEL_RESET); + } }; ns.PiskelController.prototype.selectNextFrame = function () { @@ -151,7 +189,9 @@ ns.PiskelController.prototype.setCurrentLayerIndex = function (index) { this.currentLayerIndex = index; - $.publish(Events.PISKEL_RESET); + if (!this.silenced) { + $.publish(Events.PISKEL_RESET); + } }; ns.PiskelController.prototype.selectLayer = function (layer) { @@ -161,6 +201,20 @@ } }; + ns.PiskelController.prototype.renameLayerAt = function (index, name) { + var layer = this.getLayerByIndex(index); + if (layer) { + layer.setName(name); + if (!this.silenced) { + $.publish(Events.PISKEL_SAVE_STATE, { + type : 'RENAME_LAYER', + index : index, + name : name + }); + } + } + }; + ns.PiskelController.prototype.getLayerByIndex = function (index) { var layers = this.getLayers(); if (layers[index]) { @@ -190,6 +244,13 @@ } this.piskel.addLayer(layer); this.setCurrentLayerIndex(this.piskel.getLayers().length - 1); + + if (!this.silenced) { + $.publish(Events.PISKEL_SAVE_STATE, { + type : 'CREATE_LAYER', + name : name + }); + } } else { throw 'Layer name should be unique'; } @@ -203,12 +264,22 @@ var layer = this.getCurrentLayer(); this.piskel.moveLayerUp(layer); this.selectLayer(layer); + if (!this.silenced) { + $.publish(Events.PISKEL_SAVE_STATE, { + type : 'LAYER_UP' + }); + } }; ns.PiskelController.prototype.moveLayerDown = function () { var layer = this.getCurrentLayer(); this.piskel.moveLayerDown(layer); this.selectLayer(layer); + if (!this.silenced) { + $.publish(Events.PISKEL_SAVE_STATE, { + type : 'LAYER_DOWN' + }); + } }; ns.PiskelController.prototype.removeCurrentLayer = function () { @@ -216,14 +287,28 @@ var layer = this.getCurrentLayer(); this.piskel.removeLayer(layer); this.setCurrentLayerIndex(0); + + if (!this.silenced) { + $.publish(Events.PISKEL_SAVE_STATE, { + type : 'REMOVE_LAYER' + }); + } } }; - ns.PiskelController.prototype.serialize = function () { - return pskl.utils.Serializer.serializePiskel(this.piskel); + ns.PiskelController.prototype.serialize = function (compressed) { + return pskl.utils.Serializer.serializePiskel(this.piskel, compressed); }; ns.PiskelController.prototype.load = function (data) { this.deserialize(JSON.stringify(data)); }; + + ns.PiskelController.prototype.silence = function () { + this.silenced = true; + }; + + ns.PiskelController.prototype.voice = function () { + this.silenced = false; + }; })(); \ No newline at end of file diff --git a/src/js/drawingtools/BaseTool.js b/src/js/drawingtools/BaseTool.js index c9cca264..9ab770da 100644 --- a/src/js/drawingtools/BaseTool.js +++ b/src/js/drawingtools/BaseTool.js @@ -6,16 +6,17 @@ (function() { var ns = $.namespace("pskl.drawingtools"); - ns.BaseTool = function() {}; + ns.BaseTool = function() { + this.toolId = "tool-base"; + }; ns.BaseTool.prototype.applyToolAt = function(col, row, color, frame, overlay, event) {}; - ns.BaseTool.prototype.moveToolAt = function(col, row, color, frame, overlay, event) { - $.publish(Events.CURSOR_MOVED, [col, row]); - }; + ns.BaseTool.prototype.moveToolAt = function(col, row, color, frame, overlay, event) {}; + + ns.BaseTool.prototype.replay = Constants.ABSTRACT_FUNCTION; ns.BaseTool.prototype.moveUnactiveToolAt = function(col, row, color, frame, overlay, event) { - $.publish(Events.CURSOR_MOVED, [col, row]); if (overlay.containsPixel(col, row)) { if (!isNaN(this.highlightedPixelCol) && @@ -49,6 +50,14 @@ } }; + ns.BaseTool.prototype.raiseSaveStateEvent = function (args) { + var toolInfo = { + toolId : this.toolId, + args : args + }; + $.publish(Events.PISKEL_SAVE_STATE, toolInfo); + }; + ns.BaseTool.prototype.releaseToolAt = function(col, row, color, frame, overlay, event) {}; diff --git a/src/js/drawingtools/Move.js b/src/js/drawingtools/Move.js index 30baac06..73e32d75 100644 --- a/src/js/drawingtools/Move.js +++ b/src/js/drawingtools/Move.js @@ -29,7 +29,6 @@ }; ns.Move.prototype.moveToolAt = function(col, row, color, frame, overlay, event) { - $.publish(Events.CURSOR_MOVED, [col, row]); var colDiff = col - this.startCol, rowDiff = row - this.startRow; this.shiftFrame(colDiff, rowDiff, frame, this.frameClone); }; @@ -53,5 +52,18 @@ */ ns.Move.prototype.releaseToolAt = function(col, row, color, frame, overlay, event) { this.moveToolAt(col, row, color, frame, overlay); + + $.publish(Events.PISKEL_SAVE_STATE, { + type : 'TOOL', + tool : this, + replay : { + colDiff : col - this.startCol, + rowDiff : row - this.startRow + } + }); + }; + + ns.Move.prototype.replay = function(frame, replayData) { + this.shiftFrame(replayData.colDiff, replayData.rowDiff, frame, frame.clone()); }; })(); diff --git a/src/js/drawingtools/PaintBucket.js b/src/js/drawingtools/PaintBucket.js index 34fc4714..f8cbabea 100644 --- a/src/js/drawingtools/PaintBucket.js +++ b/src/js/drawingtools/PaintBucket.js @@ -17,8 +17,21 @@ * @override */ ns.PaintBucket.prototype.applyToolAt = function(col, row, color, frame, overlay, event) { - pskl.PixelUtils.paintSimilarConnectedPixelsFromFrame(frame, col, row, color); + + $.publish(Events.PISKEL_SAVE_STATE, { + type : 'TOOL', + tool : this, + replay : { + col : col, + row : row, + color : color + } + }); + }; + + ns.PaintBucket.prototype.replay = function (frame, replayData) { + pskl.PixelUtils.paintSimilarConnectedPixelsFromFrame(frame, replayData.col, replayData.row, replayData.color); }; })(); diff --git a/src/js/drawingtools/ShapeTool.js b/src/js/drawingtools/ShapeTool.js index d0de3a0e..a069ebd9 100644 --- a/src/js/drawingtools/ShapeTool.js +++ b/src/js/drawingtools/ShapeTool.js @@ -49,7 +49,29 @@ } var coords = this.getCoordinates_(col, row, event); this.draw_(coords.col, coords.row, color, frame); + $.publish(Events.DRAG_END, [col, row]); + + $.publish(Events.PISKEL_SAVE_STATE, { + type : 'TOOL', + tool : this, + replay : { + col : col, + row : row, + startCol : this.startCol, + startRow : this.startRow, + color : color + } + }); + }; + + /** + * @override + */ + ns.ShapeTool.prototype.replay = function(frame, replayData) { + this.startCol = replayData.startCol; + this.startRow = replayData.startRow; + this.draw_(replayData.col, replayData.row, replayData.color, frame); }; /** diff --git a/src/js/drawingtools/SimplePen.js b/src/js/drawingtools/SimplePen.js index 64349e7a..fa2f9da5 100644 --- a/src/js/drawingtools/SimplePen.js +++ b/src/js/drawingtools/SimplePen.js @@ -13,6 +13,7 @@ this.previousCol = null; this.previousRow = null; + this.pixels = []; }; pskl.utils.inherit(ns.SimplePen, ns.BaseTool); @@ -21,18 +22,19 @@ * @override */ ns.SimplePen.prototype.applyToolAt = function(col, row, color, frame, overlay, event) { - if (frame.containsPixel(col, row)) { - frame.setPixel(col, row, color); - } + frame.setPixel(col, row, color); this.previousCol = col; this.previousRow = row; + this.pixels.push({ + col : col, + row : row + }); }; /** * @override */ ns.SimplePen.prototype.moveToolAt = function(col, row, color, frame, overlay, event) { - $.publish(Events.CURSOR_MOVED, [col, row]); if((Math.abs(col - this.previousCol) > 1) || (Math.abs(row - this.previousRow) > 1)) { // The pen movement is too fast for the mousemove frequency, there is a gap between the // current point and the previously drawn one. @@ -50,4 +52,24 @@ this.previousCol = col; this.previousRow = row; }; + + + ns.SimplePen.prototype.releaseToolAt = function(col, row, color, frame, overlay, event) { + $.publish(Events.PISKEL_SAVE_STATE, { + type : 'TOOL', + tool : this, + replay : { + pixels : this.pixels.slice(0), + color : color + } + }); + this.pixels = []; + }; + + ns.SimplePen.prototype.replay = function (frame, replayData) { + var pixels = replayData.pixels; + pixels.forEach(function (pixel) { + frame.setPixel(pixel.col, pixel.row, replayData.color); + }); + }; })(); diff --git a/src/js/drawingtools/Stroke.js b/src/js/drawingtools/Stroke.js index 0152806d..c684c3f3 100644 --- a/src/js/drawingtools/Stroke.js +++ b/src/js/drawingtools/Stroke.js @@ -37,8 +37,6 @@ }; ns.Stroke.prototype.moveToolAt = function(col, row, color, frame, overlay, event) { - $.publish(Events.CURSOR_MOVED, [col, row]); - overlay.clear(); // When the user moussemove (before releasing), we dynamically compute the @@ -65,18 +63,29 @@ * @override */ ns.Stroke.prototype.releaseToolAt = function(col, row, color, frame, overlay, event) { - // If the stroke tool is released outside of the canvas, we cancel the stroke: - // TODO: Mutualize this check in common method - if(frame.containsPixel(col, row)) { - // The user released the tool to draw a line. We will compute the pixel coordinate, impact - // the model and draw them in the drawing canvas (not the fake overlay anymore) - var strokePoints = this.getLinePixels_(this.startCol, col, this.startRow, row); - for(var i = 0; i< strokePoints.length; i++) { - // Change model: - frame.setPixel(strokePoints[i].col, strokePoints[i].row, color); - } + // The user released the tool to draw a line. We will compute the pixel coordinate, impact + // the model and draw them in the drawing canvas (not the fake overlay anymore) + var strokePoints = this.getLinePixels_(this.startCol, col, this.startRow, row); + for(var i = 0; i< strokePoints.length; i++) { + // Change model: + frame.setPixel(strokePoints[i].col, strokePoints[i].row, color); } // For now, we are done with the stroke tool and don't need an overlay anymore: overlay.clear(); + + $.publish(Events.PISKEL_SAVE_STATE, { + type : 'TOOL', + tool : this, + replay : { + pixels : strokePoints, + color : color + } + }); + }; + + ns.Stroke.prototype.replay = function(frame, replayData) { + replayData.pixels.forEach(function (pixel) { + frame.setPixel(pixel.col, pixel.row, replayData.color); + }); }; })(); diff --git a/src/js/drawingtools/VerticalMirrorPen.js b/src/js/drawingtools/VerticalMirrorPen.js index 435805e8..44841ea8 100644 --- a/src/js/drawingtools/VerticalMirrorPen.js +++ b/src/js/drawingtools/VerticalMirrorPen.js @@ -2,12 +2,12 @@ var ns = $.namespace("pskl.drawingtools"); ns.VerticalMirrorPen = function() { + this.superclass.constructor.call(this); + this.toolId = "tool-vertical-mirror-pen"; this.helpText = "vertical mirror pen tool"; this.swap = null; - this.mirroredPreviousCol = null; - this.mirroredPreviousRow = null; }; pskl.utils.inherit(ns.VerticalMirrorPen, ns.SimplePen); diff --git a/src/js/drawingtools/selectiontools/BaseSelect.js b/src/js/drawingtools/selectiontools/BaseSelect.js index 5892a9bb..78cae0a3 100644 --- a/src/js/drawingtools/selectiontools/BaseSelect.js +++ b/src/js/drawingtools/selectiontools/BaseSelect.js @@ -48,7 +48,6 @@ * @override */ ns.BaseSelect.prototype.moveToolAt = function(col, row, color, frame, overlay, event) { - $.publish(Events.CURSOR_MOVED, [col, row]); if(this.mode == "select") { this.onSelect_(col, row, color, frame, overlay); } @@ -75,7 +74,6 @@ * @override */ ns.BaseSelect.prototype.moveUnactiveToolAt = function(col, row, color, frame, overlay, event) { - $.publish(Events.CURSOR_MOVED, [col, row]); if (overlay.containsPixel(col, row)) { if(overlay.getPixel(col, row) != Constants.SELECTION_TRANSPARENT_COLOR) { // We're hovering the selection, show the move tool: diff --git a/src/js/model/Frame.js b/src/js/model/Frame.js index 98b4d75d..50c4e6c8 100644 --- a/src/js/model/Frame.js +++ b/src/js/model/Frame.js @@ -1,11 +1,12 @@ (function () { var ns = $.namespace("pskl.model"); - + var __idCounter = 0; ns.Frame = function (width, height) { if (width && height) { this.width = width; this.height = height; - + this.id = __idCounter++; + this.version = 0; this.pixels = ns.Frame.createEmptyPixelGrid_(width, height); this.previousStates = [this.getPixels()]; this.stateIndex = 0; @@ -59,6 +60,7 @@ */ ns.Frame.prototype.setPixels = function (pixels) { this.pixels = this.clonePixels_(pixels); + this.version++; }; ns.Frame.prototype.clear = function () { @@ -78,13 +80,14 @@ return clonedPixels; }; - ns.Frame.prototype.serialize = function () { - return JSON.stringify(this.pixels); + ns.Frame.prototype.getHash = function () { + return [this.id, this.version].join('-'); }; ns.Frame.prototype.setPixel = function (col, row, color) { if (this.containsPixel(col, row)) { this.pixels[col][row] = color; + this.version++; } }; diff --git a/src/js/rendering/frame/CachedFrameRenderer.js b/src/js/rendering/frame/CachedFrameRenderer.js index 6bdd69ea..e8a8f618 100644 --- a/src/js/rendering/frame/CachedFrameRenderer.js +++ b/src/js/rendering/frame/CachedFrameRenderer.js @@ -36,7 +36,7 @@ this.getGridWidth(), offset.x, offset.y, size.width, size.height, - frame.serialize() + frame.getHash() ].join('-'); if (this.serializedFrame != serializedFrame) { this.serializedFrame = serializedFrame; diff --git a/src/js/selection/BaseSelection.js b/src/js/selection/BaseSelection.js index 74513b58..baf28ca1 100644 --- a/src/js/selection/BaseSelection.js +++ b/src/js/selection/BaseSelection.js @@ -23,12 +23,9 @@ }; ns.BaseSelection.prototype.fillSelectionFromFrame = function (targetFrame) { - var pixelWithCopiedColor; - for(var i=0, l=this.pixels.length; i 50 && this.currentIndex > 0) { + this.currentIndex = this.currentIndex - 1; + this.loadState(this.currentIndex); + this.lastEvent = Date.now(); + } }; ns.HistoryService.prototype.redo = function () { - this.piskelController.getCurrentFrame().loadNextState(); - $.unsubscribe(Events.PISKEL_RESET, this.saveState__b); + var now = Date.now(); + if ((Date.now() - this.lastEvent) > 50 && this.currentIndex < this.stateQueue.length - 1) { + this.currentIndex = this.currentIndex + 1; + this.loadState(this.currentIndex); + this.lastEvent = Date.now(); + } + }; + + ns.HistoryService.prototype.loadState = function (index) { + $.unsubscribe(Events.PISKEL_SAVE_STATE, this.saveState__b); + this.piskelController.silence(); + + // get nearest snaphot index + var snapshotIndex = -1; + for (var i = index ; i >= 0 ; i--) { + if (this.stateQueue[i].piskel) { + snapshotIndex = i; + break; + } + } + + if (snapshotIndex === -1) { + throw 'Could not find previous SNAPSHOT saved in history stateQueue'; + } + + var serializedPiskel = this.stateQueue[snapshotIndex].piskel; + var targetState = this.stateQueue[index]; + + if (typeof serializedPiskel === "string") { + this.stateQueue[snapshotIndex].piskel = JSON.parse(serializedPiskel); + serializedPiskel = this.stateQueue[snapshotIndex].piskel; + } + + this.loadPiskel(serializedPiskel, this.onPiskelLoadedCallback.bind(this, index, snapshotIndex)); + }; + + ns.HistoryService.prototype.onPiskelLoadedCallback = function (index, snapshotIndex, piskel) { + for (var i = snapshotIndex + 1 ; i <= index ; i++) { + var state = this.stateQueue[i]; + this.piskelController.setCurrentFrameIndex(state.frameIndex); + this.piskelController.setCurrentLayerIndex(state.layerIndex); + this.replayState(state); + } + + this.piskelController.voice(); + $.subscribe(Events.PISKEL_SAVE_STATE, this.saveState__b); $.publish(Events.PISKEL_RESET); - $.subscribe(Events.PISKEL_RESET, this.saveState__b); + }; + + ns.HistoryService.prototype.loadPiskel = function (piskel, callback) { + var descriptor = this.piskelController.piskel.getDescriptor(); + pskl.utils.serialization.Deserializer.deserialize(piskel, function (piskel) { + piskel.setDescriptor(descriptor); + pskl.app.piskelController.setPiskel(piskel); + callback(piskel); + }); + }; + + ns.HistoryService.prototype.replayState = function (state) { + var type = state.action.type; + if (type === 'DELETE_FRAME') { + this.piskelController.removeFrameAt(state.action.index); + } else if (type === 'ADD_FRAME') { + this.piskelController.addFrameAt(state.action.index); + } else if (type === 'DUPLICATE_FRAME') { + this.piskelController.duplicateFrameAt(state.action.index); + } else if (type === 'CREATE_LAYER') { + this.piskelController.createLayer(state.action.name); + } else if (type === 'REMOVE_LAYER') { + this.piskelController.removeCurrentLayer(); + } else if (type === 'LAYER_UP') { + this.piskelController.moveLayerUp(); + } else if (type === 'LAYER_DOWN') { + this.piskelController.moveLayerUp(); + } else if (type === 'RENAME_LAYER') { + this.piskelController.renameLayerAt(state.action.index, state.action.name); + } else if (type === 'MOVE_FRAME') { + this.piskelController.moveFrame(state.action.from, state.action.to); + } else if (type === 'CREATE_LAYER') { + this.piskelController.createLayer(); + } else if (type === 'TOOL') { + var action = state.action; + var layer = this.piskelController.getLayerAt(state.layerIndex); + var frame = layer.getFrameAt(state.frameIndex); + action.tool.replay(frame, action.replay); + } }; })(); \ No newline at end of file diff --git a/src/js/utils/serialization/Deserializer.js b/src/js/utils/serialization/Deserializer.js index 5c35322a..85ff70e5 100644 --- a/src/js/utils/serialization/Deserializer.js +++ b/src/js/utils/serialization/Deserializer.js @@ -31,37 +31,49 @@ this.layersToLoad_ = piskelData.layers.length; piskelData.layers.forEach(function (serializedLayer) { - var layer = this.deserializeLayer(serializedLayer); - this.piskel_.addLayer(layer); + this.deserializeLayer(serializedLayer); }.bind(this)); }; ns.Deserializer.prototype.deserializeLayer = function (layerString) { - var layerData = JSON.parse(layerString); + var layerData = typeof layerString === "string" ? JSON.parse(layerString) : layerString; var layer = new pskl.model.Layer(layerData.name); - // 1 - create an image to load the base64PNG representing the layer - var base64PNG = layerData.base64PNG; - var image = new Image(); + var isCompressedLayer = !!layerData.base64PNG; - // 2 - attach the onload callback that will be triggered asynchronously - image.onload = function () { - // 5 - extract the frames from the loaded image - var frames = pskl.utils.LayerUtils.createFromImage(image, layerData.frameCount); + if (isCompressedLayer) { + // 1 - create an image to load the base64PNG representing the layer + var base64PNG = layerData.base64PNG; + var image = new Image(); - // 6 - add each image to the layer - frames.forEach(layer.addFrame.bind(layer)); + // 2 - attach the onload callback that will be triggered asynchronously + image.onload = function () { + // 5 - extract the frames from the loaded image + var frames = pskl.utils.LayerUtils.createFromImage(image, layerData.frameCount); + // 6 - add each image to the layer + this.addFramesToLayer(frames, layer); + }.bind(this); - this.onLayerLoaded_(); - }.bind(this); - - // 3 - set the source of the image - image.src = base64PNG; + // 3 - set the source of the image + image.src = base64PNG; + } else { + var frames = layerData.grids.map(function (grid) { + return pskl.model.Frame.fromPixelGrid(grid); + }); + this.addFramesToLayer(frames, layer); + } // 4 - return a pointer to the new layer instance return layer; }; + ns.Deserializer.prototype.addFramesToLayer = function (frames, layer) { + frames.forEach(layer.addFrame.bind(layer)); + + this.piskel_.addLayer(layer); + this.onLayerLoaded_(); + }; + ns.Deserializer.prototype.onLayerLoaded_ = function () { this.layersToLoad_ = this.layersToLoad_ - 1; if (this.layersToLoad_ === 0) { diff --git a/src/js/utils/serialization/Serializer.js b/src/js/utils/serialization/Serializer.js index 934de4b4..5dff406d 100644 --- a/src/js/utils/serialization/Serializer.js +++ b/src/js/utils/serialization/Serializer.js @@ -2,9 +2,9 @@ var ns = $.namespace('pskl.utils'); ns.Serializer = { - serializePiskel : function (piskel) { + serializePiskel : function (piskel, compressed) { var serializedLayers = piskel.getLayers().map(function (l) { - return pskl.utils.Serializer.serializeLayer(l); + return pskl.utils.Serializer.serializeLayer(l, compressed); }); return JSON.stringify({ modelVersion : Constants.MODEL_VERSION, @@ -16,16 +16,23 @@ }); }, - serializeLayer : function (layer) { + serializeLayer : function (layer, compressed) { + if (compressed !== false) { + compressed = true; + } var frames = layer.getFrames(); var renderer = new pskl.rendering.FramesheetRenderer(frames); - var base64PNG = renderer.renderAsCanvas().toDataURL(); - - return JSON.stringify({ + var layerToSerialize = { name : layer.getName(), - base64PNG : base64PNG, frameCount : frames.length - }); + }; + if (compressed) { + layerToSerialize.base64PNG = renderer.renderAsCanvas().toDataURL(); + return JSON.stringify(layerToSerialize); + } else { + layerToSerialize.grids = frames.map(function (f) {return f.pixels;}); + return layerToSerialize; + } } }; })();