Merge from master

This commit is contained in:
jdescottes 2013-11-26 21:40:43 +01:00
commit 5bbbdf64d9
35 changed files with 842 additions and 378 deletions

77
css/cheatsheet.css Normal file
View file

@ -0,0 +1,77 @@
#cheatsheet-wrapper {
position: absolute;
z-index: 10000;
top: 0;
right: 0;
bottom: 0;
left: 0;
padding: 50px;
box-sizing: border-box;
color: white;
}
.cheatsheet-container {
width: 100%;
height: 100%;
box-sizing: border-box;
padding: 20px 10%;
border-radius: 3px;
background: rgba(0,0,0,0.9);
overflow: auto;
}
.cheatsheet-container h3 {
font-size:24px;
margin-top: 0;
}
.cheatsheet-section {
float: left;
width : 50%;
}
.cheatsheet-shortcut {
overflow: hidden;
margin: 10px 0;
}
.cheatsheet-icon.tool-icon {
float: left;
display: inline-block;
height: 30px;
width: 30px;
margin: 0 20px 0 0;
background-size: 20px 20px;
background-position: 5px 5px;
}
.cheatsheet-description {
font-family:Courier;
color: white;
font-size : 13px;
margin-left: 20px;
line-height : 30px;
}
.cheatsheet-key {
display : inline-block;
height: 30px;
line-height: 30px;
padding: 0 10px;
border : 1px solid gold;
border-radius: 2px;
box-sizing: border-box;
text-align: center;
font-family:Courier;
font-weight: bold;
font-size : 18px;
color: gold;
}

View file

@ -12,6 +12,7 @@
<link rel="stylesheet" type="text/css" href="css/forms.css">
<link rel="stylesheet" type="text/css" href="css/settings.css">
<link rel="stylesheet" type="text/css" href="css/tools.css">
<link rel="stylesheet" type="text/css" href="css/cheatsheet.css">
<link rel="stylesheet" type="text/css" href="css/spectrum/spectrum.css"/>
<link rel="stylesheet" type="text/css" href="css/bootstrap/bootstrap.css">
<link rel="stylesheet" type="text/css" href="css/bootstrap/bootstrap-tooltip-custom.css">
@ -55,9 +56,8 @@
</div>
</div>
</div>
</div>
<iframe src="templates/cheatsheet.html" onload="iframeloader.onLoad(event)" data-iframe-loader="display"></iframe>
<script type="text/javascript" src="piskel-boot.js"></script>
</body>
</html>

View file

@ -6,7 +6,7 @@ var Constants = {
FPS : 12
},
MODEL_VERSION : 1,
MODEL_VERSION : 2,
MAX_HEIGHT : 1024,
MAX_WIDTH : 1024,

View file

