espressif_idf-extra-components/esp_jpeg/jpeg_decoder.c

229 lines
7.5 KiB
C

/*
* SPDX-FileCopyrightText: 2015-2022 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);
/*******************************************************************************
* 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);
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");
cfg->priv.read = 0;
/* Prepare image */
res = jd_prepare(&JDEC, jpeg_decode_in_cb, workbuf, JPEG_WORK_BUF_SIZE, cfg);
ESP_GOTO_ON_FALSE((res == JDR_OK), ESP_FAIL, err, TAG, "Error in preparing JPEG image!");
uint8_t scale_div = jpeg_get_div_by_scale(cfg->out_scale);
uint8_t out_color_bytes = jpeg_get_color_bytes(cfg->out_format);
/* Size of output image */
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;
/* 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!");
err:
if (workbuf) {
free(workbuf);
}
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 (JD_FORMAT == 1 && 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;
}