From fc737c603f58f559f5bc3ee3042a8d8212395b3c Mon Sep 17 00:00:00 2001
From: Gabriel Tofvesson <contact@w1zzrd.dev>
Date: Wed, 26 Aug 2020 03:19:23 +0200
Subject: [PATCH] Add files

---
 include/bitmap.h  |  26 ++++++
 include/charmap.h |  29 +++++++
 include/gfx.h     |  46 +++++++++++
 include/point.h   |   9 +++
 include/rect.h    |  50 ++++++++++++
 res/0.bm          | Bin 0 -> 32 bytes
 res/0.rbm         |  16 ++++
 res/1.bm          | Bin 0 -> 32 bytes
 res/1.rbm         |  16 ++++
 res/maps.meta     |   2 +
 src/bitmap.c      | 202 ++++++++++++++++++++++++++++++++++++++++++++++
 src/bmconv.c      |  19 +++++
 src/charmap.c     | 131 ++++++++++++++++++++++++++++++
 src/draw.c        |  91 +++++++++++++++++++++
 src/gfx.c         | 150 ++++++++++++++++++++++++++++++++++
 15 files changed, 787 insertions(+)
 create mode 100644 include/bitmap.h
 create mode 100644 include/charmap.h
 create mode 100644 include/gfx.h
 create mode 100644 include/point.h
 create mode 100644 include/rect.h
 create mode 100644 res/0.bm
 create mode 100644 res/0.rbm
 create mode 100644 res/1.bm
 create mode 100644 res/1.rbm
 create mode 100644 res/maps.meta
 create mode 100644 src/bitmap.c
 create mode 100644 src/bmconv.c
 create mode 100644 src/charmap.c
 create mode 100644 src/draw.c
 create mode 100644 src/gfx.c