@ -3,10 +3,8 @@ var Events = {
TOOL_SELECTED : "TOOL_SELECTED",
TOOL_RELEASED : "TOOL_RELEASED",
PRIMARY_COLOR_SELECTED: "PRIMARY_COLOR_SELECTED",
PRIMARY_COLOR_UPDATED: "PRIMARY_COLOR_UPDATED",
SECONDARY_COLOR_SELECTED: "SECONDARY_COLOR_SELECTED",
SECONDARY_COLOR_UPDATED: "SECONDARY_COLOR_UPDATED",
SELECT_PRIMARY_COLOR: "SELECT_PRIMARY_COLOR",
SELECT_SECONDARY_COLOR: "SELECT_SECONDARY_COLOR",
/**
* When this event is emitted, a request is sent to the localstorage
@ -15,22 +13,6 @@ var Events = {
*/
LOCALSTORAGE_REQUEST: "LOCALSTORAGE_REQUEST",
CANVAS_RIGHT_CLICKED: "CANVAS_RIGHT_CLICKED",
/**
* Event to request a refresh of the display.
* A bit overkill but, it's just workaround in our current drawing system.
* TODO: Remove or rework when redraw system is refactored.
*/
REFRESH: "REFRESH",
/**
* Temporary event to bind the redraw of right preview film to the canvas.
* This redraw should be driven by model updates.
* TODO(vincz): Remove.
*/
REDRAW_PREVIEWFILM: "REDRAW_PREVIEWFILM",
/**
* Fired each time a user setting change.
* The payload will be:
@ -39,7 +21,6 @@ var Events = {
*/
USER_SETTINGS_CHANGED: "USER_SETTINGS_CHANGED",
/* Listened to by SettingsController */
CLOSE_SETTINGS_DRAWER : "CLOSE_SETTINGS_DRAWER",
/**
@ -50,8 +31,6 @@ var Events = {
FRAME_SIZE_CHANGED : "FRAME_SIZE_CHANGED",
CURRENT_FRAME_SET: "CURRENT_FRAME_SET",
SELECTION_CREATED: "SELECTION_CREATED",
SELECTION_MOVE_REQUEST: "SELECTION_MOVE_REQUEST",
SELECTION_DISMISSED: "SELECTION_DISMISSED",
@ -59,9 +38,6 @@ var Events = {
SHOW_NOTIFICATION: "SHOW_NOTIFICATION",
HIDE_NOTIFICATION: "HIDE_NOTIFICATION",
UNDO: "UNDO",
REDO: "REDO",
CUT: "CUT",
COPY: "COPY",
PASTE: "PASTE"
// Events triggered by keyboard
SELECT_TOOL : "SELECT_TOOL"
};

View file

@ -10,6 +10,9 @@
ns.app = {
init : function () {
this.shortcutService = new pskl.service.keyboard.ShortcutService();
this.shortcutService.init();
var size = this.readSizeFromURL_();
var piskel = new pskl.model.Piskel(size.width, size.height);
@ -20,8 +23,12 @@
piskel.addLayer(layer);
this.piskelController = new pskl.controller.PiskelController(piskel);
this.piskelController.init();
this.drawingController = new pskl.controller.DrawingController(this.piskelController, $('#drawing-canvas-container'));
this.paletteController = new pskl.controller.PaletteController();
this.paletteController.init();
this.drawingController = new pskl.controller.DrawingController(this.piskelController, this.paletteController, $('#drawing-canvas-container'));
this.drawingController.init();
this.animationController = new pskl.controller.AnimatedPreviewController(this.piskelController, $('#preview-canvas-container'));
@ -39,15 +46,15 @@
this.settingsController = new pskl.controller.settings.SettingsController(this.piskelController);
this.settingsController.init();
this.toolController = new pskl.controller.ToolController();
this.toolController.init();
this.selectionManager = new pskl.selection.SelectionManager(this.piskelController);
this.selectionManager.init();
this.historyService = new pskl.service.HistoryService(this.piskelController);
this.historyService.init();
this.keyboardEventService = new pskl.service.KeyboardEventService();
this.keyboardEventService.init();
this.notificationController = new pskl.controller.NotificationController();
this.notificationController.init();
@ -57,11 +64,10 @@
this.imageUploadService = new pskl.service.ImageUploadService();
this.imageUploadService.init();
this.toolController = new pskl.controller.ToolController();
this.toolController.init();
this.paletteController = new pskl.controller.PaletteController();
this.paletteController.init();
this.cheatsheetService = new pskl.service.keyboard.CheatsheetService();
this.cheatsheetService.init();
var drawingLoop = new pskl.rendering.DrawingLoop();
drawingLoop.addCallback(this.render, this);
@ -96,9 +102,10 @@
finishInitAppEngine_ : function () {
if (pskl.framesheetData_ && pskl.framesheetData_.content) {
var piskel = pskl.utils.Serializer.createPiskel(pskl.framesheetData_.content);
pskl.app.piskelController.setPiskel(piskel);
pskl.app.animationController.setFPS(pskl.framesheetData_.fps);
pskl.utils.serialization.Deserializer.deserialize(pskl.framesheetData_.content, function (piskel) {
pskl.app.piskelController.setPiskel(piskel);
pskl.app.animationController.setFPS(pskl.framesheetData_.fps);
});
}
},
@ -159,10 +166,11 @@
xhr.responseType = 'text';
xhr.onload = function (e) {
var res = JSON.parse(this.responseText);
var piskel = pskl.utils.Serializer.createPiskel(res.framesheet);
pskl.app.piskelController.setPiskel(piskel);
pskl.app.animationController.setFPS(res.fps);
$.publish(Events.HIDE_NOTIFICATION);
pskl.utils.serialization.Deserializer.deserialize(res.framesheet, function (piskel) {
pskl.app.piskelController.setPiskel(piskel);
pskl.app.animationController.setFPS(res.fps);
$.publish(Events.HIDE_NOTIFICATION);
});
};
xhr.onerror = function () {
@ -242,8 +250,8 @@
},
getFramesheetAsPng : function () {
var renderer = new pskl.rendering.SpritesheetRenderer(this.piskelController);
var framesheetCanvas = renderer.render();
var renderer = new pskl.rendering.PiskelRenderer(this.piskelController);
var framesheetCanvas = renderer.renderAsCanvas();
return framesheetCanvas.toDataURL("image/png");
},

View file

@ -1,11 +1,13 @@
(function () {
var ns = $.namespace("pskl.controller");
ns.DrawingController = function (piskelController, container) {
ns.DrawingController = function (piskelController, paletteController,container) {
/**
* @public
*/
this.piskelController = piskelController;
this.paletteController = paletteController;
/**
* @public
*/
@ -41,34 +43,16 @@
this.isRightClicked = false;
this.previousMousemoveTime = 0;
this.currentToolBehavior = null;
this.primaryColor = Constants.DEFAULT_PEN_COLOR;
this.secondaryColor = Constants.TRANSPARENT_COLOR;
};
ns.DrawingController.prototype.init = function () {
this.initMouseBehavior();
$.subscribe(Events.TOOL_SELECTED, $.proxy(function(evt, toolBehavior) {
console.log("Tool selected: ", toolBehavior);
this.currentToolBehavior = toolBehavior;
this.overlayFrame.clear();
}, this));
/**
* TODO(grosbouddha): Primary/secondary color state are kept in this general controller.
* Find a better place to store that. Perhaps PaletteController?
*/
$.subscribe(Events.PRIMARY_COLOR_SELECTED, $.proxy(function(evt, color) {
console.log("Primary color selected: ", color);
this.primaryColor = color;
$.publish(Events.PRIMARY_COLOR_UPDATED, [color]);
}, this));
$.subscribe(Events.SECONDARY_COLOR_SELECTED, $.proxy(function(evt, color) {
console.log("Secondary color selected: ", color);
this.secondaryColor = color;
$.publish(Events.SECONDARY_COLOR_UPDATED, [color]);
}, this));
$(window).resize($.proxy(this.startResizeTimer_, this));
$.subscribe(Events.USER_SETTINGS_CHANGED, $.proxy(this.onUserSettingsChange_, this));
@ -127,7 +111,6 @@
if(event.button == 2) { // right click
this.isRightClicked = true;
$.publish(Events.CANVAS_RIGHT_CLICKED);
}
var coords = this.renderer.getCoordinates(event.clientX, event.clientY);
@ -249,9 +232,9 @@
*/
ns.DrawingController.prototype.getCurrentColor_ = function () {
if(this.isRightClicked) {
return this.secondaryColor;
return this.paletteController.getSecondaryColor();
} else {
return this.primaryColor;
return this.paletteController.getPrimaryColor();
}
};

View file

@ -1,7 +1,10 @@
(function () {
var ns = $.namespace("pskl.controller");
ns.PaletteController = function () {};
ns.PaletteController = function () {
this.primaryColor = Constants.DEFAULT_PEN_COLOR;
this.secondaryColor = Constants.TRANSPARENT_COLOR;
};
/**
* @public
@ -10,13 +13,11 @@
var transparentColorPalette = $(".palette-color[data-color=TRANSPARENT]");
transparentColorPalette.mouseup($.proxy(this.onPaletteColorClick_, this));
$.subscribe(Events.PRIMARY_COLOR_UPDATED, $.proxy(function(evt, color) {
this.updateColorPicker_(color, $('#color-picker'));
}, this));
$.subscribe(Events.SELECT_PRIMARY_COLOR, this.onColorSelected_.bind(this, {isPrimary:true}));
$.subscribe(Events.SELECT_SECONDARY_COLOR, this.onColorSelected_.bind(this, {isPrimary:false}));
$.subscribe(Events.SECONDARY_COLOR_UPDATED, $.proxy(function(evt, color) {
this.updateColorPicker_(color, $('#secondary-color-picker'));
}, this));
pskl.app.shortcutService.addShortcut('X', this.swapColors.bind(this));
pskl.app.shortcutService.addShortcut('D', this.resetColors.bind(this));
var spectrumCfg = {
showPalette: true,
@ -45,12 +46,53 @@
ns.PaletteController.prototype.onPickerChange_ = function(evt, isPrimary) {
var inputPicker = $(evt.target);
if(evt.data.isPrimary) {
$.publish(Events.PRIMARY_COLOR_SELECTED, [inputPicker.val()]);
this.setPrimaryColor(inputPicker.val());
} else {
$.publish(Events.SECONDARY_COLOR_SELECTED, [inputPicker.val()]);
this.setSecondaryColor(inputPicker.val());
}
};
/**
* @private
*/
ns.PaletteController.prototype.onColorSelected_ = function(args, evt, color) {
var inputPicker = $(evt.target);
if(args.isPrimary) {
this.setPrimaryColor(color);
} else {
this.setSecondaryColor(color);
}
};
ns.PaletteController.prototype.setPrimaryColor = function (color) {
this.primaryColor = color;
this.updateColorPicker_(color, $('#color-picker'));
};
ns.PaletteController.prototype.setSecondaryColor = function (color) {
this.secondaryColor = color;
this.updateColorPicker_(color, $('#secondary-color-picker'));
};
ns.PaletteController.prototype.getPrimaryColor = function () {
return this.primaryColor;
};
ns.PaletteController.prototype.getSecondaryColor = function () {
return this.secondaryColor;
};
ns.PaletteController.prototype.swapColors = function () {
var primaryColor = this.getPrimaryColor();
this.setPrimaryColor(this.getSecondaryColor());
this.setSecondaryColor(primaryColor);
};
ns.PaletteController.prototype.resetColors = function () {
this.setPrimaryColor(Constants.DEFAULT_PEN_COLOR);
this.setSecondaryColor(Constants.TRANSPARENT_COLOR);
};
/**
* @private
*/

View file

@ -20,6 +20,13 @@
$.publish(Events.FRAME_SIZE_CHANGED);
};
ns.PiskelController.prototype.init = function () {
pskl.app.shortcutService.addShortcut('up', this.selectPreviousFrame.bind(this));
pskl.app.shortcutService.addShortcut('down', this.selectNextFrame.bind(this));
pskl.app.shortcutService.addShortcut('n', this.addFrameAtCurrentIndex.bind(this));
pskl.app.shortcutService.addShortcut('shift+n', this.duplicateCurrentFrame.bind(this));
};
ns.PiskelController.prototype.getHeight = function () {
return this.piskel.getHeight();
};
@ -63,15 +70,21 @@
return !!this.getCurrentLayer().getFrameAt(index);
};
// backward from framesheet
ns.PiskelController.prototype.getFrameByIndex =
ns.PiskelController.prototype.getMergedFrameAt;
ns.PiskelController.prototype.addFrame = function () {
this.addFrameAt(this.getFrameCount());
};
ns.PiskelController.prototype.addEmptyFrame = function () {
ns.PiskelController.prototype.addFrameAtCurrentIndex = function () {
this.addFrameAt(this.currentFrameIndex + 1);
};
ns.PiskelController.prototype.addFrameAt = function (index) {
var layers = this.getLayers();
layers.forEach(function (l) {
l.addFrame(this.createEmptyFrame_());
l.addFrameAt(this.createEmptyFrame_(), index);
}.bind(this));
$.publish(Events.PISKEL_RESET);
};
ns.PiskelController.prototype.createEmptyFrame_ = function () {
@ -92,11 +105,17 @@
$.publish(Events.PISKEL_RESET);
};
ns.PiskelController.prototype.duplicateCurrentFrame = function () {
this.duplicateFrameAt(this.currentFrameIndex);
};
ns.PiskelController.prototype.duplicateFrameAt = function (index) {
var layers = this.getLayers();
layers.forEach(function (l) {
l.duplicateFrameAt(index);
});
$.publish(Events.PISKEL_RESET);
};
ns.PiskelController.prototype.moveFrame = function (fromIndex, toIndex) {
@ -116,6 +135,20 @@
$.publish(Events.PISKEL_RESET);
};
ns.PiskelController.prototype.selectNextFrame = function () {
var nextIndex = this.currentFrameIndex + 1;
if (nextIndex < this.getFrameCount()) {
this.setCurrentFrameIndex(nextIndex);
}
};
ns.PiskelController.prototype.selectPreviousFrame = function () {
var nextIndex = this.currentFrameIndex - 1;
if (nextIndex >= 0) {
this.setCurrentFrameIndex(nextIndex);
}
};
ns.PiskelController.prototype.setCurrentLayerIndex = function (index) {
this.currentLayerIndex = index;
$.publish(Events.PISKEL_RESET);

View file

@ -19,7 +19,7 @@
};
ns.PreviewFilmController.prototype.addFrame = function () {
this.piskelController.addEmptyFrame();
this.piskelController.addFrame();
this.piskelController.setCurrentFrameIndex(this.piskelController.getFrameCount() - 1);
this.updateScrollerOverflows();
};

View file

@ -1,36 +1,40 @@
(function () {
var ns = $.namespace("pskl.controller");
ns.ToolController = function () {
this.toolInstances = {
"simplePen" : new pskl.drawingtools.SimplePen(),
"verticalMirrorPen" : new pskl.drawingtools.VerticalMirrorPen(),
"eraser" : new pskl.drawingtools.Eraser(),
"paintBucket" : new pskl.drawingtools.PaintBucket(),
"stroke" : new pskl.drawingtools.Stroke(),
"rectangle" : new pskl.drawingtools.Rectangle(),
"circle" : new pskl.drawingtools.Circle(),
"move" : new pskl.drawingtools.Move(),
"rectangleSelect" : new pskl.drawingtools.RectangleSelect(),
"shapeSelect" : new pskl.drawingtools.ShapeSelect(),
"colorPicker" : new pskl.drawingtools.ColorPicker()
var toDescriptor = function (id, shortcut, instance) {
return {id:id, shortcut:shortcut, instance:instance};
};
this.currentSelectedTool = this.toolInstances.simplePen;
this.previousSelectedTool = this.toolInstances.simplePen;
this.tools = [
toDescriptor('simplePen', 'P', new pskl.drawingtools.SimplePen()),
toDescriptor('verticalMirrorPen', 'V', new pskl.drawingtools.VerticalMirrorPen()),
toDescriptor('eraser', 'E', new pskl.drawingtools.Eraser()),
toDescriptor('paintBucket', 'B', new pskl.drawingtools.PaintBucket()),
toDescriptor('stroke', 'L', new pskl.drawingtools.Stroke()),
toDescriptor('rectangle', 'R', new pskl.drawingtools.Rectangle()),
toDescriptor('circle', 'C', new pskl.drawingtools.Circle()),
toDescriptor('move', 'M', new pskl.drawingtools.Move()),
toDescriptor('rectangleSelect', 'S', new pskl.drawingtools.RectangleSelect()),
toDescriptor('shapeSelect', 'Z', new pskl.drawingtools.ShapeSelect()),
toDescriptor('colorPicker', 'O', new pskl.drawingtools.ColorPicker())
];
this.currentSelectedTool = this.tools[0];
this.previousSelectedTool = this.tools[0];
};
/**
* @public
*/
ns.ToolController.prototype.init = function() {
this.createToolMarkup_();
this.createToolsDom_();
this.addKeyboardShortcuts_();
// Initialize tool:
// Set SimplePen as default selected tool:
this.selectTool_(this.toolInstances.simplePen);
this.selectTool_(this.tools[0]);
// Activate listener on tool panel:
$("#tool-section").click($.proxy(this.onToolIconClicked_, this));
};
@ -44,18 +48,24 @@
if(previousSelectedToolClass) {
stage.removeClass(previousSelectedToolClass);
}
stage.addClass(tool.toolId);
stage.data("selected-tool-class", tool.toolId);
stage.addClass(tool.instance.toolId);
stage.data("selected-tool-class", tool.instance.toolId);
};
/**
* @private
*/
ns.ToolController.prototype.selectTool_ = function(tool) {
console.log("Selecting Tool:" , this.currentSelectedTool);
this.currentSelectedTool = tool;
this.activateToolOnStage_(this.currentSelectedTool);
$.publish(Events.TOOL_SELECTED, [tool]);
var selectedToolElement = $('#tool-section .tool-icon.selected');
var toolElement = $('[data-tool-id=' + tool.instance.toolId + ']');
selectedToolElement.removeClass('selected');
toolElement.addClass('selected');
$.publish(Events.TOOL_SELECTED, [tool.instance]);
};
/**
@ -70,18 +80,24 @@
var tool = this.getToolById_(toolId);
if (tool) {
this.selectTool_(tool);
}
}
};
// Show tool as selected:
$('#tool-section .tool-icon.selected').removeClass('selected');
clickedTool.addClass('selected');
ns.ToolController.prototype.onKeyboardShortcut_ = function(charkey) {
for (var i = 0 ; i < this.tools.length ; i++) {
var tool = this.tools[i];
if (tool.shortcut.toLowerCase() === charkey.toLowerCase()) {
this.selectTool_(tool);
}
}
};
ns.ToolController.prototype.getToolById_ = function (toolId) {
for(var key in this.toolInstances) {
if (this.toolInstances[key].toolId == toolId) {
return this.toolInstances[key];
for(var i = 0 ; i < this.tools.length ; i++) {
var tool = this.tools[i];
if (tool.instance.toolId == toolId) {
return tool;
}
}
return null;
@ -90,18 +106,32 @@
/**
* @private
*/
ns.ToolController.prototype.createToolMarkup_ = function() {
var currentTool, toolMarkup = '', extraClass;
// TODO(vincz): Tools rendering order is not enforced by the data stucture (this.toolInstances), fix that.
for (var toolKey in this.toolInstances) {
currentTool = this.toolInstances[toolKey];
extraClass = currentTool.toolId;
if (this.currentSelectedTool == currentTool) {
extraClass = extraClass + " selected";
}
toolMarkup += '<li rel="tooltip" data-placement="right" class="tool-icon ' + extraClass + '" data-tool-id="' + currentTool.toolId +
'" title="' + currentTool.helpText + '"></li>';
ns.ToolController.prototype.createToolsDom_ = function() {
var toolMarkup = '';
for(var i = 0 ; i < this.tools.length ; i++) {
toolMarkup += this.getToolMarkup_(this.tools[i]);
}
$('#tools-container').html(toolMarkup);
};
/**
* @private
*/
ns.ToolController.prototype.getToolMarkup_ = function(tool) {
var instance = tool.instance;
var classList = ['tool-icon', instance.toolId];
if (this.currentSelectedTool == tool) {
classList.push('selected');
}
return '<li rel="tooltip" data-placement="right" class="' + classList.join(' ') + '" data-tool-id="' + instance.toolId +
'" title="' + instance.helpText + '"></li>';
};
ns.ToolController.prototype.addKeyboardShortcuts_ = function () {
for(var i = 0 ; i < this.tools.length ; i++) {
pskl.app.shortcutService.addShortcut(this.tools[i].shortcut, this.onKeyboardShortcut_.bind(this));
}
};
})();

View file

@ -114,8 +114,9 @@
for (var i = 0; i < this.piskelController.getFrameCount(); i++) {
var frame = this.piskelController.getFrameAt(i);
var renderer = new pskl.rendering.CanvasRenderer(frame, zoom);
gif.addFrame(renderer.render(), {
var canvasRenderer = new pskl.rendering.CanvasRenderer(frame, zoom);
var canvas = canvasRenderer.render();
gif.addFrame(canvas.getContext('2d'), {
delay: 1000 / fps
});
}

View file

@ -153,10 +153,11 @@
var image = pskl.utils.ImageResizer.resize(this.importedImage_, w, h, smoothing);
var frame = pskl.utils.FrameUtils.createFromImage(image);
var piskel = pskl.utils.Serializer.createPiskel([frame]);
var layer = pskl.model.Layer.fromFrames('Layer 1', [frame]);
var piskel = pskl.model.Piskel.fromLayers([layer]);
pskl.app.piskelController.setPiskel(piskel);
pskl.app.animationController.setFPS(Constants.DEFAULT.FPS);
this.reset_();
}
}

View file

@ -12,7 +12,7 @@
};
pskl.utils.inherit(ns.ColorPicker, ns.BaseTool);
/**
* @override
*/
@ -20,9 +20,9 @@
if (frame.containsPixel(col, row)) {
var sampledColor = frame.getPixel(col, row);
if (context.button == Constants.LEFT_BUTTON) {
$.publish(Events.PRIMARY_COLOR_SELECTED, [sampledColor]);
$.publish(Events.SELECT_PRIMARY_COLOR, [sampledColor]);
} else if (context.button == Constants.RIGHT_BUTTON) {
$.publish(Events.SECONDARY_COLOR_SELECTED, [sampledColor]);
$.publish(Events.SELECT_SECONDARY_COLOR, [sampledColor]);
}
}
};

View file

@ -10,6 +10,19 @@
}
};
/**
* Create a Layer instance from an already existing set a Frames
* @static
* @param {String} name layer's name
* @param {Array<pskl.model.Frame>} frames should all have the same dimensions
* @return {pskl.model.Layer}
*/
ns.Layer.fromFrames = function (name, frames) {
var layer = new ns.Layer(name);
frames.forEach(layer.addFrame.bind(layer));
return layer;
};
ns.Layer.prototype.getName = function () {
return this.name;
};

