zoom preview 3

This commit is contained in:
jdescottes 2013-11-02 10:19:38 +01:00
parent 2a2cf7190f
commit 9840b54f54
27 changed files with 929 additions and 87 deletions

File diff suppressed because one or more lines are too long

View File

@ -11883,6 +11883,9 @@ var Events = {
*/
USER_SETTINGS_CHANGED: "USER_SETTINGS_CHANGED",
/* Listened to by SettingsController */
CLOSE_SETTINGS_DRAWER : "CLOSE_SETTINGS_DRAWER",
/**
* The framesheet was reseted and is now probably drastically different.
* Number of frames, content of frames, color used for the palette may have changed.
@ -12002,6 +12005,18 @@ if (typeof Function.prototype.bind !== "function") {
};
})();;(function () {
var ns = $.namespace('pskl.utils');
ns.FileUtils = {
readFile : function (file, callback) {
var reader = new FileReader();
reader.onload = function(event){
callback(event.target.result);
};
reader.readAsDataURL(file);
}
};
})();;(function () {
var ns = $.namespace('pskl.utils');
var colorCache = {};
ns.FrameUtils = {
merge : function (frames) {
@ -12093,10 +12108,95 @@ if (typeof Function.prototype.bind !== "function") {
}
colorCache[c] = color;
return color;
},
/*
* Create a pskl.model.Frame 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) {
var w = image.width,
h = image.height;
var canvas = pskl.CanvasUtils.createCanvas(w, h);
var context = canvas.getContext('2d');
context.drawImage(image, 0,0,w,h,0,0,w,h);
var imgData = context.getImageData(0,0,w,h).data;
// 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){
// 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];
if (a < 125) {
frame[x][y] = "TRANSPARENT";
} else {
frame[x][y] = this.rgbToHex(r,g,b);
}
}
}
return frame;
},
/**
* Convert a rgb(Number, Number, Number) color to hexadecimal representation
* @param {Number} r red value, between 0 and 255
* @param {Number} g green value, between 0 and 255
* @param {Number} b blue value, between 0 and 255
* @return {String} hex representation of the color '#ABCDEF'
*/
rgbToHex : function (r, g, b) {
return "#" + this.componentToHex(r) + this.componentToHex(g) + this.componentToHex(b);
},
/**
* Convert a color component (as a Number between 0 and 255) to its string hexa representation
* @param {Number} c component value, between 0 and 255
* @return {String} eg. '0A'
*/
componentToHex : function (c) {
var hex = c.toString(16);
return hex.length == 1 ? "0" + hex : hex;
}
};
})();
;(function () {
var ns = $.namespace('pskl.utils');
ns.ImageResizer = {
resize : function (image, targetWidth, targetHeight, smoothingEnabled) {
var canvas = pskl.CanvasUtils.createCanvas(targetWidth, targetHeight);
var context = canvas.getContext('2d');
context.save();
if (!smoothingEnabled) {
this.disableSmoothingOnContext(context);
}
context.translate(canvas.width / 2, canvas.height / 2);
context.scale(targetWidth / image.width, targetHeight / image.height);
context.drawImage(image, -image.width / 2, -image.height / 2);
context.restore();
return canvas;
},
disableSmoothingOnContext : function (context) {
context.imageSmoothingEnabled = false;
context.mozImageSmoothingEnabled = false;
context.oImageSmoothingEnabled = false;
context.webkitImageSmoothingEnabled = false;
context.msImageSmoothingEnabled = false;
}
};
})();;(function () {
var ns = $.namespace("pskl");
ns.PixelUtils = {
@ -12360,9 +12460,9 @@ if (typeof Function.prototype.bind !== "function") {
};
})();
;(function () {
var ns = $.namespace("pskl");
var ns = $.namespace("pskl.utils");
ns.utils.Template = {
ns.Template = {
get : function (templateId) {
var template = document.getElementById(templateId);
if (template) {
@ -14289,7 +14389,7 @@ var jscolor = {
* @private
*/
ns.FrameRenderer.prototype.renderFrame_ = function (frame) {
if (!this.canvas) {
if (!this.canvas || frame.getWidth() != this.canvas.width || frame.getHeight() != this.canvas.height) {
this.canvas = pskl.CanvasUtils.createCanvas(frame.getWidth(), frame.getHeight());
}
@ -14737,6 +14837,11 @@ var jscolor = {
}
},
ns.DrawingController.prototype.onFrameSizeChanged_ = function () {
this.compositeRenderer.setZoom(this.calculateZoom_());
this.compositeRenderer.setDisplaySize(this.getContainerWidth_(), this.getContainerHeight_());
};
/**
* @private
*/
@ -15238,10 +15343,11 @@ var jscolor = {
this.setFPS(Constants.DEFAULT.FPS);
var zoom = this.calculateZoom_();
var frame = this.piskelController.getCurrentFrame();
var renderingOptions = {
"zoom": zoom,
"height" : this.piskelController.getCurrentFrame().getHeight() * zoom,
"width" : this.piskelController.getCurrentFrame().getWidth() * zoom
"height" : frame.getHeight() * zoom,
"width" : frame.getWidth() * zoom
};
this.renderer = new pskl.rendering.frame.FrameRenderer(this.container, renderingOptions);
@ -15286,16 +15392,19 @@ var jscolor = {
* Calculate the preview DPI depending on the framesheet size
*/
ns.AnimatedPreviewController.prototype.calculateZoom_ = function () {
var frame = this.piskelController.getCurrentFrame();
var previewSize = 200,
hZoom = previewSize / this.piskelController.getCurrentFrame().getHeight(),
wZoom = previewSize / this.piskelController.getCurrentFrame().getWidth();
hZoom = previewSize / frame.getHeight(),
wZoom = previewSize / frame.getWidth();
return Math.min(hZoom, wZoom);
};
ns.AnimatedPreviewController.prototype.updateZoom_ = function () {
var frame = this.piskelController.getCurrentFrame();
var zoom = this.calculateZoom_();
this.renderer.setZoom(zoom);
this.renderer.setDisplaySize(frame.getWidth() * zoom, frame.getHeight() * zoom);
};
})();;(function () {
var ns = $.namespace('pskl.controller');
@ -15677,9 +15786,9 @@ var jscolor = {
];
ns.GifExportController.prototype.init = function () {
this.radioTemplate_ = pskl.utils.Template.get("export-gif-radio-template");
this.radioTemplate_ = pskl.utils.Template.get("gif-export-radio-template");
this.previewContainerEl = document.querySelectorAll(".export-gif-preview div")[0];
this.previewContainerEl = document.querySelectorAll(".gif-export-preview")[0];
this.radioGroupEl = document.querySelectorAll(".gif-export-radio-group")[0];
this.uploadForm = $("[name=gif-export-upload-form]");
@ -15709,7 +15818,7 @@ var jscolor = {
};
ns.GifExportController.prototype.updatePreview_ = function (src) {
this.previewContainerEl.innerHTML = "<img style='max-width:240px;' src='"+src+"'/>";
this.previewContainerEl.innerHTML = "<div><img style='max-width:240px;' src='"+src+"'/></div>";
};
ns.GifExportController.prototype.getSelectedDpi_ = function () {
@ -15780,16 +15889,187 @@ var jscolor = {
gif.render();
};
})();;(function () {
var ns = $.namespace("pskl.controller");
var ns = $.namespace('pskl.controller.settings');
var DEFAULT_FILE_STATUS = 'No file selected ...';
var PREVIEW_HEIGHT = 60;
ns.ImportController = function (piskelController) {
this.piskelController = piskelController;
this.importedImage_ = null;
};
ns.ImportController.prototype.init = function () {
this.importForm = $("[name=import-form]");
this.hiddenFileInput = $("[name=file-upload-input]");
this.fileInputButton = $(".file-input-button");
this.fileInputStatus = $(".file-input-status");
this.fileInputStatus.html(DEFAULT_FILE_STATUS);
this.importPreview = $(".import-section-preview");
this.resizeWidth = $("[name=resize-width]");
this.resizeHeight = $("[name=resize-height]");
this.smoothResize = $("[name=smooth-resize-checkbox]");
this.submitButton = $("[name=import-submit]");
this.importForm.submit(this.onImportFormSubmit_.bind(this));
this.hiddenFileInput.change(this.onFileUploadChange_.bind(this));
this.fileInputButton.click(this.onFileInputClick_.bind(this));
this.resizeWidth.keyup(this.onResizeInputKeyUp_.bind(this, 'width'));
this.resizeHeight.keyup(this.onResizeInputKeyUp_.bind(this, 'height'));
};
ns.ImportController.prototype.reset_ = function () {
this.importForm.get(0).reset();
this.fileInputStatus.html(DEFAULT_FILE_STATUS);
$.publish(Events.CLOSE_SETTINGS_DRAWER);
};
ns.ImportController.prototype.onResizeInputKeyUp_ = function (from, evt) {
if (this.importedImage_) {
this.synchronizeResizeFields_(evt.target.value, from);
}
};
ns.ImportController.prototype.synchronizeResizeFields_ = function (value, from) {
value = parseInt(value, 10);
if (isNaN(value)) {
value = 0;
}
var height = this.importedImage_.height, width = this.importedImage_.width;
if (from === 'width') {
this.resizeHeight.val(Math.round(value * height / width));
} else {
this.resizeWidth.val(Math.round(value * width / height));
}
};
ns.ImportController.prototype.onImportFormSubmit_ = function (evt) {
evt.originalEvent.preventDefault();
this.importImageToPiskel_();
};
ns.ImportController.prototype.onFileUploadChange_ = function (evt) {
this.importFromFile_();
};
ns.ImportController.prototype.onFileInputClick_ = function (evt) {
this.hiddenFileInput.click();
};
ns.ImportController.prototype.importFromFile_ = function () {
var files = this.hiddenFileInput.get(0).files;
if (files.length == 1) {
var file = files[0];
if (this.isImage_(file)) {
this.readImageFile_(file);
this.enableDisabledSections_();
} else {
this.reset_();
throw "File is not an image : " + file.type;
}
}
};
ns.ImportController.prototype.enableDisabledSections_ = function () {
this.resizeWidth.removeAttr('disabled');
this.resizeHeight.removeAttr('disabled');
this.smoothResize.removeAttr('disabled');
this.submitButton.removeAttr('disabled');
this.fileInputButton.removeClass('button-primary');
this.fileInputButton.blur();
$('.import-section-disabled').removeClass('import-section-disabled');
};
ns.ImportController.prototype.readImageFile_ = function (imageFile) {
pskl.utils.FileUtils.readFile(imageFile, this.processImageSource_.bind(this));
};
/**
* Create an image from the given source (url or data-url), and onload forward to onImageLoaded
* TODO : should be a generic utility method, should take a callback
* @param {String} imageSource url or data-url, will be used as src for the image
*/
ns.ImportController.prototype.processImageSource_ = function (imageSource) {
this.importedImage_ = new Image();
this.importedImage_.onload = this.onImageLoaded_.bind(this);
this.importedImage_.src = imageSource;
};
ns.ImportController.prototype.onImageLoaded_ = function (evt) {
var w = this.importedImage_.width,
h = this.importedImage_.height;
var filePath = this.hiddenFileInput.val();
var fileName = this.extractFileNameFromPath_(filePath);
this.fileInputStatus.html(fileName);
this.resizeWidth.val(w);
this.resizeHeight.val(h);
this.importPreview.width("auto");
this.importPreview.append(this.createImagePreview_());
};
ns.ImportController.prototype.createImagePreview_ = function () {
var image = document.createElement('IMG');
image.src = this.importedImage_.src;
image.setAttribute('height', PREVIEW_HEIGHT);
return image;
};
ns.ImportController.prototype.extractFileNameFromPath_ = function (path) {
var parts = [];
if (path.indexOf('/') !== -1) {
parts = path.split('/');
} else if (path.indexOf('\\') !== -1) {
parts = path.split('\\');
} else {
parts = [path];
}
return parts[parts.length-1];
};
ns.ImportController.prototype.importImageToPiskel_ = function () {
if (this.importedImage_) {
if (window.confirm("You are about to create a new Piskel, unsaved changes will be lost.")) {
var w = this.resizeWidth.val(),
h = this.resizeHeight.val(),
smoothing = !!this.smoothResize.prop('checked');
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]);
pskl.app.piskelController.setPiskel(piskel);
pskl.app.animationController.setFPS(Constants.DEFAULT.FPS);
this.reset_();
}
}
};
ns.ImportController.prototype.isImage_ = function (file) {
return file.type.indexOf('image') === 0;
};
})();;(function () {
var ns = $.namespace("pskl.controller.settings");
var settings = {
user : {
template : 'templates/settings-application.html',
controller : ns.settings.ApplicationSettingsController
'user' : {
template : 'templates/settings/application.html',
controller : ns.ApplicationSettingsController
},
gif : {
template : 'templates/settings-export-gif.html',
controller : ns.settings.GifExportController
'gif' : {
template : 'templates/settings/export-gif.html',
controller : ns.GifExportController
},
'import' : {
template : 'templates/settings/import.html',
controller : ns.ImportController
}
};
@ -15825,14 +16105,16 @@ var jscolor = {
this.closeDrawer();
}
}.bind(this));
$.subscribe(Events.CLOSE_SETTINGS_DRAWER, this.closeDrawer.bind(this));
};
ns.SettingsController.prototype.loadSetting = function (setting) {
this.drawerContainer.innerHTML = pskl.utils.Template.get(settings[setting].template);
(new settings[setting].controller(this.piskelController)).init();
this.settingsContainer.addClass(EXP_DRAWER_CLS);
$('.' + SEL_SETTING_CLS).removeClass(SEL_SETTING_CLS);
$('[data-setting='+setting+']').addClass(SEL_SETTING_CLS);
@ -15847,7 +16129,7 @@ var jscolor = {
this.expanded = false;
this.currentSetting = null;
};
})();;(function () {
var ns = $.namespace("pskl.service");
@ -15886,7 +16168,7 @@ var jscolor = {
* @private
*/
ns.LocalStorageService.prototype.persistToLocalStorage_ = function() {
console.log('[LocalStorage service]: Snapshot stored');
window.localStorage.snapShot = this.piskelController.serialize();
};
@ -15895,9 +16177,9 @@ var jscolor = {
* @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);
};
/**
@ -16874,7 +17156,7 @@ var jscolor = {
this.layersListController = new pskl.controller.LayersListController(this.piskelController);
this.layersListController.init();
this.settingsController = new pskl.controller.SettingsController(this.piskelController);
this.settingsController = new pskl.controller.settings.SettingsController(this.piskelController);
this.settingsController.init();
this.selectionManager = new pskl.selection.SelectionManager(this.piskelController);
@ -16935,7 +17217,7 @@ var jscolor = {
finishInitAppEngine_ : function () {
if (pskl.framesheetData_ && pskl.framesheetData_.content) {
var piskel = pskl.utils.Serializer.createPiskel(pskl.framesheetData_.content);
piskel.app.PiskelController.setPiskel(piskel);
pskl.app.piskelController.setPiskel(piskel);
pskl.app.animationController.setFPS(pskl.framesheetData_.fps);
}
},

69
zoom/css/forms.css Normal file
View File

@ -0,0 +1,69 @@
.textfield {
background : black;
border : 1px solid #888;
border-radius : 2px;
padding : 3px 10px;
color : white;
}
.textfield[disabled=disabled] {
background : #3a3a3a;
}
.textfield-small {
width : 50px;
}
.button {
height: 24px;
box-sizing: border-box;
background-color: #3f3f3f;
border: 1px solid #333;
border-top-color: #666;
border-bottom-color: #222;
cursor: pointer;
text-decoration: none;
color: white;
text-shadow: 0px -1px 0 black;
font-weight: bold;
font-size: 1rem;
text-align: center;
transition: background-color 0.2s linear;
}
.button:hover {
text-decoration: none;
background-color: #484848;
color: gold;
}
.button-primary {
background-color: rgb(255,215,0); /* gold */
border-color: rgb(179, 164, 0);
border-top-color: white;
border-bottom-color: rgb(151, 133, 0);
color: black;
text-shadow: 0px 1px 0 #fff;
}
.button-primary:hover {
background-color: rgb(255,235,20);
color: #333;
}
.button[disabled=disabled],
.button[disabled=disabled]:hover {
cursor:default;
background-color: #aaa;
color: #777;
text-shadow: 0px 1px 0 #bbb;
border-color: #666;
border-top-color: #999;
border-bottom-color: #555;
}

View File

@ -31,14 +31,14 @@
}
.right-sticky-section.expanded .tool-icon {
margin-right: 1px;
padding-right: 1px;
}
.right-sticky-section .tool-icon.has-expanded-drawer {
position: relative;
background-color: #444;
margin-right: 0;
padding-right: 1px;
padding-right: 2px;
}
.settings-section {
@ -81,7 +81,7 @@
top: -2px;
right: -2px;
bottom: -2px;
left: -2px;
left: -2px;
}
.background-picker:hover:after {
@ -93,11 +93,18 @@
}
/* Gif Export Setting panel*/
.export-gif-upload-button {
.gif-upload-button,
.gif-render-button {
/*float : right;*/
margin-top : 10px;
margin-right : 10px;
}
.export-gif-preview {
.gif-export-radio-group {
margin:10px 0;
}
.gif-export-preview {
margin-top:20px;
max-width:240px;
position:relative;
@ -115,4 +122,46 @@
-moz-box-sizing:border-box;
background: rgba(0,0,0,0.5);
color: white;
}
/* Import panel */
.import-section {
margin: 15px 0;
}
.import-section-disabled {
color : #888;
}
.import-section-preview {
display : inline-block;
height : 60px;
width: 60px;
border : 1px dashed #999;
border-radius: 3px;
margin-left : 10px;
}
.import-resize-field {
width: 30px;
margin: 0px 8px;
text-align: right;
}
.file-input-button {
margin: 0px 8px;
border-radius: 2px;
}
.file-input-status {
display: inline-block;
width: 130px;
overflow: hidden;
height: 1.5rem;
word-break : break-all;
vertical-align: middle;
font-style: italic;
font-weight: normal;
text-shadow: none;
}

View File

@ -1,5 +1,3 @@
body {
background: radial-gradient(circle, #000, #373737);
/* 16/06/2013 : -webkit still needed for
@ -8,6 +6,11 @@ body {
background: -webkit-radial-gradient(circle, #000, #373737);
}
/* Browser fixes */
::-ms-clear {
display: none;
}
/**
* Application layout
*/
@ -251,35 +254,19 @@ body {
}
.layers-button {
height: 24px;
margin: 0;
width: 25%;
float : left;
border: none;
box-sizing: border-box;
border-top: 1px solid #666;
border-right: 1px solid #333;
border-bottom: 1px solid #333;
background-color: #3f3f3f;
cursor: pointer;
color: white;
font-size: 0.7em;
font-weight: bold;
text-align: center;
text-decoration: none;
text-shadow: 0px -1px 0 #000;
transition: background-color 0.2s linear;
}
/* @override */
.button.layers-button {
border-left-width: 0;
}
.layers-button:last-child {
border-right-width: 0;
}
.layers-button:hover {
text-decoration: none;
background-color: #484848;
color: gold;
}
/**
* User messages
*/

View File

@ -25,7 +25,7 @@
.tool-icon:hover {
background-color: #444;
}
}
/*
* Tool icons:
@ -209,6 +209,13 @@
position: relative;
}
.tool-icon.import-icon {
background-image: url(../img/import-icon.png);
background-position: 10px 5px;
background-size: 26px;
position: relative;
}
.upload-cloud-icon .label {
position: absolute;
left: 0;

BIN
zoom/img/import-icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 720 B

View File

@ -9,6 +9,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" type="text/css" href="css/reset.css">
<link rel="stylesheet" type="text/css" href="css/style.css">
<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/bootstrap/bootstrap.css">
@ -46,8 +47,9 @@
<iframe src="templates/settings.html" onload="iframeloader.onLoad(event)" data-iframe-loader="display"></iframe>
<div class="drawer vertical-centerer">
<div class="drawer-content" id="drawer-container">
<iframe src="templates/settings-application.html" onload="iframeloader.onLoad(event)" data-iframe-loader="store"></iframe>
<iframe src="templates/settings-export-gif.html" onload="iframeloader.onLoad(event)" data-iframe-loader="store"></iframe>
<iframe src="templates/settings/application.html" onload="iframeloader.onLoad(event)" data-iframe-loader="store"></iframe>
<iframe src="templates/settings/export-gif.html" onload="iframeloader.onLoad(event)" data-iframe-loader="store"></iframe>
<iframe src="templates/settings/import.html" onload="iframeloader.onLoad(event)" data-iframe-loader="store"></iframe>
</div>
</div>
</div>

View File

@ -39,6 +39,9 @@ var Events = {
*/
USER_SETTINGS_CHANGED: "USER_SETTINGS_CHANGED",
/* Listened to by SettingsController */
CLOSE_SETTINGS_DRAWER : "CLOSE_SETTINGS_DRAWER",
/**
* The framesheet was reseted and is now probably drastically different.
* Number of frames, content of frames, color used for the palette may have changed.

View File

@ -36,7 +36,7 @@
this.layersListController = new pskl.controller.LayersListController(this.piskelController);
this.layersListController.init();
this.settingsController = new pskl.controller.SettingsController(this.piskelController);
this.settingsController = new pskl.controller.settings.SettingsController(this.piskelController);
this.settingsController.init();
this.selectionManager = new pskl.selection.SelectionManager(this.piskelController);
@ -97,7 +97,7 @@
finishInitAppEngine_ : function () {
if (pskl.framesheetData_ && pskl.framesheetData_.content) {
var piskel = pskl.utils.Serializer.createPiskel(pskl.framesheetData_.content);
piskel.app.PiskelController.setPiskel(piskel);
pskl.app.piskelController.setPiskel(piskel);
pskl.app.animationController.setFPS(pskl.framesheetData_.fps);
}
},

View File

@ -10,10 +10,11 @@
this.setFPS(Constants.DEFAULT.FPS);
var zoom = this.calculateZoom_();
var frame = this.piskelController.getCurrentFrame();
var renderingOptions = {
"zoom": zoom,
"height" : this.piskelController.getCurrentFrame().getHeight() * zoom,
"width" : this.piskelController.getCurrentFrame().getWidth() * zoom
"height" : frame.getHeight() * zoom,
"width" : frame.getWidth() * zoom
};
this.renderer = new pskl.rendering.frame.FrameRenderer(this.container, renderingOptions);
@ -58,15 +59,18 @@
* Calculate the preview DPI depending on the framesheet size
*/
ns.AnimatedPreviewController.prototype.calculateZoom_ = function () {
var frame = this.piskelController.getCurrentFrame();
var previewSize = 200,
hZoom = previewSize / this.piskelController.getCurrentFrame().getHeight(),
wZoom = previewSize / this.piskelController.getCurrentFrame().getWidth();
hZoom = previewSize / frame.getHeight(),
wZoom = previewSize / frame.getWidth();
return Math.min(hZoom, wZoom);
};
ns.AnimatedPreviewController.prototype.updateZoom_ = function () {
var frame = this.piskelController.getCurrentFrame();
var zoom = this.calculateZoom_();
this.renderer.setZoom(zoom);
this.renderer.setDisplaySize(frame.getWidth() * zoom, frame.getHeight() * zoom);
};
})();

View File

@ -110,6 +110,11 @@
}
},
ns.DrawingController.prototype.onFrameSizeChanged_ = function () {
this.compositeRenderer.setZoom(this.calculateZoom_());
this.compositeRenderer.setDisplaySize(this.getContainerWidth_(), this.getContainerHeight_());
};
/**
* @private
*/

View File

@ -24,9 +24,9 @@
];
ns.GifExportController.prototype.init = function () {
this.radioTemplate_ = pskl.utils.Template.get("export-gif-radio-template");
this.radioTemplate_ = pskl.utils.Template.get("gif-export-radio-template");
this.previewContainerEl = document.querySelectorAll(".export-gif-preview div")[0];
this.previewContainerEl = document.querySelectorAll(".gif-export-preview")[0];
this.radioGroupEl = document.querySelectorAll(".gif-export-radio-group")[0];
this.uploadForm = $("[name=gif-export-upload-form]");
@ -56,7 +56,7 @@
};
ns.GifExportController.prototype.updatePreview_ = function (src) {
this.previewContainerEl.innerHTML = "<img style='max-width:240px;' src='"+src+"'/>";
this.previewContainerEl.innerHTML = "<div><img style='max-width:240px;' src='"+src+"'/></div>";
};
ns.GifExportController.prototype.getSelectedDpi_ = function () {

View File

@ -0,0 +1,168 @@
(function () {
var ns = $.namespace('pskl.controller.settings');
var DEFAULT_FILE_STATUS = 'No file selected ...';
var PREVIEW_HEIGHT = 60;
ns.ImportController = function (piskelController) {
this.piskelController = piskelController;
this.importedImage_ = null;
};
ns.ImportController.prototype.init = function () {
this.importForm = $("[name=import-form]");
this.hiddenFileInput = $("[name=file-upload-input]");
this.fileInputButton = $(".file-input-button");
this.fileInputStatus = $(".file-input-status");
this.fileInputStatus.html(DEFAULT_FILE_STATUS);
this.importPreview = $(".import-section-preview");
this.resizeWidth = $("[name=resize-width]");
this.resizeHeight = $("[name=resize-height]");
this.smoothResize = $("[name=smooth-resize-checkbox]");
this.submitButton = $("[name=import-submit]");
this.importForm.submit(this.onImportFormSubmit_.bind(this));
this.hiddenFileInput.change(this.onFileUploadChange_.bind(this));
this.fileInputButton.click(this.onFileInputClick_.bind(this));
this.resizeWidth.keyup(this.onResizeInputKeyUp_.bind(this, 'width'));
this.resizeHeight.keyup(this.onResizeInputKeyUp_.bind(this, 'height'));
};
ns.ImportController.prototype.reset_ = function () {
this.importForm.get(0).reset();
this.fileInputStatus.html(DEFAULT_FILE_STATUS);
$.publish(Events.CLOSE_SETTINGS_DRAWER);
};
ns.ImportController.prototype.onResizeInputKeyUp_ = function (from, evt) {
if (this.importedImage_) {
this.synchronizeResizeFields_(evt.target.value, from);
}
};
ns.ImportController.prototype.synchronizeResizeFields_ = function (value, from) {
value = parseInt(value, 10);
if (isNaN(value)) {
value = 0;
}
var height = this.importedImage_.height, width = this.importedImage_.width;
if (from === 'width') {
this.resizeHeight.val(Math.round(value * height / width));
} else {
this.resizeWidth.val(Math.round(value * width / height));
}
};
ns.ImportController.prototype.onImportFormSubmit_ = function (evt) {
evt.originalEvent.preventDefault();
this.importImageToPiskel_();
};
ns.ImportController.prototype.onFileUploadChange_ = function (evt) {
this.importFromFile_();
};
ns.ImportController.prototype.onFileInputClick_ = function (evt) {
this.hiddenFileInput.click();
};
ns.ImportController.prototype.importFromFile_ = function () {
var files = this.hiddenFileInput.get(0).files;
if (files.length == 1) {
var file = files[0];
if (this.isImage_(file)) {
this.readImageFile_(file);
this.enableDisabledSections_();
} else {
this.reset_();
throw "File is not an image : " + file.type;
}
}
};
ns.ImportController.prototype.enableDisabledSections_ = function () {
this.resizeWidth.removeAttr('disabled');
this.resizeHeight.removeAttr('disabled');
this.smoothResize.removeAttr('disabled');
this.submitButton.removeAttr('disabled');
this.fileInputButton.removeClass('button-primary');
this.fileInputButton.blur();
$('.import-section-disabled').removeClass('import-section-disabled');
};
ns.ImportController.prototype.readImageFile_ = function (imageFile) {
pskl.utils.FileUtils.readFile(imageFile, this.processImageSource_.bind(this));
};
/**
* Create an image from the given source (url or data-url), and onload forward to onImageLoaded
* TODO : should be a generic utility method, should take a callback
* @param {String} imageSource url or data-url, will be used as src for the image
*/
ns.ImportController.prototype.processImageSource_ = function (imageSource) {
this.importedImage_ = new Image();
this.importedImage_.onload = this.onImageLoaded_.bind(this);
this.importedImage_.src = imageSource;
};
ns.ImportController.prototype.onImageLoaded_ = function (evt) {
var w = this.importedImage_.width,
h = this.importedImage_.height;
var filePath = this.hiddenFileInput.val();
var fileName = this.extractFileNameFromPath_(filePath);
this.fileInputStatus.html(fileName);
this.resizeWidth.val(w);
this.resizeHeight.val(h);
this.importPreview.width("auto");
this.importPreview.append(this.createImagePreview_());
};
ns.ImportController.prototype.createImagePreview_ = function () {
var image = document.createElement('IMG');
image.src = this.importedImage_.src;
image.setAttribute('height', PREVIEW_HEIGHT);
return image;
};
ns.ImportController.prototype.extractFileNameFromPath_ = function (path) {
var parts = [];
if (path.indexOf('/') !== -1) {
parts = path.split('/');
} else if (path.indexOf('\\') !== -1) {
parts = path.split('\\');
} else {
parts = [path];
}
return parts[parts.length-1];
};
ns.ImportController.prototype.importImageToPiskel_ = function () {
if (this.importedImage_) {
if (window.confirm("You are about to create a new Piskel, unsaved changes will be lost.")) {
var w = this.resizeWidth.val(),
h = this.resizeHeight.val(),
smoothing = !!this.smoothResize.prop('checked');
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]);
pskl.app.piskelController.setPiskel(piskel);
pskl.app.animationController.setFPS(Constants.DEFAULT.FPS);
this.reset_();
}
}
};
ns.ImportController.prototype.isImage_ = function (file) {
return file.type.indexOf('image') === 0;
};
})();

View File

@ -0,0 +1,76 @@
(function () {
var ns = $.namespace("pskl.controller.settings");
var settings = {
'user' : {
template : 'templates/settings/application.html',
controller : ns.ApplicationSettingsController
},
'gif' : {
template : 'templates/settings/export-gif.html',
controller : ns.GifExportController
},
'import' : {
template : 'templates/settings/import.html',
controller : ns.ImportController
}
};
var SEL_SETTING_CLS = 'has-expanded-drawer';
var EXP_DRAWER_CLS = 'expanded';
ns.SettingsController = function (piskelController) {
this.piskelController = piskelController;
this.drawerContainer = document.getElementById("drawer-container");
this.settingsContainer = $('[data-pskl-controller=settings]');
this.expanded = false;
this.currentSetting = null;
};
/**
* @public
*/
ns.SettingsController.prototype.init = function() {
// Expand drawer when clicking 'Settings' tab.
$('[data-setting]').click(function(evt) {
var el = evt.originalEvent.currentTarget;
var setting = el.getAttribute("data-setting");
if (this.currentSetting != setting) {
this.loadSetting(setting);
} else {
this.closeDrawer();
}
}.bind(this));
$('body').click(function (evt) {
var isInSettingsContainer = $.contains(this.settingsContainer.get(0), evt.target);
if (this.expanded && !isInSettingsContainer) {
this.closeDrawer();
}
}.bind(this));
$.subscribe(Events.CLOSE_SETTINGS_DRAWER, this.closeDrawer.bind(this));
};
ns.SettingsController.prototype.loadSetting = function (setting) {
this.drawerContainer.innerHTML = pskl.utils.Template.get(settings[setting].template);
(new settings[setting].controller(this.piskelController)).init();
this.settingsContainer.addClass(EXP_DRAWER_CLS);
$('.' + SEL_SETTING_CLS).removeClass(SEL_SETTING_CLS);
$('[data-setting='+setting+']').addClass(SEL_SETTING_CLS);
this.expanded = true;
this.currentSetting = setting;
};
ns.SettingsController.prototype.closeDrawer = function () {
this.settingsContainer.removeClass(EXP_DRAWER_CLS);
$('.' + SEL_SETTING_CLS).removeClass(SEL_SETTING_CLS);
this.expanded = false;
this.currentSetting = null;
};
})();

View File

@ -212,7 +212,7 @@
* @private
*/
ns.FrameRenderer.prototype.renderFrame_ = function (frame) {
if (!this.canvas) {
if (!this.canvas || frame.getWidth() != this.canvas.width || frame.getHeight() != this.canvas.height) {
this.canvas = pskl.CanvasUtils.createCanvas(frame.getWidth(), frame.getHeight());
}

View File

@ -36,7 +36,7 @@
* @private
*/
ns.LocalStorageService.prototype.persistToLocalStorage_ = function() {
console.log('[LocalStorage service]: Snapshot stored');
window.localStorage.snapShot = this.piskelController.serialize();
};
@ -45,9 +45,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);
};
/**

View File

@ -0,0 +1,13 @@
(function () {
var ns = $.namespace('pskl.utils');
ns.FileUtils = {
readFile : function (file, callback) {
var reader = new FileReader();
reader.onload = function(event){
callback(event.target.result);
};
reader.readAsDataURL(file);
}
};
})();

View File

@ -91,6 +91,62 @@
}
colorCache[c] = color;
return color;
},
/*
* Create a pskl.model.Frame 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) {
var w = image.width,
h = image.height;
var canvas = pskl.CanvasUtils.createCanvas(w, h);
var context = canvas.getContext('2d');
context.drawImage(image, 0,0,w,h,0,0,w,h);
var imgData = context.getImageData(0,0,w,h).data;
// 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){
// 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];
if (a < 125) {
frame[x][y] = "TRANSPARENT";
} else {
frame[x][y] = this.rgbToHex(r,g,b);
}
}
}
return frame;
},
/**
* Convert a rgb(Number, Number, Number) color to hexadecimal representation
* @param {Number} r red value, between 0 and 255
* @param {Number} g green value, between 0 and 255
* @param {Number} b blue value, between 0 and 255
* @return {String} hex representation of the color '#ABCDEF'
*/
rgbToHex : function (r, g, b) {
return "#" + this.componentToHex(r) + this.componentToHex(g) + this.componentToHex(b);
},
/**
* Convert a color component (as a Number between 0 and 255) to its string hexa representation
* @param {Number} c component value, between 0 and 255
* @return {String} eg. '0A'
*/
componentToHex : function (c) {
var hex = c.toString(16);
return hex.length == 1 ? "0" + hex : hex;
}
};
})();

View File

@ -0,0 +1,30 @@
(function () {
var ns = $.namespace('pskl.utils');
ns.ImageResizer = {
resize : function (image, targetWidth, targetHeight, smoothingEnabled) {
var canvas = pskl.CanvasUtils.createCanvas(targetWidth, targetHeight);
var context = canvas.getContext('2d');
context.save();
if (!smoothingEnabled) {
this.disableSmoothingOnContext(context);
}
context.translate(canvas.width / 2, canvas.height / 2);
context.scale(targetWidth / image.width, targetHeight / image.height);
context.drawImage(image, -image.width / 2, -image.height / 2);
context.restore();
return canvas;
},
disableSmoothingOnContext : function (context) {
context.imageSmoothingEnabled = false;
context.mozImageSmoothingEnabled = false;
context.oImageSmoothingEnabled = false;
context.webkitImageSmoothingEnabled = false;
context.msImageSmoothingEnabled = false;
}
};
})();

View File

@ -1,7 +1,7 @@
(function () {
var ns = $.namespace("pskl");
var ns = $.namespace("pskl.utils");
ns.utils.Template = {
ns.Template = {
get : function (templateId) {
var template = document.getElementById(templateId);
if (template) {

View File

@ -15,7 +15,9 @@ exports.scripts = [
"js/utils/core.js",
"js/utils/CanvasUtils.js",
"js/utils/Math.js",
"js/utils/FileUtils.js",
"js/utils/FrameUtils.js",
"js/utils/ImageResizer.js",
"js/utils/PixelUtils.js",
"js/utils/Serializer.js",
"js/utils/Template.js",
@ -55,9 +57,13 @@ exports.scripts = [
"js/controller/ToolController.js",
"js/controller/PaletteController.js",
"js/controller/NotificationController.js",
// Settings sub-controllers
"js/controller/settings/ApplicationSettingsController.js",
"js/controller/settings/GifExportController.js",
"js/controller/SettingsController.js",
"js/controller/settings/ImportController.js",
// Settings controller
"js/controller/settings/SettingsController.js",
// Services
"js/service/LocalStorageService.js",

View File

@ -1,10 +1,10 @@
<div class="layers-list-container">
<h3 class="layers-title">Layers</h3>
<div class="layers-button-container">
<button class="layers-button" data-action="add" >Add</button>
<button class="layers-button" data-action="delete" >Delete</button>
<button class="layers-button layers-button-arrow" data-action="up" >&#8593;</button>
<button class="layers-button layers-button-arrow" data-action="down" >&#8595;</button>
<button class="button layers-button" data-action="add" >Add</button>
<button class="button layers-button" data-action="delete" >Delete</button>
<button class="button layers-button layers-button-arrow" data-action="up" >&#8593;</button>
<button class="button layers-button layers-button-arrow" data-action="down" >&#8595;</button>
</div>
<script type="text/template" id="layer-item-template">
<li class="layer-item" data-layer-name="{{layername}}">{{layername}}</li>

View File

@ -3,19 +3,29 @@
data-setting="user"
class="tool-icon gear-icon"
title="Preferences"
rel="tooltip" data-placement="left"></div>
rel="tooltip" data-placement="left">
</div>
<a
<div
data-setting="import"
class="tool-icon import-icon"
title="Import an existing picture"
rel="tooltip" data-placement="left">
</div>
<!-- <a
class="tool-icon gallery-icon"
title="Visit gallery"
href="http://juliandescottes.github.io/piskel-website/"
rel="tooltip" data-placement="left" target="_blank"></a>
rel="tooltip" data-placement="left" target="_blank">
</a> -->
<div
class="tool-icon save-icon"
title="Save to gallery"
onclick="pskl.app.storeSheet()"
rel="tooltip" data-placement="left" ></div>
rel="tooltip" data-placement="left" >
</div>
<div
data-setting="gif"

View File

@ -0,0 +1,26 @@
<div class="settings-section">
<div class="settings-title">
Canvas settings:
</div>
<div class="settings-item">
<label>Background:</label>
<div id="background-picker-wrapper" class="background-picker-wrapper">
<div class="background-picker light-picker-background" data-background-class="light-canvas-background"
rel="tooltip" data-placement="bottom" title="light / high contrast">
</div>
<div class="background-picker medium-picker-background" data-background-class="medium-canvas-background"
rel="tooltip" data-placement="bottom" title="medium / high contrast">
</div>
<div class="background-picker lowcont-medium-picker-background" data-background-class="lowcont-medium-canvas-background"
rel="tooltip" data-placement="bottom" title="medium / low contrast">
</div>
<div class="background-picker lowcont-dark-picker-background" data-background-class="lowcont-dark-canvas-background"
rel="tooltip" data-placement="bottom" title="dark / low contrast">
</div>
</div>
</div>
<div class="settings-item">
<label for="show-grid">Show grid:</label> <input id="show-grid" type="checkbox"/>
</div>
</div>

View File

@ -0,0 +1,18 @@
<div class="settings-section">
<div class="settings-title">
Export to Animated GIF
</div>
<div class="settings-item">
<label>Select resolution:</label>
<form action="" method="POST" name="gif-export-upload-form">
<script type="text/template" id="gif-export-radio-template">
<label style="display:block"><input type="radio" name="gif-dpi" value="{{value}}"/>
{{label}}</label>
</script>
<div class="gif-export-radio-group"></div>
<input type="submit" class="button button-primary gif-upload-button" value="Upload" />
<button type="button" class="button gif-render-button">Render</button>
</form>
<div class="gif-export-preview"></div>
</div>
</div>

View File

@ -0,0 +1,31 @@
<div class="settings-section">
<div class="settings-title">
Import Picture
</div>
<div class="settings-item">
<form action="" method="POST" name="import-form">
<div class="import-section">
<span class="import-section-title">File :</span>
<button type="button" class="button button-primary file-input-button">Browse</button>
<span class="file-input-status"></span>
<input style="display:none"
type="file" name="file-upload-input"
value="file" accept="image/*"/>
</div>
<div class="import-section import-section-disabled">
<span class="import-section-title" style="vertical-align:top">Preview :</span>
<div class="import-section-preview"></div>
</div>
<div class="import-section import-section-disabled">
<span class="import-section-title">Size :</span>
<input type="text" disabled="disabled" class="textfield import-resize-field" name="resize-width"/>x
<input type="text" disabled="disabled" class="textfield import-resize-field" name="resize-height"/>
</div>
<div class="import-section import-section-disabled">
<span class="import-section-title">Smooth resize :</span>
<input type="checkbox" disabled="disabled" checked="checked" name="smooth-resize-checkbox" value="1"/>
</div>
<input type="submit" name="import-submit" disabled="disabled" class="button button-primary import-button" value="Import" />
</form>
</div>
</div>