diff --git a/css/collapsible.css b/css/collapsible.css index 84465fc..0f7ce42 100644 --- a/css/collapsible.css +++ b/css/collapsible.css @@ -1 +1 @@ -.collabsible-wrapper{margin-bottom:1.2rem 0;position:fixed;top:20px;right:20px;z-index:1000;width:600px}.collapsible-content{max-height:0px;background:rgba(8,8,8,.937254902);width:calc(100% - 10px);top:0;padding:0;margin:0;overflow:hidden;transition:max-height .25s ease-in-out;border-bottom-left-radius:4px;border-bottom-right-radius:4px}input#collapsible[type=checkbox]{display:none}.lbl-toggle{display:block;font-weight:bold;font-family:monospace;font-size:16px;text-transform:uppercase;padding:.5rem 1rem;color:#fff;background:#000;cursor:pointer;border-radius:4px;transition:all .25s ease-out;user-select:none}.lbl-toggle:hover{color:#ccc}.lbl-toggle::before{content:" ";display:inline-block;border-top:5px solid rgba(0,0,0,0);border-bottom:5px solid rgba(0,0,0,0);border-left:5px solid currentColor;vertical-align:middle;margin-right:.7rem;transform:translateY(-2px);transition:transform .2s ease-out}.toggle:checked+.lbl-toggle::before{transform:rotate(90deg) translateX(-2px)}.toggle:checked+.lbl-toggle+.collapsible-content{max-height:100vh}.toggle:checked+.lbl-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0} \ No newline at end of file +.collapsible-content{max-height:0px;background:rgba(8,8,8,.937254902);width:100%;top:0;padding:0;margin:0;overflow:hidden;transition:max-height .25s ease-in-out;border-bottom-left-radius:4px;border-bottom-right-radius:4px}input.toggle[type=checkbox]{display:none}.lbl-toggle{display:block;font-weight:bold;font-size:16px;text-transform:uppercase;padding:.5rem 1rem;color:#fff;background:#000;cursor:pointer;border-radius:4px;transition:all .25s ease-out;user-select:none}.lbl-toggle:hover{color:#ccc}.lbl-toggle::before{content:"";display:inline-block;border-top:5px solid rgba(0,0,0,0);border-bottom:5px solid rgba(0,0,0,0);border-left:5px solid currentColor;vertical-align:middle;margin-right:.7rem;transform:translateY(-2px);transition:transform .2s ease-out}.toggle:checked+.lbl-toggle::before{transform:rotate(90deg) translateX(-2px)}.toggle:checked+.lbl-toggle+.collapsible-content{max-height:100vh}.toggle:checked+.lbl-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0} \ No newline at end of file diff --git a/css/image-drop.css b/css/image-drop.css new file mode 100644 index 0000000..87b27ec --- /dev/null +++ b/css/image-drop.css @@ -0,0 +1 @@ +#drop-area{border:2px dashed #ccc;padding:10px;margin:10px;text-align:center;width:200px;border-radius:10px;cursor:pointer}#drop-area label{cursor:pointer}#drop-area.highlight{border-color:#2185d0}#image-preview{max-width:100%;image-rendering:pixelated;position:absolute} \ No newline at end of file diff --git a/css/styles.css b/css/styles.css index af9b05c..f32fb1f 100644 --- a/css/styles.css +++ b/css/styles.css @@ -1 +1 @@ -*{box-sizing:border-box}html,body{margin:0;background:#222;font-family:Arial}body{margin:20px}h1{color:#fff;font-size:18px;line-height:0}p,#downloadlink{color:#ccc;font-size:12px;padding:.5rem}#output{height:50vh;padding:10px;margin:0;color:#fff;width:100%;border:0;outline:none;background:none;font-family:courier;font-weight:bold;font-size:10px;overflow:scroll}table{background:#000;border-collapse:collapse;font-size:10px;color:#aaa;text-align:left;font-family:courier;font-weight:bold}thead td{padding:10px}td{border:0;padding:0 20px;margin:0;border:1px solid #222}tr{padding:0;margin:0;height:6px;position:relative}tr:hover{background:#333}.cell,.cell-odd,.cell-even{width:16px;height:16px}.cell{position:relative}.cell-odd{position:absolute;display:none}.odd .cell-odd{display:block}.cell-even{position:absolute;display:block}.odd .cell-even{display:none}select{margin-bottom:20px;margin-right:30px}label{font-size:12px;margin-right:10px;color:#fff}#canvas-container{position:relative}#canvas{position:absolute;display:block;margin-bottom:20px;cursor:pointer}#canvas-alt{opacity:0}.odd #canvas-alt{opacity:1}#canvas{opacity:1}.odd #canvas{opacity:0}#text{background:none;border:none;color:#fff;width:30px}#slider{-webkit-appearance:none;height:2px;background:#fff;outline:none;padding:0;margin:0}#slider::-webkit-slider-thumb{-webkit-appearance:none;outline:none;width:16px;height:16px;border-radius:50%;background:#fff;cursor:pointer} \ No newline at end of file +*{box-sizing:border-box;font-family:monospace}html,body{margin:0;background:#222;font-family:Arial}body{margin:20px}h1{color:#fff;font-size:18px;line-height:0}p,#downloadlink{color:#ccc;font-size:12px}#downloadlink{padding:.5rem}input{cursor:pointer}#output{height:50vh;padding:10px;margin:0;color:#fff;width:100%;border:0;outline:none;background:none;font-family:courier;font-weight:bold;font-size:10px;overflow:scroll}table{background:#000;border-collapse:collapse;font-size:10px;color:#aaa;text-align:left;font-family:courier;font-weight:bold;margin-top:10px}thead td{padding:10px}td{border:0;padding:0 20px;margin:0;border:1px solid #222}tr{padding:0;margin:0;height:6px;position:relative}tr:hover{background:#333}.cell,.cell-odd,.cell-even{width:16px;height:16px}.cell{position:relative}.cell-odd{position:absolute;display:none}.odd .cell-odd{display:block}.cell-even{position:absolute;display:block}.odd .cell-even{display:none}select{margin-right:20px;cursor:pointer;font-size:11px}label{font-size:10px;color:#fff}canvas{image-rendering:pixelated}#canvas-container{position:relative}#canvas{position:absolute;display:block;margin-bottom:20px;cursor:pointer}#canvas-alt{opacity:0}.odd #canvas-alt{opacity:1}#canvas{opacity:1}.odd #canvas{opacity:0}#image-1{position:absolute;opacity:0}.odd #image-1{opacity:1}#image-2{position:absolute;opacity:1}.odd #image-2{opacity:0}#text{background:none;border:none;padding-left:5px;color:#fff;font-size:10px;width:40px}#slider{-webkit-appearance:none;height:2px;background:#fff;outline:none;padding:0;margin:0}#slider::-webkit-slider-thumb{-webkit-appearance:none;outline:none;width:16px;height:16px;border-radius:50%;background:#fff;cursor:pointer}.side-panel{margin-bottom:1.2rem 0;position:fixed;top:20px;right:20px;z-index:1000;bottom:20px;width:600px;overflow-y:scroll} \ No newline at end of file diff --git a/index.html b/index.html index e451eab..81fae01 100644 --- a/index.html +++ b/index.html @@ -1 +1 @@ -C64 Palette Explorer

