From 2db04fe7d6ad7fcaba238229555c05a7288263cc Mon Sep 17 00:00:00 2001 From: jdescottes Date: Thu, 18 Dec 2014 21:42:03 +0100 Subject: [PATCH 1/7] Support rectangular resolution & maximize viewport usage --- src/css/toolbox-animated-preview.css | 16 +++++-- .../controller/AnimatedPreviewController.js | 13 +++-- src/js/controller/DrawingController.js | 14 ++++-- src/js/controller/MinimapController.js | 48 +++++++++++++++---- src/js/rendering/frame/FrameRenderer.js | 2 +- src/templates/layers-list.html | 2 +- src/templates/preview.html | 6 ++- 7 files changed, 76 insertions(+), 25 deletions(-) diff --git a/src/css/toolbox-animated-preview.css b/src/css/toolbox-animated-preview.css index 1d5bbc15..6209cd1d 100644 --- a/src/css/toolbox-animated-preview.css +++ b/src/css/toolbox-animated-preview.css @@ -3,9 +3,9 @@ */ .preview-container { - border : 0px Solid black; - border-radius:5px 0px 0px 5px; - box-shadow : 0px 0px 2px rgba(0,0,0,0.2); + border : 0 Solid black; + border-radius:5px 0 0 5px; + box-shadow : 0 0 2px rgba(0,0,0,0.2); font-size: 0; } @@ -14,7 +14,12 @@ } .preview-container canvas { - border : 0px Solid transparent; + border : 0 Solid transparent; +} + +.canvas-container .animated-preview-canvas-background { + position : relative; + height: 100%; } #animated-preview-container { @@ -23,9 +28,10 @@ overflow : hidden; } -#animated-preview-canvas-container { +.canvas-container-wrapper { height :200px; width : 200px; + overflow:hidden; } .tiled-frame-container { diff --git a/src/js/controller/AnimatedPreviewController.js b/src/js/controller/AnimatedPreviewController.js index 59d29aca..43269e4a 100644 --- a/src/js/controller/AnimatedPreviewController.js +++ b/src/js/controller/AnimatedPreviewController.js @@ -35,6 +35,7 @@ this.updateZoom_(); this.updateOnionSkinPreview_(); + this.updateContainerDimensions_(); }; ns.AnimatedPreviewController.prototype.onUserSettingsChange_ = function (evt, name, value) { @@ -140,10 +141,14 @@ containerEl.style.height = height + "px"; containerEl.style.width = width + "px"; - containerEl.style.marginTop = ((PREVIEW_SIZE - height) / 2) + "px"; - containerEl.style.marginBottom = ((PREVIEW_SIZE - height) / 2) + "px"; - containerEl.style.marginLeft = ((PREVIEW_SIZE - width) / 2) + "px"; - containerEl.style.marginRight = ((PREVIEW_SIZE - width) / 2) + "px"; + + var horizontalPadding = (PREVIEW_SIZE - height) / 2; + containerEl.style.marginTop = horizontalPadding + "px"; + containerEl.style.marginBottom = horizontalPadding + "px"; + + var verticalPadding = (PREVIEW_SIZE - width) / 2; + containerEl.style.marginLeft = verticalPadding + "px"; + containerEl.style.marginRight = verticalPadding + "px"; }; ns.AnimatedPreviewController.prototype.toggleOnionSkin_ = function () { diff --git a/src/js/controller/DrawingController.js b/src/js/controller/DrawingController.js index 538cbe1e..f8fa153f 100644 --- a/src/js/controller/DrawingController.js +++ b/src/js/controller/DrawingController.js @@ -22,7 +22,6 @@ */ this.container = container; - // TODO(vincz): Store user prefs in a localstorage string ? var renderingOptions = { "zoom": this.calculateZoom_(), "supportGridRendering" : true, @@ -32,6 +31,8 @@ "yOffset" : 0 }; + console.log('DrawingController:getContainerWidth_', this.getContainerWidth_()); + this.overlayRenderer = new pskl.rendering.frame.CachedFrameRenderer(this.container, renderingOptions, ["canvas-overlay"]); this.renderer = new pskl.rendering.frame.CachedFrameRenderer(this.container, renderingOptions, ["drawing-canvas"]); this.onionSkinRenderer = new pskl.rendering.OnionSkinRenderer(this.container, renderingOptions, piskelController); @@ -70,7 +71,10 @@ pskl.app.shortcutService.addShortcut('+', this.increaseZoom_.bind(this, 1)); pskl.app.shortcutService.addShortcut('-', this.decreaseZoom_.bind(this, 1)); - window.setTimeout(this.afterWindowResize_.bind(this), 100); + window.setTimeout(function () { + this.afterWindowResize_(); + this.resetZoom_(); + }.bind(this), 100); }; ns.DrawingController.prototype.initMouseBehavior = function() { @@ -89,6 +93,7 @@ // Deactivate right click: body.contextmenu(this.onCanvasContextMenu_); + }; ns.DrawingController.prototype.startResizeTimer_ = function () { @@ -100,6 +105,7 @@ ns.DrawingController.prototype.afterWindowResize_ = function () { var initialWidth = this.compositeRenderer.getDisplaySize().width; + this.compositeRenderer.setDisplaySize(this.getContainerWidth_(), this.getContainerHeight_()); this.centerColumnWrapperHorizontally_(); var ratio = this.compositeRenderer.getDisplaySize().width / initialWidth; @@ -358,11 +364,11 @@ }; ns.DrawingController.prototype.getContainerHeight_ = function () { - return this.calculateZoom_() * this.piskelController.getCurrentFrame().getHeight(); + return this.getAvailableHeight_(); }; ns.DrawingController.prototype.getContainerWidth_ = function () { - return this.calculateZoom_() * this.piskelController.getCurrentFrame().getWidth(); + return this.getAvailableWidth_(); }; /** diff --git a/src/js/controller/MinimapController.js b/src/js/controller/MinimapController.js index 211f66b0..2997b3c9 100644 --- a/src/js/controller/MinimapController.js +++ b/src/js/controller/MinimapController.js @@ -5,7 +5,7 @@ this.piskelController = piskelController; this.animationController = animationController; this.drawingController = drawingController; - this.container = container; + this.container = container.parent(); this.isClicked = false; }; @@ -27,6 +27,8 @@ ns.MinimapController.prototype.renderMinimap_ = function () { var zoomRatio = this.getDrawingAreaZoomRatio_(); + console.log('zoomRatio', zoomRatio); + console.log('this.animationController.getZoom()', this.animationController.getZoom()); if (zoomRatio > 1) { this.displayCropFrame_(zoomRatio, this.drawingController.getRenderer().getOffset()); } else { @@ -36,11 +38,33 @@ ns.MinimapController.prototype.displayCropFrame_ = function (ratio, offset) { this.cropFrame.style.display = 'block'; - this.cropFrame.style.top = (offset.y * this.animationController.getZoom()) + 'px'; - this.cropFrame.style.left = (offset.x * this.animationController.getZoom()) + 'px'; - var zoomRatio = this.getDrawingAreaZoomRatio_(); - this.cropFrame.style.width = (this.container.width() / zoomRatio) + 'px'; - this.cropFrame.style.height = (this.container.height() / zoomRatio) + 'px'; + + var containerHeight = this.container.height(); + var containerWidth = this.container.width(); + var displaySize = this.drawingController.getRenderer().getDisplaySize(); + var width = displaySize.width / ratio; + var height = displaySize.height / ratio; + this.cropFrame.style.width = Math.min(width, containerWidth) + 'px'; + this.cropFrame.style.height = Math.min(height, containerHeight) + 'px'; + + + var containerSize = Math.max(containerHeight, containerWidth); + var margin = this.drawingController.renderer.margin; + + var frame = this.piskelController.getCurrentFrame(); + var framePreviewWidth = frame.getWidth() * this.animationController.getZoom(); + var framePreviewHeight = frame.getHeight() * this.animationController.getZoom(); + + var left = (containerSize - Math.max(width, framePreviewWidth))/2; + left += offset.x * this.animationController.getZoom(); + left = Math.max(0, left); + this.cropFrame.style.left = left + 'px'; + + var top = (containerSize - Math.max(height, framePreviewHeight))/2; + top += offset.y * this.animationController.getZoom(); + top = Math.max(0, top); + this.cropFrame.style.top = top + 'px'; + }; @@ -82,8 +106,16 @@ ns.MinimapController.prototype.getDrawingAreaZoomRatio_ = function () { var drawingAreaZoom = this.drawingController.getRenderer().getZoom(); - var drawingAreaFullHeight = this.piskelController.getCurrentFrame().getHeight() * drawingAreaZoom; - var zoomRatio = drawingAreaFullHeight / this.drawingController.getRenderer().getDisplaySize().height; + var frame = this.piskelController.getCurrentFrame(); + var dim = Math.max(frame.getHeight(), frame.getWidth()); + var drawingAreaSize = dim * drawingAreaZoom; + + var containerHeight = this.container.height(); + var containerWidth = this.container.width(); + + var containerSize = Math.max(containerHeight, containerWidth); + + var zoomRatio = drawingAreaSize / containerSize; return zoomRatio; }; diff --git a/src/js/rendering/frame/FrameRenderer.js b/src/js/rendering/frame/FrameRenderer.js index 03dc0c8e..ee746b75 100644 --- a/src/js/rendering/frame/FrameRenderer.js +++ b/src/js/rendering/frame/FrameRenderer.js @@ -239,7 +239,7 @@ var displayContext = this.displayCanvas.getContext('2d'); displayContext.save(); - if (this.canvas.width*this.zoom < this.displayCanvas.width) { + if (this.canvas.width*this.zoom < this.displayCanvas.width || this.canvas.height*this.zoom < this.displayCanvas.height) { displayContext.fillStyle = Constants.ZOOMED_OUT_BACKGROUND_COLOR; displayContext.fillRect(0,0,this.displayCanvas.width, this.displayCanvas.height); } diff --git a/src/templates/layers-list.html b/src/templates/layers-list.html index 8930f9f4..48f28f3b 100644 --- a/src/templates/layers-list.html +++ b/src/templates/layers-list.html @@ -2,7 +2,7 @@