View file

@ -21,6 +21,24 @@
}
};
/**
* Create a piskel instance from an existing set of (non empty) layers
* Layers should all be synchronized : same number of frames, same dimensions
* @param {Array<pskl.model.Layer>} layers
* @return {pskl.model.Piskel}
*/
ns.Piskel.fromLayers = function (layers) {
var piskel = null;
if (layers.length > 0 && layers[0].length() > 0) {
var sampleFrame = layers[0].getFrameAt(0);
piskel = new pskl.model.Piskel(sampleFrame.getWidth(), sampleFrame.getHeight());
layers.forEach(piskel.addLayer.bind(piskel));
} else {
throw 'Piskel.fromLayers expects array of non empty pskl.model.Layer as first argument';
}
return piskel;
};
ns.Piskel.prototype.getLayers = function () {
return this.layers;
};

View file

@ -19,24 +19,20 @@
ns.CanvasRenderer.prototype.render = function () {
var canvas = this.createCanvas_();
var context = canvas.getContext('2d');
for(var col = 0, width = this.frame.getWidth(); col < width; col++) {
for(var row = 0, height = this.frame.getHeight(); row < height; row++) {
var color = this.frame.getPixel(col, row);
this.renderPixel_(color, col, row, context);
}
}
return context;
this.frame.forEachPixel(function (color, x, y) {
this.renderPixel_(color, x, y, context);
}.bind(this));
return canvas;
};
ns.CanvasRenderer.prototype.renderPixel_ = function (color, col, row, context) {
ns.CanvasRenderer.prototype.renderPixel_ = function (color, x, y, context) {
if(color == Constants.TRANSPARENT_COLOR) {
color = this.transparentColor_;
}
context.fillStyle = color;
context.fillRect(col * this.zoom, row * this.zoom, this.zoom, this.zoom);
context.fillRect(x * this.zoom, y * this.zoom, this.zoom, this.zoom);
};
ns.CanvasRenderer.prototype.createCanvas_ = function () {

View file

@ -0,0 +1,43 @@
(function () {
var ns = $.namespace('pskl.rendering');
/**
* Render an array of frames
* @param {Array.<pskl.model.Frame>} frames
*/
ns.FramesheetRenderer = function (frames) {
if (frames.length > 0) {
this.frames = frames;
} else {
throw 'FramesheetRenderer : Invalid argument : frames is empty';
}
};
ns.FramesheetRenderer.prototype.renderAsCanvas = function () {
var canvas = this.createCanvas_();
for (var i = 0 ; i < this.frames.length ; i++) {
var frame = this.frames[i];
this.drawFrameInCanvas_(frame, canvas, i * frame.getWidth(), 0);
}
return canvas;
};
ns.FramesheetRenderer.prototype.drawFrameInCanvas_ = function (frame, canvas, offsetWidth, offsetHeight) {
var context = canvas.getContext('2d');
frame.forEachPixel(function (color, x, y) {
if(color != Constants.TRANSPARENT_COLOR) {
context.fillStyle = color;
context.fillRect(x + offsetWidth, y + offsetHeight, 1, 1);
}
});
};
ns.FramesheetRenderer.prototype.createCanvas_ = function () {
var sampleFrame = this.frames[0];
var count = this.frames.length;
var width = count * sampleFrame.getWidth();
var height = sampleFrame.getHeight();
return pskl.CanvasUtils.createCanvas(width, height);
};
})();

View file

@ -0,0 +1,14 @@
(function () {
var ns = $.namespace("pskl.rendering");
ns.PiskelRenderer = function (piskelController) {
var frames = [];
for (var i = 0 ; i < piskelController.getFrameCount() ; i++) {
frames.push(this.piskelController.getFrameAt(i));
}
ns.FramesheetRenderer.call(this, frames);
};
pskl.utils.inherit(ns.PiskelRenderer, ns.FramesheetRenderer);
})();

View file

@ -1,44 +0,0 @@
(function () {
var ns = $.namespace("pskl.rendering");
ns.SpritesheetRenderer = function (piskelController) {
this.piskelController = piskelController;
};
ns.SpritesheetRenderer.prototype.render = function () {
var canvas = this.createCanvas_();
for (var i = 0 ; i < this.piskelController.getFrameCount() ; i++) {
var frame = this.piskelController.getFrameAt(i);
this.drawFrameInCanvas_(frame, canvas, i * this.piskelController.getWidth(), 0);
}
return canvas;
};
/**
* TODO(juliandescottes): Mutualize with code already present in FrameRenderer
*/
ns.SpritesheetRenderer.prototype.drawFrameInCanvas_ = function (frame, canvas, offsetWidth, offsetHeight) {
var context = canvas.getContext('2d');
for(var col = 0, width = frame.getWidth(); col < width; col++) {
for(var row = 0, height = frame.getHeight(); row < height; row++) {
var color = frame.getPixel(col, row);
if(color != Constants.TRANSPARENT_COLOR) {
context.fillStyle = color;
context.fillRect(col + offsetWidth, row + offsetHeight, 1, 1);
}
}
}
};
ns.SpritesheetRenderer.prototype.createCanvas_ = function () {
var frameCount = this.piskelController.getFrameCount();
if (frameCount > 0){
var width = frameCount * this.piskelController.getWidth();
var height = this.piskelController.getHeight();
return pskl.CanvasUtils.createCanvas(width, height);
} else {
throw "Cannot render empty Spritesheet";
}
};
})();

View file

@ -13,9 +13,9 @@
$.subscribe(Events.SELECTION_DISMISSED, $.proxy(this.onSelectionDismissed_, this));
$.subscribe(Events.SELECTION_MOVE_REQUEST, $.proxy(this.onSelectionMoved_, this));
$.subscribe(Events.PASTE, $.proxy(this.onPaste_, this));
$.subscribe(Events.COPY, $.proxy(this.onCopy_, this));
$.subscribe(Events.CUT, $.proxy(this.onCut_, this));
pskl.app.shortcutService.addShortcut('ctrl+V', this.paste.bind(this));
pskl.app.shortcutService.addShortcut('ctrl+X', this.cut.bind(this));
pskl.app.shortcutService.addShortcut('ctrl+C', this.copy.bind(this));
$.subscribe(Events.TOOL_SELECTED, $.proxy(this.onToolSelected_, this));
};
@ -46,10 +46,7 @@
this.cleanSelection_();
};
/**
* @private
*/
ns.SelectionManager.prototype.onCut_ = function(evt) {
ns.SelectionManager.prototype.cut = function() {
if(this.currentSelection) {
// Put cut target into the selection:
this.currentSelection.fillSelectionFromFrame(this.piskelController.getCurrentFrame());
@ -59,9 +56,8 @@
for(var i=0, l=pixels.length; i<l; i++) {
try {
currentFrame.setPixel(pixels[i].col, pixels[i].row, Constants.TRANSPARENT_COLOR);
}
catch(e) {
// Catchng out of frame's bound pixels without testing
} catch(e) {
// Catching out of frame's bound pixels without testing
}
}
}
@ -70,7 +66,7 @@
}
};
ns.SelectionManager.prototype.onPaste_ = function(evt) {
ns.SelectionManager.prototype.paste = function() {
if(this.currentSelection && this.currentSelection.hasPastedContent) {
var pixels = this.currentSelection.pixels;
var currentFrame = this.piskelController.getCurrentFrame();
@ -79,22 +75,17 @@
currentFrame.setPixel(
pixels[i].col, pixels[i].row,
pixels[i].copiedColor);
}
catch(e) {
// Catchng out of frame's bound pixels without testing
} catch(e) {
// Catching out of frame's bound pixels without testing
}
}
}
};
/**
* @private
*/
ns.SelectionManager.prototype.onCopy_ = function(evt) {
ns.SelectionManager.prototype.copy = function() {
if(this.currentSelection && this.piskelController.getCurrentFrame()) {
this.currentSelection.fillSelectionFromFrame(this.piskelController.getCurrentFrame());
}
else {
} else {
throw "Bad state for CUT callback in SelectionManager";
}
};

View file

@ -7,8 +7,9 @@
ns.HistoryService.prototype.init = function () {
$.subscribe(Events.TOOL_RELEASED, this.saveState.bind(this));
$.subscribe(Events.UNDO, this.undo.bind(this));
$.subscribe(Events.REDO, this.redo.bind(this));
pskl.app.shortcutService.addShortcut('ctrl+Z', this.undo.bind(this));
pskl.app.shortcutService.addShortcut('ctrl+Y', this.redo.bind(this));
};
ns.HistoryService.prototype.saveState = function () {

View file

@ -1,64 +0,0 @@
(function () {
var ns = $.namespace("pskl.service");
ns.KeyboardEventService = function () {};
/**
* @private
*/
ns.KeyboardEventService.prototype.KeyboardActions_ = {
"ctrl" : {
"z" : Events.UNDO,
"y" : Events.REDO,
"x" : Events.CUT,
"c" : Events.COPY,
"v" : Events.PASTE
}
};
/**
* @private
*/
ns.KeyboardEventService.prototype.CharCodeToKeyCodeMap_ = {
90 : "z",
89 : "y",
88 : "x",
67 : "c",
86 : "v"
};
/**
* @private
*/
ns.KeyboardEventService.prototype.onKeyUp_ = function(evt) {
var isMac = false;
if (navigator.appVersion.indexOf("Mac")!=-1) {
// Welcome in mac world where vowels are consons and meta used instead of ctrl:
isMac = true;
}
if (isMac ? evt.metaKey : evt.ctrlKey) {
// Get key pressed:
var letter = this.CharCodeToKeyCodeMap_[evt.which];
if(letter) {
var eventToTrigger = this.KeyboardActions_.ctrl[letter];
if(eventToTrigger) {
$.publish(eventToTrigger);
evt.preventDefault();
return false;
}
}
}
};
/**
* @public
*/
ns.KeyboardEventService.prototype.init = function() {
$(document.body).keydown($.proxy(this.onKeyUp_, this));
};
})();

View file

@ -36,7 +36,6 @@
* @private
*/
ns.LocalStorageService.prototype.persistToLocalStorage_ = function() {
console.log('[LocalStorage service]: Snapshot stored');
window.localStorage.snapShot = this.piskelController.serialize();
};
@ -46,8 +45,9 @@
*/
ns.LocalStorageService.prototype.restoreFromLocalStorage_ = function() {
var framesheet = JSON.parse(window.localStorage.snapShot);
var piskel = pskl.utils.Serializer.createPiskel(framesheet);
pskl.app.piskelController.setPiskel(piskel);
pskl.utils.serialization.Deserializer.deserialize(framesheet, function (piskel) {
pskl.app.piskelController.setPiskel(piskel);
});
};
/**

View file

@ -0,0 +1,104 @@
(function () {
var ns = $.namespace('pskl.service.keyboard');
ns.CheatsheetService = function () {
this.isDisplayed_ = false;
};
ns.CheatsheetService.prototype.init = function () {
this.cheatsheetEl_ = document.getElementById('cheatsheet-wrapper');
if (!this.cheatsheetEl_) {
throw 'cheatsheetEl_ DOM element could not be retrieved';
}
this.initMarkup_();
pskl.app.shortcutService.addShortcut('shift+?', this.toggleCheatsheet_.bind(this));
pskl.app.shortcutService.addShortcut('?', this.toggleCheatsheet_.bind(this));
$.subscribe(Events.TOGGLE_HELP, this.toggleCheatsheet_.bind(this));
$.subscribe(Events.ESCAPE, this.onEscape_.bind(this));
};
ns.CheatsheetService.prototype.toggleCheatsheet_ = function () {
if (this.isDisplayed_) {
this.hideCheatsheet_();
} else {
this.showCheatsheet_();
}
};
ns.CheatsheetService.prototype.onEscape_ = function () {
if (this.isDisplayed_) {
this.hideCheatsheet_();
}
};
ns.CheatsheetService.prototype.showCheatsheet_ = function () {
pskl.app.shortcutService.addShortcut('ESC', this.hideCheatsheet_.bind(this));
this.cheatsheetEl_.style.display = 'block';
this.isDisplayed_ = true;
};
ns.CheatsheetService.prototype.hideCheatsheet_ = function () {
pskl.app.shortcutService.removeShortcut('ESC');
this.cheatsheetEl_.style.display = 'none';
this.isDisplayed_ = false;
};
ns.CheatsheetService.prototype.initMarkup_ = function () {
this.initMarkupForTools_();
this.initMarkupForMisc_();
};
ns.CheatsheetService.prototype.initMarkupForTools_ = function () {
var shortcutTemplate = pskl.utils.Template.get('cheatsheet-shortcut-template');
var toolShortcutsContainer = $('.cheatsheet-tool-shortcuts', this.cheatsheetEl_).get(0);
var tools = pskl.app.toolController.tools;
for (var i = 0 ; i < tools.length ; i++) {
var tool = tools[i];
var shortcutEl = pskl.utils.Template.createFromHTML(
pskl.utils.Template.replace(shortcutTemplate, {
shortcutIcon : 'tool-icon ' + tool.instance.toolId,
shortcutDescription : tool.instance.helpText,
shortcutKey : tool.shortcut
})
);
toolShortcutsContainer.appendChild(shortcutEl);
}
};
ns.CheatsheetService.prototype.initMarkupForMisc_ = function () {
var shortcutTemplate = pskl.utils.Template.get('cheatsheet-shortcut-template');
var miscShortcutsContainer = $('.cheatsheet-misc-shortcuts', this.cheatsheetEl_).get(0);
var toDescriptor = function (shortcut, description) {
return {shortcut:shortcut, description:description};
};
var miscKeys = [
toDescriptor('X', 'Swap primary/secondary colors'),
toDescriptor('D', 'Reset default colors'),
toDescriptor('ctrl + X', 'Cut selection'),
toDescriptor('ctrl + C', 'Copy selection'),
toDescriptor('ctrl + V', 'Paste selection'),
toDescriptor('ctrl + Z', 'Undo'),
toDescriptor('ctrl + Y', 'Redo'),
toDescriptor('&#65514;', 'Select previous frame'), /* ASCII for up-arrow */
toDescriptor('&#65516;', 'Select next frame'), /* ASCII for down-arrow */
toDescriptor('N', 'Create new frame'),
toDescriptor('shift + N', 'Duplicate selected frame'),
toDescriptor('shift + ?', 'Open/Close this popup')
];
for (var i = 0 ; i < miscKeys.length ; i++) {
var key = miscKeys[i];
var shortcutEl = pskl.utils.Template.createFromHTML(
pskl.utils.Template.replace(shortcutTemplate, {
shortcutIcon : '',
shortcutDescription : key.description,
shortcutKey : key.shortcut
})
);
miscShortcutsContainer.appendChild(shortcutEl);
}
};
})();

