Skip to content

Commit

Permalink
refactor JS code, rewrite to TypeScript, contributed by @HerrZatacke
Browse files Browse the repository at this point in the history
  • Loading branch information
untoxa committed Dec 23, 2024
1 parent d86f213 commit 184b40f
Show file tree
Hide file tree
Showing 19 changed files with 2,086 additions and 11,817 deletions.
2 changes: 0 additions & 2 deletions fs/ac8d/remote.js

This file was deleted.

13 changes: 0 additions & 13 deletions fs/ac8d/remote.js.l.txt

This file was deleted.

18 changes: 18 additions & 0 deletions fs/consts.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Api URLs
export const OPTIONS = "/options";
export const DOWNLOAD = "/download";
export const RESET = "/reset";
export const RESET_USB_BOOT = "/reset_usb_boot";
export const STATUS_FILE = "/status.json";
export const LIST_FILE = "/list.json";
// Printer Commands
export const COMMAND_INIT = 0x01;
export const COMMAND_PRINT = 0x02;
export const COMMAND_DATA = 0x04;
export const COMMAND_TRANSFER = 0x10;
// Image specific
export const PRINTER_WIDTH = 20;
export const CAMERA_WIDTH = 16;
export const TILE_SIZE = 0x10;
export const TILE_HEIGHT = 8;
export const TILE_WIDTH = 8;
8 changes: 0 additions & 8 deletions fs/env.json

This file was deleted.

