mirror of
https://github.com/RoCry/blozi-etag.git
synced 2025-12-06 09:02:49 +08:00
feat: add support for render BWR
This commit is contained in:
Binary file not shown.
@@ -7,7 +7,7 @@
|
||||
#include "epd_bw_213.h"
|
||||
#include "epd_bwr_213.h"
|
||||
#include "epd_bw_213_ice.h"
|
||||
#include "epd_bwr_154.h"
|
||||
//#include "epd_bwr_154.h"
|
||||
#include "epd_bwr_296.h"
|
||||
#include "drivers.h"
|
||||
#include "stack/ble/ble.h"
|
||||
@@ -82,10 +82,10 @@ _attribute_ram_code_ void EPD_detect_model(void)
|
||||
{
|
||||
epd_model = 2;
|
||||
}
|
||||
else if (EPD_BWR_154_detect())// Right now this will never trigger, the 154 is same to 213BWR right now.
|
||||
{
|
||||
epd_model = 3;
|
||||
}
|
||||
// else if (EPD_BWR_154_detect())// Right now this will never trigger, the 154 is same to 213BWR right now.
|
||||
// {
|
||||
// epd_model = 3;
|
||||
// }
|
||||
else if (EPD_BW_213_ice_detect())
|
||||
{
|
||||
epd_model = 4;
|
||||
@@ -120,8 +120,8 @@ _attribute_ram_code_ uint8_t EPD_read_temp(void)
|
||||
epd_temperature = EPD_BW_213_read_temp();
|
||||
else if (epd_model == 2)
|
||||
epd_temperature = EPD_BWR_213_read_temp();
|
||||
else if (epd_model == 3)
|
||||
epd_temperature = EPD_BWR_154_read_temp();
|
||||
// else if (epd_model == 3)
|
||||
// epd_temperature = EPD_BWR_154_read_temp();
|
||||
else if (epd_model == 4 || epd_model == 5)
|
||||
epd_temperature = EPD_BW_213_ice_read_temp();
|
||||
|
||||
@@ -132,7 +132,7 @@ _attribute_ram_code_ uint8_t EPD_read_temp(void)
|
||||
return epd_temperature;
|
||||
}
|
||||
|
||||
_attribute_ram_code_ void EPD_Display(unsigned char *image, int size, uint8_t full_or_partial)
|
||||
_attribute_ram_code_ void EPD_Display(unsigned char *image, unsigned char *red_image, int size, uint8_t full_or_partial)
|
||||
{
|
||||
if (!epd_model)
|
||||
EPD_detect_model();
|
||||
@@ -151,12 +151,13 @@ _attribute_ram_code_ void EPD_Display(unsigned char *image, int size, uint8_t fu
|
||||
epd_temperature = EPD_BW_213_Display(image, size, full_or_partial);
|
||||
else if (epd_model == 2)
|
||||
epd_temperature = EPD_BWR_213_Display(image, size, full_or_partial);
|
||||
else if (epd_model == 3)
|
||||
epd_temperature = EPD_BWR_154_Display(image, size, full_or_partial);
|
||||
// else if (epd_model == 3)
|
||||
// epd_temperature = EPD_BWR_154_Display(image, size, full_or_partial);
|
||||
else if (epd_model == 4)
|
||||
epd_temperature = EPD_BW_213_ice_Display(image, size, full_or_partial);
|
||||
else if (epd_model == 5)
|
||||
epd_temperature = EPD_BWR_296_Display(image, size, full_or_partial);
|
||||
epd_temperature = EPD_BWR_296_Display_BWR(image, red_image, size, full_or_partial);
|
||||
//epd_temperature = EPD_BWR_296_Display(image, size, full_or_partial);
|
||||
|
||||
epd_temperature_is_read = 1;
|
||||
epd_update_state = 1;
|
||||
@@ -171,8 +172,8 @@ _attribute_ram_code_ void epd_set_sleep(void)
|
||||
EPD_BW_213_set_sleep();
|
||||
else if (epd_model == 2)
|
||||
EPD_BWR_213_set_sleep();
|
||||
else if (epd_model == 3)
|
||||
EPD_BWR_154_set_sleep();
|
||||
// else if (epd_model == 3)
|
||||
// EPD_BWR_154_set_sleep();
|
||||
else if (epd_model == 4 || epd_model == 5)
|
||||
EPD_BW_213_ice_set_sleep();
|
||||
|
||||
@@ -248,12 +249,12 @@ _attribute_ram_code_ void TIFFDraw(TIFFDRAW *pDraw)
|
||||
_attribute_ram_code_ void epd_display_tiff(uint8_t *pData, int iSize)
|
||||
{
|
||||
// test G4 decoder
|
||||
memset(epd_buffer, 0xff, epd_buffer_size); // clear to white
|
||||
epd_clear();
|
||||
TIFF_openRAW(&tiff, 250, 122, BITDIR_MSB_FIRST, pData, iSize, TIFFDraw);
|
||||
TIFF_setDrawParameters(&tiff, 65536, TIFF_PIXEL_1BPP, 0, 0, 250, 122, NULL);
|
||||
TIFF_decode(&tiff);
|
||||
TIFF_close(&tiff);
|
||||
EPD_Display(epd_buffer, epd_buffer_size, 1);
|
||||
EPD_Display(epd_buffer, NULL, epd_buffer_size, 1);
|
||||
}
|
||||
|
||||
extern uint8_t mac_public[6];
|
||||
@@ -296,6 +297,8 @@ _attribute_ram_code_ void epd_display(struct date_time _time, uint16_t battery_m
|
||||
resolution_h = 128;
|
||||
}
|
||||
|
||||
epd_clear();
|
||||
|
||||
obdCreateVirtualDisplay(&obd, resolution_w, resolution_h, epd_temp);
|
||||
obdFill(&obd, 0, 0); // fill with white
|
||||
|
||||
@@ -312,7 +315,7 @@ _attribute_ram_code_ void epd_display(struct date_time _time, uint16_t battery_m
|
||||
sprintf(buff, "Battery %dmV %d%%", battery_mv, battery_level);
|
||||
obdWriteStringCustom(&obd, (GFXfont *)&Dialog_plain_16, 10, 120, (char *)buff, 1);
|
||||
FixBuffer(epd_temp, epd_buffer, resolution_w, resolution_h);
|
||||
EPD_Display(epd_buffer, resolution_w * resolution_h / 8, full_or_partial);
|
||||
EPD_Display(epd_buffer, NULL, resolution_w * resolution_h / 8, full_or_partial);
|
||||
}
|
||||
|
||||
_attribute_ram_code_ void epd_display_char(uint8_t data)
|
||||
@@ -322,12 +325,13 @@ _attribute_ram_code_ void epd_display_char(uint8_t data)
|
||||
{
|
||||
epd_buffer[i] = data;
|
||||
}
|
||||
EPD_Display(epd_buffer, epd_buffer_size, 1);
|
||||
EPD_Display(epd_buffer, NULL, epd_buffer_size, 1);
|
||||
}
|
||||
|
||||
_attribute_ram_code_ void epd_clear(void)
|
||||
{
|
||||
memset(epd_buffer, 0x00, epd_buffer_size);
|
||||
memset(epd_temp, 0x00, epd_buffer_size);
|
||||
}
|
||||
void update_time_scene(struct date_time _time, uint16_t battery_mv, int16_t temperature, void (*scene)(struct date_time, uint16_t, int16_t, uint8_t)) {
|
||||
// default scene: show default time, battery, ble address, temperature
|
||||
@@ -375,6 +379,8 @@ void epd_update(struct date_time _time, uint16_t battery_mv, int16_t temperature
|
||||
void epd_display_time_with_date(struct date_time _time, uint16_t battery_mv, int16_t temperature, uint8_t full_or_partial) {
|
||||
uint16_t battery_level;
|
||||
|
||||
epd_clear();
|
||||
|
||||
obdCreateVirtualDisplay(&obd, epd_width, epd_height, epd_temp);
|
||||
obdFill(&obd, 0, 0); // fill with white
|
||||
|
||||
@@ -408,5 +414,6 @@ void epd_display_time_with_date(struct date_time _time, uint16_t battery_mv, int
|
||||
|
||||
|
||||
FixBuffer(epd_temp, epd_buffer, epd_width, epd_height);
|
||||
EPD_Display(epd_buffer, epd_width * epd_height / 8, full_or_partial);
|
||||
|
||||
EPD_Display(epd_buffer, NULL, epd_width * epd_height / 8, full_or_partial);
|
||||
}
|
||||
@@ -10,7 +10,7 @@ void set_EPD_wait_flush();
|
||||
|
||||
void init_epd(void);
|
||||
uint8_t EPD_read_temp(void);
|
||||
void EPD_Display(unsigned char *image, int size, uint8_t full_or_partial);
|
||||
void EPD_Display(unsigned char *image, unsigned char * red_image, int size, uint8_t full_or_partial);
|
||||
void epd_display_tiff(uint8_t *pData, int iSize);
|
||||
void epd_set_sleep(void);
|
||||
uint8_t epd_state_handler(void);
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#include "epd.h"
|
||||
#include "ble.h"
|
||||
|
||||
extern uint8_t *epd_temp;
|
||||
extern uint8_t epd_temp[epd_buffer_size];
|
||||
|
||||
#define ASSERT_MIN_LEN(val, min_len) \
|
||||
if (val < min_len) \
|
||||
@@ -31,14 +31,15 @@ int epd_ble_handle_write(void *p)
|
||||
{
|
||||
// Clear EPD display.
|
||||
case 0x00:
|
||||
ASSERT_MIN_LEN(payload_len, 2);
|
||||
memset(epd_buffer, payload[1], sizeof(epd_buffer));
|
||||
ASSERT_MIN_LEN(payload_len, 2);
|
||||
memset(epd_buffer, payload[1], epd_buffer_size);
|
||||
memset(epd_temp, payload[1], epd_buffer_size);
|
||||
ble_set_connection_speed(40);
|
||||
return 0;
|
||||
// Push buffer to display.
|
||||
case 0x01:
|
||||
ble_set_connection_speed(200);
|
||||
EPD_Display(epd_buffer, epd_buffer_size, 1);
|
||||
EPD_Display(epd_buffer, epd_temp, epd_buffer_size, payload[1]);
|
||||
return 0;
|
||||
// Set byte_pos.
|
||||
case 0x02:
|
||||
@@ -47,14 +48,18 @@ int epd_ble_handle_write(void *p)
|
||||
return 0;
|
||||
// Write data to image buffer.
|
||||
case 0x03:
|
||||
if ((payload[1] << 8 | payload[2]) + payload_len - 3 >= epd_buffer_size + 1)
|
||||
if ((payload[2] << 8 | payload[3]) + payload_len - 4 >= epd_buffer_size + 1)
|
||||
{
|
||||
out_buffer[0] = 0x00;
|
||||
out_buffer[1] = 0x00;
|
||||
bls_att_pushNotifyData(EPD_BLE_CMD_OUT_DP_H, out_buffer, 2);
|
||||
return 0;
|
||||
}
|
||||
memcpy(epd_buffer + (payload[1] << 8 | payload[2]), payload + 3, payload_len - 3);
|
||||
if (payload[1] == 0xff) {
|
||||
memcpy(epd_buffer + (payload[2] << 8 | payload[3]), payload + 4, payload_len - 4);
|
||||
} else {
|
||||
memcpy(epd_temp + (payload[2] << 8 | payload[3]), payload + 4, payload_len - 4);
|
||||
}
|
||||
|
||||
out_buffer[0] = payload_len >> 8;
|
||||
out_buffer[1] = payload_len & 0xff;
|
||||
|
||||
@@ -257,6 +257,125 @@ _attribute_ram_code_ uint8_t EPD_BWR_296_Display(unsigned char *image, int size,
|
||||
return epd_temperature;
|
||||
}
|
||||
|
||||
_attribute_ram_code_ uint8_t EPD_BWR_296_Display_BWR(unsigned char *image, unsigned char *red_image, int size, uint8_t full_or_partial) {
|
||||
if (red_image == NULL) {
|
||||
return EPD_BWR_296_Display(image, size, full_or_partial);
|
||||
}
|
||||
|
||||
uint8_t epd_temperature = 0 ;
|
||||
|
||||
// SW Reset
|
||||
EPD_WriteCmd(0x12);
|
||||
|
||||
EPD_CheckStatus_inverted(100);
|
||||
|
||||
// Set Analog Block control
|
||||
EPD_WriteCmd(0x74);
|
||||
EPD_WriteData(0x54);
|
||||
// Set Digital Block control
|
||||
EPD_WriteCmd(0x7E);
|
||||
EPD_WriteData(0x3B);
|
||||
|
||||
// Booster soft start
|
||||
EPD_WriteCmd(0x0C);
|
||||
EPD_WriteData(0x8B);
|
||||
EPD_WriteData(0x9C);
|
||||
EPD_WriteData(0x96);
|
||||
EPD_WriteData(0x0F);
|
||||
|
||||
// Driver output control
|
||||
EPD_WriteCmd(0x01);
|
||||
EPD_WriteData(0x28);
|
||||
EPD_WriteData(0x01);
|
||||
EPD_WriteData(0x01);
|
||||
|
||||
// Data entry mode setting
|
||||
EPD_WriteCmd(0x11);
|
||||
EPD_WriteData(0x01);
|
||||
|
||||
// Set RAM X- Address Start/End
|
||||
EPD_WriteCmd(0x44);
|
||||
EPD_WriteData(0x00);
|
||||
EPD_WriteData(0x0F);
|
||||
|
||||
// Set RAM Y- Address Start/End
|
||||
EPD_WriteCmd(0x45);
|
||||
EPD_WriteData(0x28); //0x0127-->(295+1)=296
|
||||
EPD_WriteData(0x01);
|
||||
EPD_WriteData(0x00);
|
||||
EPD_WriteData(0x00);
|
||||
|
||||
// Border waveform control
|
||||
EPD_WriteCmd(0x3C);
|
||||
EPD_WriteData(0x05);
|
||||
|
||||
// Display update control
|
||||
EPD_WriteCmd(0x21);
|
||||
EPD_WriteData(0x00);
|
||||
EPD_WriteData(0x80);
|
||||
|
||||
// Temperature sensor control
|
||||
EPD_WriteCmd(0x18);
|
||||
EPD_WriteData(0x80);
|
||||
|
||||
// Display update control
|
||||
EPD_WriteCmd(0x22);
|
||||
EPD_WriteData(0xB1);
|
||||
|
||||
// Master Activation
|
||||
EPD_WriteCmd(0x20);
|
||||
|
||||
EPD_CheckStatus_inverted(100);
|
||||
|
||||
// Temperature sensor read from register
|
||||
EPD_WriteCmd(0x1B);
|
||||
epd_temperature = EPD_SPI_read();
|
||||
EPD_SPI_read();
|
||||
|
||||
WaitMs(5);
|
||||
|
||||
// Set RAM X address
|
||||
EPD_WriteCmd(0x4E);
|
||||
EPD_WriteData(0x00);
|
||||
|
||||
// Set RAM Y address
|
||||
EPD_WriteCmd(0x4F);
|
||||
EPD_WriteData(0x28);
|
||||
EPD_WriteData(0x01);
|
||||
|
||||
EPD_LoadImage(image, size, 0x24);
|
||||
|
||||
// Set RAM X address
|
||||
EPD_WriteCmd(0x4E);
|
||||
EPD_WriteData(0x00);
|
||||
|
||||
// Set RAM Y address
|
||||
EPD_WriteCmd(0x4F);
|
||||
EPD_WriteData(0x28);
|
||||
EPD_WriteData(0x01);
|
||||
|
||||
EPD_LoadImage(red_image, size, 0x26);
|
||||
|
||||
int i;
|
||||
if (!full_or_partial)
|
||||
{
|
||||
EPD_WriteCmd(0x32);
|
||||
for (i = 0; i < sizeof(LUT_bwr_296_part); i++)
|
||||
{
|
||||
EPD_WriteData(LUT_bwr_296_part[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// Display update control
|
||||
EPD_WriteCmd(0x22);
|
||||
EPD_WriteData(0xC7);
|
||||
|
||||
// Master Activation
|
||||
EPD_WriteCmd(0x20);
|
||||
|
||||
return epd_temperature;
|
||||
}
|
||||
|
||||
_attribute_ram_code_ void EPD_BWR_296_set_sleep(void)
|
||||
{
|
||||
// deep sleep
|
||||
|
||||
@@ -3,4 +3,5 @@
|
||||
uint8_t EPD_BWR_296_detect(void);
|
||||
uint8_t EPD_BWR_296_read_temp(void);
|
||||
uint8_t EPD_BWR_296_Display(unsigned char *image, int size, uint8_t full_or_partial);
|
||||
uint8_t EPD_BWR_296_Display_BWR(unsigned char *image, unsigned char *red_image, int size, uint8_t full_or_partial);
|
||||
void EPD_BWR_296_set_sleep(void);
|
||||
@@ -14,7 +14,6 @@ $(OUT_PATH)/epd_bw_213.o \
|
||||
$(OUT_PATH)/epd_bwr_296.o \
|
||||
$(OUT_PATH)/epd_bwr_213.o \
|
||||
$(OUT_PATH)/epd_bw_213_ice.o \
|
||||
$(OUT_PATH)/epd_bwr_154.o \
|
||||
$(OUT_PATH)/ota.o \
|
||||
$(OUT_PATH)/led.o \
|
||||
$(OUT_PATH)/uart.o \
|
||||
|
||||
@@ -66,8 +66,8 @@ Firmware CRC32: 0xe62d501e
|
||||
- [x] web 支持图片切换
|
||||
- [x] 添加新的时间场景
|
||||
- [x] 支持设置年月日
|
||||
- [x] web 支持画图编辑,直接上传图片,抖动算法
|
||||
- [ ] 三色抖动算法、设备端三色显示支持,蓝牙传输支持
|
||||
- [x] web 支持画图编辑,直接上传图片,黑白抖动算法
|
||||
- [x] 三色抖动算法、设备端三色显示支持,蓝牙传输支持
|
||||
|
||||
### 计划新增
|
||||
- [ ] 安卓端控制器
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
}
|
||||
body {
|
||||
padding: 20px;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
</style>
|
||||
<script type="application/javascript" src="js/dithering.js"></script>
|
||||
@@ -112,27 +113,36 @@
|
||||
addLog(`上传tiff完成,耗时${(new Date().getTime() - startTime)/1000}s`);
|
||||
}
|
||||
|
||||
async function sendBufferData(value, type) {
|
||||
addLog(`开始发送图片模式:${type}, 大小 ${value.length/2/1024}KB`);
|
||||
let code = 'ff';
|
||||
if (type === 'bwr') {
|
||||
code = '00';
|
||||
}
|
||||
const step = 480;
|
||||
let partIndex = 0;
|
||||
for (let i = 0; i < value.length; i += step) {
|
||||
addLog(`正在发送第${partIndex+1}块. 块大小: ${step/2 + 4}byte. 起始位置: ${i/2}`);
|
||||
await sendCommand(hexToBytes("03" + code + intToHex(i / 2, 2) + value.substring(i, i + step)));
|
||||
partIndex += 1;
|
||||
}
|
||||
}
|
||||
|
||||
async function upload_image() {
|
||||
const canvas = document.getElementById('canvas');
|
||||
const value = bytesToHex(canvas2bytes(canvas));
|
||||
|
||||
|
||||
const startTime = new Date().getTime();
|
||||
|
||||
addLog(`开始刷新屏幕内存, 大小 ${value.length/2/1024}KB`);
|
||||
|
||||
await sendCommand(hexToBytes("0000"));
|
||||
|
||||
await sendCommand(hexToBytes("020000"));
|
||||
|
||||
const step = 480;
|
||||
let partIndex = 0;
|
||||
for (let i = 0; i < value.length; i += step) {
|
||||
addLog(`正在发送第${partIndex+1}块. 块大小: ${step/2 + 3}byte. 起始位置: ${i/2}`);
|
||||
await sendCommand(hexToBytes("03" + intToHex(i / 2, 2) + value.substring(i, i + step)));
|
||||
partIndex += 1;
|
||||
}
|
||||
await sendBufferData(bytesToHex(canvas2bytes(canvas)), 'bw')
|
||||
await sendBufferData(bytesToHex(canvas2bytes(canvas, 'bwr')), 'bwr')
|
||||
|
||||
await sendCommand(hexToBytes("01"))
|
||||
await sendCommand(hexToBytes("0101"))
|
||||
|
||||
addLog(`刷新完成,耗时${(new Date().getTime() - startTime)/1000}s`);
|
||||
}
|
||||
@@ -147,16 +157,11 @@
|
||||
|
||||
await sendCommand(hexToBytes("020000"));
|
||||
|
||||
const step = 480;
|
||||
let partIndex = 0;
|
||||
for (let i = 0; i < value.length; i += step) {
|
||||
addLog(`正在发送第${partIndex+1}块. 块大小: ${step/2 + 3}byte. 起始位置: ${i/2}`);
|
||||
await sendCommand(hexToBytes("03" + intToHex(i / 2, 2) + value.substring(i, i + step)));
|
||||
partIndex += 1;
|
||||
}
|
||||
await sendBufferData(value, 'bw');
|
||||
|
||||
await delay(150);
|
||||
|
||||
await sendCommand(hexToBytes("01"))
|
||||
await sendCommand(hexToBytes("0101"))
|
||||
|
||||
addLog(`刷新完成,耗时${(new Date().getTime() - startTime)/1000}s`);
|
||||
}
|
||||
@@ -301,14 +306,20 @@
|
||||
if(confirm('确认清除屏幕?')) {
|
||||
const canvas = document.getElementById('canvas');
|
||||
const ctx = canvas.getContext("2d");
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
ctx.fillStyle = 'white';
|
||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||
}
|
||||
}
|
||||
|
||||
function convert_dithering() {
|
||||
const canvas = document.getElementById('canvas');
|
||||
const ctx = canvas.getContext("2d");
|
||||
dithering(ctx, canvas.width, canvas.height, parseInt(document.getElementById('threshold').value), document.getElementById('dithering').value);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
document.body.onload = () => {
|
||||
@@ -318,6 +329,9 @@
|
||||
}, 1000);
|
||||
|
||||
const canvas = document.getElementById('canvas');
|
||||
const ctx = canvas.getContext("2d");
|
||||
ctx.fillStyle = 'white';
|
||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
let is_allow_drawing = false;
|
||||
let is_allow_move_editor = false;
|
||||
@@ -326,7 +340,7 @@
|
||||
const paint_color = document.getElementById('paint-color');
|
||||
const editor = document.getElementById('edit-font');
|
||||
const font = document.getElementById('font');
|
||||
document.getElementById('dithering').value = 'Atkinson';
|
||||
document.getElementById('dithering').value = 'bwr_Atkinson';
|
||||
image_mode.value = 'paint';
|
||||
paint_color.value = 'black';
|
||||
font.value = '黑体';
|
||||
@@ -335,6 +349,7 @@
|
||||
editor.style.fontSize = `${paint_size.value * 10}px`;
|
||||
editor.style.color = paint_color.value;
|
||||
editor.style.fontFamily = font.value;
|
||||
editor.style.fontWeight = 'bold';
|
||||
|
||||
if (is_allow_move_editor) {
|
||||
const {x, y} = get_position(canvas, e.clientX, e.clientY);
|
||||
@@ -342,8 +357,8 @@
|
||||
return;
|
||||
}
|
||||
|
||||
editor.style.left = `${e.clientX-50}px`;
|
||||
editor.style.top = `${e.clientY-50}px`;
|
||||
editor.style.left = `${e.clientX-20}px`;
|
||||
editor.style.top = `${e.clientY-20}px`;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -362,9 +377,8 @@
|
||||
return;
|
||||
}
|
||||
editor.style.display = 'none';
|
||||
const ctx = canvas.getContext("2d");
|
||||
ctx.beginPath();
|
||||
ctx.font = `${paint_size.value * 10}px ${font.value}`;
|
||||
ctx.font = `bold ${paint_size.value * 10}px ${font.value}`;
|
||||
ctx.fillStyle = paint_color.value;
|
||||
const {x, y} = get_position(canvas, parseInt(editor.style.left), parseInt(editor.style.top) + paint_size.value * 10);
|
||||
|
||||
@@ -407,7 +421,6 @@
|
||||
canvas.onmousedown = function(e) {
|
||||
let ele = get_position(canvas, e.clientX, e.clientY)
|
||||
let { x, y } = ele
|
||||
const ctx = canvas.getContext("2d");
|
||||
|
||||
switch (image_mode.value) {
|
||||
case 'paint':
|
||||
@@ -416,14 +429,13 @@
|
||||
ctx.moveTo(x, y);
|
||||
break;
|
||||
case 'font':
|
||||
if (editor.style.display === 'none') {
|
||||
editor.style.display='block';
|
||||
editor.style.left = `${e.clientX}px`;
|
||||
editor.style.top = `${e.clientY}px`;
|
||||
editor.style.fontSize = `${paint_size.value * 10}px`;
|
||||
editor.style.color = paint_color.value;
|
||||
editor.style.fontFamily = font.value;
|
||||
}
|
||||
editor.style.display='block';
|
||||
editor.style.left = `${e.clientX}px`;
|
||||
editor.style.top = `${e.clientY}px`;
|
||||
editor.style.fontSize = `${paint_size.value * 10}px`;
|
||||
editor.style.color = paint_color.value;
|
||||
editor.style.fontFamily = font.value;
|
||||
editor.style.fontWeight = 'bold';
|
||||
|
||||
break
|
||||
default:
|
||||
@@ -432,7 +444,6 @@
|
||||
};
|
||||
|
||||
canvas.onmousemove = (e) => {
|
||||
const ctx = canvas.getContext("2d");
|
||||
let ele = get_position(canvas, e.clientX, e.clientY)
|
||||
let { x, y } = ele;
|
||||
switch (image_mode.value) {
|
||||
@@ -475,9 +486,9 @@
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<h2>电子价签蓝牙控制器</h2>
|
||||
<a href="/uart_flasher.html">串口升级</a><a href="/ATC_TLSR_Paper%20BLE%20control.html">串口升级</a>
|
||||
<br>
|
||||
<h2>电子价签蓝牙控制器<a style="margin-left: 130px;" href="/uart_flasher.html">串口升级</a><a href="/ATC_TLSR_Paper%20BLE%20control.html">蓝牙OTA升级</a>
|
||||
</h2>
|
||||
|
||||
|
||||
<button id="connectbutton" type="button" onclick="preConnect();">链接</button>
|
||||
<button type="button" onclick="reConnect();">重新链接</button>
|
||||
@@ -488,7 +499,6 @@
|
||||
<input type="text" id="cmdTXT" value="0055">
|
||||
<button type="button" onclick="triggerEpdCmd(document.getElementById("cmdTXT").value);">发送指令</button>
|
||||
<br>
|
||||
<br>
|
||||
<button type="button" onclick="triggerRxTxCmd('e100')" title="点击该按钮,然后上传图片。图片将永久显示到屏幕上">设置为图片模式</button>
|
||||
<button type="button" onclick="triggerRxTxCmd('e101')" title="点击该按钮, 切换为时钟模式">设置为时钟模式1</button>
|
||||
<button type="button" onclick="triggerRxTxCmd('e102')" title="点击该按钮, 切换为时钟模式">设置为时钟模式2</button>
|
||||
@@ -497,13 +507,19 @@
|
||||
<br><br>
|
||||
|
||||
<h3>上传图片到屏幕</h3>
|
||||
<input type="file" id="image_file" onchange="update_image()">
|
||||
<input type="file" id="image_file" onchange="update_image()" accept=".png,.jpg,.bmp,.webp">
|
||||
抖动算法:
|
||||
<select id="dithering" title="抖动算法">
|
||||
<option value="none">二值化</option>
|
||||
<option value="bayer">bayer</option>
|
||||
<option value="floydsteinberg">floydsteinberg</option>
|
||||
<option value="Atkinson">Atkinson</option>
|
||||
<optgroup label="黑白">
|
||||
<option value="none">二值化</option>
|
||||
<option value="bayer">bayer</option>
|
||||
<option value="floydsteinberg">floydsteinberg</option>
|
||||
<option value="Atkinson">Atkinson</option>
|
||||
</optgroup>
|
||||
<optgroup label="黑白红多色">
|
||||
<option value="bwr_floydsteinberg">黑白红floydsteinberg</option>
|
||||
<option value="bwr_Atkinson">黑白红Atkinson</option>
|
||||
</optgroup>
|
||||
</select>
|
||||
阈值:
|
||||
<input type="number" max="255" min="0" value="125" id="threshold">
|
||||
@@ -511,7 +527,7 @@
|
||||
<br>
|
||||
|
||||
<div id="canvas-box">
|
||||
<div id="tool-box" style="margin-bottom: 5px; margin-top: 10px;">
|
||||
<div id="tool-box">
|
||||
模式:
|
||||
<select id="canvas-mode">
|
||||
<option value="paint">画笔</option>
|
||||
@@ -532,12 +548,11 @@
|
||||
<option value="仿宋">仿宋</option>
|
||||
<option value="宋体">宋体</option>
|
||||
<option value="楷体_GB2312">楷体_GB2312</option>
|
||||
<option value="仿宋_GB2312">仿宋_GB2312</option>
|
||||
<option value="华文行楷">华文行楷</option>
|
||||
</select>
|
||||
<button id="update-text" style="display: none">保存文本框</button>
|
||||
<button onclick="clear_canvas()">清屏</button>
|
||||
</div>
|
||||
<br>
|
||||
<input id="edit-font" style="max-width: 296px; position: absolute; border: black solid 1px;background-color: rgba(0,0,0,0);display: none;overflow: auto" />
|
||||
<canvas id="canvas" width="296" height="128" style="border: black solid 1px;"></canvas>
|
||||
<br>
|
||||
|
||||
@@ -1,39 +1,14 @@
|
||||
const bwrPalette = [
|
||||
[0, 0, 0, 0],
|
||||
[255, 255, 255, 0],
|
||||
[255, 0, 0, 0]
|
||||
[0, 0, 0, 255],
|
||||
[255, 255, 255, 255],
|
||||
[255, 0, 0, 255]
|
||||
]
|
||||
|
||||
const bwPalette = [
|
||||
[0, 0, 0, 0],
|
||||
[255, 255, 255, 0],
|
||||
[0, 0, 0, 255],
|
||||
[255, 255, 255, 255],
|
||||
]
|
||||
|
||||
function get_near_color(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 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 dithering(ctx, width, height, threshold, type) {
|
||||
const bayerThresholdMap = [
|
||||
[ 15, 135, 45, 165 ],
|
||||
@@ -104,7 +79,7 @@ function dithering(ctx, width, height, threshold, type) {
|
||||
ctx.putImageData(imageData, 0, 0);
|
||||
}
|
||||
|
||||
function canvas2bytes(canvas, rotate=1) {
|
||||
function canvas2bytes(canvas, type='bw') {
|
||||
const ctx = canvas.getContext("2d");
|
||||
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
||||
|
||||
@@ -114,7 +89,12 @@ function canvas2bytes(canvas, rotate=1) {
|
||||
for (let x = canvas.width - 1; x >= 0; x--) {
|
||||
for (let y = 0; y < canvas.height; y++) {
|
||||
const index = (canvas.width * 4 * y) + x * 4;
|
||||
buffer.push(imageData.data[index] > 0 ? 1 : 0);
|
||||
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 = [];
|
||||
@@ -124,25 +104,105 @@ function canvas2bytes(canvas, rotate=1) {
|
||||
return arr;
|
||||
}
|
||||
|
||||
function scaleImageData(canvas, scale) {
|
||||
const ctx = canvas.getContext("2d");
|
||||
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
||||
const scaled = ctx.createImageData(imageData.width * scale, imageData.height * scale);
|
||||
const subLine = ctx.createImageData(scale, 1).data
|
||||
for (let row = 0; row < imageData.height; row++) {
|
||||
for (let col = 0; col < imageData.width; col++) {
|
||||
let sourcePixel = imageData.data.subarray(
|
||||
(row * imageData.width + col) * 4,
|
||||
(row * imageData.width + col) * 4 + 4
|
||||
);
|
||||
for (let x = 0; x < scale; x++) subLine.set(sourcePixel, x*4)
|
||||
for (let y = 0; y < scale; y++) {
|
||||
let destRow = row * scale + y;
|
||||
let destCol = col * scale;
|
||||
scaled.data.set(subLine, (destRow * scaled.width + destCol) * 4)
|
||||
}
|
||||
}
|
||||
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 scaled;
|
||||
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);
|
||||
}
|
||||
Reference in New Issue
Block a user