Remove unnecessary wrapping of functionality in a class
This commit is contained in:
parent
bd68eb6940
commit
f937499e6f
@ -19,14 +19,14 @@ Try it out at [https://michaelshmitty.github.io/pbm-js/](https://michaelshmitty.
|
||||
_Also see `index.html` and `main.js` for a more elaborate example that renders the image and palette data to an html5 canvas and supports color cycling._
|
||||
|
||||
```javascript
|
||||
import PBM from "./src/pbm.js";
|
||||
import parsePBM from "./src/pbm.js";
|
||||
|
||||
fetch("/assets/TEST.LBM")
|
||||
.then((response) => {
|
||||
return response.arrayBuffer();
|
||||
})
|
||||
.then((buffer) => {
|
||||
const image = new PBM(buffer);
|
||||
const image = parsePBM(buffer);
|
||||
console.log(image);
|
||||
});
|
||||
```
|
||||
@ -36,10 +36,10 @@ fetch("/assets/TEST.LBM")
|
||||
```javascript
|
||||
import * as fs from "fs";
|
||||
|
||||
import PBM from "./src/pbm.js";
|
||||
import parsePBM from "./src/pbm.js";
|
||||
|
||||
const data = fs.readFileSync("./tests/fixtures/VALID.LBM");
|
||||
const image = new PBM(data.buffer);
|
||||
const image = parsePBM(data.buffer);
|
||||
console.log(image);
|
||||
```
|
||||
|
||||
|
||||
4
main.js
4
main.js
@ -24,7 +24,7 @@
|
||||
|
||||
*/
|
||||
|
||||
import PBM from "./src/pbm.js";
|
||||
import parsePBM from "./src/pbm.js";
|
||||
|
||||
const thumbnailCanvas = document.getElementById("thumbnail-canvas");
|
||||
const thumbnailContext = thumbnailCanvas.getContext("2d");
|
||||
@ -88,7 +88,7 @@ function drawImage(anImage, ctx) {
|
||||
|
||||
// Image loading
|
||||
function loadImage(buffer) {
|
||||
image = new PBM(buffer);
|
||||
image = parsePBM(buffer);
|
||||
thumbnailCanvas.width = image.thumbnail.width;
|
||||
thumbnailCanvas.height = image.thumbnail.height;
|
||||
imageCanvas.width = image.width;
|
||||
|
||||
434
src/pbm.js
434
src/pbm.js
@ -26,240 +26,212 @@
|
||||
|
||||
import BinaryStream from "./binarystream.js";
|
||||
|
||||
class PBM {
|
||||
constructor(arrayBuffer) {
|
||||
this.binaryStream = new BinaryStream(arrayBuffer);
|
||||
// Parse Bitmap Header chunk
|
||||
function parseBMHD(binaryStream, image) {
|
||||
image.width = binaryStream.readUint16BE();
|
||||
image.height = binaryStream.readUint16BE();
|
||||
image.size = image.width * image.height;
|
||||
image.xOrigin = binaryStream.readInt16BE();
|
||||
image.yOrigin = binaryStream.readInt16BE();
|
||||
image.numPlanes = binaryStream.readUint8();
|
||||
image.mask = binaryStream.readUint8();
|
||||
image.compression = binaryStream.readUint8();
|
||||
binaryStream.readUint8(); // Ignore pad1 field left "for future compatibility"
|
||||
image.transClr = binaryStream.readUint16BE();
|
||||
image.xAspect = binaryStream.readUint8();
|
||||
image.yAspect = binaryStream.readUint8();
|
||||
image.pageWidth = binaryStream.readInt16BE();
|
||||
image.pageHeight = binaryStream.readInt16BE();
|
||||
}
|
||||
|
||||
// Image properties taken from BMHD chunk
|
||||
this.width = null;
|
||||
this.height = null;
|
||||
this.size = null;
|
||||
this.xOrigin = null;
|
||||
this.yOrigin = null;
|
||||
this.numPlanes = null;
|
||||
this.mask = null;
|
||||
this.compression = null;
|
||||
this.transClr = null;
|
||||
this.xAspect = null;
|
||||
this.yAspect = null;
|
||||
this.pageWidth = null;
|
||||
this.pageHeight = null;
|
||||
// Parse Palette chunk
|
||||
function parseCMAP(binaryStream, image) {
|
||||
const numColors = 2 ** image.numPlanes;
|
||||
image.palette = [];
|
||||
|
||||
// Palette information taken from CMAP chunk
|
||||
this.palette = [];
|
||||
|
||||
// Color cycling information taken from CRNG chunk
|
||||
this.cyclingRanges = [];
|
||||
|
||||
// Thumbnail information taken from TINY chunk
|
||||
this.thumbnail = {
|
||||
width: null,
|
||||
height: null,
|
||||
size: null,
|
||||
palette: this.palette,
|
||||
pixelData: [],
|
||||
};
|
||||
|
||||
// Uncompressed pixel data referencing palette colors
|
||||
this.pixelData = [];
|
||||
|
||||
try {
|
||||
this.parseFORM();
|
||||
} catch (error) {
|
||||
if (error instanceof RangeError) {
|
||||
throw new Error(`Failed to parse file.`);
|
||||
} else {
|
||||
throw error; // re-throw the error unchanged
|
||||
}
|
||||
// TODO(m): Read 3 bytes at a time?
|
||||
for (let i = 0; i < numColors; i++) {
|
||||
const rgb = [];
|
||||
for (let j = 0; j < 3; j++) {
|
||||
rgb.push(binaryStream.readByte());
|
||||
}
|
||||
}
|
||||
|
||||
parseFORM() {
|
||||
// Parse "FORM" chunk
|
||||
let chunkId = this.binaryStream.readString(4);
|
||||
let chunkLength = this.binaryStream.readUint32BE();
|
||||
const formatId = this.binaryStream.readString(4);
|
||||
|
||||
// Validate chunk according to notes on https://en.wikipedia.org/wiki/ILBM
|
||||
if (chunkId !== "FORM") {
|
||||
throw new Error(
|
||||
`Invalid chunkId: "${chunkId}" at byte ${this.binaryStream.index}. Expected "FORM".`
|
||||
);
|
||||
}
|
||||
|
||||
if (chunkLength !== this.binaryStream.length - 8) {
|
||||
throw new Error(
|
||||
`Invalid chunk length: ${chunkLength} bytes. Expected ${
|
||||
this.binaryStream.length - 8
|
||||
} bytes.`
|
||||
);
|
||||
}
|
||||
|
||||
if (formatId !== "PBM ") {
|
||||
throw new Error(`Invalid formatId: "${formatId}". Expected "PBM ".`);
|
||||
}
|
||||
|
||||
// Parse all other chunks
|
||||
while (!this.binaryStream.EOF()) {
|
||||
chunkId = this.binaryStream.readString(4);
|
||||
chunkLength = this.binaryStream.readUint32BE();
|
||||
|
||||
switch (chunkId) {
|
||||
case "BMHD":
|
||||
this.parseBMHD();
|
||||
break;
|
||||
case "CMAP":
|
||||
this.parseCMAP();
|
||||
break;
|
||||
case "DPPS":
|
||||
// NOTE(m): Ignore unknown DPPS chunk of size 110 bytes
|
||||
this.binaryStream.jump(110);
|
||||
break;
|
||||
case "CRNG":
|
||||
this.parseCRNG();
|
||||
break;
|
||||
case "TINY":
|
||||
this.parseTINY(chunkLength);
|
||||
break;
|
||||
case "BODY":
|
||||
this.parseBODY(chunkLength);
|
||||
break;
|
||||
default:
|
||||
throw new Error(
|
||||
`Unsupported chunkId: ${chunkId} at byte ${this.binaryStream.index}`
|
||||
);
|
||||
}
|
||||
|
||||
// Skip chunk padding byte when chunkLength is not a multiple of 2
|
||||
if (chunkLength % 2 === 1) this.binaryStream.jump(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Parse Bitmap Header chunk
|
||||
parseBMHD() {
|
||||
this.width = this.binaryStream.readUint16BE();
|
||||
this.height = this.binaryStream.readUint16BE();
|
||||
this.size = this.width * this.height;
|
||||
this.xOrigin = this.binaryStream.readInt16BE();
|
||||
this.yOrigin = this.binaryStream.readInt16BE();
|
||||
this.numPlanes = this.binaryStream.readUint8();
|
||||
this.mask = this.binaryStream.readUint8();
|
||||
this.compression = this.binaryStream.readUint8();
|
||||
this.binaryStream.readUint8(); // Ignore pad1 field left "for future compatibility"
|
||||
this.transClr = this.binaryStream.readUint16BE();
|
||||
this.xAspect = this.binaryStream.readUint8();
|
||||
this.yAspect = this.binaryStream.readUint8();
|
||||
this.pageWidth = this.binaryStream.readInt16BE();
|
||||
this.pageHeight = this.binaryStream.readInt16BE();
|
||||
}
|
||||
|
||||
// Parse Palette chunk
|
||||
parseCMAP() {
|
||||
const numColors = 2 ** this.numPlanes;
|
||||
|
||||
// TODO(m): Read 3 bytes at a time?
|
||||
for (let i = 0; i < numColors; i++) {
|
||||
const rgb = [];
|
||||
for (let j = 0; j < 3; j++) {
|
||||
rgb.push(this.binaryStream.readByte());
|
||||
}
|
||||
this.palette.push(rgb);
|
||||
}
|
||||
}
|
||||
|
||||
// Parse Color range chunk
|
||||
parseCRNG() {
|
||||
this.binaryStream.jump(2); // 2 bytes padding according to https://en.wikipedia.org/wiki/ILBM#CRNG:_Colour_range
|
||||
|
||||
const rate = this.binaryStream.readInt16BE();
|
||||
const flags = this.binaryStream.readInt16BE();
|
||||
const low = this.binaryStream.readUint8();
|
||||
const high = this.binaryStream.readUint8();
|
||||
|
||||
// Parse flags according to https://en.wikipedia.org/wiki/ILBM#CRNG:_Colour_range
|
||||
// If bit 0 is 1, the color should cycle, otherwise this color register range is inactive
|
||||
// and should have no effect.
|
||||
//
|
||||
// If bit 1 is 0, the colors cycle upwards (forward), i.e. each color moves into the next
|
||||
// index position in the palette and the uppermost color in the range moves down to the
|
||||
// lowest position.
|
||||
// If bit 1 is 1, the colors cycle in the opposite direction (reverse).
|
||||
// Only those colors between the low and high entries in the palette should cycle.
|
||||
const activeBitMask = 1 << 0;
|
||||
const directionBitMask = 1 << 1;
|
||||
|
||||
this.cyclingRanges.push({
|
||||
rate,
|
||||
active: (flags & activeBitMask) !== 0,
|
||||
direction: (flags & directionBitMask) !== 0 ? "reverse" : "forward",
|
||||
low,
|
||||
high,
|
||||
});
|
||||
}
|
||||
|
||||
// Parse Thumbnail chunk
|
||||
parseTINY(chunkLength) {
|
||||
const endOfChunkIndex = this.binaryStream.index + chunkLength;
|
||||
|
||||
this.thumbnail.width = this.binaryStream.readUint16BE();
|
||||
this.thumbnail.height = this.binaryStream.readUint16BE();
|
||||
this.thumbnail.size = this.thumbnail.width * this.thumbnail.height;
|
||||
|
||||
// Decompress pixel data if necessary
|
||||
if (this.compression === 1) {
|
||||
this.thumbnail.pixelData = this.decompress(endOfChunkIndex);
|
||||
} else {
|
||||
this.thumbnail.pixelData = this.readUncompressed(endOfChunkIndex);
|
||||
}
|
||||
}
|
||||
|
||||
// Parse Image data chunk
|
||||
parseBODY(chunkLength) {
|
||||
const endOfChunkIndex = this.binaryStream.index + chunkLength;
|
||||
|
||||
// Decompress pixel data if necessary
|
||||
if (this.compression === 1) {
|
||||
this.pixelData = this.decompress(endOfChunkIndex);
|
||||
} else {
|
||||
this.pixelData = this.readUncompressed(endOfChunkIndex);
|
||||
}
|
||||
}
|
||||
|
||||
decompress(endOfChunkIndex) {
|
||||
const result = [];
|
||||
|
||||
while (this.binaryStream.index < endOfChunkIndex) {
|
||||
const byte = this.binaryStream.readByte();
|
||||
|
||||
if (byte > 128) {
|
||||
const nextByte = this.binaryStream.readByte();
|
||||
for (let i = 0; i < 257 - byte; i++) {
|
||||
result.push(nextByte);
|
||||
}
|
||||
} else if (byte < 128) {
|
||||
for (let i = 0; i < byte + 1; i++) {
|
||||
result.push(this.binaryStream.readByte());
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// TODO(m): Read a range of bytes straight into an array?
|
||||
// Use arrayBuffers throughout instead?
|
||||
readUncompressed(endOfChunkIndex) {
|
||||
const result = [];
|
||||
|
||||
while (this.binaryStream.index < endOfChunkIndex) {
|
||||
const byte = this.binaryStream.readByte();
|
||||
result.push(byte);
|
||||
}
|
||||
|
||||
return result;
|
||||
image.palette.push(rgb);
|
||||
}
|
||||
}
|
||||
|
||||
export default PBM;
|
||||
// Parse Color range chunk
|
||||
function parseCRNG(binaryStream) {
|
||||
binaryStream.jump(2); // 2 bytes padding according to https://en.wikipedia.org/wiki/ILBM#CRNG:_Colour_range
|
||||
|
||||
const rate = binaryStream.readInt16BE();
|
||||
const flags = binaryStream.readInt16BE();
|
||||
const low = binaryStream.readUint8();
|
||||
const high = binaryStream.readUint8();
|
||||
|
||||
// Parse flags according to https://en.wikipedia.org/wiki/ILBM#CRNG:_Colour_range
|
||||
// If bit 0 is 1, the color should cycle, otherwise this color register range is inactive
|
||||
// and should have no effect.
|
||||
//
|
||||
// If bit 1 is 0, the colors cycle upwards (forward), i.e. each color moves into the next
|
||||
// index position in the palette and the uppermost color in the range moves down to the
|
||||
// lowest position.
|
||||
// If bit 1 is 1, the colors cycle in the opposite direction (reverse).
|
||||
// Only those colors between the low and high entries in the palette should cycle.
|
||||
const activeBitMask = 1 << 0;
|
||||
const directionBitMask = 1 << 1;
|
||||
|
||||
return {
|
||||
rate,
|
||||
active: (flags & activeBitMask) !== 0,
|
||||
direction: (flags & directionBitMask) !== 0 ? "reverse" : "forward",
|
||||
low,
|
||||
high,
|
||||
};
|
||||
}
|
||||
|
||||
function decompress(binaryStream, endOfChunkIndex) {
|
||||
const result = [];
|
||||
|
||||
while (binaryStream.index < endOfChunkIndex) {
|
||||
const byte = binaryStream.readByte();
|
||||
|
||||
if (byte > 128) {
|
||||
const nextByte = binaryStream.readByte();
|
||||
for (let i = 0; i < 257 - byte; i++) {
|
||||
result.push(nextByte);
|
||||
}
|
||||
} else if (byte < 128) {
|
||||
for (let i = 0; i < byte + 1; i++) {
|
||||
result.push(binaryStream.readByte());
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// TODO(m): Read a range of bytes straight into an array?
|
||||
// Use arrayBuffers throughout instead?
|
||||
function readUncompressed(binaryStream, endOfChunkIndex) {
|
||||
const result = [];
|
||||
|
||||
while (binaryStream.index < endOfChunkIndex) {
|
||||
const byte = binaryStream.readByte();
|
||||
result.push(byte);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Parse Thumbnail chunk
|
||||
function parseTINY(binaryStream, image, chunkLength) {
|
||||
image.thumbnail = {
|
||||
// FIXME(m): Remove need for reference to image palette in thumbnail data
|
||||
palette: image.palette,
|
||||
};
|
||||
const endOfChunkIndex = binaryStream.index + chunkLength;
|
||||
|
||||
image.thumbnail.width = binaryStream.readUint16BE();
|
||||
image.thumbnail.height = binaryStream.readUint16BE();
|
||||
image.thumbnail.size = image.thumbnail.width * image.thumbnail.height;
|
||||
|
||||
if (image.compression === 1) {
|
||||
image.thumbnail.pixelData = decompress(binaryStream, endOfChunkIndex);
|
||||
} else {
|
||||
image.thumbnail.pixelData = readUncompressed(binaryStream, endOfChunkIndex);
|
||||
}
|
||||
}
|
||||
|
||||
// Parse Image data chunk
|
||||
function parseBODY(binaryStream, image, chunkLength) {
|
||||
const endOfChunkIndex = binaryStream.index + chunkLength;
|
||||
|
||||
if (image.compression === 1) {
|
||||
image.pixelData = decompress(binaryStream, endOfChunkIndex);
|
||||
} else {
|
||||
image.pixelData = readUncompressed(binaryStream, endOfChunkIndex);
|
||||
}
|
||||
}
|
||||
|
||||
// Parse FORM chunk
|
||||
function parseFORM(binaryStream) {
|
||||
const image = {
|
||||
cyclingRanges: [],
|
||||
};
|
||||
|
||||
let chunkId = binaryStream.readString(4);
|
||||
let chunkLength = binaryStream.readUint32BE();
|
||||
const formatId = binaryStream.readString(4);
|
||||
|
||||
// Validate chunk according to notes on https://en.wikipedia.org/wiki/ILBM
|
||||
if (chunkId !== "FORM") {
|
||||
throw new Error(
|
||||
`Invalid chunkId: "${chunkId}" at byte ${binaryStream.index}. Expected "FORM".`
|
||||
);
|
||||
}
|
||||
|
||||
if (chunkLength !== binaryStream.length - 8) {
|
||||
throw new Error(
|
||||
`Invalid chunk length: ${chunkLength} bytes. Expected ${
|
||||
binaryStream.length - 8
|
||||
} bytes.`
|
||||
);
|
||||
}
|
||||
|
||||
if (formatId !== "PBM ") {
|
||||
throw new Error(`Invalid formatId: "${formatId}". Expected "PBM ".`);
|
||||
}
|
||||
|
||||
// Parse all other chunks
|
||||
while (!binaryStream.EOF()) {
|
||||
chunkId = binaryStream.readString(4);
|
||||
chunkLength = binaryStream.readUint32BE();
|
||||
|
||||
switch (chunkId) {
|
||||
case "BMHD":
|
||||
parseBMHD(binaryStream, image);
|
||||
break;
|
||||
case "CMAP":
|
||||
parseCMAP(binaryStream, image);
|
||||
break;
|
||||
case "DPPS":
|
||||
// NOTE(m): Ignore unknown DPPS chunk of size 110 bytes
|
||||
binaryStream.jump(110);
|
||||
break;
|
||||
case "CRNG":
|
||||
image.cyclingRanges.push(parseCRNG(binaryStream));
|
||||
break;
|
||||
case "TINY":
|
||||
parseTINY(binaryStream, image, chunkLength);
|
||||
break;
|
||||
case "BODY":
|
||||
parseBODY(binaryStream, image, chunkLength);
|
||||
break;
|
||||
default:
|
||||
throw new Error(
|
||||
`Unsupported chunkId: ${chunkId} at byte ${binaryStream.index}`
|
||||
);
|
||||
}
|
||||
|
||||
// Skip chunk padding byte when chunkLength is not a multiple of 2
|
||||
if (chunkLength % 2 === 1) binaryStream.jump(1);
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
export default function parsePBM(arrayBuffer) {
|
||||
const binaryStream = new BinaryStream(arrayBuffer);
|
||||
try {
|
||||
const image = parseFORM(binaryStream);
|
||||
return image;
|
||||
} catch (error) {
|
||||
if (error instanceof RangeError) {
|
||||
throw new Error(`Failed to parse file.`);
|
||||
} else {
|
||||
throw error; // re-throw the error unchanged
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -27,7 +27,7 @@
|
||||
|
||||
import { expect, test } from "vitest";
|
||||
|
||||
import PBM from "../src/pbm.js";
|
||||
import parsePBM from "../src/pbm.js";
|
||||
|
||||
const fs = require("fs");
|
||||
|
||||
@ -35,7 +35,7 @@ test("Successfully parse a PBM file", () => {
|
||||
const data = fs.readFileSync("./tests/fixtures/VALID.LBM");
|
||||
|
||||
expect(() => {
|
||||
new PBM(data.buffer);
|
||||
parsePBM(data.buffer);
|
||||
}).not.toThrowError();
|
||||
});
|
||||
|
||||
@ -43,7 +43,7 @@ test("Fail to parse a PBM file with an invalid chunk id", () => {
|
||||
const data = fs.readFileSync("./tests/fixtures/INVALID_CHUNK_ID.LBM");
|
||||
|
||||
expect(() => {
|
||||
new PBM(data.buffer);
|
||||
parsePBM(data.buffer);
|
||||
}).toThrowError(/^Invalid chunkId: "FARM" at byte 12. Expected "FORM".$/);
|
||||
});
|
||||
|
||||
@ -51,7 +51,7 @@ test("Fail to parse a PBM file with an invalid chunk length", () => {
|
||||
const data = fs.readFileSync("./tests/fixtures/INVALID_CHUNK_LENGTH.LBM");
|
||||
|
||||
expect(() => {
|
||||
new PBM(data.buffer);
|
||||
parsePBM(data.buffer);
|
||||
}).toThrowError(/^Invalid chunk length: 7070 bytes. Expected 7012 bytes.$/);
|
||||
});
|
||||
|
||||
@ -59,13 +59,13 @@ test("Fail to parse an IFF file that is not a PBM file", () => {
|
||||
const data = fs.readFileSync("./tests/fixtures/SEASCAPE.LBM");
|
||||
|
||||
expect(() => {
|
||||
new PBM(data.buffer);
|
||||
parsePBM(data.buffer);
|
||||
}).toThrowError(/^Invalid formatId: "ILBM". Expected "PBM ".$/);
|
||||
});
|
||||
|
||||
test("Parse a PBM bitmap header", () => {
|
||||
const data = fs.readFileSync("./tests/fixtures/VALID.LBM");
|
||||
const image = new PBM(data.buffer);
|
||||
const image = parsePBM(data.buffer);
|
||||
|
||||
expect(image.width).toStrictEqual(640);
|
||||
expect(image.height).toStrictEqual(480);
|
||||
@ -84,7 +84,7 @@ test("Parse a PBM bitmap header", () => {
|
||||
|
||||
test("Parse PBM palette information", () => {
|
||||
const data = fs.readFileSync("./tests/fixtures/VALID.LBM");
|
||||
const image = new PBM(data.buffer);
|
||||
const image = parsePBM(data.buffer);
|
||||
|
||||
expect(image.palette.length).toStrictEqual(256);
|
||||
expect(image.palette[10]).toStrictEqual([87, 255, 87]);
|
||||
@ -92,14 +92,14 @@ test("Parse PBM palette information", () => {
|
||||
|
||||
test("Parse PBM color cycling information", () => {
|
||||
const data = fs.readFileSync("./tests/fixtures/VALID.LBM");
|
||||
const image = new PBM(data.buffer);
|
||||
const image = parsePBM(data.buffer);
|
||||
|
||||
expect(image.cyclingRanges.length).toStrictEqual(16);
|
||||
});
|
||||
|
||||
test("Parse PBM thumbnail", () => {
|
||||
const data = fs.readFileSync("./tests/fixtures/VALID.LBM");
|
||||
const image = new PBM(data.buffer);
|
||||
const image = parsePBM(data.buffer);
|
||||
|
||||
expect(image.thumbnail.width).toStrictEqual(80);
|
||||
expect(image.thumbnail.height).toStrictEqual(60);
|
||||
@ -108,7 +108,7 @@ test("Parse PBM thumbnail", () => {
|
||||
|
||||
test("Decode PBM thumbnail pixel data", () => {
|
||||
const data = fs.readFileSync("./tests/fixtures/VALID.LBM");
|
||||
const image = new PBM(data.buffer);
|
||||
const image = parsePBM(data.buffer);
|
||||
|
||||
expect(image.thumbnail.pixelData.length).toStrictEqual(4800);
|
||||
// FIXME(m): Verify these values are correct in the test image thumbnail:
|
||||
@ -118,7 +118,7 @@ test("Decode PBM thumbnail pixel data", () => {
|
||||
|
||||
test("Decode PBM image pixel data", () => {
|
||||
const data = fs.readFileSync("./tests/fixtures/VALID.LBM");
|
||||
const image = new PBM(data.buffer);
|
||||
const image = parsePBM(data.buffer);
|
||||
|
||||
expect(image.pixelData.length).toStrictEqual(307_200);
|
||||
// FIXME(m): Verify these values are correct in the test image:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user