Fix problems highlighted by eslint

This commit is contained in:
Michael Smith 2023-05-26 10:57:58 +02:00
parent f7a90d5433
commit 8375261eb1
4 changed files with 128 additions and 140 deletions

175
main.js
View File

@ -24,33 +24,14 @@
*/ */
import PBM from "./src/pbm.js"; import PBM from './src/pbm.js';
const thumbnailCanvas = document.getElementById("thumbnail-canvas"); const thumbnailCanvas = document.getElementById('thumbnail-canvas');
const thumbnailContext = thumbnailCanvas.getContext("2d"); const thumbnailContext = thumbnailCanvas.getContext('2d');
const imageCanvas = document.getElementById("image-canvas"); const imageCanvas = document.getElementById('image-canvas');
const imageContext = imageCanvas.getContext("2d"); const imageContext = imageCanvas.getContext('2d');
const paletteCanvas = document.getElementById("palette-canvas"); const paletteCanvas = document.getElementById('palette-canvas');
const paletteContext = paletteCanvas.getContext("2d"); const paletteContext = paletteCanvas.getContext('2d');
const inputElement = document.getElementById("imagefile");
inputElement.addEventListener("change", handleFile, false);
document.getElementById("paletteLeft").addEventListener("click", paletteLeft);
document.getElementById("paletteRight").addEventListener("click", paletteRight);
document.getElementById("cycleColors").addEventListener("click", () => {
if (running) {
running = false;
} else {
running = true;
animate();
}
});
const palettePageLabelEl = document.getElementById("palettePageLabel");
const cyclingSpeedLabel = document.getElementById("cyclingSpeedLabel");
cyclingSpeedLabel.innerText = cyclingSpeedSlider.value;
let currentPalettePage = 0; let currentPalettePage = 0;
let image = null; let image = null;
@ -58,38 +39,6 @@ let running = false;
let cycleSpeed = 15.0; let cycleSpeed = 15.0;
document
.getElementById("cyclingSpeedSlider")
.addEventListener("input", (evt) => {
cycleSpeed = evt.target.value;
cyclingSpeedLabel.innerText = cycleSpeed;
});
fetch("/assets/TEST.LBM")
.then((response) => {
return response.arrayBuffer();
})
.then((buffer) => {
image = loadImage(buffer);
drawPalette(image.palette, currentPalettePage, paletteContext);
drawImage(image.thumbnail, thumbnailContext);
drawImage(image, imageContext);
});
function handleFile() {
const imageFile = this.files[0];
const reader = new FileReader();
reader.onload = (evt) => {
image = loadImage(evt.target.result);
drawPalette(image.palette, currentPalettePage, paletteContext);
drawImage(image.thumbnail, thumbnailContext);
drawImage(image, imageContext);
};
reader.readAsArrayBuffer(imageFile);
}
function loadImage(buffer) { function loadImage(buffer) {
image = new PBM(buffer); image = new PBM(buffer);
thumbnailCanvas.width = image.thumbnail.width; thumbnailCanvas.width = image.thumbnail.width;
@ -100,26 +49,6 @@ function loadImage(buffer) {
return image; return image;
} }
function paletteLeft(evt) {
if (currentPalettePage === 0) {
currentPalettePage = 3;
} else {
currentPalettePage--;
}
palettePageLabelEl.innerText = currentPalettePage + 1;
drawPalette();
}
function paletteRight(evt) {
if (currentPalettePage === 3) {
currentPalettePage = 0;
} else {
currentPalettePage++;
}
palettePageLabelEl.innerText = currentPalettePage + 1;
drawPalette();
}
function drawPalette() { function drawPalette() {
const colorSize = 20; // in pixels const colorSize = 20; // in pixels
const width = 4 * colorSize; // 4 columns const width = 4 * colorSize; // 4 columns
@ -142,19 +71,19 @@ function drawPalette() {
} }
} }
function drawImage(image, ctx) { function drawImage(anImage, ctx) {
ctx.clearRect(0, 0, image.width, image.height); ctx.clearRect(0, 0, anImage.width, anImage.height);
let pixels = ctx.createImageData(image.width, image.height); const pixels = ctx.createImageData(anImage.width, anImage.height);
for (let x = 0; x < image.width; x++) { for (let x = 0; x < anImage.width; x++) {
for (let y = 0; y < image.height; y++) { for (let y = 0; y < anImage.height; y++) {
const index = y * image.width + x; const index = y * anImage.width + x;
const paletteIndex = image.pixelData[index]; const paletteIndex = anImage.pixelData[index];
const pixelIndex = index * 4; const pixelIndex = index * 4;
const r = image.palette[paletteIndex][0]; const r = anImage.palette[paletteIndex][0];
const g = image.palette[paletteIndex][1]; const g = anImage.palette[paletteIndex][1];
const b = image.palette[paletteIndex][2]; const b = anImage.palette[paletteIndex][2];
pixels.data[pixelIndex] = r; pixels.data[pixelIndex] = r;
pixels.data[pixelIndex + 1] = g; pixels.data[pixelIndex + 1] = g;
@ -166,17 +95,53 @@ function drawImage(image, ctx) {
ctx.putImageData(pixels, 0, 0); ctx.putImageData(pixels, 0, 0);
} }
function handleFile() {
const imageFile = this.files[0];
const reader = new FileReader();
reader.onload = (evt) => {
image = loadImage(evt.target.result);
drawPalette(image.palette, currentPalettePage, paletteContext);
drawImage(image.thumbnail, thumbnailContext);
drawImage(image, imageContext);
};
reader.readAsArrayBuffer(imageFile);
}
function paletteLeft() {
if (currentPalettePage === 0) {
currentPalettePage = 3;
} else {
currentPalettePage -= 1;
}
document.getElementById('palettePageLabel').innerText =
currentPalettePage + 1;
drawPalette();
}
function paletteRight() {
if (currentPalettePage === 3) {
currentPalettePage = 0;
} else {
currentPalettePage += 1;
}
document.getElementById('palettePageLabel').innerText =
currentPalettePage + 1;
drawPalette();
}
function cycleColors(now) { function cycleColors(now) {
image.cyclingRanges.forEach((range) => { image.cyclingRanges.forEach((range) => {
if (range.active) { if (range.active) {
if (!range.lastTime) range.lastTime = now; if (!range.lastTime) range.lastTime = now;
if (now - range.lastTime > range.rate / cycleSpeed) { if (now - range.lastTime > range.rate / cycleSpeed) {
if (range.direction === "forward") { if (range.direction === 'forward') {
// Move last color to first position // Move last color to first position
const lastColor = image.palette.splice(range.high, 1)[0]; const lastColor = image.palette.splice(range.high, 1)[0];
image.palette.splice(range.low, 0, lastColor); image.palette.splice(range.low, 0, lastColor);
} else if (range.direction === "reverse") { } else if (range.direction === 'reverse') {
// Move first color to last position // Move first color to last position
const firstColor = image.palette.splice(range.low, 1)[0]; const firstColor = image.palette.splice(range.low, 1)[0];
image.palette.splice(range.high, 0, firstColor); image.palette.splice(range.high, 0, firstColor);
@ -194,3 +159,33 @@ function animate(now) {
if (running) requestAnimationFrame(animate); if (running) requestAnimationFrame(animate);
} }
const inputElement = document.getElementById('imagefile');
inputElement.addEventListener('change', handleFile, false);
document.getElementById('paletteLeft').addEventListener('click', paletteLeft);
document.getElementById('paletteRight').addEventListener('click', paletteRight);
document.getElementById('cycleColors').addEventListener('click', () => {
if (running) {
running = false;
} else {
running = true;
animate();
}
});
document
.getElementById('cyclingSpeedSlider')
.addEventListener('input', (evt) => {
cycleSpeed = evt.target.value;
document.getElementById('cyclingSpeedLabel').innerText = cycleSpeed;
});
fetch('/assets/TEST.LBM')
.then((response) => response.arrayBuffer())
.then((buffer) => {
image = loadImage(buffer);
drawPalette(image.palette, currentPalettePage, paletteContext);
drawImage(image.thumbnail, thumbnailContext);
drawImage(image, imageContext);
});

View File

@ -46,7 +46,7 @@ class BinaryStream {
readByte() { readByte() {
const byte = this.dataView.getUint8(this.index); const byte = this.dataView.getUint8(this.index);
this.index++; this.index += 1;
return byte; return byte;
} }
@ -58,7 +58,7 @@ class BinaryStream {
} }
readString(length) { readString(length) {
let string = ""; let string = '';
for (let i = 0; i < length; i++) { for (let i = 0; i < length; i++) {
const byte = this.dataView.getUint8(this.index + i); const byte = this.dataView.getUint8(this.index + i);

View File

@ -24,7 +24,7 @@
*/ */
import BinaryStream from "./binarystream.js"; import BinaryStream from './binarystream.js';
class PBM { class PBM {
constructor(arrayBuffer) { constructor(arrayBuffer) {
@ -81,7 +81,7 @@ class PBM {
const formatId = this.binaryStream.readString(4); const formatId = this.binaryStream.readString(4);
// Validate chunk according to notes on https://en.wikipedia.org/wiki/ILBM // Validate chunk according to notes on https://en.wikipedia.org/wiki/ILBM
if (chunkId !== "FORM") { if (chunkId !== 'FORM') {
throw new Error( throw new Error(
`Invalid chunkId: "${chunkId}" at byte ${this.binaryStream.index}. Expected "FORM".` `Invalid chunkId: "${chunkId}" at byte ${this.binaryStream.index}. Expected "FORM".`
); );
@ -95,7 +95,7 @@ class PBM {
); );
} }
if (formatId !== "PBM ") { if (formatId !== 'PBM ') {
throw new Error(`Invalid formatId: "${formatId}". Expected "PBM ".`); throw new Error(`Invalid formatId: "${formatId}". Expected "PBM ".`);
} }
@ -105,23 +105,23 @@ class PBM {
chunkLength = this.binaryStream.readUint32BE(); chunkLength = this.binaryStream.readUint32BE();
switch (chunkId) { switch (chunkId) {
case "BMHD": case 'BMHD':
this.parseBMHD(); this.parseBMHD();
break; break;
case "CMAP": case 'CMAP':
this.parseCMAP(); this.parseCMAP();
break; break;
case "DPPS": case 'DPPS':
// NOTE(m): Ignore unknown DPPS chunk of size 110 bytes // NOTE(m): Ignore unknown DPPS chunk of size 110 bytes
this.binaryStream.jump(110); this.binaryStream.jump(110);
break; break;
case "CRNG": case 'CRNG':
this.parseCRNG(); this.parseCRNG();
break; break;
case "TINY": case 'TINY':
this.parseTINY(chunkLength); this.parseTINY(chunkLength);
break; break;
case "BODY": case 'BODY':
this.parseBODY(chunkLength); this.parseBODY(chunkLength);
break; break;
default: default:
@ -159,7 +159,7 @@ class PBM {
// TODO(m): Read 3 bytes at a time? // TODO(m): Read 3 bytes at a time?
for (let i = 0; i < numColors; i++) { for (let i = 0; i < numColors; i++) {
let rgb = []; const rgb = [];
for (let j = 0; j < 3; j++) { for (let j = 0; j < 3; j++) {
rgb.push(this.binaryStream.readByte()); rgb.push(this.binaryStream.readByte());
} }
@ -189,11 +189,11 @@ class PBM {
const directionBitMask = 1 << 1; const directionBitMask = 1 << 1;
this.cyclingRanges.push({ this.cyclingRanges.push({
rate: rate, rate,
active: (flags & activeBitMask) !== 0, active: (flags & activeBitMask) !== 0,
direction: (flags & directionBitMask) !== 0 ? "reverse" : "forward", direction: (flags & directionBitMask) !== 0 ? 'reverse' : 'forward',
low: low, low,
high: high, high,
}); });
} }
@ -226,7 +226,7 @@ class PBM {
} }
decompress(endOfChunkIndex) { decompress(endOfChunkIndex) {
let result = []; const result = [];
while (this.binaryStream.index < endOfChunkIndex) { while (this.binaryStream.index < endOfChunkIndex) {
const byte = this.binaryStream.readByte(); const byte = this.binaryStream.readByte();
@ -251,7 +251,7 @@ class PBM {
// TODO(m): Read a range of bytes straight into an array? // TODO(m): Read a range of bytes straight into an array?
// Use arrayBuffers throughout instead? // Use arrayBuffers throughout instead?
readUncompressed(endOfChunkIndex) { readUncompressed(endOfChunkIndex) {
let result = []; const result = [];
while (this.binaryStream.index < endOfChunkIndex) { while (this.binaryStream.index < endOfChunkIndex) {
const byte = this.binaryStream.readByte(); const byte = this.binaryStream.readByte();

View File

@ -1,3 +1,4 @@
/* eslint-disable no-new */
/* /*
MIT License MIT License
@ -24,49 +25,46 @@
*/ */
import { expect, test } from "vitest"; import { expect, test } from 'vitest';
import PBM from "../src/pbm.js"; import PBM from '../src/pbm.js';
test("Successfully parse a PBM file", () => { const fs = require('fs');
const fs = require("fs");
const data = fs.readFileSync("./tests/fixtures/VALID.LBM"); test('Successfully parse a PBM file', () => {
const data = fs.readFileSync('./tests/fixtures/VALID.LBM');
expect(() => { expect(() => {
new PBM(data.buffer); new PBM(data.buffer);
}).not.toThrowError(); }).not.toThrowError();
}); });
test("Fail to parse a PBM file with an invalid chunk id", () => { test('Fail to parse a PBM file with an invalid chunk id', () => {
const fs = require("fs"); const data = fs.readFileSync('./tests/fixtures/INVALID_CHUNK_ID.LBM');
const data = fs.readFileSync("./tests/fixtures/INVALID_CHUNK_ID.LBM");
expect(() => { expect(() => {
new PBM(data.buffer); new PBM(data.buffer);
}).toThrowError(/^Invalid chunkId: "FARM" at byte 12. Expected "FORM".$/); }).toThrowError(/^Invalid chunkId: "FARM" at byte 12. Expected "FORM".$/);
}); });
test("Fail to parse a PBM file with an invalid chunk length", () => { test('Fail to parse a PBM file with an invalid chunk length', () => {
const fs = require("fs"); const data = fs.readFileSync('./tests/fixtures/INVALID_CHUNK_LENGTH.LBM');
const data = fs.readFileSync("./tests/fixtures/INVALID_CHUNK_LENGTH.LBM");
expect(() => { expect(() => {
new PBM(data.buffer); new PBM(data.buffer);
}).toThrowError(/^Invalid chunk length: 7070 bytes. Expected 7012 bytes.$/); }).toThrowError(/^Invalid chunk length: 7070 bytes. Expected 7012 bytes.$/);
}); });
test("Fail to parse an IFF file that is not a PBM file", () => { test('Fail to parse an IFF file that is not a PBM file', () => {
const fs = require("fs"); const data = fs.readFileSync('./tests/fixtures/SEASCAPE.LBM');
const data = fs.readFileSync("./tests/fixtures/SEASCAPE.LBM");
expect(() => { expect(() => {
new PBM(data.buffer); new PBM(data.buffer);
}).toThrowError(/^Invalid formatId: "ILBM". Expected "PBM ".$/); }).toThrowError(/^Invalid formatId: "ILBM". Expected "PBM ".$/);
}); });
test("Parse a PBM bitmap header", () => { test('Parse a PBM bitmap header', () => {
const fs = require("fs"); const data = fs.readFileSync('./tests/fixtures/VALID.LBM');
const data = fs.readFileSync("./tests/fixtures/VALID.LBM");
const image = new PBM(data.buffer); const image = new PBM(data.buffer);
expect(image.width).toStrictEqual(640); expect(image.width).toStrictEqual(640);
@ -84,26 +82,23 @@ test("Parse a PBM bitmap header", () => {
expect(image.pageHeight).toStrictEqual(480); expect(image.pageHeight).toStrictEqual(480);
}); });
test("Parse PBM palette information", () => { test('Parse PBM palette information', () => {
const fs = require("fs"); const data = fs.readFileSync('./tests/fixtures/VALID.LBM');
const data = fs.readFileSync("./tests/fixtures/VALID.LBM");
const image = new PBM(data.buffer); const image = new PBM(data.buffer);
expect(image.palette.length).toStrictEqual(256); expect(image.palette.length).toStrictEqual(256);
expect(image.palette[10]).toStrictEqual([87, 255, 87]); expect(image.palette[10]).toStrictEqual([87, 255, 87]);
}); });
test("Parse PBM color cycling information", () => { test('Parse PBM color cycling information', () => {
const fs = require("fs"); const data = fs.readFileSync('./tests/fixtures/VALID.LBM');
const data = fs.readFileSync("./tests/fixtures/VALID.LBM");
const image = new PBM(data.buffer); const image = new PBM(data.buffer);
expect(image.cyclingRanges.length).toStrictEqual(16); expect(image.cyclingRanges.length).toStrictEqual(16);
}); });
test("Parse PBM thumbnail", () => { test('Parse PBM thumbnail', () => {
const fs = require("fs"); const data = fs.readFileSync('./tests/fixtures/VALID.LBM');
const data = fs.readFileSync("./tests/fixtures/VALID.LBM");
const image = new PBM(data.buffer); const image = new PBM(data.buffer);
expect(image.thumbnail.width).toStrictEqual(80); expect(image.thumbnail.width).toStrictEqual(80);
@ -111,9 +106,8 @@ test("Parse PBM thumbnail", () => {
expect(image.thumbnail.size).toStrictEqual(4800); expect(image.thumbnail.size).toStrictEqual(4800);
}); });
test("Decode PBM thumbnail pixel data", () => { test('Decode PBM thumbnail pixel data', () => {
const fs = require("fs"); const data = fs.readFileSync('./tests/fixtures/VALID.LBM');
const data = fs.readFileSync("./tests/fixtures/VALID.LBM");
const image = new PBM(data.buffer); const image = new PBM(data.buffer);
expect(image.thumbnail.pixelData.length).toStrictEqual(4800); expect(image.thumbnail.pixelData.length).toStrictEqual(4800);
@ -122,9 +116,8 @@ test("Decode PBM thumbnail pixel data", () => {
expect(image.palette[14]).toStrictEqual([255, 255, 87]); expect(image.palette[14]).toStrictEqual([255, 255, 87]);
}); });
test("Decode PBM image pixel data", () => { test('Decode PBM image pixel data', () => {
const fs = require("fs"); const data = fs.readFileSync('./tests/fixtures/VALID.LBM');
const data = fs.readFileSync("./tests/fixtures/VALID.LBM");
const image = new PBM(data.buffer); const image = new PBM(data.buffer);
expect(image.pixelData.length).toStrictEqual(307_200); expect(image.pixelData.length).toStrictEqual(307_200);