decoder_BMP.c (6864B)
1 #include "decoder_BMP.h" 2 #include <stdlib.h> 3 #include <string.h> 4 #include <lauxlib.h> 5 6 /* Read little-endian values */ 7 static inline uint16_t read_le16(const uint8_t* data) { 8 return data[0] | (data[1] << 8); 9 } 10 11 static inline uint32_t read_le32(const uint8_t* data) { 12 return data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24); 13 } 14 15 /* Decode BMP from memory */ 16 image_t* bmp_decode(const uint8_t* data, uint32_t data_size) { 17 if (!data || data_size < 54) return NULL; // Minimum BMP size 18 19 /* Read file header */ 20 bmp_file_header_t file_header; 21 file_header.signature = read_le16(data); 22 file_header.file_size = read_le32(data + 2); 23 file_header.reserved1 = read_le16(data + 6); 24 file_header.reserved2 = read_le16(data + 8); 25 file_header.data_offset = read_le32(data + 10); 26 27 /* Check BMP signature */ 28 if (file_header.signature != 0x4D42) { // "BM" 29 return NULL; 30 } 31 32 /* Read info header */ 33 bmp_info_header_t info_header; 34 const uint8_t* info_data = data + 14; 35 info_header.header_size = read_le32(info_data); 36 info_header.width = (int32_t)read_le32(info_data + 4); 37 info_header.height = (int32_t)read_le32(info_data + 8); 38 info_header.planes = read_le16(info_data + 12); 39 info_header.bpp = read_le16(info_data + 14); 40 info_header.compression = read_le32(info_data + 16); 41 42 /* Validate */ 43 if (info_header.width <= 0 || info_header.height == 0) return NULL; 44 if (info_header.compression != BMP_BI_RGB) { 45 /* Only support uncompressed BMPs for now */ 46 return NULL; 47 } 48 49 /* Determine if bottom-up or top-down */ 50 int is_top_down = (info_header.height < 0); 51 uint32_t height = is_top_down ? -info_header.height : info_header.height; 52 uint32_t width = info_header.width; 53 54 /* Only support 24-bit and 32-bit BMPs */ 55 if (info_header.bpp != 24 && info_header.bpp != 32) { 56 return NULL; 57 } 58 59 /* Create image */ 60 image_t* img = image_create(width, height, 24); // Always output as 24-bit RGB 61 if (!img) return NULL; 62 63 /* Calculate row size (padded to 4-byte boundary) */ 64 uint32_t bytes_per_pixel = info_header.bpp / 8; 65 uint32_t row_size = ((width * bytes_per_pixel + 3) / 4) * 4; 66 67 /* Check if we have enough data */ 68 if (file_header.data_offset + row_size * height > data_size) { 69 image_destroy(img); 70 return NULL; 71 } 72 73 /* Read pixel data */ 74 const uint8_t* pixel_data = data + file_header.data_offset; 75 76 for (uint32_t y = 0; y < height; y++) { 77 /* BMP is stored bottom-up by default */ 78 uint32_t dest_y = is_top_down ? y : (height - 1 - y); 79 const uint8_t* row = pixel_data + y * row_size; 80 81 for (uint32_t x = 0; x < width; x++) { 82 const uint8_t* pixel = row + x * bytes_per_pixel; 83 84 /* BMP stores as BGR(A), we need RGB */ 85 uint8_t b = pixel[0]; 86 uint8_t g = pixel[1]; 87 uint8_t r = pixel[2]; 88 uint8_t a = (info_header.bpp == 32) ? pixel[3] : 255; 89 90 image_set_pixel(img, x, dest_y, r, g, b, a); 91 } 92 } 93 94 return img; 95 } 96 97 /* Load BMP from file (stub - would need filesystem) */ 98 image_t* bmp_load_file(const char* filename) { 99 (void)filename; /* Unused parameter */ 100 /* TODO: Implement file loading once filesystem is available */ 101 /* For now, this would need to be called with data already loaded into memory */ 102 return NULL; 103 } 104 105 /* Encode image to BMP format */ 106 int bmp_encode(image_t* img, uint8_t** out_data, uint32_t* out_size) { 107 if (!img || !out_data || !out_size) return -1; 108 109 /* Calculate sizes */ 110 uint32_t bytes_per_pixel = 3; // 24-bit output 111 uint32_t row_size = ((img->width * bytes_per_pixel + 3) / 4) * 4; // Padded to 4 bytes 112 uint32_t pixel_data_size = row_size * img->height; 113 uint32_t file_size = 54 + pixel_data_size; // Header + pixel data 114 115 /* Allocate output buffer */ 116 uint8_t* buffer = (uint8_t*)malloc(file_size); 117 if (!buffer) return -1; 118 119 /* Fill file header */ 120 buffer[0] = 'B'; 121 buffer[1] = 'M'; 122 *(uint32_t*)(buffer + 2) = file_size; 123 *(uint32_t*)(buffer + 6) = 0; // Reserved 124 *(uint32_t*)(buffer + 10) = 54; // Offset to pixel data 125 126 /* Fill info header */ 127 *(uint32_t*)(buffer + 14) = 40; // Header size 128 *(int32_t*)(buffer + 18) = img->width; 129 *(int32_t*)(buffer + 22) = img->height; // Positive = bottom-up 130 *(uint16_t*)(buffer + 26) = 1; // Planes 131 *(uint16_t*)(buffer + 28) = 24; // BPP 132 *(uint32_t*)(buffer + 30) = 0; // Compression (none) 133 *(uint32_t*)(buffer + 34) = pixel_data_size; 134 *(uint32_t*)(buffer + 38) = 2835; // X pixels/meter 135 *(uint32_t*)(buffer + 42) = 2835; // Y pixels/meter 136 *(uint32_t*)(buffer + 46) = 0; // Colors used 137 *(uint32_t*)(buffer + 50) = 0; // Important colors 138 139 /* Fill pixel data (bottom-up, BGR format) */ 140 uint8_t* pixel_data = buffer + 54; 141 142 for (uint32_t y = 0; y < img->height; y++) { 143 uint32_t src_y = img->height - 1 - y; // Flip vertically 144 uint8_t* row = pixel_data + y * row_size; 145 146 for (uint32_t x = 0; x < img->width; x++) { 147 uint8_t r, g, b, a; 148 image_get_pixel(img, x, src_y, &r, &g, &b, &a); 149 150 /* Write as BGR */ 151 row[x * 3 + 0] = b; 152 row[x * 3 + 1] = g; 153 row[x * 3 + 2] = r; 154 } 155 156 /* Pad row to 4-byte boundary */ 157 uint32_t padding = row_size - (img->width * 3); 158 for (uint32_t i = 0; i < padding; i++) { 159 row[img->width * 3 + i] = 0; 160 } 161 } 162 163 *out_data = buffer; 164 *out_size = file_size; 165 return 0; 166 } 167 168 /* Save BMP to file (stub - would need filesystem) */ 169 int bmp_save_file(image_t* img, const char* filename) { 170 (void)img; /* Unused parameter */ 171 (void)filename; /* Unused parameter */ 172 /* TODO: Implement file saving once filesystem is available */ 173 return -1; 174 } 175 176 /* Lua binding: Load BMP from memory */ 177 int lua_bmp_load(lua_State* L) { 178 size_t data_size; 179 const uint8_t* data = (const uint8_t*)luaL_checklstring(L, 1, &data_size); 180 181 image_t* img = bmp_decode(data, data_size); 182 183 if (img) { 184 lua_pushlightuserdata(L, img); 185 return 1; 186 } else { 187 lua_pushnil(L); 188 lua_pushstring(L, "Failed to decode BMP"); 189 return 2; 190 } 191 } 192 193 /* Lua binding: Save image as BMP 194 int lua_bmp_save(lua_State* L) { 195 image_t* img = (image_t*)lua_touserdata(L, 1); 196 197 if (!img) { 198 lua_pushnil(L); 199 lua_pushstring(L, "Invalid image"); 200 return 2; 201 } 202 203 uint8_t* data; 204 uint32_t size; 205 206 if (bmp_encode(img, &data, &size) == 0) { 207 lua_pushlstring(L, (const char*)data, size); 208 free(data); 209 return 1; 210 } else { 211 lua_pushnil(L); 212 lua_pushstring(L, "Failed to encode BMP"); 213 return 2; 214 } 215 } 216 */