Adding JPEG XL & QOI Support to my Website OS
Tags: webdev, javascript, programming, tutorial
Published: 2022-12-30 07:10:31

I'm always having fun adding things to my desktop environment in the browser ([daedalOS](https://github.com/DustinBrett/daedalOS)), which also powers my personal website [dustinbrett.com](https://dustinbrett.com/) where everything I show here can be tried out.

Recently I'd added [TIFF](https://en.wikipedia.org/wiki/TIFF) support to my project via [UTIF.js](https://github.com/photopea/UTIF.js/) and now I decided I wanted to add [JPEG XL](https://jpeg.org/jpegxl/) & [QOI](https://qoiformat.org/).

## JPEG XL Support ([Sample Images](https://jpegxl.info/art/2021-04_jon.html))

![JPEG XL Images](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/swzyf7pagd7j4oazu1jk.png)

For [adding JPEG XL support](https://github.com/DustinBrett/daedalOS/commit/21e400671a3f581d13e692fbeac4eeef7a73db6a) I went with [jxl.js](https://github.com/niutech/jxl.js) which I modified for my use case. After looking through the main file, which is also called [jxl.js](https://github.com/niutech/jxl.js/blob/main/jxl.js), I decided I only needed 2 relevant code blocks. The one to decode the image and the one to turn the `ImageData` into something I could display in my existing codebase (which I already partially had implemented for another use case).

To decode the image it creates a worker with [jxl_dec.js](https://raw.githubusercontent.com/niutech/jxl.js/main/jxl_dec.js), also I updated the path (hardcoded inside `jxl_dec.js`) for [jxl_dec.wasm](https://github.com/niutech/jxl.js/raw/main/jxl_dec.wasm) to point to where I've stored it within my Next.js `public` directory.

Then all that is needed is to run `decodeJxl` with image data in `Buffer`/`Uint8Array` form. Once it returns `ImageData` I use `imgDataToBuffer` to convert it back into a `Buffer` which I use for making thumbnails and displaying the picture in the `Photos` app.

```ts
type JxlDecodeResponse = { data: { imgData: ImageData } };

export const decodeJxl = async (image: Buffer): Promise<ImageData> =>
  new Promise((resolve) => {
    const worker = new Worker("System/JXL.js/jxl_dec.js");

    worker.postMessage({ image, jxlSrc: "image.jxl" });
    worker.addEventListener("message", (message: JxlDecodeResponse) =>
      resolve(message?.data?.imgData)
    );
  });

export const imgDataToBuffer = (imageData: ImageData): Buffer => {
  const canvas = document.createElement("canvas");

  canvas.width = imageData.width;
  canvas.height = imageData.height;
  canvas.getContext("2d")?.putImageData(imageData, 0, 0);

  return Buffer.from(
    canvas?.toDataURL("image/png").replace("data:image/png;base64,", ""),
    "base64"
  );
};
```

## QOI Support ([Sample Images](https://gist.github.com/nicolaslegland/f0577cb49b1e56b729a2c0fc0aa151ba))

![QOI Images](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l6c6mg6vgcyblyj898i8.png)

When it came to [adding QOI support](https://github.com/DustinBrett/daedalOS/commit/e841b2632299e5594b576e115716d7073564e1be#diff-0c7764029c0bd0ee3b532b3ee879a8c328b536e965edf5cd9fceb279e666214d), I used an [open source "Gist"](https://gist.github.com/nicolaslegland/f0577cb49b1e56b729a2c0fc0aa151ba) example that I basically didn't touch except to turn the `qoi.js` file into a `.ts` file so I could export a function which wrapped `transcode_qoi_to_png` and exported it as a `Buffer` for my use cases.

```ts
export const decodeQoi = (imgBuffer: Buffer): Buffer =>
  Buffer.from(new Uint8Array(transcode_qoi_to_png(new Uint8Array(imgBuffer))));
```

## Thanks! ([@DustinBrett](https://www.youtube.com/@DustinBrett))

I hope you enjoyed my article about adding image formats to my desktop environment in the browser. If you're interested in more stuff about my [side project](https://github.com/DustinBrett/daedalOS) which I have been working on for over 2 years, I recently did a video going over 2022's progress.

Please check it out and leave a comment/like if you enjoy it or have something to say.

{% youtube AMnLsUytCRQ %}