Layers

diff --git a/src/templates/preview.html b/src/templates/preview.html index d7129d62..fb4ade6b 100644 --- a/src/templates/preview.html +++ b/src/templates/preview.html @@ -1,6 +1,8 @@
-
-
+
+
+
+
Date: Thu, 18 Dec 2014 22:29:14 +0100 Subject: [PATCH 2/7] cleanup MinimapController --- .gitignore | 3 +++ src/js/app.js | 2 +- src/js/controller/MinimapController.js | 4 +--- src/templates/preview.html | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index e25a5dc9..dca3c965 100644 --- a/.gitignore +++ b/.gitignore @@ -20,5 +20,8 @@ diff.txt dest build/closure/closure_compiled_binary.js +# plato report directory +report + # marked as private *.private.* \ No newline at end of file diff --git a/src/js/app.js b/src/js/app.js index 8d626e02..6fa651fe 100644 --- a/src/js/app.js +++ b/src/js/app.js @@ -60,7 +60,7 @@ this.animationController = new pskl.controller.AnimatedPreviewController(this.piskelController, $('#animated-preview-canvas-container')); this.animationController.init(); - this.minimapController = new pskl.controller.MinimapController(this.piskelController, this.animationController, this.drawingController, $('#animated-preview-canvas-container')); + this.minimapController = new pskl.controller.MinimapController(this.piskelController, this.animationController, this.drawingController, $('.minimap-container')); this.minimapController.init(); this.previewFilmController = new pskl.controller.PreviewFilmController(this.piskelController, $('#preview-list')); diff --git a/src/js/controller/MinimapController.js b/src/js/controller/MinimapController.js index 2997b3c9..e64e6149 100644 --- a/src/js/controller/MinimapController.js +++ b/src/js/controller/MinimapController.js @@ -5,7 +5,7 @@ this.piskelController = piskelController; this.animationController = animationController; this.drawingController = drawingController; - this.container = container.parent(); + this.container = container; this.isClicked = false; }; @@ -27,8 +27,6 @@ ns.MinimapController.prototype.renderMinimap_ = function () { var zoomRatio = this.getDrawingAreaZoomRatio_(); - console.log('zoomRatio', zoomRatio); - console.log('this.animationController.getZoom()', this.animationController.getZoom()); if (zoomRatio > 1) { this.displayCropFrame_(zoomRatio, this.drawingController.getRenderer().getOffset()); } else { diff --git a/src/templates/preview.html b/src/templates/preview.html index fb4ade6b..b0bfe93e 100644 --- a/src/templates/preview.html +++ b/src/templates/preview.html @@ -1,5 +1,5 @@
-
+
From 1402394d077d8fc0cb96a27feb34aef3ab7deb67 Mon Sep 17 00:00:00 2001 From: jdescottes Date: Thu, 18 Dec 2014 23:57:34 +0100 Subject: [PATCH 3/7] Animation pauses when FPS slider at 0FPS + slight perf improvement --- src/js/Events.js | 1 + .../controller/AnimatedPreviewController.js | 67 ++++++++++++++----- src/js/controller/DrawingController.js | 4 +- src/js/model/frame/CachedFrameProcessor.js | 2 +- src/js/rendering/frame/FrameRenderer.js | 14 ++-- src/js/utils/core.js | 13 +++- src/templates/preview.html | 2 +- 7 files changed, 72 insertions(+), 31 deletions(-) diff --git a/src/js/Events.js b/src/js/Events.js index fb2203a3..21eb133b 100644 --- a/src/js/Events.js +++ b/src/js/Events.js @@ -5,6 +5,7 @@ var Events = { SELECT_TOOL : "SELECT_TOOL", TOOL_RELEASED : "TOOL_RELEASED", + TOOL_PRESSED : "TOOL_PRESSED", SELECT_PRIMARY_COLOR: "SELECT_PRIMARY_COLOR", SELECT_SECONDARY_COLOR: "SELECT_SECONDARY_COLOR", PRIMARY_COLOR_SELECTED : 'PRIMARY_COLOR_SELECTED', diff --git a/src/js/controller/AnimatedPreviewController.js b/src/js/controller/AnimatedPreviewController.js index 43269e4a..9d5f2eb4 100644 --- a/src/js/controller/AnimatedPreviewController.js +++ b/src/js/controller/AnimatedPreviewController.js @@ -1,5 +1,5 @@ (function () { - var ns = $.namespace("pskl.controller"); + var ns = $.namespace('pskl.controller'); // Preview is a square of PREVIEW_SIZE x PREVIEW_SIZE var PREVIEW_SIZE = 200; @@ -11,6 +11,11 @@ this.elapsedTime = 0; this.currentIndex = 0; + this.renderFlag = true; + + this.fpsRangeInput = $('#preview-fps'); + this.fpsCounterDisplay = $('#display-fps'); + this.setFPS(Constants.DEFAULT.FPS); var frame = this.piskelController.getCurrentFrame(); @@ -22,10 +27,10 @@ // the oninput event won't work on IE10 unfortunately, but at least will provide a // consistent behavior across all other browsers that support the input type range // see https://bugzilla.mozilla.org/show_bug.cgi?id=853670 - $("#preview-fps").on('input change', this.onFPSSliderChange.bind(this)); - document.querySelector(".right-column").style.width = Constants.ANIMATED_PREVIEW_WIDTH + 'px'; + this.fpsRangeInput.on('input change', this.onFPSSliderChange.bind(this)); + document.querySelector('.right-column').style.width = Constants.ANIMATED_PREVIEW_WIDTH + 'px'; - this.toggleOnionSkinEl = document.querySelector(".preview-toggle-onion-skin"); + this.toggleOnionSkinEl = document.querySelector('.preview-toggle-onion-skin'); this.toggleOnionSkinEl.addEventListener('click', this.toggleOnionSkin_.bind(this)); pskl.app.shortcutService.addShortcut('alt+O', this.toggleOnionSkin_.bind(this)); @@ -33,6 +38,9 @@ $.subscribe(Events.FRAME_SIZE_CHANGED, this.onFrameSizeChange_.bind(this)); $.subscribe(Events.USER_SETTINGS_CHANGED, $.proxy(this.onUserSettingsChange_, this)); + $.subscribe(Events.TOOL_RELEASED, this.setRenderFlag_.bind(this, true)); + $.subscribe(Events.TOOL_PRESSED, this.setRenderFlag_.bind(this, false)); + this.updateZoom_(); this.updateOnionSkinPreview_(); this.updateContainerDimensions_(); @@ -78,14 +86,16 @@ }; ns.AnimatedPreviewController.prototype.onFPSSliderChange = function (evt) { - this.setFPS(parseInt($("#preview-fps")[0].value, 10)); + this.setFPS(parseInt(this.fpsRangeInput[0].value, 10)); + }; ns.AnimatedPreviewController.prototype.setFPS = function (fps) { - if (fps) { + if (typeof fps === 'number') { this.fps = fps; - $("#preview-fps").val(this.fps); - $("#display-fps").html(this.fps + " FPS"); + this.fpsRangeInput.val(this.fps); + this.fpsRangeInput.blur(); + this.fpsCounterDisplay.html(this.fps + ' FPS'); } }; @@ -94,7 +104,24 @@ }; ns.AnimatedPreviewController.prototype.render = function (delta) { - this.elapsedTime += delta; + if (this.renderFlag) { + this.elapsedTime += delta; + if (this.fps === 0) { + this._renderSelectedFrame(); + } else { + this._renderCurrentAnimationFrame(); + } + } + }; + + ns.AnimatedPreviewController.prototype._renderSelectedFrame = function (delta) { + // the selected frame is the currentFrame from the PiskelController perspective + var selectedFrameIndex = this.piskelController.getCurrentFrameIndex(); + var selectedFrame = this.piskelController.getFrameAt(selectedFrameIndex); + this.renderer.render(selectedFrame); + }; + + ns.AnimatedPreviewController.prototype._renderCurrentAnimationFrame = function (delta) { var index = Math.floor(this.elapsedTime / (1000/this.fps)); if (index != this.currentIndex) { this.currentIndex = index; @@ -112,9 +139,8 @@ */ ns.AnimatedPreviewController.prototype.calculateZoom_ = function () { var frame = this.piskelController.getCurrentFrame(); - var previewSize = 200, - hZoom = previewSize / frame.getHeight(), - wZoom = previewSize / frame.getWidth(); + var hZoom = PREVIEW_SIZE / frame.getHeight(), + wZoom = PREVIEW_SIZE / frame.getWidth(); return Math.min(hZoom, wZoom); }; @@ -139,16 +165,21 @@ width = frame.getWidth() * zoom; } - containerEl.style.height = height + "px"; - containerEl.style.width = width + "px"; + containerEl.style.height = height + 'px'; + containerEl.style.width = width + 'px'; var horizontalPadding = (PREVIEW_SIZE - height) / 2; - containerEl.style.marginTop = horizontalPadding + "px"; - containerEl.style.marginBottom = horizontalPadding + "px"; + containerEl.style.marginTop = horizontalPadding + 'px'; + containerEl.style.marginBottom = horizontalPadding + 'px'; var verticalPadding = (PREVIEW_SIZE - width) / 2; - containerEl.style.marginLeft = verticalPadding + "px"; - containerEl.style.marginRight = verticalPadding + "px"; + containerEl.style.marginLeft = verticalPadding + 'px'; + containerEl.style.marginRight = verticalPadding + 'px'; + }; + + ns.AnimatedPreviewController.prototype.setRenderFlag_ = function (bool) { + console.log('setRenderFlag_', bool); + this.renderFlag = bool; }; ns.AnimatedPreviewController.prototype.toggleOnionSkin_ = function () { diff --git a/src/js/controller/DrawingController.js b/src/js/controller/DrawingController.js index f8fa153f..f6ed0653 100644 --- a/src/js/controller/DrawingController.js +++ b/src/js/controller/DrawingController.js @@ -31,8 +31,6 @@ "yOffset" : 0 }; - console.log('DrawingController:getContainerWidth_', this.getContainerWidth_()); - this.overlayRenderer = new pskl.rendering.frame.CachedFrameRenderer(this.container, renderingOptions, ["canvas-overlay"]); this.renderer = new pskl.rendering.frame.CachedFrameRenderer(this.container, renderingOptions, ["drawing-canvas"]); this.onionSkinRenderer = new pskl.rendering.OnionSkinRenderer(this.container, renderingOptions, piskelController); @@ -153,7 +151,7 @@ this.dragHandler.startDrag(event.clientX, event.clientY); } else { this.currentToolBehavior.hideHighlightedPixel(this.overlayFrame); - + $.publish(Events.TOOL_PRESSED); this.currentToolBehavior.applyToolAt( coords.x, coords.y, diff --git a/src/js/model/frame/CachedFrameProcessor.js b/src/js/model/frame/CachedFrameProcessor.js index 4f884b91..7e20b9d4 100644 --- a/src/js/model/frame/CachedFrameProcessor.js +++ b/src/js/model/frame/CachedFrameProcessor.js @@ -65,7 +65,7 @@ if (cache[cacheKey]) { processedFrame = cache[cacheKey]; } else { - var frameAsString = JSON.stringify(frame.getPixels()); + var frameAsString = pskl.utils.hashCode(JSON.stringify(frame.getPixels())); if (cache[frameAsString]) { processedFrame = this.outputCloner(cache[frameAsString], frame); } else { diff --git a/src/js/rendering/frame/FrameRenderer.js b/src/js/rendering/frame/FrameRenderer.js index ee746b75..9a0eaa74 100644 --- a/src/js/rendering/frame/FrameRenderer.js +++ b/src/js/rendering/frame/FrameRenderer.js @@ -164,13 +164,6 @@ } }; - ns.FrameRenderer.prototype.renderPixel_ = function (color, x, y, context) { - if(color != Constants.TRANSPARENT_COLOR) { - context.fillStyle = color; - context.fillRect(x, y, 1, 1); - } - }; - /** * Transform a screen pixel-based coordinate (relative to the top-left corner of the rendered * frame) into a sprite coordinate in column and row. @@ -264,4 +257,11 @@ } displayContext.restore(); }; + + ns.FrameRenderer.prototype.renderPixel_ = function (color, x, y, context) { + if(color != Constants.TRANSPARENT_COLOR) { + context.fillStyle = color; + context.fillRect(x, y, 1, 1); + } + }; })(); \ No newline at end of file diff --git a/src/js/utils/core.js b/src/js/utils/core.js index 2fd92c14..af71c3de 100644 --- a/src/js/utils/core.js +++ b/src/js/utils/core.js @@ -89,6 +89,18 @@ if (!Function.prototype.bind) { } }; + ns.hashCode = function(str) { + var hash = 0; + if (str.length !== 0) { + for (var i = 0, l = str.length; i < l; i++) { + var chr = str.charCodeAt(i); + hash = ((hash << 5) - hash) + chr; + hash |= 0; // Convert to 32bit integer + } + } + return hash; + }; + var entityMap = { "&": "&", "<": "<", @@ -97,7 +109,6 @@ if (!Function.prototype.bind) { "'": ''', "/": '/' }; - ns.escapeHtml= function (string) { return String(string).replace(/[&<>"'\/]/g, function (s) { return entityMap[s]; diff --git a/src/templates/preview.html b/src/templates/preview.html index b0bfe93e..7c01cca4 100644 --- a/src/templates/preview.html +++ b/src/templates/preview.html @@ -10,6 +10,6 @@ data-placement="bottom" class="piskel-icon-onion preview-toggle-onion-skin">
- +
\ No newline at end of file From 0642e17aa8d2e1fa72a648ea836bf91b6b144b6c Mon Sep 17 00:00:00 2001 From: jdescottes Date: Fri, 19 Dec 2014 08:28:15 +0100 Subject: [PATCH 4/7] Draw lines of pixels instead of single pixels --- .../controller/AnimatedPreviewController.js | 1 - src/js/rendering/CanvasRenderer.js | 22 ++++++++++++++++--- src/js/rendering/frame/FrameRenderer.js | 14 +++++++++++- 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/src/js/controller/AnimatedPreviewController.js b/src/js/controller/AnimatedPreviewController.js index 9d5f2eb4..2ada9fad 100644 --- a/src/js/controller/AnimatedPreviewController.js +++ b/src/js/controller/AnimatedPreviewController.js @@ -178,7 +178,6 @@ }; ns.AnimatedPreviewController.prototype.setRenderFlag_ = function (bool) { - console.log('setRenderFlag_', bool); this.renderFlag = bool; }; diff --git a/src/js/rendering/CanvasRenderer.js b/src/js/rendering/CanvasRenderer.js index c124c73b..3108419e 100644 --- a/src/js/rendering/CanvasRenderer.js +++ b/src/js/rendering/CanvasRenderer.js @@ -19,9 +19,18 @@ ns.CanvasRenderer.prototype.render = function () { var canvas = this.createCanvas_(); var context = canvas.getContext('2d'); - this.frame.forEachPixel(function (color, x, y) { - this.renderPixel_(color, x, y, context); - }.bind(this)); + + for(var x = 0, width = this.frame.getWidth(); x < width; x++) { + for(var y = 0, height = this.frame.getHeight(); y < height; y++) { + var color = this.frame.getPixel(x, y); + var w = 1; + while (color === this.frame.getPixel(x, y+w)) { + w++; + } + this.renderLine_(color, x, y, w, context); + y = y + w - 1; + } + } var scaledCanvas = this.createCanvas_(this.zoom); var scaledContext = scaledCanvas.getContext('2d'); @@ -40,6 +49,13 @@ context.fillRect(x, y, 1, 1); }; + ns.CanvasRenderer.prototype.renderLine_ = function (color, x, y, width, context) { + if(color != Constants.TRANSPARENT_COLOR) { + context.fillStyle = color; + context.fillRect(x, y, 1, width); + } + }; + ns.CanvasRenderer.prototype.createCanvas_ = function (zoom) { zoom = zoom || 1; var width = this.frame.getWidth() * zoom; diff --git a/src/js/rendering/frame/FrameRenderer.js b/src/js/rendering/frame/FrameRenderer.js index 9a0eaa74..d8291a5f 100644 --- a/src/js/rendering/frame/FrameRenderer.js +++ b/src/js/rendering/frame/FrameRenderer.js @@ -223,7 +223,12 @@ for(var x = 0, width = frame.getWidth(); x < width; x++) { for(var y = 0, height = frame.getHeight(); y < height; y++) { var color = frame.getPixel(x, y); - this.renderPixel_(color, x, y, context); + var w = 1; + while (color === frame.getPixel(x, y+w)) { + w++; + } + this.renderLine_(color, x, y, w, context); + y = y + w - 1; } } @@ -264,4 +269,11 @@ context.fillRect(x, y, 1, 1); } }; + + ns.FrameRenderer.prototype.renderLine_ = function (color, x, y, width, context) { + if(color != Constants.TRANSPARENT_COLOR) { + context.fillStyle = color; + context.fillRect(x, y, 1, width); + } + }; })(); \ No newline at end of file From df5aef363b3e9ae942fbf1a284c1091cee68c29a Mon Sep 17 00:00:00 2001 From: jdescottes Date: Sun, 21 Dec 2014 16:44:10 +0100 Subject: [PATCH 5/7] Move to imgstore-b, change body bg --- src/css/style.css | 6 +---- src/js/Constants.js | 31 +++++------------------ src/js/service/AppEngineStorageService.js | 2 +- 3 files changed, 8 insertions(+), 31 deletions(-) diff --git a/src/css/style.css b/src/css/style.css index d1aa309d..37b0da2e 100644 --- a/src/css/style.css +++ b/src/css/style.css @@ -1,9 +1,5 @@ body { - background: radial-gradient(circle, #000, #373737); - /* 16/06/2013 : -webkit still needed for - safari, safari mobile and android browser and chrome for android - cf http://caniuse.com/css-gradients */ - background: -webkit-radial-gradient(circle, #000, #373737); + background: #1D1D1D; } /* Browser fixes */ diff --git a/src/js/Constants.js b/src/js/Constants.js index 0ce59ab4..d8480e79 100644 --- a/src/js/Constants.js +++ b/src/js/Constants.js @@ -21,16 +21,8 @@ var Constants = { DEFAULT_PEN_COLOR : '#000000', TRANSPARENT_COLOR : 'rgba(0, 0, 0, 0)', - OVERLAY_ONION_SKIN : 'onion-skin', - OVERLAY_LAYER_PREVIEW : 'layer-preview', - OVERLAY_DISABLED : 'no-overlay', - - NO_PALETTE_ID : '__no-palette', CURRENT_COLORS_PALETTE_ID : '__current-colors', - // Used for Spectrum input - PREFERRED_COLOR_FORMAT : 'rgb', - /* * Fake semi-transparent color used to highlight transparent * strokes and rectangles: @@ -43,22 +35,6 @@ var Constants = { */ TOOL_TARGET_HIGHLIGHT_COLOR: 'rgba(255, 255, 255, 0.2)', - /* - * Default entry point for piskel web service: - */ - STATIC : { - URL : { - SAVE : 'http://3.piskel-app.appspot.com/store', - GET : 'http://3.piskel-app.appspot.com/get' - } - }, - APPENGINE : { - URL : { - SAVE : 'save' - } - }, - IMAGE_SERVICE_UPLOAD_URL : 'http://piskel-imgstore-a.appspot.com/__/upload', - IMAGE_SERVICE_GET_URL : 'http://piskel-imgstore-a.appspot.com/img/', ZOOMED_OUT_BACKGROUND_COLOR : '#A0A0A0', @@ -71,5 +47,10 @@ var Constants = { EMPTY_FUNCTION : function () {}, // TESTS - DRAWING_TEST_FOLDER : 'drawing' + DRAWING_TEST_FOLDER : 'drawing', + + // SERVICE URLS + APPENGINE_SAVE_URL : 'save', + IMAGE_SERVICE_UPLOAD_URL : 'http://piskel-imgstore-b.appspot.com/__/upload', + IMAGE_SERVICE_GET_URL : 'http://piskel-imgstore-b.appspot.com/img/' }; \ No newline at end of file diff --git a/src/js/service/AppEngineStorageService.js b/src/js/service/AppEngineStorageService.js index 4d377139..4e51c08e 100644 --- a/src/js/service/AppEngineStorageService.js +++ b/src/js/service/AppEngineStorageService.js @@ -35,6 +35,6 @@ callbacks.after(); }; - pskl.utils.Xhr.post(Constants.APPENGINE.URL.SAVE, data, success, error); + pskl.utils.Xhr.post(Constants.APPENGINE_SAVE_URL, data, success, error); }; })(); \ No newline at end of file From 123ea31191fb2ce241c880c2a2e4af993129ea0f Mon Sep 17 00:00:00 2001 From: jdescottes Date: Sun, 21 Dec 2014 18:38:14 +0100 Subject: [PATCH 6/7] Cleanup minimapController, center previewFilm canvas --- .../controller/AnimatedPreviewController.js | 12 +- src/js/controller/MinimapController.js | 126 +++++++++++------- src/js/controller/PreviewFilmController.js | 15 ++- 3 files changed, 94 insertions(+), 59 deletions(-) diff --git a/src/js/controller/AnimatedPreviewController.js b/src/js/controller/AnimatedPreviewController.js index 2ada9fad..7045aa19 100644 --- a/src/js/controller/AnimatedPreviewController.js +++ b/src/js/controller/AnimatedPreviewController.js @@ -168,13 +168,13 @@ containerEl.style.height = height + 'px'; containerEl.style.width = width + 'px'; - var horizontalPadding = (PREVIEW_SIZE - height) / 2; - containerEl.style.marginTop = horizontalPadding + 'px'; - containerEl.style.marginBottom = horizontalPadding + 'px'; + var horizontalMargin = (PREVIEW_SIZE - height) / 2; + containerEl.style.marginTop = horizontalMargin + 'px'; + containerEl.style.marginBottom = horizontalMargin + 'px'; - var verticalPadding = (PREVIEW_SIZE - width) / 2; - containerEl.style.marginLeft = verticalPadding + 'px'; - containerEl.style.marginRight = verticalPadding + 'px'; + var verticalMargin = (PREVIEW_SIZE - width) / 2; + containerEl.style.marginLeft = verticalMargin + 'px'; + containerEl.style.marginRight = verticalMargin + 'px'; }; ns.AnimatedPreviewController.prototype.setRenderFlag_ = function (bool) { diff --git a/src/js/controller/MinimapController.js b/src/js/controller/MinimapController.js index e64e6149..97e2c269 100644 --- a/src/js/controller/MinimapController.js +++ b/src/js/controller/MinimapController.js @@ -8,14 +8,15 @@ this.container = container; this.isClicked = false; + this.isVisible = false; }; ns.MinimapController.prototype.init = function () { // Create minimap DOM elements - this.cropFrame = document.createElement('DIV'); - this.cropFrame.className = 'minimap-crop-frame'; - this.cropFrame.style.display = 'none'; - $(this.container).append(this.cropFrame); + this.minimapEl = document.createElement('DIV'); + this.minimapEl.className = 'minimap-crop-frame'; + this.minimapEl.style.display = 'none'; + $(this.container).append(this.minimapEl); // Init mouse events $(this.container).mousedown(this.onMinimapMousedown_.bind(this)); @@ -26,56 +27,81 @@ }; ns.MinimapController.prototype.renderMinimap_ = function () { - var zoomRatio = this.getDrawingAreaZoomRatio_(); - if (zoomRatio > 1) { - this.displayCropFrame_(zoomRatio, this.drawingController.getRenderer().getOffset()); + var verticalRatio = this.getVerticalRatio_(); + var horizontalRatio = this.getHorizontalRatio_(); + if (verticalRatio > 1 || horizontalRatio > 1) { + this.displayMinimap_(); } else { - this.hideCropFrame_(); + this.hideMinimap_(); } }; - ns.MinimapController.prototype.displayCropFrame_ = function (ratio, offset) { - this.cropFrame.style.display = 'block'; + ns.MinimapController.prototype.displayMinimap_ = function () { + var minimapSize = this.getMinimapSize_(); + var previewSize = this.getPreviewSize_(); var containerHeight = this.container.height(); var containerWidth = this.container.width(); - var displaySize = this.drawingController.getRenderer().getDisplaySize(); - var width = displaySize.width / ratio; - var height = displaySize.height / ratio; - this.cropFrame.style.width = Math.min(width, containerWidth) + 'px'; - this.cropFrame.style.height = Math.min(height, containerHeight) + 'px'; + // offset(x, y) in frame pixels + var offset = this.drawingController.getRenderer().getOffset(); - var containerSize = Math.max(containerHeight, containerWidth); - var margin = this.drawingController.renderer.margin; + // the preview is centered in a square container + // if the sprite is not a square, a margin is needed on the appropriate coordinate + // before adding the offset coming from the drawing area + var leftMargin = (containerWidth - Math.max(minimapSize.width, previewSize.width))/2; + var leftOffset = offset.x * this.animationController.getZoom(); + var left = leftMargin + leftOffset; - var frame = this.piskelController.getCurrentFrame(); - var framePreviewWidth = frame.getWidth() * this.animationController.getZoom(); - var framePreviewHeight = frame.getHeight() * this.animationController.getZoom(); - - var left = (containerSize - Math.max(width, framePreviewWidth))/2; - left += offset.x * this.animationController.getZoom(); - left = Math.max(0, left); - this.cropFrame.style.left = left + 'px'; - - var top = (containerSize - Math.max(height, framePreviewHeight))/2; - top += offset.y * this.animationController.getZoom(); - top = Math.max(0, top); - this.cropFrame.style.top = top + 'px'; + var topMargin = (containerHeight - Math.max(minimapSize.height, previewSize.height))/2; + var topOffset = offset.y * this.animationController.getZoom(); + var top = topMargin + topOffset; + this.minimapEl.style.display = 'block'; + this.minimapEl.style.width = Math.min(minimapSize.width, containerWidth) + 'px'; + this.minimapEl.style.height = Math.min(minimapSize.height, containerHeight) + 'px'; + this.minimapEl.style.left = Math.max(0, left) + 'px'; + this.minimapEl.style.top = Math.max(0, top) + 'px'; + this.isVisible = true; }; - ns.MinimapController.prototype.hideCropFrame_ = function () { - this.cropFrame.style.display = 'none'; + ns.MinimapController.prototype.getMinimapSize_ = function () { + // Calculate the ratio to translate drawing area sizes to animated preview sizes + var drawingAreaZoom = this.drawingController.getRenderer().getZoom(); + var animatedPreviewZoom = this.animationController.getZoom(); + var ratio = drawingAreaZoom / animatedPreviewZoom; + + var displaySize = this.drawingController.getRenderer().getDisplaySize(); + var minimapWidth = displaySize.width / ratio; + var minimapHeight = displaySize.height / ratio; + + return { + width : minimapWidth, + height: minimapHeight + }; + }; + + ns.MinimapController.prototype.getPreviewSize_ = function () { + var frame = this.piskelController.getCurrentFrame(); + var previewWidth = frame.getWidth() * this.animationController.getZoom(); + var previewHeight = frame.getHeight() * this.animationController.getZoom(); + + return { + width : previewWidth, + height: previewHeight + }; + }; + + ns.MinimapController.prototype.hideMinimap_ = function () { + this.minimapEl.style.display = 'none'; + this.isVisible = false; }; ns.MinimapController.prototype.onMinimapMousemove_ = function (evt) { - if (this.isClicked) { - if (this.getDrawingAreaZoomRatio_() > 1) { - var coords = this.getCoordinatesCenteredAround_(evt.clientX, evt.clientY); - this.drawingController.setOffset(coords.x, coords.y); - } + if (this.isVisible && this.isClicked) { + var coords = this.getCoordinatesCenteredAround_(evt.clientX, evt.clientY); + this.drawingController.setOffset(coords.x, coords.y); } }; @@ -89,12 +115,12 @@ ns.MinimapController.prototype.getCoordinatesCenteredAround_ = function (x, y) { var frameCoords = this.animationController.getCoordinates(x, y); - var zoomRatio = this.getDrawingAreaZoomRatio_(); + var frameWidth = this.piskelController.getCurrentFrame().getWidth(); var frameHeight = this.piskelController.getCurrentFrame().getHeight(); - var width = frameWidth / zoomRatio; - var height = frameHeight / zoomRatio; + var width = frameWidth / this.getHorizontalRatio_(); + var height = frameHeight / this.getVerticalRatio_(); return { x : frameCoords.x - (width/2), @@ -102,19 +128,21 @@ }; }; - ns.MinimapController.prototype.getDrawingAreaZoomRatio_ = function () { + ns.MinimapController.prototype.getVerticalRatio_ = function () { var drawingAreaZoom = this.drawingController.getRenderer().getZoom(); var frame = this.piskelController.getCurrentFrame(); - var dim = Math.max(frame.getHeight(), frame.getWidth()); - var drawingAreaSize = dim * drawingAreaZoom; + var frameTotalHeight = frame.getHeight() * drawingAreaZoom; + var frameDisplayHeight = this.drawingController.getRenderer().getDisplaySize().height; - var containerHeight = this.container.height(); - var containerWidth = this.container.width(); + return frameTotalHeight / frameDisplayHeight; + }; - var containerSize = Math.max(containerHeight, containerWidth); + ns.MinimapController.prototype.getHorizontalRatio_ = function () { + var drawingAreaZoom = this.drawingController.getRenderer().getZoom(); + var frame = this.piskelController.getCurrentFrame(); + var frameTotalWidth = frame.getWidth() * drawingAreaZoom; + var frameDisplayWidth = this.drawingController.getRenderer().getDisplaySize().width; - var zoomRatio = drawingAreaSize / containerSize; - - return zoomRatio; + return frameTotalWidth / frameDisplayWidth; }; })(); \ No newline at end of file diff --git a/src/js/controller/PreviewFilmController.js b/src/js/controller/PreviewFilmController.js index 0571c02e..47777a1d 100644 --- a/src/js/controller/PreviewFilmController.js +++ b/src/js/controller/PreviewFilmController.js @@ -162,6 +162,14 @@ var canvasContainer = document.createElement("div"); canvasContainer.classList.add("canvas-container", pskl.UserSettings.get(pskl.UserSettings.CANVAS_BACKGROUND)); + var height = this.zoom * this.piskelController.getCurrentFrame().getHeight(); + var horizontalMargin = (Constants.PREVIEW_FILM_SIZE - height) / 2; + canvasContainer.style.marginTop = horizontalMargin + 'px'; + + var width = this.zoom * this.piskelController.getCurrentFrame().getWidth(); + var verticalMargin = (Constants.PREVIEW_FILM_SIZE - width) / 2; + canvasContainer.style.marginLeft = verticalMargin + 'px'; + var canvasBackground = document.createElement("div"); canvasBackground.className = "canvas-background"; @@ -227,10 +235,9 @@ * Calculate the preview zoom depending on the piskel size */ ns.PreviewFilmController.prototype.calculateZoom_ = function () { - var curFrame = this.piskelController.getCurrentFrame(), - frameHeight = curFrame.getHeight(), - frameWidth = curFrame.getWidth(); + var frame = this.piskelController.getCurrentFrame(); + var frameSize = Math.max(frame.getHeight(), frame.getWidth()); - return Math.min(Constants.PREVIEW_FILM_SIZE/frameHeight, Constants.PREVIEW_FILM_SIZE/frameWidth); + return Constants.PREVIEW_FILM_SIZE/frameSize; }; })(); \ No newline at end of file From 5cb1d0cd030291dcb3d99108dd148c720196d1be Mon Sep 17 00:00:00 2001 From: jdescottes Date: Sun, 21 Dec 2014 18:56:40 +0100 Subject: [PATCH 7/7] Fixed CanvasRenderer regression + added unit test --- src/js/rendering/CanvasRenderer.js | 7 +++-- test/js/rendering/CanvasRendererTest.js | 39 +++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 3 deletions(-) create mode 100644 test/js/rendering/CanvasRendererTest.js diff --git a/src/js/rendering/CanvasRenderer.js b/src/js/rendering/CanvasRenderer.js index 3108419e..7196cdea 100644 --- a/src/js/rendering/CanvasRenderer.js +++ b/src/js/rendering/CanvasRenderer.js @@ -50,10 +50,11 @@ }; ns.CanvasRenderer.prototype.renderLine_ = function (color, x, y, width, context) { - if(color != Constants.TRANSPARENT_COLOR) { - context.fillStyle = color; - context.fillRect(x, y, 1, width); + if(color == Constants.TRANSPARENT_COLOR) { + color = this.transparentColor_; } + context.fillStyle = color; + context.fillRect(x, y, 1, width); }; ns.CanvasRenderer.prototype.createCanvas_ = function (zoom) { diff --git a/test/js/rendering/CanvasRendererTest.js b/test/js/rendering/CanvasRendererTest.js new file mode 100644 index 00000000..043be14b --- /dev/null +++ b/test/js/rendering/CanvasRendererTest.js @@ -0,0 +1,39 @@ +describe("Canvas Renderer test", function() { + var BLACK = '#000000'; + var WHITE = '#ffffff'; + var TRANS = Constants.TRANSPARENT_COLOR; + + var toFrameGrid = function (normalGrid) { + var frameGrid = []; + var w = normalGrid[0].length; + var h = normalGrid.length; + for (var x = 0 ; x < w ; x++) { + frameGrid[x] = []; + for (var y = 0 ; y < h ; y++) { + frameGrid[x][y] = normalGrid[y][x]; + } + } + return frameGrid; + }; + + beforeEach(function() {}); + afterEach(function() {}); + + it("draws transparent as white by default", function() { + // create frame + var frame = pskl.model.Frame.fromPixelGrid(toFrameGrid([ + [BLACK, TRANS], + [TRANS, BLACK] + ])); + + var renderer = new pskl.rendering.CanvasRenderer(frame, 1); + var canvas = renderer.render(); + + var frameFromCanvas = pskl.utils.FrameUtils.createFromImage(canvas); + + expect(frameFromCanvas.getPixel(0,0)).toBe(BLACK); + expect(frameFromCanvas.getPixel(0,1)).toBe(WHITE); + expect(frameFromCanvas.getPixel(1,0)).toBe(WHITE); + expect(frameFromCanvas.getPixel(1,1)).toBe(BLACK); + }); +}); \ No newline at end of file