36 changes: 36 additions & 0 deletions fs/functions/appendCanvasToGallery.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { updateButtons } from "./updateButtons.js";
import { downloadImage } from "./saveImage.js";
const gallery = document.getElementById("gallery");
export const appendCanvasToGallery = (canvas, timestamp) => {
if (canvas.height > 1) {
const imageContainer = document.createElement("div");
imageContainer.classList.add("gallery-image");
const img = new Image();
img.src = canvas.toDataURL();
imageContainer.appendChild(img);
imageContainer.appendChild(document.createElement("br"));
if (timestamp) {
imageContainer.dataset.timestamp = timestamp.toString(10);
}
const input = document.createElement("input");
input.setAttribute("type", "checkbox");
input.addEventListener("change", function () {
if (input.checked) {
imageContainer.classList.add('marked-for-action');
}
else {
imageContainer.classList.remove('marked-for-action');
}
updateButtons();
});
imageContainer.appendChild(input);
const btn = document.createElement("button");
btn.textContent = "Save";
btn.addEventListener("click", function () {
downloadImage(img);
});
imageContainer.appendChild(btn);
gallery.appendChild(imageContainer);
updateButtons();
}
};
49 changes: 49 additions & 0 deletions fs/functions/database.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
export const initDb = async () => {
const dbName = "pico_printer";
return new Promise((resolve) => {
const request = indexedDB.open(dbName, 2);
request.onerror = (event) => {
console.error(event);
};
request.onsuccess = () => {
resolve({
add: async (dlData) => {
return new Promise((resolve) => {
const objectStore = request.result.transaction('downloads', 'readwrite').objectStore('downloads');
const rq = objectStore.add({
timestamp: dlData.timestamp,
data: dlData.data.join(',')
});
rq.onsuccess = () => resolve();
});
},
getAll: async () => {
return new Promise((resolve) => {
const objectStore = request.result.transaction('downloads').objectStore('downloads');
const rq = objectStore.getAll();
rq.onsuccess = (ev) => {
// @ts-ignore
const stored = ev.target?.result.map(({ timestamp, data }) => ({
timestamp,
data: new Uint8Array(data.split(',').map((i) => parseInt(i, 10))),
}));
resolve(stored);
};
});
},
delete: async (timestamp) => {
return new Promise((resolve) => {
const objectStore = request.result.transaction('downloads', 'readwrite').objectStore('downloads');
const rq = objectStore.delete(timestamp);
rq.onsuccess = () => resolve();
});
}
});
};
request.onupgradeneeded = () => {
console.log('onupgradeneeded');
const objectStore = request.result.createObjectStore("downloads", { keyPath: "timestamp" });
objectStore.createIndex("timestamp", "timestamp", { unique: false });
};
});
};
29 changes: 29 additions & 0 deletions fs/functions/decode.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
export const decode = (is_compressed, source, source_size, source_data_len, source_ptr, dest, dest_ptr) => {
if (source_ptr + source_data_len <= source_size) {
if (is_compressed) {
const stop = source_ptr + source_data_len;
while (source_ptr < stop) {
const tag = source[source_ptr++];
if (tag & 0x80) {
const data = source[source_ptr++];
for (let i = 0; i < ((tag & 0x7f) + 2); i++) {
dest[dest_ptr++] = data;
}
}
else {
for (let i = 0; i < (tag + 1); i++) {
dest[dest_ptr++] = source[source_ptr++];
}
}
}
return dest_ptr;
}
else {
for (let i = 0; i < source_data_len; i++) {
dest[dest_ptr++] = source[source_ptr++];
}
return dest_ptr;
}
}
return dest_ptr;
};
64 changes: 64 additions & 0 deletions fs/functions/getCameraImage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { CAMERA_WIDTH, COMMAND_DATA, COMMAND_INIT, COMMAND_PRINT, COMMAND_TRANSFER, PRINTER_WIDTH } from "../consts.js";
import { appendCanvasToGallery } from "./appendCanvasToGallery.js";
import { resetCanvas } from "./resetCanvas.js";
import { decode } from "./decode.js";
import { render } from "./render.js";
export async function getCameraImage(canvas, dlData) {
const resData = dlData.data;
const data_size = resData.byteLength;
const processed_data = new Uint8Array(Math.max(1024 * 1024, data_size));
resetCanvas(canvas);
let buffer_start = 0;
let ptr = 0;
let idx = 0;
let len = 0;
while (idx < data_size) {
const command = resData[idx++];
switch (command) {
case COMMAND_INIT:
break;
case COMMAND_PRINT: {
if ((len = resData[idx++] | (resData[idx++] << 8)) != 4) {
idx = data_size;
break;
}
let sheets = resData[idx++];
let margins = resData[idx++];
let palette = resData[idx++];
let exposure = Math.min(0xFF, 0x80 + resData[idx++]);
palette = (palette) ? palette : 0xE4;
if (render(canvas, processed_data, buffer_start, ptr, PRINTER_WIDTH, sheets, margins, palette, exposure)) {
appendCanvasToGallery(canvas, dlData.timestamp);
resetCanvas(canvas);
}
buffer_start = ptr;
break;
}
case COMMAND_TRANSFER: {
len = resData[idx++] | (resData[idx++] << 8);
let current_image_start = ptr;
ptr = decode(false, resData, data_size, len, idx, processed_data, ptr);
idx += len;
render(canvas, processed_data, current_image_start, ptr, CAMERA_WIDTH, 1, 0x03, 0xE4, 0xFF);
appendCanvasToGallery(canvas, dlData.timestamp);
resetCanvas(canvas);
buffer_start = ptr;
break;
}
case COMMAND_DATA: {
const compression = !!resData[idx++];
len = resData[idx++] | (resData[idx++] << 8);
ptr = decode(compression, resData, data_size, len, idx, processed_data, ptr);
idx += len;
break;
}
default:
idx = data_size;
break;
}
}
if (canvas.height > 1) {
appendCanvasToGallery(canvas, dlData.timestamp);
resetCanvas(canvas);
}
}
99 changes: 99 additions & 0 deletions fs/functions/initButtons.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { updateButtons } from "./updateButtons.js";
import { appendCanvasToGallery } from "./appendCanvasToGallery.js";
const gallery = document.getElementById("gallery");
// const getImageBtn = document.getElementById("get_image_btn") as HTMLButtonElement;
// const tearBtn = document.getElementById("tear_btn") as HTMLButtonElement;
const deleteSelectedBtn = document.getElementById("delete_selected_btn");
const selectAllBtn = document.getElementById("select_all_btn");
const averageSelectedBtn = document.getElementById("average_selected_btn");
export const initButtons = (store) => {
selectAllBtn.addEventListener("click", function () {
var items = gallery.children;
if (items.length != 0) {
[...items].forEach(item => {
const checkbox = item.querySelector("input");
checkbox.checked = true;
item.classList.add('marked-for-action');
});
}
updateButtons();
});
deleteSelectedBtn.addEventListener("click", function () {
const items = gallery.children;
for (let i = items.length - 1; i >= 0; i--) {
const item = items[i];
if (item.classList.contains('marked-for-action')) {
const imageTime = item.dataset.timestamp;
if (imageTime) {
store.delete(parseInt(imageTime, 10));
}
item.remove();
}
}
updateButtons();
});
averageSelectedBtn.addEventListener("click", function () {
const items = gallery.children;
const avgCanvas = document.createElement('canvas');
const avgCtx = avgCanvas.getContext('2d', { willReadFrequently: true });
const tmpCanvas = document.createElement('canvas');
const tmpCtx = tmpCanvas.getContext('2d', { willReadFrequently: true });
// Verify that image dimensions are the same
const firstImg = items[0].querySelector("img");
if (!firstImg)
return;
const tmpW = firstImg.width;
const tmpH = firstImg.height;
for (let i = 1; i < items.length; i++) {
const img = items[i].querySelector("img");
if (!img)
return;
if (tmpW != img.width || tmpH != img.height) {
alert("Image dimensions should be the same to do an average");
return;
}
}
tmpCanvas.width = tmpW;
tmpCanvas.height = tmpH;
avgCanvas.width = tmpW;
avgCanvas.height = tmpH;
const sumImgData = [];
const avgImgData = avgCtx.createImageData(avgCanvas.width, avgCanvas.height);
let selectedItems = 0;
// Generate average image
for (let i = items.length - 1; i >= 0; i--) {
if (items[i].classList.contains('marked-for-action')) {
selectedItems++;
const item = items[i];
const img = item.querySelector("img");
if (!img)
return;
tmpCtx.drawImage(img, 0, 0);
const tmpImgData = tmpCtx.getImageData(0, 0, tmpCanvas.width, tmpCanvas.height);
for (let j = 0; j < tmpImgData.data.length; j += 1) {
if (!sumImgData[j]) {
sumImgData.push(0);
}
sumImgData[j] += tmpImgData.data[j];
}
}
}
for (let i = 0; i < avgImgData.data.length; i += 1) {
avgImgData.data[i] = (sumImgData[i] / selectedItems);
}
avgCtx.putImageData(avgImgData, 0, 0);
appendCanvasToGallery(avgCanvas);
});
// tearBtn.addEventListener("click", async function () {
// fetch(resetPath)
// .then((response) => {
// return response.json();
// })
// .then((data) => {
// console.log(data);
// if (data.result != "ok") return;
// getImageBtn.click();
// });
// });
updateButtons();
};
36 changes: 36 additions & 0 deletions fs/functions/render.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { TILE_HEIGHT, TILE_SIZE } from "../consts.js";
import { resizeCanvas } from "./resizeCanvas.js";
export const render = (canvas, image_data, image_start, image_end, image_tile_width, sheets, margin, palette, exposure) => {
const pal = new Uint8Array(4);
pal[0] = ((exposure * ((palette >> 0) & 0x03)) / 3) >> 0;
pal[1] = ((exposure * ((palette >> 2) & 0x03)) / 3) >> 0;
pal[2] = ((exposure * ((palette >> 4) & 0x03)) / 3) >> 0;
pal[3] = ((exposure * ((palette >> 6) & 0x03)) / 3) >> 0;
let tile_y = ((canvas.height / TILE_HEIGHT) >> 0);
let tile_x = 0;
resizeCanvas(canvas, (image_tile_width * 8), ((canvas.height >> 3) << 3) + ((Math.max(0, image_end - image_start) / (TILE_SIZE * image_tile_width)) >> 0) * 8);
if (canvas.width * canvas.height !== 0) {
const ctx = canvas.getContext("2d", { willReadFrequently: true });
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const writeData = imageData.data;
for (let i = image_start; i < image_end;) {
for (let t = 0; t < 8; t++) {
let b1 = image_data[i++];
let b2 = image_data[i++];
for (let b = 0; b < 8; b++) {
let offset = (((tile_y << 3) + t) * canvas.width + (tile_x << 3) + b) << 2;
let color_index = ((b1 >> (7 - b)) & 1) | (((b2 >> (7 - b)) & 1) << 1);
writeData[offset] = writeData[offset + 1] = writeData[offset + 2] = 0xFF - pal[color_index];
writeData[offset + 3] = 0xff;
}
}
tile_x += 1;
if (tile_x >= image_tile_width) {
tile_x = 0;
tile_y++;
}
}
ctx.putImageData(imageData, 0, 0);
}
return ((margin & 0x0f) != 0);
};
4 changes: 4 additions & 0 deletions fs/functions/resetCanvas.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export const resetCanvas = (canvas) => {
canvas.height = 1;
canvas.width = 1;
};
7 changes: 7 additions & 0 deletions fs/functions/resizeCanvas.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export const resizeCanvas = (canvas, new_w, new_h) => {
const ctx = canvas.getContext("2d", { willReadFrequently: true });
let temp = ctx.getImageData(0, 0, canvas.width, canvas.height);
canvas.width = new_w;
canvas.height = new_h;
ctx.putImageData(temp, 0, 0);
};
Loading

0 comments on commit 184b40f

Please sign in to comment.