diff --git a/esp_jpeg/CHANGELOG.md b/esp_jpeg/CHANGELOG.md new file mode 100644 index 0000000000..1e72e7ae52 --- /dev/null +++ b/esp_jpeg/CHANGELOG.md @@ -0,0 +1,28 @@ +## 1.2.0 + +- Added option to for passing user defined working buffer + +## 1.1.0 + +- Added support for decoding images without Huffman tables +- Fixed undefined configuration options from Kconfig + +## 1.0.5~3 + +- Added option to swap output color bytes regardless of JD_FORMAT + +## 1.0.4 + +- Added ROM implementation support for ESP32-C6 + +## 1.0.2 + +- Fixed compiler warnings + +## 1.0.1 + +- Fixed: exclude ESP32-C2 from list of ROM implementations + +## 1.0.0 + +- Initial version diff --git a/esp_jpeg/README.md b/esp_jpeg/README.md index bd470ebcb1..ce27d38c7a 100644 --- a/esp_jpeg/README.md +++ b/esp_jpeg/README.md @@ -1,6 +1,7 @@ # JPEG Decoder: TJpgDec - Tiny JPEG Decompressor [![Component Registry](https://components.espressif.com/components/espressif/esp_jpeg/badge.svg)](https://components.espressif.com/components/espressif/esp_jpeg) +![maintenance-status](https://img.shields.io/badge/maintenance-actively--developed-brightgreen.svg) TJpgDec is a lightweight JPEG image decompressor optimized for embedded systems with minimal memory consumption. diff --git a/esp_jpeg/examples/get_started/sdkconfig.defaults b/esp_jpeg/examples/get_started/sdkconfig.defaults new file mode 100644 index 0000000000..15e8bc2b8e --- /dev/null +++ b/esp_jpeg/examples/get_started/sdkconfig.defaults @@ -0,0 +1,4 @@ +# This file was generated using idf.py save-defconfig. It can be edited manually. +# Espressif IoT Development Framework (ESP-IDF) 5.5.0 Project Minimal Configuration +# +CONFIG_TOUCH_SUPPRESS_DEPRECATE_WARN=y diff --git a/esp_jpeg/idf_component.yml b/esp_jpeg/idf_component.yml index 006c8edc44..03d8cc4d3f 100644 --- a/esp_jpeg/idf_component.yml +++ b/esp_jpeg/idf_component.yml @@ -1,4 +1,4 @@ -version: "1.1.0" +version: "1.2.0" description: "JPEG Decoder: TJpgDec" url: https://github.com/espressif/idf-extra-components/tree/master/esp_jpeg/ dependencies: diff --git a/esp_jpeg/include/jpeg_decoder.h b/esp_jpeg/include/jpeg_decoder.h index 849b946753..d7cab7aa3b 100644 --- a/esp_jpeg/include/jpeg_decoder.h +++ b/esp_jpeg/include/jpeg_decoder.h @@ -48,6 +48,13 @@ typedef struct esp_jpeg_image_cfg_s { uint8_t swap_color_bytes: 1; /*!< Swap first and last color bytes */ } flags; + struct { + void *working_buffer; /*!< If set to NULL, a working buffer will be allocated in esp_jpeg_decode(). + Tjpgd does not use dynamic allocation, se we pass this buffer to Tjpgd that uses it as scratchpad */ + size_t working_buffer_size; /*!< Size of the working buffer. Must be set it working_buffer != NULL. + Default size is 3.1kB or 65kB if JD_FASTDECODE == 2 */ + } advanced; + struct { uint32_t read; /*!< Internal count of read bytes */ } priv; diff --git a/esp_jpeg/jpeg_decoder.c b/esp_jpeg/jpeg_decoder.c index 9f052bd32f..e2bb4e149a 100644 --- a/esp_jpeg/jpeg_decoder.c +++ b/esp_jpeg/jpeg_decoder.c @@ -79,13 +79,21 @@ esp_err_t esp_jpeg_decode(esp_jpeg_image_cfg_t *cfg, esp_jpeg_image_output_t *im assert(cfg != NULL); assert(img != NULL); - workbuf = heap_caps_malloc(JPEG_WORK_BUF_SIZE, MALLOC_CAP_DEFAULT); - ESP_GOTO_ON_FALSE(workbuf, ESP_ERR_NO_MEM, err, TAG, "no mem for JPEG work buffer"); + const bool allocate_buffer = (cfg->advanced.working_buffer == NULL); + const size_t workbuf_size = allocate_buffer ? JPEG_WORK_BUF_SIZE : cfg->advanced.working_buffer_size; + if (allocate_buffer) { + workbuf = heap_caps_malloc(JPEG_WORK_BUF_SIZE, MALLOC_CAP_DEFAULT); + ESP_GOTO_ON_FALSE(workbuf, ESP_ERR_NO_MEM, err, TAG, "no mem for JPEG work buffer"); + } else { + workbuf = cfg->advanced.working_buffer; + ESP_RETURN_ON_FALSE(workbuf_size != 0, ESP_ERR_INVALID_ARG, TAG, "Working buffer size not defined!"); + } + cfg->priv.read = 0; /* Prepare image */ - res = jd_prepare(&JDEC, jpeg_decode_in_cb, workbuf, JPEG_WORK_BUF_SIZE, cfg); + res = jd_prepare(&JDEC, jpeg_decode_in_cb, workbuf, workbuf_size, cfg); ESP_GOTO_ON_FALSE((res == JDR_OK), ESP_FAIL, err, TAG, "Error in preparing JPEG image! %d", res); uint8_t scale_div = jpeg_get_div_by_scale(cfg->out_scale); @@ -104,7 +112,7 @@ esp_err_t esp_jpeg_decode(esp_jpeg_image_cfg_t *cfg, esp_jpeg_image_output_t *im ESP_GOTO_ON_FALSE((res == JDR_OK), ESP_FAIL, err, TAG, "Error in decoding JPEG image! %d", res); err: - if (workbuf) { + if (workbuf && allocate_buffer) { free(workbuf); } diff --git a/esp_jpeg/test_apps/main/tjpgd_test.c b/esp_jpeg/test_apps/main/tjpgd_test.c index a7e58838de..d0870a297d 100644 --- a/esp_jpeg/test_apps/main/tjpgd_test.c +++ b/esp_jpeg/test_apps/main/tjpgd_test.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Unlicense OR CC0-1.0 */ @@ -18,16 +18,29 @@ #define TESTW 46 #define TESTH 46 -TEST_CASE("Test JPEG decompression library", "[esp_jpeg]") +void esp_jpeg_print_ascii(unsigned char *rgb888, esp_jpeg_image_output_t *outimg) { char aapix[] = " .:;+=xX$$"; + unsigned char *p = rgb888 + 2; + + for (int y = 0; y < outimg->width; y++) { + for (int x = 0; x < outimg->height; x++) { + int v = ((*p) * (sizeof(aapix) - 2) * 2) / 256; + printf("%c%c", aapix[v / 2], aapix[(v + 1) / 2]); + p += 3; + } + printf("%c%c", ' ', '\n'); + } +} + +TEST_CASE("Test JPEG decompression library", "[esp_jpeg]") +{ unsigned char *decoded, *p; const unsigned char *o; - int x, y, v; int decoded_outsize = TESTW * TESTH * 3; decoded = malloc(decoded_outsize); - for (x = 0; x < decoded_outsize; x += 2) { + for (int x = 0; x < decoded_outsize; x += 2) { decoded[x] = 0; decoded[x + 1] = 0xff; } @@ -54,7 +67,7 @@ TEST_CASE("Test JPEG decompression library", "[esp_jpeg]") p = decoded; o = logo_rgb888; - for (x = 0; x < outimg.width * outimg.height; x++) { + for (int x = 0; x < outimg.width * outimg.height; x++) { /* The color can be +- 2 */ TEST_ASSERT_UINT8_WITHIN(2, o[0], p[0]); TEST_ASSERT_UINT8_WITHIN(2, o[1], p[1]); @@ -64,17 +77,64 @@ TEST_CASE("Test JPEG decompression library", "[esp_jpeg]") o += 3; } - p = decoded + 2; - for (y = 0; y < outimg.width; y++) { - for (x = 0; x < outimg.height; x++) { - v = ((*p) * (sizeof(aapix) - 2) * 2) / 256; - printf("%c%c", aapix[v / 2], aapix[(v + 1) / 2]); - p += 3; - } - printf("%c%c", ' ', '\n'); + esp_jpeg_print_ascii(decoded, &outimg); + + free(decoded); +} + +#define WORKING_BUFFER_SIZE 4096 +TEST_CASE("Test JPEG decompression library: User defined working buffer", "[esp_jpeg]") +{ + unsigned char *decoded, *p; + const unsigned char *o; + int decoded_outsize = TESTW * TESTH * 3; + + decoded = malloc(decoded_outsize); + uint8_t *working_buf = malloc(WORKING_BUFFER_SIZE); + assert(decoded); + assert(working_buf); + + for (int x = 0; x < decoded_outsize; x += 2) { + decoded[x] = 0; + decoded[x + 1] = 0xff; } + /* JPEG decode */ + esp_jpeg_image_cfg_t jpeg_cfg = { + .indata = (uint8_t *)logo_jpg, + .indata_size = logo_jpg_len, + .outbuf = decoded, + .outbuf_size = decoded_outsize, + .out_format = JPEG_IMAGE_FORMAT_RGB888, + .out_scale = JPEG_IMAGE_SCALE_0, + .flags = { + .swap_color_bytes = 0, + }, + .advanced = { + .working_buffer = working_buf, + .working_buffer_size = WORKING_BUFFER_SIZE, + }, + }; + esp_jpeg_image_output_t outimg; + esp_err_t err = esp_jpeg_decode(&jpeg_cfg, &outimg); + TEST_ASSERT_EQUAL(err, ESP_OK); + /* Decoded image size */ + TEST_ASSERT_EQUAL(outimg.width, TESTW); + TEST_ASSERT_EQUAL(outimg.height, TESTH); + + p = decoded; + o = logo_rgb888; + for (int x = 0; x < outimg.width * outimg.height; x++) { + /* The color can be +- 2 */ + TEST_ASSERT_UINT8_WITHIN(2, o[0], p[0]); + TEST_ASSERT_UINT8_WITHIN(2, o[1], p[1]); + TEST_ASSERT_UINT8_WITHIN(2, o[2], p[2]); + + p += 3; + o += 3; + } + free(working_buf); free(decoded); }