Features

  • paint data directly onto a compressed image's data to make it glitch out
  • multithreaded image encoding updates your corrupted image in real time as you work
  • you can even break the image if you can figure out how (don't worry you have undo)
  • different color/brush effects corrupt the data differently
  • toggle between different encoding styles (at the moment only supports PNG post-filter corruption)
  • export your corrupted image to use however you want (greed, boasting, lust, etc.)

This tool was made as a part of the Handmade Network's Wheel Reinvention Jam 2023. I'm releasing this app in it's primitive state after a week of development to get it in people's hands ASAP so others can try playing with it.

Warning: as this is was jam project, new features and experimentation were prioritized over optimization. The app only runs at a full framerate with smaller images. Larger images can still be edited but at the cost of performance.

The jam page for this project, including logs of the jam's development, can be found here: https://handmade.network/p/441/badpaint/.

Installation & basic usage

The jam version of the application (v0.0.1) is available for tinkering here. To use the application, uncompress it and open it. Then drag any image onto the window and paint the bottom image.

Controls & keyboard shortcuts

  • Brush effect: Erase (e): Erase painted data
  • Brush effect: Remove (r): Replaces PNG data with 0
  • Brush effect: Max (a): Replaces PNG data with the max color bit: 255
  • Brush effect: Shift (s): Shifts PNG data forward by 36 bits (the 36 is arbitrary)
  • Brush effect: Random (d): Adds a random value (between 0-255)
  • Undo with Ctrl-Z
  • Change the size of the brush with the brush size slider
  • Press 1-5 to toggle different PNG filter algorithms (Sub, Up, Average, Paeth, Optimal). Each distorts the image differently.

If the app crashes for any reason, a crash reporter will prompt you if you like to send a crash report via a discord webhook.

What the heck is going on?

The editor is surprisingly simple. The bottom image displays the raw data from a PNG in the middle of it's encoding process. Each byte is represented by a pixel ranging from black to white (0-255). The data is the state of the PNG after the PNG has gone through algorithmic filtering and before it is compressed. The pixels you insert via painting adds unintended arbitrary bytes into the data stream which are then compressed incorrectly by the PNG encoding algorithm resulting in the striking colorful glitches you see in the final top image.

You can press 1-5 to toggle different PNG filter algorithms, the effects of which will be visible in both windows. Something I didn't expect from this project is that playing with the data gives you an intuitive view of how a PNG is encoded.

Here's an overview on PNG encoding and corruption behaviors that I used as reference while making this tool: https://ucnv.github.io/pnglitch/

Implementation details

This app was codded in C-styled C++. raylib is used for handling platform needs and rendering. The app uses my own custom string library, math library, and immediate-mode UI which were repurposed from my in-progress self-developed game engine. The crash report code, (which is also repurposed from my engine), is based on Phillip Trudeau-Tavara's wonderful implementation. The multithreading API is implemented as inspired by Casey Muratori's multithreaded job system as seen in his Handmade Hero series. The loadpng library is used for PNG decoding and a modified version of stb_image_write.h is used for PNG encoding.

TODO for the future whenever I have time

  • More image encoding options. I definitely want to explore JPEG.
  • Layers, like in Photoshop, each layer with its own image encoding transforming the corrupted data from the previous layer
  • Iteration on the editing process, brush effects, and painting styles. At the moment you only have a marker/paint tool. It would be nice to have all of the tools you see in old school MS Paint (bucket, rectangle, spray can, etc.)
  • zoom & resizing
  • Optimization & performance improvements (SIMD, more GPU utilization, etc).

Recent Activity

I was not expecting to need to have a Day 1 patch for &badpaint. But since the webhook token for the crash handler was in the public repo, a bot or someone started spamming invalid crash reports to the webhook. Don't have your tokens be public, kids!

Anyway, here's the finished jam release! Feel free to play with it :) Attached is an image a friend made with it https://github.com/Ahmaykmewsik/badpaint

&badpaint is ready to be released! As a super duper preview version, but I'm pretty proud of the main concept I stumbled upon. This turned out a LOT better than I was expecting! I'm looking forward to seeing what other people do with this.

made a new logo for the jam hope everyone likes it &badpaint

It's surprising how much an undo functionality goes. I just finished implementing it and immediatley found myself drawn compelled to try to actually craft something that I put a little bit more time into. I spent maybe 5 minutes on this, trying to make more subtle effects. Having this much control over what the glitches produce is maybe a little antithetical to the philosophy of glitch art I'm realizing, but hey, it's pretty cool to be more in control of what happens. &badpaint

have the beginnings of a toolbar with brush size, tool type etc. Also made the paint show the asyncronous nature of the rendering, as processing larger images can take a while. Nothing that a little multithreading can't handle though.

there's many more features I want to add but I'm going to start focusing on getting a privative release version of &badpaint ready by the end of the jam. I want to put this in people's hands and see what happens. the other artist type folks I've shown this to seem eager to play with it &badpaint

through experimentation i discovered a very intuitive way to display the compressed that you're distorting which lets you be very hands on. I'm having way too much fun with this &badpaint

we have drawing!!! &badpaint

**[WARNING, FLASHING IMAGES] **things got really cool really quickly. This is randomly corruptting the data right after the png is filtered for beautiful results. Forcing the filter to be a particular algorithm (displayed here) gives different characteristic results. And when running -02 it even does all this in a frame! (with smaller images). Now to figure out how to "paint" this... &badpaint

we have png corruption! After struggling with png encoder libraries, I finally got loadpng working, which has great error reporting as well as settings to turn off important checks that the decoding algorithm does to allow for more corruption. Randomly changing some bytes somewhere only works some of the time, you can see the errors the decoder is throwing in the console. Corrupting the png itself is quite error prone to just break the file, but with some checks turned off it works pretty well. According to some info I found which I found (which I'll post in #the-library ) png corruption becomes more powerful when you corrupt the data in-between one of the stages of encoding the png instead of just at the end. &badpaint

I ported over my UI code from my in-progress engine into the project so I can have a immediate mode rendering thing going on, that took a good deal of work. But I still wanted to get something happening on day 1 so I made a simple thing that pushes some pixels to the side randomly when you hold down a key. It's not much, but hey it already looks cool! :D

Before I get started with implementing UI proper for manipulating an image, I think I'm going to first build up an architecture for swapping the image in-between different formats. The characteristic of how an image distorts is heavily based on the format the image is in (you can already see that here, the handmade logo distorts differently than the animal pics), so I want to have that more solid before I start making "tools" proper. This is just manipulating raw pixel data, which is nice, but the kind of magic I wanna explore comes from breaking more complex compressed formats! &badpaint

first steps with badpaint, dragging an image into a window and display using raylib functionality. Attempted to load ANY file, but discovered it's not as simple as disabling confirming the header is valid, you also need the header info to actually read the file at all, so putting that aside for now and moving onto image manipulation basics. &badpaint