Feature : add onion skin option

New option in application settings : onion skin.
You can choose the overlay to display now :
- no overlay
- onion skin (default)
- layer preview (previous default)

Available in Application Settings panel.

Only one overlay can be used at the same time.
The onion skin overlay is driven by a new OnionSkinRenderer maanged by the
drawing.

The drawing controller is responsible for instanciating and 'choosing' the
overlay renderer.

When switching to a new overlay, other overlays are cleared and flushed
(they cache their rendering frame, flush empties the cache).

NB : flush is only available on LayersRenderer and OnionSkinRenderer for
now.
This commit is contained in:
jdescottes 2014-06-19 23:33:57 +02:00
parent fbb5ccc7e2
commit 98f59fecf1
9 changed files with 129 additions and 13 deletions

View File

@ -136,12 +136,14 @@ body {
background: url(../img/canvas_background/lowcont_dark_canvas_background.png) repeat;
}
.layers-canvas {
.layers-canvas,
.canvas.onion-skin-canvas {
opacity: 0.2;
}
.canvas.canvas-overlay,
.canvas.layers-canvas {
.canvas.layers-canvas,
.canvas.onion-skin-canvas {
position: absolute;
top: 0;
left: 0;
@ -157,7 +159,8 @@ body {
.canvas.layers-below-canvas {z-index: 7;}
.canvas.drawing-canvas {z-index: 8;}
.canvas.layers-above-canvas {z-index: 9;}
.canvas.canvas-overlay {z-index: 10;}
.canvas.onion-skin-canvas {z-index: 10;}
.canvas.canvas-overlay {z-index: 11;}
/**
* Animated preview styles.

View File

@ -19,6 +19,10 @@ 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',
MANAGE_PALETTE_ID : '__manage-palettes',

View File

@ -32,13 +32,15 @@
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);
this.layersRenderer = new pskl.rendering.layer.LayersRenderer(this.container, renderingOptions, piskelController);
this.compositeRenderer = new pskl.rendering.CompositeRenderer();
this.compositeRenderer
.add(this.overlayRenderer)
.add(this.renderer)
.add(this.layersRenderer);
.add(this.layersRenderer)
.add(this.onionSkinRenderer);
// State of drawing controller:
this.isClicked = false;
@ -111,6 +113,12 @@
ns.DrawingController.prototype.onUserSettingsChange_ = function (evt, settingsName, settingsValue) {
if(settingsName == pskl.UserSettings.SHOW_GRID) {
console.warn('DrawingController:onUserSettingsChange_ not implemented !');
} else if (settingsName == pskl.UserSettings.OVERLAY) {
this.onionSkinRenderer.clear();
this.onionSkinRenderer.flush();
this.layersRenderer.clear();
this.layersRenderer.flush();
this.render();
}
};
@ -311,7 +319,13 @@
this.overlayFrame = pskl.model.Frame.createEmptyFromFrame(currentFrame);
}
this.layersRenderer.render();
var overlaySetting = pskl.UserSettings.get(pskl.UserSettings.OVERLAY);
if (overlaySetting === Constants.OVERLAY_ONION_SKIN) {
this.onionSkinRenderer.render();
} else if (overlaySetting === Constants.OVERLAY_LAYER_PREVIEW) {
this.layersRenderer.render();
}
this.renderer.render(currentFrame);
this.overlayRenderer.render(this.overlayFrame);
};

View File

@ -18,9 +18,9 @@
$('#grid-width').val(gridWidth);
$('#grid-width').change(this.onGridWidthChange.bind(this));
var tiledPreview = pskl.UserSettings.get(pskl.UserSettings.TILED_PREVIEW);
$('#tiled-preview').prop('checked', tiledPreview);
$('#tiled-preview').change(this.onTiledPreviewChange.bind(this));
var overlay = pskl.UserSettings.get(pskl.UserSettings.OVERLAY);
$('#overlay').val(overlay);
$('#overlay').change(this.onOverlayChange.bind(this));
// Handle canvas background changes:
$('#background-picker-wrapper').click(this.onBackgroundClick.bind(this));
@ -31,9 +31,9 @@
pskl.UserSettings.set(pskl.UserSettings.GRID_WIDTH, parseInt(width, 10));
};
ns.ApplicationSettingsController.prototype.onTiledPreviewChange = function (evt) {
var checked = $('#tiled-preview').prop('checked');
pskl.UserSettings.set(pskl.UserSettings.TILED_PREVIEW, checked);
ns.ApplicationSettingsController.prototype.onOverlayChange = function (evt) {
var overlay = $('#overlay').val();
pskl.UserSettings.set(pskl.UserSettings.OVERLAY, overlay);
};
ns.ApplicationSettingsController.prototype.onBackgroundClick = function (evt) {

View File

@ -0,0 +1,77 @@
(function () {
var ns = $.namespace('pskl.rendering');
ns.OnionSkinRenderer = function (container, renderingOptions, piskelController) {
pskl.rendering.CompositeRenderer.call(this);
this.piskelController = piskelController;
// Do not use CachedFrameRenderers here, since the caching will be performed in the render method of LayersRenderer
this.renderer = new pskl.rendering.frame.FrameRenderer(container, renderingOptions, ["onion-skin-canvas"]);
this.add(this.renderer);
this.serializedRendering = '';
};
pskl.utils.inherit(pskl.rendering.OnionSkinRenderer, pskl.rendering.CompositeRenderer);
ns.OnionSkinRenderer.prototype.render = function () {
var offset = this.getOffset();
var size = this.getDisplaySize();
var layers = this.piskelController.getLayers();
var currentFrameIndex = this.piskelController.getCurrentFrameIndex();
var frames = [];
this.addFrameAtIndexToArray_(currentFrameIndex - 1, frames);
this.addFrameAtIndexToArray_(currentFrameIndex + 1, frames);
var serializedRendering = [
this.getZoom(),
this.getGridWidth(),
offset.x,
offset.y,
size.width,
size.height,
frames.map(function (f) {
return f.getHash();
}).join('-'),
layers.length
].join("-");
if (this.serializedRendering != serializedRendering) {
this.serializedRendering = serializedRendering;
if (frames.length > 0) {
this.clear();
var mergedFrame = pskl.utils.FrameUtils.merge(frames);
this.renderer.render(mergedFrame);
}
}
};
ns.OnionSkinRenderer.prototype.addFrameAtIndexToArray_ = function (frameIndex, frames) {
var layer = this.piskelController.getCurrentLayer();
if (this.piskelController.hasFrameAt(frameIndex)) {
frames.push(layer.getFrameAt(frameIndex));
}
};
/**
* See @pskl.rendering.frame.CachedFrameRenderer
* Same issue : FrameRenderer setDisplaySize destroys the canvas
* @param {Number} width
* @param {Number} height
*/
ns.OnionSkinRenderer.prototype.setDisplaySize = function (width, height) {
var size = this.getDisplaySize();
if (size.width !== width || size.height !== height) {
this.superclass.setDisplaySize.call(this, width, height);
}
};
ns.OnionSkinRenderer.prototype.flush = function () {
this.serializedRendering = '';
};
})();

View File

@ -58,7 +58,6 @@
}
};
/**
* See @pskl.rendering.frame.CachedFrameRenderer
* Same issue : FrameRenderer setDisplaySize destroys the canvas
@ -78,4 +77,8 @@
});
return pskl.utils.FrameUtils.merge(frames);
};
ns.LayersRenderer.prototype.flush = function () {
this.serializedRendering = '';
};
})();

View File

@ -6,12 +6,14 @@
CANVAS_BACKGROUND : 'CANVAS_BACKGROUND',
SELECTED_PALETTE : 'SELECTED_PALETTE',
TILED_PREVIEW : 'TILED_PREVIEW',
OVERLAY : 'OVERLAY',
KEY_TO_DEFAULT_VALUE_MAP_ : {
'GRID_WIDTH' : 0,
'CANVAS_BACKGROUND' : 'lowcont-dark-canvas-background',
'SELECTED_PALETTE' : Constants.CURRENT_COLORS_PALETTE_ID,
'TILED_PREVIEW' : false
'TILED_PREVIEW' : false,
'OVERLAY' : Constants.OVERLAY_ONION_SKIN
},
/**

View File

@ -60,6 +60,7 @@
"js/rendering/CompositeRenderer.js",
"js/rendering/layer/LayersRenderer.js",
"js/rendering/frame/FrameRenderer.js",
"js/rendering/OnionSkinRenderer.js",
"js/rendering/frame/TiledFrameRenderer.js",
"js/rendering/frame/CachedFrameRenderer.js",
"js/rendering/CanvasRenderer.js",

View File

@ -29,6 +29,18 @@
<option value="4">Enabled - 4px wide</option>
</select>
</div>
<div class="settings-title">
Overlay:
</div>
<div class="settings-item">
<label for="overlay">Display :</label>
<select id="overlay">
<option value="no-overlay">No overlay</option>
<option value="onion-skin">Onion skin</option>
<option value="layer-preview">Layer preview</option>
</select>
</div>
<!-- <div class="settings-item">
<label for="tiled-preview">Display tiled preview :</label>
<input type="checkbox" value="1" id="tiled-preview" name="tiled-preview-checkbox"/>