Commit Graph

38 Commits

Author SHA1 Message Date
5b3c58fad5 Create a readme, incl. instructions for test exec
It's about time I have a README for this thing. I need somewhere to put
the test setup instructions, making now as good a time as any to get
around to it.
2025-10-14 13:23:43 -05:00
e2ba02f4d1 Make test-case magic ctor's image size-aware.
Those construction functions were setting an image size of 0x0, which is
no longer acceptable because of the iteration limiter. There is now an
extra argument for the expected pixel count.
2025-10-13 11:02:45 -05:00
8ccf6bd475 Track decode progress and stop at W*H pixels
I was thinking about doing some partial parsing where I retrieve a few
extra bytes to see if it's a footer, but that's really hard with an
iterator as an input stream.

Instead, I'm just going to count how many pixels the decoder has output
and stop iterating at that point.

Now to fix the test cases that don't have an image size assigned.
2025-10-13 10:56:23 -05:00
04f83a55d2 Add "full" image decode test to decoder.rs
This is a full QOI file decode routine, although the image it contains
is just one single blue pixel. I've added this because I finally found
the bug I've been hunting:

I'm using `tests/codec.rs` to load reference files and decode them, but
this is resulting in data size errors. There are 32 extra bytes coming
out of the decoder -- too much for the WIDTH x HEIGHT number of pixels,
and it happens to every image.

The cause is the 8-byte end marker. It is not properly detected by the
decoder and is instead used as additional data (probably QOI_OP_INDEX).
Since the decoder emits *pixels*, each of which are 4 bytes, we have 32
bytes of garbage data coming out of the decoder.

Bug found! Now to make the test pass...
2025-10-13 10:05:31 -05:00
0b2408bda1 Fix test: panic when decoded data is wrong size
This started life as a dev utility so I was only printing the message.
Now that it's a `#[test]`, I'll have it panic on the error path.
2025-10-13 08:33:29 -05:00
a6f18e5ea9 Rename codec.rs test function, use new err enum
I'm trying to minimize the number of panics even in test code. Some are
going to stay because I'm lazy and it makes no difference for a test.
2025-10-13 08:03:43 -05:00
7ba2e7760b Add an error enum to test/codec.rs 2025-10-13 08:02:38 -05:00
e31aa5decb Move main.rs to a test, drop dep on png crate
The codec library doesn't use any external dependencies. The PNG crate
is only to load some source material for testing the codec. Turning the
test driver into a real test lets me turn the PNG dependency into a
dev-dependency.
2025-10-12 21:18:32 -05:00
5ae4aa5823 Drop hack to fix byte count.
The byte count on this 800x600 32-bit image should come to 1,920,000
bytes... But it came to 1,920,032 bytes. I'm not sure where the extra
32 bytes come from, but I have to go fix it before I get to call the
decoder complete.
2024-10-14 21:07:30 -05:00
ef5d962621 Add PNG write-out machinery
I've pulled in the PNG crate to write the images back out as regular
PNG images. Now I can compare the results of my decoder against the
reference images.

I also made a weird utility iterator thing to wrap the Decoder's pixel
iterator because the PNG crate wants a &[u8], not a bunch of u32's or
custom pixels structs. I should learn how to impl the right traits so
that I can just `.to_components()` on an Iterator<Item=PixelRGBA>
instead of the wrapper constructor thing I have now.
2024-10-14 21:07:30 -05:00
9cffd665ca Make Decoder parts public so they can be used
I should maybe set the properties to be private and accessible strictly
through accessor methods. That way nobody can accidentally write into
the values. Oh well, that's a thing for later.
2024-10-14 21:07:30 -05:00
431c808b72 Fix test module Decoder calls and constructors
The other magic constructors were no longer valid because of the added
struct members. The calls to `new()` have been reattached to
`new_with_no_metadata()`. Tests pass, still!
2024-10-14 21:07:30 -05:00
f4eef2f863 Relocate the old Decoder::new method to mod test
The old `Decoder::new()` method would not pull the image header out of
the iterator. This was fine for the design then, but I've just decided
that I want to collect this info and store it in the Decoder struct.

I've moved the old method down into the test module so that I can still
use it to test the decoder parts alone. Same philosophy as the
`new_with_previous_pixel()` and `new_with_backbuffer()` methods.
2024-10-14 21:07:30 -05:00
d5e7b0c818 New Decoder creation function, more metadata
The qoi_header is specified as knowing the width, height, channels, and
colorspace. I figure this information would be captured by the decoder,
if anywhere. I've added more properties to the struct, and created a
new, fallible construction function.
2024-10-14 21:07:30 -05:00
ef977d7990 Ignore the test images folder 2024-10-14 21:07:30 -05:00
3b73033599 Add gitignore template from Gitea 2024-10-14 21:07:30 -05:00
d88a1eab51 Add try_read_u32 function to read 4 u8s to a u32 2024-10-14 21:07:30 -05:00
c6e94fcb5b Check-in broken & partial try_read_u32 function
I'm saving this to the repo so I can possibly come back and fix it up
correctly.

