diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bbd52bd --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +bmconv +draw 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 + +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 + +#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 0000000..41969ef Binary files /dev/null and b/res/0.bm differ 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 0000000..73a180d Binary files /dev/null and b/res/1.bm differ 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 +#include +#include +#include +#include +#include +#include + +#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 +#include +#include + +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 +#include +#include +#include +#include +#include +#include + + +// 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 +#include +#include + +#include + + +//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 + +#include +#include +#include + +#include + +#include +#include + +#include +#include + +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); +}