From 4f54715f705090805846eaa2ca64d90ee946ff7e Mon Sep 17 00:00:00 2001 From: jdescottes Date: Fri, 8 Nov 2013 00:44:24 +0100 Subject: [PATCH] fix : reduce piskel model size - Initial implementation : working but ... - MODEL_VERSION has been bumped to 2 - The loading process is now theoretically asynchronous (loading images to read the content of the layers), but for now, the asynchronous behaviour is hidden behind a nasty hack, which is somehow similar to lazy loading. When loading the piskel, a Piskel is created synchronously, with fake empty frames, and as the images will get loaded, the fake frames will be replaced by the actual frames. I really don't like this, and the asynchronous nature of the loading should be clearly expressed - There is no backward compatible deserializer for the previous version of the model (1) - The Serializer utils is just badly designed. Serialization and deserialization should be splitted into two different classes - Saving & loading are still done in app.js and should be moved to services BUT : the size of the piskels is now pretty small. A piskel which was using 890kB previously is now using only 10kB. Although it should be noted, that after gzip there is no significant difference between this version and the existing one. The only gains we can really expect with this are : less disk space used on appengine, ability to reuse the layers' pngs directly on piskel-website (but to be honest I can't see any valid use case for this) --- js/Constants.js | 2 +- js/app.js | 4 +- js/controller/settings/GifExportController.js | 5 +- js/rendering/CanvasRenderer.js | 18 ++--- js/rendering/FramesheetRenderer.js | 43 +++++++++++ js/rendering/PiskelRenderer.js | 14 ++++ js/rendering/SpritesheetRenderer.js | 44 ------------ js/service/LocalStorageService.js | 7 +- js/utils/FrameUtils.js | 22 +++--- js/utils/LayerUtils.js | 31 ++++++++ js/utils/Serializer.js | 72 ++++++++++++++----- piskel-script-list.js | 4 +- 12 files changed, 175 insertions(+), 91 deletions(-) create mode 100644 js/rendering/FramesheetRenderer.js create mode 100644 js/rendering/PiskelRenderer.js delete mode 100644 js/rendering/SpritesheetRenderer.js create mode 100644 js/utils/LayerUtils.js diff --git a/js/Constants.js b/js/Constants.js index fd0bcdc2..f4a37de4 100644 --- a/js/Constants.js +++ b/js/Constants.js @@ -6,7 +6,7 @@ var Constants = { FPS : 12 }, - MODEL_VERSION : 1, + MODEL_VERSION : 2, MAX_HEIGHT : 128, MAX_WIDTH : 128, diff --git a/js/app.js b/js/app.js index 3262db09..3b76f30f 100644 --- a/js/app.js +++ b/js/app.js @@ -239,8 +239,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"); }, diff --git a/js/controller/settings/GifExportController.js b/js/controller/settings/GifExportController.js index 42e57d3d..ae0b6832 100644 --- a/js/controller/settings/GifExportController.js +++ b/js/controller/settings/GifExportController.js @@ -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, dpi); - gif.addFrame(renderer.render(), { + var canvasRenderer = new pskl.rendering.CanvasRenderer(frame, dpi); + var canvas = canvasRenderer.render(); + gif.addFrame(canvas.getContext('2d'), { delay: 1000 / fps }); } diff --git a/js/rendering/CanvasRenderer.js b/js/rendering/CanvasRenderer.js index 2153f207..2e5e99af 100644 --- a/js/rendering/CanvasRenderer.js +++ b/js/rendering/CanvasRenderer.js @@ -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.dpi, row * this.dpi, this.dpi, this.dpi); + context.fillRect(x * this.dpi, y * this.dpi, this.dpi, this.dpi); }; ns.CanvasRenderer.prototype.createCanvas_ = function () { diff --git a/js/rendering/FramesheetRenderer.js b/js/rendering/FramesheetRenderer.js new file mode 100644 index 00000000..06eb4d99 --- /dev/null +++ b/js/rendering/FramesheetRenderer.js @@ -0,0 +1,43 @@ +(function () { + var ns = $.namespace('pskl.rendering'); + + /** + * Render an array of frames + * @param {Array.} 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); + }; + +})(); \ No newline at end of file diff --git a/js/rendering/PiskelRenderer.js b/js/rendering/PiskelRenderer.js new file mode 100644 index 00000000..33a0f2f4 --- /dev/null +++ b/js/rendering/PiskelRenderer.js @@ -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); +})(); \ No newline at end of file diff --git a/js/rendering/SpritesheetRenderer.js b/js/rendering/SpritesheetRenderer.js deleted file mode 100644 index d634da0f..00000000 --- a/js/rendering/SpritesheetRenderer.js +++ /dev/null @@ -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"; - } - }; -})(); \ No newline at end of file diff --git a/js/service/LocalStorageService.js b/js/service/LocalStorageService.js index 6267773f..7f15243a 100644 --- a/js/service/LocalStorageService.js +++ b/js/service/LocalStorageService.js @@ -36,7 +36,6 @@ * @private */ ns.LocalStorageService.prototype.persistToLocalStorage_ = function() { - console.log('[LocalStorage service]: Snapshot stored'); window.localStorage.snapShot = this.piskelController.serialize(); }; @@ -45,9 +44,9 @@ * @private */ ns.LocalStorageService.prototype.restoreFromLocalStorage_ = function() { - - this.piskelController.deserialize(window.localStorage.snapShot); - this.piskelController.setCurrentFrameIndex(0); + var framesheet = JSON.parse(window.localStorage.snapShot); + var piskel = pskl.utils.Serializer.createPiskel(framesheet); + pskl.app.piskelController.setPiskel(piskel); }; /** diff --git a/js/utils/FrameUtils.js b/js/utils/FrameUtils.js index ef14f935..7b55cd76 100644 --- a/js/utils/FrameUtils.js +++ b/js/utils/FrameUtils.js @@ -36,21 +36,25 @@ 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); + }, + + createFromImageData : function (imageData, width, height) { // Draw the zoomed-up pixels to a different canvas context var frame = []; - for (var x=0;x