add some code

This commit is contained in:
2025-09-05 13:25:11 +08:00
parent 9ff0a99e7a
commit 3cf1229a85
8911 changed files with 2535396 additions and 0 deletions

View File

@@ -0,0 +1 @@
defb83669293cbf86d0fa86b475ba5517aceed04ed70db435388c151ab37b5d7

View 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

File diff suppressed because one or more lines are too long

View 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})

View 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

View File

@@ -0,0 +1,112 @@
# 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.
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);
```

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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

View 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

View 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];
}

View File

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

View 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.

View File

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

View File

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

View File

@@ -0,0 +1,4 @@
dependencies:
espressif/esp_jpeg:
version: "*"
override_path: "../../"

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,6 @@
import pytest
@pytest.mark.generic
def test_esp_jpeg(dut) -> None:
dut.run_all_single_board_cases()

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View 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 */

View 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