/ components / micropython / port / src / omv / img / bmp.c
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  }