diff --git a/misc/icons/source/common-warning-red.svg b/misc/icons/source/common-warning-red.svg
new file mode 100644
index 00000000..ba09fc4d
--- /dev/null
+++ b/misc/icons/source/common-warning-red.svg
@@ -0,0 +1,64 @@
+
+
+
+
\ No newline at end of file
diff --git a/src/css/animations.css b/src/css/animations.css
index 83463b4a..f24f9e72 100644
--- a/src/css/animations.css
+++ b/src/css/animations.css
@@ -1,3 +1,9 @@
@keyframes fade {
50% { opacity: 0.5; }
}
+
+@keyframes glow {
+ 0% { opacity: 0.66; }
+ 50% { opacity: 1; }
+ 100% { opacity: 0.66; }
+}
diff --git a/src/css/dialogs-performance-info.css b/src/css/dialogs-performance-info.css
new file mode 100644
index 00000000..132e8954
--- /dev/null
+++ b/src/css/dialogs-performance-info.css
@@ -0,0 +1,66 @@
+.performance-link {
+ display: none;
+ position: fixed;
+ bottom: 10px;
+ right: 10px;
+ z-index: 11000;
+ cursor: pointer;
+ opacity: 0;
+ transition : opacity 0.3s;
+}
+
+.performance-link.visible {
+ display: block;
+ opacity: 0.66;
+ animation: glow 2s infinite;
+}
+
+.performance-link.visible:hover {
+ opacity: 1;
+ animation: none;
+}
+
+#dialog-container.performance-info {
+ width: 500px;
+ height: 525px;
+ top : 50%;
+ left : 50%;
+ position : absolute;
+ margin-left: -250px;
+}
+
+.dialog-performance-info-body {
+ font-size: 13px;
+ letter-spacing: 1px;
+ padding: 10px 20px;
+}
+
+.dialog-performance-info-body ul {
+ border: 1px solid #666;
+ padding: 5px;
+ border-radius: 3px;
+}
+
+.dialog-performance-info-body li {
+ list-style-type: initial;
+ margin: 0 20px;
+}
+
+.dialog-performance-info-body sup {
+ color: gold;
+ cursor: pointer;
+}
+
+.show #dialog-container.performance-info {
+ margin-top: -300px;
+}
+
+.dialog-performance-info-body .warning-icon {
+ float: left;
+ margin-top: 10px;
+}
+
+.dialog-performance-info-body .warning-icon-info {
+ overflow: hidden;
+ margin-left: 30px;
+}
diff --git a/src/img/icons/common/common-warning-red.png b/src/img/icons/common/common-warning-red.png
new file mode 100644
index 00000000..c5f4ea80
Binary files /dev/null and b/src/img/icons/common/common-warning-red.png differ
diff --git a/src/img/icons/common/common-warning-red@2x.png b/src/img/icons/common/common-warning-red@2x.png
new file mode 100644
index 00000000..bb24901b
Binary files /dev/null and b/src/img/icons/common/common-warning-red@2x.png differ
diff --git a/src/index.html b/src/index.html
index d0f5f478..6d52399d 100644
--- a/src/index.html
+++ b/src/index.html
@@ -71,13 +71,17 @@
@@include('templates/misc-templates.html', {})
@@include('templates/popup-preview.html', {})
-
+
+
@@include('templates/dialogs/create-palette.html', {})
@@include('templates/dialogs/import-image.html', {})
@@include('templates/dialogs/browse-local.html', {})
@@include('templates/dialogs/cheatsheet.html', {})
+ @@include('templates/dialogs/performance-info.html', {})
@@include('templates/settings/application.html', {})
diff --git a/src/js/Events.js b/src/js/Events.js
index 75c37067..65f921f1 100644
--- a/src/js/Events.js
+++ b/src/js/Events.js
@@ -80,6 +80,8 @@ var Events = {
CURRENT_COLORS_UPDATED : 'CURRENT_COLORS_UPDATED',
+ PERFORMANCE_REPORT_CHANGED : 'PERFORMANCE_REPORT_CHANGED',
+
// Tests
MOUSE_EVENT : 'MOUSE_EVENT',
KEYBOARD_EVENT : 'KEYBOARD_EVENT',
diff --git a/src/js/app.js b/src/js/app.js
index 83ce44a4..2959beb6 100644
--- a/src/js/app.js
+++ b/src/js/app.js
@@ -153,6 +153,13 @@
document.querySelector('#drawing-canvas-container'));
this.fileDropperService.init();
+ this.userWarningController = new pskl.controller.UserWarningController(this.piskelController);
+ this.userWarningController.init();
+
+ this.performanceReportService = new pskl.service.performance.PerformanceReportService(
+ this.piskelController, this.currentColorsService);
+ this.performanceReportService.init();
+
this.drawingLoop = new pskl.rendering.DrawingLoop();
this.drawingLoop.addCallback(this.render, this);
this.drawingLoop.start();
diff --git a/src/js/controller/UserWarningController.js b/src/js/controller/UserWarningController.js
new file mode 100644
index 00000000..c1b62147
--- /dev/null
+++ b/src/js/controller/UserWarningController.js
@@ -0,0 +1,55 @@
+(function () {
+ var ns = $.namespace('pskl.controller');
+
+ ns.UserWarningController = function (piskelController, currentColorsService) {
+ this.piskelController = piskelController;
+ this.currentColorsService = currentColorsService;
+ };
+
+ // This method is not attached to the prototype because we want to trigger it
+ // from markup generated for a notification message.
+ ns.UserWarningController.showPerformanceInfoDialog = function () {
+ $.publish(Events.DIALOG_DISPLAY, {
+ dialogId: 'performance-info'
+ });
+ };
+
+ ns.UserWarningController.prototype.init = function () {
+ $.subscribe(Events.PERFORMANCE_REPORT_CHANGED, this.onPerformanceReportChanged_.bind(this));
+
+ this.performanceLinkEl = document.querySelector('.performance-link');
+ pskl.utils.Event.addEventListener(
+ this.performanceLinkEl,
+ 'click',
+ ns.UserWarningController.showPerformanceInfoDialog,
+ this
+ );
+ };
+
+ ns.UserWarningController.prototype.destroy = function () {
+ pskl.utils.Event.removeAllEventListeners(this);
+ this.performanceLinkEl = null;
+ };
+
+ ns.UserWarningController.prototype.onPerformanceReportChanged_ = function (event, report) {
+ var shouldDisplayWarning = report.hasProblem();
+
+ // Check if a performance warning is already displayed.
+ var isWarningDisplayed = this.performanceLinkEl.classList.contains('visible');
+
+ // Show/hide the performance warning link depending on the received report.
+ this.performanceLinkEl.classList.toggle('visible', shouldDisplayWarning);
+
+ // Show a notification message if the new report indicates a performance issue
+ // and we were not displaying a warning before.
+ if (shouldDisplayWarning && !isWarningDisplayed) {
+ $.publish(Events.SHOW_NOTIFICATION, [{
+ 'content': 'Performance problem detected, ' +
+ '' +
+ 'learn more?',
+ 'hideDelay' : 5000
+ }]);
+ }
+ };
+})();
diff --git a/src/js/controller/dialogs/DialogsController.js b/src/js/controller/dialogs/DialogsController.js
index 7afb6561..cf6a9e63 100644
--- a/src/js/controller/dialogs/DialogsController.js
+++ b/src/js/controller/dialogs/DialogsController.js
@@ -17,6 +17,10 @@
'import-image' : {
template : 'templates/dialogs/import-image.html',
controller : ns.ImportImageController
+ },
+ 'performance-info' : {
+ template : 'templates/dialogs/performance-info.html',
+ controller : ns.PerformanceInfoController
}
};
diff --git a/src/js/controller/dialogs/PerformanceInfoController.js b/src/js/controller/dialogs/PerformanceInfoController.js
new file mode 100644
index 00000000..87fd57b5
--- /dev/null
+++ b/src/js/controller/dialogs/PerformanceInfoController.js
@@ -0,0 +1,11 @@
+(function () {
+ var ns = $.namespace('pskl.controller.dialogs');
+
+ ns.PerformanceInfoController = function () {};
+
+ pskl.utils.inherit(ns.PerformanceInfoController, ns.AbstractDialogController);
+
+ ns.PerformanceInfoController.prototype.init = function () {
+ this.superclass.init.call(this);
+ };
+})();
diff --git a/src/js/service/performance/PerformanceReport.js b/src/js/service/performance/PerformanceReport.js
new file mode 100644
index 00000000..2cdebf20
--- /dev/null
+++ b/src/js/service/performance/PerformanceReport.js
@@ -0,0 +1,45 @@
+(function () {
+ var ns = $.namespace('pskl.service.performance');
+
+ /**
+ * We consider that piskel should behave correctly for a sprite with the following specs:
+ * - 256*256
+ * - 30 frames
+ * - 5 layers
+ * - 30 colors
+ * Based on these assumptions, as well as a few arbitrary hard limits we try to check
+ * if the provided sprite might present a performance issue.
+ *
+ * @param {Piskel} piskel the sprite to analyze
+ * @param {Number} colorsCount number of colors for the current sprite
+ * (not part of the piskel model so has to be provided separately).
+ */
+ ns.PerformanceReport = function (piskel, colorsCount) {
+ var pixels = piskel.getWidth() * piskel.getHeight();
+ this.resolution = pixels > (500 * 500);
+
+ var layersCount = piskel.getLayers().length;
+ this.layers = layersCount > 25;
+
+ var framesCount = piskel.getLayerAt(0).size();
+ this.frames = framesCount > 100;
+
+ this.colors = colorsCount > 100;
+
+ var overallScore = (pixels / 2500) + (layersCount * 4) + framesCount + colorsCount;
+ this.overall = overallScore > 100;
+ };
+
+ ns.PerformanceReport.prototype.equals = function (report) {
+ return (report instanceof ns.PerformanceReport &&
+ this.resolution == report.resolution &&
+ this.layers == report.layers &&
+ this.frames == report.frames &&
+ this.colors == report.colors &&
+ this.overall == report.overall);
+ };
+
+ ns.PerformanceReport.prototype.hasProblem = function () {
+ return this.resolution || this.layers || this.frames || this.colors || this.overall;
+ };
+})();
diff --git a/src/js/service/performance/PerformanceReportService.js b/src/js/service/performance/PerformanceReportService.js
new file mode 100644
index 00000000..43a8ab8b
--- /dev/null
+++ b/src/js/service/performance/PerformanceReportService.js
@@ -0,0 +1,25 @@
+(function () {
+ var ns = $.namespace('pskl.service.performance');
+
+ ns.PerformanceReportService = function (piskelController, currentColorsService) {
+ this.piskelController = piskelController;
+ this.currentColorsService = currentColorsService;
+
+ this.currentReport = null;
+ };
+
+ ns.PerformanceReportService.prototype.init = function () {
+ $.subscribe(Events.HISTORY_STATE_SAVED, this.createReport_.bind(this));
+ };
+
+ ns.PerformanceReportService.prototype.createReport_ = function () {
+ var report = new ns.PerformanceReport(
+ this.piskelController.getPiskel(),
+ this.currentColorsService.getCurrentColors().length);
+
+ if (!report.equals(this.currentReport)) {
+ $.publish(Events.PERFORMANCE_REPORT_CHANGED, [report]);
+ this.currentReport = report;
+ }
+ };
+})();
diff --git a/src/piskel-script-list.js b/src/piskel-script-list.js
index e5d6f605..cb2e8d3d 100644
--- a/src/piskel-script-list.js
+++ b/src/piskel-script-list.js
@@ -115,6 +115,7 @@
"js/controller/NotificationController.js",
"js/controller/TransformationsController.js",
"js/controller/CanvasBackgroundController.js",
+ "js/controller/UserWarningController.js",
// Settings sub-controllers
"js/controller/settings/AbstractSettingController.js",
@@ -139,6 +140,7 @@
"js/controller/dialogs/ImportImageController.js",
"js/controller/dialogs/BrowseLocalController.js",
"js/controller/dialogs/CheatsheetController.js",
+ "js/controller/dialogs/PerformanceInfoController.js",
// Dialogs controller
"js/controller/dialogs/DialogsController.js",
@@ -181,6 +183,8 @@
"js/service/FileDropperService.js",
"js/service/SelectedColorsService.js",
"js/service/MouseStateService.js",
+ "js/service/performance/PerformanceReport.js",
+ "js/service/performance/PerformanceReportService.js",
"js/service/storage/LocalStorageService.js",
"js/service/storage/GalleryStorageService.js",
"js/service/storage/DesktopStorageService.js",
diff --git a/src/piskel-style-list.js b/src/piskel-style-list.js
index 9199543e..a208b9c1 100644
--- a/src/piskel-style-list.js
+++ b/src/piskel-style-list.js
@@ -22,6 +22,7 @@
"css/dialogs-cheatsheet.css",
"css/dialogs-create-palette.css",
"css/dialogs-import-image.css",
+ "css/dialogs-performance-info.css",
"css/notifications.css",
"css/toolbox.css",
"css/toolbox-layers-list.css",
diff --git a/src/templates/dialogs/performance-info.html b/src/templates/dialogs/performance-info.html
new file mode 100644
index 00000000..98d72da1
--- /dev/null
+++ b/src/templates/dialogs/performance-info.html
@@ -0,0 +1,32 @@
+
\ No newline at end of file