chore: 添加 esp_jpeg 托管组件到仓库
将 ESP Component Registry 自动下载的 espressif__esp_jpeg 组件纳入版本管理, 避免其他开发者拉取后需要重新下载。 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
c4de88d7ee
commit
38b71ce221
1
managed_components/espressif__esp_jpeg/.component_hash
Normal file
1
managed_components/espressif__esp_jpeg/.component_hash
Normal file
@ -0,0 +1 @@
|
|||||||
|
defb83669293cbf86d0fa86b475ba5517aceed04ed70db435388c151ab37b5d7
|
||||||
40
managed_components/espressif__esp_jpeg/CHANGELOG.md
Normal file
40
managed_components/espressif__esp_jpeg/CHANGELOG.md
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
## 1.3.1
|
||||||
|
|
||||||
|
- Fixed the format of Kconfig file
|
||||||
|
|
||||||
|
## 1.3.0
|
||||||
|
|
||||||
|
- Added option to get image size without decoding it
|
||||||
|
|
||||||
|
## 1.2.1
|
||||||
|
|
||||||
|
- Fixed decoding of non-conforming 0xFFFF marker
|
||||||
|
|
||||||
|
## 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
|
||||||
1
managed_components/espressif__esp_jpeg/CHECKSUMS.json
Normal file
1
managed_components/espressif__esp_jpeg/CHECKSUMS.json
Normal file
File diff suppressed because one or more lines are too long
14
managed_components/espressif__esp_jpeg/CMakeLists.txt
Normal file
14
managed_components/espressif__esp_jpeg/CMakeLists.txt
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
set(sources "jpeg_decoder.c")
|
||||||
|
set(includes "include")
|
||||||
|
|
||||||
|
# Compile only when cannot use ROM code
|
||||||
|
if(NOT CONFIG_JD_USE_ROM)
|
||||||
|
list(APPEND sources "tjpgd/tjpgd.c")
|
||||||
|
list(APPEND includes "tjpgd")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(CONFIG_JD_DEFAULT_HUFFMAN)
|
||||||
|
list(APPEND sources "jpeg_default_huffman_table.c")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
idf_component_register(SRCS ${sources} INCLUDE_DIRS ${includes})
|
||||||
80
managed_components/espressif__esp_jpeg/Kconfig
Normal file
80
managed_components/espressif__esp_jpeg/Kconfig
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
menu "JPEG Decoder"
|
||||||
|
|
||||||
|
config JD_USE_ROM
|
||||||
|
bool "Use TinyJPG Decoder from ROM"
|
||||||
|
depends on ESP_ROM_HAS_JPEG_DECODE
|
||||||
|
default y
|
||||||
|
help
|
||||||
|
By default, Espressif SoCs use TJpg decoder implemented in ROM code.
|
||||||
|
If this feature is disabled, new configuration of TJpg decoder can be used.
|
||||||
|
Refer to REAME.md for more details.
|
||||||
|
|
||||||
|
config JD_SZBUF
|
||||||
|
int "Size of stream input buffer"
|
||||||
|
depends on !JD_USE_ROM
|
||||||
|
default 512
|
||||||
|
|
||||||
|
config JD_FORMAT
|
||||||
|
int
|
||||||
|
depends on !JD_USE_ROM
|
||||||
|
default 0 if JD_FORMAT_RGB888
|
||||||
|
default 1 if JD_FORMAT_RGB565
|
||||||
|
|
||||||
|
choice
|
||||||
|
prompt "Output pixel format"
|
||||||
|
depends on !JD_USE_ROM
|
||||||
|
default JD_FORMAT_RGB888
|
||||||
|
help
|
||||||
|
Output format is selected at runtime.
|
||||||
|
|
||||||
|
config JD_FORMAT_RGB888
|
||||||
|
bool "Support RGB565 and RGB888 output (16-bit/pix and 24-bit/pix)"
|
||||||
|
config JD_FORMAT_RGB565
|
||||||
|
bool "Support RGB565 output (16-bit/pix)"
|
||||||
|
endchoice
|
||||||
|
|
||||||
|
config JD_USE_SCALE
|
||||||
|
bool "Enable descaling"
|
||||||
|
depends on !JD_USE_ROM
|
||||||
|
default y
|
||||||
|
help
|
||||||
|
If scaling is enabled, size of output image can be lowered during decoding.
|
||||||
|
|
||||||
|
config JD_TBLCLIP
|
||||||
|
bool "Use table conversion for saturation arithmetic"
|
||||||
|
depends on !JD_USE_ROM
|
||||||
|
default y
|
||||||
|
help
|
||||||
|
Use table conversion for saturation arithmetic. A bit faster, but increases 1 KB of code size.
|
||||||
|
|
||||||
|
config JD_FASTDECODE
|
||||||
|
int
|
||||||
|
depends on !JD_USE_ROM
|
||||||
|
default 0 if JD_FASTDECODE_BASIC
|
||||||
|
default 1 if JD_FASTDECODE_32BIT
|
||||||
|
default 2 if JD_FASTDECODE_TABLE
|
||||||
|
|
||||||
|
choice
|
||||||
|
prompt "Optimization level"
|
||||||
|
depends on !JD_USE_ROM
|
||||||
|
default JD_FASTDECODE_32BIT
|
||||||
|
|
||||||
|
config JD_FASTDECODE_BASIC
|
||||||
|
bool "Basic optimization. Suitable for 8/16-bit MCUs"
|
||||||
|
config JD_FASTDECODE_32BIT
|
||||||
|
bool "+ 32-bit barrel shifter. Suitable for 32-bit MCUs"
|
||||||
|
config JD_FASTDECODE_TABLE
|
||||||
|
bool "+ Table conversion for huffman decoding (wants 6 << HUFF_BIT bytes of RAM)"
|
||||||
|
endchoice
|
||||||
|
|
||||||
|
config JD_DEFAULT_HUFFMAN
|
||||||
|
bool "Support images without Huffman table"
|
||||||
|
depends on !JD_USE_ROM
|
||||||
|
default n
|
||||||
|
help
|
||||||
|
Enable this option to support decoding JPEG images that lack an embedded Huffman table.
|
||||||
|
When enabled, a default Huffman table is used during decoding, allowing the JPEG decoder to handle
|
||||||
|
images without explicitly provided Huffman tables.
|
||||||
|
|
||||||
|
Note: Enabling this option increases ROM usage due to the inclusion of default Huffman tables.
|
||||||
|
endmenu
|
||||||
112
managed_components/espressif__esp_jpeg/README.md
Normal file
112
managed_components/espressif__esp_jpeg/README.md
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
# JPEG Decoder: TJpgDec - Tiny JPEG Decompressor
|
||||||
|
|
||||||
|
[](https://components.espressif.com/components/espressif/esp_jpeg)
|
||||||
|

|
||||||
|
|
||||||
|
TJpgDec is a lightweight JPEG image decompressor optimized for embedded systems with minimal memory consumption.
|
||||||
|
|
||||||
|
On some microcontrollers, TJpgDec is available in ROM and will be used by default, though this can be disabled in menuconfig if desired[^1].
|
||||||
|
|
||||||
|
[^1]: **_NOTE:_** When the ROM decoder is used, the configuration can't be changed. The configuration is fixed.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
**Compilation configuration:**
|
||||||
|
- Stream input buffer size (default: 512 bytes)
|
||||||
|
- Output pixel format (default: RGB888; options: RGB888/RGB565)
|
||||||
|
- Enable/disable output descaling (default: enabled)
|
||||||
|
- Use table-based saturation for arithmetic operations (default: enabled)
|
||||||
|
- Use default Huffman tables: Useful from decoding frames from cameras, that do not provide Huffman tables (default: disabled to save ROM)
|
||||||
|
- Three optimization levels (default: 32-bit MCUs) for different CPU types:
|
||||||
|
- 8/16-bit MCUs
|
||||||
|
- 32-bit MCUs
|
||||||
|
- Table-based Huffman decoding
|
||||||
|
|
||||||
|
**Runtime configuration:**
|
||||||
|
- Pixel format options: RGB888, RGB565
|
||||||
|
- Selectable scaling ratios: 1/1, 1/2, 1/4, or 1/8 (chosen at decompression)
|
||||||
|
- Option to swap the first and last bytes of color values
|
||||||
|
|
||||||
|
## TJpgDec in ROM
|
||||||
|
|
||||||
|
On certain microcontrollers, TJpgDec is available in ROM and used by default. This can be disabled in menuconfig if you prefer to use the library code provided in this component.
|
||||||
|
|
||||||
|
### List of MCUs, which have TJpgDec in ROM
|
||||||
|
- ESP32
|
||||||
|
- ESP32-S3
|
||||||
|
- ESP32-C3
|
||||||
|
- ESP32-C6
|
||||||
|
- ESP32-C5
|
||||||
|
- ESP32-C61
|
||||||
|
|
||||||
|
### Fixed compilation configuration of the ROM code
|
||||||
|
The ROM version uses the following fixed settings:
|
||||||
|
- Stream input buffer: 512 bytes
|
||||||
|
- Output pixel format: RGB888
|
||||||
|
- Output descaling: enabled
|
||||||
|
- Saturation table: enabled
|
||||||
|
- Optimization level: Basic (JD_FASTDECODE = 0)
|
||||||
|
|
||||||
|
### Pros and cons using ROM code
|
||||||
|
|
||||||
|
**Advantages:**
|
||||||
|
- Saves approximately 5 KB of flash memory with the same configuration
|
||||||
|
|
||||||
|
**Disadvantages:**
|
||||||
|
- Compilation configuration cannot be changed
|
||||||
|
- Certain configurations may provide faster performance
|
||||||
|
|
||||||
|
## Speed comparison
|
||||||
|
|
||||||
|
The table below shows example decoding times for a JPEG image using various configurations:
|
||||||
|
* Image size: 320 x 180 px
|
||||||
|
* Output format: RGB565
|
||||||
|
* CPU: ESP32-S3
|
||||||
|
* CPU frequency: 240 MHz
|
||||||
|
* SPI mode: DIO
|
||||||
|
* Internal RAM used
|
||||||
|
* Measured in 1000 retries
|
||||||
|
|
||||||
|
| ROM used | JD_SZBUF | JD_FORMAT | JD_USE_SCALE | JD_TBLCLIP | JD_FASTDECODE | RAM buffer | Flash size | Approx. time |
|
||||||
|
| :------: | :------: | :-------: | :----------: | :--------: | :-----------: | :--------: | :--------: | :----------: |
|
||||||
|
| YES | 512 | RGB888 | 1 | 1 | 0 | 3.1 kB | 0 kB | 52 ms |
|
||||||
|
| NO | 512 | RGB888 | 1 | 1 | 0 | 3.1 kB | 5 kB | 50 ms |
|
||||||
|
| NO | 512 | RGB888 | 1 | 0 | 0 | 3.1 kB | 4 kB | 68 ms |
|
||||||
|
| NO | 512 | RGB888 | 1 | 1 | 1 | 3.1 kB | 5 kB | 50 ms |
|
||||||
|
| NO | 512 | RGB888 | 1 | 0 | 1 | 3.1 kB | 4 kB | 62 ms |
|
||||||
|
| NO | 512 | RGB888 | 1 | 1 | 2 | 65.5 kB | 5.5 kB | 46 ms |
|
||||||
|
| NO | 512 | RGB888 | 1 | 0 | 2 | 65.5 kB | 4.5 kB | 59 ms |
|
||||||
|
| NO | 512 | RGB565 | 1 | 1 | 0 | 5 kB | 5 kB | 60 ms |
|
||||||
|
| NO | 512 | RGB565 | 1 | 1 | 1 | 5 kB | 5 kB | 59 ms |
|
||||||
|
| NO | 512 | RGB565 | 1 | 1 | 2 | 65.5 kB | 5.5 kB | 56 ms |
|
||||||
|
|
||||||
|
## Add to project
|
||||||
|
|
||||||
|
Packages from this repository are uploaded to [Espressif's component service](https://components.espressif.com/).
|
||||||
|
You can add them to your project via `idf.py add-dependancy`, e.g.
|
||||||
|
```
|
||||||
|
idf.py add-dependency esp_jpeg==1.0.0
|
||||||
|
```
|
||||||
|
|
||||||
|
Alternatively, you can create `idf_component.yml`. More is in [Espressif's documentation](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/tools/idf-component-manager.html).
|
||||||
|
|
||||||
|
## Example use
|
||||||
|
|
||||||
|
Here is example of usage. This calling is **blocking**.
|
||||||
|
|
||||||
|
```
|
||||||
|
esp_jpeg_image_cfg_t jpeg_cfg = {
|
||||||
|
.indata = (uint8_t *)jpeg_img_buf,
|
||||||
|
.indata_size = jpeg_img_buf_size,
|
||||||
|
.outbuf = out_img_buf,
|
||||||
|
.outbuf_size = out_img_buf_size,
|
||||||
|
.out_format = JPEG_IMAGE_OUT_FORMAT_RGB565,
|
||||||
|
.out_scale = JPEG_IMAGE_SCALE_0,
|
||||||
|
.flags = {
|
||||||
|
.swap_color_bytes = 1,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
esp_jpeg_image_output_t outimg;
|
||||||
|
|
||||||
|
esp_jpeg_decode(&jpeg_cfg, &outimg);
|
||||||
|
```
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
# The following lines of boilerplate have to be in your project's CMakeLists
|
||||||
|
# in this exact order for cmake to work correctly
|
||||||
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
|
||||||
|
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||||
|
set(COMPONENTS main)
|
||||||
|
project(lcd_tjpgd)
|
||||||
@ -0,0 +1,54 @@
|
|||||||
|
# LCD tjpgd example
|
||||||
|
|
||||||
|
This example shows how to decode a jpeg image and display it on an SPI-interfaced LCD, and rotates the image periodically.
|
||||||
|
|
||||||
|
Example using initialization of the LCD from [ESP-BSP](https://github.com/espressif/esp-bsp) project. For change the Espressif's board, go to [idf_component.yml](main/idf_component.yml) and change `esp-box` to another board from BSP.
|
||||||
|
|
||||||
|
## How to Use Example
|
||||||
|
|
||||||
|
### Hardware Required
|
||||||
|
|
||||||
|
* An ESP development board
|
||||||
|
* An SPI-interfaced LCD
|
||||||
|
* An USB cable for power supply and programming
|
||||||
|
|
||||||
|
### Hardware Connection
|
||||||
|
|
||||||
|
The connection between ESP Board and the LCD is as follows:
|
||||||
|
|
||||||
|
```text
|
||||||
|
ESP Board LCD Screen
|
||||||
|
+---------+ +---------------------------------+
|
||||||
|
| | | |
|
||||||
|
| 3V3 +--------------+ VCC +----------------------+ |
|
||||||
|
| | | | | |
|
||||||
|
| GND +--------------+ GND | | |
|
||||||
|
| | | | | |
|
||||||
|
| DATA0 +--------------+ MOSI | | |
|
||||||
|
| | | | | |
|
||||||
|
| PCLK +--------------+ SCK | | |
|
||||||
|
| | | | | |
|
||||||
|
| CS +--------------+ CS | | |
|
||||||
|
| | | | | |
|
||||||
|
| D/C +--------------+ D/C | | |
|
||||||
|
| | | | | |
|
||||||
|
| RST +--------------+ RST | | |
|
||||||
|
| | | | | |
|
||||||
|
|BK_LIGHT +--------------+ BCKL +----------------------+ |
|
||||||
|
| | | |
|
||||||
|
+---------+ +---------------------------------+
|
||||||
|
```
|
||||||
|
|
||||||
|
The GPIO numbers used by this example is taken from BSP.
|
||||||
|
|
||||||
|
### Build and Flash
|
||||||
|
|
||||||
|
Run `idf.py -p PORT flash monitor` to build, flash and monitor the project. A flowing picture will be shown on the LCD screen.
|
||||||
|
|
||||||
|
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||||
|
|
||||||
|
See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects.
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
For any technical queries, please open an [issue] (https://github.com/espressif/idf-extra-components/issues) on GitHub. We will get back to you soon.
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
set(srcs "pretty_effect.c"
|
||||||
|
"lcd_tjpgd_example_main.c"
|
||||||
|
"decode_image.c"
|
||||||
|
)
|
||||||
|
|
||||||
|
idf_component_register(SRCS ${srcs}
|
||||||
|
INCLUDE_DIRS "."
|
||||||
|
EMBED_FILES image.jpg
|
||||||
|
PRIV_REQUIRES esp_lcd)
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
menu "Example Configuration"
|
||||||
|
config EXAMPLE_LCD_FLUSH_PARALLEL_LINES
|
||||||
|
int "LCD flush parallel lines"
|
||||||
|
default 12 if IDF_TARGET_ESP32C2
|
||||||
|
default 16
|
||||||
|
help
|
||||||
|
To speed up transfers, every SPI transfer sends a bunch of lines.
|
||||||
|
|
||||||
|
endmenu
|
||||||
@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: CC0-1.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
The image used for the effect on the LCD in the SPI master example is stored in flash
|
||||||
|
as a jpeg file. This file contains the decode_image routine, which uses the tiny JPEG
|
||||||
|
decoder library to decode this JPEG into a format that can be sent to the display.
|
||||||
|
|
||||||
|
Keep in mind that the decoder library cannot handle progressive files (will give
|
||||||
|
``Image decoder: jd_prepare failed (8)`` as an error) so make sure to save in the correct
|
||||||
|
format if you want to use a different image file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include "decode_image.h"
|
||||||
|
#include "jpeg_decoder.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "esp_check.h"
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
|
||||||
|
//Reference the binary-included jpeg file
|
||||||
|
extern const uint8_t image_jpg_start[] asm("_binary_image_jpg_start");
|
||||||
|
extern const uint8_t image_jpg_end[] asm("_binary_image_jpg_end");
|
||||||
|
//Define the height and width of the jpeg file. Make sure this matches the actual jpeg
|
||||||
|
//dimensions.
|
||||||
|
|
||||||
|
const char *TAG = "ImageDec";
|
||||||
|
|
||||||
|
//Decode the embedded image into pixel lines that can be used with the rest of the logic.
|
||||||
|
esp_err_t decode_image(uint16_t **pixels)
|
||||||
|
{
|
||||||
|
*pixels = NULL;
|
||||||
|
esp_err_t ret = ESP_OK;
|
||||||
|
|
||||||
|
//Alocate pixel memory. Each line is an array of IMAGE_W 16-bit pixels; the `*pixels` array itself contains pointers to these lines.
|
||||||
|
*pixels = calloc(IMAGE_H * IMAGE_W, sizeof(uint16_t));
|
||||||
|
ESP_GOTO_ON_FALSE((*pixels), ESP_ERR_NO_MEM, err, TAG, "Error allocating memory for lines");
|
||||||
|
|
||||||
|
//JPEG decode config
|
||||||
|
esp_jpeg_image_cfg_t jpeg_cfg = {
|
||||||
|
.indata = (uint8_t *)image_jpg_start,
|
||||||
|
.indata_size = image_jpg_end - image_jpg_start,
|
||||||
|
.outbuf = (uint8_t *)(*pixels),
|
||||||
|
.outbuf_size = IMAGE_W * IMAGE_H * sizeof(uint16_t),
|
||||||
|
.out_format = JPEG_IMAGE_FORMAT_RGB565,
|
||||||
|
.out_scale = JPEG_IMAGE_SCALE_0,
|
||||||
|
.flags = {
|
||||||
|
.swap_color_bytes = 1,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//JPEG decode
|
||||||
|
esp_jpeg_image_output_t outimg;
|
||||||
|
esp_jpeg_decode(&jpeg_cfg, &outimg);
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "JPEG image decoded! Size of the decoded image is: %dpx x %dpx", outimg.width, outimg.height);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
err:
|
||||||
|
//Something went wrong! Exit cleanly, de-allocating everything we allocated.
|
||||||
|
if (*pixels != NULL) {
|
||||||
|
free(*pixels);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: CC0-1.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "esp_err.h"
|
||||||
|
|
||||||
|
#define IMAGE_W 320
|
||||||
|
#define IMAGE_H 240
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Decode the jpeg ``image.jpg`` embedded into the program file into pixel data.
|
||||||
|
*
|
||||||
|
* @param pixels A pointer to a pointer for an array of rows, which themselves are an array of pixels.
|
||||||
|
* Effectively, you can get the pixel data by doing ``decode_image(&myPixels); pixelval=myPixels[ypos][xpos];``
|
||||||
|
* @return - ESP_ERR_NOT_SUPPORTED if image is malformed or a progressive jpeg file
|
||||||
|
* - ESP_ERR_NO_MEM if out of memory
|
||||||
|
* - ESP_OK on succesful decode
|
||||||
|
*/
|
||||||
|
esp_err_t decode_image(uint16_t **pixels);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
dependencies:
|
||||||
|
esp-box:
|
||||||
|
rules:
|
||||||
|
- if: target == esp32s3
|
||||||
|
version: ^2.4
|
||||||
|
esp32_s2_kaluga_kit:
|
||||||
|
rules:
|
||||||
|
- if: target == esp32s2
|
||||||
|
version: ^3.0
|
||||||
|
esp_jpeg:
|
||||||
|
version: '>=1.0.2'
|
||||||
|
esp_wrover_kit:
|
||||||
|
rules:
|
||||||
|
- if: target == esp32
|
||||||
|
version: ^1.5
|
||||||
|
idf: '>=5.0'
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 43 KiB |
@ -0,0 +1,98 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: CC0-1.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "sdkconfig.h"
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
#include "esp_lcd_panel_ops.h"
|
||||||
|
#include "esp_heap_caps.h"
|
||||||
|
#include "pretty_effect.h"
|
||||||
|
#include "bsp/esp-bsp.h"
|
||||||
|
#include "bsp/display.h"
|
||||||
|
|
||||||
|
// Using SPI2 in the example, as it also supports octal modes on some targets
|
||||||
|
#define LCD_HOST SPI2_HOST
|
||||||
|
// To speed up transfers, every SPI transfer sends a bunch of lines. This define specifies how many.
|
||||||
|
// More means more memory use, but less overhead for setting up / finishing transfers. Make sure 240
|
||||||
|
// is dividable by this.
|
||||||
|
#define PARALLEL_LINES CONFIG_EXAMPLE_LCD_FLUSH_PARALLEL_LINES
|
||||||
|
// The number of frames to show before rotate the graph
|
||||||
|
#define ROTATE_FRAME 30
|
||||||
|
|
||||||
|
#if BSP_LCD_H_RES > BSP_LCD_V_RES
|
||||||
|
#define EXAMPLE_LCD_SWAP 0
|
||||||
|
#define EXAMPLE_LCD_H_RES BSP_LCD_H_RES
|
||||||
|
#define EXAMPLE_LCD_V_RES BSP_LCD_V_RES
|
||||||
|
#else
|
||||||
|
#define EXAMPLE_LCD_SWAP 1
|
||||||
|
#define EXAMPLE_LCD_H_RES BSP_LCD_V_RES
|
||||||
|
#define EXAMPLE_LCD_V_RES BSP_LCD_H_RES
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Simple routine to generate some patterns and send them to the LCD. Because the
|
||||||
|
// SPI driver handles transactions in the background, we can calculate the next line
|
||||||
|
// while the previous one is being sent.
|
||||||
|
static uint16_t *s_lines[2];
|
||||||
|
static void display_pretty_colors(esp_lcd_panel_handle_t panel_handle)
|
||||||
|
{
|
||||||
|
int frame = 0;
|
||||||
|
// Indexes of the line currently being sent to the LCD and the line we're calculating
|
||||||
|
int sending_line = 0;
|
||||||
|
int calc_line = 0;
|
||||||
|
|
||||||
|
// After ROTATE_FRAME frames, the image will be rotated
|
||||||
|
while (frame <= ROTATE_FRAME) {
|
||||||
|
frame++;
|
||||||
|
for (int y = 0; y < EXAMPLE_LCD_V_RES; y += PARALLEL_LINES) {
|
||||||
|
// Calculate a line
|
||||||
|
pretty_effect_calc_lines(s_lines[calc_line], y, frame, PARALLEL_LINES);
|
||||||
|
sending_line = calc_line;
|
||||||
|
calc_line = !calc_line;
|
||||||
|
// Send the calculated data
|
||||||
|
esp_lcd_panel_draw_bitmap(panel_handle, 0, y, 0 + EXAMPLE_LCD_H_RES, y + PARALLEL_LINES, s_lines[sending_line]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void app_main(void)
|
||||||
|
{
|
||||||
|
esp_lcd_panel_io_handle_t io_handle = NULL;
|
||||||
|
esp_lcd_panel_handle_t panel_handle = NULL;
|
||||||
|
|
||||||
|
bsp_display_config_t disp_cfg = {
|
||||||
|
.max_transfer_sz = EXAMPLE_LCD_H_RES * PARALLEL_LINES * sizeof(uint16_t),
|
||||||
|
};
|
||||||
|
// Display initialize from BSP
|
||||||
|
bsp_display_new(&disp_cfg, &panel_handle, &io_handle);
|
||||||
|
esp_lcd_panel_disp_on_off(panel_handle, true);
|
||||||
|
bsp_display_backlight_on();
|
||||||
|
|
||||||
|
// Initialize the effect displayed
|
||||||
|
ESP_ERROR_CHECK(pretty_effect_init());
|
||||||
|
|
||||||
|
// "Rotate or not" flag
|
||||||
|
bool is_rotated = false;
|
||||||
|
|
||||||
|
// Allocate memory for the pixel buffers
|
||||||
|
for (int i = 0; i < 2; i++) {
|
||||||
|
s_lines[i] = heap_caps_malloc(EXAMPLE_LCD_H_RES * PARALLEL_LINES * sizeof(uint16_t), MALLOC_CAP_DMA);
|
||||||
|
assert(s_lines[i] != NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if EXAMPLE_LCD_SWAP
|
||||||
|
esp_lcd_panel_swap_xy(panel_handle, true);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Start and rotate
|
||||||
|
while (1) {
|
||||||
|
// Set driver configuration to rotate 180 degrees each time
|
||||||
|
ESP_ERROR_CHECK(esp_lcd_panel_mirror(panel_handle, is_rotated, is_rotated));
|
||||||
|
// Display
|
||||||
|
display_pretty_colors(panel_handle);
|
||||||
|
is_rotated = !is_rotated;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: CC0-1.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
#include "pretty_effect.h"
|
||||||
|
#include "decode_image.h"
|
||||||
|
|
||||||
|
uint16_t *pixels;
|
||||||
|
|
||||||
|
//Grab a rgb16 pixel from the esp32_tiles image
|
||||||
|
static inline uint16_t get_bgnd_pixel(int x, int y)
|
||||||
|
{
|
||||||
|
//Get color of the pixel on x,y coords
|
||||||
|
return (uint16_t) * (pixels + (y * IMAGE_W) + x);
|
||||||
|
}
|
||||||
|
|
||||||
|
//This variable is used to detect the next frame.
|
||||||
|
static int prev_frame = -1;
|
||||||
|
|
||||||
|
//Instead of calculating the offsets for each pixel we grab, we pre-calculate the valueswhenever a frame changes, then re-use
|
||||||
|
//these as we go through all the pixels in the frame. This is much, much faster.
|
||||||
|
static int8_t xofs[320], yofs[240];
|
||||||
|
static int8_t xcomp[320], ycomp[240];
|
||||||
|
|
||||||
|
//Calculate the pixel data for a set of lines (with implied line size of 320). Pixels go in dest, line is the Y-coordinate of the
|
||||||
|
//first line to be calculated, linect is the amount of lines to calculate. Frame increases by one every time the entire image
|
||||||
|
//is displayed; this is used to go to the next frame of animation.
|
||||||
|
void pretty_effect_calc_lines(uint16_t *dest, int line, int frame, int linect)
|
||||||
|
{
|
||||||
|
if (frame != prev_frame) {
|
||||||
|
//We need to calculate a new set of offset coefficients. Take some random sines as offsets to make everything
|
||||||
|
//look pretty and fluid-y.
|
||||||
|
for (int x = 0; x < 320; x++) {
|
||||||
|
xofs[x] = sin(frame * 0.15 + x * 0.06) * 4;
|
||||||
|
}
|
||||||
|
for (int y = 0; y < 240; y++) {
|
||||||
|
yofs[y] = sin(frame * 0.1 + y * 0.05) * 4;
|
||||||
|
}
|
||||||
|
for (int x = 0; x < 320; x++) {
|
||||||
|
xcomp[x] = sin(frame * 0.11 + x * 0.12) * 4;
|
||||||
|
}
|
||||||
|
for (int y = 0; y < 240; y++) {
|
||||||
|
ycomp[y] = sin(frame * 0.07 + y * 0.15) * 4;
|
||||||
|
}
|
||||||
|
prev_frame = frame;
|
||||||
|
}
|
||||||
|
for (int y = line; y < line + linect; y++) {
|
||||||
|
for (int x = 0; x < 320; x++) {
|
||||||
|
*dest++ = get_bgnd_pixel(x + yofs[y] + xcomp[x], y + xofs[x] + ycomp[y]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
esp_err_t pretty_effect_init(void)
|
||||||
|
{
|
||||||
|
return decode_image(&pixels);
|
||||||
|
}
|
||||||
@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: CC0-1.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "esp_err.h"
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Calculate the effect for a bunch of lines.
|
||||||
|
*
|
||||||
|
* @param dest Destination for the pixels. Assumed to be LINECT * 320 16-bit pixel values.
|
||||||
|
* @param line Starting line of the chunk of lines.
|
||||||
|
* @param frame Current frame, used for animation
|
||||||
|
* @param linect Amount of lines to calculate
|
||||||
|
*/
|
||||||
|
void pretty_effect_calc_lines(uint16_t *dest, int line, int frame, int linect);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize the effect
|
||||||
|
*
|
||||||
|
* @return ESP_OK on success, an error from the jpeg decoder otherwise.
|
||||||
|
*/
|
||||||
|
esp_err_t pretty_effect_init(void);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@ -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
|
||||||
9
managed_components/espressif__esp_jpeg/idf_component.yml
Normal file
9
managed_components/espressif__esp_jpeg/idf_component.yml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
dependencies:
|
||||||
|
idf: '>=5.0'
|
||||||
|
description: 'JPEG Decoder: TJpgDec'
|
||||||
|
repository: git://github.com/espressif/idf-extra-components.git
|
||||||
|
repository_info:
|
||||||
|
commit_sha: 746e83ddbea0db9c3d24993a87c4c737a60337ae
|
||||||
|
path: esp_jpeg
|
||||||
|
url: https://github.com/espressif/idf-extra-components/tree/master/esp_jpeg/
|
||||||
|
version: 1.3.1
|
||||||
106
managed_components/espressif__esp_jpeg/include/jpeg_decoder.h
Normal file
106
managed_components/espressif__esp_jpeg/include/jpeg_decoder.h
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esp_err.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Scale of output image
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
JPEG_IMAGE_SCALE_0 = 0, /*!< No scale */
|
||||||
|
JPEG_IMAGE_SCALE_1_2, /*!< Scale 1:2 */
|
||||||
|
JPEG_IMAGE_SCALE_1_4, /*!< Scale 1:4 */
|
||||||
|
JPEG_IMAGE_SCALE_1_8, /*!< Scale 1:8 */
|
||||||
|
} esp_jpeg_image_scale_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Format of output image
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
JPEG_IMAGE_FORMAT_RGB888 = 0, /*!< Format RGB888 */
|
||||||
|
JPEG_IMAGE_FORMAT_RGB565, /*!< Format RGB565 */
|
||||||
|
} esp_jpeg_image_format_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief JPEG Configuration Type
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
typedef struct esp_jpeg_image_cfg_s {
|
||||||
|
uint8_t *indata; /*!< Input JPEG image */
|
||||||
|
uint32_t indata_size; /*!< Size of input image */
|
||||||
|
uint8_t *outbuf; /*!< Output buffer */
|
||||||
|
uint32_t outbuf_size; /*!< Output buffer size */
|
||||||
|
esp_jpeg_image_format_t out_format; /*!< Output image format */
|
||||||
|
esp_jpeg_image_scale_t out_scale; /*!< Output scale */
|
||||||
|
|
||||||
|
struct {
|
||||||
|
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;
|
||||||
|
} esp_jpeg_image_cfg_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief JPEG output info
|
||||||
|
*/
|
||||||
|
typedef struct esp_jpeg_image_output_s {
|
||||||
|
uint16_t width; /*!< Width of the output image */
|
||||||
|
uint16_t height; /*!< Height of the output image */
|
||||||
|
size_t output_len; /*!< Length of the output image in bytes */
|
||||||
|
} esp_jpeg_image_output_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Decode JPEG image
|
||||||
|
*
|
||||||
|
* @note This function is blocking.
|
||||||
|
*
|
||||||
|
* @param[in] cfg: Configuration structure
|
||||||
|
* @param[out] img: Output image info
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK on success
|
||||||
|
* - ESP_ERR_NO_MEM if there is no memory for allocating main structure
|
||||||
|
* - ESP_FAIL if there is an error in decoding JPEG
|
||||||
|
*/
|
||||||
|
esp_err_t esp_jpeg_decode(esp_jpeg_image_cfg_t *cfg, esp_jpeg_image_output_t *img);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get information about the JPEG image
|
||||||
|
*
|
||||||
|
* Use this function to get the size of the JPEG image without decoding it.
|
||||||
|
* Allocate a buffer of size img->output_len to store the decoded image.
|
||||||
|
*
|
||||||
|
* @note cfg->outbuf and cfg->outbuf_size are not used in this function.
|
||||||
|
* @param[in] cfg: Configuration structure
|
||||||
|
* @param[out] img: Output image info
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK on success
|
||||||
|
* - ESP_ERR_INVALID_ARG if cfg or img is NULL
|
||||||
|
* - ESP_FAIL if there is an error in decoding JPEG
|
||||||
|
*/
|
||||||
|
esp_err_t esp_jpeg_get_image_info(esp_jpeg_image_cfg_t *cfg, esp_jpeg_image_output_t *img);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
287
managed_components/espressif__esp_jpeg/jpeg_decoder.c
Normal file
287
managed_components/espressif__esp_jpeg/jpeg_decoder.c
Normal file
@ -0,0 +1,287 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "esp_system.h"
|
||||||
|
#include "esp_rom_caps.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include "esp_check.h"
|
||||||
|
#include "jpeg_decoder.h"
|
||||||
|
|
||||||
|
#if CONFIG_JD_USE_ROM
|
||||||
|
/* When supported in ROM, use ROM functions */
|
||||||
|
#if defined(ESP_ROM_HAS_JPEG_DECODE)
|
||||||
|
#include "rom/tjpgd.h"
|
||||||
|
#else
|
||||||
|
#error Using JPEG decoder from ROM is not supported for selected target. Please select external code in menuconfig.
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* The ROM code of TJPGD is older and has different return type in decode callback */
|
||||||
|
typedef unsigned int jpeg_decode_out_t;
|
||||||
|
#else
|
||||||
|
/* When Tiny JPG Decoder is not in ROM or selected external code */
|
||||||
|
#include "tjpgd.h"
|
||||||
|
|
||||||
|
/* The TJPGD outside the ROM code is newer and has different return type in decode callback */
|
||||||
|
typedef int jpeg_decode_out_t;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static const char *TAG = "JPEG";
|
||||||
|
|
||||||
|
#define LOBYTE(u16) ((uint8_t)(((uint16_t)(u16)) & 0xff))
|
||||||
|
#define HIBYTE(u16) ((uint8_t)((((uint16_t)(u16))>>8) & 0xff))
|
||||||
|
|
||||||
|
#if defined(JD_FASTDECODE) && (JD_FASTDECODE == 2)
|
||||||
|
#define JPEG_WORK_BUF_SIZE 65472
|
||||||
|
#else
|
||||||
|
#define JPEG_WORK_BUF_SIZE 3100 /* Recommended buffer size; Independent on the size of the image */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* If not set JD_FORMAT, it is set in ROM to RGB888, otherwise, it can be set in config */
|
||||||
|
#ifndef JD_FORMAT
|
||||||
|
#define JD_FORMAT 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Output color bytes from tjpgd (depends on JD_FORMAT) */
|
||||||
|
#if (JD_FORMAT==0)
|
||||||
|
#define ESP_JPEG_COLOR_BYTES 3
|
||||||
|
#elif (JD_FORMAT==1)
|
||||||
|
#define ESP_JPEG_COLOR_BYTES 2
|
||||||
|
#elif (JD_FORMAT==2)
|
||||||
|
#error Grayscale image output format is not supported
|
||||||
|
#define ESP_JPEG_COLOR_BYTES 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* Function definitions
|
||||||
|
*******************************************************************************/
|
||||||
|
static uint8_t jpeg_get_div_by_scale(esp_jpeg_image_scale_t scale);
|
||||||
|
static uint8_t jpeg_get_color_bytes(esp_jpeg_image_format_t format);
|
||||||
|
|
||||||
|
static unsigned int jpeg_decode_in_cb(JDEC *jd, uint8_t *buff, unsigned int nbyte);
|
||||||
|
static jpeg_decode_out_t jpeg_decode_out_cb(JDEC *jd, void *bitmap, JRECT *rect);
|
||||||
|
static inline uint16_t ldb_word(const void *ptr);
|
||||||
|
/*******************************************************************************
|
||||||
|
* Public API functions
|
||||||
|
*******************************************************************************/
|
||||||
|
|
||||||
|
esp_err_t esp_jpeg_decode(esp_jpeg_image_cfg_t *cfg, esp_jpeg_image_output_t *img)
|
||||||
|
{
|
||||||
|
esp_err_t ret = ESP_OK;
|
||||||
|
uint8_t *workbuf = NULL;
|
||||||
|
JRESULT res;
|
||||||
|
JDEC JDEC;
|
||||||
|
|
||||||
|
assert(cfg != NULL);
|
||||||
|
assert(img != NULL);
|
||||||
|
|
||||||
|
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, workbuf_size, cfg);
|
||||||
|
ESP_GOTO_ON_FALSE((res == JDR_OK), ESP_FAIL, err, TAG, "Error in preparing JPEG image! %d", res);
|
||||||
|
|
||||||
|
const uint8_t scale_div = jpeg_get_div_by_scale(cfg->out_scale);
|
||||||
|
const uint8_t out_color_bytes = jpeg_get_color_bytes(cfg->out_format);
|
||||||
|
|
||||||
|
/* Size of output image */
|
||||||
|
const uint32_t outsize = (JDEC.height / scale_div) * (JDEC.width / scale_div) * out_color_bytes;
|
||||||
|
ESP_GOTO_ON_FALSE((outsize <= cfg->outbuf_size), ESP_ERR_NO_MEM, err, TAG, "Not enough size in output buffer!");
|
||||||
|
|
||||||
|
/* Size of output image */
|
||||||
|
img->height = JDEC.height / scale_div;
|
||||||
|
img->width = JDEC.width / scale_div;
|
||||||
|
img->output_len = outsize;
|
||||||
|
|
||||||
|
/* Decode JPEG */
|
||||||
|
res = jd_decomp(&JDEC, jpeg_decode_out_cb, cfg->out_scale);
|
||||||
|
ESP_GOTO_ON_FALSE((res == JDR_OK), ESP_FAIL, err, TAG, "Error in decoding JPEG image! %d", res);
|
||||||
|
|
||||||
|
err:
|
||||||
|
if (workbuf && allocate_buffer) {
|
||||||
|
free(workbuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_jpeg_get_image_info(esp_jpeg_image_cfg_t *cfg, esp_jpeg_image_output_t *img)
|
||||||
|
{
|
||||||
|
if (cfg == NULL || img == NULL) {
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
} else if (cfg->indata == NULL || cfg->indata_size < 5) {
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
esp_err_t ret = ESP_FAIL;
|
||||||
|
|
||||||
|
if (ldb_word(cfg->indata) != 0xFFD8) {
|
||||||
|
return ESP_FAIL; /* Err: SOI is not detected */
|
||||||
|
}
|
||||||
|
unsigned ofs = 2; // Start after SOI marker
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
/* Get a JPEG marker */
|
||||||
|
uint8_t *seg = cfg->indata + ofs; /* Segment pointer */
|
||||||
|
unsigned short marker = ldb_word(seg); /* Marker */
|
||||||
|
unsigned int len = ldb_word(seg + 2); /* Length field */
|
||||||
|
if (len <= 2 || (marker >> 8) != 0xFF) {
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
ofs += 2 + len; /* Number of bytes loaded */
|
||||||
|
if (ofs > cfg->indata_size) {
|
||||||
|
return ESP_FAIL; // No more data
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((marker & 0xFF) == 0xC0) { /* SOF0 (baseline JPEG) */
|
||||||
|
seg += 4; /* Skip marker and length field */
|
||||||
|
|
||||||
|
/* Size of output image */
|
||||||
|
img->height = ldb_word(seg + 1);
|
||||||
|
img->width = ldb_word(seg + 3);
|
||||||
|
const uint8_t scale_div = jpeg_get_div_by_scale(cfg->out_scale);
|
||||||
|
const uint8_t out_color_bytes = jpeg_get_color_bytes(cfg->out_format);
|
||||||
|
img->output_len = (img->height / scale_div) * (img->width / scale_div) * out_color_bytes;
|
||||||
|
ret = ESP_OK;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* Private API functions
|
||||||
|
*******************************************************************************/
|
||||||
|
|
||||||
|
static unsigned int jpeg_decode_in_cb(JDEC *dec, uint8_t *buff, unsigned int nbyte)
|
||||||
|
{
|
||||||
|
assert(dec != NULL);
|
||||||
|
|
||||||
|
uint32_t to_read = nbyte;
|
||||||
|
esp_jpeg_image_cfg_t *cfg = (esp_jpeg_image_cfg_t *)dec->device;
|
||||||
|
assert(cfg != NULL);
|
||||||
|
|
||||||
|
if (buff) {
|
||||||
|
if (cfg->priv.read + to_read > cfg->indata_size) {
|
||||||
|
to_read = cfg->indata_size - cfg->priv.read;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copy data from JPEG image */
|
||||||
|
memcpy(buff, &cfg->indata[cfg->priv.read], to_read);
|
||||||
|
cfg->priv.read += to_read;
|
||||||
|
} else if (buff == NULL) {
|
||||||
|
/* Skip data */
|
||||||
|
cfg->priv.read += to_read;
|
||||||
|
}
|
||||||
|
|
||||||
|
return to_read;
|
||||||
|
}
|
||||||
|
|
||||||
|
static jpeg_decode_out_t jpeg_decode_out_cb(JDEC *dec, void *bitmap, JRECT *rect)
|
||||||
|
{
|
||||||
|
uint16_t color = 0;
|
||||||
|
assert(dec != NULL);
|
||||||
|
|
||||||
|
esp_jpeg_image_cfg_t *cfg = (esp_jpeg_image_cfg_t *)dec->device;
|
||||||
|
assert(cfg != NULL);
|
||||||
|
assert(bitmap != NULL);
|
||||||
|
assert(rect != NULL);
|
||||||
|
|
||||||
|
uint8_t scale_div = jpeg_get_div_by_scale(cfg->out_scale);
|
||||||
|
uint8_t out_color_bytes = jpeg_get_color_bytes(cfg->out_format);
|
||||||
|
|
||||||
|
/* Copy decoded image data to output buffer */
|
||||||
|
uint8_t *in = (uint8_t *)bitmap;
|
||||||
|
uint32_t line = dec->width / scale_div;
|
||||||
|
uint8_t *dst = (uint8_t *)cfg->outbuf;
|
||||||
|
for (int y = rect->top; y <= rect->bottom; y++) {
|
||||||
|
for (int x = rect->left; x <= rect->right; x++) {
|
||||||
|
if ( (JD_FORMAT == 0 && cfg->out_format == JPEG_IMAGE_FORMAT_RGB888) ||
|
||||||
|
(JD_FORMAT == 1 && cfg->out_format == JPEG_IMAGE_FORMAT_RGB565) ) {
|
||||||
|
/* Output image format is same as set in TJPGD */
|
||||||
|
for (int b = 0; b < ESP_JPEG_COLOR_BYTES; b++) {
|
||||||
|
if (cfg->flags.swap_color_bytes) {
|
||||||
|
dst[(y * line * out_color_bytes) + x * out_color_bytes + b] = in[out_color_bytes - b - 1];
|
||||||
|
} else {
|
||||||
|
dst[(y * line * out_color_bytes) + x * out_color_bytes + b] = in[b];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (JD_FORMAT == 0 && cfg->out_format == JPEG_IMAGE_FORMAT_RGB565) {
|
||||||
|
/* Output image format is not same as set in TJPGD */
|
||||||
|
/* We need to convert the 3 bytes in `in` to a rgb565 value */
|
||||||
|
color = ((in[0] & 0xF8) << 8);
|
||||||
|
color |= ((in[1] & 0xFC) << 3);
|
||||||
|
color |= (in[2] >> 3);
|
||||||
|
|
||||||
|
if (cfg->flags.swap_color_bytes) {
|
||||||
|
dst[(y * line * out_color_bytes) + (x * out_color_bytes)] = HIBYTE(color);
|
||||||
|
dst[(y * line * out_color_bytes) + (x * out_color_bytes) + 1] = LOBYTE(color);
|
||||||
|
} else {
|
||||||
|
dst[(y * line * out_color_bytes) + (x * out_color_bytes) + 1] = HIBYTE(color);
|
||||||
|
dst[(y * line * out_color_bytes) + (x * out_color_bytes)] = LOBYTE(color);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ESP_LOGE(TAG, "Selected output format is not supported!");
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
in += ESP_JPEG_COLOR_BYTES;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t jpeg_get_div_by_scale(esp_jpeg_image_scale_t scale)
|
||||||
|
{
|
||||||
|
switch (scale) {
|
||||||
|
/* Not scaled */
|
||||||
|
case JPEG_IMAGE_SCALE_0:
|
||||||
|
return 1;
|
||||||
|
/* Scaled 1:2 */
|
||||||
|
case JPEG_IMAGE_SCALE_1_2:
|
||||||
|
return 2;
|
||||||
|
/* Scaled 1:4 */
|
||||||
|
case JPEG_IMAGE_SCALE_1_4:
|
||||||
|
return 4;
|
||||||
|
/* Scaled 1:8 */
|
||||||
|
case JPEG_IMAGE_SCALE_1_8:
|
||||||
|
return 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t jpeg_get_color_bytes(esp_jpeg_image_format_t format)
|
||||||
|
{
|
||||||
|
switch (format) {
|
||||||
|
/* RGB888 (24-bit/pix) */
|
||||||
|
case JPEG_IMAGE_FORMAT_RGB888:
|
||||||
|
return 3;
|
||||||
|
/* RGB565 (16-bit/pix) */
|
||||||
|
case JPEG_IMAGE_FORMAT_RGB565:
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint16_t ldb_word(const void *ptr)
|
||||||
|
{
|
||||||
|
const uint8_t *p = (const uint8_t *)ptr;
|
||||||
|
return ((uint16_t)p[0] << 8) | p[1];
|
||||||
|
}
|
||||||
@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Default Huffman tables for baseline JPEG
|
||||||
|
|
||||||
|
// These values are taken directly from CCITT Rec. T.81 (1992 E) Appendix K.3.3
|
||||||
|
// The *_num_bits array always contains exactly 16 elements.
|
||||||
|
// Each element represents the number of Huffman codes of a specific length:
|
||||||
|
// - The first element corresponds to codes of length 1 bit,
|
||||||
|
// - The second element to codes of length 2 bits, and so forth up to 16 bits.
|
||||||
|
//
|
||||||
|
// The *_values array has a length equal to the sum of all elements in the *_num_bits array,
|
||||||
|
// representing the actual values associated with each Huffman code in order.
|
||||||
|
|
||||||
|
// Luminance DC Table
|
||||||
|
const unsigned char esp_jpeg_lum_dc_num_bits[16] = {0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0};
|
||||||
|
const unsigned esp_jpeg_lum_dc_codes_total = 12;
|
||||||
|
const unsigned char esp_jpeg_lum_dc_values[12] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
|
||||||
|
|
||||||
|
// Chrominance DC Table
|
||||||
|
const unsigned char esp_jpeg_chrom_dc_num_bits[16] = {0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0};
|
||||||
|
const unsigned esp_jpeg_chrom_dc_codes_total = 12;
|
||||||
|
const unsigned char esp_jpeg_chrom_dc_values[12] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
|
||||||
|
|
||||||
|
// Luminance AC Table
|
||||||
|
const unsigned char esp_jpeg_lum_ac_num_bits[16] = {0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 125};
|
||||||
|
const unsigned esp_jpeg_lum_ac_codes_total = 162;
|
||||||
|
const unsigned char esp_jpeg_lum_ac_values[162] = {
|
||||||
|
0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07,
|
||||||
|
0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1, 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0,
|
||||||
|
0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28,
|
||||||
|
0x29, 0x2A, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
|
||||||
|
0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
|
||||||
|
0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
|
||||||
|
0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7,
|
||||||
|
0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5,
|
||||||
|
0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE1, 0xE2,
|
||||||
|
0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8,
|
||||||
|
0xF9, 0xFA
|
||||||
|
};
|
||||||
|
|
||||||
|
// Chrominance AC Table
|
||||||
|
const unsigned char esp_jpeg_chrom_ac_num_bits[16] = {0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 119};
|
||||||
|
const unsigned esp_jpeg_chrom_ac_codes_total = 162;
|
||||||
|
const unsigned char esp_jpeg_chrom_ac_values[162] = {
|
||||||
|
0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,
|
||||||
|
0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xA1, 0xB1, 0xC1, 0x09, 0x23, 0x33, 0x52, 0xF0,
|
||||||
|
0x15, 0x62, 0x72, 0xD1, 0x0A, 0x16, 0x24, 0x34, 0xE1, 0x25, 0xF1, 0x17, 0x18, 0x19, 0x1A, 0x26,
|
||||||
|
0x27, 0x28, 0x29, 0x2A, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
|
||||||
|
0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
|
||||||
|
0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
|
||||||
|
0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5,
|
||||||
|
0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3,
|
||||||
|
0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA,
|
||||||
|
0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8,
|
||||||
|
0xF9, 0xFA
|
||||||
|
};
|
||||||
202
managed_components/espressif__esp_jpeg/license.txt
Normal file
202
managed_components/espressif__esp_jpeg/license.txt
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
|
||||||
|
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||||
|
set(COMPONENTS main)
|
||||||
|
project(esp_jpeg_test)
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
idf_component_register(SRCS "tjpgd_test.c" "test_tjpgd_main.c"
|
||||||
|
INCLUDE_DIRS "."
|
||||||
|
PRIV_REQUIRES "unity"
|
||||||
|
WHOLE_ARCHIVE
|
||||||
|
EMBED_FILES "logo.jpg" "usb_camera.jpg" "usb_camera_2.jpg")
|
||||||
@ -0,0 +1,4 @@
|
|||||||
|
dependencies:
|
||||||
|
espressif/esp_jpeg:
|
||||||
|
version: "*"
|
||||||
|
override_path: "../../"
|
||||||
@ -0,0 +1,64 @@
|
|||||||
|
from PIL import Image
|
||||||
|
|
||||||
|
|
||||||
|
def jpg_to_rgb888_hex_c_array(input_filename: str, output_filename: str) -> str:
|
||||||
|
"""
|
||||||
|
Convert a .jpg file to RGB888 hex data and format it as a C-style array.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
input_filename (str): The path to the JPEG file.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: A string representing the RGB888 hex data formatted as a C array.
|
||||||
|
"""
|
||||||
|
# Open the image file
|
||||||
|
with Image.open(input_filename) as img:
|
||||||
|
# Ensure the image is in RGB mode
|
||||||
|
rgb_img = img.convert("RGB")
|
||||||
|
|
||||||
|
# Get image dimensions
|
||||||
|
width, height = rgb_img.size
|
||||||
|
|
||||||
|
# List to store hex values as C-style entries
|
||||||
|
hex_data = []
|
||||||
|
|
||||||
|
# Iterate over each pixel to get RGB values
|
||||||
|
for y in range(height):
|
||||||
|
for x in range(width):
|
||||||
|
r, g, b = rgb_img.getpixel((x, y))
|
||||||
|
# Format each RGB value as C-style hex (e.g., 0xRRGGBB)
|
||||||
|
hex_data.append(f"0x{r:02X}{g:02X}{b:02X}")
|
||||||
|
|
||||||
|
# Format as a C-style array with line breaks for readability
|
||||||
|
hex_array = ",\n ".join(hex_data)
|
||||||
|
c_array = f"unsigned int image_data[{width * height}] = {{\n {hex_array}\n}};"
|
||||||
|
|
||||||
|
# Write the C array to the output file
|
||||||
|
with open(output_filename, "w") as file:
|
||||||
|
file.write(c_array)
|
||||||
|
|
||||||
|
print(f"C-style RGB888 hex array saved to {output_filename}")
|
||||||
|
|
||||||
|
return c_array
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""
|
||||||
|
Main function to convert a JPEG file to an RGB888 C-style hex array.
|
||||||
|
|
||||||
|
Instructions:
|
||||||
|
1. Replace 'input.jpg' with the path to your JPEG file.
|
||||||
|
2. Run the script to get the C-style array output.
|
||||||
|
"""
|
||||||
|
# Input JPEG file path
|
||||||
|
input_filename = "usb_camera.jpg" # Replace with your JPEG file path
|
||||||
|
|
||||||
|
# Output file path for the C array
|
||||||
|
output_filename = "output_array.c" # Specify your desired output filename
|
||||||
|
|
||||||
|
# Convert JPEG to C-style RGB888 hex array
|
||||||
|
jpg_to_rgb888_hex_c_array(input_filename, output_filename)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
BIN
managed_components/espressif__esp_jpeg/test_apps/main/logo.jpg
Normal file
BIN
managed_components/espressif__esp_jpeg/test_apps/main/logo.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.4 KiB |
@ -0,0 +1,7 @@
|
|||||||
|
// JPEG encoded image 46x46, 7561 bytes
|
||||||
|
extern const unsigned char logo_jpg[] asm("_binary_logo_jpg_start");
|
||||||
|
|
||||||
|
extern char _binary_logo_jpg_start;
|
||||||
|
extern char _binary_logo_jpg_end;
|
||||||
|
// Must be defined as macro because extern variables are not known at compile time (but at link time)
|
||||||
|
#define logo_jpg_len (&_binary_logo_jpg_end - &_binary_logo_jpg_start)
|
||||||
File diff suppressed because one or more lines are too long
@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "unity.h"
|
||||||
|
#include "unity_test_runner.h"
|
||||||
|
#include "esp_heap_caps.h"
|
||||||
|
#include "esp_newlib.h"
|
||||||
|
|
||||||
|
#include "unity_test_utils_memory.h"
|
||||||
|
|
||||||
|
void setUp(void)
|
||||||
|
{
|
||||||
|
unity_utils_record_free_mem();
|
||||||
|
}
|
||||||
|
|
||||||
|
void tearDown(void)
|
||||||
|
{
|
||||||
|
esp_reent_cleanup(); //clean up some of the newlib's lazy allocations
|
||||||
|
unity_utils_evaluate_leaks_direct(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void app_main(void)
|
||||||
|
{
|
||||||
|
printf("Running esp_jpeg component tests\n");
|
||||||
|
unity_run_menu();
|
||||||
|
}
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
/*
|
||||||
|
Raw data from Logitech C170 USB camera was reconstructed to usb_camera_2.jpg
|
||||||
|
It was converted to RGB888 array with jpg_to_rgb888_hex.py
|
||||||
|
*/
|
||||||
|
|
||||||
|
// JPEG encoded frame 160x120, 1384 bytes, has broken 0xFFFF marker
|
||||||
|
extern const unsigned char camera_2_jpg[] asm("_binary_usb_camera_2_jpg_start");
|
||||||
|
|
||||||
|
extern char _binary_usb_camera_2_jpg_start;
|
||||||
|
extern char _binary_usb_camera_2_jpg_end;
|
||||||
|
// Must be defined as macro because extern variables are not known at compile time (but at link time)
|
||||||
|
#define camera_2_jpg_len (&_binary_usb_camera_2_jpg_end - &_binary_usb_camera_2_jpg_start)
|
||||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,12 @@
|
|||||||
|
/*
|
||||||
|
Raw data from Logitech C270 USB camera was reconstructed to usb_camera.jpg
|
||||||
|
It was converted to RGB888 array with jpg_to_rgb888_hex.py
|
||||||
|
*/
|
||||||
|
|
||||||
|
// JPEG encoded frame 160x120, 2632 bytes, no huffman tables, double block size (16x8 pixels)
|
||||||
|
extern const unsigned char jpeg_no_huffman[] asm("_binary_usb_camera_jpg_start");
|
||||||
|
|
||||||
|
extern char _binary_usb_camera_jpg_start;
|
||||||
|
extern char _binary_usb_camera_jpg_end;
|
||||||
|
// Must be defined as macro because extern variables are not known at compile time (but at link time)
|
||||||
|
#define jpeg_no_huffman_len (&_binary_usb_camera_jpg_end - &_binary_usb_camera_jpg_start)
|
||||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,328 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||||
|
*/
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "sdkconfig.h"
|
||||||
|
#include "unity.h"
|
||||||
|
|
||||||
|
|
||||||
|
#include "jpeg_decoder.h"
|
||||||
|
#include "test_logo_jpg.h"
|
||||||
|
#include "test_logo_rgb888.h"
|
||||||
|
#include "test_usb_camera_2_jpg.h"
|
||||||
|
#include "test_usb_camera_2_rgb888.h"
|
||||||
|
|
||||||
|
#define TESTW 46
|
||||||
|
#define TESTH 46
|
||||||
|
|
||||||
|
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 decoded_outsize = TESTW * TESTH * 3;
|
||||||
|
|
||||||
|
decoded = malloc(decoded_outsize);
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_jpeg_print_ascii(decoded, &outimg);
|
||||||
|
|
||||||
|
free(decoded);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief JPEG unknown size test
|
||||||
|
*
|
||||||
|
* This test case verifies the functionality of the JPEG decompression library
|
||||||
|
* when decoding an image with unknown size. The image is decoded from a
|
||||||
|
* JPEG file, and the output size is determined dynamically. The test checks
|
||||||
|
* that the decoded image dimensions match the expected values and that the
|
||||||
|
* pixel data is within an acceptable tolerance range.
|
||||||
|
*/
|
||||||
|
TEST_CASE("Test JPEG unknown size", "[esp_jpeg]")
|
||||||
|
{
|
||||||
|
unsigned char *decoded, *p;
|
||||||
|
const unsigned char *o;
|
||||||
|
|
||||||
|
/* JPEG decode */
|
||||||
|
esp_jpeg_image_cfg_t jpeg_cfg = {
|
||||||
|
.indata = (uint8_t *)logo_jpg,
|
||||||
|
.indata_size = logo_jpg_len,
|
||||||
|
.out_format = JPEG_IMAGE_FORMAT_RGB888,
|
||||||
|
};
|
||||||
|
|
||||||
|
// 1. Get required output size
|
||||||
|
esp_jpeg_image_output_t outimg;
|
||||||
|
esp_err_t err = esp_jpeg_get_image_info(&jpeg_cfg, &outimg);
|
||||||
|
TEST_ASSERT_EQUAL(err, ESP_OK);
|
||||||
|
TEST_ASSERT_EQUAL(TESTW * TESTH * 3, outimg.output_len);
|
||||||
|
TEST_ASSERT_EQUAL(outimg.width, TESTW);
|
||||||
|
TEST_ASSERT_EQUAL(outimg.height, TESTH);
|
||||||
|
|
||||||
|
// 2. Allocate output buffer and assign it to the config
|
||||||
|
decoded = malloc(outimg.output_len);
|
||||||
|
TEST_ASSERT_NOT_NULL(decoded);
|
||||||
|
jpeg_cfg.outbuf = decoded;
|
||||||
|
jpeg_cfg.outbuf_size = outimg.output_len;
|
||||||
|
|
||||||
|
// 3. Decode the image
|
||||||
|
err = esp_jpeg_decode(&jpeg_cfg, &outimg);
|
||||||
|
TEST_ASSERT_EQUAL(err, ESP_OK);
|
||||||
|
|
||||||
|
/* Decoded image size */
|
||||||
|
TEST_ASSERT_EQUAL(TESTW * TESTH * 3, outimg.output_len);
|
||||||
|
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(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);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if CONFIG_JD_DEFAULT_HUFFMAN
|
||||||
|
#include "test_usb_camera_jpg.h"
|
||||||
|
#include "test_usb_camera_rgb888.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Test for JPEG decompression without Huffman tables
|
||||||
|
*
|
||||||
|
* This test case verifies the functionality of the JPEG decompression library
|
||||||
|
* when decoding an image that lacks Huffman tables, such as a USB frame
|
||||||
|
* from a Logitech C270 USB camera. The image was reconstructed from raw USB data
|
||||||
|
* (using `hex_to_jpg.py`) and then converted into an RGB888 C-style array
|
||||||
|
* (using `jpg_to_rgb888_hex.py`).
|
||||||
|
*
|
||||||
|
* Due to the unique structure of the JPEG data (double block size, 16x8 pixels)
|
||||||
|
* and absence of Huffman tables, this test assesses whether the decompression
|
||||||
|
* library correctly decodes the image and outputs RGB888 pixel data within
|
||||||
|
* an acceptable tolerance range.
|
||||||
|
*
|
||||||
|
* The test performs the following steps:
|
||||||
|
* - Allocates a buffer for the decoded image.
|
||||||
|
* - Configures and runs the JPEG decoder with the RGB888 output format.
|
||||||
|
* - Checks that the decoded image dimensions match expected values.
|
||||||
|
* - Compares the decompressed image data against the reference RGB888 data,
|
||||||
|
* allowing a tolerance of ±16 in each color component due to potential
|
||||||
|
* differences in Huffman tables or decompression accuracy.
|
||||||
|
*
|
||||||
|
* @note This test allows a margin of error in pixel values due to potential
|
||||||
|
* differences in how color data is interpreted across different decoders.
|
||||||
|
*
|
||||||
|
* @param None
|
||||||
|
*
|
||||||
|
* @return None
|
||||||
|
*
|
||||||
|
* @test Requirements:
|
||||||
|
* - JPEG decompression library support for images without Huffman tables.
|
||||||
|
* - JPEG decompression accuracy within acceptable error margins.
|
||||||
|
*/
|
||||||
|
TEST_CASE("Test JPEG decompression library: No Huffman tables", "[esp_jpeg]")
|
||||||
|
{
|
||||||
|
unsigned char *decoded, *p;
|
||||||
|
const unsigned int *o;
|
||||||
|
int decoded_outsize = 160 * 120 * 3;
|
||||||
|
|
||||||
|
decoded = malloc(decoded_outsize);
|
||||||
|
|
||||||
|
/* JPEG decode */
|
||||||
|
esp_jpeg_image_cfg_t jpeg_cfg = {
|
||||||
|
.indata = (uint8_t *)jpeg_no_huffman,
|
||||||
|
.indata_size = jpeg_no_huffman_len,
|
||||||
|
.outbuf = decoded,
|
||||||
|
.outbuf_size = decoded_outsize,
|
||||||
|
.out_format = JPEG_IMAGE_FORMAT_RGB888,
|
||||||
|
.out_scale = JPEG_IMAGE_SCALE_0,
|
||||||
|
.flags = {
|
||||||
|
.swap_color_bytes = 0,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
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, 160);
|
||||||
|
TEST_ASSERT_EQUAL(outimg.height, 120);
|
||||||
|
|
||||||
|
p = decoded;
|
||||||
|
o = jpeg_no_huffman_rgb888;
|
||||||
|
for (int x = 0; x < outimg.width * outimg.height; x++) {
|
||||||
|
/* The color can be +- 16 */
|
||||||
|
// Here we allow bigger decoding error
|
||||||
|
// It might be that the Windows decoder used slightly different Huffman tables
|
||||||
|
TEST_ASSERT_UINT8_WITHIN(16, (*o) & 0xff, p[0]);
|
||||||
|
TEST_ASSERT_UINT8_WITHIN(16, (*o >> 8) & 0xff, p[1]);
|
||||||
|
TEST_ASSERT_UINT8_WITHIN(16, (*o >> 16) & 0xff, p[2]);
|
||||||
|
|
||||||
|
p += 3; // this is uint8_t
|
||||||
|
o ++; // this is unt32_t
|
||||||
|
}
|
||||||
|
|
||||||
|
free(decoded);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Invalid JPEG marker test
|
||||||
|
*
|
||||||
|
* This test case verifies the behavior of the JPEG decompression library
|
||||||
|
* when encountering an invalid marker (0xFFFF) in the JPEG data stream.
|
||||||
|
* The test uses a known JPEG image (camera_2_jpg) that contains this invalid
|
||||||
|
* marker. The test checks whether the library can handle the invalid marker
|
||||||
|
* gracefully and still decode the image correctly.
|
||||||
|
*/
|
||||||
|
TEST_CASE("Test JPEG invalid marker 0xFFFF", "[esp_jpeg]")
|
||||||
|
{
|
||||||
|
unsigned char *decoded;
|
||||||
|
int decoded_outsize = 160 * 120 * 3;
|
||||||
|
|
||||||
|
decoded = malloc(decoded_outsize);
|
||||||
|
assert(decoded);
|
||||||
|
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 *)camera_2_jpg,
|
||||||
|
.indata_size = camera_2_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,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
esp_jpeg_image_output_t outimg;
|
||||||
|
esp_err_t err = esp_jpeg_decode(&jpeg_cfg, &outimg);
|
||||||
|
TEST_ASSERT_EQUAL(ESP_OK, err);
|
||||||
|
|
||||||
|
/* Decoded image size */
|
||||||
|
TEST_ASSERT_EQUAL(160, outimg.width);
|
||||||
|
TEST_ASSERT_EQUAL(120, outimg.height);
|
||||||
|
|
||||||
|
free(decoded);
|
||||||
|
}
|
||||||
|
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 2.6 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
@ -0,0 +1,6 @@
|
|||||||
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.generic
|
||||||
|
def test_esp_jpeg(dut) -> None:
|
||||||
|
dut.run_all_single_board_cases()
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
# This file was generated using idf.py save-defconfig. It can be edited manually.
|
||||||
|
# Espressif IoT Development Framework (ESP-IDF) 5.4.0 Project Minimal Configuration
|
||||||
|
#
|
||||||
|
CONFIG_ESP_TASK_WDT_INIT=n
|
||||||
|
CONFIG_JD_USE_ROM=n
|
||||||
|
CONFIG_JD_DEFAULT_HUFFMAN=y
|
||||||
@ -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.4.0 Project Minimal Configuration
|
||||||
|
#
|
||||||
|
CONFIG_ESP_TASK_WDT_INIT=n
|
||||||
1392
managed_components/espressif__esp_jpeg/tjpgd/tjpgd.c
Normal file
1392
managed_components/espressif__esp_jpeg/tjpgd/tjpgd.c
Normal file
File diff suppressed because it is too large
Load Diff
102
managed_components/espressif__esp_jpeg/tjpgd/tjpgd.h
Normal file
102
managed_components/espressif__esp_jpeg/tjpgd/tjpgd.h
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
/*----------------------------------------------------------------------------/
|
||||||
|
/ TJpgDec - Tiny JPEG Decompressor R0.03 include file (C)ChaN, 2021
|
||||||
|
/----------------------------------------------------------------------------*/
|
||||||
|
#ifndef DEF_TJPGDEC
|
||||||
|
#define DEF_TJPGDEC
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "tjpgdcnf.h"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#if defined(_WIN32) /* VC++ or some compiler without stdint.h */
|
||||||
|
typedef unsigned char uint8_t;
|
||||||
|
typedef unsigned short uint16_t;
|
||||||
|
typedef short int16_t;
|
||||||
|
typedef unsigned long uint32_t;
|
||||||
|
typedef long int32_t;
|
||||||
|
#else /* Embedded platform */
|
||||||
|
#include <stdint.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if JD_FASTDECODE >= 1
|
||||||
|
typedef int16_t jd_yuv_t;
|
||||||
|
#else
|
||||||
|
typedef uint8_t jd_yuv_t;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/* Error code */
|
||||||
|
typedef enum {
|
||||||
|
JDR_OK = 0, /* 0: Succeeded */
|
||||||
|
JDR_INTR, /* 1: Interrupted by output function */
|
||||||
|
JDR_INP, /* 2: Device error or wrong termination of input stream */
|
||||||
|
JDR_MEM1, /* 3: Insufficient memory pool for the image */
|
||||||
|
JDR_MEM2, /* 4: Insufficient stream input buffer */
|
||||||
|
JDR_PAR, /* 5: Parameter error */
|
||||||
|
JDR_FMT1, /* 6: Data format error (may be broken data) */
|
||||||
|
JDR_FMT2, /* 7: Right format but not supported */
|
||||||
|
JDR_FMT3 /* 8: Not supported JPEG standard */
|
||||||
|
} JRESULT;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Rectangular region in the output image */
|
||||||
|
typedef struct {
|
||||||
|
uint16_t left; /* Left end */
|
||||||
|
uint16_t right; /* Right end */
|
||||||
|
uint16_t top; /* Top end */
|
||||||
|
uint16_t bottom; /* Bottom end */
|
||||||
|
} JRECT;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Decompressor object structure */
|
||||||
|
typedef struct JDEC JDEC;
|
||||||
|
struct JDEC {
|
||||||
|
size_t dctr; /* Number of bytes available in the input buffer */
|
||||||
|
uint8_t *dptr; /* Current data read ptr */
|
||||||
|
uint8_t *inbuf; /* Bit stream input buffer */
|
||||||
|
uint8_t dbit; /* Number of bits availavble in wreg or reading bit mask */
|
||||||
|
uint8_t scale; /* Output scaling ratio */
|
||||||
|
uint8_t msx, msy; /* MCU size in unit of block (width, height) */
|
||||||
|
uint8_t qtid[3]; /* Quantization table ID of each component, Y, Cb, Cr */
|
||||||
|
uint8_t ncomp; /* Number of color components 1:grayscale, 3:color */
|
||||||
|
int16_t dcv[3]; /* Previous DC element of each component */
|
||||||
|
uint16_t nrst; /* Restart inverval */
|
||||||
|
uint16_t width, height; /* Size of the input image (pixel) */
|
||||||
|
uint8_t *huffbits[2][2]; /* Huffman bit distribution tables [id][dcac] */
|
||||||
|
uint16_t *huffcode[2][2]; /* Huffman code word tables [id][dcac] */
|
||||||
|
uint8_t *huffdata[2][2]; /* Huffman decoded data tables [id][dcac] */
|
||||||
|
int32_t *qttbl[4]; /* Dequantizer tables [id] */
|
||||||
|
#if JD_FASTDECODE >= 1
|
||||||
|
uint32_t wreg; /* Working shift register */
|
||||||
|
uint8_t marker; /* Detected marker (0:None) */
|
||||||
|
#if JD_FASTDECODE == 2
|
||||||
|
uint8_t longofs[2][2]; /* Table offset of long code [id][dcac] */
|
||||||
|
uint16_t *hufflut_ac[2]; /* Fast huffman decode tables for AC short code [id] */
|
||||||
|
uint8_t *hufflut_dc[2]; /* Fast huffman decode tables for DC short code [id] */
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
void *workbuf; /* Working buffer for IDCT and RGB output */
|
||||||
|
jd_yuv_t *mcubuf; /* Working buffer for the MCU */
|
||||||
|
void *pool; /* Pointer to available memory pool */
|
||||||
|
size_t sz_pool; /* Size of momory pool (bytes available) */
|
||||||
|
size_t (*infunc)(JDEC *, uint8_t *, size_t); /* Pointer to jpeg stream input function */
|
||||||
|
void *device; /* Pointer to I/O device identifiler for the session */
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* TJpgDec API functions */
|
||||||
|
JRESULT jd_prepare (JDEC *jd, size_t (*infunc)(JDEC *, uint8_t *, size_t), void *pool, size_t sz_pool, void *dev);
|
||||||
|
JRESULT jd_decomp (JDEC *jd, int (*outfunc)(JDEC *, void *, JRECT *), uint8_t scale);
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* _TJPGDEC */
|
||||||
48
managed_components/espressif__esp_jpeg/tjpgd/tjpgdcnf.h
Normal file
48
managed_components/espressif__esp_jpeg/tjpgd/tjpgdcnf.h
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
/*----------------------------------------------*/
|
||||||
|
/* TJpgDec System Configurations R0.03 */
|
||||||
|
/*----------------------------------------------*/
|
||||||
|
|
||||||
|
#include "sdkconfig.h"
|
||||||
|
|
||||||
|
#define JD_SZBUF CONFIG_JD_SZBUF
|
||||||
|
/* Specifies size of stream input buffer */
|
||||||
|
|
||||||
|
#define JD_FORMAT CONFIG_JD_FORMAT
|
||||||
|
/* Specifies output pixel format.
|
||||||
|
/ 0: RGB888 (24-bit/pix)
|
||||||
|
/ 1: RGB565 (16-bit/pix)
|
||||||
|
/ 2: Grayscale (8-bit/pix)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if defined(CONFIG_JD_USE_SCALE)
|
||||||
|
#define JD_USE_SCALE CONFIG_JD_USE_SCALE
|
||||||
|
#else
|
||||||
|
#define JD_USE_SCALE 0
|
||||||
|
#endif
|
||||||
|
/* Switches output descaling feature.
|
||||||
|
/ 0: Disable
|
||||||
|
/ 1: Enable
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if defined(CONFIG_JD_TBLCLIP)
|
||||||
|
#define JD_TBLCLIP CONFIG_JD_TBLCLIP
|
||||||
|
#else
|
||||||
|
#define JD_TBLCLIP 0
|
||||||
|
#endif
|
||||||
|
/* Use table conversion for saturation arithmetic. A bit faster, but increases 1 KB of code size.
|
||||||
|
/ 0: Disable
|
||||||
|
/ 1: Enable
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define JD_FASTDECODE CONFIG_JD_FASTDECODE
|
||||||
|
/* Optimization level
|
||||||
|
/ 0: Basic optimization. Suitable for 8/16-bit MCUs.
|
||||||
|
/ 1: + 32-bit barrel shifter. Suitable for 32-bit MCUs.
|
||||||
|
/ 2: + Table conversion for huffman decoding (wants 6 << HUFF_BIT bytes of RAM)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if defined(CONFIG_JD_DEFAULT_HUFFMAN)
|
||||||
|
#define JD_DEFAULT_HUFFMAN CONFIG_JD_DEFAULT_HUFFMAN
|
||||||
|
#else
|
||||||
|
#define JD_DEFAULT_HUFFMAN 0
|
||||||
|
#endif
|
||||||
Loading…
x
Reference in New Issue
Block a user