Merge pull request #97 from kwagyeman/master

Updated code to use fb_alloc.
This commit is contained in:
Ibrahim Abd Elkader 2016-03-07 22:56:00 +02:00
commit cad38847e9
9 changed files with 178 additions and 118 deletions

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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);
}

View File

@ -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)

View File

@ -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);

View File

@ -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;

View File

@ -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);
}

View File

@ -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)

View File

@ -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);
}