#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); }