diff --git a/html/index.html b/html/index.html index 44efe5c..45d3052 100644 --- a/html/index.html +++ b/html/index.html @@ -25,6 +25,7 @@ +
@@ -60,10 +61,14 @@ - + + + + +
@@ -96,6 +101,7 @@ diff --git a/html/js/dithering.js b/html/js/dithering.js index 9f5d78b..78407d7 100644 --- a/html/js/dithering.js +++ b/html/js/dithering.js @@ -1,9 +1,22 @@ -const bwrPalette = [ - [0, 0, 0, 255], - [255, 255, 255, 255], - [255, 0, 0, 255] +const bwPalette = [ + [0, 0, 0, 255], // black + [255, 255, 255, 255] // white ] +const bwrPalette = [ + [0, 0, 0, 255], // black + [255, 255, 255, 255], // white + [255, 0, 0, 255] // red +] + +const bwryPalette = [ + [0, 0, 0, 255], // black + [255, 255, 255, 255], // white + [255, 255, 0, 255], // yellow + [255, 0, 0, 255] // red +] + +// black-white dithering function dithering(ctx, width, height, threshold, type) { const bayerThresholdMap = [ [ 15, 135, 45, 165 ], @@ -43,7 +56,6 @@ function dithering(ctx, width, height, threshold, type) { newPixel = imageData.data[currentPixel] < threshold ? 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; @@ -52,7 +64,6 @@ function dithering(ctx, width, height, threshold, type) { 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; @@ -68,7 +79,6 @@ function dithering(ctx, width, height, threshold, type) { ctx.putImageData(imageData, 0, 0); } -// white: 1, black/red: 0 function canvas2bytes(canvas, step = 'bw', invert = false) { const ctx = canvas.getContext("2d"); const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); @@ -79,25 +89,37 @@ function canvas2bytes(canvas, step = 'bw', invert = false) { for (let y = 0; y < canvas.height; y++) { for (let x = 0; x < canvas.width; x++) { const i = (canvas.width * y + x) * 4; - if (step === 'bw') { - buffer.push(imageData.data[i] === 0 && imageData.data[i+1] === 0 && imageData.data[i+2] === 0 ? 0 : 1); - } else { - buffer.push(imageData.data[i] > 0 && imageData.data[i+1] === 0 && imageData.data[i+2] === 0 ? 0 : 1); - } - if (buffer.length === 8) { - const data = parseInt(buffer.join(''), 2); - arr.push(invert ? ~data : data); - buffer = []; + const r = imageData.data[i]; + const g = imageData.data[i + 1]; + const b = imageData.data[i + 2]; + + if (step === 'bwry') { // black: 0, white: 1, yellow: 2, red: 3 + buffer.push(getNearColorIdx([r, g, b, 255], bwryPalette)); + if (buffer.length === 4) { + const byte = (buffer[0] << 6) | (buffer[1] << 4) | (buffer[2] << 2) | buffer[3]; + arr.push(invert ? ~byte & 0xFF : byte); + buffer = []; + } + } else { // white: 1, black/red: 0 + if (step === 'bw') { + buffer.push(r === 0 && g === 0 && b === 0 ? 0 : 1); + } else if (step === 'red') { + buffer.push(r > 0 && g === 0 && b === 0 ? 0 : 1); + } + if (buffer.length === 8) { + const data = parseInt(buffer.join(''), 2); + arr.push(invert ? ~data : data); + buffer = []; + } } } } return arr; } -function getNearColorV2(color, palette) { +function getNearColorIdx(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); @@ -109,7 +131,7 @@ function getNearColorV2(color, palette) { bestIndex = i; } } - return palette[bestIndex]; + return bestIndex; } function updatePixel(imageData, index, color) { @@ -133,27 +155,32 @@ function updatePixelErr(imageData, index, err, rate) { imageData[index+2] += err[2] * rate; } -function ditheringCanvasByPalette(canvas, palette, type) { - palette = palette || bwrPalette; - +// Dithering by palette +function ditheringByPalette(canvas, type) { const ctx = canvas.getContext('2d'); const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); const w = imageData.width; + let palette = bwPalette; + + if (type.startsWith("bwry")) { + palette = bwryPalette; + } else if (type.startsWith("bwr")) { + palette = bwrPalette; + } 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); + const color = imageData.data.slice(currentPixel, currentPixel+4); + const newColor = palette[getNearColorIdx(color, palette)]; + if (type.endsWith("floydsteinberg")) { + const err = getColorErr(color, 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); - + const err = getColorErr(color, newColor, 8); updatePixel(imageData.data, currentPixel, newColor); updatePixelErr(imageData.data, currentPixel +4, err, 1); updatePixelErr(imageData.data, currentPixel +8, err, 1); diff --git a/html/js/main.js b/html/js/main.js index 251f37b..b542556 100644 --- a/html/js/main.js +++ b/html/js/main.js @@ -107,7 +107,7 @@ async function epdWriteImage(step = 'bw') { for (let i = 0; i < data.length; i += chunkSize) { let currentTime = (new Date().getTime() - startTime) / 1000.0; - setStatus(`${step == 'bw' ? '黑白' : '红色'}块: ${chunkIdx+1}/${count+1}, 总用时: ${currentTime}s`); + setStatus(`${step == 'bw' ? '黑白' : '颜色'}块: ${chunkIdx+1}/${count+1}, 总用时: ${currentTime}s`); const payload = [ (step == 'bw' ? 0x0F : 0x00) | ( i == 0 ? 0x00 : 0xF0), ...data.slice(i, i + chunkSize), @@ -169,15 +169,22 @@ async function sendimg() { updateButtonStatus(true); if (appVersion < 0x16) { - if (mode.startsWith('bwr')) { + if (mode.startsWith('bwry')) { + addLog("当前固件版本不支持四色屏幕。"); + return; + } if (mode.startsWith('bwr')) { await epdWrite(driver === "02" ? 0x24 : 0x10, canvas2bytes(canvas, 'bw')); await epdWrite(driver === "02" ? 0x26 : 0x13, canvas2bytes(canvas, 'red', driver === '02')); } else { await epdWrite(driver === "04" ? 0x24 : 0x13, canvas2bytes(canvas, 'bw')); } } else { - await epdWriteImage('bw'); - if (mode.startsWith('bwr')) await epdWriteImage('red'); + if (mode.startsWith('bwry')) { + await epdWriteImage('bwry'); + } else { + await epdWriteImage('bw'); + if (mode.startsWith('bwr')) await epdWriteImage('red'); + } } await write(EpdCmd.REFRESH); @@ -381,7 +388,7 @@ function convertDithering() { if (mode === '') return; if (mode.startsWith('bwr')) { - ditheringCanvasByPalette(canvas, bwrPalette, mode); + ditheringByPalette(canvas, mode); } else { const threshold = document.getElementById('threshold').value; dithering(ctx, canvas.width, canvas.height, parseInt(threshold), mode); diff --git a/html/js/paint.js b/html/js/paint.js index 04d164e..a30ddf5 100644 --- a/html/js/paint.js +++ b/html/js/paint.js @@ -121,6 +121,11 @@ function updateBrushOptions() { option.removeAttribute('disabled'); else option.setAttribute('disabled', 'disabled'); + } else if (option.value === '#FFFF00') { + if (dithering.startsWith('bwry')) + option.removeAttribute('disabled'); + else + option.setAttribute('disabled', 'disabled'); } } // Revert brush color to black if red is not allowed