View file

@ -0,0 +1,24 @@
(function () {
var specialKeys = {
191 : "?",
27 : "esc",
38 : "up",
40 : "down"
};
var ns = $.namespace('pskl.service.keyboard');
ns.KeycodeTranslator= {
toChar : function (keycode) {
if (keycode >= 48 && keycode <= 57) {
// key is 0-9
return (keycode - 48) + "";
} else if (keycode >= 65 && keycode <= 90) {
// key is a-z, use base 36 to get the string representation
return (keycode - 65 + 10).toString(36);
} else {
return specialKeys[keycode];
}
}
};
})();

View file

@ -0,0 +1,90 @@
(function () {
var ns = $.namespace('pskl.service.keyboard');
ns.ShortcutService = function () {
this.shortcuts_ = {};
};
/**
* @public
*/
ns.ShortcutService.prototype.init = function() {
$(document.body).keydown($.proxy(this.onKeyUp_, this));
};
ns.ShortcutService.prototype.addShortcut = function (rawKey, callback) {
var parsedKey = this.parseKey_(rawKey.toLowerCase());
var key = parsedKey.key,
meta = parsedKey.meta;
this.shortcuts_[key] = this.shortcuts_[key] || {};
if (this.shortcuts_[key][meta]) {
throw 'Shortcut ' + meta + ' + ' + key + ' already registered';
} else {
this.shortcuts_[key][meta] = callback;
}
};
ns.ShortcutService.prototype.removeShortcut = function (rawKey) {
var parsedKey = this.parseKey_(rawKey.toLowerCase());
var key = parsedKey.key,
meta = parsedKey.meta;
this.shortcuts_[key] = this.shortcuts_[key] || {};
this.shortcuts_[key][meta] = null;
};
ns.ShortcutService.prototype.parseKey_ = function (key) {
var meta = 'normal';
if (key.indexOf('ctrl+') === 0) {
meta = 'ctrl';
key = key.replace('ctrl+', '');
} else if (key.indexOf('shift+') === 0) {
meta = 'shift';
key = key.replace('shift+', '');
}
return {meta : meta, key : key};
};
/**
* @private
*/
ns.ShortcutService.prototype.onKeyUp_ = function(evt) {
// jquery names FTW ...
var keycode = evt.which;
var charkey = pskl.service.keyboard.KeycodeTranslator.toChar(keycode);
var keyShortcuts = this.shortcuts_[charkey];
if(keyShortcuts) {
var cb;
if (this.isCtrlKeyPressed_(evt)) {
cb = keyShortcuts.ctrl;
} else if (this.isShiftKeyPressed_(evt)) {
cb = keyShortcuts.shift;
} else {
cb = keyShortcuts.normal;
}
if(cb) {
cb(charkey);
evt.preventDefault();
}
}
};
ns.ShortcutService.prototype.isCtrlKeyPressed_ = function (evt) {
return this.isMac_() ? evt.metaKey : evt.ctrlKey;
};
ns.ShortcutService.prototype.isShiftKeyPressed_ = function (evt) {
return evt.shiftKey;
};
ns.ShortcutService.prototype.isMac_ = function () {
return navigator.appVersion.indexOf("Mac") != -1;
};
})();

