184 Followers
83 Following
575 Posts

As a sidequest of a sidequest of a sidequest [1], I'm adding a "Convert to Bilevel…" feature to SerenityOS's PixelPaint.

I've been wanting to implement Floyd-Steinberg error diffusion dithering (something much simpler than this long name might suggest) for literally decades, cool that I finally got around to it :)

1: PDFs can contain an obscure file format called JBIG2, so I needed to write a JBIG2 decoder. That needs tests, there are very few JBIG2 encoders out there, and the ones that exist use way fewer features than what's found in PDFs, so I'm writing a JBIG2 encoder too. JBIG2 can only store black and white pixels, so I needed to write a "convert to bilevel" feature. And since I have that anyway, might as well hook it up!

PR: https://github.com/SerenityOS/serenity/pull/26182

For some PDFs, the (by default hidden) vector text is more pleasant to look at, since you get to read, well, vector text instead of potentially lowish-res bitmap text. Here's page 9 from ignition.pdf (https://library.sciencemadness.org/library/books/ignition.pdf) both ways (vector text forced on with debug options first, "normal" view second). But if the OCR got a word wrong (e.g. "Kvery" at the start of the last paragraph) you get to see it, which is a bit distracting.

It's also interesting how it's possible to select text in these OCRd books. The OCR software also includes vector text, but draws it with the "hide text" text style. This allows you to select the vector text. Serenity's PDF viewer has debug options to disable drawing images and drawing hidden text. Here's page 2 in that view.

(The text looks a bit wonky—might be a LibPDF bug, or maybe it just looks wonky and since you usually can't see it it's fine.)

It's interesting how quite a few scanned book PDFs work: The OCR software separates background and foreground, then compresses the background image as a lower-res JPEG2000. The foreground is separated into mask, stored as JBIG2, and color, which is either a solid color or also a JPEG2000 (which only has meaningful data where the JBIG2 says that there is color). Here are these three images for page 2 of disquisitionesa00gaus.pdf, together with what the final composited page looks like.

Over the summer break, I added support for clipping to arbitrary paths to Serenity's LibPDF. Here's a before/after of one of my test files, together with a visualization of the clipping paths.

https://github.com/SerenityOS/serenity/pull/26035, the PR that added the feature, has many more before/after screenshots.

The PR looks fairly obvious and straightforward in hindsight, but it took me a while to get it to that shape. My first local proof of concept was extremely slow: For my initial test page (page 1140 of the PDF 1.7 spec), it originally was a 250x (25000%) slowdown. The final version is "just" 5% slower for that page.

There's like 4 larger features missing and LibPDF will be able to render most complicated files :^)

Here's what it looks like. Firefox's built in PDF viewer isn't very good, so it doesn't work great in Firefox. Safari's built-in PDF iframes look nicer in this demo than Chrome's, but both work.

Took a break from jpeg2000 and started on the PDF `sh` operator over the weekend. Many before/after screenshots at https://github.com/SerenityOS/serenity/pull/25717

The most dramatic one:

LibPDF: Implement painting of axial shadings; stitching function improvements by nico · Pull Request #25717 · SerenityOS/serenity

Or, as they're called elsewhere, linear gradients. Since this uses PDF function objects and PDF color spaces, it can't use the existing gradient code in LibGfx. The way shadings are drawn w...

GitHub

Over the last few months, I wrote a JPEG2000 decoder for SerenityOS. JPEG2000 is one of the image formats that can appear in PDF files. I finally sent out the last pull request in that JPEG2000 support patch series that connects the image decoder to the PDF library: https://github.com/SerenityOS/serenity/pull/25693

I found writing a JPEG2000 decoder pretty difficult!

I thought it's because it has so much more stuff, but looking at line counts it's in the same ballpark as other image decoders in the system:

JBIG2: ~2.9 kLoC
JPEG: ~2 kLoC
JPEG2000: ~3 kLoC
PNG: ~1.5 kLoC (not counting the deflate code though)
WebP(lossy+lossless): ~3 kLoC

Maybe it's because the other formats are a bit more horizontal, in that you don't need to do all the features before you can decode a simple image. JPEG2000 feels like it needs a lot of machinery working before anything works. Also, the example codestream in the spec is too simple to be useful.

Maybe I'll write a bit more about the format later. In the meantime, the PR description of https://github.com/SerenityOS/serenity/pull/25670 has an overview of the rough steps. (I think it's pretty readable, but it's well possible that it's very cryptic for someone new to the format.)

LibPDF+LibGfx: Add initial support for decoding JPEG2000 images in PDFs :^) by nico · Pull Request #25693 · SerenityOS/serenity

PDF files are one of the few places where JPEG2000 found adoption. This adds support for decoding rgb and greyscale jpeg2000 images in PDF files. JPEG2000 images in PDFs have a few features that ot...

GitHub

…and one more.

These last 5 are only 36 bytes. And the 36 bytes all identical for these five fairly different images except for image dimensions and for the 8 bits that store the predictor kind.

I wrote a small program that lets you pick a constant color and a constant predictor, and that writes a webp file with them. See image description for tool invocation command.

(https://github.com/SerenityOS/serenity/compare/master...nico:webp-constant-size?expand=1 - set write_color_index_transform to false to get my results.)

GitHub - SerenityOS/serenity: The Serenity Operating System 🐞

The Serenity Operating System 🐞. Contribute to SerenityOS/serenity development by creating an account on GitHub.

GitHub