250 lines
7.2 KiB
C
250 lines
7.2 KiB
C
/**
|
|
* @file lv_bmp.c
|
|
*
|
|
*/
|
|
|
|
/*********************
|
|
* INCLUDES
|
|
*********************/
|
|
#include "../../../../lvgl/lvgl.h"
|
|
#if LV_USE_BMP
|
|
|
|
#include <string.h>
|
|
|
|
/*********************
|
|
* DEFINES
|
|
*********************/
|
|
|
|
/**********************
|
|
* TYPEDEFS
|
|
**********************/
|
|
|
|
typedef struct {
|
|
lv_fs_file_t f;
|
|
unsigned int px_offset;
|
|
int px_width;
|
|
int px_height;
|
|
unsigned int bpp;
|
|
int row_size_bytes;
|
|
} bmp_dsc_t;
|
|
|
|
/**********************
|
|
* STATIC PROTOTYPES
|
|
**********************/
|
|
static lv_res_t decoder_info(lv_img_decoder_t * decoder, const void * src, lv_img_header_t * header);
|
|
static lv_res_t decoder_open(lv_img_decoder_t * dec, lv_img_decoder_dsc_t * dsc);
|
|
|
|
|
|
static lv_res_t decoder_read_line(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc,
|
|
lv_coord_t x, lv_coord_t y, lv_coord_t len, uint8_t * buf);
|
|
|
|
static void decoder_close(lv_img_decoder_t * dec, lv_img_decoder_dsc_t * dsc);
|
|
|
|
/**********************
|
|
* STATIC VARIABLES
|
|
**********************/
|
|
|
|
/**********************
|
|
* MACROS
|
|
**********************/
|
|
|
|
/**********************
|
|
* GLOBAL FUNCTIONS
|
|
**********************/
|
|
void lv_bmp_init(void)
|
|
{
|
|
lv_img_decoder_t * dec = lv_img_decoder_create();
|
|
lv_img_decoder_set_info_cb(dec, decoder_info);
|
|
lv_img_decoder_set_open_cb(dec, decoder_open);
|
|
lv_img_decoder_set_read_line_cb(dec, decoder_read_line);
|
|
lv_img_decoder_set_close_cb(dec, decoder_close);
|
|
}
|
|
|
|
/**********************
|
|
* STATIC FUNCTIONS
|
|
**********************/
|
|
|
|
/**
|
|
* Get info about a PNG image
|
|
* @param src can be file name or pointer to a C array
|
|
* @param header store the info here
|
|
* @return LV_RES_OK: no error; LV_RES_INV: can't get the info
|
|
*/
|
|
static lv_res_t decoder_info(lv_img_decoder_t * decoder, const void * src, lv_img_header_t * header)
|
|
{
|
|
LV_UNUSED(decoder);
|
|
|
|
lv_img_src_t src_type = lv_img_src_get_type(src); /*Get the source type*/
|
|
|
|
/*If it's a BMP file...*/
|
|
if(src_type == LV_IMG_SRC_FILE) {
|
|
const char * fn = src;
|
|
if(!strcmp(&fn[strlen(fn) - 3], "bmp")) { /*Check the extension*/
|
|
/*Save the data in the header*/
|
|
lv_fs_file_t f;
|
|
lv_fs_res_t res = lv_fs_open(&f, src, LV_FS_MODE_RD);
|
|
if(res != LV_FS_RES_OK) return LV_RES_INV;
|
|
uint8_t headers[54];
|
|
|
|
lv_fs_read(&f, headers, 54, NULL);
|
|
uint32_t w;
|
|
uint32_t h;
|
|
memcpy(&w, headers + 18, 4);
|
|
memcpy(&h, headers + 22, 4);
|
|
header->w = w;
|
|
header->h = h;
|
|
header->always_zero = 0;
|
|
lv_fs_close(&f);
|
|
#if LV_COLOR_DEPTH == 32
|
|
uint16_t bpp;
|
|
memcpy(&bpp, headers + 28, 2);
|
|
header->cf = bpp == 32 ? LV_IMG_CF_TRUE_COLOR_ALPHA : LV_IMG_CF_TRUE_COLOR;
|
|
#else
|
|
header->cf = LV_IMG_CF_TRUE_COLOR;
|
|
#endif
|
|
return LV_RES_OK;
|
|
}
|
|
}
|
|
/* BMP file as data not supported for simplicity.
|
|
* Convert them to LVGL compatible C arrays directly. */
|
|
else if(src_type == LV_IMG_SRC_VARIABLE) {
|
|
return LV_RES_INV;
|
|
}
|
|
|
|
return LV_RES_INV; /*If didn't succeeded earlier then it's an error*/
|
|
}
|
|
|
|
|
|
/**
|
|
* Open a PNG image and return the decided image
|
|
* @param src can be file name or pointer to a C array
|
|
* @param style style of the image object (unused now but certain formats might use it)
|
|
* @return pointer to the decoded image or `LV_IMG_DECODER_OPEN_FAIL` if failed
|
|
*/
|
|
static lv_res_t decoder_open(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc)
|
|
{
|
|
LV_UNUSED(decoder);
|
|
|
|
/*If it's a PNG file...*/
|
|
if(dsc->src_type == LV_IMG_SRC_FILE) {
|
|
const char * fn = dsc->src;
|
|
|
|
if(strcmp(&fn[strlen(fn) - 3], "bmp")) return LV_RES_INV; /*Check the extension*/
|
|
|
|
bmp_dsc_t b;
|
|
memset(&b, 0x00, sizeof(b));
|
|
|
|
lv_fs_res_t res = lv_fs_open(&b.f, dsc->src, LV_FS_MODE_RD);
|
|
if(res == LV_RES_OK) return LV_RES_INV;
|
|
|
|
uint8_t header[54];
|
|
lv_fs_read(&b.f, header, 54, NULL);
|
|
|
|
if(0x42 != header[0] || 0x4d != header[1]) {
|
|
lv_fs_close(&b.f);
|
|
return LV_RES_INV;
|
|
}
|
|
|
|
memcpy(&b.px_offset, header + 10, 4);
|
|
memcpy(&b.px_width, header + 18, 4);
|
|
memcpy(&b.px_height, header + 22, 4);
|
|
memcpy(&b.bpp, header + 28, 2);
|
|
b.row_size_bytes = ((b.bpp * b.px_width + 31) / 32) * 4;
|
|
|
|
bool color_depth_error = false;
|
|
if(LV_COLOR_DEPTH == 32 && (b.bpp != 32 && b.bpp != 24)) {
|
|
LV_LOG_WARN("LV_COLOR_DEPTH == 32 but bpp is %d (should be 32 or 24)", b.bpp);
|
|
color_depth_error = true;
|
|
}
|
|
else if(LV_COLOR_DEPTH == 16 && b.bpp != 16) {
|
|
LV_LOG_WARN("LV_COLOR_DEPTH == 16 but bpp is %d (should be 16)", b.bpp);
|
|
color_depth_error = true;
|
|
}
|
|
else if(LV_COLOR_DEPTH == 8 && b.bpp != 8) {
|
|
LV_LOG_WARN("LV_COLOR_DEPTH == 8 but bpp is %d (should be 8)", b.bpp);
|
|
color_depth_error = true;
|
|
}
|
|
|
|
if(color_depth_error) {
|
|
dsc->error_msg = "Color depth mismatch";
|
|
lv_fs_close(&b.f);
|
|
return LV_RES_INV;
|
|
}
|
|
|
|
dsc->user_data = lv_mem_alloc(sizeof(bmp_dsc_t));
|
|
LV_ASSERT_MALLOC(dsc->user_data);
|
|
if(dsc->user_data == NULL) return LV_RES_INV;
|
|
memcpy(dsc->user_data, &b, sizeof(b));
|
|
|
|
dsc->img_data = NULL;
|
|
return LV_RES_OK;
|
|
}
|
|
/* BMP file as data not supported for simplicity.
|
|
* Convert them to LVGL compatible C arrays directly. */
|
|
else if(dsc->src_type == LV_IMG_SRC_VARIABLE) {
|
|
return LV_RES_INV;
|
|
}
|
|
|
|
return LV_RES_INV; /*If not returned earlier then it failed*/
|
|
}
|
|
|
|
|
|
static lv_res_t decoder_read_line(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc,
|
|
lv_coord_t x, lv_coord_t y, lv_coord_t len, uint8_t * buf)
|
|
{
|
|
LV_UNUSED(decoder);
|
|
|
|
bmp_dsc_t * b = dsc->user_data;
|
|
y = (b->px_height - 1) - y; /*BMP images are stored upside down*/
|
|
uint32_t p = b->px_offset + b->row_size_bytes * y;
|
|
p += x * (b->bpp / 8);
|
|
lv_fs_seek(&b->f, p, LV_FS_SEEK_SET);
|
|
lv_fs_read(&b->f, buf, len * (b->bpp / 8), NULL);
|
|
|
|
#if LV_COLOR_DEPTH == 32
|
|
if(b->bpp == 32) {
|
|
lv_coord_t i;
|
|
for(i = 0; i < len; i++) {
|
|
uint8_t b0 = buf[i * 4];
|
|
uint8_t b1 = buf[i * 4 + 1];
|
|
uint8_t b2 = buf[i * 4 + 2];
|
|
uint8_t b3 = buf[i * 4 + 3];
|
|
lv_color32_t * c = (lv_color32_t *)&buf[i * 4];
|
|
c->ch.red = b2;
|
|
c->ch.green = b1;
|
|
c->ch.blue = b0;
|
|
c->ch.alpha = b3;
|
|
}
|
|
}
|
|
if(b->bpp == 24) {
|
|
lv_coord_t i;
|
|
|
|
for(i = len - 1; i >= 0; i--) {
|
|
uint8_t * t = &buf[i * 3];
|
|
lv_color32_t * c = (lv_color32_t *)&buf[i * 4];
|
|
c->ch.red = t[2];
|
|
c->ch.green = t[1];
|
|
c->ch.blue = t[0];
|
|
c->ch.alpha = 0xff;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return LV_RES_OK;
|
|
}
|
|
|
|
|
|
/**
|
|
* Free the allocated resources
|
|
*/
|
|
static void decoder_close(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc)
|
|
{
|
|
LV_UNUSED(decoder);
|
|
bmp_dsc_t * b = dsc->user_data;
|
|
lv_fs_close(&b->f);
|
|
lv_mem_free(dsc->user_data);
|
|
|
|
}
|
|
|
|
#endif /*LV_USE_BMP*/
|