From 6445b44d02a26ffd5a86bf2b16f85d17a245a5dd Mon Sep 17 00:00:00 2001 From: jdescottes Date: Sat, 20 Sep 2014 09:14:21 +0200 Subject: [PATCH] Moved image import to worker --- src/css/font-icon.css | 73 +++++++- src/css/fonts/icomoon.eot | Bin 0 -> 4416 bytes src/css/fonts/icomoon.svg | 28 +++ src/css/fonts/icomoon.ttf | Bin 0 -> 4252 bytes src/css/fonts/icomoon.woff | Bin 0 -> 4328 bytes src/css/toolbox-layers-list.css | 5 - src/css/toolbox-palettes-list.css | 154 ++++++++-------- src/js/controller/LayersListController.js | 43 ++++- src/js/controller/PalettesListController.js | 24 ++- .../dialogs/AbstractDialogController.js | 6 +- .../dialogs/CreatePaletteController.js | 8 +- src/js/service/keyboard/CheatsheetService.js | 13 -- src/js/service/palette/PaletteImageReader.js | 41 ++++- src/js/utils/FrameUtils.js | 17 +- src/js/worker/ImageProcessor.js | 167 ++++++++++++++++++ src/piskel-script-list.js | 3 + src/templates/dialogs/create-palette.html | 2 +- src/templates/layers-list.html | 41 +++-- src/templates/palettes-list.html | 15 +- 19 files changed, 486 insertions(+), 154 deletions(-) create mode 100644 src/css/fonts/icomoon.eot create mode 100644 src/css/fonts/icomoon.svg create mode 100644 src/css/fonts/icomoon.ttf create mode 100644 src/css/fonts/icomoon.woff create mode 100644 src/js/worker/ImageProcessor.js diff --git a/src/css/font-icon.css b/src/css/font-icon.css index ec44f24a..f771603a 100644 --- a/src/css/font-icon.css +++ b/src/css/font-icon.css @@ -1,10 +1,10 @@ @font-face { font-family: 'piskel'; - src:url('fonts/piskel.eot?-3olv93'); - src:url('fonts/piskel.eot?#iefix-3olv93') format('embedded-opentype'), - url('fonts/piskel.woff?-3olv93') format('woff'), - url('fonts/piskel.ttf?-3olv93') format('truetype'), - url('fonts/piskel.svg?-3olv93#icomoon') format('svg'); + src:url('fonts/icomoon.eot?-3olv93'); + src:url('fonts/icomoon.eot?#iefix-3olv93') format('embedded-opentype'), + url('fonts/icomoon.woff?-3olv93') format('woff'), + url('fonts/icomoon.ttf?-3olv93') format('truetype'), + url('fonts/icomoon.svg?-3olv93#icomoon') format('svg'); font-weight: normal; font-style: normal; } @@ -31,3 +31,66 @@ content: "\e601"; } +.piskel-icon-download:before { + content: "\e600"; +} + +.piskel-icon-rotateleft:before { + content: "\e603"; +} + +.piskel-icon-rotateright:before { + content: "\e604"; +} + +.piskel-icon-fliph:before { + content: "\e605"; +} + +.piskel-icon-flipv:before { + content: "\e606"; +} + +.piskel-icon-trashplain:before { + content: "\e607"; +} + +.piskel-icon-trash:before { + content: "\e608"; +} + +.piskel-icon-merge:before { + content: "\e609"; +} + +.piskel-icon-pencil:before { + content: "\e610"; +} + +.piskel-icon-close:before { + content: "\e611"; +} + +.piskel-icon-minus:before { + content: "\e60a"; +} + +.piskel-icon-plus:before { + content: "\e60b"; +} + +.piskel-icon-arrow-up-fat:before { + content: "\e60c"; +} + +.piskel-icon-arrow-down-fat:before { + content: "\e60d"; +} + +.piskel-icon-arrow-up-thin:before { + content: "\e60e"; +} + +.piskel-icon-arrow-down-thin:before { + content: "\e60f"; +} diff --git a/src/css/fonts/icomoon.eot b/src/css/fonts/icomoon.eot new file mode 100644 index 0000000000000000000000000000000000000000..f5314adf7b63f3e372c57b1dc2c12280cfbba943 GIT binary patch literal 4416 zcmb7IU2Ggz6+UNX?%es?pIPthdi|H#tnGDNaBZ*G+Y;KY8Z|$vqeRd^6;#zGO_Nk{ zylR4!B2=v?Dnjam)z>0q@qk1nO2i8i;$ffQg-R8D=^M+B`h*}4sRRPneD}`U-d#cj zcV}nLJ@?%6ch5cd%rrxy?`Mga3>t1syR0x97l-kRHfirpow__aMwF%-bcJ?k8~+`; z1%fnviLTN$`Uc&kEqWc80(}LT*XcHB6-`Vlv_#9Ac62t7N}x||GHLn3mmgnsj{o!5 z!2A;b%`4kmt%*y&xelT!^f#_;-EQHjECP<4Yd7C`b@s3J9Qwcqo$FV(Ug019^zYD0 ziCe!8g!u=ffxe=jy}o_t?p%{UkN!L8zj$-!%GScWv+tw-C-j%Lx9+xRhQCLY$)g|N z+SjVX@uh2jhk zT?JdFdGM-4tLu%YdGM;PEOB+@T2n+TRk=J5rqKw~nIVn|_lNKWhx*$5DCZ0p@=@L% z(KR|$w0NN0=tQu>RV_A(k*q3XM@@qwBV23I@lpAWl>`2VE=ZJIX&hs4)m|KQ*dfbf zeBh(H_Rit_2D)qObv|3-3{P5?tpeg)oHSd474q52fu}La@PX7;J{6EHn7ILZoCSZJ8#p7-GXRObI!*Oxbo|%M92! zZdN*hux!(?Kx0}qusP56ZQ)q9WHZHVf}Oz@Lm)UAreTA*E!@28f;(74gj84)1Ok2L zwUUwMNtgh-lr6JPHfIUYLV)4eG8gz$=~T%%Fv$-FO*%f|9h#&iW{*3jG64U!v;9a zaZ6#iSn`ykeId0;5GjNQkBju@Ov_K(wy!)VGcuL6ZBL54i^UTzB!H!5sg)=wO)S%a z`BDVZ#-zZu(@rXsGVKV!DVZ)gX%=b6&ADz~$^b$v8G;KINIIC|8e?Y4aq^y*bKEp2 zY%Hqfg2?qk*GY>n99k)1rEeNiSbzT||Ks zITACaka0xs(g@DoPaYO+;|$sTg&R zAZ{0<=;Z9|$tWrwL6?+_$oCO

)#%+td}FHx{RsbBm?%_38RcmC8bLt{v#mNRE((Uev)wD9T!0 zCQGeDAR>r~^NX2r$8|O9{CpT5OP%dJAD$={S0^>|&h+|tX)(8)ipO%0t+aHcj5O60 zw9|UxT+?M1bm#&?RWMerhxIbx9&z01#T}3+8gkNpYXXM1eI% zR4k8`=hRsnHOj?$*_G!whoUMaq8Y9cQ;uI2`%D0vS(8Y5P0qx6PSNchC`;ub~ z_qs{L8t58M$BGO87EsAlWVIrra=j|bxU(*68WrwbY_%>n4QlpqQ>)l)?e4a^tyVjZ z+nqS(xZU2})v>n+ezB@e$B-6k<<<44cx}D{zeVIq?n>8{y;!=O-Bh5ysd>7d>e4LM zrGoW%44G+~u@=*uFPv0`fO7Uzj3lG^@%?*x@;$|50OWWSlMI6Gp$wbI`R_Y7OhvNQ6Ga2Q`~kGEAa4 z3GgscyapkIWWW3q~@xzY;cw5P&^m; zUO4T0DHIa66}Ms>xj2t|t$3FgTD?#Bba$#d#od(WPlu{l@&$-28|1y+-B{E7LXl{H z@c!?4pMOU$Ag-T4_1nM=nRtEvag=_lCPZIWaYG)Q*K4RwJ@kue1mkw$7|-b18vTYE z!FT}+{Io8x`5*7Sr%OBQoavz!G0;T;JeVrKHP{dpPG92<;)j9 zBVQ+7`smrl4-`Im_PvjHL`sHLoRE6gLIQ3Q8qmNDHROS#rUuniqe&b3dC)(4#=tlK z%b3C+j3shByt~cNv&J{gMf0Zlf!Q&CYd)~PZM|>(%KDq|#FV%$ekShQn-HL86!^Qy z;i@SSqb*Gz^z~ + + +Generated by IcoMoon + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/css/fonts/icomoon.ttf b/src/css/fonts/icomoon.ttf new file mode 100644 index 0000000000000000000000000000000000000000..5ae72b63d94aa0e3ec7d7054ec589def6ca9f394 GIT binary patch literal 4252 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 0) { + var html = colors.map(function (color) { + return pskl.utils.Template.replace(this.paletteColorTemplate_, {color : color}); + }.bind(this)).join(''); + this.colorListContainer_.innerHTML = html; - this.highlightSelectedColors(); + this.highlightSelectedColors(); - var hasScrollbar = colors.length > NO_SCROLL_MAX_COLORS; - if (hasScrollbar && !pskl.utils.UserAgent.isChrome) { - this.colorListContainer_.classList.add(HAS_SCROLL_CLASSNAME); + var hasScrollbar = colors.length > NO_SCROLL_MAX_COLORS; + if (hasScrollbar && !pskl.utils.UserAgent.isChrome) { + this.colorListContainer_.classList.add(HAS_SCROLL_CLASSNAME); + } else { + this.colorListContainer_.classList.remove(HAS_SCROLL_CLASSNAME); + } } else { - this.colorListContainer_.classList.remove(HAS_SCROLL_CLASSNAME); + this.colorListContainer_.innerHTML = pskl.utils.Template.get('palettes-list-no-colors-partial'); } }; @@ -105,6 +110,7 @@ ns.PalettesListController.prototype.onPaletteSelected_ = function (evt) { var paletteId = this.colorPaletteSelect_.value; this.selectPalette(paletteId); + this.colorPaletteSelect_.blur(); }; ns.PalettesListController.prototype.onCreatePaletteClick_ = function (evt) { diff --git a/src/js/controller/dialogs/AbstractDialogController.js b/src/js/controller/dialogs/AbstractDialogController.js index 130d351e..296b78e9 100644 --- a/src/js/controller/dialogs/AbstractDialogController.js +++ b/src/js/controller/dialogs/AbstractDialogController.js @@ -17,9 +17,9 @@ }; ns.AbstractDialogController.prototype.setTitle = function (title) { - var dialogHead = document.querySelector('.dialog-head'); - if (dialogHead) { - dialogHead.innerText = title; + var dialogTitle = document.querySelector('.dialog-title'); + if (dialogTitle) { + dialogTitle.innerText = title; } }; diff --git a/src/js/controller/dialogs/CreatePaletteController.js b/src/js/controller/dialogs/CreatePaletteController.js index 5fff0cb3..fa777fcc 100644 --- a/src/js/controller/dialogs/CreatePaletteController.js +++ b/src/js/controller/dialogs/CreatePaletteController.js @@ -111,10 +111,16 @@ ns.CreatePaletteController.prototype.onFileInputChange_ = function (evt) { var files = this.hiddenFileInput.files; if (files.length == 1) { - this.paletteImportService.read(files[0], this.setPalette_.bind(this)); + this.paletteImportService.read(files[0], this.setPalette_.bind(this), this.displayErrorMessage_.bind(this)); } }; + ns.CreatePaletteController.prototype.displayErrorMessage_ = function (message) { + message = "Could not import palette : " + message; + $.publish(Events.SHOW_NOTIFICATION, [{"content": message}]); + window.setTimeout($.publish.bind($, Events.HIDE_NOTIFICATION), 2000); + }; + ns.CreatePaletteController.prototype.onNameInputChange_ = function (evt) { this.palette.name = pskl.utils.escapeHtml(this.nameInput.value); }; diff --git a/src/js/service/keyboard/CheatsheetService.js b/src/js/service/keyboard/CheatsheetService.js index 56818073..f2194c1e 100644 --- a/src/js/service/keyboard/CheatsheetService.js +++ b/src/js/service/keyboard/CheatsheetService.js @@ -14,8 +14,6 @@ pskl.app.shortcutService.addShortcut('shift+?', this.toggleCheatsheet_.bind(this)); pskl.app.shortcutService.addShortcut('?', this.toggleCheatsheet_.bind(this)); - document.body.addEventListener('click', this.onBodyClick_.bind(this)); - var link = $('.cheatsheet-link'); link.click(this.toggleCheatsheet_.bind(this)); @@ -24,17 +22,6 @@ $.subscribe(Events.ESCAPE, this.onEscape_.bind(this)); }; - ns.CheatsheetService.prototype.onBodyClick_ = function (event) { - if (this.isDisplayed_) { - var target = event.target; - var cheatsheetContainerEl = document.querySelector('.cheatsheet-container'); - var isInCheatsheetContainer = pskl.utils.Dom.isParent(target, cheatsheetContainerEl); - if (!isInCheatsheetContainer) { - this.hideCheatsheet_(); - } - } - }; - ns.CheatsheetService.prototype.toggleCheatsheet_ = function () { if (this.isDisplayed_) { this.hideCheatsheet_(); diff --git a/src/js/service/palette/PaletteImageReader.js b/src/js/service/palette/PaletteImageReader.js index 330bb0d7..61945904 100644 --- a/src/js/service/palette/PaletteImageReader.js +++ b/src/js/service/palette/PaletteImageReader.js @@ -12,18 +12,41 @@ }; ns.PaletteImageReader.prototype.onImageLoaded_ = function (image) { - var frame = pskl.utils.FrameUtils.createFromImage(image); - var colorsMap = {}; - frame.forEachPixel(function (color, x, y) { - colorsMap[color] = true; - }); + var imageProcessor = new pskl.worker.ImageProcessor(image, + this.onWorkerSuccess_.bind(this), + this.onWorkerStep_.bind(this), + this.onWorkerError_.bind(this)); + imageProcessor.process(); + }; - delete colorsMap[Constants.TRANSPARENT_COLOR]; + ns.PaletteImageReader.prototype.onWorkerSuccess_ = function (event) { + var data = event.data; + var colorsMap = data.colorsMap; var colors = Object.keys(colorsMap); - var uuid = pskl.utils.Uuid.generate(); - var palette = new pskl.model.Palette(uuid, this.file.name + ' palette', colors); - this.onSuccess(palette); + if (colors.length > 200) { + this.onError('Too many colors : ' + colors.length); + } else { + var uuid = pskl.utils.Uuid.generate(); + var palette = new pskl.model.Palette(uuid, this.file.name + ' palette', colors); + + this.onSuccess(palette); + } + }; + ns.PaletteImageReader.prototype.onWorkerStep_ = function (event) { + var data = event.data; + var step = data.step; + var total = data.total; + + var progress = ((step/total)*100).toFixed(1); + + if (this.currentProgress !== progress) { + this.currentProgress = progress; + console.log("Image processing completed at : " + progress + "%"); + } + }; + ns.PaletteImageReader.prototype.onWorkerError_ = function (event) { + this.onError('Unable to process the image : ' + event.data.message); }; })(); \ No newline at end of file diff --git a/src/js/utils/FrameUtils.js b/src/js/utils/FrameUtils.js index b98c2f43..4eb55b91 100644 --- a/src/js/utils/FrameUtils.js +++ b/src/js/utils/FrameUtils.js @@ -106,17 +106,24 @@ * @return {pskl.model.Frame} corresponding frame */ createFromImage : function (image) { + var w = image.width, + h = image.height; + var imgData = pskl.utils.FrameUtils.imageToImageData(image); + var grid = pskl.utils.FrameUtils.imageDataToGrid(imgData,w, h, Constants.TRANSPARENT_COLOR); + return pskl.model.Frame.fromGrid(grid); + }, + + imageToImageData : 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; - return pskl.utils.FrameUtils.createFromImageData(imgData, w, h); + return context.getImageData(0,0,w,h).data; }, - createFromImageData : function (imageData, width, height) { + imageDataToGrid : function (imageData, width, height, transparent) { // Draw the zoomed-up pixels to a different canvas context var grid = []; for (var x = 0 ; x < width ; x++){ @@ -129,13 +136,13 @@ var b = imageData[i+2]; var a = imageData[i+3]; if (a < 125) { - grid[x][y] = Constants.TRANSPARENT_COLOR; + grid[x][y] = transparent; } else { grid[x][y] = pskl.utils.FrameUtils.rgbToHex(r,g,b); } } } - return pskl.model.Frame.fromPixelGrid(grid); + return grid; }, /** diff --git a/src/js/worker/ImageProcessor.js b/src/js/worker/ImageProcessor.js new file mode 100644 index 00000000..9c05e986 --- /dev/null +++ b/src/js/worker/ImageProcessor.js @@ -0,0 +1,167 @@ +(function () { + var ns = $.namespace('pskl.worker'); + + var worker = function () { + + var postStep_ = function (step, total) { + this.postMessage({ + type : 'STEP', + step : step, + total : total + }); + }; + + var imageDataToGrid = function (imageData, width, height, transparent) { + // Draw the zoomed-up pixels to a different canvas context + var grid = []; + for (var x = 0 ; x < width ; x++){ + grid[x] = []; + postStep_(x, (width-1)*2); + for (var y = 0 ; y < height ; y++){ + // Find the starting index in the one-dimensional image data + var i = (y * width + x)*4; + var r = imageData[i ]; + var g = imageData[i+1]; + var b = imageData[i+2]; + var a = imageData[i+3]; + if (a < 125) { + grid[x][y] = transparent; + } else { + grid[x][y] = pskl.utils.FrameUtils.rgbToHex(r,g,b); + } + } + } + return grid; + }; + + var getColorsMapFromImageData = function (imageData, width, height) { + var grid = imageDataToGrid(imageData, width, height, 'transparent'); + + var colorsMap = {}; + for (var i = 0 ; i < grid.length ; i++) { + var step = (grid.length-1) + i; + var total = (grid.length-1)*2; + postStep_(step, total); + for (var j = 0 ; j < grid[i].length ; j++) { + var color = grid[i][j]; + if (color != 'transparent') { + colorsMap[color] = true; + } + } + } + return colorsMap; + }; + + this.onmessage = function(event) { + var data = event.data; + if (data.type === 'RUN_SCRIPT') { + this.importScripts(data.script); + } else { + try { + var colorsMap = getColorsMapFromImageData(data.imageData, data.width, data.height); + this.postMessage({ + type : 'SUCCESS', + colorsMap : colorsMap + }); + } catch(e) { + this.postMessage({ + type : 'ERROR', + message : e.message + }); + } + } + }; + }; + + try { + // create worker from blob + var typedArray = [(worker+"").replace(/function \(\) \{/,"").replace(/\}[^}]*$/, "")]; + var blob = new Blob(typedArray, {type: "application/javascript"}); // pass a useful mime type here + var blobUrl = window.URL.createObjectURL(blob); + } catch (e) { + console.error("Could not create worker", e.message); + } + + ns.ImageProcessor = function (image, onSuccess, onStep, onError) { + this.image = image; + + this.onStep = onStep; + this.onSuccess = onSuccess; + this.onError = onError; + + this.worker = new Worker(blobUrl); + this.worker.onmessage = this.onWorkerMessage.bind(this); + }; + + ns.ImageProcessor.prototype.process = function () { + this.importAll(pskl.utils.FrameUtils, 'pskl.utils.FrameUtils'); + this.importAll(pskl.utils.CanvasUtils, 'pskl.utils.CanvasUtils'); + + var imageData = pskl.utils.FrameUtils.imageToImageData(this.image); + this.worker.postMessage({ + imageData : imageData, + width : this.image.width, + height : this.image.height + }); + }; + + ns.ImageProcessor.prototype.createNamespace = function (name) { + var createNamespace = (function () { + var parts = name.split('.'); + if (parts.length > 0) { + var node = this; + for (var i = 0 ; i < parts.length ; i++) { + if (!node[parts[i]]) { + node[parts[i]] = {}; + } + node = node[parts[i]]; + } + } + }); + var script = createNamespace + ""; + script = script.replace(/function \(\) \{/,"").replace(/\}[^}]*$/, ""); + script = "var name = '" + name + "';" + script; + + this.runScript(script); + }; + + ns.ImageProcessor.prototype.importAll = function (classToImport, classpath) { + this.createNamespace('pskl.utils.FrameUtils'); + for (var key in classToImport) { + if (classToImport.hasOwnProperty(key)) { + this.addMethod(classToImport[key], classpath + '.' + key); + } + } + }; + + ns.ImageProcessor.prototype.addMethod = function (method, name) { + this.runScript(name + "=" + method); + }; + + ns.ImageProcessor.prototype.runScript = function (script) { + this.worker.postMessage({ + type : 'RUN_SCRIPT', + script : this.getScriptAsUrl(script) + }); + }; + + ns.ImageProcessor.prototype.getScriptAsUrl = function (script) { + var blob = new Blob([script], {type: "application/javascript"}); // pass a useful mime type here + return window.URL.createObjectURL(blob); + }; + + ns.ImageProcessor.prototype.onWorkerMessage = function (event) { + if (event.data.type === 'STEP') { + this.onStep(event); + } else if (event.data.type === 'SUCCESS') { + this.onSuccess(event); + this.worker.terminate(); + } else if (event.data.type === 'ERROR') { + this.onError(event); + this.worker.terminate(); + } + }; +})(); + + + diff --git a/src/piskel-script-list.js b/src/piskel-script-list.js index 5d0ea962..fed83252 100644 --- a/src/piskel-script-list.js +++ b/src/piskel-script-list.js @@ -163,6 +163,9 @@ "js/devtools/TestRecordController.js", "js/devtools/init.js", + // Workers + "js/worker/ImageProcessor.js", + // Application controller and initialization "js/app.js", // Bonus features !! diff --git a/src/templates/dialogs/create-palette.html b/src/templates/dialogs/create-palette.html index c9908913..a644f98a 100644 --- a/src/templates/dialogs/create-palette.html +++ b/src/templates/dialogs/create-palette.html @@ -1,6 +1,6 @@

- Create palette + Create palette X

diff --git a/src/templates/layers-list.html b/src/templates/layers-list.html index 70389fc5..8930f9f4 100644 --- a/src/templates/layers-list.html +++ b/src/templates/layers-list.html @@ -6,24 +6,35 @@ class="layers-toggle-preview piskel-icon-eye">
- - - - - - + + + + + + + + + + +
- + +
    + -
      diff --git a/src/templates/palettes-list.html b/src/templates/palettes-list.html index c68d03f2..2dc7dded 100644 --- a/src/templates/palettes-list.html +++ b/src/templates/palettes-list.html @@ -5,11 +5,11 @@
      - + class="button palettes-list-button create-palette-button piskel-icon-plus" data-action="add" + title="Create a new palette" rel="tooltip" data-placement="top" > +
      @@ -18,6 +18,13 @@
      + + +