mirror of
https://github.com/tsl0922/EPD-nRF5.git
synced 2025-12-06 15:42:48 +08:00
update html
This commit is contained in:
@@ -413,6 +413,23 @@ body.debug-mode .tool-button.active {
|
|||||||
border-color: var(--primary-hover);
|
border-color: var(--primary-hover);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input[type=text]:disabled,
|
||||||
|
input[type=number]:disabled,
|
||||||
|
select:disabled {
|
||||||
|
opacity: 0.65;
|
||||||
|
cursor: not-allowed;
|
||||||
|
background-color: #e9ecef;
|
||||||
|
color: #6c757d;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.debug-mode input[type=text]:disabled,
|
||||||
|
body.debug-mode input[type=number]:disabled,
|
||||||
|
body.debug-mode select:disabled {
|
||||||
|
background-color: #1a1a1a;
|
||||||
|
color: #666;
|
||||||
|
border-color: #2a2a2a;
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.flex-container {
|
.flex-container {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|||||||
@@ -48,12 +48,12 @@
|
|||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>蓝牙传图</legend>
|
<legend>蓝牙传图</legend>
|
||||||
<div class="flex-container">
|
<div class="flex-container">
|
||||||
<input type="file" id="image_file" onchange="update_image(true)" accept=".png,.jpg,.bmp,.webp,.jpeg">
|
<input type="file" id="image_file" onchange="updateImage(true)" accept=".png,.jpg,.bmp,.webp,.jpeg">
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-container">
|
<div class="flex-container">
|
||||||
<div class="flex-group">
|
<div class="flex-group">
|
||||||
<label for="dithering">取模算法</label>
|
<label for="dithering">取模算法</label>
|
||||||
<select id="dithering" title="取模算法" onchange="update_image(false)">
|
<select id="dithering" title="取模算法" onchange="onDitheringChange()">
|
||||||
<optgroup data-driver="01|04" label="黑白">
|
<optgroup data-driver="01|04" label="黑白">
|
||||||
<option value="none">二值化</option>
|
<option value="none">二值化</option>
|
||||||
<option value="bayer">bayer</option>
|
<option value="bayer">bayer</option>
|
||||||
@@ -68,7 +68,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="flex-group">
|
<div class="flex-group">
|
||||||
<label for="threshold">阈值</label>
|
<label for="threshold">阈值</label>
|
||||||
<input type="number" max="255" min="0" value="125" id="threshold" onchange="update_image(false)">
|
<input type="number" max="255" min="1" value="125" id="threshold" oninput="updateImage(false)">
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-group debug">
|
<div class="flex-group debug">
|
||||||
<label for="mtusize">MTU</label>
|
<label for="mtusize">MTU</label>
|
||||||
@@ -80,7 +80,7 @@
|
|||||||
<div class="status-bar"><b>状态:</b><span id="status"></span></div>
|
<div class="status-bar"><b>状态:</b><span id="status"></span></div>
|
||||||
<div class="flex-container">
|
<div class="flex-container">
|
||||||
<div class="flex-group">
|
<div class="flex-group">
|
||||||
<button id="clearcanvasbutton" type="button" class="secondary" onclick="clear_canvas()">清除画布</button>
|
<button id="clearcanvasbutton" type="button" class="secondary" onclick="clearCanvas()">清除画布</button>
|
||||||
<button id="sendimgbutton" type="button" class="primary" onclick="sendimg()">发送图片</button>
|
<button id="sendimgbutton" type="button" class="primary" onclick="sendimg()">发送图片</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -4,11 +4,6 @@ const bwrPalette = [
|
|||||||
[255, 0, 0, 255]
|
[255, 0, 0, 255]
|
||||||
]
|
]
|
||||||
|
|
||||||
const bwPalette = [
|
|
||||||
[0, 0, 0, 255],
|
|
||||||
[255, 255, 255, 255],
|
|
||||||
]
|
|
||||||
|
|
||||||
function dithering(ctx, width, height, threshold, type) {
|
function dithering(ctx, width, height, threshold, type) {
|
||||||
const bayerThresholdMap = [
|
const bayerThresholdMap = [
|
||||||
[ 15, 135, 45, 165 ],
|
[ 15, 135, 45, 165 ],
|
||||||
@@ -26,7 +21,6 @@ function dithering(ctx, width, height, threshold, type) {
|
|||||||
lumB[i] = i*0.114;
|
lumB[i] = i*0.114;
|
||||||
}
|
}
|
||||||
const imageData = ctx.getImageData(0, 0, width, height);
|
const imageData = ctx.getImageData(0, 0, width, height);
|
||||||
|
|
||||||
const imageDataLength = imageData.data.length;
|
const imageDataLength = imageData.data.length;
|
||||||
|
|
||||||
// Greyscale luminance (sets r pixels to luminance of rgb)
|
// Greyscale luminance (sets r pixels to luminance of rgb)
|
||||||
@@ -38,21 +32,15 @@ function dithering(ctx, width, height, threshold, type) {
|
|||||||
let newPixel, err;
|
let newPixel, err;
|
||||||
|
|
||||||
for (let currentPixel = 0; currentPixel <= imageDataLength; currentPixel+=4) {
|
for (let currentPixel = 0; currentPixel <= imageDataLength; currentPixel+=4) {
|
||||||
if (type === "gray") {
|
if (type ==="none") { // No dithering
|
||||||
const factor = 255 / (threshold - 1);
|
|
||||||
imageData.data[currentPixel] = Math.round(imageData.data[currentPixel] / factor) * factor;
|
|
||||||
} else if (type ==="none") {
|
|
||||||
// No dithering
|
|
||||||
imageData.data[currentPixel] = imageData.data[currentPixel] < threshold ? 0 : 255;
|
imageData.data[currentPixel] = imageData.data[currentPixel] < threshold ? 0 : 255;
|
||||||
} else if (type ==="bayer") {
|
} else if (type ==="bayer") { // 4x4 Bayer ordered dithering algorithm
|
||||||
// 4x4 Bayer ordered dithering algorithm
|
|
||||||
var x = currentPixel/4 % w;
|
var x = currentPixel/4 % w;
|
||||||
var y = Math.floor(currentPixel/4 / w);
|
var y = Math.floor(currentPixel/4 / w);
|
||||||
var map = Math.floor( (imageData.data[currentPixel] + bayerThresholdMap[x%4][y%4]) / 2 );
|
var map = Math.floor( (imageData.data[currentPixel] + bayerThresholdMap[x%4][y%4]) / 2 );
|
||||||
imageData.data[currentPixel] = (map < threshold) ? 0 : 255;
|
imageData.data[currentPixel] = (map < threshold) ? 0 : 255;
|
||||||
} else if (type ==="floydsteinberg") {
|
} else if (type ==="floydsteinberg") { // Floyda€"Steinberg dithering algorithm
|
||||||
// Floyda€"Steinberg dithering algorithm
|
newPixel = imageData.data[currentPixel] < threshold ? 0 : 255;
|
||||||
newPixel = imageData.data[currentPixel] < 129 ? 0 : 255;
|
|
||||||
err = Math.floor((imageData.data[currentPixel] - newPixel) / 16);
|
err = Math.floor((imageData.data[currentPixel] - newPixel) / 16);
|
||||||
imageData.data[currentPixel] = newPixel;
|
imageData.data[currentPixel] = newPixel;
|
||||||
|
|
||||||
@@ -60,8 +48,7 @@ function dithering(ctx, width, height, threshold, type) {
|
|||||||
imageData.data[currentPixel + 4*w - 4 ] += err*3;
|
imageData.data[currentPixel + 4*w - 4 ] += err*3;
|
||||||
imageData.data[currentPixel + 4*w ] += err*5;
|
imageData.data[currentPixel + 4*w ] += err*5;
|
||||||
imageData.data[currentPixel + 4*w + 4 ] += err*1;
|
imageData.data[currentPixel + 4*w + 4 ] += err*1;
|
||||||
} else {
|
} else { // Bill Atkinson's dithering algorithm
|
||||||
// Bill Atkinson's dithering algorithm
|
|
||||||
newPixel = imageData.data[currentPixel] < threshold ? 0 : 255;
|
newPixel = imageData.data[currentPixel] < threshold ? 0 : 255;
|
||||||
err = Math.floor((imageData.data[currentPixel] - newPixel) / 8);
|
err = Math.floor((imageData.data[currentPixel] - newPixel) / 8);
|
||||||
imageData.data[currentPixel] = newPixel;
|
imageData.data[currentPixel] = newPixel;
|
||||||
@@ -108,36 +95,6 @@ function canvas2bytes(canvas, step = 'bw', invert = false) {
|
|||||||
return arr;
|
return arr;
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
function getNearColorV2(color, palette) {
|
||||||
let minDistanceSquared = 255*255 + 255*255 + 255*255 + 1;
|
let minDistanceSquared = 255*255 + 255*255 + 255*255 + 1;
|
||||||
|
|
||||||
@@ -153,10 +110,8 @@ function getNearColorV2(color, palette) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return palette[bestIndex];
|
return palette[bestIndex];
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function updatePixel(imageData, index, color) {
|
function updatePixel(imageData, index, color) {
|
||||||
imageData[index] = color[0];
|
imageData[index] = color[0];
|
||||||
imageData[index+1] = color[1];
|
imageData[index+1] = color[1];
|
||||||
|
|||||||
@@ -22,6 +22,24 @@ const EpdCmd = {
|
|||||||
CFG_ERASE: 0x99,
|
CFG_ERASE: 0x99,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function hex2bytes(hex) {
|
||||||
|
for (var bytes = [], c = 0; c < hex.length; c += 2)
|
||||||
|
bytes.push(parseInt(hex.substr(c, 2), 16));
|
||||||
|
return new Uint8Array(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
function bytes2hex(data) {
|
||||||
|
return new Uint8Array(data).reduce(
|
||||||
|
function (memo, i) {
|
||||||
|
return memo + ("0" + i.toString(16)).slice(-2);
|
||||||
|
}, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
function intToHex(intIn) {
|
||||||
|
let stringOut = ("0000" + intIn.toString(16)).substr(-4)
|
||||||
|
return stringOut.substring(2, 4) + stringOut.substring(0, 2);
|
||||||
|
}
|
||||||
|
|
||||||
function resetVariables() {
|
function resetVariables() {
|
||||||
gattServer = null;
|
gattServer = null;
|
||||||
epdService = null;
|
epdService = null;
|
||||||
@@ -324,29 +342,18 @@ function clearLog() {
|
|||||||
document.getElementById("log").innerHTML = '';
|
document.getElementById("log").innerHTML = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
function hex2bytes(hex) {
|
function onDitheringChange() {
|
||||||
for (var bytes = [], c = 0; c < hex.length; c += 2)
|
const mode = document.getElementById('dithering').value;
|
||||||
bytes.push(parseInt(hex.substr(c, 2), 16));
|
const thresholdInput = document.getElementById('threshold');
|
||||||
return new Uint8Array(bytes);
|
thresholdInput.disabled = (mode === '' || mode.startsWith('bwr'));
|
||||||
|
updateImage(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
function bytes2hex(data) {
|
function updateImage(clear = false) {
|
||||||
return new Uint8Array(data).reduce(
|
|
||||||
function (memo, i) {
|
|
||||||
return memo + ("0" + i.toString(16)).slice(-2);
|
|
||||||
}, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
function intToHex(intIn) {
|
|
||||||
let stringOut = ("0000" + intIn.toString(16)).substr(-4)
|
|
||||||
return stringOut.substring(2, 4) + stringOut.substring(0, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function update_image(clear = false) {
|
|
||||||
const image_file = document.getElementById('image_file');
|
const image_file = document.getElementById('image_file');
|
||||||
if (image_file.files.length == 0) return;
|
if (image_file.files.length == 0) return;
|
||||||
|
|
||||||
if (clear) clear_canvas();
|
if (clear) clearCanvas();
|
||||||
|
|
||||||
const file = image_file.files[0];
|
const file = image_file.files[0];
|
||||||
let image = new Image();;
|
let image = new Image();;
|
||||||
@@ -354,11 +361,11 @@ async function update_image(clear = false) {
|
|||||||
image.onload = function(event) {
|
image.onload = function(event) {
|
||||||
URL.revokeObjectURL(this.src);
|
URL.revokeObjectURL(this.src);
|
||||||
ctx.drawImage(image, 0, 0, image.width, image.height, 0, 0, canvas.width, canvas.height);
|
ctx.drawImage(image, 0, 0, image.width, image.height, 0, 0, canvas.width, canvas.height);
|
||||||
convert_dithering()
|
convertDithering()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function clear_canvas() {
|
function clearCanvas() {
|
||||||
if (confirm('清除画布已有内容?')) {
|
if (confirm('清除画布已有内容?')) {
|
||||||
ctx.fillStyle = 'white';
|
ctx.fillStyle = 'white';
|
||||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||||
@@ -367,14 +374,15 @@ function clear_canvas() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function convert_dithering() {
|
function convertDithering() {
|
||||||
const mode = document.getElementById('dithering').value;
|
const mode = document.getElementById('dithering').value;
|
||||||
if (mode === '') return;
|
if (mode === '') return;
|
||||||
|
|
||||||
if (mode.startsWith('bwr')) {
|
if (mode.startsWith('bwr')) {
|
||||||
ditheringCanvasByPalette(canvas, bwrPalette, mode);
|
ditheringCanvasByPalette(canvas, bwrPalette, mode);
|
||||||
} else {
|
} else {
|
||||||
dithering(ctx, canvas.width, canvas.height, parseInt(document.getElementById('threshold').value), mode);
|
const threshold = document.getElementById('threshold').value;
|
||||||
|
dithering(ctx, canvas.width, canvas.height, parseInt(threshold), mode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user