mirror of
https://github.com/OpenEPaperLink/OpenEPaperLink.git
synced 2026-03-21 05:06:39 +01:00
Painter
- new paint option for freehand drawing and quick texts - added gamma correction on rendering - option to turn dither on/off on jpg upload - python version for packagebinaries (same output as the php version)
This commit is contained in:
@@ -38,6 +38,7 @@
|
||||
<option value="11">google calendar</option>
|
||||
<option value="5">firmware update</option>
|
||||
</select>
|
||||
<button id="paintbutton"><i>A</i>🖌</button>
|
||||
</p>
|
||||
<div id="customoptions"></div>
|
||||
<p>
|
||||
|
||||
@@ -92,14 +92,17 @@ input {
|
||||
border-radius: 0px;
|
||||
}
|
||||
|
||||
input[type=button] {
|
||||
input[type=button], button {
|
||||
border: 0px;
|
||||
padding: 4px 10px;
|
||||
cursor:pointer;
|
||||
}
|
||||
input[type=button]:hover {
|
||||
|
||||
input[type=button]:hover,
|
||||
button:hover {
|
||||
background-color:#aaaaaa;
|
||||
}
|
||||
|
||||
select {
|
||||
padding: 4px;
|
||||
border-radius: 0px;
|
||||
@@ -128,7 +131,7 @@ select {
|
||||
|
||||
#configbox input, #apconfigbox input {
|
||||
border: solid 1px #666666;
|
||||
padding: 4px;
|
||||
/*padding: 4px;*/
|
||||
}
|
||||
|
||||
#configbox label, #apconfigbox label {
|
||||
@@ -326,6 +329,81 @@ ul.messages li.new {
|
||||
color: red;
|
||||
}
|
||||
|
||||
#paintbutton {
|
||||
padding: 1px 3px;
|
||||
border: 1px solid black;
|
||||
font-size: 1.3em;
|
||||
vertical-align: top;
|
||||
margin-left:12px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#paintbutton:hover {
|
||||
background-color: #aaaaaa;
|
||||
}
|
||||
|
||||
/* painter */
|
||||
|
||||
#canvasdiv {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
#canvasdiv canvas {
|
||||
border: 1px solid black;
|
||||
}
|
||||
|
||||
#buttonbar {
|
||||
padding: 5px;
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
#buttonbar button,
|
||||
#layersdiv button {
|
||||
padding: 1px 2px;
|
||||
border: 1px solid #cccccc;
|
||||
background-color: #dddddd;
|
||||
width: 40px;
|
||||
}
|
||||
#buttonbar button {
|
||||
font-size: 1.2em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#buttonbar .active {
|
||||
background-color: #ffffff;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#buttonbar button:hover,
|
||||
#layersdiv button:hover {
|
||||
background-color: #cccccc;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#layersdiv {
|
||||
padding: 0px 5px;
|
||||
}
|
||||
|
||||
#layersdiv>div {
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
#layersdiv input,
|
||||
#layersdiv select {
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
#font-select {
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
#savebar button {
|
||||
border: solid 1px #666666;
|
||||
}
|
||||
|
||||
|
||||
@media(max-width: 460px) {
|
||||
.messages li div, ul.messages li div.date, ul.messages li div.message {
|
||||
|
||||
@@ -31,6 +31,7 @@ contentModeOptions[12] = [];
|
||||
const imageQueue = [];
|
||||
let isProcessing = false;
|
||||
let servertimediff = 0;
|
||||
let paintLoaded = false, paintShow = false;
|
||||
|
||||
let socket;
|
||||
connect();
|
||||
@@ -343,6 +344,40 @@ $('#apcfgsave').onclick = function () {
|
||||
$('#apconfigbox').style.display = 'none';
|
||||
}
|
||||
|
||||
$('#paintbutton').onclick = function () {
|
||||
if (paintShow) {
|
||||
paintShow = false;
|
||||
$('#cfgsave').parentNode.style.display = 'block';
|
||||
contentselected();
|
||||
} else {
|
||||
paintShow = true;
|
||||
$('#cfgsave').parentNode.style.display = 'none';
|
||||
$('#customoptions').innerHTML = "<div id=\"buttonbar\"></div><div id=\"canvasdiv\"></div><div id=\"layersdiv\"></div><p id=\"savebar\"></p>";
|
||||
const mac = $('#cfgmac').dataset.mac
|
||||
const hwtype = $('#tag' + mac).dataset.hwtype;
|
||||
var [width, height] = displaySizeLookup[hwtype] || [0, 0];
|
||||
if (height > width) [width, height] = [height, width];
|
||||
if (paintLoaded) {
|
||||
startPainter(mac, width, height);
|
||||
} else {
|
||||
loadScript('painter.js', function () {
|
||||
startPainter(mac, width, height);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function loadScript(url, callback) {
|
||||
var script = document.createElement('script');
|
||||
script.src = url;
|
||||
script.onload = function () {
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
document.head.appendChild(script);
|
||||
}
|
||||
|
||||
function contentselected() {
|
||||
let contentMode = $('#cfgcontent').value;
|
||||
let extraoptions = contentModeOptions[contentMode];
|
||||
@@ -352,6 +387,7 @@ function contentselected() {
|
||||
obj = JSON.parse($('#cfgcontent').dataset.json);
|
||||
}
|
||||
if (contentMode) {
|
||||
$('#paintbutton').style.display = (contentMode == 0 ? 'inline-block' : 'none');
|
||||
extraoptions.forEach(element => {
|
||||
var label = document.createElement("label");
|
||||
label.innerHTML = element;
|
||||
@@ -366,6 +402,8 @@ function contentselected() {
|
||||
$('#customoptions').appendChild(p);
|
||||
});
|
||||
}
|
||||
paintShow = false;
|
||||
$('#cfgsave').parentNode.style.display = 'block';
|
||||
}
|
||||
|
||||
function showMessage(message,iserr) {
|
||||
|
||||
360
ESP32_AP-Flasher/data/www/painter.js
Normal file
360
ESP32_AP-Flasher/data/www/painter.js
Normal file
@@ -0,0 +1,360 @@
|
||||
function startPainter(mac, width, height) {
|
||||
let isDrawing = false;
|
||||
let lastX = 0;
|
||||
let lastY = 0;
|
||||
let color = 'black';
|
||||
let linewidth = 3;
|
||||
let cursor = 'auto';
|
||||
let isAddingText = false;
|
||||
let layerDiv, intervalId, showCursor, input, textX, textY, font, sizeSelect, isDragging;
|
||||
|
||||
var fonts = ['Roboto', 'Open Sans', 'Lato', 'Montserrat', 'PT Sans', 'Barlow Condensed', 'Headland One', 'Sofia Sans Extra Condensed', 'Mynerve', 'Lilita One', 'Passion One', 'Big Shoulders Display'];
|
||||
|
||||
loadGoogleFonts(fonts);
|
||||
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
ctx.fillStyle = 'white';
|
||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||
ctx.imageSmoothingEnabled = false;
|
||||
|
||||
$("#canvasdiv").appendChild(canvas);
|
||||
canvas.style.imageRendering = 'pixelated';
|
||||
|
||||
canvas.addEventListener('mousedown', startDrawing);
|
||||
canvas.addEventListener('mouseup', stopDrawing);
|
||||
canvas.addEventListener('mousemove', draw);
|
||||
|
||||
canvas.addEventListener('touchstart', startDrawing, { passive: true });
|
||||
canvas.addEventListener('touchend', stopDrawing);
|
||||
canvas.addEventListener('touchmove', draw, { passive: true });
|
||||
|
||||
const bgCanvas = document.createElement('canvas');
|
||||
bgCanvas.width = canvas.width;
|
||||
bgCanvas.height = canvas.height;
|
||||
const bgCtx = bgCanvas.getContext('2d');
|
||||
|
||||
bgCtx.fillStyle = '#ffffff';
|
||||
bgCtx.fillRect(0, 0, bgCanvas.width, bgCanvas.height);
|
||||
|
||||
const txtButton = document.createElement('button');
|
||||
txtButton.innerHTML = 'tT';
|
||||
txtButton.style.fontStyle = 'italic';
|
||||
txtButton.addEventListener('click', addText);
|
||||
|
||||
const blackButton = document.createElement('button');
|
||||
blackButton.innerHTML = '﹏🖌';
|
||||
blackButton.style.color = 'black';
|
||||
blackButton.addEventListener('click', () => {
|
||||
color = 'black';
|
||||
linewidth = 3;
|
||||
cursor = 'url("data:image/svg+xml;utf8,<svg xmlns=\'http://www.w3.org/2000/svg\'><circle cx=\'2\' cy=\'2\' r=\'2\' opacity=\'0.5\'/></svg>") 2 2, auto';
|
||||
blackButton.classList.add('active');
|
||||
redButton.classList.remove('active');
|
||||
whiteButton.classList.remove('active');
|
||||
});
|
||||
blackButton.classList.add('active');
|
||||
|
||||
const redButton = document.createElement('button');
|
||||
redButton.innerHTML = '﹏🖌';
|
||||
redButton.style.color = 'red';
|
||||
redButton.addEventListener('click', () => {
|
||||
color = 'red';
|
||||
linewidth = 3;
|
||||
cursor = 'url("data:image/svg+xml;utf8,<svg xmlns=\'http://www.w3.org/2000/svg\'><circle cx=\'2\' cy=\'2\' r=\'2\' opacity=\'0.5\'/></svg>") 2 2, auto';
|
||||
blackButton.classList.remove('active');
|
||||
redButton.classList.add('active');
|
||||
whiteButton.classList.remove('active');
|
||||
});
|
||||
|
||||
const whiteButton = document.createElement('button');
|
||||
whiteButton.innerHTML = '⬤';
|
||||
whiteButton.style.color = 'white';
|
||||
whiteButton.addEventListener('click', () => {
|
||||
color = 'white';
|
||||
linewidth = 20;
|
||||
cursor = 'url("data:image/svg+xml;utf8,<svg xmlns=\'http://www.w3.org/2000/svg\'><circle cx=\'10\' cy=\'10\' r=\'10\' opacity=\'0.5\'/></svg>") 10 10, auto';
|
||||
blackButton.classList.remove('active');
|
||||
redButton.classList.remove('active');
|
||||
whiteButton.classList.add('active');
|
||||
});
|
||||
|
||||
const clearButton = document.createElement('button');
|
||||
clearButton.innerHTML = '🖵';
|
||||
clearButton.addEventListener('click', () => {
|
||||
if (isAddingText) handleFinish(false);
|
||||
ctx.fillStyle = 'white';
|
||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||
});
|
||||
|
||||
const uploadButton = document.createElement('button');
|
||||
uploadButton.innerHTML = 'Upload';
|
||||
uploadButton.addEventListener('click', () => {
|
||||
if (isAddingText) handleFinish(true);
|
||||
const dataURL = canvas.toDataURL('image/jpeg');
|
||||
const binaryImage = dataURLToBlob(dataURL);
|
||||
const formData = new FormData();
|
||||
formData.append('mac', mac);
|
||||
formData.append('dither', '0');
|
||||
formData.append('file', binaryImage, 'image.jpg');
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.open('POST', '/imgupload');
|
||||
xhr.send(formData);
|
||||
$('#configbox').style.display = 'none';
|
||||
});
|
||||
|
||||
$("#buttonbar").appendChild(blackButton);
|
||||
$("#buttonbar").appendChild(redButton);
|
||||
$("#buttonbar").appendChild(whiteButton);
|
||||
$("#buttonbar").appendChild(txtButton);
|
||||
$("#buttonbar").appendChild(clearButton);
|
||||
$("#savebar").appendChild(uploadButton);
|
||||
|
||||
canvas.addEventListener('mouseenter', function () {
|
||||
if (!isAddingText) {
|
||||
canvas.style.cursor = cursor;
|
||||
} else {
|
||||
canvas.style.cursor = 'move';
|
||||
}
|
||||
});
|
||||
|
||||
canvas.addEventListener('mouseleave', function () {
|
||||
canvas.style.cursor = 'auto';
|
||||
});
|
||||
|
||||
function startDrawing(e) {
|
||||
if (isAddingText) return;
|
||||
isDrawing = true;
|
||||
var rect = canvas.getBoundingClientRect();
|
||||
lastX = e.pageX - rect.left - window.pageXOffset;
|
||||
lastY = e.pageY - rect.top - window.pageYOffset;
|
||||
}
|
||||
|
||||
function stopDrawing() {
|
||||
if (isAddingText) return;
|
||||
isDrawing = false;
|
||||
}
|
||||
|
||||
function draw(e) {
|
||||
if (isAddingText) return;
|
||||
if (!isDrawing) return;
|
||||
var rect = canvas.getBoundingClientRect();
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(lastX, lastY);
|
||||
ctx.lineTo(e.pageX - rect.left - window.pageXOffset, e.pageY - rect.top - window.pageYOffset);
|
||||
ctx.strokeStyle = color;
|
||||
ctx.lineWidth = linewidth;
|
||||
ctx.lineCap = "round";
|
||||
ctx.stroke();
|
||||
lastX = e.pageX - rect.left - window.pageXOffset;
|
||||
lastY = e.pageY - rect.top - window.pageYOffset;
|
||||
}
|
||||
|
||||
function addText() {
|
||||
if (isAddingText) {
|
||||
handleFinish(true);
|
||||
return;
|
||||
}
|
||||
txtButton.classList.add('active');
|
||||
bgCtx.drawImage(canvas, 0, 0);
|
||||
|
||||
const defaultX = 5;
|
||||
const defaultY = 40;
|
||||
isDragging = false;
|
||||
let startX, startY;
|
||||
showCursor = true;
|
||||
|
||||
textX = defaultX;
|
||||
textY = defaultY;
|
||||
font = '24px ' + fonts[0];
|
||||
|
||||
input = document.createElement('textarea');
|
||||
input.type = 'text';
|
||||
input.placeholder = 'Type text here';
|
||||
input.style.opacity = '0';
|
||||
input.style.position = 'absolute';
|
||||
input.style.left = '-200px'
|
||||
|
||||
input.addEventListener('input', () => {
|
||||
drawText(input.value, textX, textY);
|
||||
});
|
||||
input.addEventListener('keyup', () => {
|
||||
input.selectionStart = input.selectionEnd = input.value.length;
|
||||
});
|
||||
|
||||
input.addEventListener('blur', function () {
|
||||
input.focus();
|
||||
});
|
||||
|
||||
intervalId = setInterval(function () {
|
||||
showCursor = !showCursor;
|
||||
drawText(input.value, textX, textY);
|
||||
}, 300);
|
||||
|
||||
canvas.addEventListener('mouseup', handleMouseUp);
|
||||
canvas.addEventListener('mousedown', handleMouseDown);
|
||||
canvas.addEventListener('mousemove', handleMouseMove);
|
||||
|
||||
canvas.addEventListener('touchstart', handleTouchStart, { passive: true });
|
||||
canvas.addEventListener('touchend', handleTouchEnd);
|
||||
canvas.addEventListener('touchmove', handleTouchMove, { passive: true });
|
||||
|
||||
var sizes = [10,11,12,13,14,16,18,20,24,28,32,36,40,48,56,64,72,84];
|
||||
|
||||
const fontSelect = document.createElement('select');
|
||||
fontSelect.id = 'font-select';
|
||||
for (var i = 0; i < fonts.length; i++) {
|
||||
const option = document.createElement('option');
|
||||
option.value = fonts[i];
|
||||
option.text = fonts[i];
|
||||
option.style.fontFamily = fonts[i];
|
||||
fontSelect.appendChild(option);
|
||||
}
|
||||
|
||||
sizeSelect = document.createElement('select');
|
||||
sizeSelect.id = 'size-select';
|
||||
for (var i = 0; i < sizes.length; i++) {
|
||||
const option = document.createElement('option');
|
||||
option.value = sizes[i];
|
||||
option.text = sizes[i] + ' px';
|
||||
sizeSelect.appendChild(option);
|
||||
}
|
||||
|
||||
function updateFont() {
|
||||
var selectedFont = fontSelect.value;
|
||||
var selectedSize = sizeSelect.value;
|
||||
fontSelect.style.fontFamily = selectedFont;
|
||||
font = selectedSize + 'px ' + selectedFont;
|
||||
drawText(input.value, textX, textY);
|
||||
}
|
||||
|
||||
fontSelect.value = fonts[0];
|
||||
sizeSelect.value = '24';
|
||||
fontSelect.addEventListener('change', updateFont);
|
||||
sizeSelect.addEventListener('change', updateFont);
|
||||
|
||||
const finishButton = document.createElement('button');
|
||||
finishButton.innerHTML = '✔';
|
||||
finishButton.addEventListener('click', clickHandleFinish);
|
||||
|
||||
layerDiv = document.createElement('div');
|
||||
|
||||
layerDiv.appendChild(input);
|
||||
layerDiv.appendChild(fontSelect);
|
||||
layerDiv.appendChild(sizeSelect);
|
||||
layerDiv.appendChild(finishButton);
|
||||
$("#layersdiv").appendChild(layerDiv);
|
||||
input.focus();
|
||||
|
||||
isAddingText = true;
|
||||
//cursor = 'move';
|
||||
blackButton.innerHTML = 'aA'
|
||||
redButton.innerHTML = 'aA'
|
||||
if (color=='white') {
|
||||
whiteButton.classList.remove('active');
|
||||
blackButton.classList.add('active');
|
||||
color='black';
|
||||
}
|
||||
}
|
||||
|
||||
function handleFinish(apply) {
|
||||
canvas.removeEventListener('mousedown', handleMouseDown);
|
||||
canvas.removeEventListener('mouseup', handleMouseUp);
|
||||
canvas.removeEventListener('mousemove', handleMouseMove);
|
||||
|
||||
canvas.removeEventListener('touchstart', handleTouchStart);
|
||||
canvas.removeEventListener('touchend', handleTouchEnd);
|
||||
canvas.removeEventListener('touchmove', handleTouchMove);
|
||||
isAddingText = false;
|
||||
cursor = 'auto';
|
||||
layerDiv.remove();
|
||||
clearInterval(intervalId);
|
||||
showCursor = false;
|
||||
if (apply) drawText(input.value, textX, textY);
|
||||
txtButton.classList.remove('active');
|
||||
blackButton.innerHTML = '﹏🖌'
|
||||
redButton.innerHTML = '﹏🖌'
|
||||
}
|
||||
|
||||
function drawText(text, x, y) {
|
||||
ctx.drawImage(bgCanvas, 0, 0);
|
||||
ctx.save();
|
||||
ctx.translate(x, y);
|
||||
ctx.font = font;
|
||||
ctx.fillStyle = color;
|
||||
const lines = text.split('\n');
|
||||
lines.forEach((line, index) => {
|
||||
ctx.fillText(line + (showCursor && index === lines.length - 1 ? '|' : ''), 0, index * (sizeSelect.value * 1.1));
|
||||
});
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
function handleMouseDown(e) {
|
||||
isDragging = true;
|
||||
startX = textX;
|
||||
startY = textY;
|
||||
({ clientX: lastMouseX, clientY: lastMouseY } = e);
|
||||
}
|
||||
|
||||
function handleMouseMove(e) {
|
||||
if (isDragging) {
|
||||
const { clientX, clientY } = e;
|
||||
textX = startX + clientX - lastMouseX;
|
||||
textY = startY + clientY - lastMouseY;
|
||||
drawText(input.value, textX, textY);
|
||||
}
|
||||
}
|
||||
|
||||
function handleTouchStart(e) {
|
||||
isDragging = true;
|
||||
startX = textX;
|
||||
startY = textY;
|
||||
({ clientX: lastTouchX, clientY: lastTouchY } = e.touches[0]);
|
||||
}
|
||||
|
||||
function handleTouchMove(e) {
|
||||
if (isDragging) {
|
||||
const { clientX, clientY } = e.touches[0];
|
||||
textX = startX + clientX - lastTouchX;
|
||||
textY = startY + clientY - lastTouchY;
|
||||
drawText(input.value, textX, textY);
|
||||
}
|
||||
}
|
||||
|
||||
function handleMouseUp(e) {
|
||||
isDragging = false;
|
||||
}
|
||||
|
||||
function handleTouchEnd(e) {
|
||||
isDragging = false;
|
||||
}
|
||||
|
||||
function clickHandleFinish() {
|
||||
handleFinish(true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function loadGoogleFonts(fonts) {
|
||||
var link = document.createElement('link');
|
||||
link.rel = 'stylesheet';
|
||||
link.href = 'https://fonts.googleapis.com/css?family=' + fonts.join('|');
|
||||
document.head.appendChild(link);
|
||||
}
|
||||
|
||||
function dataURLToBlob(dataURL) {
|
||||
const byteString = atob(dataURL.split(',')[1]);
|
||||
const mimeString = dataURL.split(',')[0].split(':')[1].split(';')[0];
|
||||
const arrayBuffer = new ArrayBuffer(byteString.length);
|
||||
const uint8Array = new Uint8Array(arrayBuffer);
|
||||
for (let i = 0; i < byteString.length; i++) {
|
||||
uint8Array[i] = byteString.charCodeAt(i);
|
||||
}
|
||||
return new Blob([arrayBuffer], { type: mimeString });
|
||||
}
|
||||
|
||||
paintLoaded = true;
|
||||
@@ -107,7 +107,7 @@ void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo) {
|
||||
case Image:
|
||||
|
||||
if (cfgobj["filename"].as<String>() && cfgobj["filename"].as<String>() != "null" && !cfgobj["#fetched"].as<bool>()) {
|
||||
if (cfgobj["dither"] && cfgobj["dither"].as<bool>() == false) imageParams.dither = false;
|
||||
if (cfgobj["dither"] && cfgobj["dither"] == "0") imageParams.dither = false;
|
||||
jpg2buffer(cfgobj["filename"].as<String>(), filename, imageParams);
|
||||
if (imageParams.hasRed) imageParams.dataType = DATATYPE_IMG_RAW_2BPP;
|
||||
if (prepareDataAvail(&filename, imageParams.dataType, mac, cfgobj["timetolive"].as<int>())) {
|
||||
|
||||
@@ -62,10 +62,31 @@ struct Error {
|
||||
float b;
|
||||
};
|
||||
|
||||
// Gamma brightness lookup table <https://victornpb.github.io/gamma-table-generator>
|
||||
// gamma = 1.50 steps = 256 range = 0-255
|
||||
const uint8_t gamma_lut[256] PROGMEM = {
|
||||
0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4,
|
||||
4, 4, 5, 5, 6, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11,
|
||||
11, 12, 12, 13, 14, 14, 15, 15, 16, 16, 17, 18, 18, 19, 20, 20,
|
||||
21, 21, 22, 23, 23, 24, 25, 26, 26, 27, 28, 28, 29, 30, 31, 31,
|
||||
32, 33, 34, 34, 35, 36, 37, 37, 38, 39, 40, 41, 41, 42, 43, 44,
|
||||
45, 46, 46, 47, 48, 49, 50, 51, 52, 53, 53, 54, 55, 56, 57, 58,
|
||||
59, 60, 61, 62, 63, 64, 65, 65, 66, 67, 68, 69, 70, 71, 72, 73,
|
||||
74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 88, 89, 90,
|
||||
91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 102, 103, 104, 105, 106, 107,
|
||||
108, 109, 110, 112, 113, 114, 115, 116, 117, 119, 120, 121, 122, 123, 124, 126,
|
||||
127, 128, 129, 130, 132, 133, 134, 135, 136, 138, 139, 140, 141, 142, 144, 145,
|
||||
146, 147, 149, 150, 151, 152, 154, 155, 156, 158, 159, 160, 161, 163, 164, 165,
|
||||
167, 168, 169, 171, 172, 173, 174, 176, 177, 178, 180, 181, 182, 184, 185, 187,
|
||||
188, 189, 191, 192, 193, 195, 196, 197, 199, 200, 202, 203, 204, 206, 207, 209,
|
||||
210, 211, 213, 214, 216, 217, 218, 220, 221, 223, 224, 226, 227, 228, 230, 231,
|
||||
233, 234, 236, 237, 239, 240, 242, 243, 245, 246, 248, 249, 251, 252, 254, 255,
|
||||
};
|
||||
|
||||
uint32_t colorDistance(const Color &c1, const Color &c2, const Error &e1) {
|
||||
float r_diff = c1.r + e1.r - c2.r;
|
||||
float g_diff = c1.g + e1.g - c2.g;
|
||||
float b_diff = c1.b + e1.b - c2.b;
|
||||
float r_diff = gamma_lut[c1.r] + e1.r - gamma_lut[c2.r];
|
||||
float g_diff = gamma_lut[c1.g] + e1.g - gamma_lut[c2.g];
|
||||
float b_diff = gamma_lut[c1.b] + e1.b - gamma_lut[c2.b];
|
||||
return round(r_diff * r_diff + g_diff * g_diff + b_diff * b_diff);
|
||||
}
|
||||
|
||||
|
||||
@@ -365,12 +365,16 @@ void doImageUpload(AsyncWebServerRequest *request, String filename, size_t index
|
||||
request->_tempFile.close();
|
||||
if (request->hasParam("mac", true)) {
|
||||
String dst = request->getParam("mac", true)->value();
|
||||
bool dither = true;
|
||||
if (request->hasParam("dither", true)) {
|
||||
if (request->getParam("dither", true)->value() == "0") dither = false;
|
||||
}
|
||||
uint8_t mac[6];
|
||||
if (sscanf(dst.c_str(), "%02X%02X%02X%02X%02X%02X", &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]) == 6) {
|
||||
tagRecord *taginfo = nullptr;
|
||||
taginfo = tagRecord::findByMAC(mac);
|
||||
if (taginfo != nullptr) {
|
||||
taginfo->modeConfigJson = "{\"filename\":\"" + dst + ".jpg\",\"timetolive\":\"0\"}";
|
||||
taginfo->modeConfigJson = "{\"filename\":\"" + dst + ".jpg\",\"timetolive\":\"0\",\"dither\":\"" + String(dither) + "\"}";
|
||||
taginfo->contentMode = 0;
|
||||
taginfo->nextupdate = 0;
|
||||
wsSendTaginfo(mac);
|
||||
|
||||
57
zbs243_AP_FW/packagebinaries.py
Normal file
57
zbs243_AP_FW/packagebinaries.py
Normal file
@@ -0,0 +1,57 @@
|
||||
import os
|
||||
import json
|
||||
|
||||
types = {
|
||||
0x00: "AP_FW_1.54.bin",
|
||||
0x01: "AP_FW_2.9.bin",
|
||||
0xF0: "AP_FW_Segmented_UK.bin",
|
||||
0xFF: "AP_FW_Nodisplay.bin"
|
||||
}
|
||||
|
||||
binpath = "../binaries/"
|
||||
tocmaxsize = 512
|
||||
|
||||
toc = []
|
||||
output = bytearray(tocmaxsize)
|
||||
|
||||
# Read version from main.c
|
||||
with open("main.c") as file:
|
||||
lines = file.readlines()
|
||||
version_line = next(line for line in lines if "version" in line and "uint16_t" in line)
|
||||
_, version = version_line.split("= 0x")
|
||||
version = int(version.strip().split(";")[0], 16)
|
||||
|
||||
binaries = [file for file in os.listdir(binpath) if file.startswith("AP_FW") and not file.endswith("Pack")]
|
||||
for file in binaries:
|
||||
file = file.strip()
|
||||
type_id = -1
|
||||
for typeid, typefile in types.items():
|
||||
if typefile == file:
|
||||
type_id = typeid
|
||||
break
|
||||
if type_id == -1:
|
||||
raise ValueError("We don't recognize filetype <{}>, sorry...".format(file))
|
||||
with open(os.path.join(binpath, file), "rb") as binary_file:
|
||||
binary = binary_file.read()
|
||||
length = len(binary)
|
||||
offset = len(output)
|
||||
subarr = {
|
||||
'type': type_id,
|
||||
'version': version,
|
||||
'name': file,
|
||||
'offset': offset,
|
||||
'length': length
|
||||
}
|
||||
toc.append(subarr)
|
||||
output.extend(binary)
|
||||
|
||||
jtoc = json.dumps(toc)
|
||||
tocsize = len(jtoc)
|
||||
if tocsize > tocmaxsize:
|
||||
raise ValueError("TOC is too big! Adjust size and try again.")
|
||||
output[:len(jtoc)] = jtoc.encode()
|
||||
with open(os.path.join(binpath, "AP_FW_Pack.bin"), "wb") as output_file:
|
||||
output_file.write(bytes(output))
|
||||
|
||||
print(toc)
|
||||
print("All done.")
|
||||
Reference in New Issue
Block a user