Conversation
Co-authored-by: Hexagon <419737+Hexagon@users.noreply.github.com> Agent-Logs-Url: https://github.com/cross-org/image/sessions/87a6b200-83c3-4461-8907-3df4836d773c
|
@copilot compare qoi implementation to https://github.com/phoboslab/qoi |
There was a problem hiding this comment.
Pull request overview
Adds three additional image formats (PGM, PBM, QOI) to the library’s pluggable format system, expanding Netpbm coverage and introducing QOI as a modern lossless option.
Changes:
- Implemented new format handlers:
PGMFormat(P2/P5),PBMFormat(P1/P4), andQOIFormat(encode/decode). - Registered/exported the new formats through
src/image.tsandmod.ts. - Added comprehensive tests and updated docs/README/changelog to reflect new format support.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
src/formats/qoi.ts |
Adds QOI encode/decode + metadata extraction. |
src/formats/pgm.ts |
Adds PGM (P2/P5) decode + P5 encode. |
src/formats/pbm.ts |
Adds PBM (P1/P4) decode + P4 encode. |
src/image.ts |
Registers PGM/PBM/QOI formats for auto-detection. |
mod.ts |
Exports the new format classes and updates module docs. |
test/formats/qoi.test.ts |
New tests for QOI behavior and round-trips. |
test/formats/pgm.test.ts |
New tests for PGM decode/encode and error cases. |
test/formats/pbm.test.ts |
New tests for PBM decode/encode and error cases. |
README.md |
Updates supported formats list/table. |
docs/src/formats.md |
Documents new format support in matrices/details. |
CHANGELOG.md |
Notes added format support in Unreleased section. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // Reserve 8 bytes for end marker at tail | ||
| const dataEnd = data.length - 8; | ||
|
|
||
| while (pixelIndex < pixelCount && offset < dataEnd) { | ||
| const byte = data[offset++]; |
There was a problem hiding this comment.
QOI decoding currently assumes the last 8 bytes are the end marker (via dataEnd = data.length - 8) and will silently ignore them even if the marker is missing/incorrect. It also returns successfully even if the loop exits before producing width*height pixels (e.g., truncated files), leaving the output buffer partially uninitialized. Consider verifying the 8-byte end marker, and throwing if pixelIndex !== pixelCount after parsing.
| if (byte === QOI_OP_RGB) { | ||
| r = data[offset++]; | ||
| g = data[offset++]; | ||
| b = data[offset++]; | ||
| // a unchanged |
There was a problem hiding this comment.
The decoder reads chunk payload bytes without checking that enough bytes remain before dataEnd (e.g., QOI_OP_RGB needs 3 bytes, QOI_OP_RGBA needs 4, QOI_OP_LUMA needs 1). With truncated/corrupt input this can read past the intended pixel stream and yield incorrect output instead of throwing. Add bounds checks before consuming chunk payload bytes and throw a descriptive error on premature EOF.
| extractMetadata(data: Uint8Array): Promise<ImageMetadata | undefined> { | ||
| if (!this.canDecode(data)) return Promise.resolve(undefined); | ||
|
|
||
| const channels = data[12]; | ||
|
|
||
| return Promise.resolve({ | ||
| format: "qoi", | ||
| compression: "none", | ||
| frameCount: 1, | ||
| bitDepth: 8, | ||
| colorType: channels === 4 ? "rgba" : "rgb", | ||
| }); |
There was a problem hiding this comment.
extractMetadata() treats any non-4 channels value as RGB (channels === 4 ? "rgba" : "rgb"). For malformed QOI headers (e.g., channels=0/2/5) this returns misleading metadata even though decode() rejects such files. Consider validating channels (3 or 4) here too and returning undefined (or a safe fallback) when unsupported.
| test("QOI: decode - invalid data throws", async () => { | ||
| const format = new QOIFormat(); | ||
|
|
||
| await assertRejects( | ||
| async () => await format.decode(new Uint8Array([0, 1, 2, 3])), | ||
| Error, | ||
| "Invalid QOI signature", | ||
| ); | ||
| }); |
There was a problem hiding this comment.
QOI tests cover invalid signature and encode-side length mismatch, but there are no negative decode tests for truncated/corrupt QOI streams (e.g., missing/incorrect 8-byte end marker, truncated QOI_OP_RGB/RGBA payload, or pixel stream ending before width*height). Adding these would prevent silent partial decodes if decoder validation is tightened.
|
@copilot apply changes based on the comments in this thread |
…ng reference qoi.h) Co-authored-by: Hexagon <419737+Hexagon@users.noreply.github.com> Agent-Logs-Url: https://github.com/cross-org/image/sessions/e7a44ae9-80eb-4cd0-9a7b-f21aad38724d
Compared against the reference
Found one difference: the encoder was computing channel diffs as plain integers ( Fixed in Warning Firewall rules blocked me from connecting to one or more addresses (expand for details)I tried to connect to the following addresses, but was blocked by firewall rules:
If you need me to access, download, or install something from one of these locations, you can either:
|
…pixel detection, extractMetadata channels validation, and 7 new negative tests Co-authored-by: Hexagon <419737+Hexagon@users.noreply.github.com> Agent-Logs-Url: https://github.com/cross-org/image/sessions/82c277d1-99d4-4c56-b8c3-a21a2187967f
Applied all 4 review comments in
Added 7 new negative decode tests covering all of the above. Full suite passes (612 tests). |
Extends Netpbm coverage to the full P1–P6 family and adds QOI as a modern lossless option, completing the most practical gap in common format support.
New formats
PGM — Portable GrayMap (
src/formats/pgm.ts)PBM — Portable BitMap (
src/formats/pbm.ts)0/1) and P4 (packed bits, MSB-first, row-padded)0=white,1=blackQOI — Quite OK Image (
src/formats/qoi.ts)RGB,RGBA,INDEX,DIFF,LUMA,RUN), 64-entry running color array, big-endian header, end markerphoboslab/qoisigned charsemanticsextractMetadatavalidates channels (3 or 4) and returnsundefinedfor unsupported valuesWiring
src/image.tsand exported frommod.tsREADME.md,CHANGELOG.md, anddocs/src/formats.md⌨️ Start Copilot coding agent tasks without leaving your editor — available in VS Code, Visual Studio, JetBrains IDEs and Eclipse.