| 1 | #include <stdint.h> |
| 2 | #include <stddef.h> |
| 3 | #include <lib/image.h> |
| 4 | #include <lib/config.h> |
| 5 | #include <lib/misc.h> |
| 6 | #include <mm/pmm.h> |
| 7 | #include <lib/qoi.h> |
| 8 | #include <lib/stb_image.h> |
| 9 | |
| 10 | void image_make_centered(struct image *image, int frame_x_size, int frame_y_size, uint32_t back_colour) { |
| 11 | image->type = IMAGE_CENTERED; |
| 12 | |
| 13 | image->x_displacement = (int64_t)frame_x_size / 2 - (int64_t)image->x_size / 2; |
| 14 | image->y_displacement = (int64_t)frame_y_size / 2 - (int64_t)image->y_size / 2; |
| 15 | image->back_colour = back_colour; |
| 16 | } |
| 17 | |
| 18 | |
| 19 | void image_make_stretched(struct image *image, int new_x_size, int new_y_size) { |
| 20 | image->type = IMAGE_STRETCHED; |
| 21 | |
| 22 | image->x_size = new_x_size; |
| 23 | image->y_size = new_y_size; |
| 24 | } |
| 25 | |
| 26 | static void free_image_data(struct image *image) { |
| 27 | if (image->isQoi) { |
| 28 | qoi_free(image->img); |
| 29 | } else { |
| 30 | stbi_image_free(image->img); |
| 31 | } |
| 32 | } |
| 33 | |
| 34 | struct image *image_open(struct file_handle *file) { |
| 35 | struct image *image = ext_mem_alloc(sizeof(struct image)); |
| 36 | |
| 37 | image->type = IMAGE_TILED; |
| 38 | |
| 39 | void *src = ext_mem_alloc(file->size); |
| 40 | |
| 41 | fread(file, src, 0, file->size); |
| 42 | |
| 43 | int x = 0, y = 0; |
| 44 | image->isQoi = file->size >= 4 |
| 45 | && ((const uint8_t *)src)[0] == 'q' |
| 46 | && ((const uint8_t *)src)[1] == 'o' |
| 47 | && ((const uint8_t *)src)[2] == 'i' |
| 48 | && ((const uint8_t *)src)[3] == 'f'; |
| 49 | |
| 50 | if (image->isQoi) { |
| 51 | image->img = qoi_decode(src, file->size, &x, &y); |
| 52 | } else { |
| 53 | int bpp; |
| 54 | image->img = stbi_load_from_memory(src, file->size, &x, &y, &bpp, 4); |
| 55 | } |
| 56 | |
| 57 | pmm_free(src, file->size); |
| 58 | |
| 59 | if (image->img == NULL || x == 0 || y == 0) { |
| 60 | free_image_data(image); |
| 61 | pmm_free(image, sizeof(struct image)); |
| 62 | return NULL; |
| 63 | } |
| 64 | |
| 65 | // stb_image returns RGBA bytes (little-endian uint32 ABGR); convert to |
| 66 | // the framebuffer-native XRGB layout. The QOI decoder already produces |
| 67 | // XRGB, so this step is skipped on that path. |
| 68 | if (!image->isQoi) { |
| 69 | uint32_t *pptr = (void *)image->img; |
| 70 | size_t pixel_count = CHECKED_MUL((size_t)x, (size_t)y, |
| 71 | ({ free_image_data(image); pmm_free(image, sizeof(struct image)); return NULL; })); |
| 72 | for (size_t i = 0; i < pixel_count; i++) { |
| 73 | pptr[i] = (pptr[i] & 0x0000ff00) | ((pptr[i] & 0x00ff0000) >> 16) | ((pptr[i] & 0x000000ff) << 16); |
| 74 | } |
| 75 | } |
| 76 | |
| 77 | image->x_size = x; |
| 78 | image->y_size = y; |
| 79 | image->pitch = (int)CHECKED_MUL((size_t)x, 4, |
| 80 | ({ free_image_data(image); pmm_free(image, sizeof(struct image)); return NULL; })); |
| 81 | image->bpp = 32; |
| 82 | image->img_width = x; |
| 83 | image->img_height = y; |
| 84 | |
| 85 | return image; |
| 86 | } |
| 87 | |
| 88 | void image_close(struct image *image) { |
| 89 | free_image_data(image); |
| 90 | pmm_free(image, sizeof(struct image)); |
| 91 | } |