mirror of
https://github.com/openmv/openmv.git
synced 2025-11-04 14:49:50 +08:00
Merge pull request #97 from kwagyeman/master
Updated code to use fb_alloc.
This commit is contained in:
commit
cad38847e9
@ -7,7 +7,10 @@
|
||||
*
|
||||
*/
|
||||
#include <mp.h>
|
||||
#include "mdefs.h"
|
||||
#include "fb_alloc.h"
|
||||
#include "ff_wrapper.h"
|
||||
#define FF_MIN(x,y) (((x)<(y))?(x):(y))
|
||||
|
||||
extern const char *ffs_strerror(FRESULT res);
|
||||
|
||||
@ -83,6 +86,56 @@ void file_seek(FIL *fp, UINT offset)
|
||||
if (res != FR_OK) ff_fail(fp, res);
|
||||
}
|
||||
|
||||
// When a sector boundary is encountered while writing a file and there are
|
||||
// more than 512 bytes left to write FatFs will detect that it can bypass
|
||||
// its internal write buffer and pass the data buffer passed to it directly
|
||||
// to the disk write function. However, the disk write function needs the
|
||||
// buffer to be aligned to a 4-byte boundary. FatFs doesn't know this and
|
||||
// will pass an unaligned buffer if we don't fix the issue. To fix this problem
|
||||
// we use a temporary buffer to fix the alignment and to speed everything up.
|
||||
|
||||
static uint32_t file_buffer_offset = 0;
|
||||
static uint8_t *file_buffer_pointer = 0;
|
||||
static uint32_t file_buffer_size = 0;
|
||||
static uint32_t file_buffer_index = 0;
|
||||
|
||||
ALWAYS_INLINE static void file_flush(FIL *fp)
|
||||
{
|
||||
if (file_buffer_index == file_buffer_size) {
|
||||
UINT bytes;
|
||||
FRESULT res = f_write(fp, file_buffer_pointer, file_buffer_index, &bytes);
|
||||
if (res != FR_OK) ff_fail(fp, res);
|
||||
if (bytes != file_buffer_index) ff_write_fail(fp);
|
||||
file_buffer_pointer -= file_buffer_offset;
|
||||
file_buffer_size += file_buffer_offset;
|
||||
file_buffer_offset = 0;
|
||||
file_buffer_index = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void file_buffer_on(FIL *fp)
|
||||
{
|
||||
file_buffer_offset = f_tell(fp) % 4;
|
||||
file_buffer_pointer = fb_alloc_all(&file_buffer_size) + file_buffer_offset;
|
||||
if (!file_buffer_size) {
|
||||
nlr_raise(mp_obj_new_exception_msg(&mp_type_MemoryError, "No memory!"));
|
||||
}
|
||||
file_buffer_size -= file_buffer_offset;
|
||||
file_buffer_index = 0;
|
||||
}
|
||||
|
||||
void file_buffer_off(FIL *fp)
|
||||
{
|
||||
if ((fp->flag & FA_WRITE) && file_buffer_index) {
|
||||
UINT bytes;
|
||||
FRESULT res = f_write(fp, file_buffer_pointer, file_buffer_index, &bytes);
|
||||
if (res != FR_OK) ff_fail(fp, res);
|
||||
if (bytes != file_buffer_index) ff_write_fail(fp);
|
||||
}
|
||||
file_buffer_pointer = 0;
|
||||
fb_free();
|
||||
}
|
||||
|
||||
void read_byte(FIL *fp, uint8_t *value)
|
||||
{
|
||||
UINT bytes;
|
||||
@ -156,32 +209,75 @@ void read_data(FIL *fp, void *data, UINT size)
|
||||
|
||||
void write_byte(FIL *fp, uint8_t value)
|
||||
{
|
||||
UINT bytes;
|
||||
FRESULT res = f_write(fp, &value, sizeof(value), &bytes);
|
||||
if (res != FR_OK) ff_fail(fp, res);
|
||||
if (bytes != sizeof(value)) ff_write_fail(fp);
|
||||
if (file_buffer_pointer) {
|
||||
// We get a massive speed boost by buffering up as much data as possible
|
||||
// before a write to the SD card. So much so that the time wasted by
|
||||
// all these operations does not cost us.
|
||||
for (int i = 0; i < sizeof(value); i++) {
|
||||
file_buffer_pointer[file_buffer_index++] = ((uint8_t *) &value)[i];
|
||||
file_flush(fp);
|
||||
}
|
||||
} else {
|
||||
UINT bytes;
|
||||
FRESULT res = f_write(fp, &value, sizeof(value), &bytes);
|
||||
if (res != FR_OK) ff_fail(fp, res);
|
||||
if (bytes != sizeof(value)) ff_write_fail(fp);
|
||||
}
|
||||
}
|
||||
|
||||
void write_word(FIL *fp, uint16_t value)
|
||||
{
|
||||
UINT bytes;
|
||||
FRESULT res = f_write(fp, &value, sizeof(value), &bytes);
|
||||
if (res != FR_OK) ff_fail(fp, res);
|
||||
if (bytes != sizeof(value)) ff_write_fail(fp);
|
||||
if (file_buffer_pointer) {
|
||||
// We get a massive speed boost by buffering up as much data as possible
|
||||
// before a write to the SD card. So much so that the time wasted by
|
||||
// all these operations does not cost us.
|
||||
for (int i = 0; i < sizeof(value); i++) {
|
||||
file_buffer_pointer[file_buffer_index++] = ((uint8_t *) &value)[i];
|
||||
file_flush(fp);
|
||||
}
|
||||
} else {
|
||||
UINT bytes;
|
||||
FRESULT res = f_write(fp, &value, sizeof(value), &bytes);
|
||||
if (res != FR_OK) ff_fail(fp, res);
|
||||
if (bytes != sizeof(value)) ff_write_fail(fp);
|
||||
}
|
||||
}
|
||||
|
||||
void write_long(FIL *fp, uint32_t value)
|
||||
{
|
||||
UINT bytes;
|
||||
FRESULT res = f_write(fp, &value, sizeof(value), &bytes);
|
||||
if (res != FR_OK) ff_fail(fp, res);
|
||||
if (bytes != sizeof(value)) ff_write_fail(fp);
|
||||
if (file_buffer_pointer) {
|
||||
// We get a massive speed boost by buffering up as much data as possible
|
||||
// before a write to the SD card. So much so that the time wasted by
|
||||
// all these operations does not cost us.
|
||||
for (int i = 0; i < sizeof(value); i++) {
|
||||
file_buffer_pointer[file_buffer_index++] = ((uint8_t *) &value)[i];
|
||||
file_flush(fp);
|
||||
}
|
||||
} else {
|
||||
UINT bytes;
|
||||
FRESULT res = f_write(fp, &value, sizeof(value), &bytes);
|
||||
if (res != FR_OK) ff_fail(fp, res);
|
||||
if (bytes != sizeof(value)) ff_write_fail(fp);
|
||||
}
|
||||
}
|
||||
|
||||
void write_data(FIL *fp, const void *data, UINT size)
|
||||
{
|
||||
UINT bytes;
|
||||
FRESULT res = f_write(fp, data, size, &bytes);
|
||||
if (res != FR_OK) ff_fail(fp, res);
|
||||
if (bytes != size) ff_write_fail(fp);
|
||||
if (file_buffer_pointer) {
|
||||
// We get a massive speed boost by buffering up as much data as possible
|
||||
// before a write to the SD card. So much so that the time wasted by
|
||||
// all these operations does not cost us.
|
||||
for (int i = 0; i < size;) {
|
||||
int can_do = FF_MIN(size, file_buffer_size - file_buffer_index);
|
||||
memcpy(file_buffer_pointer+file_buffer_index, ((uint8_t *) data)+i, can_do);
|
||||
file_buffer_index += can_do;
|
||||
file_flush(fp);
|
||||
i += can_do;
|
||||
}
|
||||
} else {
|
||||
UINT bytes;
|
||||
FRESULT res = f_write(fp, data, size, &bytes);
|
||||
if (res != FR_OK) ff_fail(fp, res);
|
||||
if (bytes != size) ff_write_fail(fp);
|
||||
}
|
||||
}
|
||||
|
||||
@ -18,6 +18,8 @@ void file_read_open(FIL *fp, const char *path);
|
||||
void file_write_open(FIL *fp, const char *path);
|
||||
void file_close(FIL *fp);
|
||||
void file_seek(FIL *fp, UINT offset);
|
||||
void file_buffer_on(FIL *fp); // does fb_alloc_all
|
||||
void file_buffer_off(FIL *fp); // does fb_free
|
||||
void read_byte(FIL *fp, uint8_t *value);
|
||||
void read_byte_expect(FIL *fp, uint8_t value);
|
||||
void read_byte_ignore(FIL *fp);
|
||||
|
||||
@ -181,6 +181,7 @@ void bmp_write_subimg(image_t *img, const char *path, rectangle_t *r)
|
||||
FIL fp;
|
||||
file_write_open(&fp, path);
|
||||
|
||||
file_buffer_on(&fp);
|
||||
if (IM_IS_GS(img)) {
|
||||
const int row_bytes = (((rect.w * 8) + 31) / 32) * 4;
|
||||
const int data_size = (row_bytes * rect.h);
|
||||
@ -256,6 +257,7 @@ void bmp_write_subimg(image_t *img, const char *path, rectangle_t *r)
|
||||
}
|
||||
}
|
||||
}
|
||||
file_buffer_off(&fp);
|
||||
|
||||
file_close(&fp);
|
||||
}
|
||||
|
||||
@ -6,15 +6,16 @@
|
||||
* A super simple GIF encoder.
|
||||
*
|
||||
*/
|
||||
#include "mp.h"
|
||||
#include <mp.h>
|
||||
#include "fb_alloc.h"
|
||||
#include "ff_wrapper.h"
|
||||
#include "imlib.h"
|
||||
#include "fb_alloc.h"
|
||||
|
||||
#define BLOCK_SIZE (126) // (2^(7)) - 2 (DO NOT CHANGE!)
|
||||
#define BLOCK_SIZE (126) // (2^7) - 2 // (DO NOT CHANGE!)
|
||||
|
||||
void gif_open(FIL *fp, int width, int height, bool color, bool loop)
|
||||
{
|
||||
file_buffer_on(fp);
|
||||
|
||||
write_data(fp, "GIF89a", 6);
|
||||
write_word(fp, width);
|
||||
write_word(fp, height);
|
||||
@ -39,10 +40,14 @@ void gif_open(FIL *fp, int width, int height, bool color, bool loop)
|
||||
write_data(fp, "NETSCAPE2.0", 11);
|
||||
write_data(fp, (uint8_t []) {0x03, 0x01, 0x00, 0x00, 0x00}, 5);
|
||||
}
|
||||
|
||||
file_buffer_off(fp);
|
||||
}
|
||||
|
||||
void gif_add_frame(FIL *fp, image_t *img, uint16_t delay)
|
||||
{
|
||||
file_buffer_on(fp);
|
||||
|
||||
if (delay) {
|
||||
write_data(fp, (uint8_t []) {'!', 0xF9, 0x04, 0x04}, 4);
|
||||
write_word(fp, delay);
|
||||
@ -55,65 +60,36 @@ void gif_add_frame(FIL *fp, image_t *img, uint16_t delay)
|
||||
write_word(fp, img->h);
|
||||
write_data(fp, (uint8_t []) {0x00, 0x07}, 2); // 7-bits
|
||||
|
||||
uint32_t size;
|
||||
uint8_t *buf = fb_alloc_all(&size);
|
||||
int bytes = img->h * img->w;
|
||||
int blocks = (bytes + BLOCK_SIZE - 1) / BLOCK_SIZE;
|
||||
|
||||
// This should never happen unless someone forgot to free.
|
||||
if (size < (sizeof(uint32_t) * 2)) { // We need atleast 8 bytes.
|
||||
nlr_raise(mp_obj_new_exception_msg(&mp_type_MemoryError,
|
||||
"Memory leak detected!!!"));
|
||||
}
|
||||
|
||||
int buf_ofs = 0;
|
||||
int bytes = img->h * img->w;
|
||||
|
||||
// When a sector boundary is encountered while writing a file and there are
|
||||
// more than 512 bytes left to write FatFs will detect that it can bypass
|
||||
// its internal write buffer and pass the data buffer passed to it directly
|
||||
// to the disk write function. However, the disk write function needs the
|
||||
// buffer to be aligned to a 4-byte boundary. FatFs doesn't know this and
|
||||
// will pass an unaligned buffer if we don't fix the issue.
|
||||
|
||||
int fat_ofs = f_tell(fp) % 4;
|
||||
buf += fat_ofs;
|
||||
size -= fat_ofs;
|
||||
|
||||
// Since we're about to do a more than 512 byte write which will trigger
|
||||
// the above issue we can fix the problem by misaligning our buffer before
|
||||
// calling write so that the first partial write gets us back aligned.
|
||||
|
||||
for (int x=0; x<bytes; x++) {
|
||||
if ((x%BLOCK_SIZE)==0) {
|
||||
buf[buf_ofs++] = 1 + IM_MIN(BLOCK_SIZE, bytes - x);
|
||||
buf[buf_ofs++] = 0x80; // clear code
|
||||
if (IM_IS_GS(img)) {
|
||||
for (int y=0; y<blocks; y++) {
|
||||
int block_size = IM_MIN(BLOCK_SIZE, bytes - (y*BLOCK_SIZE));
|
||||
write_byte(fp, 1 + block_size);
|
||||
write_byte(fp, 0x80); // clear code
|
||||
for (int x=0; x<block_size; x++) {
|
||||
write_byte(fp, img->pixels[(y*BLOCK_SIZE)+x]>>1);
|
||||
}
|
||||
}
|
||||
|
||||
if (IM_IS_GS(img)) {
|
||||
buf[buf_ofs++] = img->pixels[x]>>1;
|
||||
} else {
|
||||
int pixel = ((uint16_t *) img->pixels)[x];
|
||||
int red = IM_R565(pixel) >> 3;
|
||||
int green = IM_G565(pixel) >> 3;
|
||||
int blue = IM_B565(pixel) >> 3;
|
||||
buf[buf_ofs++] = (red << 5) | (green << 2) | blue;
|
||||
} else {
|
||||
for (int y=0; y<blocks; y++) {
|
||||
int block_size = IM_MIN(BLOCK_SIZE, bytes - (y*BLOCK_SIZE));
|
||||
write_byte(fp, 1 + block_size);
|
||||
write_byte(fp, 0x80); // clear code
|
||||
for (int x=0; x<block_size; x++) {
|
||||
int pixel = ((uint16_t *) img->pixels)[(y*BLOCK_SIZE)+x];
|
||||
int red = IM_R565(pixel)>>3;
|
||||
int green = IM_G565(pixel)>>3;
|
||||
int blue = IM_B565(pixel)>>3;
|
||||
write_byte(fp, (red<<5) | (green<<2) | blue);
|
||||
}
|
||||
}
|
||||
|
||||
if (buf_ofs==size) { // flush data
|
||||
buf_ofs = 0;
|
||||
write_data(fp, buf, size);
|
||||
// Undo alignment fix. fp will be aligned from now on.
|
||||
buf -= fat_ofs;
|
||||
size += fat_ofs;
|
||||
fat_ofs = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (buf_ofs) { // flush remaining
|
||||
write_data(fp, buf, buf_ofs);
|
||||
}
|
||||
|
||||
write_data(fp, (uint8_t []) {0x01, 0x81, 0x00}, 3); // end code
|
||||
fb_free();
|
||||
|
||||
file_buffer_off(fp);
|
||||
}
|
||||
|
||||
void gif_close(FIL *fp)
|
||||
|
||||
@ -104,10 +104,7 @@ static save_image_format_t imblib_parse_extension(image_t *img, const char *path
|
||||
&& ((p[-3] == 'p') || (p[-3] == 'P'))
|
||||
&& ((p[-4] == 'j') || (p[-4] == 'J'))
|
||||
&& ((p[-5] == '.') || (p[-5] == '.'))) {
|
||||
if (!IM_IS_JPEG(img)) {
|
||||
nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError,
|
||||
"Image is not JPEG!"));
|
||||
}
|
||||
// Will convert to JPG if not.
|
||||
return FORMAT_JPG;
|
||||
}
|
||||
}
|
||||
@ -116,10 +113,7 @@ static save_image_format_t imblib_parse_extension(image_t *img, const char *path
|
||||
&& ((p[-2] == 'p') || (p[-2] == 'P'))
|
||||
&& ((p[-3] == 'j') || (p[-3] == 'J'))
|
||||
&& ((p[-4] == '.') || (p[-4] == '.'))) {
|
||||
if (!IM_IS_JPEG(img)) {
|
||||
nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError,
|
||||
"Image is not JPG!"));
|
||||
}
|
||||
// Will convert to JPG if not.
|
||||
return FORMAT_JPG;
|
||||
} else if (((p[-1] == 'p') || (p[-1] == 'P'))
|
||||
&& ((p[-2] == 'm') || (p[-2] == 'M'))
|
||||
@ -206,12 +200,18 @@ void imlib_image_operation(image_t *img, const char *path, image_t *other, line_
|
||||
// the line operation on each line in that window before moving to the
|
||||
// next window. The vflipped part is here because BMP files can be saved
|
||||
// vertically flipped resulting in us reading the image backwards.
|
||||
temp.h = IM_IS_GS(img) ? GS_LINE_BUFFER_SIZE : RGB565_LINE_BUFFER_SIZE;
|
||||
uint32_t size;
|
||||
temp.pixels = fb_alloc_all(&size);
|
||||
temp.h = (size / (temp.w * temp.bpp)); // round down
|
||||
// This should never happen unless someone forgot to free.
|
||||
if (!temp.h) {
|
||||
nlr_raise(mp_obj_new_exception_msg(&mp_type_MemoryError,
|
||||
"Memory leak detected!!!"));
|
||||
}
|
||||
// When processing vertically flipped images the read function will fill
|
||||
// the window up from the bottom. The read function assumes that the
|
||||
// window is equal to an image in size. However, since this is not the
|
||||
// case we shrink the window size to how many lines we're buffering.
|
||||
temp.pixels = fb_alloc(temp.w * temp.h * temp.bpp);
|
||||
for (int i=0; i<img->h; i+=temp.h) { // goes past end
|
||||
int can_do = IM_MIN(temp.h, img->h-i);
|
||||
imlib_read_pixels(&fp, &temp, 0, can_do, &rs);
|
||||
|
||||
@ -175,36 +175,6 @@ extern const int8_t lab_table[196608];
|
||||
|
||||
#define IM_JPEG_QUALITY (50)
|
||||
|
||||
// Main Ram = 196,608 bytes
|
||||
// -
|
||||
// struct framebuffer = 20 bytes
|
||||
// FB_JPEG_OFFS_SIZE = 1,024 bytes
|
||||
// GRAYSCALE 320x240x1 image = 76,800 bytes
|
||||
// fb_alloc = 4 bytes
|
||||
// flash cache = 16,384 bytes
|
||||
// =
|
||||
// 102,376 bytes
|
||||
// /
|
||||
// GRAYSCALE 320x1 lines
|
||||
// =
|
||||
// 319 GRAYSCALE 320x1 lines
|
||||
#define GS_LINE_BUFFER_SIZE (64) // double rgb565
|
||||
|
||||
// Main Ram = 196,608 bytes
|
||||
// -
|
||||
// struct framebuffer = 20 bytes
|
||||
// FB_JPEG_OFFS_SIZE = 1,024 bytes
|
||||
// RGB565 320x240x2 image = 153,600 bytes
|
||||
// fb_alloc = 4 bytes
|
||||
// flash cache = 16,384 bytes
|
||||
// =
|
||||
// 25,576 bytes
|
||||
// /
|
||||
// RGB565 320x2 lines
|
||||
// =
|
||||
// 39 RGB565 320x2 lines
|
||||
#define RGB565_LINE_BUFFER_SIZE (32)
|
||||
|
||||
typedef struct size {
|
||||
int w;
|
||||
int h;
|
||||
|
||||
@ -10,9 +10,9 @@
|
||||
#include <arm_math.h>
|
||||
#include <stdio.h>
|
||||
#include <stm32f4xx_hal.h>
|
||||
#include <ff.h>
|
||||
#include "ff_wrapper.h"
|
||||
#include "xalloc.h"
|
||||
#include "fb_alloc.h"
|
||||
#include "ff_wrapper.h"
|
||||
#include "imlib.h"
|
||||
#define TIME_JPEG (0)
|
||||
|
||||
@ -687,7 +687,7 @@ void jpeg_read(image_t *img, const char *path)
|
||||
{
|
||||
FIL fp;
|
||||
jpeg_read_geometry(&fp, img, path);
|
||||
if (!img->pixels) img->pixels = xalloc(img->w * img->h * img->bpp);
|
||||
if (!img->pixels) img->pixels = xalloc(img->bpp);
|
||||
jpeg_read_pixels(&fp, img);
|
||||
file_close(&fp);
|
||||
}
|
||||
@ -696,6 +696,18 @@ void jpeg_write(image_t *img, const char *path)
|
||||
{
|
||||
FIL fp;
|
||||
file_write_open(&fp, path);
|
||||
write_data(&fp, img->pixels, img->bpp);
|
||||
if (IM_IS_JPEG(img)) {
|
||||
write_data(&fp, img->pixels, img->bpp);
|
||||
} else {
|
||||
uint32_t size;
|
||||
uint8_t *buffer = fb_alloc_all(&size);
|
||||
image_t out = { .w=img->w, .h=img->h, .bpp=size, .pixels=buffer };
|
||||
// When jpeg_compress needs more memory than in currently allocated it
|
||||
// will try to realloc. MP will detect that the pointer is outside of
|
||||
// the heap and return NULL which will cause an out of memory error.
|
||||
jpeg_compress(img, &out, IM_JPEG_QUALITY);
|
||||
write_data(&fp, out.pixels, out.bpp);
|
||||
fb_free();
|
||||
}
|
||||
file_close(&fp);
|
||||
}
|
||||
|
||||
@ -6,9 +6,9 @@
|
||||
* A super simple MJPEG encoder.
|
||||
*
|
||||
*/
|
||||
#include "fb_alloc.h"
|
||||
#include "ff_wrapper.h"
|
||||
#include "imlib.h"
|
||||
#include "fb_alloc.h"
|
||||
|
||||
#define SIZE_OFFSET (1*4)
|
||||
#define MICROS_OFFSET (8*4)
|
||||
|
||||
@ -126,6 +126,7 @@ void ppm_write_subimg(image_t *img, const char *path, rectangle_t *r)
|
||||
FIL fp;
|
||||
file_write_open(&fp, path);
|
||||
|
||||
file_buffer_on(&fp);
|
||||
if (IM_IS_GS(img)) {
|
||||
char buffer[20]; // exactly big enough for 5-digit w/h
|
||||
int len = snprintf(buffer, 20, "P5\n%d %d\n255\n", rect.w, rect.h);
|
||||
@ -154,6 +155,7 @@ void ppm_write_subimg(image_t *img, const char *path, rectangle_t *r)
|
||||
}
|
||||
}
|
||||
}
|
||||
file_buffer_off(&fp);
|
||||
|
||||
file_close(&fp);
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user