View file

@ -107,25 +107,29 @@
context.drawImage(image, 0,0,w,h,0,0,w,h);
var imgData = context.getImageData(0,0,w,h).data;
return pskl.utils.FrameUtils.createFromImageData(imgData, w, h);
},
createFromImageData : function (imageData, width, height) {
// Draw the zoomed-up pixels to a different canvas context
var frame = [];
for (var x=0;x<image.width;++x){
frame[x] = [];
for (var y=0;y<image.height;++y){
var grid = [];
for (var x = 0 ; x < width ; x++){
grid[x] = [];
for (var y = 0 ; y < height ; y++){
// Find the starting index in the one-dimensional image data
var i = (y*image.width + x)*4;
var r = imgData[i ];
var g = imgData[i+1];
var b = imgData[i+2];
var a = imgData[i+3];
var i = (y * width + x)*4;
var r = imageData[i ];
var g = imageData[i+1];
var b = imageData[i+2];
var a = imageData[i+3];
if (a < 125) {
frame[x][y] = Constants.TRANSPARENT_COLOR;
grid[x][y] = Constants.TRANSPARENT_COLOR;
} else {
frame[x][y] = this.rgbToHex(r,g,b);
grid[x][y] = pskl.utils.FrameUtils.rgbToHex(r,g,b);
}
}
}
return frame;
return pskl.model.Frame.fromPixelGrid(grid);
},
/**

31
js/utils/LayerUtils.js Normal file
View file

@ -0,0 +1,31 @@
(function () {
var ns = $.namespace('pskl.utils');
ns.LayerUtils = {
/**
* Create a pskl.model.Layer from an Image object.
* Transparent pixels will either be converted to completely opaque or completely transparent pixels.
* @param {Image} image source image
* @return {pskl.model.Frame} corresponding frame
*/
createFromImage : function (image, frameCount) {
var w = image.width,
h = image.height,
frameWidth = w / frameCount;
var canvas = pskl.CanvasUtils.createCanvas(w, h);
var context = canvas.getContext('2d');
context.drawImage(image, 0,0,w,h,0,0,w,h);
// Draw the zoomed-up pixels to a different canvas context
var frames = [];
for (var i = 0 ; i < frameCount ; i++) {
var imgData = context.getImageData(frameWidth*i,0,frameWidth,h).data;
var frame = pskl.utils.FrameUtils.createFromImageData(imgData, frameWidth, h);
frames.push(frame);
}
return frames;
}
};
})();

