Delete pixart.html
This commit is contained in:
949
html/pixart.html
949
html/pixart.html
@@ -1,949 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
|
|
||||||
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
|
|
||||||
<meta http-equiv="Pragma" content="no-cache">
|
|
||||||
<meta http-equiv="Expires" content="0">
|
|
||||||
<title>Led Matrix Pixel Art Convertor</title>
|
|
||||||
|
|
||||||
<!-- not in use in minified version
|
|
||||||
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
|
|
||||||
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
|
|
||||||
<link rel="manifest" href="/site.webmanifest">
|
|
||||||
-->
|
|
||||||
<!--For minifyed-->
|
|
||||||
<style>
|
|
||||||
|
|
||||||
.box {
|
|
||||||
border: 2px solid white;
|
|
||||||
}
|
|
||||||
body {
|
|
||||||
font-family: 'Arcade', Arial, sans-serif;
|
|
||||||
background-color: #151515;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
.top-part {
|
|
||||||
width: 600px;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
.container {
|
|
||||||
max-width: 100% -40px;
|
|
||||||
border-radius: 0px;
|
|
||||||
padding: 20px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
h1 {
|
|
||||||
font-size: 2.3em;
|
|
||||||
color: rgb(126, 76, 128);
|
|
||||||
margin: 20px 0;
|
|
||||||
font-family: 'Arcade', Arial, sans-serif;
|
|
||||||
line-height: 0.5;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
h2 {
|
|
||||||
font-size: 1.3em;
|
|
||||||
color: rgba(126, 76, 128, 0.61);
|
|
||||||
margin: 20px 0;
|
|
||||||
font-family: 'Arcade', Arial, sans-serif;
|
|
||||||
line-height: 0.5;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
font-size: 1.2em;
|
|
||||||
color: rgb(119, 119, 119);
|
|
||||||
line-height: 1.5;
|
|
||||||
font-family: 'Arcade', Arial, sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
#fieldTable {
|
|
||||||
font-size: 1 em;
|
|
||||||
color: #777;
|
|
||||||
line-height: 1;
|
|
||||||
font-family: 'Arcade', Arial, sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
#drop-zone {
|
|
||||||
display: block;
|
|
||||||
width: 100%-40px;
|
|
||||||
border: 3px dashed #7E4C80;
|
|
||||||
border-radius: 0px;
|
|
||||||
text-align: center;
|
|
||||||
padding: 20px;
|
|
||||||
margin: 0px;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
font-family: 'Arcade', Arial, sans-serif;
|
|
||||||
font-size: 15px;
|
|
||||||
color: #777;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
*.button {
|
|
||||||
display: block;
|
|
||||||
width: 100% - 40px;
|
|
||||||
border: 2px dashed #ccc;
|
|
||||||
border-radius: 20px;
|
|
||||||
text-align: center;
|
|
||||||
padding: 20px;
|
|
||||||
margin: 0px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
#file-picker {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
* select {
|
|
||||||
background-color: #333333;
|
|
||||||
color: #C0C0C0;
|
|
||||||
border: 1px solid #C0C0C0;
|
|
||||||
margin-top: 0.5em;
|
|
||||||
margin-bottom: 0.5em;
|
|
||||||
padding: 0em;
|
|
||||||
width: 100%;
|
|
||||||
height: 27px;
|
|
||||||
font-size: 15px;
|
|
||||||
color: rgb(119, 119, 119);
|
|
||||||
border-radius: 0;
|
|
||||||
}
|
|
||||||
* input[type=range] {
|
|
||||||
-webkit-appearance:none;
|
|
||||||
flex-grow: 1;
|
|
||||||
border-radius: 0px;
|
|
||||||
background: linear-gradient(to right, #333333 0%, #333333 100%);
|
|
||||||
color: #C0C0C0;
|
|
||||||
border: 1px solid #C0C0C0;
|
|
||||||
margin-top: 0.5em;
|
|
||||||
margin-left: 0em;
|
|
||||||
}
|
|
||||||
input[type="range"]::-webkit-slider-thumb{
|
|
||||||
-webkit-appearance:none;
|
|
||||||
width: 25px;
|
|
||||||
height:25px;
|
|
||||||
background:#7E4C80;
|
|
||||||
position:relative;
|
|
||||||
z-index:3;
|
|
||||||
}
|
|
||||||
.rangeNumber{
|
|
||||||
width: 20px;
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
* input[type=text] {
|
|
||||||
background-color: #333333;
|
|
||||||
border: 1px solid #C0C0C0;
|
|
||||||
padding-inline-start: 5px;
|
|
||||||
margin-top: 10px;
|
|
||||||
width: 100%;
|
|
||||||
height: 27px;
|
|
||||||
border-radius: 0px;
|
|
||||||
font-family: 'Arcade', Arial, sans-serif;
|
|
||||||
font-size: 15px;
|
|
||||||
color: rgb(119, 119, 119);
|
|
||||||
}
|
|
||||||
* input[type="checkbox"] {
|
|
||||||
}
|
|
||||||
* input[type=submit] {
|
|
||||||
background-color: #333333;
|
|
||||||
border: 1px solid #C0C0C0;
|
|
||||||
padding: 0.5em;
|
|
||||||
width: 100%;
|
|
||||||
border-radius: 0px;
|
|
||||||
font-family: 'Arcade', Arial, sans-serif;
|
|
||||||
font-size: 1.3em;
|
|
||||||
color: rgb(119, 119, 119);
|
|
||||||
}
|
|
||||||
* button {
|
|
||||||
background-color: #333333;
|
|
||||||
border: 1px solid #C0C0C0;
|
|
||||||
padding: 0.5em;
|
|
||||||
margin-bottom: 15px;
|
|
||||||
width: 100%;
|
|
||||||
border-radius: 0px;
|
|
||||||
font-family: 'Arcade', Arial, sans-serif;
|
|
||||||
font-size: 1.3em;
|
|
||||||
color: rgb(119, 119, 119);
|
|
||||||
}
|
|
||||||
* textarea {
|
|
||||||
background-color: #333333;
|
|
||||||
border: 1px solid #C0C0C0;
|
|
||||||
padding: 0em;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
width: 100%;
|
|
||||||
height: 200px;
|
|
||||||
border-radius: 0px;
|
|
||||||
font-family: 'Courier', Arial, sans-serif;
|
|
||||||
font-size: 1em;
|
|
||||||
color: rgb(119, 119, 119);
|
|
||||||
}
|
|
||||||
.hide {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
.gridstyle{
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100vw;
|
|
||||||
height: 100vh;
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(auto-fill, 20px);
|
|
||||||
grid-template-rows: repeat(auto-fill, 20px);
|
|
||||||
grid-gap: 0px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<div class = top-part>
|
|
||||||
<h1>Led Matrix Pixel Art Converter</h1>
|
|
||||||
<h2>Convert image to WLED JSON (pixel art on WLED matrix)</h2>
|
|
||||||
<p>
|
|
||||||
<table id="fieldTable" style="width: 100%; table-layout: fixed; align-content: center;">
|
|
||||||
<tr>
|
|
||||||
<td style="vertical-align: middle;">
|
|
||||||
<label for="ledSetupSelector">Led setup:</label>
|
|
||||||
</td>
|
|
||||||
<td style="vertical-align: middle;">
|
|
||||||
<select id="ledSetupSelector">
|
|
||||||
<option value="matrix" selected>2D Matrix</option>
|
|
||||||
<option value="r2l">Serpentine, first row right to left <-</option>
|
|
||||||
<option value="l2r">Serpentine, first row left to right -></option>
|
|
||||||
</select>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td style="vertical-align: middle;">
|
|
||||||
<label for="formatSelector">Output format:</label>
|
|
||||||
</td>
|
|
||||||
<td style="vertical-align: middle;">
|
|
||||||
<select id="formatSelector">
|
|
||||||
<option value="wled" selected>WLED JSON</option>
|
|
||||||
<option value="curl">CURL</option>
|
|
||||||
<option value="ha">Home Assistant YAML</option>
|
|
||||||
</select>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td style="vertical-align: middle;">
|
|
||||||
<label for="colorFormatSelector">Color code format:</label>
|
|
||||||
</td>
|
|
||||||
<td style="vertical-align: middle;">
|
|
||||||
<select id="colorFormatSelector">
|
|
||||||
<option value="hex" selected>HEX (#f4f4f4)</option>
|
|
||||||
<option value="dec">DEC (244,244,244)</option>
|
|
||||||
</select>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td style="vertical-align: middle;">
|
|
||||||
<label for="addressingSelector">Addressing:</label>
|
|
||||||
</td>
|
|
||||||
<td style="vertical-align: middle;">
|
|
||||||
<select id="addressingSelector">
|
|
||||||
<option value="range" selected>Range (10, 17, #f4f4f4)</option>
|
|
||||||
<option value="single">Single (#f4f4f4)</option>
|
|
||||||
</select>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td style="vertical-align: middle;">
|
|
||||||
<label for="brightnessNumber">Brightness:</label>
|
|
||||||
</td>
|
|
||||||
<td style="vertical-align: middle; display: flex; align-items: center;">
|
|
||||||
<input type="range" id="brightnessNumber" min="1" max="255" value="127">
|
|
||||||
<span id="brightnessValue">100</span>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td style="vertical-align: middle;">
|
|
||||||
<label for="colorLimitNumber">Max no of colors/JSON:</label>
|
|
||||||
</td>
|
|
||||||
<td style="vertical-align: middle; display: flex; align-items: center;">
|
|
||||||
<input type="range" id="colorLimitNumber" min="1" max="512" value="256">
|
|
||||||
<span id="colorLimitValue" >256</span>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr class="ha-hide">
|
|
||||||
<td style="vertical-align: middle;">
|
|
||||||
<label for="haID">HA Device ID:</label>
|
|
||||||
</td>
|
|
||||||
<td style="vertical-align: middle;">
|
|
||||||
<input type="text" id="haID" value="pixel_art_controller_001">
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr class="ha-hide">
|
|
||||||
<td style="vertical-align: middle;">
|
|
||||||
<label for="haUID">HA Device Unique ID:</label>
|
|
||||||
</td>
|
|
||||||
<td style="vertical-align: middle;">
|
|
||||||
<input type="text" id="haUID" value="pixel_art_controller_001a">
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr class="ha-hide">
|
|
||||||
<td style="vertical-align: middle;">
|
|
||||||
<label for="haName">HA Device Name:</label>
|
|
||||||
</td>
|
|
||||||
<td style="vertical-align: middle;">
|
|
||||||
<input type="text" id="haName" value="Pixel Art Kitchen">
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td style="vertical-align: middle;">
|
|
||||||
<label for="curlUrl">Device IP/host name:</label>
|
|
||||||
</td>
|
|
||||||
<td style="vertical-align: middle;">
|
|
||||||
<input type="text" id="curlUrl" value="">
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td style="vertical-align: middle;">
|
|
||||||
<label for="renderCheckbox">Show pixel rendering</label>
|
|
||||||
</td>
|
|
||||||
<td style="vertical-align: middle;">
|
|
||||||
<input type="checkbox" id="renderCheckbox" checked>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td style="vertical-align: middle;">
|
|
||||||
<label for="helpCheckbox">Show help/about</label>
|
|
||||||
</td>
|
|
||||||
<td style="vertical-align: middle;">
|
|
||||||
<input type="checkbox" id="helpCheckbox">
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
|
|
||||||
</p>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div id="help-container" style="display: none">>
|
|
||||||
<p>
|
|
||||||
1a. Create a GIF or PNG ( <a href="https://www.pixilart.com/" target="_blank">www.pixilart.com</a> ) <br>
|
|
||||||
1b. Download some image that fits your led matrix ( <a href="https://www.spriters-resource.com/" target="_blank">www.spriters-resource.com</a>) <br>
|
|
||||||
2. Upload the image using the file picker or drag-and-drop it <br>
|
|
||||||
3. Select the led setup that matches your WLED. ( <a href="https://kno.wled.ge/advanced/mapping/" target="_blank">https://kno.wled.ge</a>) <br>
|
|
||||||
4. Select Otput format. <br>
|
|
||||||
- WLED is pure JSON in the way the documentation descripbes it.<br> Use in WLED (presets?)<br>
|
|
||||||
- CURL is formated as a curl command you can past into a command <br> window and test yor led matrix<br>
|
|
||||||
- Home Assistant is teh full YAML you can past into your <br> configuration.yaml in Home Assistant<br>
|
|
||||||
5. Select Color format. Select HEX if possible (more efficiant)<br>
|
|
||||||
6. Select Addressing scheme. Send all pixels individually or try to <br> send ranges of the same color.<br>
|
|
||||||
7. Select brighness value<br>
|
|
||||||
8. According to docs WLED can handle max 256 colors/command. So the JSON is <br> split if you have larger images. <br> Lower this value if you have issues.<br>
|
|
||||||
9. Set Home Assistant Device values if you are going to use HA<br>
|
|
||||||
10. Set the device IP/host for HA and CURL to work<br>
|
|
||||||
11. Press the convert button <br>
|
|
||||||
12. Copy the generated JSON and put it somewhere in WLED<br> , Run CURL or paste to Home Assistant
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
This tool is a proof of concept and work in progress. As you might expect, there is absolutely no warranty, what so ever.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
The Arcade font is copyright (c) Jakob Fischer at www.pizzadude.dk, all rights reserved. Do not distribute without the author's permission.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
It should be said that I don‘t acctually own a setup for this. I basically did it to play around over new years. But I have ordered a matrix now, and a small controller chip... let's see how that works out.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<label for="file-picker">
|
|
||||||
<div id="drop-zone">
|
|
||||||
Drop image here <br>or <br>
|
|
||||||
Click to select a file
|
|
||||||
</div>
|
|
||||||
</label>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<input type="file" id="file-picker" style="display: none;">
|
|
||||||
<div style="width: 100%; text-align: center;" >
|
|
||||||
<img id="preview" style="display: block; margin: 0 auto;"><br>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<form id="form">
|
|
||||||
<input id="submitConvert" type="submit" value="Convert to JSON for WLED" style="display: none">
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<div id="raw-image-container" style="display: none">
|
|
||||||
<img id="image" src="" alt="RawImage image">
|
|
||||||
</div>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div id="image-container" style="display: none">
|
|
||||||
<div id="image-info" style="display: none"></div>
|
|
||||||
<p>
|
|
||||||
<div>
|
|
||||||
<textarea id="JSONled"></textarea>
|
|
||||||
<br>
|
|
||||||
<button id="copyJSONledbutton" >Copy led-JSON to Clipboard</button>
|
|
||||||
<br>
|
|
||||||
<button id="sendJSONledbutton" >Send to Device</button>
|
|
||||||
<br>
|
|
||||||
</div>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id=bottom-part style="display: none" class=bottom-part></div>
|
|
||||||
<canvas id="pixelCanvas"></canvas>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
|
|
||||||
// STATICS.JS
|
|
||||||
var curlStart = 'curl -X POST "http://';
|
|
||||||
var curlMid1 = '/json/state" -d \'';
|
|
||||||
var curlEnd = '\' -H "Content-Type: application/json"';
|
|
||||||
|
|
||||||
const haStart = '#Uncomment if you don\'t allready have these defined in your switch section of your configuration.yaml\n#- platform: command_line\n #switches:\n ';
|
|
||||||
const haMid1 = '\n friendly_name: ';
|
|
||||||
const haMid2 = '\n unique_id: ';
|
|
||||||
const haMid3= '\n command_on: >\n ';
|
|
||||||
const haMid4 = '\n command_off: >\n curl -X POST "http://';
|
|
||||||
const haEnd = '/json/state" -d \'{"on":false}\' -H "Content-Type: application/json"';
|
|
||||||
const haCommandLeading = ' ';
|
|
||||||
|
|
||||||
const JSONledStringStart = '{"on":true, "bri":';
|
|
||||||
const JSONledStringMid1 = ', "seg":{"i":[';
|
|
||||||
const JSONledShortStringStart = '{';
|
|
||||||
const JSONledShortStringMid1 = '"seg":{"i":[';
|
|
||||||
const JSONledStringEnd = ']}}';
|
|
||||||
|
|
||||||
// INDEX.JS
|
|
||||||
|
|
||||||
//Start up code
|
|
||||||
console.log(location.host);
|
|
||||||
document.getElementById('curlUrl').value = location.host;
|
|
||||||
let httpArray = [];
|
|
||||||
|
|
||||||
|
|
||||||
//On submit button pressed =======================
|
|
||||||
document.getElementById('form').addEventListener('submit', function(event) {
|
|
||||||
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
let base64Image = document.getElementById('preview').src;
|
|
||||||
if (isValidBase64Gif(base64Image)) {
|
|
||||||
document.getElementById('image').src = base64Image;
|
|
||||||
getPixelRGBValues(base64Image);
|
|
||||||
document.getElementById('image-container').style.display = "block"
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
let infoDiv = document.getElementById('image-info');
|
|
||||||
let imageInfo = '<p><b>WARNING!</b> File does not appear to be a valid GIF image</p>'
|
|
||||||
infoDiv.innerHTML = imageInfo;
|
|
||||||
infoDiv.style.display = "block"
|
|
||||||
document.getElementById('image-container').style.display = "none";
|
|
||||||
document.getElementById('JSONled').value = '';
|
|
||||||
console.log("The string '" + base64Image + "' is not a valid base64 GIF image.");
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
// Code for copying the generated string to clipboard
|
|
||||||
|
|
||||||
copyJSONledbutton.addEventListener('click', async () => {
|
|
||||||
JSONled.select();
|
|
||||||
try {
|
|
||||||
await navigator.clipboard.writeText('test text');
|
|
||||||
console.log('Text copied to clipboard');
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Failed to copy text: ', err);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
sendJSONledbutton.addEventListener('click', async () => {
|
|
||||||
if (window.location.protocol === "https:") {
|
|
||||||
alert('Will only be available when served over http (or WLED is run over https)');
|
|
||||||
} else {
|
|
||||||
postPixels();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
async function postPixels() {
|
|
||||||
for (let i of httpArray) {
|
|
||||||
try {
|
|
||||||
console.log(i);
|
|
||||||
console.log(i.length);
|
|
||||||
const response = await fetch('http://'+document.getElementById('curlUrl').value+'/json/state', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
//'Content-Type': 'text/html; charset=UTF-8'
|
|
||||||
},
|
|
||||||
body: i
|
|
||||||
});
|
|
||||||
const data = await response.json();
|
|
||||||
console.log(data);
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let helpCheckbox = document.getElementById("helpCheckbox");
|
|
||||||
let helpDiv = document.getElementById("help-container");
|
|
||||||
|
|
||||||
helpCheckbox.addEventListener("change", function() {
|
|
||||||
if (helpCheckbox.checked) {
|
|
||||||
helpDiv.style.display = "block";
|
|
||||||
} else {
|
|
||||||
helpDiv.style.display = "none";
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
//File uploader code
|
|
||||||
const dropZone = document.getElementById('drop-zone');
|
|
||||||
const filePicker = document.getElementById('file-picker');
|
|
||||||
const preview = document.getElementById('preview');
|
|
||||||
|
|
||||||
// Listen for dragenter, dragover, and drop events
|
|
||||||
dropZone.addEventListener('dragenter', dragEnter);
|
|
||||||
dropZone.addEventListener('dragover', dragOver);
|
|
||||||
dropZone.addEventListener('drop', dropped);
|
|
||||||
dropZone.addEventListener('click', zoneClicked);
|
|
||||||
|
|
||||||
// Listen for change event on file picker
|
|
||||||
filePicker.addEventListener('change', filePicked);
|
|
||||||
|
|
||||||
// Handle zone click
|
|
||||||
function zoneClicked(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
//this.classList.add('drag-over');
|
|
||||||
//alert('Hej');
|
|
||||||
filePicker.click();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle dragenter
|
|
||||||
function dragEnter(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
this.classList.add('drag-over');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle dragover
|
|
||||||
function dragOver(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle drop
|
|
||||||
function dropped(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
this.classList.remove('drag-over');
|
|
||||||
|
|
||||||
// Get the dropped file
|
|
||||||
const file = e.dataTransfer.files[0];
|
|
||||||
updatePreview(file);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle file picked
|
|
||||||
function filePicked(e) {
|
|
||||||
// Get the picked file
|
|
||||||
const file = e.target.files[0];
|
|
||||||
updatePreview(file);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the preview image
|
|
||||||
function updatePreview(file) {
|
|
||||||
// Use FileReader to read the file
|
|
||||||
const reader = new FileReader();
|
|
||||||
reader.onload = function() {
|
|
||||||
// Update the preview image
|
|
||||||
preview.src = reader.result;
|
|
||||||
document.getElementById("submitConvert").style.display = "block";
|
|
||||||
};
|
|
||||||
reader.readAsDataURL(file);
|
|
||||||
}
|
|
||||||
|
|
||||||
function isValidBase64Gif(string) {
|
|
||||||
// Use a regular expression to check that the string is a valid base64 string
|
|
||||||
/*
|
|
||||||
const base64gifPattern = /^data:image\/gif;base64,([A-Za-z0-9+/:]{4})*([A-Za-z0-9+/:]{3}=|[A-Za-z0-9+/:]{2}==)?$/;
|
|
||||||
const base64pngPattern = /^data:image\/png;base64,([A-Za-z0-9+/:]{4})*([A-Za-z0-9+/:]{3}=|[A-Za-z0-9+/:]{2}==)?$/;
|
|
||||||
const base64jpgPattern = /^data:image\/jpg;base64,([A-Za-z0-9+/:]{4})*([A-Za-z0-9+/:]{3}=|[A-Za-z0-9+/:]{2}==)?$/;
|
|
||||||
const base64webpPattern = /^data:image\/webp;base64,([A-Za-z0-9+/:]{4})*([A-Za-z0-9+/:]{3}=|[A-Za-z0-9+/:]{2}==)?$/;
|
|
||||||
*/
|
|
||||||
//REMOVED, Any image appear to work as long as it can be drawn to the canvas. Leavingg code in for future use, possibly
|
|
||||||
if (1==1 || base64gifPattern.test(string) || base64pngPattern.test(string) || base64jpgPattern.test(string) || base64webpPattern.test(string)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
//Not OK
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
document.getElementById("brightnessNumber").oninput = function() {
|
|
||||||
document.getElementById("brightnessValue").textContent = this.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
document.getElementById("colorLimitNumber").oninput = function() {
|
|
||||||
document.getElementById("colorLimitValue").textContent = this.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
var formatSelector = document.getElementById("formatSelector");
|
|
||||||
var hideableRows = document.querySelectorAll(".ha-hide");
|
|
||||||
for (var i = 0; i < hideableRows.length; i++) {
|
|
||||||
hideableRows[i].classList.add("hide");
|
|
||||||
}
|
|
||||||
formatSelector.addEventListener("change", function() {
|
|
||||||
for (var i = 0; i < hideableRows.length; i++) {
|
|
||||||
hideableRows[i].classList.toggle("hide", this.value !== "ha");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
//GETPIXELVALUES.JS
|
|
||||||
|
|
||||||
function getPixelRGBValues(base64Image) {
|
|
||||||
httpArray = [];
|
|
||||||
const copyJSONledbutton = document.getElementById('copyJSONledbutton');
|
|
||||||
const JSONled = document.getElementById('JSONled');
|
|
||||||
const maxNoOfColorsInCommandSting = document.getElementById('colorLimitNumber').value;
|
|
||||||
|
|
||||||
let selectedIndex = -1;
|
|
||||||
|
|
||||||
let selector = document.getElementById("formatSelector");
|
|
||||||
selectedIndex = selector.selectedIndex;
|
|
||||||
const formatSelection = selector.options[selectedIndex].value;
|
|
||||||
|
|
||||||
selector = document.getElementById("ledSetupSelector");
|
|
||||||
selectedIndex = selector.selectedIndex;
|
|
||||||
const ledSetupSelection = selector.options[selectedIndex].value;
|
|
||||||
|
|
||||||
selector = document.getElementById("colorFormatSelector");
|
|
||||||
selectedIndex = selector.selectedIndex;
|
|
||||||
let hexValueCheck = true;
|
|
||||||
if (selector.options[selectedIndex].value == 'dec'){
|
|
||||||
hexValueCheck = false
|
|
||||||
}
|
|
||||||
|
|
||||||
selector = document.getElementById("addressingSelector");
|
|
||||||
selectedIndex = selector.selectedIndex;
|
|
||||||
let segmentValueCheck = true;
|
|
||||||
if (selector.options[selectedIndex].value == 'single'){
|
|
||||||
segmentValueCheck = false
|
|
||||||
}
|
|
||||||
|
|
||||||
let segmentString = ''
|
|
||||||
let curlString = ''
|
|
||||||
let haString = ''
|
|
||||||
let haCommandCurlString = '';
|
|
||||||
|
|
||||||
|
|
||||||
let colorSeparatorStart = '\'';
|
|
||||||
let colorSeparatorEnd = '\'';
|
|
||||||
if (!hexValueCheck){
|
|
||||||
colorSeparatorStart = '[';
|
|
||||||
colorSeparatorEnd = ']';
|
|
||||||
}
|
|
||||||
// Warnings
|
|
||||||
let hasTransparency = false; //If alpha < 255 is detected on any pixel, this is set to true in code below
|
|
||||||
let imageInfo = '';
|
|
||||||
|
|
||||||
// Create an off-screen canvas
|
|
||||||
var canvas = document.createElement('canvas');
|
|
||||||
var context = canvas.getContext('2d');
|
|
||||||
|
|
||||||
// Create an image element and set its src to the base64 image
|
|
||||||
var image = new Image();
|
|
||||||
image.src = base64Image;
|
|
||||||
|
|
||||||
// Wait for the image to load before drawing it onto the canvas
|
|
||||||
image.onload = function() {
|
|
||||||
// Set the canvas size to the same as the image
|
|
||||||
canvas.width = image.width;
|
|
||||||
canvas.height = image.height;
|
|
||||||
imageInfo = '<p>Width: ' + image.width + ', Height: ' + image.height + ' (make sure this matches your led matrix setup)</p>'
|
|
||||||
|
|
||||||
// Draw the image onto the canvas
|
|
||||||
context.drawImage(image, 0, 0);
|
|
||||||
|
|
||||||
// Get the pixel data from the canvas
|
|
||||||
var pixelData = context.getImageData(0, 0, image.width, image.height).data;
|
|
||||||
|
|
||||||
// Create an array to hold the RGB values of each pixel
|
|
||||||
var pixelRGBValues = [];
|
|
||||||
|
|
||||||
// If the first row of the led matrix is right -> left
|
|
||||||
let right2leftAdjust = 1;
|
|
||||||
|
|
||||||
if (ledSetupSelection == 'l2r'){
|
|
||||||
right2leftAdjust = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Loop through the pixel data and get the RGB values of each pixel
|
|
||||||
for (var i = 0; i < pixelData.length; i += 4) {
|
|
||||||
var r = pixelData[i];
|
|
||||||
var g = pixelData[i + 1];
|
|
||||||
var b = pixelData[i + 2];
|
|
||||||
var a = pixelData[i + 3];
|
|
||||||
|
|
||||||
let pixel = i/4
|
|
||||||
let row = Math.floor(pixel/image.width);
|
|
||||||
let led = pixel;
|
|
||||||
if (ledSetupSelection == 'matrix'){
|
|
||||||
//Do nothing, the matrix is set upp like the index in the image
|
|
||||||
//Every row starts from the left, i.e. no zigzagging
|
|
||||||
}
|
|
||||||
else if ((row + right2leftAdjust) % 2 === 0) {
|
|
||||||
//Setup is traditional zigzag
|
|
||||||
//right2leftAdjust basically flips the row order if = 1
|
|
||||||
//Row is left to right
|
|
||||||
//Leave led index as pixel index
|
|
||||||
|
|
||||||
} else {
|
|
||||||
//Setup is traditional zigzag
|
|
||||||
//Row is right to left
|
|
||||||
//Invert index of row for led
|
|
||||||
let indexOnRow = led - (row * image.width);
|
|
||||||
let maxIndexOnRow = image.width - 1;
|
|
||||||
let reversedIndexOnRow = maxIndexOnRow - indexOnRow;
|
|
||||||
led = (row * image.width) + reversedIndexOnRow;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add the RGB values to the pixel RGB values array
|
|
||||||
pixelRGBValues.push([r, g, b, a, led, pixel, row]);
|
|
||||||
}
|
|
||||||
|
|
||||||
pixelRGBValues.sort((a, b) => a[5] - b[5]);
|
|
||||||
|
|
||||||
//Copy the values to a new array for resorting
|
|
||||||
let ledRGBValues = [... pixelRGBValues];
|
|
||||||
|
|
||||||
//Sort the array based on led index
|
|
||||||
ledRGBValues.sort((a, b) => a[4] - b[4]);
|
|
||||||
|
|
||||||
//Generate JSON in WLED format
|
|
||||||
let JSONledString = '';
|
|
||||||
let JSONledStringShort = '';
|
|
||||||
|
|
||||||
//Set starting values for the segment check to something that is no color
|
|
||||||
let segmentStart = -1;
|
|
||||||
let maxi = ledRGBValues.length;
|
|
||||||
let curentColorIndex = 0
|
|
||||||
let commandArray = [];
|
|
||||||
|
|
||||||
//For evry pixel in the LED array
|
|
||||||
for (let i = 0; i < maxi; i++) {
|
|
||||||
let pixel = ledRGBValues[i];
|
|
||||||
let r = pixel[0];
|
|
||||||
let g = pixel[1];
|
|
||||||
let b = pixel[2];
|
|
||||||
let a = pixel[3];
|
|
||||||
let segmentString = '';
|
|
||||||
let segmentEnd = -1;
|
|
||||||
|
|
||||||
if(segmentValueCheck){
|
|
||||||
if (segmentStart < 0){
|
|
||||||
//This is the first led of a new segment
|
|
||||||
segmentStart = i;
|
|
||||||
} //Else we allready have a start index
|
|
||||||
|
|
||||||
if (i < maxi - 1){
|
|
||||||
|
|
||||||
let iNext = i + 1;
|
|
||||||
let nextPixel = ledRGBValues[iNext];
|
|
||||||
|
|
||||||
if (nextPixel[0] != r || nextPixel[1] != g || nextPixel[2] != b ){
|
|
||||||
//Next pixel has new color
|
|
||||||
//The current segment ends with this pixel
|
|
||||||
segmentEnd = i + 1 //WLED wants the NEXT LED as the stop led...
|
|
||||||
segmentString = segmentStart + ',' + segmentEnd + ',';
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
//This is the last pixel, so the segment must end
|
|
||||||
segmentEnd = i + 1;
|
|
||||||
segmentString = segmentStart + ',' + segmentEnd + ',';
|
|
||||||
}
|
|
||||||
} else{
|
|
||||||
//Write every pixel
|
|
||||||
if (JSONledString == ''){
|
|
||||||
//If addressing is single, we ned to start every command with a starting possition
|
|
||||||
JSONledString = i + ', \'dummy\',';
|
|
||||||
}
|
|
||||||
|
|
||||||
segmentStart = i
|
|
||||||
segmentEnd = i
|
|
||||||
//Segment string should be empty for when addressing single. So no need to set it again.
|
|
||||||
}
|
|
||||||
|
|
||||||
if (a < 255){
|
|
||||||
hasTransparency = true; //If ANY pixel has alpha < 255 then this is set to true to warn the user
|
|
||||||
}
|
|
||||||
|
|
||||||
if (segmentEnd > -1){
|
|
||||||
//This is the last pixel in the segment, write to the JSONledString
|
|
||||||
//Return color value in selected format
|
|
||||||
let colorValueString = r + ',' + g + ',' + b ;
|
|
||||||
|
|
||||||
if (hexValueCheck){
|
|
||||||
const [red, green, blue] = [r, g, b];
|
|
||||||
colorValueString = `${[red, green, blue].map(x => x.toString(16).padStart(2, '0')).join('')}`;
|
|
||||||
} else{
|
|
||||||
//do nothing, allready set
|
|
||||||
}
|
|
||||||
|
|
||||||
JSONledString = JSONledString + segmentString + colorSeparatorStart + colorValueString + colorSeparatorEnd;
|
|
||||||
|
|
||||||
curentColorIndex = curentColorIndex + 1; // We've just added a new color to the string so up the count with one
|
|
||||||
|
|
||||||
if (curentColorIndex % maxNoOfColorsInCommandSting === 0 || i == maxi - 1) {
|
|
||||||
|
|
||||||
//If we have accumulated the max number of colors to send in a single command or if this is the last pixel, we should write the current colorstring to the array
|
|
||||||
commandArray.push(JSONledString);
|
|
||||||
JSONledString = ''; //Start on an new command string
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
//Add a comma to continue the command string
|
|
||||||
JSONledString = JSONledString + ','
|
|
||||||
}
|
|
||||||
//Reset segment values
|
|
||||||
segmentStart = - 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
JSONledString = ''
|
|
||||||
|
|
||||||
//For evry commandString in the array
|
|
||||||
for (let i = 0; i < commandArray.length; i++) {
|
|
||||||
let thisJSONledString = JSONledStringStart + document.getElementById('brightnessNumber').value + JSONledStringMid1 + commandArray[i] + JSONledStringEnd;
|
|
||||||
httpArray.push(thisJSONledString);
|
|
||||||
|
|
||||||
let thiscurlString = curlStart + document.getElementById('curlUrl').value + curlMid1 + thisJSONledString + curlEnd;
|
|
||||||
|
|
||||||
//Aggregated Strings That should be returned to the user
|
|
||||||
if (i > 0){
|
|
||||||
JSONledString = JSONledString + '\n';
|
|
||||||
curlString = curlString + ' && ';
|
|
||||||
}
|
|
||||||
JSONledString = JSONledString + thisJSONledString;
|
|
||||||
curlString = curlString + thiscurlString;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
haString = haStart + document.getElementById('haID').value + haMid1 + document.getElementById('haName').value + haMid2 + document.getElementById('haUID').value + haMid3 +curlString + haMid3 + document.getElementById('curlUrl').value + haEnd;
|
|
||||||
|
|
||||||
if (formatSelection == 'wled'){
|
|
||||||
JSONled.value = JSONledString;
|
|
||||||
} else if (formatSelection == 'curl'){
|
|
||||||
JSONled.value = curlString;
|
|
||||||
} else if (formatSelection == 'ha'){
|
|
||||||
JSONled.value = haString;
|
|
||||||
} else {
|
|
||||||
JSONled.value = 'ERROR!/n' + formatSelection + ' is an unknown format.'
|
|
||||||
}
|
|
||||||
|
|
||||||
let infoDiv = document.getElementById('image-info');
|
|
||||||
let canvasDiv = document.getElementById('image-info');
|
|
||||||
if (hasTransparency){
|
|
||||||
imageInfo = imageInfo + '<p><b>WARNING!</b> Transparency info detected in image. Transparency (alpha) has been ignored. To ensure you get the result you desire, use only solid colors in your image.</p>'
|
|
||||||
}
|
|
||||||
|
|
||||||
infoDiv.innerHTML = imageInfo;
|
|
||||||
canvasDiv.style.display = "block"
|
|
||||||
|
|
||||||
//Drawing the image
|
|
||||||
drawBoxes(pixelRGBValues, image.width, image.width);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//BOXDRAW.JS
|
|
||||||
|
|
||||||
function drawBoxes(inputPixelArray, widthPixels, heightPixels) {
|
|
||||||
|
|
||||||
// Get a reference to the canvas element
|
|
||||||
var canvas = document.getElementById('pixelCanvas');
|
|
||||||
|
|
||||||
// Get the canvas context
|
|
||||||
var ctx = canvas.getContext('2d');
|
|
||||||
|
|
||||||
// Set the width and height of the canvas
|
|
||||||
if (window.innerHeight < window.innerWidth) {
|
|
||||||
canvas.width = Math.floor(window.innerHeight * 0.98);
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
canvas.width = Math.floor(window.innerWidth * 0.98);
|
|
||||||
}
|
|
||||||
//canvas.height = window.innerWidth;
|
|
||||||
|
|
||||||
let pixelSize = Math.floor(canvas.width/widthPixels);
|
|
||||||
|
|
||||||
//Set the canvas height to fit the right number of pixelrows
|
|
||||||
canvas.height = (pixelSize * heightPixels) + 10
|
|
||||||
|
|
||||||
//Iterate through the matrix
|
|
||||||
for (let y = 0; y < heightPixels; y++) {
|
|
||||||
for (let x = 0; x < widthPixels; x++) {
|
|
||||||
|
|
||||||
// Calculate the index of the current pixel
|
|
||||||
let i = (y*widthPixels) + x;
|
|
||||||
|
|
||||||
//Gets the RGB of the current pixel
|
|
||||||
let pixel = inputPixelArray[i];
|
|
||||||
|
|
||||||
let pixelColor = 'rgb(' + pixel[0] + ', ' + pixel[1] + ', ' + pixel[2] + ')';
|
|
||||||
let r = pixel[0];
|
|
||||||
let g = pixel[1];
|
|
||||||
let b = pixel[2];
|
|
||||||
let pos = pixel[4];
|
|
||||||
|
|
||||||
let textColor = 'rgb(128,128,128)';
|
|
||||||
|
|
||||||
// Set the fill style to the pixel color
|
|
||||||
ctx.fillStyle = pixelColor;
|
|
||||||
|
|
||||||
//Draw the rectangle
|
|
||||||
ctx.fillRect(x * pixelSize, y * pixelSize, pixelSize, pixelSize);
|
|
||||||
|
|
||||||
// Draw a border on the box
|
|
||||||
ctx.strokeStyle = '#888888';
|
|
||||||
ctx.lineWidth = 1;
|
|
||||||
ctx.strokeRect(x * pixelSize, y * pixelSize, pixelSize, pixelSize);
|
|
||||||
|
|
||||||
//Write text to box
|
|
||||||
ctx.font = "10px Arial";
|
|
||||||
ctx.fillStyle = textColor;
|
|
||||||
ctx.textAlign = "center";
|
|
||||||
ctx.textBaseline = 'middle';
|
|
||||||
ctx.fillText((pos + 1), (x * pixelSize) + (pixelSize /2), (y * pixelSize) + (pixelSize /2));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function drawBackground() {
|
|
||||||
const grid = document.createElement("div");
|
|
||||||
grid.id = "grid";
|
|
||||||
grid.classList.add("grid-class");
|
|
||||||
grid.style.cssText = "";
|
|
||||||
|
|
||||||
const boxSize = 20;
|
|
||||||
const boxCount = Math.ceil(window.innerWidth / boxSize) * Math.ceil(window.innerHeight / boxSize);;
|
|
||||||
|
|
||||||
for (let i = 0; i < boxCount; i++) {
|
|
||||||
const box = document.createElement("div");
|
|
||||||
box.classList.add("box");
|
|
||||||
box.style.backgroundColor = getRandomColor();
|
|
||||||
grid.appendChild(box);
|
|
||||||
}
|
|
||||||
grid.style.zIndex = -1;
|
|
||||||
document.body.appendChild(grid);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getRandomColor() {
|
|
||||||
const letters = "0123456789ABCDEF";
|
|
||||||
let color = "rgba(";
|
|
||||||
for (let i = 0; i < 3; i++) {
|
|
||||||
color += Math.floor(Math.random() * 256) + ",";
|
|
||||||
}
|
|
||||||
color += "0.05)";
|
|
||||||
return color;
|
|
||||||
}
|
|
||||||
|
|
||||||
window.drawBackground = drawBackground;
|
|
||||||
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
Reference in New Issue
Block a user