update html

This commit is contained in:
Shuanglei Tao
2024-11-13 19:32:01 +08:00
parent de809d7531
commit bd5addaa70
9 changed files with 122 additions and 117 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 472 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 601 KiB

File diff suppressed because one or more lines are too long

View File

@@ -1,251 +0,0 @@
const bwrPalette = [
[0, 0, 0, 255],
[255, 255, 255, 255],
[255, 0, 0, 255]
]
const bwPalette = [
[0, 0, 0, 255],
[255, 255, 255, 255],
]
function dithering(ctx, width, height, threshold, type) {
const bayerThresholdMap = [
[ 15, 135, 45, 165 ],
[ 195, 75, 225, 105 ],
[ 60, 180, 30, 150 ],
[ 240, 120, 210, 90 ]
];
const lumR = [];
const lumG = [];
const lumB = [];
for (let i=0; i<256; i++) {
lumR[i] = i*0.299;
lumG[i] = i*0.587;
lumB[i] = i*0.114;
}
const imageData = ctx.getImageData(0, 0, width, height);
const imageDataLength = imageData.data.length;
// Greyscale luminance (sets r pixels to luminance of rgb)
for (let i = 0; i <= imageDataLength; i += 4) {
imageData.data[i] = Math.floor(lumR[imageData.data[i]] + lumG[imageData.data[i+1]] + lumB[imageData.data[i+2]]);
}
const w = imageData.width;
let newPixel, err;
for (let currentPixel = 0; currentPixel <= imageDataLength; currentPixel+=4) {
if (type ==="none") {
// No dithering
imageData.data[currentPixel] = imageData.data[currentPixel] < threshold ? 0 : 255;
} else if (type ==="bayer") {
// 4x4 Bayer ordered dithering algorithm
var x = currentPixel/4 % w;
var y = Math.floor(currentPixel/4 / w);
var map = Math.floor( (imageData.data[currentPixel] + bayerThresholdMap[x%4][y%4]) / 2 );
imageData.data[currentPixel] = (map < threshold) ? 0 : 255;
} else if (type ==="floydsteinberg") {
// Floyda€"Steinberg dithering algorithm
newPixel = imageData.data[currentPixel] < 129 ? 0 : 255;
err = Math.floor((imageData.data[currentPixel] - newPixel) / 16);
imageData.data[currentPixel] = newPixel;
imageData.data[currentPixel + 4 ] += err*7;
imageData.data[currentPixel + 4*w - 4 ] += err*3;
imageData.data[currentPixel + 4*w ] += err*5;
imageData.data[currentPixel + 4*w + 4 ] += err*1;
} else {
// Bill Atkinson's dithering algorithm
newPixel = imageData.data[currentPixel] < threshold ? 0 : 255;
err = Math.floor((imageData.data[currentPixel] - newPixel) / 8);
imageData.data[currentPixel] = newPixel;
imageData.data[currentPixel + 4 ] += err;
imageData.data[currentPixel + 8 ] += err;
imageData.data[currentPixel + 4*w - 4 ] += err;
imageData.data[currentPixel + 4*w ] += err;
imageData.data[currentPixel + 4*w + 4 ] += err;
imageData.data[currentPixel + 8*w ] += err;
}
// Set g and b pixels equal to r
imageData.data[currentPixel + 1] = imageData.data[currentPixel + 2] = imageData.data[currentPixel];
}
ctx.putImageData(imageData, 0, 0);
}
function canvas2bytes(canvas, type='bw') {
const ctx = canvas.getContext("2d");
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const arr = [];
let buffer = [];
for (let y = 0; y < canvas.height; y++) {
for (let x = 0; x < canvas.width; x++) {
const index = (canvas.width * y + x) * 4;
if (type !== 'bwr') {
buffer.push(imageData.data[index] > 0 && imageData.data[index+1] > 0 && imageData.data[index+2] > 0 ? 1 : 0);
} else {
buffer.push(imageData.data[index] > 0 && imageData.data[index+1] === 0 && imageData.data[index+2] === 0 ? 1 : 0);
}
if (buffer.length === 8) {
arr.push(parseInt(buffer.join(''), 2));
buffer = [];
}
}
}
return arr;
}
function bytes2canvas(bytes, canvas) {
const ctx = canvas.getContext("2d");
const imageData = ctx.createImageData(canvas.width, canvas.height);
let buffer = [];
for (let byte of bytes) {
const binaryString = byte.toString(2).padStart(8, '0');
buffer.push(...binaryString.split('').map(bit => parseInt(bit, 10)));
}
let len = buffer.length;
// bw
for (let y = 0; y < canvas.height; y++) {
for (let x = 0; x < canvas.width; x++) {
const index = (canvas.width * y + x) * 4;
const bit = buffer.shift();
const value = bit ? 255 : 0;
imageData.data[index] = value; // R
imageData.data[index + 1] = value; // G
imageData.data[index + 2] = value; // B
imageData.data[index + 3] = 255; // A
}
}
// bwr
if (buffer.length * 2 == len) {
for (let y = 0; y < canvas.height; y++) {
for (let x = 0; x < canvas.width; x++) {
const index = (canvas.width * y + x) * 4;
const bit = buffer.shift();
if (bit) {
imageData.data[index] = 255; // R
imageData.data[index + 1] = 0; // G
imageData.data[index + 2] = 0; // B
imageData.data[index + 3] = 255; // A
}
}
}
}
ctx.putImageData(imageData, 0, 0);
}
function getColorDistance(rgba1, rgba2) {
const [r1, b1, g1] = rgba1;
const [r2, b2, g2] = rgba2;
const rm = (r1 + r2 ) / 2;
const r = r1 - r2;
const g = g1 - g2;
const b = b1 - b2;
return Math.sqrt((2 + rm / 256) * r * r + 4 * g * g + (2 + (255 - rm) / 256) * b * b);
}
function getNearColor(pixel, palette) {
let minDistance = 255 * 255 * 3 + 1;
let paletteIndex = 0;
for (let i = 0; i < palette.length; i++) {
const targetColor = palette[i];
const distance = getColorDistance(pixel, targetColor);
if (distance < minDistance) {
minDistance = distance;
paletteIndex = i;
}
}
return palette[paletteIndex];
}
function getNearColorV2(color, palette) {
let minDistanceSquared = 255*255 + 255*255 + 255*255 + 1;
let bestIndex = 0;
for (let i = 0; i < palette.length; i++) {
let rdiff = (color[0] & 0xff) - (palette[i][0] & 0xff);
let gdiff = (color[1] & 0xff) - (palette[i][1] & 0xff);
let bdiff = (color[2] & 0xff) - (palette[i][2] & 0xff);
let distanceSquared = rdiff*rdiff + gdiff*gdiff + bdiff*bdiff;
if (distanceSquared < minDistanceSquared) {
minDistanceSquared = distanceSquared;
bestIndex = i;
}
}
return palette[bestIndex];
}
function updatePixel(imageData, index, color) {
imageData[index] = color[0];
imageData[index+1] = color[1];
imageData[index+2] = color[2];
imageData[index+3] = color[3];
}
function getColorErr(color1, color2, rate) {
const res = [];
for (let i = 0; i < 3; i++) {
res.push(Math.floor((color1[i] - color2[i]) / rate));
}
return res;
}
function updatePixelErr(imageData, index, err, rate) {
imageData[index] += err[0] * rate;
imageData[index+1] += err[1] * rate;
imageData[index+2] += err[2] * rate;
}
function ditheringCanvasByPalette(canvas, palette, type) {
palette = palette || bwrPalette;
const ctx = canvas.getContext('2d');
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const w = imageData.width;
for (let currentPixel = 0; currentPixel <= imageData.data.length; currentPixel+=4) {
const newColor = getNearColorV2(imageData.data.slice(currentPixel, currentPixel+4), palette);
if (type === "bwr_floydsteinberg") {
const err = getColorErr(imageData.data.slice(currentPixel, currentPixel+4), newColor, 16);
updatePixel(imageData.data, currentPixel, newColor);
updatePixelErr(imageData.data, currentPixel +4, err, 7);
updatePixelErr(imageData.data, currentPixel + 4*w - 4, err, 3);
updatePixelErr(imageData.data, currentPixel + 4*w, err, 5);
updatePixelErr(imageData.data, currentPixel + 4*w + 4, err, 1);
} else {
const err = getColorErr(imageData.data.slice(currentPixel, currentPixel+4), newColor, 8);
updatePixel(imageData.data, currentPixel, newColor);
updatePixelErr(imageData.data, currentPixel +4, err, 1);
updatePixelErr(imageData.data, currentPixel +8, err, 1);
updatePixelErr(imageData.data, currentPixel +4 * w - 4, err, 1);
updatePixelErr(imageData.data, currentPixel +4 * w, err, 1);
updatePixelErr(imageData.data, currentPixel +4 * w + 4, err, 1);
updatePixelErr(imageData.data, currentPixel +8 * w, err, 1);
}
}
ctx.putImageData(imageData, 0, 0);
}