View file

@ -1,87 +0,0 @@
(function () {
var ns = $.namespace('pskl.utils');
ns.Serializer = {
serializePiskel : function (piskel) {
var serializedLayers = piskel.getLayers().map(function (l) {
return pskl.utils.Serializer.serializeLayer(l);
});
return JSON.stringify({
modelVersion : Constants.MODEL_VERSION,
piskel : {
height : piskel.getHeight(),
width : piskel.getWidth(),
layers : serializedLayers
}
});
},
serializeLayer : function (layer) {
var serializedFrames = layer.getFrames().map(function (f) {
return f.serialize();
});
return JSON.stringify({
name : layer.getName(),
frames : serializedFrames
});
},
deserializePiskel : function (piskelString) {
var piskelData = JSON.parse(piskelString);
return this.createPiskelFromData(piskelData);
},
/**
* Similar to deserializePiskel, but dealing directly with a parsed piskel
* @param {Object} piskelData JSON.parse of a serialized piskel
* @return {pskl.model.Piskel} a piskel
*/
createPiskel : function (piskelData) {
var piskel = null;
if (piskelData.modelVersion == Constants.MODEL_VERSION) {
var pData = piskelData.piskel;
piskel = new pskl.model.Piskel(pData.width, pData.height);
pData.layers.forEach(function (serializedLayer) {
var layer = pskl.utils.Serializer.deserializeLayer(serializedLayer);
piskel.addLayer(layer);
});
} else {
piskel = pskl.utils.Serializer.backwardDeserializer_(piskelData);
}
return piskel;
},
deserializeLayer : function (layerString) {
var lData = JSON.parse(layerString);
var layer = new pskl.model.Layer(lData.name);
lData.frames.forEach(function (serializedFrame) {
var frame = pskl.utils.Serializer.deserializeFrame(serializedFrame);
layer.addFrame(frame);
});
return layer;
},
deserializeFrame : function (frameString) {
var framePixelGrid = JSON.parse(frameString);
return pskl.model.Frame.fromPixelGrid(framePixelGrid);
},
/**
* Deserialize old piskel framesheets. Initially piskels were stored as arrays of frames : "[[pixelGrid],[pixelGrid],[pixelGrid]]".
*/
backwardDeserializer_ : function (frames) {
var layer = new pskl.model.Layer('Layer 1');
frames.forEach(function (frame) {
layer.addFrame(pskl.model.Frame.fromPixelGrid(frame));
});
var width = layer.getFrameAt(0).getWidth(), height = layer.getFrameAt(0).getHeight();
var piskel = new pskl.model.Piskel(width, height);
piskel.addLayer(layer);
return piskel;
}
};
})();

