shell/imgview.c (view raw)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214
#include "cedos.h"
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "assert.h"
#include <stdint.h>
#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);
}