View File

@@ -1,248 +0,0 @@
let bleDevice;
let gattServer;
let Theservice;
let writeCharacteristic;
let reconnectTrys = 0;
let canvas;
let epdDriver;
let startTime;
let chunkSize = 38;
function resetVariables() {
gattServer = null;
Theservice = null;
writeCharacteristic = null;
document.getElementById("log").value = '';
}
function handleError(error) {
console.log(error);
resetVariables();
if (bleDevice == null)
return;
if (reconnectTrys <= 5) {
reconnectTrys++;
connect();
}
else {
addLog("Was not able to connect, aborting");
reconnectTrys = 0;
}
}
async function sendCommand(cmd) {
if (writeCharacteristic) {
await writeCharacteristic.writeValue(cmd);
}
}
async function sendcmd(cmdTXT) {
let cmd = hexToBytes(cmdTXT);
addLog('Send CMD: ' + cmdTXT);
await sendCommand(cmd);
}
async function setDriver() {
epdDriver = document.getElementById("epddriver").value;
let pins = document.getElementById("epdpins").value;
await sendcmd("00" + pins);
}
async function clearscreen() {
if(confirm('确认清除屏幕内容?')) {
await sendcmd("01" + epdDriver);
await sendcmd("02");
await sendcmd("06");
}
}
async function sendIMGArray(imgArray, type = 'bw'){
const count = Math.round(imgArray.length / chunkSize);
let chunkIdx = 0;
for (let i = 0; i < imgArray.length; i += chunkSize) {
let currentTime = (new Date().getTime() - startTime) / 1000.0;
let chunk = imgArray.substring(i, i + chunkSize);
setStatus('正在发送' + (type === 'bwr' ? "红色" : '黑白') + '块: '
+ (chunkIdx+1) + "/" + (count+1) + ", 用时: " + currentTime + "s");
addLog('Sending chunk: ' + chunk);
await sendCommand(hexToBytes("04" + chunk))
chunkIdx++;
}
}
async function sendimg(cmdIMG) {
startTime = new Date().getTime();
let imgArray = cmdIMG.replace(/(?:\r\n|\r|\n|,|0x| )/g, '');
const bwArrLen = (canvas.width/8) * canvas.height * 2;
await sendcmd("01" + epdDriver);
if (imgArray.length == bwArrLen * 2) {
await sendcmd("0310");
await sendIMGArray(imgArray.slice(0, bwArrLen - 1));
await sendcmd("0313");
await sendIMGArray(imgArray.slice(bwArrLen), 'bwr');
} else {
await sendcmd("0313");
await sendIMGArray(imgArray);
}
await sendcmd("05");
let sendTime = (new Date().getTime() - startTime) / 1000.0;
addLog("Done! Time used: " + sendTime + "s");
setStatus("发送完成!耗时: " + sendTime + "s");
await sendcmd("06");
}
function updateButtonStatus() {
let connected = gattServer != null && gattServer.connected;
let status = connected ? null : 'disabled';
document.getElementById("sendcmdbutton").disabled = status;
document.getElementById("clearscreenbutton").disabled = status;
document.getElementById("sendimgbutton").disabled = status;
document.getElementById("setDriverbutton").disabled = status;
}
function disconnect() {
resetVariables();
addLog('Disconnected.');
document.getElementById("connectbutton").innerHTML = '连接';
updateButtonStatus();
}
function preConnect() {
if (gattServer != null && gattServer.connected) {
if (bleDevice != null && bleDevice.gatt.connected)
bleDevice.gatt.disconnect();
}
else {
connectTrys = 0;
navigator.bluetooth.requestDevice({ optionalServices: ['62750001-d828-918d-fb46-b6c11c675aec'], acceptAllDevices: true }).then(device => {
device.addEventListener('gattserverdisconnected', disconnect);
bleDevice = device;
connect();
}).catch(handleError);
}
}
function reConnect() {
connectTrys = 0;
if (bleDevice != null && bleDevice.gatt.connected)
bleDevice.gatt.disconnect();
resetVariables();
addLog("Reconnect");
setTimeout(function () { connect(); }, 300);
}
function connect() {
if (writeCharacteristic == null) {
addLog("Connecting to: " + bleDevice.name);
bleDevice.gatt.connect().then(server => {
addLog('> Found GATT Server');
gattServer = server;
return gattServer.getPrimaryService('62750001-d828-918d-fb46-b6c11c675aec');
}).then(service => {
addLog('> Found Service');
Theservice = service;
return Theservice.getCharacteristic('62750002-d828-918d-fb46-b6c11c675aec');
}).then(characteristic => {
addLog('> Found Characteristic');
document.getElementById("connectbutton").innerHTML = '断开';
updateButtonStatus();
writeCharacteristic = characteristic;
return;
}).catch(handleError);
}
}
function setStatus(statusText) {
document.getElementById("status").innerHTML = statusText;
}
function addLog(logTXT) {
var today = new Date();
var time = ("0" + today.getHours()).slice(-2) + ":" + ("0" + today.getMinutes()).slice(-2) + ":" + ("0" + today.getSeconds()).slice(-2) + " : ";
document.getElementById("log").innerHTML += time + logTXT + '<br>';
console.log(time + logTXT);
while ((document.getElementById("log").innerHTML.match(/<br>/g) || []).length > 10) {
var logs_br_position = document.getElementById("log").innerHTML.search("<br>");
document.getElementById("log").innerHTML = document.getElementById("log").innerHTML.substring(logs_br_position + 4);
}
}
function hexToBytes(hex) {
for (var bytes = [], c = 0; c < hex.length; c += 2)
bytes.push(parseInt(hex.substr(c, 2), 16));
return new Uint8Array(bytes);
}
function bytesToHex(data) {
return new Uint8Array(data).reduce(
function (memo, i) {
return memo + ("0" + i.toString(16)).slice(-2);
}, "");
}
function intToHex(intIn) {
var stringOut = "";
stringOut = ("0000" + intIn.toString(16)).substr(-4)
return stringOut.substring(2, 4) + stringOut.substring(0, 2);
}
function updateImageData(canvas) {
document.getElementById('cmdIMAGE').value = bytesToHex(canvas2bytes(canvas, 'bw'));
if (document.getElementById("epddriver").value == '03' &&
document.getElementById('dithering').value.startsWith('bwr')) {
document.getElementById('cmdIMAGE').value += bytesToHex(canvas2bytes(canvas, 'bwr'));
}
}
async function update_image () {
const image_file = document.getElementById('image_file');
if (image_file.files.length > 0) {
const file = image_file.files[0];
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
const image = new Image();
image.src = URL.createObjectURL(file);
image.onload = function(event) {
URL.revokeObjectURL(this.src);
ctx.drawImage(image, 0, 0, image.width, image.height, 0, 0, canvas.width, canvas.height);
convert_dithering()
}
}
}
function clear_canvas() {
if(confirm('确认清除画布内容?')) {
const ctx = canvas.getContext("2d");
ctx.fillStyle = 'white';
ctx.fillRect(0, 0, canvas.width, canvas.height);
document.getElementById('cmdIMAGE').value = '';
}
}
function convert_dithering() {
const ctx = canvas.getContext("2d");
const mode = document.getElementById('dithering').value;
if (mode.startsWith('bwr')) {
ditheringCanvasByPalette(canvas, bwrPalette, mode);
} else {
dithering(ctx, canvas.width, canvas.height, parseInt(document.getElementById('threshold').value), mode);
}
updateImageData(canvas);
}
document.body.onload = () => {
epdDriver = document.getElementById("epddriver").value;
canvas = document.getElementById('canvas');
updateButtonStatus();
bytes2canvas(hexToBytes(document.getElementById('cmdIMAGE').value), canvas);
document.getElementById('dithering').value = 'none';
}