View file

@ -0,0 +1,68 @@
(function () {
var ns = $.namespace('pskl.utils.serialization');
ns.Deserializer = function (data, callback) {
this.layersToLoad_ = 0;
this.data_ = data;
this.callback_ = callback;
this.piskel_ = null;
};
ns.Deserializer.deserialize = function (data, callback) {
var deserializer;
if (data.modelVersion == Constants.MODEL_VERSION) {
deserializer = new ns.Deserializer(data, callback);
} else if (data.modelVersion == 1) {
deserializer = new ns.backward.Deserializer_v1(data, callback);
} else {
deserializer = new ns.backward.Deserializer_v0(data, callback);
}
deserializer.deserialize();
};
ns.Deserializer.prototype.deserialize = function () {
var data = this.data_;
var piskelData = data.piskel;
this.piskel_ = new pskl.model.Piskel(piskelData.width, piskelData.height);
this.layersToLoad_ = piskelData.layers.length;
piskelData.layers.forEach(function (serializedLayer) {
var layer = this.deserializeLayer(serializedLayer);
this.piskel_.addLayer(layer);
}.bind(this));
};
ns.Deserializer.prototype.deserializeLayer = function (layerString) {
var layerData = JSON.parse(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();
// 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
frames.forEach(layer.addFrame.bind(layer));
this.onLayerLoaded_();
}.bind(this);
// 3 - set the source of the image
image.src = base64PNG;
// 4 - return a pointer to the new layer instance
return layer;
};
ns.Deserializer.prototype.onLayerLoaded_ = function () {
this.layersToLoad_ = this.layersToLoad_ - 1;
if (this.layersToLoad_ === 0) {
this.callback_(this.piskel_);
}
};
})();

