From 845e384ae06ca38565e79d0260db5e5f2ad39ca9 Mon Sep 17 00:00:00 2001 From: Theo Cavignac Date: Thu, 22 Dec 2022 21:17:49 +0100 Subject: [PATCH 1/3] Implement importing GIMP and .hex palettes. --- js/FileManager.js | 105 ++++++++++++++++++++++++++++++++++++++-------- views/holders.hbs | 4 +- 2 files changed, 89 insertions(+), 20 deletions(-) diff --git a/js/FileManager.js b/js/FileManager.js index fcae2b5..49827c2 100644 --- a/js/FileManager.js +++ b/js/FileManager.js @@ -295,28 +295,42 @@ const FileManager = (() => { function loadPalette() { if (browsePaletteHolder.files && browsePaletteHolder.files[0]) { //make sure file is allowed filetype - var fileContentType = browsePaletteHolder.files[0].type; - if (fileContentType == 'image/png' || fileContentType == 'image/gif') { + let file = browsePaletteHolder.files[0]; + var fileContentType = + file.type + || file.name.split('.').slice(-1)[0]; + var fileReader = new FileReader(); + + let addPalette = (colors) => { + //add to palettes so that it can be loaded when they click okay + palettes['Loaded palette'] = {}; + palettes['Loaded palette'].colors = colors; + Util.setText('palette-button', 'Loaded palette'); + Util.setText('palette-button-splash', 'Loaded palette'); + Util.toggle('palette-menu-splash'); + } + + switch (fileContentType) { + case 'image/png': + case 'image/gif': //load file - var fileReader = new FileReader(); fileReader.onload = function(e) { var img = new Image(); img.onload = function() { - //draw image onto the temporary canvas var loadPaletteCanvas = document.getElementById('load-palette-canvas-holder'); var loadPaletteContext = loadPaletteCanvas.getContext('2d'); - + loadPaletteCanvas.width = img.width; loadPaletteCanvas.height = img.height; - + loadPaletteContext.drawImage(img, 0, 0); - + //create array to hold found colors var colorPalette = []; var imagePixelData = loadPaletteContext.getImageData(0,0,this.width, this.height).data; - + //loop through pixels looking for colors to add to palette for (var i = 0; i < imagePixelData.length; i += 4) { const newColor = {r:imagePixelData[i],g:imagePixelData[i + 1],b:imagePixelData[i + 2]}; @@ -325,19 +339,74 @@ const FileManager = (() => { colorPalette.push(color); } } - - //add to palettes so that it can be loaded when they click okay - palettes['Loaded palette'] = {}; - palettes['Loaded palette'].colors = colorPalette; - Util.setText('palette-button', 'Loaded palette'); - Util.setText('palette-button-splash', 'Loaded palette'); - Util.toggle('palette-menu-splash'); + + addPalette(colorPalette); }; img.src = e.target.result; }; - fileReader.readAsDataURL(browsePaletteHolder.files[0]); + break; + case 'gpl': + fileReader.onload = function() { + file.text().then((content) => { + let colorPalette = content.split(/\r?\n/) + // Skip header line + .slice(1) + .map((line) => line.trim()) + .filter((line) => line != "") + // discard comment lines + .filter((line) => !line.startsWith('#')) + // discard meta data lines + .filter((line) => !line.includes(':')) + .map((line) => { + let components = line.split(/\s+/); + + if (components.length < 3) { + alert(`Invalid color specification ${line}.`); + return "#000000" + } + + let [r, g, b, ...rest] = components; + let color = { + r: parseInt(r), + g: parseInt(g), + b: parseInt(b), + }; + + if (isNaN(color.r) || isNaN(color.g) || isNaN(color.b)) { + alert(`Invalid color specification ${line}.`); + return "#000000" + } + + return '#' + Color.rgbToHex(color); + }); + + addPalette(colorPalette); + }); + }; + break; + case 'hex': + fileReader.onload = function() { + file.text().then((content) => { + let colorPalette = content.split(/\r?\n/) + .map((line) => line.trim()) + .filter((line) => line != "") + // discard comment lines + .filter((line) => !line.startsWith('#')) + .map((line) => { + if (line.match(/[0-9A-Fa-f]{6}/)) { + return '#' + line; + } + alert(`Invalid hex color ${line}.`); + return '#000000'; + }); + addPalette(colorPalette); + }); + }; + break; + default: + alert('Only PNG, GIF, .hex and .gpl files are supported at this time.'); } - else alert('Only PNG and GIF files are supported at this time.'); + fileReader.readAsDataURL(browsePaletteHolder.files[0]); } browsePaletteHolder.value = null; @@ -526,4 +595,4 @@ const FileManager = (() => { } }) return ret; -})(); \ No newline at end of file +})(); diff --git a/views/holders.hbs b/views/holders.hbs index a144583..054aaad 100644 --- a/views/holders.hbs +++ b/views/holders.hbs @@ -2,7 +2,7 @@ dl dl - + - \ No newline at end of file + From c08c7f2d0b6cc45bde16ac36ee03582f1a9e09f2 Mon Sep 17 00:00:00 2001 From: Theo Cavignac Date: Thu, 22 Dec 2022 23:37:38 +0100 Subject: [PATCH 2/3] Refactor palette loading. --- js/FileManager.js | 195 ++++++++++++++++++++++++---------------------- 1 file changed, 100 insertions(+), 95 deletions(-) diff --git a/js/FileManager.js b/js/FileManager.js index 49827c2..963491a 100644 --- a/js/FileManager.js +++ b/js/FileManager.js @@ -292,9 +292,9 @@ const FileManager = (() => { return JSON.stringify(dictionary); } + function loadPalette() { if (browsePaletteHolder.files && browsePaletteHolder.files[0]) { - //make sure file is allowed filetype let file = browsePaletteHolder.files[0]; var fileContentType = file.type @@ -302,116 +302,121 @@ const FileManager = (() => { var fileReader = new FileReader(); - let addPalette = (colors) => { - //add to palettes so that it can be loaded when they click okay - palettes['Loaded palette'] = {}; - palettes['Loaded palette'].colors = colors; - Util.setText('palette-button', 'Loaded palette'); - Util.setText('palette-button-splash', 'Loaded palette'); - Util.toggle('palette-menu-splash'); - } - + // dispatch on file type switch (fileContentType) { case 'image/png': case 'image/gif': - //load file - fileReader.onload = function(e) { - var img = new Image(); - img.onload = function() { - //draw image onto the temporary canvas - var loadPaletteCanvas = document.getElementById('load-palette-canvas-holder'); - var loadPaletteContext = loadPaletteCanvas.getContext('2d'); - - loadPaletteCanvas.width = img.width; - loadPaletteCanvas.height = img.height; - - loadPaletteContext.drawImage(img, 0, 0); - - //create array to hold found colors - var colorPalette = []; - var imagePixelData = loadPaletteContext.getImageData(0,0,this.width, this.height).data; - - //loop through pixels looking for colors to add to palette - for (var i = 0; i < imagePixelData.length; i += 4) { - const newColor = {r:imagePixelData[i],g:imagePixelData[i + 1],b:imagePixelData[i + 2]}; - var color = '#' + Color.rgbToHex(newColor); - if (colorPalette.indexOf(color) == -1) { - colorPalette.push(color); - } - } - - addPalette(colorPalette); - }; - img.src = e.target.result; - }; + fileReader.onload = loadPaletteFromImage; + fileReader.readAsDataURL(browsePaletteHolder.files[0]); break; case 'gpl': - fileReader.onload = function() { - file.text().then((content) => { - let colorPalette = content.split(/\r?\n/) - // Skip header line - .slice(1) - .map((line) => line.trim()) - .filter((line) => line != "") - // discard comment lines - .filter((line) => !line.startsWith('#')) - // discard meta data lines - .filter((line) => !line.includes(':')) - .map((line) => { - let components = line.split(/\s+/); - - if (components.length < 3) { - alert(`Invalid color specification ${line}.`); - return "#000000" - } - - let [r, g, b, ...rest] = components; - let color = { - r: parseInt(r), - g: parseInt(g), - b: parseInt(b), - }; - - if (isNaN(color.r) || isNaN(color.g) || isNaN(color.b)) { - alert(`Invalid color specification ${line}.`); - return "#000000" - } - - return '#' + Color.rgbToHex(color); - }); - - addPalette(colorPalette); - }); - }; + fileReader.onload = loadPaletteFromGimp; + fileReader.readAsText(browsePaletteHolder.files[0]); break; case 'hex': - fileReader.onload = function() { - file.text().then((content) => { - let colorPalette = content.split(/\r?\n/) - .map((line) => line.trim()) - .filter((line) => line != "") - // discard comment lines - .filter((line) => !line.startsWith('#')) - .map((line) => { - if (line.match(/[0-9A-Fa-f]{6}/)) { - return '#' + line; - } - alert(`Invalid hex color ${line}.`); - return '#000000'; - }); - addPalette(colorPalette); - }); - }; + fileReader.onload = loadPaletteFromHex; + fileReader.readAsText(browsePaletteHolder.files[0]); break; default: alert('Only PNG, GIF, .hex and .gpl files are supported at this time.'); } - fileReader.readAsDataURL(browsePaletteHolder.files[0]); } browsePaletteHolder.value = null; } + function addPalette(colors) { + //add to palettes so that it can be loaded when they click okay + palettes['Loaded palette'] = {}; + palettes['Loaded palette'].colors = colors; + Util.setText('palette-button', 'Loaded palette'); + Util.setText('palette-button-splash', 'Loaded palette'); + Util.toggle('palette-menu-splash'); + } + + function loadPaletteFromImage(e) { + var img = new Image(); + img.onload = function() { + //draw image onto the temporary canvas + var loadPaletteCanvas = document.getElementById('load-palette-canvas-holder'); + var loadPaletteContext = loadPaletteCanvas.getContext('2d'); + + loadPaletteCanvas.width = img.width; + loadPaletteCanvas.height = img.height; + + loadPaletteContext.drawImage(img, 0, 0); + + //create array to hold found colors + var colorPalette = []; + var imagePixelData = loadPaletteContext.getImageData(0,0,this.width, this.height).data; + + //loop through pixels looking for colors to add to palette + for (var i = 0; i < imagePixelData.length; i += 4) { + const newColor = {r:imagePixelData[i],g:imagePixelData[i + 1],b:imagePixelData[i + 2]}; + var color = '#' + Color.rgbToHex(newColor); + if (colorPalette.indexOf(color) == -1) { + colorPalette.push(color); + } + } + + addPalette(colorPalette); + }; + img.src = e.target.result; + } + + function loadPaletteFromGimp(e) { + let content = e.target.result; + let colorPalette = content.split(/\r?\n/) + // Skip header line + .slice(1) + .map((line) => line.trim()) + .filter((line) => line != "") + // discard comment lines + .filter((line) => !line.startsWith('#')) + // discard meta data lines + .filter((line) => !line.includes(':')) + .map((line) => { + let components = line.split(/\s+/); + + if (components.length < 3) { + alert(`Invalid color specification ${line}.`); + return "#000000" + } + + let [r, g, b, ...rest] = components; + let color = { + r: parseInt(r), + g: parseInt(g), + b: parseInt(b), + }; + + if (isNaN(color.r) || isNaN(color.g) || isNaN(color.b)) { + alert(`Invalid color specification ${line}.`); + return "#000000" + } + + return '#' + Color.rgbToHex(color); + }); + addPalette(colorPalette); + } + + function loadPaletteFromHex(e) { + let content = e.target.result; + let colorPalette = content.split(/\r?\n/) + .map((line) => line.trim()) + .filter((line) => line != "") + // discard comment lines + .filter((line) => !line.startsWith('#')) + .map((line) => { + if (line.match(/[0-9A-Fa-f]{6}/)) { + return '#' + line; + } + alert(`Invalid hex color ${line}.`); + return '#000000'; + }); + addPalette(colorPalette); + } + currentImportPivotElement = undefined; currentImportPivotPosition = 'middle'; isImportWindowInitialized = false; From a36281935b11a478cf55c1d10ca6653c4e054033 Mon Sep 17 00:00:00 2001 From: Theo Cavignac Date: Tue, 27 Dec 2022 18:18:38 +0100 Subject: [PATCH 3/3] Implement loading a palette from the menu. --- js/ColorModule.js | 10 ++++++---- js/FileManager.js | 25 +++++++++++++++++++------ js/TopMenuModule.js | 5 ++++- views/main-menu.hbs | 3 ++- 4 files changed, 31 insertions(+), 12 deletions(-) diff --git a/js/ColorModule.js b/js/ColorModule.js index c791bc5..44974e3 100644 --- a/js/ColorModule.js +++ b/js/ColorModule.js @@ -388,10 +388,12 @@ const ColorModule = (() => { * * @param {*} paletteColors The colours of the palette */ - function createColorPalette(paletteColors) { + function createColorPalette(paletteColors, clearCurrent=true) { //remove current palette - while (colorsMenu.childElementCount > 1) - colorsMenu.children[0].remove(); + if (clearCurrent) { + while (colorsMenu.childElementCount > 1) + colorsMenu.children[0].remove(); + } var lightestColor = new Color("hex", '#000000'); var darkestColor = new Color("hex", '#ffffff'); @@ -527,4 +529,4 @@ const ColorModule = (() => { updateCurrentColor, getSelectedColor, } -})(); \ No newline at end of file +})(); diff --git a/js/FileManager.js b/js/FileManager.js index 963491a..730181d 100644 --- a/js/FileManager.js +++ b/js/FileManager.js @@ -293,6 +293,13 @@ const FileManager = (() => { return JSON.stringify(dictionary); } + let fromMenu = false; + + function openImportPaletteWindow() { + fromMenu = true; + document.getElementById('load-palette-browse-holder').click(); + } + function loadPalette() { if (browsePaletteHolder.files && browsePaletteHolder.files[0]) { let file = browsePaletteHolder.files[0]; @@ -326,12 +333,17 @@ const FileManager = (() => { } function addPalette(colors) { - //add to palettes so that it can be loaded when they click okay - palettes['Loaded palette'] = {}; - palettes['Loaded palette'].colors = colors; - Util.setText('palette-button', 'Loaded palette'); - Util.setText('palette-button-splash', 'Loaded palette'); - Util.toggle('palette-menu-splash'); + if (fromMenu) { + ColorModule.createColorPalette(colors, clearCurrent=false); + } else { + // From splash screen + // add to palettes so that it can be loaded when they click okay + palettes['Loaded palette'] = {}; + palettes['Loaded palette'].colors = colors; + Util.setText('palette-button', 'Loaded palette'); + Util.setText('palette-button-splash', 'Loaded palette'); + Util.toggle('palette-menu-splash'); + } } function loadPaletteFromImage(e) { @@ -587,6 +599,7 @@ const FileManager = (() => { openPixelExportWindow, openSaveProjectWindow, openImportImageWindow, + openImportPaletteWindow, open } diff --git a/js/TopMenuModule.js b/js/TopMenuModule.js index 6b88ede..6dfa558 100644 --- a/js/TopMenuModule.js +++ b/js/TopMenuModule.js @@ -46,6 +46,9 @@ const TopMenuModule = (() => { case 'Import': Events.on('click', currSubmenuButton, FileManager.openImportImageWindow); break; + case 'Load palette': + Events.on('click', currSubmenuButton, FileManager.openImportPaletteWindow); + break; case 'Export': Events.on('click', currSubmenuButton, FileManager.openPixelExportWindow); break; @@ -139,4 +142,4 @@ const TopMenuModule = (() => { addInfoElement, resetInfos } -})(); \ No newline at end of file +})(); diff --git a/views/main-menu.hbs b/views/main-menu.hbs index 2b43857..059037f 100644 --- a/views/main-menu.hbs +++ b/views/main-menu.hbs @@ -7,6 +7,7 @@
  • +
  • Exit
  • @@ -74,4 +75,4 @@
  • {{> checkbox}}
  • - \ No newline at end of file +