Retro C64 Palette Explorer v0.53

by Digger/Elysium © 2016-24

\ No newline at end of file +C64 Palette Explorer

Retro C64 Palette Explorer v0.6.1

by Digger/Elysium © 2016-24

\ No newline at end of file diff --git a/js/file-upload.js b/js/file-upload.js new file mode 100644 index 0000000..390a04d --- /dev/null +++ b/js/file-upload.js @@ -0,0 +1,50 @@ +var FileUpload; + +FileUpload = class FileUpload { + static handleDragOver(event) { + event.preventDefault(); + return document.getElementById('drop-area').classList.add('highlight'); + } + + static handleDragLeave(event) { + event.preventDefault(); + return document.getElementById('drop-area').classList.remove('highlight'); + } + + static handleDrop(event) { + var files; + event.preventDefault(); + document.getElementById('drop-area').classList.remove('highlight'); + files = event.dataTransfer.files; + if (files.length > 0) { + return this.handleFile(files[0]); + } + } + + static handleFileSelect(event) { + var files; + files = event.target.files; + if (files.length > 0) { + return this.handleFile(files[0]); + } + } + + static handleFile(file) { + var imagePreview, reader; + imagePreview = document.getElementById('image-preview'); + if (file.type.startsWith('image/')) { + reader = new FileReader(); + reader.onload = function(event) { + var data; + data = event.target.result; + imagePreview.src = data; + imagePreview.style = "display: block"; + return ProcessImage.processImage(data); + }; + return reader.readAsDataURL(file); + } else { + return alert('Please select a valid image file.'); + } + } + +}; diff --git a/js/main.js b/js/main.js index ebea7cb..9166ffb 100644 --- a/js/main.js +++ b/js/main.js @@ -3,6 +3,7 @@ var Main, main; Main = (function() { class Main { constructor() { + this.imageContainer = document.getElementById('image-container'); this.canvasContainer = document.getElementById('canvas-container'); this.canvasContainer.addEventListener('click', (event) => { this.scale = this.scale === 1 ? 2 : 1; @@ -30,7 +31,6 @@ Main = (function() { this.ditherSelect = document.getElementById('dither'); this.ditherSelect.onchange = (event) => { this.ditherType = event.target.options[event.target.selectedIndex].value; - console.log(this.ditherType); if (this.ditherType === 'lace') { this.stopLoop = false; this.loop(); @@ -39,7 +39,8 @@ Main = (function() { this.stopLoop = true; setTimeout(() => { this.table.classList.remove('odd'); - return this.canvasContainer.classList.remove('odd'); + this.canvasContainer.classList.remove('odd'); + return this.imageContainer.classList.remove('odd'); }, 100); } return this.filter(); @@ -75,7 +76,8 @@ Main = (function() { ; slots will be set to white (FFFFFFFF). If there are more, then the remaining colors will be ignored. `; this.mixed = []; - this.json = []; + Main.json = {}; + this.paletteData = []; for (index1 = j = 0; j <= 15; index1 = ++j) { for (index2 = k = ref = index1; (ref <= 0xf ? k <= 0xf : k >= 0xf); index2 = ref <= 0xf ? ++k : --k) { col1 = ColorUtils.colorToHEX(this.palette[index1]); @@ -88,14 +90,8 @@ Main = (function() { distance = ColorUtils.getHEXDistance(col1, col2); diffLuma = Math.abs(Palettes.lumas[index1] - Palettes.lumas[index2]) / 32; diffL = Math.abs(col2HSL.l - col1HSL.l); - if (this.paletteType === 'PICO8') { - if (diffL > this.lumaDiffThreshold) { - continue; - } - } else { - if (diffLuma > this.lumaDiffThreshold) { - continue; - } + if (diffLuma > this.lumaDiffThreshold) { + continue; } // if @excludeNative and (col1 is col2) then continue if (this.excludeNative && (col1 !== col2)) { @@ -145,10 +141,10 @@ Main = (function() { distToGreen: ColorUtils.getHEXDistance(mix, ColorUtils.colorToHEX(0x00ff00)), distToBlue: ColorUtils.getHEXDistance(mix, ColorUtils.colorToHEX(0x0000ff)) }); - // @json.push - // index1: index1 - // index2: index2 - // mix: mix + Main.json[mix] = { + color1: ColorUtils.colorToHEX(this.palette[index1]), + color2: ColorUtils.colorToHEX(this.palette[index2]) + }; this.output.value += mix.split('#').join('ff').toUpperCase() + '\n'; } } @@ -159,13 +155,11 @@ Main = (function() { this.output.value += 'FFFFFFFF' + '\n'; } } - this.json = this.output.value; + this.paletteData = this.output.value; + log(this.json); } filter() { - if (this.paletteType === 'PICO8') { - this.sortBy = this.sortBy.join(',').replace('luma', 'l').split(','); - } this.createData(); this.sortBy.unshift('diffLuma'); if (this.sortBy.length > 0) { @@ -174,11 +168,11 @@ Main = (function() { this.createTable(); this.fillTable(); this.drawColors(); - // @createGradient() - // @print JSON.stringify @json - this.print(this.json); } + // @createGradient() + // @print JSON.stringify @json + // @print @paletteData print(buffer) { FileSaver.saveAsTextFile(buffer); } @@ -252,7 +246,7 @@ Main = (function() { while (this.table.firstChild) { this.table.removeChild(this.table.firstChild); } - fields = ["#", "mixed", "color1", "color2", "color1", "color2", "RGB distance", "hue", "saturation", "luma", "luma diff", "hex luma", "hex luma diff", "hex value"]; + fields = ["# of ", "mixed", "#col1", "#col2", "col1", "col2", "RGB dist", "hue", "saturation", "luma", "luma diff", "hex luma", "hex luma diff", "hex value"]; header = this.table.createTHead(); row = header.insertRow(0); for (i = j = 0, len = fields.length; j < len; i = ++j) { @@ -312,7 +306,7 @@ Main = (function() { } fillTable() { - var body, canvas, cellSize, context, i, j, len, obj, ref, row; + var body, canvas, cellSize, context, i, j, len, numCols, obj, ref, ref1, ref2, row; cellSize = 16; body = this.table.createTBody(); ref = this.mixed; @@ -343,12 +337,15 @@ Main = (function() { row.insertCell(11).innerHTML = obj.l.toFixed(2); row.insertCell(12).innerHTML = obj.diffL.toFixed(4); row.insertCell(13).innerHTML = obj.mix; + numCols = (ref1 = this.table.querySelector('thead')) != null ? (ref2 = ref1.childNodes[0]) != null ? ref2.childNodes[0] : void 0 : void 0; + numCols.innerHTML = '#/' + this.mixed.length; } } loop() { this.table.classList.toggle('odd'); this.canvasContainer.classList.toggle('odd'); + this.imageContainer.classList.toggle('odd'); if (this.stopLoop) { return; } @@ -377,6 +374,8 @@ Main = (function() { Main.prototype.stopLoop = false; + Main.json = {}; + return Main; }).call(this); diff --git a/js/palettes.js b/js/palettes.js index dd7365d..76c94e0 100644 --- a/js/palettes.js +++ b/js/palettes.js @@ -46,8 +46,6 @@ Palettes = (function() { Palettes.JAMPAL = [0x000000, 0xffffff, 0x7d202c, 0x4fb3a5, 0x84258c, 0x339840, 0x2a1b9d, 0xbfd04a, 0x7f410d, 0x4c2e00, 0xb44f5c, 0x3c3c3c, 0x646464, 0x7ce587, 0x6351db, 0x939393]; - Palettes.PICO8 = [0x000000, 0x161e42, 0x6b1442, 0x03783e, 0x9b3e26, 0x4d453e, 0xb5b6bb, 0xffeee2, 0xff0039, 0xff9300, 0xffff00, 0x0bea3c, 0x1c98ff, 0x70608b, 0xff5a97, 0xffc197]; - Palettes.DEEKAY = [ 0x000000, //0 0xffffff, //1 diff --git a/js/process-image.js b/js/process-image.js new file mode 100644 index 0000000..6b5e025 --- /dev/null +++ b/js/process-image.js @@ -0,0 +1,66 @@ +var ProcessImage; + +ProcessImage = class ProcessImage { + static processImage(data) { + var img; + this.canvas = document.createElement('canvas'); + this.context = this.canvas.getContext('2d'); + this.canvas1 = document.getElementById('image-1'); + this.canvas2 = document.getElementById('image-2'); + this.context1 = this.canvas1.getContext('2d'); + this.context2 = this.canvas2.getContext('2d'); + img = new Image(); + img.src = data; + return img.onload = () => { + var imageData, pixelData; + this.canvas.width = img.width; + this.canvas.height = img.height; + this.canvas1.width = img.width; + this.canvas1.height = img.height; + this.canvas1.imageSmoothingEnabled = false; + this.canvas2.width = img.width; + this.canvas2.height = img.height; + this.canvas2.imageSmoothingEnabled = false; + this.context.drawImage(img, 0, 0); + imageData = this.context.getImageData(0, 0, this.canvas.width, this.canvas.height).data; + return pixelData = this.processPixelData(imageData); + }; + } + + // console.table(pixelData) + static processPixelData(imageData) { + var b, color1, color2, g, hex, i, j, pixelData, r, ref, ref1, ref2, x, y; + pixelData = {}; + for (i = j = 0, ref = imageData.length; j < ref; i = j += 4) { + r = imageData[i]; + g = imageData[i + 1]; + b = imageData[i + 2]; + hex = this.rgbToHex(r, g, b); + pixelData[hex] = (pixelData[hex] || 0) + 1; + // plot pixel on canvas1 with color1 + x = (i / 4) % this.canvas.width; + y = Math.floor((i / 4) / this.canvas.width); + color1 = (ref1 = Main.json[hex]) != null ? ref1.color1 : void 0; + this.context1.fillStyle = color1; + this.context1.fillRect(x, y, 1, 1); + color2 = (ref2 = Main.json[hex]) != null ? ref2.color2 : void 0; + this.context2.fillStyle = color2; + this.context2.fillRect(x, y, 1, 1); + } + return pixelData; + } + + static componentToHex(c) { + var hex; + hex = c.toString(16); + if (hex.length === 1) { + return '0' + hex; + } + return hex; + } + + static rgbToHex(r, g, b) { + return '#' + this.componentToHex(r) + this.componentToHex(g) + this.componentToHex(b); + } + +};