View file

@ -0,0 +1,31 @@
(function () {
var ns = $.namespace('pskl.utils');
ns.Serializer = {
serializePiskel : function (piskel) {
var serializedLayers = piskel.getLayers().map(function (l) {
return pskl.utils.Serializer.serializeLayer(l);
});
return JSON.stringify({
modelVersion : Constants.MODEL_VERSION,
piskel : {
height : piskel.getHeight(),
width : piskel.getWidth(),
layers : serializedLayers
}
});
},
serializeLayer : function (layer) {
var frames = layer.getFrames();
var renderer = new pskl.rendering.FramesheetRenderer(frames);
var base64PNG = renderer.renderAsCanvas().toDataURL();
return JSON.stringify({
name : layer.getName(),
base64PNG : base64PNG,
frameCount : frames.length
});
}
};
})();

View file

@ -0,0 +1,18 @@
(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 layer = pskl.model.Layer.fromFrames('Layer 1', frames);
this.callback_(pskl.model.Piskel.fromLayers([layer]));
};
})();

View file

@ -0,0 +1,36 @@
(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 piskel = new pskl.model.Piskel(piskelData.width, piskelData.height);
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);
};
})();

View file

@ -19,11 +19,15 @@ exports.scripts = [
"js/utils/Math.js",
"js/utils/FileUtils.js",
"js/utils/FrameUtils.js",
"js/utils/LayerUtils.js",
"js/utils/ImageResizer.js",
"js/utils/PixelUtils.js",
"js/utils/Serializer.js",
"js/utils/Template.js",
"js/utils/UserSettings.js",
"js/utils/serialization/Serializer.js",
"js/utils/serialization/Deserializer.js",
"js/utils/serialization/backward/Deserializer_v0.js",
"js/utils/serialization/backward/Deserializer_v1.js",
// Application libraries-->
"js/rendering/DrawingLoop.js",
@ -46,7 +50,8 @@ exports.scripts = [
"js/rendering/frame/FrameRenderer.js",
"js/rendering/frame/CachedFrameRenderer.js",
"js/rendering/CanvasRenderer.js",
"js/rendering/SpritesheetRenderer.js",
"js/rendering/FramesheetRenderer.js",
"js/rendering/PiskelRenderer.js",
// Controllers
"js/controller/PiskelController.js",
@ -69,7 +74,9 @@ exports.scripts = [
// Services
"js/service/LocalStorageService.js",
"js/service/HistoryService.js",
"js/service/KeyboardEventService.js",
"js/service/keyboard/ShortcutService.js",
"js/service/keyboard/KeycodeTranslator.js",
"js/service/keyboard/CheatsheetService.js",
"js/service/ImageUploadService.js",
// Tools

19
templates/cheatsheet.html Normal file
View file

@ -0,0 +1,19 @@
<div id="cheatsheet-wrapper" style="display:none">
<div class="cheatsheet-container">
<div class="cheatsheet-section">
<h3>Tool shortcuts</h3>
<ul class="cheatsheet-tool-shortcuts"></ul>
</div>
<div class="cheatsheet-section">
<h3>Misc shortcuts</h3>
<ul class="cheatsheet-misc-shortcuts"></ul>
</div>
</div>
</div>
<script type="text/template" id="cheatsheet-shortcut-template">
<li class="cheatsheet-shortcut">
<div class="cheatsheet-icon {{shortcutIcon}}"></div>
<span class="cheatsheet-key">{{shortcutKey}}</span>
<span class="cheatsheet-description">{{shortcutDescription}}</span>
</li>
</script>