<template> <div class="container"> <Breadcrumb :items="[$t('menu.list'), $t('menu.image')]" /> <a-row :gutter="20" align="stretch"> <a-col :span="24"> <a-spin :loading="state.loading" tip="写入中..." style="width: 100%;"> <a-card class="general-card" :title="$t('menu.image') + $t('global.onStart')"> <div id="canvasDiv" style="zoom: 250%; display: none"></div> <div> <table style="padding: 0; margin: 0; border-spacing: 0"> <tr v-for="col, y in state.matrix"> <td @mousedown="state.mousedown = true; changePixel(x, y)" @mouseup="state.mousedown = false;" @mouseover="changePixel(x, y)" v-for="row, x in col" :style="'background-color: ' + row + '; height: 5px; width: 3.5px;'"></td> </tr> </table> </div> <br> 色彩阈值:<t-slider v-model="state.threshold" :max="256" style="width: 200px;" @change-end="changeThreshold" /> <br> <a-space> <a-button @click="selectFile">{{ $t('tool.selectImage') }}</a-button> <a-button :disabled="state.matrix.length < 64" @click="negativeIt">{{ $t('image.negative') }}</a-button> <a-button :disabled="state.matrix.length < 64" @click="saveIt">{{ $t('cps.save') }}</a-button> <a-button type="primary" :disabled="state.matrix.length < 64" @click="flashIt">{{ $t('tool.write') }}</a-button> </a-space> </a-card> </a-spin> </a-col> </a-row> </div> </template> <script lang="ts" setup> import { reactive, onMounted } from 'vue'; import { useRoute } from 'vue-router'; import { useAppStore } from '@/store'; import { eeprom_write, eeprom_reboot, eeprom_init } from '@/utils/serial.js'; const appStore = useAppStore(); const state : { binaryFile: any, loading: boolean, matrix: any, mousedown: boolean, threshold: number, cache: any } = reactive({ binaryFile: undefined, loading: false, matrix: [], mousedown: false, threshold: 128, cache: undefined }) const route = useRoute(); onMounted(async ()=>{ if(route.query.url){ const img = await fetch(route.query.url, { responseType: 'blob' }); useImg(window.URL.createObjectURL(await img.blob())) } }) const negativeIt = () => { const matrix = state.matrix matrix.map((y: any, yi: any)=>{ y.map((x: any, xi: any)=>{ matrix[yi][xi] = x == '#fff' ? '#000' : '#fff' }) }) state.matrix = matrix } const changePixel = (x: int, y: int) => { if(state.mousedown){ const matrix = state.matrix matrix[y][x] = state.matrix[y][x] == '#fff' ? '#000' : '#fff' state.matrix = matrix } } const useImg = (url: string) => { const canvas = document.createElement("canvas"); canvas.width = 128; canvas.height = 64; const canvas2 = canvas.cloneNode(); const canvasDiv = document.getElementById('canvasDiv'); canvasDiv.innerHTML = ""; canvasDiv?.append(canvas, canvas2); const img = new Image() img.src = url; img.onload = () => { const ctx = canvas.getContext('2d'); ctx?.drawImage(img, 0, 0, 128, 64); const imageData = ctx?.getImageData(0, 0, canvas.width, canvas.height).data; state.cache = imageData; function getPixel(x: any, y: any) { const index = y * 128 + x; const i = index * 4; return imageData[i] + imageData[i + 1] + imageData[i + 2] > state.threshold * 3 ? 0 : 1; } const matrix = []; for (let y = 0; y < 64; y++) { matrix.push([]) matrix[y] = [] for (let x = 0; x < 128; x++) { const pixel = !getPixel(x, y); matrix[y][x] = pixel ? '#fff' : '#000'; } } state.matrix = matrix } } const selectFile = () => { const input = document.createElement('input'); input.type = 'file'; input.onchange = async () => { const blob = new Blob([input.files[0]], { type: 'application/octet-stream' }); const file = URL.createObjectURL(blob); const canvas = document.createElement("canvas"); canvas.width = 128; canvas.height = 64; const img = new Image() img.src = file; img.onload = () => { const ctx = canvas.getContext('2d'); ctx?.drawImage(img, 0, 0, 128, 64); const imageData = ctx?.getImageData(0, 0, canvas.width, canvas.height).data; state.cache = imageData; function getPixel(x: any, y: any) { const index = y * 128 + x; const i = index * 4; return imageData[i] + imageData[i + 1] + imageData[i + 2] > state.threshold * 3 ? 0 : 1; } const matrix = []; for (let y = 0; y < 64; y++) { matrix.push([]) matrix[y] = [] for (let x = 0; x < 128; x++) { const pixel = !getPixel(x, y); matrix[y][x] = pixel ? '#fff' : '#000'; } } state.matrix = matrix } }; input.click(); } const saveIt = async () => { const matrix = state.matrix const canvas = document.createElement("canvas"); canvas.width = 128; canvas.height = 64; const ctx = canvas.getContext('2d'); if(ctx){ ctx.fillStyle = "#fff"; ctx.fillRect(0, 0, canvas.width, canvas.height); ctx.fillStyle = "#000"; } for (let y = 0; y < 64; y++) { for (let x = 0; x < 128; x++) { if(matrix[y][x] == '#000'){ ctx?.beginPath(); ctx?.rect(x, y, 1, 1); ctx?.fill(); } } } const el = document.createElement('a'); el.href = canvas.toDataURL("image/jpeg", 1.0); el.download = 'image.jpg'; el.click() } const flashIt = async () => { const outputArray = new Uint8Array(1024); // getPixel(i) outputs the pixel value for any x y coordinate. 0 = black, 1 = white. // the outputArray is 1024 bytes, where each byte is 8 pixels IN VERTICAL ORDER. let i = 0; for (let y = 0; y < 64; y += 8) { for (let x = 0; x < 128; x++) { let byte = 0; for (let i = 0; i < 8; i++) { byte |= (state.matrix[y + i][x] == '#000' ? 1 : 0 ) << i; } outputArray[i++] = byte; } } state.binaryFile = outputArray; if(appStore.connectState != true){alert(sessionStorage.getItem('noticeConnectK5')); return;}; if(appStore.configuration?.uart == "official"){ alert(sessionStorage.getItem('noticeVersionNoSupport')); return; } if(appStore.configuration?.charset != "losehu" && appStore.configuration?.charset != "gb2312"){ alert(sessionStorage.getItem('noticeVersionNoSupport')); return; } state.loading = true let position = 0x1E350; if(appStore.configuration?.charset == "gb2312")position = 0x2080; await eeprom_init(appStore.connectPort); const rawEEPROM = state.binaryFile; for (let i = position; i < rawEEPROM.length + position; i += 0x80) { await eeprom_write(appStore.connectPort, i, rawEEPROM.slice(i - position, i - position + 0x80), rawEEPROM.slice(i - position, i - position + 0x80).length, appStore.configuration?.uart); } await eeprom_reboot(appStore.connectPort); state.loading = false } const changeThreshold = () => { const imageData = state.cache; function getPixel(x: any, y: any) { const index = y * 128 + x; const i = index * 4; return imageData[i] + imageData[i + 1] + imageData[i + 2] > state.threshold * 3 ? 0 : 1; } const matrix = []; for (let y = 0; y < 64; y++) { matrix.push([]) matrix[y] = [] for (let x = 0; x < 128; x++) { const pixel = !getPixel(x, y); matrix[y][x] = pixel ? '#fff' : '#000'; } } state.matrix = matrix } </script> <script lang="ts"> export default { name: 'Image', }; </script> <style scoped lang="less"> .container { padding: 0 20px 20px 20px; :deep(.arco-list-content) { overflow-x: hidden; } :deep(.arco-card-meta-title) { font-size: 14px; } } :deep(.arco-list-col) { display: flex; flex-direction: row; flex-wrap: wrap; justify-content: space-between; } :deep(.arco-list-item) { width: 33%; } :deep(.block-title) { margin: 0 0 12px 0; font-size: 14px; } :deep(.list-wrap) { // min-height: 140px; .list-row { align-items: stretch; .list-col { margin-bottom: 16px; } } :deep(.arco-space) { width: 100%; .arco-space-item { &:last-child { flex: 1; } } } } </style>