Files
PixelArtConverter/beta/pixart.htm
Henrik 755377286f Adjusted duration calculation and default values.
Also set repeat on as default
2023-04-20 19:22:13 +02:00

119 lines
37 KiB
HTML

<!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>
WLED Pixel Art Converter</title> <style>
.box{border:2px solid #fff}body{font-family:Arial,sans-serif;background-color:#111}.top-part{width:600px;margin:0 auto}.container{max-width:100% -40px;border-radius:0;padding:20px;text-align:center}h1{font-size:2.3em;color:#ddd;margin:1px 0;font-family:Arial,sans-serif;line-height:.5}h2{font-size:1.1em;color:rgba(221,221,221,.61);margin:1px 0;font-family:Arial,sans-serif;line-height:.5;text-align:center}h3{font-size:.7em;color:rgba(221,221,221,.61);margin:1px 0;font-family:Arial,sans-serif;line-height:1.4;text-align:center;align-items:center;justify-content:center;display:flex}p{font-size:1em;color:#777;line-height:1.5;font-family:Arial,sans-serif}#fieldTable{font-size:1 em;color:#777;line-height:1;font-family:Arial,sans-serif}.scaleTableClass{font-size:1 em;color:#777;line-height:1;font-family:Arial,sans-serif}#drop-zone{display:block;width:100%-40px;border:3px dashed #ddd;border-radius:0;text-align:center;padding:20px;margin:0;cursor:pointer;font-family:Arial,sans-serif;font-size:15px;color:#777}#file-picker{display:none}.adaptiveTD{display:flex;flex-direction:row;flex-wrap:nowrap;align-items:center;vertical-align:middle}.mainSelector{background-color:#222;color:#ddd;border:1px solid #333;margin-top:4px;margin-bottom:4px;padding:0 8px;height:28px;font-size:15px;border-radius:7px;flex-grow:1;display:flex;align-items:center;justify-content:center}.adaptiveSelector{background-color:#222;color:#ddd;border:1px solid #333;margin-top:4px;margin-bottom:4px;padding:0 8px;height:28px;font-size:15px;border-radius:7px;flex-grow:1;display:none}.segmentsDiv{width:36px;padding-left:5px}.sndRnDiv{width:36px;padding-left:5px}* input[type=range]{appearance:none;-moz-appearance:none;-webkit-appearance:none;flex-grow:1;padding:0;margin:4px 8px 4px 0;background-color:transparent;cursor:pointer;background:linear-gradient(to right,#bbb 50%,#333 50%);border-radius:7px}input[type=range]:focus{outline:0}input[type=range]::-webkit-slider-runnable-track{height:28px;cursor:pointer;background:0 0;border-radius:7px}input[type=range]::-webkit-slider-thumb{height:16px;width:16px;border-radius:50%;background:#fff;cursor:pointer;-webkit-appearance:none;margin-top:4px;border-radius:7px}input[type=range]::-moz-range-track{height:28px;background-color:rgba(0,0,0,0);border-radius:7px}input[type=range]::-moz-range-thumb{border:0 solid transparent;height:16px;width:16px;border-radius:7px;background:#fff}.rangeNumber{width:20px;vertical-align:middle}.fullTextField[type=text]{background-color:#222;border:1px solid #333;padding-inline-start:5px;margin-top:4px;margin-bottom:4px;height:24px;border-radius:0;font-family:Arial,sans-serif;font-size:15px;color:#ddd;border-radius:7px;flex-grow:1;display:flex;align-items:center;justify-content:center}.flxTFld{background-color:#222;border:1px solid #333;padding-inline-start:5px;height:24px;border-radius:0;font-family:Arial,sans-serif;font-size:15px;color:#ddd;border-radius:7px;flex-grow:1;display:flex;align-items:center;justify-content:center;width:100%;box-sizing:border-box}.durFld{background-color:#222;border:1px solid #333;padding-inline-start:5px;height:24px;border-radius:0;font-family:Arial,sans-serif;font-size:15px;color:#ddd;border-radius:7px;flex-grow:1;display:flex;align-items:center;justify-content:center}* input[type=submit]{background-color:#222;border:1px solid #333;padding:.5em;width:100%;border-radius:24px;font-family:Arial,sans-serif;font-size:1.3em;color:#ddd}* button{background-color:#222;border:1px solid #333;padding-inline:5px;width:100%;border-radius:24px;font-family:Arial,sans-serif;font-size:1em;color:#ddd;display:flex;align-items:center;justify-content:center;cursor:pointer}#scaleDiv{display:flex;align-items:center;vertical-align:middle}#runDiv{display:flex;align-items:center;vertical-align:middle}textarea{grid-row:1/2;width:100%;height:200px;background-color:#222;border:1px solid #333;color:#ddd}.hide{display:none}.svg-icon{vertical-align:middle}#image-container{display:grid;grid-template-rows:1fr 1fr}#button-container{display:flex;padding-bottom:10px;padding-top:10px}.buttonclass{flex:1;padding-top:5px;padding-bottom:5px}.gap{width:10px}#submitConvert::before{content:"";display:inline-block;background-image:url('data:image/svg+xml;utf8, <svg style="width:24px;height:24px" viewBox="0 0 24 24" <path fill="currentColor" d="M12,6V9L16,5L12,1V4A8,8 0 0,0 4,12C4,13.57 4.46,15.03 5.24,16.26L6.7,14.8C6.25,13.97 6,13 6,12A6,6 0 0,1 12,6M18.76,7.74L17.3,9.2C17.74,10.04 18,11 18,12A6,6 0 0,1 12,18V15L8,19L12,23V20A8,8 0 0,0 20,12C20,10.43 19.54,8.97 18.76,7.74Z" /></svg>');width:36px;height:36px}#sizeDiv *{display:inline-block}.sizeInputFields{width:50px;background-color:#222;border:1px solid #333;padding-inline-start:5px;margin-top:-5px;height:24px;border-radius:7px;font-family:Arial,sans-serif;font-size:15px;color:#ddd}a:link{color:rgba(221,221,221,.61);background-color:transparent;text-decoration:none}a:visited{color:rgba(221,221,221,.61);background-color:transparent;text-decoration:none}a:hover{color:#ddd;background-color:transparent;text-decoration:none}a:active{color:rgba(221,221,221,.61);background-color:transparent;text-decoration:none}.stpIconTD{width:36px;margin:0}.stpImgTD{width:36px;margin:0}.stpDur{display:flex;align-items:center;justify-content:center}
</style> <link rel="shortcut icon"
href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAkNJREFUOE+lUstO21AUnHNsF0V+0EXVivAXfAe7qlL7EZEcQ5cQ8nCICbDJF7Trvhas6YbyDdBu2hSk7kihscK1fU91HRKKaFf1xhrP8ejMnCH850P/+v/cC2qcqw4gsGD3Hk/G23+bvSdw7nk1K887BeCQ4AGMAiEjcGaB7gndEfhRqdRQSC9ncQikAVisAWGBFggR5QvQ7UcTNd9mLvDdC2p2rhIN2G4U/vRbneyynzhps70oAPlhNPLjZna1v4/xVmtQHU8tzQXO3CCk4nrPD8MLN+6qd8ef8erwBKvH750XK1Xy4li9/fQFrw9PAGSDg8azuwJDN6jbuep7a+GFF3ezN0en5fDq8Qfn+UqVgjhW5bePpwCKwUHj6a2ACY7zrCdCC34UjrzutjLZiQC/kh0HlyPyu9tKzM4iGPf7uG5sDEwWpYWvrh85RZYAYAIp7+XayNtqytXeLqWbjUUNcLC+fuG2mpLu7tPl5sZDAnpLk0mzFDir+BFJlkCECaKFuSBYSlA4pLWtiRjEygIyErFzyIKjreYTlbZKgaHrRlzoHotweXeAhEhIxPBTDBhQYgEYhNbybINhxY1sbSwwkbHOZrzs3VRmjmnOk6bOkrqxMHT9yCqyxNybAFMg1mWRBGYlg2eboOSFGdT+IwM3Eil6zBwXZF1xkSWkOa6qtHleceuQvC+g4pZXiaOt9jyDb24Q2qKC5XTSvrlK3ZHMW04nLdMPKtQOMzoGTzPz69YNP2vi7D11ftvQWYCzyt7jfwO10TQgL5hT6QAAAABJRU5ErkJggg==">
<script type="text/javascript">
function gId(e){return d.getElementById(e)}function cE(e){return d.createElement(e)}var d=document
</script> </head> <body> <body> <div class="top-part"> <div
style="display:flex;justify-content:center"> <h1
style="display:flex;align-items:center"> <svg
style="width:36px;height:36px;margin-right:6px" viewBox="0 0 32 32"><path
fill="#003fff" d="M6 22h8v4H6zM14 14h4v8h-4zM18 10h4v8h-4zM22 6h8v4h-8z"/></svg>
WLED Pixel Art Converter </h1> </div> <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 class="adaptiveTD"> <select id="ledSetupSelector"
class="mainSelector"> <option value="matrix" selected="selected">2D Matrix
</option> <option value="r2l">Serpentine, first row right to left &lt;-</option>
<option value="l2r">Serpentine, first row left to right -&gt;</option>
</select> </td> </tr> <tr> <td style="vertical-align:middle"> <label
for="formatSelector">Output format:</label> </td> <td class="adaptiveTD">
<select id="formatSelector" class="mainSelector"> <option value="wled"
selected="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 class="adaptiveTD"> <select
id="colorFormatSelector" class="mainSelector"> <option value="hex"
selected="selected">HEX (&quot;f4f4f4&quot;)</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 class="adaptiveTD"> <select id="addressingSelector"
class="mainSelector"> <option value="hybrid" selected="selected">
Hybrid (&quot;f0f0f0&quot;,10, 17, &quot;f4f4f4&quot;)</option> <option
value="range">Range (10, 17, &quot;f4f4f4&quot;)</option> <option
value="single">Single (&quot;f4f4f4&quot;)</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="128"> <span
id="brightnessValue">128</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
class="adaptiveTD"> <input class="fullTextField" 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 class="adaptiveTD"> <input class="fullTextField" 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 class="adaptiveTD"> <input class="fullTextField" 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 class="adaptiveTD"> <input class="fullTextField" type="text"
id="curlUrl" value> </td> </tr> <tr> <td style="vertical-align:middle"> <label
for="targetSegment">Target segment id:</label> </td> <td class="adaptiveTD">
<input class="flxTFld" type="number" id="segID" value="0" min="0" max="63">
<select id="targetSegment" class="adaptiveSelector"> </select> <div
id="getSegmentsDiv" class="segmentsDiv"></div> </td> </tr> </table> <table
class="scaleTableClass" id="scaleTable"
style="width:100%;table-layout:fixed;align-content:center"> <tr> <td
style="vertical-align:middle"> <div id="scaleDiv"> <svg
style="width:36px;height:36px" viewBox="0 0 24 24" onclick="switchScale()"
cursor="pointer"><path fill="currentColor"
d="M17 7H7a5 5 0 0 0-5 5 5 5 0 0 0 5 5h10a5 5 0 0 0 5-5 5 5 0 0 0-5-5M7 15a3 3 0 0 1-3-3 3 3 0 0 1 3-3 3 3 0 0 1 3 3 3 3 0 0 1-3 3z"/>
</svg> &nbsp;Scale image </div> </td> <td style="vertical-align:middle"> <div
id="sizeDiv" style="display:none"> <label for="sizeX">W : </label> &nbsp;<input
class="sizeInputFields" type="number" id="sizeX" min="1" value="16">
&nbsp;&nbsp;&nbsp; <label for="sizeY">H : </label> &nbsp;<input
class="sizeInputFields" type="number" id="sizeY" min="1" value="16"> </div>
</td> </tr> </table> <table class="scaleTableClass" id="runTable"
style="width:100%;table-layout:fixed;align-content:center"> <tr
style="height:40px"> <td style="vertical-align:middle"> <div id="runDiv"> <svg
style="width:36px;height:36px" viewBox="0 0 24 24" onclick="switchRun()"
cursor="pointer"><path fill="currentColor"
d="M17 7H7a5 5 0 0 0-5 5 5 5 0 0 0 5 5h10a5 5 0 0 0 5-5 5 5 0 0 0-5-5M7 15a3 3 0 0 1-3-3 3 3 0 0 1 3-3 3 3 0 0 1 3 3 3 3 0 0 1-3 3z"/>
</svg> &nbsp;Generate flow file </div> </td> <td id="rnTD" class="adaptiveTD">
<input class="flxTFld" type="text" id="rnID" value="ID"> <div id="repRnDiv"
class="sndRnDiv" title="Toggle repeat on/off" onclick="setRep()"> </div> <div
id="prwRnDiv" class="sndRnDiv" title="Preview animation"> </div> <div
id="sndRnDiv" class="sndRnDiv" title="Send animation file to device"> </div>
</td> </tr> <tr id="rnLstTR" style="display:none"> <td
style="vertical-align:middle"> <table id="rnLst"
style="width:100%;table-layout:fixed;align-content:center;vertical-align:middle">
<tr id="stp1" data-json style="display:flex;align-items:center"> </tr> </table>
</td> </tr> </table> </p> <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"> </p><div id="lib"
style="width:100%;text-align:center"> <img id="i0"
style="display:none;margin:0 auto" alt="Library image 1" width="64" height="64"
src="data:text/html;base64,"> </div> <div id="raw-image-container"
style="display:none"> <img id="image" src="data:text/html;base64,"
alt="RawImage image"> </div> <p></p> <div id="image-container"
style="display:none"> <div id="image-info" style="display:none"></div> <textarea
id="JSONled" readonly="readonly"></textarea> </div> <div id="button-container"
style="display:none"> <button id="copyJSONledbutton" class="buttonclass">
</button> <div id="gap1" class="gap"></div> <button id="sendJSONledbutton"
class="buttonclass"></button> </div> <div> <h3><div id="version"
title="PAC version 2023.04.20.19.18.22">Version 1.1.0.b</div>&nbsp;-&nbsp; <a
href="https://github.com/werkstrom/WLED-PixelArtConverter/blob/main/README.md"
target="_blank">Help/About</a></h3> </div> </div> <div id="bottom-part"
style="display:none" class="bottom-part"></div> <canvas id="pixelCanvas">
</canvas> <p> </p><div style="width:100%;text-align:center"> <div> <h3><div
id="orig">Original image</div></h3> </div> <img id="preview"
style="display:none;margin:0 auto" src="data:text/html;base64,"> </div> <p></p>
<script type="text/javascript">
for(var gurl=gId("curlUrl"),szX=gId("sizeX"),szY=gId("sizeY"),szDiv=gId("sizeDiv"),prw=gId("preview"),lib=gId("lib"),sID=gId("segID"),JLD=gId("JSONled"),tSg=gId("targetSegment"),brgh=gId("brightnessNumber"),seDiv=gId("getSegmentsDiv"),cjb=gId("copyJSONledbutton"),frm=gId("formatSelector"),cLN=gId("colorLimitNumber"),haIDe=gId("haID"),haUe=gId("haUID"),haNe=gId("haName"),aS=gId("addressingSelector"),cFS=gId("colorFormatSelector"),lSS=gId("ledSetupSelector"),imin=gId("image-info"),imcn=gId("image-container"),bcn=gId("button-container"),im=gId("image"),scDiv=gId("scaleDiv"),rnDiv=gId("runDiv"),w=window,canvas=gId("pixelCanvas"),brgV=gId("brightnessValue"),cLV=gId("colorLimitValue"),sndRnDivgId=gId("sndRnDiv"),httpArray=[],rawRGBArray=[],fileJSON="",hideableRows=d.querySelectorAll(".ha-hide"),i=0;i<hideableRows.length;i++)hideableRows[i].classList.add("hide");var accentColor="#eeeeee",inactiveColor="#444444",accentTextColor="#777",prsCol="#ccc",greenColor="#056b0a",redColor="#6b050c",scaleToggleOffd="M17,7H7A5,5 0 0,0 2,12A5,5 0 0,0 7,17H17A5,5 0 0,0 22,12A5,5 0 0,0 17,7M7,15A3,3 0 0,1 4,12A3,3 0 0,1 7,9A3,3 0 0,1 10,12A3,3 0 0,1 7,15Z",scaleToggleOnd="M17,7H7A5,5 0 0,0 2,12A5,5 0 0,0 7,17H17A5,5 0 0,0 22,12A5,5 0 0,0 17,7M17,15A3,3 0 0,1 14,12A3,3 0 0,1 17,9A3,3 0 0,1 20,12A3,3 0 0,1 17,15Z",repSVG='<svg id="repSVG" style="width:36px;height:36px;cursor:pointer" viewBox="0 0 24 24"><path id="repPath" fill="#44444" d="M17,17H7V14L3,18L7,22V19H19V13H17M7,7H17V10L21,6L17,2V5H5V11H7V7Z"/></svg>',playSVG='<svg id=playSVG style="width:36px;height:36px;cursor:pointer" viewBox="0 0 24 24"><path fill="#eeeeee" d="M8,5.14V19.14L19,12.14L8,5.14Z"/></svg>',stopSVGPath='<path id="stopSVGPath" fill="#eee" d="M18,18H6V6H18V18Z"/>',playSVGPath='<path id="playSVGPath" fill="#eee" d="M8,5.14V19.14L19,12.14L8,5.14Z"/>',sendSVG='<svg class="svg-icon" style="width:36px;height:36px;cursor:pointer" viewBox="0 0 24 24"> <path id=sendSvgP fill="#eee" d="M6.5 20Q4.22 20 2.61 18.43 1 16.85 1 14.58 1 12.63 2.17 11.1 3.35 9.57 5.25 9.15 5.88 6.85 7.75 5.43 9.63 4 12 4 14.93 4 16.96 6.04 19 8.07 19 11 20.73 11.2 21.86 12.5 23 13.78 23 15.5 23 17.38 21.69 18.69 20.38 20 18.5 20H13Q12.18 20 11.59 19.41 11 18.83 11 18V12.85L9.4 14.4L8 13L12 9L16 13L14.6 14.4L13 12.85V18H18.5Q19.55 18 20.27 17.27 21 16.55 21 15.5 21 14.45 20.27 13.73 19.55 13 18.5 13H17V11Q17 8.93 15.54 7.46 14.08 6 12 6 9.93 6 8.46 7.46 7 8.93 7 11H6.5Q5.05 11 4.03 12.03 3 13.05 3 14.5 3 15.95 4.03 17 5.05 18 6.5 18H9V20M12 13Z" /> </svg>',sSg=gId("getSegmentsSVGpath"),sRnSVG=gId("sendRunnerSVGpath"),stpTR='<td id="stpXxXLoad" class="stpIconTD" title="Set selected image to step"><svg id=loadImgSVGXxX style="width:36px;height:36px;cursor:pointer" viewBox="0 0 24 24"><path fill="#eee" d="M15 3h4V0l5 5-5 5V7h-4V3m6 8.94V19a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h7.06c-.06.33-.06.67-.06 1a8 8 0 0 0 8 8c.33 0 .67 0 1-.06M19 18l-4.5-6-3.5 4.5-2.5-3L5 18h14Z"/></svg></td><td id="stpXxXImgTD" class="stpImgTD"><svg id=empImgSVGXxX style="width:36px;height:36px;cursor:pointer" viewBox="0 0 24 24"><path fill="#eee" d="M15,5H17V3H15M15,21H17V19H15M11,5H13V3H11M19,5H21V3H19M19,9H21V7H19M19,21H21V19H19M19,13H21V11H19M19,17H21V15H19M3,5H5V3H3M3,9H5V7H3M3,13H5V11H3M3,17H5V15H3M3,21H5V19H3M11,21H13V19H11M7,21H9V19H7M7,5H9V3H7V5Z"/></svg></td><td id="stpXxXDur" title="Duration in ms" class="stpDur" style="vertical-align: middle;">Duration:&nbsp;<input class="durFld" type="number" id="stpXxXDurFld" value="50" min="0">&nbsp;1&#47;100s</td><td id="stpXxXAdd" title="Add new step below" class="stpIconTD"><svg id=addRowSVGXxX style="width:36px;height:36px;cursor:pointer" viewBox="0 0 24 24"><path fill="#eee" d="M2,16H10V14H2M18,14V10H16V14H12V16H16V20H18V16H22V14M14,6H2V8H14M14,10H2V12H14V10Z"/></svg></td><td id="stpXxXDel" title="Delete step" class="stpIconTD"><svg id=delRowSVGXxX style="width:36px;height:36px;cursor:pointer" viewBox="0 0 24 24"><path fill="#eee" d="M2,6V8H14V6H2M2,10V12H11V10H2M14.17,10.76L12.76,12.17L15.59,15L12.76,17.83L14.17,19.24L17,16.41L19.83,19.24L21.24,17.83L18.41,15L21.24,12.17L19.83,10.76L17,13.59L14.17,10.76M2,14V16H11V14H2Z"/></svg></td>'
</script> <script type="text/javascript">
function getPixelRGBValues(e){httpArray=[],rawRGBArray=[],console.log("Generating RGB"),fileJSON=`{"on":true,"bri":${brgh.value},"seg":{"id":${tSg.value},"i":[`;let t=0;t="flex"==tSg.style.display?tSg.value:sID.value;const l=parseInt(cLN.value);let n=!1,a=-1;a=frm.selectedIndex;const i=frm.options[a].value;a=lSS.selectedIndex;const o=lSS.options[a].value;a=cFS.selectedIndex;let r=!0;"dec"==cFS.options[a].value&&(r=!1),a=aS.selectedIndex;let s=!0;"single"==aS.options[a].value?s=!1:"hybrid"==aS.options[a].value&&(n=!0);let u="",d="",c='"',h='"';r||(c="[",h="]");let p=!1,g="";var f=cE("canvas"),m=f.getContext("2d",{willReadFrequently:!0}),v=new Image;v.src=e,v.onload=function(){let e=scDiv.children[0].children[0].getAttribute("fill"),a=szX.value,y=szY.value;(e!=accentColor||a<1||y<1)&&(a=v.width,y=v.height,(v.width>512||v.height>512)&&(a=16,y=16)),f.width=a,f.height=y;new Uint8Array(a*y);g="<p>Width: "+a+", Height: "+y+" (make sure this matches your led matrix setup)</p>",m.drawImage(v,0,0,a,y);var S=m.getImageData(0,0,a,y).data;rawRGBArray=[...S.filter((e,t)=>(t+1)%4!=0)];var w=[];let I=1;"l2r"==o&&(I=0);for(var $=0;$<S.length;$+=4){var b=S[$],x=S[$+1],A=S[$+2],N=S[$+3];let e=$/4,t=Math.floor(e/a),l=e;if("matrix"==o);else if((t+I)%2==0);else{l=t*a+(a-1-(l-t*a))}w.push([b,x,A,N,l,e,t])}w.sort((e,t)=>e[5]-t[5]);let R=[...w];R.sort((e,t)=>e[4]-t[4]);let T="",O=-1,D=R.length,G=0,j=[];for(let e=0;e<D;e++){let t=R[e],a=t[0],i=t[1],o=t[2],u=t[3],d="",g=-1;if(s)if(O<0&&(O=e),e<D-1){let t=R[e+1];t[0]==a&&t[1]==i&&t[2]==o||(g=e+1,d=O==e&&n?""==T?e+",":"":O+","+g+",")}else g=e+1,d=O+1==g&&n?""==T?e+",":"":O+","+g+",";else""==T&&(T=e+","),O=e,g=e;if(u<255&&(p=!0),g>-1){let t=a+","+i+","+o;if(r){const[e,l,n]=[a,i,o];t=""+[e,l,n].map(e=>e.toString(16).padStart(2,"0")).join("")}T+=d+c+t+h,fileJSON=T+d+c+t+h,G+=1,G%l==0||e==D-1?(j.push(T),T=""):T+=",",O=-1}}T="";for(let e=0;e<j.length;e++){let l=`{"on":true,"bri":${brgh.value},"seg":{"id":${t},"i":[${j[e]}]}}`;httpArray.push(l);let n=`curl -X POST "http://${gurl.value}/json/state" -d '${l}' -H "Content-Type: application/json"`;e>0&&(T+="\n<NEXT COMMAND (multiple commands not supported in API/preset setup)>\n",u+=" && "),T+=l,u+=n}d=`#Uncomment if you don't allready have these defined in your switch section of your configuration.yaml \n#- platform: command_line \n #switches: \n ${haIDe.value} \n friendly_name: ${haNe.value} \n unique_id: ${haUe.value} \n command_on: > \n ${u} \n command_off: > \n curl -X POST "http://${gurl.value}/json/state" -d '{"on":false}' -H "Content-Type: application/json"`,JLD.value="wled"==i?T:"curl"==i?u:"ha"==i?d:"ERROR!/n"+i+" is an unknown format.",fileJSON+="]}}";let B=imin,C=imin;p&&(g+="<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>"),B.innerHTML=g,C.style.display="block",drawBoxes(w,a,y)}}
</script> <script type="text/javascript">
function drawBoxes(t,e,a){var i=window,n=canvas.getContext("2d",{willReadFrequently:!0});i.innerHeight<i.innerWidth?canvas.width=Math.floor(.98*i.innerHeight):canvas.width=Math.floor(.98*i.innerWidth);let l=Math.floor(canvas.width/e),h=(i.innerWidth-e*l)/2;canvas.height=l*a+10;for(let i=0;i<a;i++)for(let a=0;a<e;a++){let h=t[i*e+a],r="rgb("+h[0]+", "+h[1]+", "+h[2]+")",d="rgb(128,128,128)";n.fillStyle=r,n.fillRect(a*l,i*l,l,l),n.strokeStyle="#888888",n.lineWidth=1,n.strokeRect(a*l,i*l,l,l),n.font="10px Arial",n.fillStyle=d,n.textAlign="center",n.textBaseline="middle",n.fillText(h[4]+1,a*l+l/2,i*l+l/2)}var r=n.getImageData(0,0,canvas.width,canvas.height);n.clearRect(0,0,canvas.width,canvas.height),canvas.width=i.innerWidth,n.putImageData(r,h,0)}
</script> <script type="text/javascript">
gurl.value=location.host;var animTOId,aAr=[],curStp=0,isRep=0;const urlParams=new URLSearchParams(window.location.search);function gen(e=!1){if((szX.value>0&&szY.value>0||"none"==szDiv.style.display)&&gurl.value.length>0&&"none"!=prw.style.display){let t=prw.src;if(isValidBase64Gif(t)){if(im.src=t,e)return getPixelRGBValues(t,!0);getPixelRGBValues(t),imcn.style.display="block",bcn.style.display=""}else{let e="<p><b>WARNING!</b> File does not appear to be a valid image</p>";imin.innerHTML=e,imin.style.display="block",imcn.style.display="none",JLD.value=""}}if(gurl.value.length>0)gId("sSg").setAttribute("fill",accentColor);else{gId("sSg").setAttribute("fill",accentTextColor);let e=tSg;e.style.display="none",e.innerHTML="",sID.style.display="flex"}}async function postPixels(){let e=gId("sendSvgP");e.setAttribute("fill",prsCol);let t=!1;for(let e of httpArray)try{const t=await fetch("http://"+gId("curlUrl").value+"/json/state",{method:"POST",headers:{"Content-Type":"application/json"},body:e});await t.json()}catch(e){console.error(e),t=!0}t?(e.setAttribute("fill",redColor),setTimeout((function(){e.setAttribute("fill",accentTextColor)}),1e3)):(e.setAttribute("fill",greenColor),setTimeout((function(){e.setAttribute("fill",accentColor)}),1e3))}async function postAnim(e){let t=gId("sendSvgP");t.setAttribute("fill",prsCol);let n=!1,i=[`{"pixart":{"anim": "${e}"}}`];console.log(i);for(let e of i)try{const t=await fetch("http://"+gId("curlUrl").value+"/json/state",{method:"POST",headers:{"Content-Type":"application/json"},body:e});await t.json()}catch(e){console.error(e),n=!0}n?(t.setAttribute("fill",redColor),setTimeout((function(){t.setAttribute("fill",accentTextColor)}),1e3)):(t.setAttribute("fill",greenColor),setTimeout((function(){t.setAttribute("fill",accentColor)}),1e3))}gurl.value.length<1&&(gurl.value="Missing_Host"),cjb.addEventListener("click",async()=>{let e=JLD;e.select();try{await navigator.clipboard.writeText(e.value)}catch(e){try{await d.execCommand("copy")}catch(e){console.error("Failed to copy text: ",e)}}}),lSS.addEventListener("change",gen),szY.addEventListener("change",gen),szX.addEventListener("change",gen),cFS.addEventListener("change",gen),aS.addEventListener("change",gen),brgh.addEventListener("change",gen),cLN.addEventListener("change",gen),haIDe.addEventListener("change",gen),haUe.addEventListener("change",gen),haNe.addEventListener("change",gen),gurl.addEventListener("change",gen),sID.addEventListener("change",gen),prw.addEventListener("load",gen),tSg.addEventListener("change",()=>{sop=tSg.options[tSg.selectedIndex],szX.value=sop.dataset.x,szY.value=sop.dataset.y,gen()}),gId("sendJSONledbutton").addEventListener("click",async()=>{"https:"===window.location.protocol?alert("Will only be available when served over http (or WLED is run over https)"):postPixels()}),brgh.oninput=()=>{brgV.textContent=brgh.value;let e=100*parseInt(brgh.value)/255;var t=`linear-gradient(90deg, #bbb ${e}%, #333 ${e}%)`;brgh.style.backgroundImage=t},cLN.oninput=()=>{let e=cLN;cLV.textContent=e.value;let t=100*parseInt(e.value)/512;var n=`linear-gradient(90deg, #bbb ${t}%, #333 ${t}%)`;e.style.backgroundImage=n},frm.addEventListener("change",()=>{for(var e=0;e<hideableRows.length;e++)hideableRows[e].classList.toggle("hide","ha"!==frm.value),gen()});const dropZone=gId("drop-zone"),filePicker=gId("file-picker"),preview=prw;function zoneClicked(e){e.preventDefault(),filePicker.click()}function dragEnter(e){e.preventDefault(),this.classList.add("drag-over")}function dragOver(e){e.preventDefault()}function dropped(e){e.preventDefault(),this.classList.remove("drag-over");updatePreview(e.dataTransfer.files[0])}function filePicked(e){updatePreview(e.target.files[0])}function updatePreview(e){let t="";const n=new FileReader;n.onload=()=>{preview.src=n.result;const e=document.querySelectorAll("#lib img");let i=0;e.forEach(e=>{const t=parseInt(e.id.slice(1));t>i&&(i=t)});const s=gId("i"+i);nNo=i+1;let r="i"+nNo;nNo;s.insertAdjacentHTML("afterend",`<img id="${r}" src="${preview.src}" alt="Library image ${nNo}" title="Image ${nNo}" width="64" height="64">`),t=gId(r),t.setAttribute("data-issel",1),t.style.border="2px solid #eee",t.addEventListener("click",clkImg),toDel=gId("i0"),toDel&&toDel.parentNode.removeChild(toDel),prw.style.display="",lib.style.display=""},n.readAsDataURL(e),setFoc(t)}function setFoc(e){document.querySelectorAll("#lib img").forEach(t=>{e.srcElement!=t?(t.style.border="2px solid #444",t.setAttribute("data-isSel",0)):(t.style.border="2px solid #eee",t.setAttribute("data-isSel",1))})}function clkImg(e){setFoc(e),prw.src=e.srcElement.src}function isValidBase64Gif(e){return!0}dropZone.addEventListener("dragenter",dragEnter),dropZone.addEventListener("dragover",dragOver),dropZone.addEventListener("drop",dropped),dropZone.addEventListener("click",zoneClicked),filePicker.addEventListener("change",filePicked);for(var hideableRows=d.querySelectorAll(".ha-hide"),i=0;i<hideableRows.length;i++)hideableRows[i].classList.add("hide");function switchScale(){let e=scDiv.children[0].children[0],t=e.getAttribute("fill"),n="";t===accentColor?(t=accentTextColor,n=scaleToggleOffd,szDiv.style.display="none"):(t=accentColor,n=scaleToggleOnd,szDiv.style.display=""),e.setAttribute("fill",t),e.setAttribute("d",n),gen()}function switchRun(){let e=rnDiv.children[0].children[0],t=e.getAttribute("fill"),n="";t===accentColor?(t=accentTextColor,n=scaleToggleOffd,gId("rnTD").style.display="none",gId("rnLstTR").style.display="none"):(t=accentColor,n=scaleToggleOnd,gId("rnTD").style.display="",gId("rnLstTR").style.display=""),e.setAttribute("fill",t),e.setAttribute("d",n),gen()}function setStpImg(e){i=e.srcElement.ownerSVGElement,selim="",nr=i?i.id.replace("loadImgSVG",""):e.srcElement.id.replace("loadImgSVG","");const t=gId("lib").getElementsByTagName("img");for(let e=0;e<t.length;e++){if(1==t[e].getAttribute("data-issel")){selim=t[e];break}}td=gId(`stp${nr}ImgTD`),td.innerHTML=`<img id="stp${nr}Img" src="${selim.src}" alt="Step image ${nr}" title="Image: ${selim.title}" width="36" height="36" data-baseimg=${selim.id}>`}function addStpImg(e){i=e.srcElement.ownerSVGElement,selim="",nr=i?i.id.replace("addRowSVG",""):e.srcElement.id.replace("addRowSVG","");let t=parseInt(nr)+1;thisTR=gId("stp"+nr);const n=stpTR.replace(/XxX/g,e=>t);var s=Array.from(gId("rnLst").getElementsByTagName("tr")),r=[];s.forEach((function(e){thisNr=parseInt(e.id.replace("stp","")),thisNr>nr&&r.push(thisNr)})),r.sort((function(e,t){return t-e})),r.forEach((function(e){moveStep(e,e+1)}));var l=document.createElement("tr");l.id="stp"+t,l.setAttribute("data-json",""),l.style="display: flex; align-items: center;",l.innerHTML=n,thisTR.parentNode.insertBefore(l,thisTR.nextSibling),gId("loadImgSVG"+t).addEventListener("click",setStpImg),gId("addRowSVG"+t).addEventListener("click",addStpImg),gId("delRowSVG"+t).addEventListener("click",delStpImg)}function delStpImg(e){i=e.srcElement.ownerSVGElement,selim="",nr=i?i.id.replace("delRowSVG",""):e.srcElement.id.replace("delRowSVG",""),gId("stp"+nr).remove();var t=Array.from(gId("rnLst").getElementsByTagName("tr")),n=[];t.forEach((function(e){thisNr=parseInt(e.id.replace("stp","")),thisNr>nr&&n.push(thisNr)})),n.sort(),n.forEach((function(e){moveStep(e,e-1)}))}function chkDelOK(){gId(`stp${newN}Del`).style.display=""}function moveStep(e,t){gId("stp"+e).id="stp"+t,gId(`stp${e}Load`).id=`stp${t}Load`,gId("loadImgSVG"+e).id="loadImgSVG"+t,gId(`stp${e}ImgTD`).id=`stp${t}ImgTD`;gId("empImgSVG"+e)?gId("empImgSVG"+e).id="empImgSVG"+t:gId(`stp${e}Img`).id=`stp${t}Img`,gId(`stp${e}Dur`).id=`stp${t}Dur`,gId(`stp${e}DurFld`).id=`stp${t}DurFld`,gId(`stp${e}Add`).id=`stp${t}Add`,gId("addRowSVG"+e).id="addRowSVG"+t,gId(`stp${e}Del`).id=`stp${t}Del`,gId("delRowSVG"+e).id="delRowSVG"+t}function generateSegmentOptions(e){tSg.innerHTML="";for(var t=0;t<e.length;t++){var n=cE("option");n.value=e[t].value,n.text=e[t].text,n.dataset.x=e[t].x,n.dataset.y=e[t].y,tSg.appendChild(n),0===t&&(n.selected=!0,szX.value=n.dataset.x,szY.value=n.dataset.y)}}async function getSegments(){if(cv=gurl.value,cv.length>0)try{var e=[];const n=await fetch("http://"+cv+"/json/state");let i=(await n.json()).seg.map(e=>({id:e.id,n:e.n,xs:e.start,xe:e.stop,ys:e.startY,ye:e.stopY}));for(var t=0;t<i.length;t++)e.push({value:i[t].id,text:i[t].n+" (index: "+i[t].id+")",x:i[t].xe-i[t].xs,y:i[t].ye-i[t].ys});generateSegmentOptions(e),tSg.style.display="flex",sID.style.display="none",gId("sSg").setAttribute("fill",greenColor),setTimeout((function(){gId("sSg").setAttribute("fill",accentColor)}),1e3)}catch(e){console.error(e),gId("sSg").setAttribute("fill",redColor),setTimeout((function(){gId("sSg").setAttribute("fill",accentColor)}),1e3),tSg.style.display="none",sID.style.display="flex"}else gId("sSg").setAttribute("fill",redColor),setTimeout((function(){gId("sSg").setAttribute("fill",accentTextColor)}),1e3),tSg.style.display="none",sID.style.display="flex"}function generateSegmentArray(e){for(var t=[],n=0;n<e;n++)t.push({value:n,text:"Segment index "+n});return t}function anim(){gId("sendSvgP").setAttribute("fill",inactiveColor),gId("sndRnDiv").removeEventListener("click",sendAnim),gId("sndRnDiv").title="Sending disabled while animating",gId("playSVG").innerHTML=stopSVGPath,gId("prwRnDiv").removeEventListener("click",anim),gId("prwRnDiv").addEventListener("click",stopAnim),aAr=[],curStp=0,Array.from(gId("rnLst").getElementsByTagName("tr")).forEach((function(e){const t=e.id.replace("stp",""),n=e.getElementsByClassName("stpImgTD")[0].querySelector("img").getAttribute("data-baseimg"),i=10*e.getElementsByClassName("stpDur")[0].querySelector("input").value;aAr.push({stpId:t,imgId:n,dur:i})})),animateSteps(curStp=0)}function animateSteps(e){if(e>=aAr.length||e<0)stopAnim();else{var t=aAr[e];gId(t.imgId).click(),e+1>=aAr.length&&1==isRep?nStp=0:nStp=e+1,animTOId=setTimeout((function(){animateSteps(nStp)}),t.dur)}}function stopAnim(){clearTimeout(animTOId),gId("playSVG").innerHTML=playSVGPath,gId("prwRnDiv").removeEventListener("click",stopAnim),gId("prwRnDiv").addEventListener("click",anim),gId("sendSvgP").setAttribute("fill",accentColor),gId("sndRnDiv").removeEventListener("click",sendAnim),gId("sndRnDiv").addEventListener("click",sendAnim),gId("sndRnDiv").title="Send animation file to device"}function setRep(e){pth=gId("repPath"),console.log(e,pth.getAttribute("fill"),accentColor,inactiveColor),pth.getAttribute("fill")==accentColor?(pth.setAttribute("fill",inactiveColor),isRep=0):(pth.setAttribute("fill",accentColor),isRep=1)}async function sendAnim(e){gId("sendSvgP").setAttribute("fill",inactiveColor);let t=gId("rnID").value,n={description:t,created:(new Date).toLocaleString().substring(0,16).replace(",","")};n.repeat=1===isRep,sAr=[],Array.from(gId("rnLst").getElementsByTagName("tr")).forEach((function(e){const t=e.id.replace("stp",""),n=e.getElementsByClassName("stpImgTD")[0].querySelector("img").getAttribute("data-baseimg"),i=e.getElementsByClassName("stpDur")[0].querySelector("input").value;sAr.push({stpId:t,imgId:n,dur:i})}));let i=[...new Set(sAr.map(e=>e.imgId))];sets=[];const s=await itcAr(i);n.sets=s.map(e=>e[2]),sAr.sort((e,t)=>e.stpId-t.stpId);const r=sAr.map((e,t)=>({step:t+1,commandId:e.imgId,duration:e.dur}));n.steps=r;uploadAnimation(s,r,t)}async function itcAr(e){const t=[];for(const n of e){await setImg(n);const e=httpArray.map((e,t)=>({position:t+1,command:JSON.parse(e)})),i=new Uint8Array(rawRGBArray);let s={id:n,commands:e};t.push([n,i,s])}return t}async function setImg(e){return new Promise(t=>{gId(e).click(),setTimeout(()=>{t(e)},200)})}function uploadAnimation(e,t,n){t.sort((e,t)=>e.step-t.step);var i=e.map((function(e){return[e[0],e[1]]}));let s=[],r=[],l=[],a=[];if(t.forEach((function(e,t){a.push([t,parseInt(e.duration)]);const n=i.find(t=>t[0]===e.commandId)[1];let o=[];if(0==t){for(let e=0;e<n.length;e+=3)o.push([t,e/3,n[e],n[e+1],n[e+2]]);l=o.slice(),s=o.slice()}else{let e=[];for(let i=0;i<n.length;i+=3){e.push([t,i/3,n[i],n[i+1],n[i+2]]);let r=s[i/3];r[2]==n[i]&&r[3]==n[i+1]&&r[4]==n[i+2]||o.push([t,i/3,n[i],n[i+1],n[i+2]])}s=e.slice()}r.push(...o)})),1==isRep){let e=[];l.forEach((function(t,n){let i=s[n];i[2]==t[2]&&i[3]==t[3]&&i[4]==t[4]||e.push([255,n,t[2],t[3],t[4]])})),r.push(...e)}let o=[];r.forEach((function(e,t){const n=e[1],i=n>>8&255,s=255&n;o.push(e[0],i,s,e[2],e[3],e[4])}));let d=[];a.forEach((function(e,t){const n=e[1],i=n>>8&255,s=255&n;d.push(e[0],i,s)}));const c=new XMLHttpRequest,g=new Blob([new Uint8Array(o)],{type:"application/octet-stream"});fileName=`/${n}.frm`,c.fileName=fileName,console.log(`Writing ${fileName} to device`),addListenerToHTTP(c,fileName),c.open("POST","/upload");var p=new FormData;p.append("data",g,fileName),c.send(p);const u=new XMLHttpRequest,m=new Blob([new Uint8Array(d)],{type:"application/octet-stream"});fileName=`/${n}.ani`,u.fileName=fileName,console.log(`Writing ${fileName} to device`),addListenerToHTTP(u,fileName),u.open("POST","/upload");var f=new FormData;return f.append("data",m,fileName),u.send(f),!1}function addListenerToHTTP(e,t){e.addEventListener("load",(function(){console.log(`Writing ${t} succeeded. `,this.responseText," - ",this.status),gId("sendSvgP").setAttribute("fill",greenColor),setTimeout((function(){gId("sendSvgP").setAttribute("fill",accentColor)}),1e3)})),e.addEventListener("error",(function(e){console.log("Error: ",e),console.log(" Status: ",this.status),gId("sendSvgP").setAttribute("fill",greenColor),setTimeout((function(){gId("sendSvgP").setAttribute("fill",accentColor)}),2e3)}))}frm.addEventListener("change",()=>{for(var e=0;e<hideableRows.length;e++)hideableRows[e].classList.toggle("hide","ha"!==frm.value)}),document.addEventListener("dblclick",(function(){stopAnim()}));var segmentData=generateSegmentArray(10);generateSegmentOptions(segmentData),seDiv.innerHTML='<svg id=getSegmentsSVG style="width:36px;height:36px;cursor:pointer" viewBox="0 0 24 24" onclick="getSegments()"><path id=sSg fill="currentColor" d="M6.5 20Q4.22 20 2.61 18.43 1 16.85 1 14.58 1 12.63 2.17 11.1 3.35 9.57 5.25 9.15 5.68 7.35 7.38 5.73 9.07 4.1 11 4.1 11.83 4.1 12.41 4.69 13 5.28 13 6.1V12.15L14.6 10.6L16 12L12 16L8 12L9.4 10.6L11 12.15V6.1Q9.1 6.45 8.05 7.94 7 9.43 7 11H6.5Q5.05 11 4.03 12.03 3 13.05 3 14.5 3 15.95 4.03 17 5.05 18 6.5 18H18.5Q19.55 18 20.27 17.27 21 16.55 21 15.5 21 14.45 20.27 13.73 19.55 13 18.5 13H17V11Q17 9.8 16.45 8.76 15.9 7.73 15 7V4.68Q16.85 5.55 17.93 7.26 19 9 19 11 20.73 11.2 21.86 12.5 23 13.78 23 15.5 23 17.38 21.69 18.69 20.38 20 18.5 20M12 11.05Z" /></svg>',cjb.innerHTML='<svg class="svg-icon" style="width:36px;height:36px" viewBox="0 0 24 24"> <path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z" /> </svg>&nbsp; Copy to clipboard',gId("sendJSONledbutton").innerHTML='<svg class="svg-icon" style="width:36px;height:36px" viewBox="0 0 24 24"> <path id=sendSvgP fill="currentColor" d="M6.5 20Q4.22 20 2.61 18.43 1 16.85 1 14.58 1 12.63 2.17 11.1 3.35 9.57 5.25 9.15 5.88 6.85 7.75 5.43 9.63 4 12 4 14.93 4 16.96 6.04 19 8.07 19 11 20.73 11.2 21.86 12.5 23 13.78 23 15.5 23 17.38 21.69 18.69 20.38 20 18.5 20H13Q12.18 20 11.59 19.41 11 18.83 11 18V12.85L9.4 14.4L8 13L12 9L16 13L14.6 14.4L13 12.85V18H18.5Q19.55 18 20.27 17.27 21 16.55 21 15.5 21 14.45 20.27 13.73 19.55 13 18.5 13H17V11Q17 8.93 15.54 7.46 14.08 6 12 6 9.93 6 8.46 7.46 7 8.93 7 11H6.5Q5.05 11 4.03 12.03 3 13.05 3 14.5 3 15.95 4.03 17 5.05 18 6.5 18H9V20M12 13Z" /> </svg>&nbsp; Send to device',gId("repRnDiv").innerHTML=repSVG,gId("prwRnDiv").innerHTML=playSVG,gId("sndRnDiv").innerHTML=sendSVG,gId("repPath").setAttribute("fill",accentColor);const regex=/XxX/g;gId("stp1").innerHTML=stpTR.replace(regex,e=>1),gId("rnTD").style.display="none",gId("loadImgSVG1").addEventListener("click",setStpImg),gId("addRowSVG1").addEventListener("click",addStpImg),gId("delRowSVG1").addEventListener("click",delStpImg),gId("sndRnDiv").addEventListener("click",sendAnim),gId("prwRnDiv").addEventListener("click",anim),gurl.value.length>0&&gId("sSg").setAttribute("fill",accentColor)
</script> </body> </body></html>