diff --git a/include/bitmap.h b/include/bitmap.h
new file mode 100644
index 0000000..68ae312
--- /dev/null
+++ b/include/bitmap.h
@@ -0,0 +1,26 @@
+#ifndef BITMAP_H
+#define BITMAP_H
+
+#include <stddef.h>
+
+struct bitmap {
+  void *data;
+  size_t width;
+  size_t height;
+};
+
+struct bitmap bitmap_load_readable (const char *path);
+struct bitmap bitmap_load_raw (const char *path);
+struct bitmap bitmap_parse_readable (const char *data, int width, int height);
+void bitmap_save_readable (const char *path, struct bitmap bitmap);
+void bitmap_save_raw (const char *path, struct bitmap bitmap);
+
+void bitmap_compress (const char *readable_path, const char *raw_path);
+void bitmap_decompress (const char *raw_path, const char *readable_path);
+
+char bitmap_read_bit (struct bitmap bitmap, size_t x, size_t y);
+void bitmap_write_bit (struct bitmap bitmap, size_t x, size_t y, char bit);
+
+void bitmap_discard (struct bitmap bitmap);
+
+#endif
diff --git a/include/charmap.h b/include/charmap.h
new file mode 100644
index 0000000..6c57126
--- /dev/null
+++ b/include/charmap.h
@@ -0,0 +1,29 @@
+#ifndef CHARMAP_H
+#define CHARMAP_H
+
+#include "bitmap.h"
+
+struct charmap {
+  char c;
+  struct bitmap bitmap;
+};
+
+struct charmap_meta {
+  const char *root;   // Root directory for maps
+  struct charmap *maps;
+  size_t map_count;
+  char mapped[16];    // Bitmap of mapped characters
+};
+
+// Character map operations
+struct charmap charmap_load (const char *path, char mapping);
+struct charmap charmap_load_readable (const char *path, char mapping);
+void charmap_unload (struct charmap map);
+
+// Character map bulk operations
+struct charmap_meta charmap_load_all (const char *root, const char *meta);
+void charmap_unload_all (struct charmap_meta meta);
+
+const struct charmap * charmap_find (struct charmap_meta meta, char mapping);
+
+#endif
diff --git a/include/gfx.h b/include/gfx.h
new file mode 100644
index 0000000..6146a10
--- /dev/null
+++ b/include/gfx.h
@@ -0,0 +1,46 @@
+#ifndef GFX_H
+#define GFX_H
+
+#include "rect.h"
+#include "point.h"
+#include "bitmap.h"
+
+#include <stdbool.h>
+
+#define BUFFER_SIZE(screen) (((screen).dims.width) * ((screen).dims.height) * ((screen).dims.bpp))
+
+struct screen_dims {
+  int width;
+  int height;
+  int bpp;
+};
+
+struct screen {
+  struct screen_dims dims;
+  void *buffer;             // Double-buffer
+  void *realBuffer;         // Actual framebuffer
+  int buflen;
+  int fd;
+};
+
+struct triangle {
+  struct point p1;
+  struct point p2;
+  struct point p3;
+};
+
+void draw_pixel (void *pixelBuf, int pixel, int bpp);
+
+void draw_rect (struct screen screen, struct rect rect, int color);
+
+void draw_rect_b (struct screen screen, struct rect rect, int color);
+
+void draw_bitmap (struct screen screen, struct bitmap bitmap, struct point tl, int color, bool transparent_bg);
+
+void screen_commit (struct screen screen);
+
+struct screen open_screen ();
+
+void close_screen (struct screen screen);
+
+#endif
diff --git a/include/point.h b/include/point.h
new file mode 100644
index 0000000..e65621f
--- /dev/null
+++ b/include/point.h
@@ -0,0 +1,9 @@
+#ifndef POINT_H
+#define POINT_H
+
+struct point {
+  int x;
+  int y;
+};
+
+#endif
diff --git a/include/rect.h b/include/rect.h
new file mode 100644
index 0000000..e84649d
--- /dev/null
+++ b/include/rect.h
@@ -0,0 +1,50 @@
+#ifndef RECT_H
+#define RECT_H
+
+#include "point.h"
+
+#define RECT_WIDTH(r) (((r).br.x) - (r).tl.x)
+#define RECT_HEIGHT(r) (((r).br.y) - (r).tl.y)
+#define RECT_AREA(r) (((r).br.x - (r).tl.x) * ((r).br.y - (r).tl.y))
+#define RECT_OFFSET(r, _x, _y) {\
+  (r).tl.x += (_x);\
+  (r).br.x += (_x);\
+  (r).tl.y += (_y);\
+  (r).br.y += (_y);\
+}
+#define RECT_ALIGN_TL(r, _x, _y) {\
+  int _w = (_x);\
+  int _h = (_y);\
+  (r).br.x += (_w) - (r).tl.x;\
+  (r).br.y += (_h) - (r).tl.y;\
+  (r).tl.x = (_w);\
+  (r).tl.y = (_h);\
+}
+#define RECT_ALIGN_BR(r, _x, _y) {\
+  int _w = (_x);\
+  int _h = (_y);\
+  (r).tl.x += (_w) - (r).br.x;\
+  (r).tl.y += (_h) - (r).br.y;\
+  (r).br.x = (_w);\
+  (r).br.y = (_h);\
+}
+//#define RECT_CENTER(r, _x, _y) {\
+//  int _w = RECT_WIDTH((r));\
+//  int _h = RECT_HEIGHT((r));\
+//  RECT_ALIGN_TL((r), (_x) - (_w / 2), (_y) - (_h / 2));\
+//}
+#define RECT_CENTER(r, _x, _y) RECT_ALIGN_TL((r), (_x) - (RECT_WIDTH((r)) / 2), (_y) - (RECT_HEIGHT((r)) / 2))
+#define RECT_STRETCH(r, _x, _y) {\
+  (r).br.x += (_x);\
+  (r).tl.x -= (_x);\
+  (r).br.y += (_x);\
+  (r).tl.y -= (_x);\
+}
+#define RECT(w, h) { {0, 0}, {w ,h} }
+
+struct rect {
+  struct point tl;
+  struct point br;
+};
+
+#endif
diff --git a/res/0.bm b/res/0.bm
new file mode 100644
index 0000000000000000000000000000000000000000..41969ef1ba8042fe0638bbe982353042442cffa3
GIT binary patch
literal 32
ecmd;KU|<jcVg?5O9|tXY4jaJHoM!nC3=9Bg5(r2D

literal 0
HcmV?d00001

diff --git a/res/0.rbm b/res/0.rbm
new file mode 100644
index 0000000..e4b824a
--- /dev/null
+++ b/res/0.rbm
@@ -0,0 +1,16 @@
+000000000000
+000011110000
+000111111000
+001110011100
+001100001100
+001100001100
+001100001100
+001100001100
+001100001100
+001100001100
+001100001100
+001100001100
+001110011100
+000111111000
+000011110000
+000000000000
diff --git a/res/1.bm b/res/1.bm
new file mode 100644
index 0000000000000000000000000000000000000000..73a180def6b6d6931f9fdc78dddca5271184a508
GIT binary patch
literal 32
ecmd;KU|<jcVg?5GiUaI<3~UK-bin=(GXnrUodstA

literal 0
HcmV?d00001

diff --git a/res/1.rbm b/res/1.rbm
new file mode 100644
index 0000000..54478fc
--- /dev/null
+++ b/res/1.rbm
@@ -0,0 +1,16 @@
+000000000000
+000011100000
+000111100000
+001111100000
+011101100000
+000001100000
+000001100000
+000001100000
+000001100000
+000001100000
+000001100000
+000001100000
+000001100000
+001111111100
+001111111100
+000000000000
diff --git a/res/maps.meta b/res/maps.meta
new file mode 100644
index 0000000..42a293d
--- /dev/null
+++ b/res/maps.meta
@@ -0,0 +1,2 @@
+00.bm
+11.bm
diff --git a/src/bitmap.c b/src/bitmap.c
new file mode 100644
index 0000000..8a539ec
--- /dev/null
+++ b/src/bitmap.c
@@ -0,0 +1,202 @@
+#include "bitmap.h"
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#define BITS_TO_BYTES(bits) (((bits) / 8) + (((bits) & 7) ? 1 : 0))
+
+// Internal write function where integrity of bit is ensured (0 or 1)
+static void _write_bit (struct bitmap bitmap, size_t x, size_t y, char bit);
+
+
+struct bitmap bitmap_load_readable (const char *path) {
+  struct bitmap result = {
+    NULL,
+    0,
+    0
+  };
+  int fd = open (path, O_RDONLY);
+
+  if (fd >= 0) {
+    off_t len = lseek (fd, 0, SEEK_END);
+    lseek (fd, 0, SEEK_SET);
+
+    char *data = mmap (NULL, len, PROT_READ, MAP_SHARED, fd, 0);
+
+    if (data && data != MAP_FAILED) {
+      // Find width of bitmap
+      int width = -1;
+      for (int i = 0; i < len; ++i)
+        if (data[i] == '\n') {
+          width = i;
+          break;
+        }
+
+      // Compute height of bitmap
+      int height = (len + (data[len - 1] == '\n' ? 0 : 1)) / (width + 1);
+
+      result = bitmap_parse_readable (data, width, height);
+
+      munmap (data, len);
+      close (fd);
+    } else {
+      perror ("mmap");
+      close (fd);
+    }
+  } else {
+    perror ("open");
+  }
+
+  return result;
+}
+
+struct bitmap bitmap_load_raw (const char *path) {
+  struct bitmap result = {
+    NULL,
+    0,
+    0
+  };
+  int fd = open (path, O_RDONLY);
+
+  if (fd >= 0) {
+    off_t len = lseek (fd, 0, SEEK_END);
+    lseek (fd, 0, SEEK_SET);
+
+    if (len >= 8) {
+      void *data = mmap (NULL, len, PROT_READ, MAP_SHARED, fd, 0);
+      if (data && data != MAP_FAILED) {
+        memcpy (&result.width, data, 4);
+        memcpy (&result.height, data + 4, 4);
+        
+        size_t reportedLen = BITS_TO_BYTES(result.width * result.height);
+        if (reportedLen == len - 8) {
+          result.data = malloc (reportedLen);
+          memcpy (result.data, data + 8, reportedLen);
+        } else {
+          fputs ("Reported file size is incorrect", stderr);
+        }
+
+        munmap (data, len);
+        close (fd);
+      } else {
+        perror ("mmap");
+        close (fd);
+      }
+    } else {
+      fputs ("File is too small", stderr);
+      close (fd);
+    }
+  } else {
+    perror ("open");
+  }
+
+  return result;
+}
+
+struct bitmap bitmap_parse_readable (const char *data, int width, int height) {
+  struct bitmap result = {
+    NULL,
+    0,
+    0
+  };
+
+  int len = (width + 1) * height;
+
+  // Verify integrity of data
+  for (int i = 0; i < len; ++i) {
+    int cindex = (i + 1) % (width + 1);
+    if ((cindex && data[i] != '0' && data[i] != '1') || (!cindex && data[i] != '\n')) {
+      perror ("integrity");
+      return result;
+    }
+  }
+
+  int contentLen = BITS_TO_BYTES(width * height);
+  result.data = malloc (contentLen);
+  if (contentLen > 0)
+    ((char *)result.data)[contentLen - 1] = 0; // Zero-out possible stray/unused bits
+  result.width = width;
+  result.height = height;
+
+  for (int y = 0; y < height; ++y)
+    for (int x = 0; x < width; ++x)
+      _write_bit (result, x, y, data[x + (y * (width + 1))] - '0');
+
+  return result;
+}
+
+void bitmap_save_readable (const char *path, struct bitmap bitmap) {
+  int fd = open (path, O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR);
+
+  if (fd >= 0) {
+    char nl = '\n';
+    for (int y = 0; y < bitmap.height; ++y) {
+      for (int x = 0; x < bitmap.width; ++x) {
+        char bit = '0' + bitmap_read_bit (bitmap, x, y);
+        write (fd, &bit, 1);
+      }
+      write (fd, &nl, 1);
+    }
+
+    close (fd);
+  } else {
+    perror ("open");
+  }
+}
+
+void bitmap_save_raw (const char *path, struct bitmap bitmap) {
+  int fd = open (path, O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR);
+
+  if (fd >= 0) {
+    size_t len = BITS_TO_BYTES(bitmap.width * bitmap.height);
+    write (fd, (const char*)&bitmap.width, 4);
+    write (fd, (const char*)&bitmap.height, 4);
+    write (fd, bitmap.data, len);
+
+    close (fd);
+  } else {
+    perror ("open");
+  }
+}
+
+
+void bitmap_compress (const char *readable_path, const char *raw_path) {
+  struct bitmap bmp = bitmap_load_readable (readable_path);
+  if (bmp.data) {
+    bitmap_save_raw (raw_path, bmp);
+    bitmap_discard (bmp);
+  }
+}
+
+void bitmap_decompress (const char *raw_path, const char *readable_path) {
+  struct bitmap bmp = bitmap_load_raw (raw_path);
+  if (bmp.data) {
+    bitmap_save_readable (readable_path, bmp);
+    bitmap_discard (bmp);
+  }
+}
+
+char bitmap_read_bit (struct bitmap bitmap, size_t x, size_t y) {
+  int byteIdx = (x + (y * bitmap.width)) / 8;
+  int bitIdx = (x + (y * bitmap.width)) % 8;
+  return ((((char*)bitmap.data)[byteIdx]) >> bitIdx) & 1;
+}
+
+static void _write_bit (struct bitmap bitmap, size_t x, size_t y, char bit) {
+  int byteIdx = (x + (y * bitmap.width)) / 8;
+  int bitIdx = (x + (y * bitmap.width)) % 8;
+  ((char *)bitmap.data)[byteIdx] = (((char*)bitmap.data)[byteIdx] & ((1 << bitIdx) ^ 0xFF)) | (bit << bitIdx);
+}
+
+void bitmap_write_bit (struct bitmap bitmap, size_t x, size_t y, char bit) {
+  _write_bit (bitmap, x, y, bit ? 1 : 0);
+}
+
+void bitmap_discard (struct bitmap bitmap) {
+  free (bitmap.data);
+}
diff --git a/src/bmconv.c b/src/bmconv.c
new file mode 100644
index 0000000..ee4840a
--- /dev/null
+++ b/src/bmconv.c
@@ -0,0 +1,19 @@
+#include "bitmap.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+int main(int argc, char **argv) {
+  if (argc != 4) {
+    fprintf (stderr, "No args!\nReadable-to-Raw bitmap converter\nUsage:\n  %s [input file] [output file]\n", argv[0]);
+    exit (1);
+  }
+
+  if (strlen(argv[1]) == 1 && argv[1][0] == 'c') {
+    bitmap_compress (argv[2], argv[3]);
+  } else if (strlen(argv[1]) == 1 && argv[1][0] == 'd') {
+    bitmap_decompress (argv[2], argv[3]);
+  } else {
+    fprintf (stderr, "Unknown command '%s'\n", argv[1]);
+  }
+}
diff --git a/src/charmap.c b/src/charmap.c
new file mode 100644
index 0000000..65e8c1d
--- /dev/null
+++ b/src/charmap.c
@@ -0,0 +1,131 @@
+#include "charmap.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+
+// simple strcat with allocation
+static char *to_path_full (
+    const char *root,
+    size_t r_off,
+    size_t r_len,
+    const char *file_name,
+    size_t f_off,
+    size_t f_len
+) {
+  // Allocate string
+  char *path = malloc (r_len + f_len + 1);
+
+  // Copy substrings
+  memcpy (path, root + r_off, r_len);
+  memcpy (path + r_len, file_name + f_off, f_len);
+
+  // Null-terminate
+  path[r_len + f_len] = 0;
+
+  return path;
+}
+
+static char * to_path (const char *root, const char *file_name) {
+  return to_path_full (root, 0, strlen(root), file_name, 0, strlen(file_name));
+}
+
+static int index_of (const char *str, char find, int offset, int len) {
+  for (int i = offset; i < len; ++i)
+    if (str[i] == find)
+      return i;
+  return -1;
+}
+
+struct charmap charmap_load (const char *path, char mapping) {
+  struct charmap result = {
+    mapping,
+    bitmap_load_raw (path)
+  };
+
+  return result;
+}
+
+struct charmap charmap_load_readable (const char *path, char mapping) {
+  struct charmap result = {
+    mapping,
+    bitmap_load_readable (path)
+  };
+
+  return result;
+}
+
+void charmap_unload (struct charmap map) {
+  bitmap_discard (map.bitmap);
+}
+
+
+struct charmap_meta charmap_load_all (const char *root, const char *meta_file) {
+  struct charmap_meta meta;
+  meta.root = root;
+  meta.maps = NULL;
+  meta.map_count = 0;
+  memset (meta.mapped, 0, 16);
+
+  char *meta_path = to_path (root, meta_file);
+
+  int fd = open (meta_path, O_RDONLY);
+
+  // Path string is no longer needed
+  free (meta_path);
+
+  if (fd >= 0) {
+    off_t len = lseek (fd, 0, SEEK_END);
+    lseek (fd, 0, SEEK_SET);
+
+    char *data = mmap (NULL, len, PROT_READ, MAP_SHARED, fd, 0);
+    if (data && data != MAP_FAILED) {
+      for (int i = 0; i < len; ++i) {
+        char mapping = data[i];
+        int nl = index_of (data, '\n', i + 1, len - 1);
+        if (nl == -1)
+          nl = len - 1;
+        char *map_path = to_path_full (root, 0, strlen(root), data, i + 1, nl - i - 1);
+        printf ("Opening: '%s'\n", map_path);
+        struct charmap map = charmap_load (map_path, mapping);
+        free (map_path);
+        if (map.bitmap.data) {
+          ++meta.map_count;
+          struct charmap *new_maps = realloc (meta.maps, sizeof(struct charmap) * meta.map_count);
+
+          if (new_maps) {
+            meta.maps = new_maps;
+            memcpy (&new_maps[meta.map_count - 1], &map, sizeof(struct charmap));
+
+            // Mark character as mapped
+            meta.mapped[mapping >> 3] |= 1 << (mapping & 7);
+          } else {
+            --meta.map_count;
+            perror ("realloc");
+          }
+        }
+
+        i = nl;
+      }
+    } else {
+      perror ("mmap");
+    }
+  } else {
+    perror ("open");
+  }
+  return meta;
+}
+
+const struct charmap * charmap_find (struct charmap_meta meta, char mapping) {
+  if ((meta.mapped[mapping >> 3] >> (mapping & 7)) & 1) {
+    for (int i = 0; i < meta.map_count; ++i)
+      if (meta.maps[i].c == mapping)
+        return &meta.maps[i];
+  }
+  return NULL;
+}
diff --git a/src/draw.c b/src/draw.c
new file mode 100644
index 0000000..031d1a8
--- /dev/null
+++ b/src/draw.c
@@ -0,0 +1,91 @@
+#include "gfx.h"
+#include "rect.h"
+#include "bitmap.h"
+#include "charmap.h"
+#include <string.h>
+#include <stdbool.h>
+#include <stdio.h>
+
+#include <unistd.h>
+
+
+//void hide_tty0_cursor() {
+//  FILE *file = fopen ("/dev/tty0", "w");
+//  if (file != NULL) {
+//    fputs ("\e[?25l", file);
+//
+//    fclose (file);
+//  }
+//}
+//
+//void show_tty0_cursor() {
+//  FILE *file = fopen ("/dev/tty0", "w");
+//  if (file != NULL) {
+//    fputs ("\e[?25h", file);
+//
+//    fclose (file);
+//  }
+//}
+
+struct rect center_offset (struct rect to_center, struct point center_on) {
+  struct rect result = {
+    { center_on.x - ((to_center.br.x - to_center.tl.x) / 2), center_on.y - ((to_center.br.y - to_center.tl.y) / 2) },
+    { center_on.x + ((to_center.br.x - to_center.tl.y) / 2), center_on.y + ((to_center.br.y - to_center.tl.y) / 2) }
+  };
+
+  return result;
+}
+
+int main()
+{
+  struct screen screen = open_screen ();
+
+  if (screen.buffer == NULL)
+    return 1;
+
+
+  struct charmap_meta meta = charmap_load_all ("res/", "maps.meta");
+  const struct charmap *map_0 = charmap_find (meta, '0');
+  const struct charmap *map_1 = charmap_find (meta, '1');
+
+  // Virtual rectangle representing character
+  struct rect rect_0_virt = RECT(map_0->bitmap.width, map_0->bitmap.height);
+  RECT_CENTER(rect_0_virt, screen.dims.width / 2, screen.dims.height / 2);
+
+
+  // Make a 100x100 rectangle and center it in on the screen
+  struct rect window = RECT(100, 100);
+  RECT_CENTER(window, screen.dims.width / 2, screen.dims.height / 2);
+  
+
+  // Clear screen
+  memset (screen.buffer, 0, BUFFER_SIZE(screen));
+
+  // Draw stuff 
+  for (int j = 1; j <= 10; ++j) {
+    int stretch = (j % 2) - ((j % 2) ^ 1);
+    for (int i = 0; i < 200; ++i) {
+      draw_rect_b (screen, window, 0x00);
+      RECT_STRETCH (window, stretch, stretch);
+      draw_rect_b (screen, window, 0xFF);
+      draw_bitmap (screen, (stretch - 1 ? map_0 : map_1)->bitmap, rect_0_virt.tl, 0x0000, true);
+
+      screen_commit (screen);
+      usleep (5000);
+    }
+  }
+
+  struct point tl = {100, 100};
+
+  for (int i = 0; i < 10; ++i) {
+    draw_bitmap (screen, map_0->bitmap, tl, 0xFFFF, true);
+    tl.x += (int)(map_0->bitmap.width * 1.5f);
+  }
+
+  screen_commit (screen);
+
+
+  close_screen (screen);
+
+  return 0;
+}
diff --git a/src/gfx.c b/src/gfx.c
new file mode 100644
index 0000000..370eb09
--- /dev/null
+++ b/src/gfx.c
@@ -0,0 +1,150 @@
+#include "gfx.h"
+
+#include <string.h>
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+
+#include <linux/fb.h>
+
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+int to_index (int x, int y, struct screen_dims dims) {
+  return (x * dims.bpp) + (y * dims.width * dims.bpp);
+}
+
+void draw_pixel (void *pixelBuf, int pixel, int bpp) {
+  memcpy (pixelBuf, &pixel, bpp);
+}
+
+void draw_rect (struct screen screen, struct rect rect, int color) {
+  for (int y = rect.tl.y; y < rect.br.y; ++y)
+    for (int x = rect.tl.x; x < rect.br.x; ++x)
+      draw_pixel (screen.buffer + to_index (x, y, screen.dims), color, screen.dims.bpp);
+}
+
+void draw_rect_b (struct screen screen, struct rect rect, int color) {
+  for (int y = rect.tl.y; y < rect.br.y; ++y)
+    memset (screen.buffer + to_index (rect.tl.x, y, screen.dims), color, (rect.br.x - rect.tl.x) * screen.dims.bpp);
+}
+
+void draw_bitmap (struct screen screen, struct bitmap bitmap, struct point tl, int color, bool transparent_bg) {
+
+  for (int y = 0; y < bitmap.height; ++y)
+    for (int x = 0; x < bitmap.width; ++x) {
+      char bit = bitmap_read_bit (bitmap, x, y);
+      if (bit)
+        memcpy (screen.buffer + to_index (x + tl.x, y + tl.y, screen.dims), &color, screen.dims.bpp);
+      else if (!transparent_bg)
+        memset (screen.buffer + to_index (x + tl.x, y + tl.y, screen.dims), 0, screen.dims.bpp);
+    }
+}
+
+struct screen open_screen () {
+  struct fb_var_screeninfo screen_info;
+  struct fb_fix_screeninfo fixed_info;
+  char *buffer = NULL;
+  size_t buflen;
+  int fd = -1;
+
+  fd = open("/dev/fb0", O_RDWR);
+  if (fd >= 0)
+  {
+    if (!ioctl(fd, FBIOGET_VSCREENINFO, &screen_info) &&
+        !ioctl(fd, FBIOGET_FSCREENINFO, &fixed_info))
+    {
+      buflen = screen_info.yres_virtual * fixed_info.line_length;
+      buffer = mmap(NULL,
+                    buflen,
+                    PROT_READ|PROT_WRITE,
+                    MAP_SHARED,
+                    fd,
+                    0);
+      if (buffer != MAP_FAILED)
+      {
+        void *doubleBuffer = mmap (NULL,
+                                  buflen,
+                                  PROT_READ|PROT_WRITE,
+                                  MAP_PRIVATE|MAP_ANONYMOUS,
+                                  -1,
+                                  0);
+
+        if (doubleBuffer != MAP_FAILED) {
+          int bytesPerPixel = screen_info.bits_per_pixel / 8;
+
+          struct screen_dims dims = {
+            fixed_info.line_length / bytesPerPixel,
+            screen_info.yres_virtual,
+            bytesPerPixel
+          };
+
+
+          struct screen result = {
+            dims,
+            doubleBuffer,
+            buffer,
+            buflen,
+            fd
+          };
+
+          return result;
+        }
+        else {
+          perror ("mmap dbuf");
+        }
+
+        munmap (buffer, buflen);
+      }
+      else
+      {
+         perror("mmap");
+      }
+    }
+    else
+    {
+       perror("ioctl");
+    }
+
+    close (fd);
+  }
+  else
+  {
+     perror("open");
+  }
+
+  struct screen scr = {
+    { 0, 0, 0 },
+    NULL,
+    NULL,
+    0,
+    -1
+  };
+
+  return scr;
+}
+
+void screen_commit (struct screen screen) {
+  memcpy (screen.realBuffer, screen.buffer, screen.buflen);
+}
+
+void close_screen (struct screen screen) {
+  if (screen.buffer && screen.buffer != MAP_FAILED)
+    munmap (
+      screen.buffer,
+      screen.buflen
+    );
+
+  if (screen.realBuffer && screen.realBuffer != MAP_FAILED)
+    munmap (
+      screen.realBuffer,
+      screen.buflen
+    );
+
+  if (screen.fd >= 0)
+    close (screen.fd);
+}