A major API change is going to be attempted. The iterator-based stream
processing has proven to be fiddly and annoying, so I'm going to switch
to a processing the complete image.
2024-10-14 21:07:30 -05:00
6a5e2b3ab9 Use the external constant value MAGIC. 2024-10-14 21:07:30 -05:00
0a43a8b9f7 Split into a library and many modules
Wow, what a mess. Why was I making one giant file to contain everything?
I'm splitting it out into several files and taking another crack at
completing the decoder.
2024-10-14 21:07:29 -05:00
3a35dc3a20 Iterators are melting my brain 2024-10-14 21:07:29 -05:00
761beac5fd Cargo format
No, car go road
2024-10-14 21:07:29 -05:00
41d547f3ed Relocate the Decoder constructor hacks
The special constructors can be put in an impl block in the test module
instead of hanging out as dead code in the main library chunk.
2024-10-14 21:07:29 -05:00
c0778ae887 Make those tests pass!
I want to refactor this at some point, but at least it passes the tests.
2024-10-14 21:07:29 -05:00
69fad49286 Fix previous pixel test
The previous pixel needs to be captured before the iterator is advanced.
Otherwise it's just getting the same pixel back out, and all that's
tested is the assignment operator.

The expected alpha value for the first OP_RGBA was set wrong.

The OP_LUMA was missing whole bits. SMH my head.

After changing the prev-pixel check order, the final OP_RGBA *is*
required. Include that in the expected output.
2024-10-14 21:07:29 -05:00
97ec583442 Fix backbuffer test
The backbuffer indices were all kinds of wrong, and there was even a
state corruption bug.

Hash values were simply done wrong. I've added some value delta notes
to the compressed byte array, and then tracked all the state changes
next to the expected indices array. The last RGBA value also needed to
be included. Off by one, oops.

The state corruption exists as a side effect of malformed OP_INDEXs.
This is actually a bigger issue, and I'll need to add a new test for it.
Basically: A pixel produced by an OP_INDEX will hash to that index.
That's the definition of an OP_INDEX. What happens if an OP_INDEX picks
up a pixel that hashes to a different index? Because an OP_INDEX is
reading from the backbuffer array without modification, it doesn't need
to write. An otherwise valid encoder can then skip the write step. This
causes a missing buffer update when the pixel should land on another
index. The decoder is now in a corrupted state.
2024-10-14 21:07:29 -05:00
b9aa3cf28f Tests for prev_pixel and backbuffer
Gotta check that the previous pixel is recorded and the backbuffer is
filled. There are errors in the decoder that revolve around this.
2024-10-14 21:07:29 -05:00
8b99642e4c Fix: Luma unpack test
The compressed sample had the wrong value and was unpacking to a non-
zero delta-green value. This threw off the following iterations.
2024-10-14 21:07:29 -05:00
b05daa503e Fix: wrapping, store previous pixel value
The wrapping wasn't working properly, clearly, but I forgot about the
previous pixel entirely.

I'll add some more explicit tests for the previous pixel behavior. I
think it's still broken for RGB and RGBA, because I'm dumb.
2024-10-14 21:07:29 -05:00
62b35b5aba Cargo Format 2024-10-14 21:07:29 -05:00
85ad2fa252 OP_DIFF and OP_LUMA roll over and under test cases
I wasn't explicitly testing the wrapping behavior of the diff and luma
op codes.
2024-10-14 21:07:29 -05:00
ac4d88eb15 Fix OP_RUN test case
My did my numbers wrong. 0b1111 is definitely not 13. Not under normal
encodings, not with the -1 OP_RUN bias. Bro what.
2024-10-14 21:07:29 -05:00
79109cc40b Proper operation decoding
The op codes are now being recognized properly. I just gotta decode them
the right way. I fixed an error where I was bitwise-or -ing the values
instead of bitwise-and -ing them. Small brain up in here.
2024-10-14 21:07:29 -05:00
aa18ba5992 Cargo Format 2024-10-14 21:07:29 -05:00
1ca0394d4a Run length stored and checked during iteration
The run length can simply be extracted and stored as part of the
iterator state. For each iteration, check if the length is > 0, and emit
more pixels until it isn't. Then continue the normal decode process.
2024-10-14 21:07:29 -05:00
ef4b31dab6 Most of the decoders, but the match is broken
I was going to try to match over the op codes, but that isn't possible.

Most of the decoders are done, too, but I'm quite sure that the delta
operations will encounter a wrap around and then panic. The OP_RUN is a
problem, as well. I think I'm going to cheat this problem by holding a
counter. This way I can see if I'm in a run, and then emit another pixel
copy until counter == 0.
2024-10-14 21:07:29 -05:00
4a31b1e20a Change struct to grab input iterator, not slice
I want to iterate over the input elements, and I couldn't figure out how
to get the slice lifetime to be specified properly. I've swapped the
slice for any Iterator that emits u8's.
2024-10-14 21:07:29 -05:00
b77f32b2df Unit tests for unpacking each of the op codes
These tests can be used to verify the fundamentals of the decoder: That
individual instructions are parsed correctly and converted back into
the correct pixel values.
2024-10-14 21:07:29 -05:00