Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions kernel/core/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,12 @@ static void init_subsystems(void *dtb) {
ramfs_create_file_bytes("Pictures/test.png", 0644, bootstrap_test_png,
bootstrap_test_png_len);

/* Add BMP test image to Pictures */
extern const unsigned char bootstrap_test_bmp[];
extern const unsigned int bootstrap_test_bmp_len;
ramfs_create_file_bytes("Pictures/test.bmp", 0644, bootstrap_test_bmp,
bootstrap_test_bmp_len);

/* Mount proc, sys, dev (placeholders) */
printk(KERN_INFO " Mounting procfs...\n");

Expand Down
5 changes: 3 additions & 2 deletions kernel/gui/terminal.c
Original file line number Diff line number Diff line change
Expand Up @@ -667,8 +667,9 @@ void term_execute_command(struct terminal *term, const char *cmd) {
return;
}

if (!str_ends_with_ci(path, ".jpg") && !str_ends_with_ci(path, ".jpeg")) {
term_puts(term, "view: only .jpg/.jpeg supported\n");
if (!str_ends_with_ci(path, ".jpg") && !str_ends_with_ci(path, ".jpeg") &&
!str_ends_with_ci(path, ".png") && !str_ends_with_ci(path, ".bmp")) {
term_puts(term, "view: only .jpg/.jpeg/.png/.bmp supported\n");
return;
}

Expand Down
15 changes: 13 additions & 2 deletions kernel/gui/window.c
Original file line number Diff line number Diff line change
Expand Up @@ -1047,7 +1047,8 @@ static int fm_render_callback(void *ctx, const char *name, int len,
bmp = icon_notepad;
color = 0xFFFFFF;
} else if (str_ends_with_ci(name, ".jpg") ||
str_ends_with_ci(name, ".jpeg")) {
str_ends_with_ci(name, ".jpeg") ||
str_ends_with_ci(name, ".bmp")) {
color = 0xF9E2AF;
} else if (str_ends_with_ci(name, ".mp3")) {
color = 0xA6E3A1;
Expand Down Expand Up @@ -1280,7 +1281,8 @@ static void fm_on_mouse(struct window *win, int x, int y, int buttons) {
gui_open_notepad(full_path);
} else if (str_ends_with_ci(st->selected, ".jpg") ||
str_ends_with_ci(st->selected, ".jpeg") ||
str_ends_with_ci(st->selected, ".png")) {
str_ends_with_ci(st->selected, ".png") ||
str_ends_with_ci(st->selected, ".bmp")) {
gui_open_image_viewer(full_path);
} else if (str_ends_with_ci(st->selected, ".mp3")) {
gui_play_mp3_file(full_path);
Expand Down Expand Up @@ -1423,6 +1425,11 @@ void gui_open_image_viewer(const char *path) {
if (decode_ret != 0) {
printk("Image Viewer: PNG decode failed\n");
}
} else if (size >= 2 && data[0] == 'B' && data[1] == 'M') {
decode_ret = media_decode_bmp(data, size, &g_imgview.image);
if (decode_ret != 0) {
printk("Image Viewer: BMP decode failed\n");
}
} else {
/* Assume JPEG */
decode_ret = media_decode_jpeg(data, size, &g_imgview.image);
Expand Down Expand Up @@ -4413,6 +4420,8 @@ static void image_viewer_load_bootstrap(int index) {
if (len >= 4 && data[0] == 0x89 && data[1] == 'P' && data[2] == 'N' &&
data[3] == 'G') {
ret = media_decode_png(data, len, &g_imgview.image);
} else if (len >= 2 && data[0] == 'B' && data[1] == 'M') {
ret = media_decode_bmp(data, len, &g_imgview.image);
} else {
ret = media_decode_jpeg(data, len, &g_imgview.image);
}
Expand Down Expand Up @@ -4475,6 +4484,8 @@ static void image_viewer_load_from_folder(int index) {
if (size >= 4 && data[0] == 0x89 && data[1] == 'P' && data[2] == 'N' &&
data[3] == 'G') {
ret = media_decode_png(data, size, &g_imgview.image);
} else if (size >= 2 && data[0] == 'B' && data[1] == 'M') {
ret = media_decode_bmp(data, size, &g_imgview.image);
} else {
ret = media_decode_jpeg(data, size, &g_imgview.image);
}
Expand Down
3 changes: 3 additions & 0 deletions kernel/include/media/media.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,7 @@ void media_free_audio(media_audio_t *audio);

int media_decode_png(const uint8_t *data, size_t size, media_image_t *out);

/* Uncompressed 24/32bpp BMP (BITMAPINFOHEADER and V4/V5 variants) */
int media_decode_bmp(const uint8_t *data, size_t size, media_image_t *out);

#endif /* _KERNEL_MEDIA_H */
Binary file added kernel/media/bootstrap_images/test.bmp
Binary file not shown.
5,634 changes: 5,634 additions & 0 deletions kernel/media/bootstrap_test_bmp.c

Large diffs are not rendered by default.

87 changes: 87 additions & 0 deletions kernel/media/media.c
Original file line number Diff line number Diff line change
Expand Up @@ -300,3 +300,90 @@ int media_decode_png(const uint8_t *data, size_t size, media_image_t *out) {
out->pixels = pixels;
return 0;
}

/* --------------------------------------------------------------------- */
/* BMP decoding (uncompressed 24/32bpp) */
/* --------------------------------------------------------------------- */

static uint32_t bmp_read_le32(const uint8_t *p) {
return (uint32_t)p[0] | ((uint32_t)p[1] << 8) | ((uint32_t)p[2] << 16) |
((uint32_t)p[3] << 24);
}

static uint16_t bmp_read_le16(const uint8_t *p) {
return (uint16_t)((uint32_t)p[0] | ((uint32_t)p[1] << 8));
}

int media_decode_bmp(const uint8_t *data, size_t size, media_image_t *out) {
if (!data || !out || size < 54 || data[0] != 'B' || data[1] != 'M')
return -EINVAL;

uint32_t pixel_offset = bmp_read_le32(data + 10);
uint32_t dib_size = bmp_read_le32(data + 14);

/* BITMAPINFOHEADER (40) or the larger V4/V5 headers that extend it */
if (dib_size < 40 || (size_t)14 + dib_size > size) {
printk(KERN_ERR "BMP: unsupported DIB header (%u bytes)\n", dib_size);
return -EINVAL;
}

int32_t width = (int32_t)bmp_read_le32(data + 18);
int32_t height = (int32_t)bmp_read_le32(data + 22);
uint16_t planes = bmp_read_le16(data + 26);
uint16_t bpp = bmp_read_le16(data + 28);
uint32_t compression = bmp_read_le32(data + 30);

int top_down = 0;
if (height < 0) {
top_down = 1;
height = -height;
}

/* BI_RGB only; BI_BITFIELDS (3) is accepted for 32bpp since the
* standard BGRA masks match our channel extraction anyway */
if (planes != 1 || (bpp != 24 && bpp != 32) ||
(compression != 0 && !(compression == 3 && bpp == 32))) {
printk(KERN_ERR "BMP: unsupported format (%u bpp, compression %u)\n", bpp,
compression);
return -EINVAL;
}

if (width <= 0 || height <= 0 ||
(size_t)width > SIZE_MAX / (size_t)height) {
printk(KERN_ERR "BMP: bad dimensions\n");
return -EINVAL;
}

size_t pixel_count = (size_t)width * (size_t)height;
if (pixel_count > 16 * 1024 * 1024) {
printk(KERN_ERR "BMP: image too large (%zu pixels)\n", pixel_count);
return -EINVAL;
}

size_t bytes_per_px = bpp / 8;
size_t stride = ((size_t)width * bytes_per_px + 3) & ~(size_t)3;
if (pixel_offset > size || stride > (size - pixel_offset) / (size_t)height) {
printk(KERN_ERR "BMP: pixel data out of bounds\n");
return -EINVAL;
}

uint32_t *pixels =
(uint32_t *)kmalloc(pixel_count * sizeof(uint32_t), GFP_KERNEL);
if (!pixels)
return -ENOMEM;

for (int32_t y = 0; y < height; y++) {
int32_t src_y = top_down ? y : height - 1 - y;
const uint8_t *row = data + pixel_offset + (size_t)src_y * stride;
for (int32_t x = 0; x < width; x++) {
const uint8_t *px = row + (size_t)x * bytes_per_px;
pixels[(size_t)y * width + x] =
((uint32_t)px[2] << 16) | ((uint32_t)px[1] << 8) | px[0];
}
}

out->width = (uint32_t)width;
out->height = (uint32_t)height;
out->pixels = pixels;
return 0;
}
10 changes: 5 additions & 5 deletions kernel/syscall/syscall.c
Original file line number Diff line number Diff line change
Expand Up @@ -762,7 +762,7 @@ void handle_sync_exception(struct pt_regs *regs) {
case 0x20: /* Instruction abort from lower EL */
case 0x21: /* Instruction abort from same EL */
printk(KERN_EMERG "Instruction abort at PC=0x%llx\n",
(unsigned long long)arch_context_get_pc(regs));
(unsigned long long)regs->pc);
panic("Instruction abort");
break;

Expand All @@ -773,28 +773,28 @@ void handle_sync_exception(struct pt_regs *regs) {
uint64_t far;
asm volatile("mrs %0, far_el1" : "=r"(far));
printk(KERN_EMERG "Data abort at PC=0x%llx, FAR=0x%llx\n",
(unsigned long long)arch_context_get_pc(regs),
(unsigned long long)regs->pc,
(unsigned long long)far);
#elif defined(ARCH_X86_64) || defined(ARCH_X86)
uint64_t cr2;
asm volatile("mov %%cr2, %0" : "=r"(cr2));
printk(KERN_EMERG "Page fault at PC=0x%llx, CR2=0x%llx\n",
(unsigned long long)arch_context_get_pc(regs),
(unsigned long long)regs->pc,
(unsigned long long)cr2);
#endif
panic("Data abort");
} break;

case 0x00: /* Unknown reason */
printk(KERN_EMERG "Unknown exception at PC=0x%llx\n",
(unsigned long long)arch_context_get_pc(regs));
(unsigned long long)regs->pc);
panic("Unknown exception");
break;

default:
printk(KERN_EMERG "Unhandled exception class 0x%x, ISS=0x%x\n", ec, iss);
printk(KERN_EMERG "PC=0x%llx\n",
(unsigned long long)arch_context_get_pc(regs));
(unsigned long long)regs->pc);
panic("Unhandled exception");
break;
}
Expand Down