bmp.c
1 /* 2 * This file is part of the OpenMV project. 3 * Copyright (c) 2013/2014 Ibrahim Abdelkader <i.abdalkader@gmail.com> 4 * This work is licensed under the MIT license, see the file LICENSE for details. 5 * 6 * BMP reader/writer. 7 * 8 */ 9 #include <math.h> 10 #include <stdlib.h> 11 #include <ff.h> 12 #include "vfs_wrapper.h" 13 #include "xalloc.h" 14 #include "imlib.h" 15 #include "omv_boardconfig.h" 16 #include "framebuffer.h" 17 #include "py/runtime.h" 18 19 // This function inits the geometry values of an image (opens file). 20 bool bmp_read_geometry(mp_obj_t fp, image_t *img, bmp_read_settings_t *rs) 21 { 22 read_byte_expect(fp, 'B'); 23 read_byte_expect(fp, 'M'); 24 uint32_t file_size; 25 read_long_raise(fp, &file_size); 26 read_word_ignore(fp); 27 read_word_ignore(fp); 28 29 uint32_t header_size; 30 read_long_raise(fp, &header_size); 31 if (file_size <= header_size) file_corrupted_raise(fp); 32 33 uint32_t data_size = file_size - header_size; 34 // if (data_size % 4) file_corrupted_raise(fp); 35 // if (file_size % 4) file_corrupted_raise(fp); 36 uint32_t header2_size; 37 read_long_raise(fp, &header2_size); 38 read_long_raise(fp, (uint32_t*) &rs->bmp_w); 39 read_long_raise(fp, (uint32_t*) &rs->bmp_h); 40 if ((rs->bmp_w == 0) || (rs->bmp_h == 0)) file_corrupted_raise(fp); 41 img->w = abs(rs->bmp_w); 42 img->h = abs(rs->bmp_h); 43 44 read_word_expect(fp, 1); 45 read_word_raise(fp, &rs->bmp_bpp); 46 if ((rs->bmp_bpp != 8) && (rs->bmp_bpp != 16) && (rs->bmp_bpp != 24)) fs_unsupported_format(fp); 47 img->bpp = (rs->bmp_bpp == 8) ? 1 : 2; 48 49 read_long_raise(fp, &rs->bmp_fmt); 50 if ((rs->bmp_fmt != 0) && (rs->bmp_fmt != 3)) fs_unsupported_format(fp); 51 52 read_long_expect(fp, data_size); 53 read_long_ignore(fp); 54 read_long_ignore(fp); 55 read_long_ignore(fp); 56 read_long_ignore(fp); 57 58 if (rs->bmp_bpp == 8) { 59 if (rs->bmp_fmt != 0) fs_unsupported_format(fp); 60 // Color Table (1024 bytes) 61 for (int i = 0; i < 256; i++) { 62 read_long_expect(fp, ((i) << 16) | ((i) << 8) | i); 63 } 64 } else if (rs->bmp_bpp == 16) { 65 if (rs->bmp_fmt != 3) fs_unsupported_format(fp); 66 // Bit Masks (12 bytes) 67 read_long_expect(fp, 0x1F << 11); 68 read_long_expect(fp, 0x3F << 5); 69 read_long_expect(fp, 0x1F); 70 } else if (rs->bmp_bpp == 24) { 71 if (rs->bmp_fmt == 3) { 72 // Bit Masks (12 bytes) 73 read_long_expect(fp, 0xFF << 16); 74 read_long_expect(fp, 0xFF << 8); 75 read_long_expect(fp, 0xFF); 76 } 77 } 78 int err; 79 vfs_internal_seek(fp, header_size, VFS_SEEK_SET, &err); 80 if(err != 0) 81 mp_raise_OSError(err); 82 83 rs->bmp_row_bytes = (((img->w * rs->bmp_bpp) + 31) / 32) * 4; 84 if (data_size < (rs->bmp_row_bytes * img->h)) file_corrupted_raise(fp); 85 return (rs->bmp_h >= 0); 86 } 87 88 // This function reads the pixel values of an image. 89 bool bmp_read_pixels(mp_obj_t fp, image_t *img, int line_start, int line_end, bmp_read_settings_t *rs) 90 { 91 if (rs->bmp_bpp == 8) { 92 if ((rs->bmp_h < 0) && (rs->bmp_w >= 0) && (img->w == rs->bmp_row_bytes)) { 93 if(read_data(fp, // Super Fast - Zoom, Zoom! 94 img->pixels + (line_start * img->w), 95 (line_end - line_start) * img->w)) 96 { 97 return false; 98 } 99 } else { 100 for (int i = line_start; i < line_end; i++) { 101 for (int j = 0; j < rs->bmp_row_bytes; j++) { 102 uint8_t pixel; 103 if(read_byte(fp, &pixel)) 104 return false; 105 if (j < img->w) { 106 if (rs->bmp_h < 0) { // vertical flip (BMP file perspective) 107 if (rs->bmp_w < 0) { // horizontal flip (BMP file perspective) 108 IM_SET_GS_PIXEL(img, (img->w-j-1), i, pixel); 109 } else { 110 IM_SET_GS_PIXEL(img, j, i, pixel); 111 } 112 } else { 113 if (rs->bmp_w < 0) { 114 IM_SET_GS_PIXEL(img, (img->w-j-1), (img->h-i-1), pixel); 115 } else { 116 IM_SET_GS_PIXEL(img, j, (img->h-i-1), pixel); 117 } 118 } 119 } 120 } 121 } 122 } 123 } else if (rs->bmp_bpp == 16) { 124 for (int i = line_start; i < line_end; i++) { 125 for (int j = 0, jj = rs->bmp_row_bytes / 2; j < jj; j++) { 126 uint16_t pixel; 127 if(read_word(fp, &pixel) != 0) 128 return false; 129 pixel = IM_SWAP16(pixel); 130 if (j < img->w) { 131 if (rs->bmp_h < 0) { // vertical flip (BMP file perspective) 132 if (rs->bmp_w < 0) { // horizontal flip (BMP file perspective) 133 IM_SET_RGB565_PIXEL(img, (img->w-j-1), i, pixel); 134 } else { 135 IM_SET_RGB565_PIXEL(img, j, i, pixel); 136 } 137 } else { 138 if (rs->bmp_w < 0) { 139 IM_SET_RGB565_PIXEL(img, (img->w-j-1), (img->h-i-1), pixel); 140 } else { 141 IM_SET_RGB565_PIXEL(img, j, (img->h-i-1), pixel); 142 } 143 } 144 } 145 } 146 } 147 } else if (rs->bmp_bpp == 24) { 148 for (int i = line_start; i < line_end; i++) { 149 for (int j = 0, jj = rs->bmp_row_bytes / 3; j < jj; j++) { 150 uint8_t r, g, b; 151 if(read_byte(fp, &r)) { 152 return false; 153 } 154 if(read_byte(fp, &g)) { 155 return false; 156 } 157 if(read_byte(fp, &b)) { 158 return false; 159 } 160 uint16_t pixel = IM_RGB565(IM_R825(b), IM_G826(g), IM_B825(r)); 161 if (j < img->w) { 162 if (rs->bmp_h < 0) { // vertical flip 163 if (rs->bmp_w < 0) { // horizontal flip 164 IM_SET_RGB565_PIXEL(img, (img->w-j-1), i, pixel); 165 } else { 166 IM_SET_RGB565_PIXEL(img, j, i, pixel); 167 } 168 } else { 169 if (rs->bmp_w < 0) { 170 IM_SET_RGB565_PIXEL(img, (img->w-j-1), (img->h-i-1), pixel); 171 } else { 172 IM_SET_RGB565_PIXEL(img, j, (img->h-i-1), pixel); 173 } 174 } 175 } 176 } 177 for (int j = 0, jj = rs->bmp_row_bytes % 3; j < jj; j++) { 178 if(read_byte_ignore(fp)) { 179 return false; 180 } 181 } 182 } 183 } 184 return true; 185 } 186 187 void bmp_read(image_t *img, const char *path) 188 { 189 int err; 190 bmp_read_settings_t rs; 191 192 mp_obj_t file = vfs_internal_open(path, "rb", &err); 193 if( file == MP_OBJ_NULL || err != 0) 194 { 195 mp_raise_OSError(err); 196 } 197 bmp_read_geometry(file, img, &rs); 198 if (!img->pixels) 199 img->pixels = xalloc(img->w * img->h * img->bpp); 200 else 201 { 202 if( (img->w * img->h * img->bpp) > MAIN_FB()->w_max * MAIN_FB()->h_max * OMV_INIT_BPP ) 203 { 204 mp_raise_OSError(MP_EINVAL); 205 } 206 } 207 if(!bmp_read_pixels(file, img, 0, img->h, &rs)) 208 { 209 #if CONFIG_MAIXPY_OMV_DOUBLE_BUFF 210 if(img->pixels != MAIN_FB()->pixels[0] )//FIXME: 211 #else 212 if(img->pixels != MAIN_FB()->pixels ) 213 #endif 214 { 215 xfree(img->pixels); 216 } 217 vfs_internal_close(file, &err); 218 mp_raise_OSError(MP_EIO); 219 } 220 vfs_internal_close(file, &err); 221 } 222 223 void bmp_write_subimg(image_t *img, const char *path, rectangle_t *r) 224 { 225 rectangle_t rect; 226 if (!rectangle_subimg(img, r, &rect)) fs_no_intersection(NULL); 227 228 int err; 229 mp_obj_t file = vfs_internal_open(path, "wb", &err); 230 if(file == MP_OBJ_NULL || err != 0) 231 mp_raise_OSError(err); 232 233 if (IM_IS_GS(img)) { 234 const int row_bytes = (((rect.w * 8) + 31) / 32) * 4; 235 const int data_size = (row_bytes * rect.h); 236 const int waste = (row_bytes / sizeof(uint8_t)) - rect.w; 237 // File Header (14 bytes) 238 write_byte_raise(file, 'B'); 239 write_byte_raise(file, 'M'); 240 write_long_raise(file, 14 + 40 + 1024 + data_size); 241 write_word_raise(file, 0); 242 write_word_raise(file, 0); 243 write_long_raise(file, 14 + 40 + 1024); 244 // Info Header (40 bytes) 245 write_long_raise(file, 40); 246 write_long_raise(file, rect.w); 247 write_long_raise(file, -rect.h); // store the image flipped (correctly) 248 write_word_raise(file, 1); 249 write_word_raise(file, 8); 250 write_long_raise(file, 0); 251 write_long_raise(file, data_size); 252 write_long_raise(file, 0); 253 write_long_raise(file, 0); 254 write_long_raise(file, 0); 255 write_long_raise(file, 0); 256 // Color Table (1024 bytes) 257 for (int i = 0; i < 256; i++) { 258 write_long_raise(file, ((i) << 16) | ((i) << 8) | i); 259 } 260 if ((rect.x == 0) && (rect.w == img->w) && (img->w == row_bytes)) { 261 write_data_raise(file, // Super Fast - Zoom, Zoom! 262 img->pixels + (rect.y * img->w), 263 rect.w * rect.h); 264 } else { 265 for (int i = 0; i < rect.h; i++) { 266 write_data_raise(file, img->pixels+((rect.y+i)*img->w)+rect.x, rect.w); 267 for (int j = 0; j < waste; j++) { 268 write_byte_raise(file, 0); 269 } 270 } 271 } 272 } else { 273 const int row_bytes = (((rect.w * 16) + 31) / 32) * 4; 274 const int data_size = (row_bytes * rect.h); 275 const int waste = (row_bytes / sizeof(uint16_t)) - rect.w; 276 // File Header (14 bytes) 277 write_byte_raise(file, 'B'); 278 write_byte_raise(file, 'M'); 279 write_long_raise(file, 14 + 40 + 12 + data_size); 280 write_word_raise(file, 0); 281 write_word_raise(file, 0); 282 write_long_raise(file, 14 + 40 + 12); 283 // Info Header (40 bytes) 284 write_long_raise(file, 40); 285 write_long_raise(file, rect.w); 286 write_long_raise(file, -rect.h); // store the image flipped (correctly) 287 write_word_raise(file, 1); 288 write_word_raise(file, 16); 289 write_long_raise(file, 3); 290 write_long_raise(file, data_size); 291 write_long_raise(file, 0); 292 write_long_raise(file, 0); 293 write_long_raise(file, 0); 294 write_long_raise(file, 0); 295 // Bit Masks (12 bytes) 296 write_long_raise(file, 0x1F << 11); 297 write_long_raise(file, 0x3F << 5); 298 write_long_raise(file, 0x1F); 299 for (int i = 0; i < rect.h; i++) { 300 for (int j = 0; j < rect.w; j++) { 301 write_word_raise(file, IM_SWAP16(IM_GET_RGB565_PIXEL(img, (rect.x + j), (rect.y + i)))); 302 } 303 for (int j = 0; j < waste; j++) { 304 write_word_raise(file, 0); 305 } 306 } 307 } 308 vfs_internal_close(file, &err); 309 }