+
+/* -------------------------------------------------------------------------------------------------
+ * RLE STUFF
+ * -------------------------------------------------------------------------------------------------
+ */
+/* Limit streak count per scanline so we can directly jump to specific scanline */
+#define RLE_STREAKS_PER_SCANLINE 4
+/* Every streak is encoded by 2 bytes: offset and count of black pixels in the streak */
+#define RLE_BYTES_PER_SCANLINE RLE_STREAKS_PER_SCANLINE * 2
+
+static RLEBitmap createRLEBitmap(unsigned int w, unsigned int h) {
+ RLEBitmap ret;
+ ret.w = w;
+ ret.h = h;
+
+ /* Add some padding at the end of the buffer, with the worst case for a scanline (w/2 streaks) */
+ ret.scans = (unsigned char*) calloc(h * RLE_BYTES_PER_SCANLINE + w, 1);
+
+ return ret;
+}
+
+static destroyRLEBitmap(RLEBitmap b) {
+ free(b.scans);
+}
+
+static RLEBitmap rleEncode(unsigned char *pixels, unsigned int w, unsigned int h) {
+ int scanline;
+ int i;
+ int penActive = 0;
+ int counter = 0;
+ int accum = 0;
+ RLEBitmap ret;
+ unsigned char *output;
+
+ /* https://www.youtube.com/watch?v=RKMR02o1I88&feature=youtu.be&t=55 */
+ ret = createRLEBitmap(w, h);
+
+ for (scanline = 0; scanline < h; scanline++) {
+ output = ret.scans + scanline * RLE_BYTES_PER_SCANLINE;
+ accum = 0;
+ for (i = 0; i < w; i++) {
+ if (*pixels++) {
+ if (penActive) {
+ if (counter >= PIXEL_PADDING) {
+ *output++ = (unsigned char) counter;
+ counter = 0;
+ *output++ = (unsigned char)accum;
+ }
+ counter++;
+ accum++;
+ } else {
+ *output++ = (unsigned char)accum;
+ counter = 1;
+ accum++;
+ penActive = 1;
+ }
+ } else {
+ if (penActive) {
+ *output++ = (unsigned char)counter;
+ counter = 1;
+ accum++;
+ penActive = 0;
+ } else {
+ counter++;
+ accum++;
+ }
+ }
+ }
+
+ if (penActive) {
+ *output++ = (unsigned char)counter;
+ }
+ penActive = 0;
+ counter = 0;
+ }
+
+ return ret;
+}