#include "cedos.h" #include "stdio.h" #include "stdlib.h" #include "string.h" #include "assert.h" #include #define GMODE_TEXT 0x02 #define GMODE_VIDEO 0x13 #define VGA_BUFFER (uint8_t*)(0xA0000) #define VGA_MODE_13_WIDTH 320 #define VGA_MODE_13_HEIGHT 200 #define MAX(x, y) (x > y ? x : y) #define MIN(x, y) (x < y ? x : y) typedef struct __attribute__((packed)) { uint16_t signature; uint32_t file_size; uint16_t reserved_1; uint16_t reserved_2; uint32_t offset_pixelarray; } BMP_FILE_HEADER; /* Compression methods */ #define BI_RGB 0 // uncompressed #define BI_RLE8 1 // RLE 8-bit/pixel #define BI_RLE4 2 // RLE 4-bit/pixel /* ... */ typedef struct { uint32_t DIB_header_size; uint32_t image_width; uint32_t image_height; uint16_t num_color_planes; uint16_t bits_per_pixel; uint32_t compression_method; uint32_t raw_data_size; /* ... */ } DIB_HEADER; uint32_t convert_endian(uint32_t value) { uint32_t res = 0; return value; for (int i = 0; i < 2; i++) { uint8_t c = value & 0x0000FFFF; value = value >> 16; res = res << 16; res |= c; } return res; } typedef struct { char* buffer; int width; int height; int x; int y; } image_object; static inline void imgbuf_newline(image_object* imgobj) { imgobj->x = 0; imgobj->y = MIN(imgobj->y + 1, imgobj->height - 1); } static inline void imgbuf_write_single(image_object* imgobj, uint8_t value) { if (imgobj->x >= imgobj->width) { imgbuf_newline(imgobj); } imgobj->buffer[imgobj->y * imgobj->width + imgobj->x] = value; imgobj->x++; } static inline void imgbuf_offset(image_object* imgobj, uint8_t xoff, uint8_t yoff) { imgobj->x += xoff; imgobj->y += yoff; } static void decode_rle(char* rle_data, int rle_size, char* img_buf, int width, int height) { image_object imgobj = { .buffer = img_buf, .width = width, .height = height, .x = 0, .y = 0 }; int data_i = 0; while (1) { if (data_i >= rle_size) { break; } const int count = rle_data[data_i++]; const int value = rle_data[data_i++]; if (count == 0) { /* escape sequence */ switch (value) { case 0: /* end of line */ imgbuf_newline(&imgobj); break; case 1: /* end of bitmap; ignore for now */ break; case 2: /* delta */ uint8_t xoff = rle_data[data_i++]; uint8_t yoff = rle_data[data_i++]; imgbuf_offset(&imgobj, xoff, yoff); break; default: /* non-RLE subsequence */ for (int i = 0; i < value; i++) { imgbuf_write_single(&imgobj, rle_data[data_i++]); } data_i += value % 2; break; } } else { /* regular RLE content */ for (int j = 0; j < count; j++) { imgbuf_write_single(&imgobj, value); } } } } void main(char *args) { // open image file FILE *fd = fopen(args, "r"); if (fd == NULL) { printf("Could not find file: %s\n", args); return; } BMP_FILE_HEADER bmp_header; DIB_HEADER dib_header; int size = 0; size = fread(&bmp_header, sizeof(bmp_header), 1, fd); if (size != sizeof(bmp_header)) { printf("Error while reading BMP header\n"); return; } size = fread(&dib_header, sizeof(dib_header), 1, fd); if (size != sizeof(dib_header)) { printf("Error while reading DIB header\n"); return; } const int offset = bmp_header.offset_pixelarray; const int width = dib_header.image_width; const int height = dib_header.image_height; assert(dib_header.bits_per_pixel == 8); assert(dib_header.num_color_planes == 1); const int data_size = width * height * (dib_header.bits_per_pixel / 8); char *imgbuf = malloc(data_size); memset(imgbuf, 0, data_size); fseek(fd, offset, SEEK_SET); if (dib_header.compression_method == BI_RLE8) { const int rle_size = dib_header.raw_data_size; char *databuf = malloc(rle_size); size = fread(databuf, sizeof(uint8_t), rle_size, fd); /* TODO: check returned size */ decode_rle(databuf, size, imgbuf, width, height); free(databuf); } else if (dib_header.compression_method == BI_RGB) { size = fread(imgbuf, sizeof(uint8_t), data_size, fd); /* TODO: check returned size */ } // switch video mode and display image graphics_set_mode(GMODE_VIDEO); uint8_t *gbuff = VGA_BUFFER; for (int y = 0; y < MIN(height, VGA_MODE_13_HEIGHT); y++) { for (int x = 0; x < MIN(width, VGA_MODE_13_WIDTH); x++) { int g_i = (VGA_MODE_13_HEIGHT - 1 - y) * VGA_MODE_13_WIDTH + x; gbuff[g_i] = imgbuf[y * width + x]; } } free(imgbuf); while (1) { char c = getchar(); if (c == 0x1B || c == 'q') { break; } } graphics_set_mode(GMODE_TEXT); }