diff --git a/.github/workflows/ccpp.yml b/.github/workflows/ccpp.yml index e096691fe7..0769990f12 100644 --- a/.github/workflows/ccpp.yml +++ b/.github/workflows/ccpp.yml @@ -86,7 +86,7 @@ jobs: install: | apt-get update -y - apt-get install build-essential ccache python3 libpng-dev ruby-full gcovr cmake -q -y + apt-get install build-essential ccache python3 libpng-dev ruby-full gcovr cmake libjpeg62-turbo-dev -q -y /usr/sbin/update-ccache-symlinks echo 'export PATH="/usr/lib/ccache:$PATH"' | tee -a ~/.bashrc diff --git a/Kconfig b/Kconfig index fcd09dd01f..8705894da9 100644 --- a/Kconfig +++ b/Kconfig @@ -1017,6 +1017,9 @@ menu "LVGL configuration" config LV_USE_TJPGD bool "TJPGD decoder library" + config LV_USE_LIBJPEG_TURBO + bool "libjpeg-turbo decoder library" + config LV_USE_GIF bool "GIF decoder library" diff --git a/docs/libs/index.rst b/docs/libs/index.rst index 185baaee39..eea3a90343 100644 --- a/docs/libs/index.rst +++ b/docs/libs/index.rst @@ -9,7 +9,7 @@ fs bmp tjpgd - sjpg + libjpeg_turbo lodepng gif freetype diff --git a/docs/libs/libjpeg_turbo.rst b/docs/libs/libjpeg_turbo.rst new file mode 100644 index 0000000000..56af25e645 --- /dev/null +++ b/docs/libs/libjpeg_turbo.rst @@ -0,0 +1,43 @@ +===================== +libjpge-turbo decoder +===================== + +libjpeg-turbo is a JPEG image codec that uses SIMD instructions to accelerate baseline JPEG compression and decompression on x86, +x86-64, Arm, PowerPC, and MIPS systems, as well as progressive JPEG compression on x86, x86-64, and Arm systems. +Detailed introduction: `libjpeg-turbo `__. + +Install +------- + +.. code:: bash + + sudo apt install libjpeg-turbo8-dev + +Add libjpge-turbo to your project +--------------------------------- + +.. code:: cmake + + find_package(JPEG REQUIRED) + include_directories(${JPEG_INCLUDE_DIR}) + target_link_libraries(${PROJECT_NAME} PRIVATE ${JPEG_LIBRARIES}) + +Usage +----- + +Enable :c:macro:`LV_USE_LIBJEPG_TURBO` in ``lv_conf.h``. + +See the examples below. +It should be noted that each image of this decoder needs to consume ``image width x image height x 3`` bytes of RAM, +and it needs to be combined with the ref:`image-caching` feature to ensure that the memory usage is within a reasonable range. + +Example +------- + +.. include:: ../examples/libs/libjpeg_turbo/index.rst + +API +--- + +:ref:`libjpeg_turbo` + diff --git a/examples/libs/libjpeg_turbo/flower.jpg b/examples/libs/libjpeg_turbo/flower.jpg new file mode 100644 index 0000000000..b34ca5d315 Binary files /dev/null and b/examples/libs/libjpeg_turbo/flower.jpg differ diff --git a/examples/libs/libjpeg_turbo/index.rst b/examples/libs/libjpeg_turbo/index.rst new file mode 100644 index 0000000000..ce6c3715d5 --- /dev/null +++ b/examples/libs/libjpeg_turbo/index.rst @@ -0,0 +1,6 @@ +Load an JPG image +----------------- + +.. lv_example:: libs/libjpeg_turbo/lv_example_libjpeg_turbo_1 + :language: c + diff --git a/examples/libs/libjpeg_turbo/lv_example_libjpeg_turbo.h b/examples/libs/libjpeg_turbo/lv_example_libjpeg_turbo.h new file mode 100644 index 0000000000..21f97acd8e --- /dev/null +++ b/examples/libs/libjpeg_turbo/lv_example_libjpeg_turbo.h @@ -0,0 +1,38 @@ +/** + * @file lv_example_libjpeg_turbo.h + * + */ + +#ifndef LV_EXAMPLE_LIBJPEG_TURBO_H +#define LV_EXAMPLE_LIBJPEG_TURBO_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ +void lv_example_libjpeg_turbo_1(void); + +/********************** + * MACROS + **********************/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_EXAMPLE_LIBJPEG_TURBO_H*/ diff --git a/examples/libs/libjpeg_turbo/lv_example_libjpeg_turbo_1.c b/examples/libs/libjpeg_turbo/lv_example_libjpeg_turbo_1.c new file mode 100644 index 0000000000..80fba46652 --- /dev/null +++ b/examples/libs/libjpeg_turbo/lv_example_libjpeg_turbo_1.c @@ -0,0 +1,18 @@ +#include "../../lv_examples.h" +#if LV_USE_LIBJPEG_TURBO && LV_BUILD_EXAMPLES + +/** + * Load a JPG image + */ +void lv_example_libjpeg_turbo_1(void) +{ + lv_obj_t * wp; + + wp = lv_image_create(lv_scr_act()); + /* Assuming a File system is attached to letter 'A' + * E.g. set LV_USE_FS_STDIO 'A' in lv_conf.h */ + lv_image_set_src(wp, "A:lvgl/examples/libs/libjpeg_turbo/flower.jpg"); + lv_obj_center(wp); +} + +#endif diff --git a/examples/libs/libjpeg_turbo/lv_example_libjpeg_turbo_1.py b/examples/libs/libjpeg_turbo/lv_example_libjpeg_turbo_1.py new file mode 100755 index 0000000000..a1f24f597a --- /dev/null +++ b/examples/libs/libjpeg_turbo/lv_example_libjpeg_turbo_1.py @@ -0,0 +1,13 @@ +#!/opt/bin/lv_micropython -i +import lvgl as lv +import display_driver +import fs_driver + +fs_drv = lv.fs_drv_t() +fs_driver.fs_register(fs_drv, 'S') + +wp = lv.image(lv.scr_act()) +# The File system is attached to letter 'S' + +wp.set_src("S:flower.jpg") +wp.center() diff --git a/examples/libs/lv_example_libs.h b/examples/libs/lv_example_libs.h index 8413586db8..2a7b60c118 100644 --- a/examples/libs/lv_example_libs.h +++ b/examples/libs/lv_example_libs.h @@ -22,6 +22,7 @@ extern "C" { #include "qrcode/lv_example_qrcode.h" #include "rlottie/lv_example_rlottie.h" #include "tjpgd/lv_example_tjpgd.h" +#include "libjpeg_turbo/lv_example_libjpeg_turbo.h" #include "tiny_ttf/lv_example_tiny_ttf.h" /********************* diff --git a/lv_conf_template.h b/lv_conf_template.h index 229d196ea6..4ecce906b4 100644 --- a/lv_conf_template.h +++ b/lv_conf_template.h @@ -591,6 +591,10 @@ * Split JPG is a custom format optimized for embedded systems. */ #define LV_USE_TJPGD 0 +/* libjpeg-turbo decoder library. + * Supports complete JPEG specifications and high-performance JPEG decoding. */ +#define LV_USE_LIBJPEG_TURBO 0 + /*GIF decoder library*/ #define LV_USE_GIF 0 diff --git a/lvgl.h b/lvgl.h index 744f260d46..36321440b5 100644 --- a/lvgl.h +++ b/lvgl.h @@ -96,6 +96,7 @@ extern "C" { #include "src/libs/gif/lv_gif.h" #include "src/libs/qrcode/lv_qrcode.h" #include "src/libs/tjpgd/lv_tjpgd.h" +#include "src/libs/libjpeg_turbo/lv_libjpeg_turbo.h" #include "src/libs/freetype/lv_freetype.h" #include "src/libs/rlottie/lv_rlottie.h" #include "src/libs/ffmpeg/lv_ffmpeg.h" diff --git a/scripts/install-prerequisites.sh b/scripts/install-prerequisites.sh index 539f023c42..9c931c01af 100755 --- a/scripts/install-prerequisites.sh +++ b/scripts/install-prerequisites.sh @@ -6,4 +6,4 @@ # # Note: This script is run by the CI workflows. sudo apt update -sudo apt install gcc python3 libpng-dev ruby-full gcovr cmake +sudo apt install gcc python3 libpng-dev ruby-full gcovr cmake libjpeg-turbo8-dev diff --git a/src/libs/libjpeg_turbo/lv_libjpeg_turbo.c b/src/libs/libjpeg_turbo/lv_libjpeg_turbo.c new file mode 100644 index 0000000000..330b06c99e --- /dev/null +++ b/src/libs/libjpeg_turbo/lv_libjpeg_turbo.c @@ -0,0 +1,477 @@ +/** + * @file lv_libjpeg_turbo.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "../../../lvgl.h" +#if LV_USE_LIBJPEG_TURBO + +#include "lv_libjpeg_turbo.h" +#include +#include +#include + +/********************* + * DEFINES + *********************/ +#define JPEG_PIXEL_SIZE 3 /* RGB888 */ +#define JPEG_SIGNATURE 0xFFD8FF +#define IS_JPEG_SIGNATURE(x) (((x) & 0x00FFFFFF) == JPEG_SIGNATURE) + +/********************** + * TYPEDEFS + **********************/ +typedef struct error_mgr_s { + struct jpeg_error_mgr pub; + jmp_buf jb; +} error_mgr_t; + +/********************** + * STATIC PROTOTYPES + **********************/ +static lv_result_t decoder_info(lv_image_decoder_t * decoder, const void * src, lv_image_header_t * header); +static lv_result_t decoder_open(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc); +static void decoder_close(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc); +static const void * decode_jpeg_file(const char * filename); +static bool get_jpeg_size(const char * filename, uint32_t * width, uint32_t * height); +static void error_exit(j_common_ptr cinfo); +static lv_result_t try_cache(lv_image_decoder_dsc_t * dsc); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Register the JPEG decoder functions in LVGL + */ +void lv_libjpeg_turbo_init(void) +{ + lv_image_decoder_t * dec = lv_image_decoder_create(); + lv_image_decoder_set_info_cb(dec, decoder_info); + lv_image_decoder_set_open_cb(dec, decoder_open); + lv_image_decoder_set_close_cb(dec, decoder_close); +} + +void lv_libjpeg_turbo_deinit(void) +{ + lv_image_decoder_t * dec = NULL; + while((dec = lv_image_decoder_get_next(dec)) != NULL) { + if(dec->info_cb == decoder_info) { + lv_image_decoder_delete(dec); + break; + } + } +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +/** + * Get info about a JPEG image + * @param src can be file name or pointer to a C array + * @param header store the info here + * @return LV_RESULT_OK: no error; LV_RESULT_INVALID: can't get the info + */ +static lv_result_t decoder_info(lv_image_decoder_t * decoder, const void * src, lv_image_header_t * header) +{ + LV_UNUSED(decoder); /*Unused*/ + lv_image_src_t src_type = lv_image_src_get_type(src); /*Get the source type*/ + + /*If it's a JPEG file...*/ + if(src_type == LV_IMAGE_SRC_FILE) { + const char * fn = src; + + lv_fs_file_t f; + lv_fs_res_t res = lv_fs_open(&f, fn, LV_FS_MODE_RD); + if(res != LV_FS_RES_OK) { + LV_LOG_WARN("Can't open file: %s", fn); + return LV_RESULT_INVALID; + } + + uint32_t jpg_signature = 0; + uint32_t rn; + lv_fs_read(&f, &jpg_signature, sizeof(jpg_signature), &rn); + lv_fs_close(&f); + + if(rn != sizeof(jpg_signature)) { + LV_LOG_WARN("file: %s signature len = %" LV_PRIu32 " error", fn, rn); + return LV_RESULT_INVALID; + } + + bool is_jpeg_ext = (strcmp(lv_fs_get_ext(fn), "jpg") == 0) + || (strcmp(lv_fs_get_ext(fn), "jpeg") == 0); + + if(!IS_JPEG_SIGNATURE(jpg_signature)) { + if(is_jpeg_ext) { + LV_LOG_WARN("file: %s signature = 0X%" LV_PRIX32 " error", fn, jpg_signature); + } + return LV_RESULT_INVALID; + } + + uint32_t width; + uint32_t height; + + if(!get_jpeg_size(fn, &width, &height)) { + return LV_RESULT_INVALID; + } + + /*Save the data in the header*/ + header->always_zero = 0; + header->cf = LV_COLOR_FORMAT_RGB888; + header->w = width; + header->h = height; + + return LV_RESULT_OK; + } + + return LV_RESULT_INVALID; /*If didn't succeeded earlier then it's an error*/ +} + +/** + * Open a JPEG 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_IMAGE_DECODER_OPEN_FAIL` if failed + */ +static lv_result_t decoder_open(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc) +{ + LV_UNUSED(decoder); /*Unused*/ + + /*Check the cache first*/ + if(try_cache(dsc) == LV_RESULT_OK) return LV_RESULT_OK; + + /*If it's a JPEG file...*/ + if(dsc->src_type == LV_IMAGE_SRC_FILE) { + const char * fn = dsc->src; + + lv_cache_lock(); + lv_cache_entry_t * cache = lv_cache_add(dsc->header.w * dsc->header.h * JPEG_PIXEL_SIZE); + if(cache == NULL) { + lv_cache_unlock(); + return LV_RESULT_INVALID; + } + + uint32_t t = lv_tick_get(); + const void * decoded_img = decode_jpeg_file(fn); + t = lv_tick_elaps(t); + cache->weight = t; + cache->data = decoded_img; + cache->free_data = 1; + if(dsc->src_type == LV_IMAGE_SRC_FILE) { + cache->src = lv_strdup(dsc->src); + cache->src_type = LV_CACHE_SRC_TYPE_STR; + cache->free_src = 1; + } + else { + cache->src_type = LV_CACHE_SRC_TYPE_PTR; + cache->src = dsc->src; + } + + dsc->img_data = lv_cache_get_data(cache); + dsc->user_data = cache; + + lv_cache_unlock(); + return LV_RESULT_OK; /*If not returned earlier then it failed*/ + } + + return LV_RESULT_INVALID; /*If not returned earlier then it failed*/ +} + +/** + * Free the allocated resources + */ +static void decoder_close(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc) +{ + LV_UNUSED(decoder); /*Unused*/ + lv_cache_lock(); + lv_cache_release(dsc->user_data); + lv_cache_unlock(); +} + +static lv_result_t try_cache(lv_image_decoder_dsc_t * dsc) +{ + lv_cache_lock(); + if(dsc->src_type == LV_IMAGE_SRC_FILE) { + const char * fn = dsc->src; + + lv_cache_entry_t * cache = lv_cache_find(fn, LV_CACHE_SRC_TYPE_STR, 0, 0); + if(cache) { + dsc->img_data = lv_cache_get_data(cache); + dsc->user_data = cache; /*Save the cache to release it in decoder_close*/ + lv_cache_unlock(); + return LV_RESULT_OK; + } + } + + lv_cache_unlock(); + return LV_RESULT_INVALID; +} + +static uint8_t * alloc_file(const char * filename, uint32_t * size) +{ + uint8_t * data = NULL; + lv_fs_file_t f; + uint32_t data_size; + uint32_t rn; + lv_fs_res_t res; + + *size = 0; + + res = lv_fs_open(&f, filename, LV_FS_MODE_RD); + if(res != LV_FS_RES_OK) { + LV_LOG_WARN("can't open %s", filename); + return NULL; + } + + res = lv_fs_seek(&f, 0, LV_FS_SEEK_END); + if(res != LV_FS_RES_OK) { + goto failed; + } + + res = lv_fs_tell(&f, &data_size); + if(res != LV_FS_RES_OK) { + goto failed; + } + + res = lv_fs_seek(&f, 0, LV_FS_SEEK_SET); + if(res != LV_FS_RES_OK) { + goto failed; + } + + /*Read file to buffer*/ + data = lv_malloc(data_size); + if(data == NULL) { + LV_LOG_WARN("malloc failed for data"); + goto failed; + } + + res = lv_fs_read(&f, data, data_size, &rn); + + if(res == LV_FS_RES_OK && rn == data_size) { + *size = rn; + } + else { + LV_LOG_WARN("read file failed"); + lv_free(data); + data = NULL; + } + +failed: + lv_fs_close(&f); + + return data; +} + +static const void * decode_jpeg_file(const char * filename) +{ + /* This struct contains the JPEG decompression parameters and pointers to + * working space (which is allocated as needed by the JPEG library). + */ + struct jpeg_decompress_struct cinfo; + /* We use our private extension JPEG error handler. + * Note that this struct must live as long as the main JPEG parameter + * struct, to avoid dangling-pointer problems. + */ + error_mgr_t jerr; + + /* More stuff */ + JSAMPARRAY buffer; /* Output row buffer */ + int row_stride; /* physical row width in output buffer */ + + uint8_t * output_buffer = NULL; + + /* In this example we want to open the input file before doing anything else, + * so that the setjmp() error recovery below can assume the file is open. + * VERY IMPORTANT: use "b" option to fopen() if you are on a machine that + * requires it in order to read binary files. + */ + + uint32_t data_size; + uint8_t * data = alloc_file(filename, &data_size); + if(data == NULL) { + LV_LOG_WARN("can't load file %s", filename); + return NULL; + } + + /* allocate and initialize JPEG decompression object */ + + /* We set up the normal JPEG error routines, then override error_exit. */ + cinfo.err = jpeg_std_error(&jerr.pub); + jerr.pub.error_exit = error_exit; + /* Establish the setjmp return context for my_error_exit to use. */ + if(setjmp(jerr.jb)) { + + LV_LOG_WARN("decoding error"); + + if(output_buffer) { + lv_draw_buf_free(output_buffer); + } + + /* If we get here, the JPEG code has signaled an error. + * We need to clean up the JPEG object, close the input file, and return. + */ + jpeg_destroy_decompress(&cinfo); + lv_free(data); + return NULL; + } + /* Now we can initialize the JPEG decompression object. */ + jpeg_create_decompress(&cinfo); + + /* specify data source (eg, a file or buffer) */ + + jpeg_mem_src(&cinfo, data, data_size); + + /* read file parameters with jpeg_read_header() */ + + jpeg_read_header(&cinfo, TRUE); + + /* We can ignore the return value from jpeg_read_header since + * (a) suspension is not possible with the stdio data source, and + * (b) we passed TRUE to reject a tables-only JPEG file as an error. + * See libjpeg.doc for more info. + */ + + /* set parameters for decompression */ + + cinfo.out_color_space = JCS_EXT_BGR; + + /* In this example, we don't need to change any of the defaults set by + * jpeg_read_header(), so we do nothing here. + */ + + /* Start decompressor */ + + jpeg_start_decompress(&cinfo); + + /* We can ignore the return value since suspension is not possible + * with the stdio data source. + */ + + /* We may need to do some setup of our own at this point before reading + * the data. After jpeg_start_decompress() we have the correct scaled + * output image dimensions available, as well as the output colormap + * if we asked for color quantization. + * In this example, we need to make an output work buffer of the right size. + */ + /* JSAMPLEs per row in output buffer */ + row_stride = cinfo.output_width * cinfo.output_components; + /* Make a one-row-high sample array that will go away when done with image */ + buffer = (*cinfo.mem->alloc_sarray) + ((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1); + + size_t output_buffer_size = cinfo.output_width * cinfo.output_height * JPEG_PIXEL_SIZE; + output_buffer = lv_draw_buf_malloc(output_buffer_size, LV_COLOR_FORMAT_RGB888); + if(output_buffer) { + uint8_t * cur_pos = output_buffer; + size_t stride = cinfo.output_width * JPEG_PIXEL_SIZE; + + /* while (scan lines remain to be read) */ + /* jpeg_read_scanlines(...); */ + + /* Here we use the library's state variable cinfo.output_scanline as the + * loop counter, so that we don't have to keep track ourselves. + */ + while(cinfo.output_scanline < cinfo.output_height) { + /* jpeg_read_scanlines expects an array of pointers to scanlines. + * Here the array is only one element long, but you could ask for + * more than one scanline at a time if that's more convenient. + */ + jpeg_read_scanlines(&cinfo, buffer, 1); + + /* Assume put_scanline_someplace wants a pointer and sample count. */ + lv_memcpy(cur_pos, buffer[0], stride); + cur_pos += stride; + } + } + + /* Finish decompression */ + + jpeg_finish_decompress(&cinfo); + + /* We can ignore the return value since suspension is not possible + * with the stdio data source. + */ + + /* Release JPEG decompression object */ + + /* This is an important step since it will release a good deal of memory. */ + jpeg_destroy_decompress(&cinfo); + + /* After finish_decompress, we can close the input file. + * Here we postpone it until after no more JPEG errors are possible, + * so as to simplify the setjmp error logic above. (Actually, I don't + * think that jpeg_destroy can do an error exit, but why assume anything...) + */ + lv_free(data); + + /* At this point you may want to check to see whether any corrupt-data + * warnings occurred (test whether jerr.pub.num_warnings is nonzero). + */ + + /* And we're done! */ + return output_buffer; +} + +static bool get_jpeg_size(const char * filename, uint32_t * width, uint32_t * height) +{ + struct jpeg_decompress_struct cinfo; + error_mgr_t jerr; + + uint8_t * data = NULL; + uint32_t data_size; + data = alloc_file(filename, &data_size); + if(data == NULL) { + return false; + } + + cinfo.err = jpeg_std_error(&jerr.pub); + jerr.pub.error_exit = error_exit; + + if(setjmp(jerr.jb)) { + LV_LOG_WARN("read jpeg head failed"); + jpeg_destroy_decompress(&cinfo); + lv_free(data); + return false; + } + + jpeg_create_decompress(&cinfo); + + jpeg_mem_src(&cinfo, data, data_size); + + int ret = jpeg_read_header(&cinfo, TRUE); + + if(ret == JPEG_HEADER_OK) { + *width = cinfo.image_width; + *height = cinfo.image_height; + } + else { + LV_LOG_WARN("read jpeg head failed: %d", ret); + } + + jpeg_destroy_decompress(&cinfo); + + lv_free(data); + + return (ret == JPEG_HEADER_OK); +} + +static void error_exit(j_common_ptr cinfo) +{ + error_mgr_t * myerr = (error_mgr_t *)cinfo->err; + (*cinfo->err->output_message)(cinfo); + longjmp(myerr->jb, 1); +} + +#endif /*LV_USE_LIBJPEG_TURBO*/ diff --git a/src/libs/libjpeg_turbo/lv_libjpeg_turbo.h b/src/libs/libjpeg_turbo/lv_libjpeg_turbo.h new file mode 100644 index 0000000000..ff5e5df7d4 --- /dev/null +++ b/src/libs/libjpeg_turbo/lv_libjpeg_turbo.h @@ -0,0 +1,48 @@ +/** + * @file lv_libjpeg_turbo.h + * + */ + +#ifndef LV_LIBJPEG_TURBO_H +#define LV_LIBJPEG_TURBO_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include "../../lv_conf_internal.h" +#if LV_USE_LIBJPEG_TURBO + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Register the JPEG-Turbo decoder functions in LVGL + */ +void lv_libjpeg_turbo_init(void); + +void lv_libjpeg_turbo_deinit(void); + +/********************** + * MACROS + **********************/ + +#endif /*LV_USE_LIBJPEG_TURBO*/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*LV_LIBJPEG_TURBO_H*/ \ No newline at end of file diff --git a/src/lv_conf_internal.h b/src/lv_conf_internal.h index 5ca226eced..bd5dec37bf 100644 --- a/src/lv_conf_internal.h +++ b/src/lv_conf_internal.h @@ -1983,6 +1983,16 @@ #endif #endif +/* libjpeg-turbo decoder library. + * Supports complete JPEG specifications and high-performance JPEG decoding. */ +#ifndef LV_USE_LIBJPEG_TURBO + #ifdef CONFIG_LV_USE_LIBJPEG_TURBO + #define LV_USE_LIBJPEG_TURBO CONFIG_LV_USE_LIBJPEG_TURBO + #else + #define LV_USE_LIBJPEG_TURBO 0 + #endif +#endif + /*GIF decoder library*/ #ifndef LV_USE_GIF #ifdef CONFIG_LV_USE_GIF diff --git a/src/lv_init.c b/src/lv_init.c index 33bc50be96..7daa29b3b1 100644 --- a/src/lv_init.c +++ b/src/lv_init.c @@ -17,6 +17,7 @@ #include "libs/fsdrv/lv_fsdrv.h" #include "libs/gif/lv_gif.h" #include "libs/tjpgd/lv_tjpgd.h" +#include "libs/libjpeg_turbo/lv_libjpeg_turbo.h" #include "libs/lodepng/lv_lodepng.h" #include "draw/lv_draw.h" #include "misc/lv_cache.h" @@ -237,6 +238,10 @@ void lv_init(void) lv_tjpgd_init(); #endif +#if LV_USE_LIBJPEG_TURBO + lv_libjpeg_turbo_init(); +#endif + #if LV_USE_BMP lv_bmp_init(); #endif diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 9eb1ad50dd..f5a4eccaf5 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -176,6 +176,10 @@ set(generate_test_runner_rb ${CMAKE_CURRENT_SOURCE_DIR}/unity/generate_test_runner.rb) set(generate_test_runner_config ${CMAKE_CURRENT_SOURCE_DIR}/config.yml) +# libjpeg is required for the jpeg test case +find_package(JPEG REQUIRED) +include_directories(${JPEG_INCLUDE_DIR}) + # disable test targets for build only tests if (ENABLE_TESTS) file( GLOB_RECURSE TEST_CASE_FILES src/test_cases/*.c ) @@ -204,7 +208,7 @@ foreach( test_case_fname ${TEST_CASE_FILES} ) ${test_case_fname} ${test_runner_fname} ) - target_link_libraries(${test_name} test_common lvgl_demos lvgl png m ${TEST_LIBS}) + target_link_libraries(${test_name} test_common lvgl_demos lvgl png ${JPEG_LIBRARIES} m ${TEST_LIBS}) target_include_directories(${test_name} PUBLIC ${TEST_INCLUDE_DIRS}) target_compile_options(${test_name} PUBLIC ${LVGL_TESTFILE_COMPILE_OPTIONS}) diff --git a/tests/ref_imgs/libs/jpg_2.png b/tests/ref_imgs/libs/jpg_2.png new file mode 100644 index 0000000000..85046571c1 Binary files /dev/null and b/tests/ref_imgs/libs/jpg_2.png differ diff --git a/tests/src/lv_test_conf_full.h b/tests/src/lv_test_conf_full.h index 59ad269abf..29485e8d4d 100644 --- a/tests/src/lv_test_conf_full.h +++ b/tests/src/lv_test_conf_full.h @@ -64,6 +64,7 @@ #define LV_USE_LODEPNG 1 #define LV_USE_BMP 1 #define LV_USE_TJPGD 1 +#define LV_USE_LIBJPEG_TURBO 1 #define LV_USE_GIF 1 #define LV_USE_QRCODE 1 #define LV_USE_BARCODE 1 diff --git a/tests/src/test_cases/libs/test_libjpeg_turbo.c b/tests/src/test_cases/libs/test_libjpeg_turbo.c new file mode 100644 index 0000000000..f3b0c3edf8 --- /dev/null +++ b/tests/src/test_cases/libs/test_libjpeg_turbo.c @@ -0,0 +1,54 @@ +#if LV_BUILD_TEST +#include "../lvgl.h" + +#include "unity/unity.h" +#include "lv_test_helpers.h" + +void setUp(void) +{ + /* Function run before every test */ +} + +void tearDown(void) +{ + /* Function run after every test */ +} + +static void create_images(void) +{ + lv_obj_clean(lv_scr_act()); + + lv_obj_t * img; + + img = lv_img_create(lv_scr_act()); + lv_img_set_src(img, "A:src/test_assets/test_img_lvgl_logo.jpg"); + lv_obj_center(img); +} + +void test_jpg_2(void) +{ + /* Temporarily remove tjpgd decoder */ + lv_tjpgd_deinit(); + + create_images(); + + TEST_ASSERT_EQUAL_SCREENSHOT("libs/jpg_2.png"); + + + uint32_t mem_before = lv_test_get_free_mem(); + for(uint32_t i = 0; i < 20; i++) { + create_images(); + + lv_obj_invalidate(lv_scr_act()); + lv_refr_now(NULL); + } + + TEST_ASSERT_EQUAL_SCREENSHOT("libs/jpg_2.png"); + + TEST_ASSERT_EQUAL(mem_before, lv_test_get_free_mem()); + + /* Re-add tjpgd decoder */ + lv_tjpgd_init(); +} + +#endif diff --git a/tests/src/test_cases/libs/test_tjpgd.c b/tests/src/test_cases/libs/test_tjpgd.c index 9695a5f0b6..8f98e93e42 100644 --- a/tests/src/test_cases/libs/test_tjpgd.c +++ b/tests/src/test_cases/libs/test_tjpgd.c @@ -41,6 +41,9 @@ static void create_images(void) void test_tjpgd_1(void) { + /* Temporarily remove libjpeg_turbo decoder */ + lv_libjpeg_turbo_deinit(); + create_images(); TEST_ASSERT_EQUAL_SCREENSHOT("libs/jpg_1.png"); @@ -58,6 +61,8 @@ void test_tjpgd_1(void) TEST_ASSERT_EQUAL(mem_before, lv_test_get_free_mem()); + /* Re-add libjpeg_turbo decoder */ + lv_libjpeg_turbo_init(); } #endif