diff --git a/.gitignore b/.gitignore index f39d5fda1b..77f2db2e6e 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ **/build **/sdkconfig **/sdkconfig.old -**/dependencies.lock \ No newline at end of file +**/dependencies.lock +**/managed_components/** \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 6ebc992074..36cefe0f74 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,7 +7,6 @@ set(srcs conversions/to_jpg.cpp conversions/to_bmp.c conversions/jpge.cpp - conversions/esp_jpg_decode.c ) set(priv_include_dirs @@ -61,7 +60,6 @@ if(IDF_TARGET STREQUAL "esp32" OR IDF_TARGET STREQUAL "esp32s2" OR IDF_TARGET ST list(APPEND srcs target/xclk.c target/esp32s2/ll_cam.c - target/tjpgd.c ) list(APPEND priv_include_dirs @@ -83,7 +81,7 @@ if(IDF_TARGET STREQUAL "esp32" OR IDF_TARGET STREQUAL "esp32s2" OR IDF_TARGET ST endif() # include the SCCB I2C driver - # this uses either the legacy I2C API or the newwer version from IDF v5.4 + # this uses either the legacy I2C API or the newer version from IDF v5.4 # as this features a method to obtain the I2C driver from a port number if (idf_version VERSION_GREATER_EQUAL "5.4") list(APPEND srcs driver/sccb-ng.c) @@ -93,17 +91,6 @@ if(IDF_TARGET STREQUAL "esp32" OR IDF_TARGET STREQUAL "esp32s2" OR IDF_TARGET ST endif() -# CONFIG_ESP_ROM_HAS_JPEG_DECODE is available from IDF v4.4 but -# previous IDF supported chips already support JPEG decoder, hence okay to use this -if(idf_version VERSION_GREATER_EQUAL "4.4" AND NOT CONFIG_ESP_ROM_HAS_JPEG_DECODE) - list(APPEND srcs - target/tjpgd.c - ) - list(APPEND priv_include_dirs - target/jpeg_include/ - ) -endif() - idf_component_register( SRCS ${srcs} INCLUDE_DIRS ${include_dirs} diff --git a/conversions/esp_jpg_decode.c b/conversions/esp_jpg_decode.c deleted file mode 100644 index cd9a3558bd..0000000000 --- a/conversions/esp_jpg_decode.c +++ /dev/null @@ -1,140 +0,0 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD -// -// 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. -#include "esp_jpg_decode.h" - -#include "esp_system.h" -#if ESP_IDF_VERSION_MAJOR >= 4 // IDF 4+ -#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4 -#include "esp32/rom/tjpgd.h" -#elif CONFIG_IDF_TARGET_ESP32S3 -#include "esp32s3/rom/tjpgd.h" -#elif CONFIG_IDF_TARGET_ESP32C3 -#include "esp32c3/rom/tjpgd.h" -#elif CONFIG_ESP_ROM_HAS_JPEG_DECODE // available since IDF 4.4 -#include "rom/tjpgd.h" // latest IDFs have `rom/` includes available -#else -#include "tjpgd.h" // using software decoder -#endif -#else // ESP32 Before IDF 4.0 -#include "rom/tjpgd.h" -#endif - -#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG) -#include "esp32-hal-log.h" -#define TAG "" -#else -#include "esp_log.h" -static const char* TAG = "esp_jpg_decode"; -#endif - -typedef struct { - jpg_scale_t scale; - jpg_reader_cb reader; - jpg_writer_cb writer; - void * arg; - size_t len; - size_t index; -} esp_jpg_decoder_t; - -static const char * jd_errors[] = { - "Succeeded", - "Interrupted by output function", - "Device error or wrong termination of input stream", - "Insufficient memory pool for the image", - "Insufficient stream input buffer", - "Parameter error", - "Data format error", - "Right format but not supported", - "Not supported JPEG standard" -}; - -static unsigned int _jpg_write(JDEC *decoder, void *bitmap, JRECT *rect) -{ - uint16_t x = rect->left; - uint16_t y = rect->top; - uint16_t w = rect->right + 1 - x; - uint16_t h = rect->bottom + 1 - y; - uint8_t *data = (uint8_t *)bitmap; - - esp_jpg_decoder_t * jpeg = (esp_jpg_decoder_t *)decoder->device; - - if (jpeg->writer) { - return jpeg->writer(jpeg->arg, x, y, w, h, data); - } - return 0; -} - -static unsigned int _jpg_read(JDEC *decoder, uint8_t *buf, unsigned int len) -{ - esp_jpg_decoder_t * jpeg = (esp_jpg_decoder_t *)decoder->device; - if (jpeg->len && len > (jpeg->len - jpeg->index)) { - len = jpeg->len - jpeg->index; - } - if (len) { - len = jpeg->reader(jpeg->arg, jpeg->index, buf, len); - if (!len) { - ESP_LOGE(TAG, "Read Fail at %u/%u", jpeg->index, jpeg->len); - } - jpeg->index += len; - } - return len; -} - -esp_err_t esp_jpg_decode(size_t len, jpg_scale_t scale, jpg_reader_cb reader, jpg_writer_cb writer, void * arg) -{ - static uint8_t work[3100]; - JDEC decoder; - esp_jpg_decoder_t jpeg; - - jpeg.len = len; - jpeg.reader = reader; - jpeg.writer = writer; - jpeg.arg = arg; - jpeg.scale = scale; - jpeg.index = 0; - - JRESULT jres = jd_prepare(&decoder, _jpg_read, work, 3100, &jpeg); - if(jres != JDR_OK){ - ESP_LOGE(TAG, "JPG Header Parse Failed! %s", jd_errors[jres]); - return ESP_FAIL; - } - - uint16_t output_width = decoder.width / (1 << (uint8_t)(jpeg.scale)); - uint16_t output_height = decoder.height / (1 << (uint8_t)(jpeg.scale)); - - //output start - if (!writer(arg, 0, 0, output_width, output_height, NULL)) { - ESP_LOGE(TAG, "JPG Writer Start Failed!"); - return ESP_FAIL; - } - //output write - jres = jd_decomp(&decoder, _jpg_write, (uint8_t)jpeg.scale); - //output end - if (!writer(arg, output_width, output_height, output_width, output_height, NULL)) { - ESP_LOGE(TAG, "JPG Writer End Failed!"); - return ESP_FAIL; - } - - if (jres != JDR_OK) { - ESP_LOGE(TAG, "JPG Decompression Failed! %s", jd_errors[jres]); - return ESP_FAIL; - } - //check if all data has been consumed. - if (len && jpeg.index < len) { - _jpg_read(&decoder, NULL, len - jpeg.index); - } - - return ESP_OK; -} - diff --git a/conversions/include/esp_jpg_decode.h b/conversions/include/esp_jpg_decode.h deleted file mode 100644 index f13536edf4..0000000000 --- a/conversions/include/esp_jpg_decode.h +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD -// -// 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. -#ifndef _ESP_JPG_DECODE_H_ -#define _ESP_JPG_DECODE_H_ - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include -#include -#include "esp_err.h" - -typedef enum { - JPG_SCALE_NONE, - JPG_SCALE_2X, - JPG_SCALE_4X, - JPG_SCALE_8X, - JPG_SCALE_MAX = JPG_SCALE_8X -} jpg_scale_t; - -typedef size_t (* jpg_reader_cb)(void * arg, size_t index, uint8_t *buf, size_t len); -typedef bool (* jpg_writer_cb)(void * arg, uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint8_t *data); - -esp_err_t esp_jpg_decode(size_t len, jpg_scale_t scale, jpg_reader_cb reader, jpg_writer_cb writer, void * arg); - -#ifdef __cplusplus -} -#endif - -#endif /* _ESP_JPG_DECODE_H_ */ diff --git a/conversions/include/img_converters.h b/conversions/include/img_converters.h index f736200a9b..6e71ce93fd 100644 --- a/conversions/include/img_converters.h +++ b/conversions/include/img_converters.h @@ -1,4 +1,4 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// Copyright 2015-2025 Espressif Systems (Shanghai) PTE LTD // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -22,7 +22,7 @@ extern "C" { #include #include #include "esp_camera.h" -#include "esp_jpg_decode.h" +#include "jpeg_decoder.h" typedef size_t (* jpg_out_cb)(void * arg, size_t index, const void* data, size_t len); @@ -121,7 +121,13 @@ bool frame2bmp(camera_fb_t * fb, uint8_t ** out, size_t * out_len); */ bool fmt2rgb888(const uint8_t *src_buf, size_t src_len, pixformat_t format, uint8_t * rgb_buf); -bool jpg2rgb565(const uint8_t *src, size_t src_len, uint8_t * out, jpg_scale_t scale); +// Macros for backwards compatibility +#define JPG_SCALE_NONE JPEG_IMAGE_SCALE_0 +#define JPG_SCALE_2X JPEG_IMAGE_SCALE_1_2 +#define JPG_SCALE_4X JPEG_IMAGE_SCALE_1_4 +#define JPG_SCALE_8X JPEG_IMAGE_SCALE_1_8 +#define JPG_SCALE_MAX JPEG_IMAGE_SCALE_1_8 +bool jpg2rgb565(const uint8_t *src, size_t src_len, uint8_t * out, esp_jpeg_image_scale_t scale); #ifdef __cplusplus } diff --git a/conversions/to_bmp.c b/conversions/to_bmp.c index 9f016aa97d..8c1a6f05a0 100644 --- a/conversions/to_bmp.c +++ b/conversions/to_bmp.c @@ -1,4 +1,4 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// Copyright 2015-2025 Espressif Systems (Shanghai) PTE LTD // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ #include "esp_heap_caps.h" #include "yuv.h" #include "sdkconfig.h" -#include "esp_jpg_decode.h" +#include "jpeg_decoder.h" #include "esp_system.h" @@ -31,6 +31,7 @@ static const char* TAG = "to_bmp"; #endif static const int BMP_HEADER_LEN = 54; +static uint8_t work[3100]; // 3.1kB for JPEG decoder, static for legacy reasons typedef struct { uint32_t filesize; @@ -49,14 +50,6 @@ typedef struct { uint32_t mostimpcolor; } bmp_header_t; -typedef struct { - uint16_t width; - uint16_t height; - uint16_t data_offset; - const uint8_t *input; - uint8_t *output; -} rgb_jpg_decoder; - static void *_malloc(size_t size) { // check if SPIRAM is enabled and allocate on SPIRAM if allocatable @@ -67,178 +60,104 @@ static void *_malloc(size_t size) return malloc(size); } -//output buffer and image width -static bool _rgb_write(void * arg, uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint8_t *data) +static bool jpg2rgb888(const uint8_t *src, size_t src_len, uint8_t * out, esp_jpeg_image_scale_t scale) { - rgb_jpg_decoder * jpeg = (rgb_jpg_decoder *)arg; - if(!data){ - if(x == 0 && y == 0){ - //write start - jpeg->width = w; - jpeg->height = h; - //if output is null, this is BMP - if(!jpeg->output){ - size_t out_size = (w*h*3)+jpeg->data_offset; - jpeg->output = (uint8_t *)_malloc(out_size); - if(!jpeg->output){ - ESP_LOGE(TAG, "_malloc failed! %zu", out_size); - return false; - } - } - } else { - //write end - } - return true; - } - - size_t jw = jpeg->width*3; - size_t t = y * jw; - size_t b = t + (h * jw); - size_t l = x * 3; - uint8_t *out = jpeg->output+jpeg->data_offset; - uint8_t *o = out; - size_t iy, ix; - - w = w * 3; - - for(iy=t; iywidth = w; - jpeg->height = h; - //if output is null, this is BMP - if(!jpeg->output){ - size_t out_size = (w*h*3)+jpeg->data_offset; - jpeg->output = (uint8_t *)_malloc(out_size); - if(!jpeg->output){ - ESP_LOGE(TAG, "_malloc failed! %zu", out_size); - return false; - } - } - } else { - //write end - } - return true; - } - - size_t jw = jpeg->width*3; - size_t jw2 = jpeg->width*2; - size_t t = y * jw; - size_t t2 = y * jw2; - size_t b = t + (h * jw); - size_t l = x * 2; - uint8_t *out = jpeg->output+jpeg->data_offset; - uint8_t *o = out; - size_t iy, iy2, ix, ix2; - - w = w * 3; - - for(iy=t, iy2=t2; iy> 3); - o[ix2+1] = c>>8; - o[ix2] = c&0xff; - } - data+=w; + esp_jpeg_image_cfg_t jpeg_cfg = { + .indata = (uint8_t *)src, + .indata_size = src_len, + .outbuf = out, + .outbuf_size = UINT32_MAX, // @todo: this is very bold assumption, keeping this like this for now, not to break existing code + .out_format = JPEG_IMAGE_FORMAT_RGB565, + .out_scale = scale, + .flags.swap_color_bytes = 0, + .advanced.working_buffer = work, + .advanced.working_buffer_size = sizeof(work), + }; + + esp_jpeg_image_output_t output_img = {}; + + if(esp_jpeg_decode(&jpeg_cfg, &output_img) != ESP_OK){ + return false; } return true; } -//input buffer -static unsigned int _jpg_read(void * arg, size_t index, uint8_t *buf, size_t len) -{ - rgb_jpg_decoder * jpeg = (rgb_jpg_decoder *)arg; - if(buf) { - memcpy(buf, jpeg->input + index, len); - } - return len; -} - -static bool jpg2rgb888(const uint8_t *src, size_t src_len, uint8_t * out, jpg_scale_t scale) +bool jpg2bmp(const uint8_t *src, size_t src_len, uint8_t ** out, size_t * out_len) { - rgb_jpg_decoder jpeg; - jpeg.width = 0; - jpeg.height = 0; - jpeg.input = src; - jpeg.output = out; - jpeg.data_offset = 0; - - if(esp_jpg_decode(src_len, scale, _jpg_read, _rgb_write, (void*)&jpeg) != ESP_OK){ + esp_jpeg_image_cfg_t jpeg_cfg = { + .indata = (uint8_t *)src, + .indata_size = src_len, + .out_format = JPEG_IMAGE_FORMAT_RGB888, + .out_scale = JPEG_IMAGE_SCALE_0, + .flags.swap_color_bytes = 0, + .advanced.working_buffer = work, + .advanced.working_buffer_size = sizeof(work), + }; + + esp_jpeg_image_output_t output_img = {}; + if (esp_jpeg_get_image_info(&jpeg_cfg, &output_img) != ESP_OK) { + ESP_LOGE(TAG, "Failed to get image info"); return false; } - return true; -} - -bool jpg2rgb565(const uint8_t *src, size_t src_len, uint8_t * out, jpg_scale_t scale) -{ - rgb_jpg_decoder jpeg; - jpeg.width = 0; - jpeg.height = 0; - jpeg.input = src; - jpeg.output = out; - jpeg.data_offset = 0; - - if(esp_jpg_decode(src_len, scale, _jpg_read, _rgb565_write, (void*)&jpeg) != ESP_OK){ + + // @todo here we allocate memory and we assume that the user will free it + // this is not the best way to do it, but we need to keep the API + // compatible with the previous version + const size_t output_size = output_img.output_len + BMP_HEADER_LEN; + uint8_t *output = _malloc(output_size); + if (!output) { + ESP_LOGE(TAG, "Failed to allocate output buffer"); return false; } - return true; -} -bool jpg2bmp(const uint8_t *src, size_t src_len, uint8_t ** out, size_t * out_len) -{ - - rgb_jpg_decoder jpeg; - jpeg.width = 0; - jpeg.height = 0; - jpeg.input = src; - jpeg.output = NULL; - jpeg.data_offset = BMP_HEADER_LEN; - - if(esp_jpg_decode(src_len, JPG_SCALE_NONE, _jpg_read, _rgb_write, (void*)&jpeg) != ESP_OK){ + // Start writing decoded data after the BMP header + jpeg_cfg.outbuf = output + BMP_HEADER_LEN; + jpeg_cfg.outbuf_size = output_img.output_len; + if(esp_jpeg_decode(&jpeg_cfg, &output_img) != ESP_OK){ return false; } - size_t output_size = jpeg.width*jpeg.height*3; - - jpeg.output[0] = 'B'; - jpeg.output[1] = 'M'; - bmp_header_t * bitmap = (bmp_header_t*)&jpeg.output[2]; + output[0] = 'B'; + output[1] = 'M'; + bmp_header_t * bitmap = (bmp_header_t*)&output[2]; bitmap->reserved = 0; - bitmap->filesize = output_size+BMP_HEADER_LEN; + bitmap->filesize = output_size; bitmap->fileoffset_to_pixelarray = BMP_HEADER_LEN; bitmap->dibheadersize = 40; - bitmap->width = jpeg.width; - bitmap->height = -jpeg.height;//set negative for top to bottom + bitmap->width = output_img.width; + bitmap->height = -output_img.height; //set negative for top to bottom bitmap->planes = 1; bitmap->bitsperpixel = 24; bitmap->compression = 0; - bitmap->imagesize = output_size; + bitmap->imagesize = output_img.output_len; bitmap->ypixelpermeter = 0x0B13 ; //2835 , 72 DPI bitmap->xpixelpermeter = 0x0B13 ; //2835 , 72 DPI bitmap->numcolorspallette = 0; bitmap->mostimpcolor = 0; - *out = jpeg.output; - *out_len = output_size+BMP_HEADER_LEN; + *out = output; + *out_len = output_size; return true; } @@ -247,7 +166,7 @@ bool fmt2rgb888(const uint8_t *src_buf, size_t src_len, pixformat_t format, uint { int pix_count = 0; if(format == PIXFORMAT_JPEG) { - return jpg2rgb888(src_buf, src_len, rgb_buf, JPG_SCALE_NONE); + return jpg2rgb888(src_buf, src_len, rgb_buf, JPEG_IMAGE_SCALE_0); } else if(format == PIXFORMAT_RGB888) { memcpy(rgb_buf, src_buf, src_len); } else if(format == PIXFORMAT_RGB565) { diff --git a/idf_component.yml b/idf_component.yml index d51d3691b7..8b454fcd01 100644 --- a/idf_component.yml +++ b/idf_component.yml @@ -3,3 +3,7 @@ url: https://github.com/espressif/esp32-camera issues: https://github.com/espressif/esp32-camera/issues documentation: https://github.com/espressif/esp32-camera/tree/main/README.md repository: https://github.com/espressif/esp32-camera.git +dependencies: + esp_jpeg: + version: "^1.3.0" + public: true diff --git a/target/jpeg_include/tjpgd.h b/target/jpeg_include/tjpgd.h deleted file mode 100644 index 31fbc97cce..0000000000 --- a/target/jpeg_include/tjpgd.h +++ /dev/null @@ -1,99 +0,0 @@ -/*----------------------------------------------------------------------------/ -/ TJpgDec - Tiny JPEG Decompressor include file (C)ChaN, 2012 -/----------------------------------------------------------------------------*/ -#ifndef _TJPGDEC -#define _TJPGDEC -/*---------------------------------------------------------------------------*/ -/* System Configurations */ - -#define JD_SZBUF 512 /* Size of stream input buffer */ -#define JD_FORMAT 0 /* Output pixel format 0:RGB888 (3 BYTE/pix), 1:RGB565 (1 WORD/pix) */ -#define JD_USE_SCALE 1 /* Use descaling feature for output */ -#define JD_TBLCLIP 1 /* Use table for saturation (might be a bit faster but increases 1K bytes of code size) */ - -/*---------------------------------------------------------------------------*/ - -#ifdef __cplusplus -extern "C" { -#endif - -/* These types must be 16-bit, 32-bit or larger integer */ -typedef int INT; -typedef unsigned int UINT; - -/* These types must be 8-bit integer */ -typedef char CHAR; -typedef unsigned char UCHAR; -typedef unsigned char BYTE; - -/* These types must be 16-bit integer */ -typedef short SHORT; -typedef unsigned short USHORT; -typedef unsigned short WORD; -typedef unsigned short WCHAR; - -/* These types must be 32-bit integer */ -typedef long LONG; -typedef unsigned long ULONG; -typedef unsigned long DWORD; - - -/* 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 damaged data) */ - JDR_FMT2, /* 7: Right format but not supported */ - JDR_FMT3 /* 8: Not supported JPEG standard */ -} JRESULT; - - - -/* Rectangular structure */ -typedef struct { - WORD left, right, top, bottom; -} JRECT; - - - -/* Decompressor object structure */ -typedef struct JDEC JDEC; -struct JDEC { - UINT dctr; /* Number of bytes available in the input buffer */ - BYTE* dptr; /* Current data read ptr */ - BYTE* inbuf; /* Bit stream input buffer */ - BYTE dmsk; /* Current bit in the current read byte */ - BYTE scale; /* Output scaling ratio */ - BYTE msx, msy; /* MCU size in unit of block (width, height) */ - BYTE qtid[3]; /* Quantization table ID of each component */ - SHORT dcv[3]; /* Previous DC element of each component */ - WORD nrst; /* Restart inverval */ - UINT width, height; /* Size of the input image (pixel) */ - BYTE* huffbits[2][2]; /* Huffman bit distribution tables [id][dcac] */ - WORD* huffcode[2][2]; /* Huffman code word tables [id][dcac] */ - BYTE* huffdata[2][2]; /* Huffman decoded data tables [id][dcac] */ - LONG* qttbl[4]; /* Dequaitizer tables [id] */ - void* workbuf; /* Working buffer for IDCT and RGB output */ - BYTE* mcubuf; /* Working buffer for the MCU */ - void* pool; /* Pointer to available memory pool */ - UINT sz_pool; /* Size of momory pool (bytes available) */ - UINT (*infunc)(JDEC*, BYTE*, UINT);/* Pointer to jpeg stream input function */ - void* device; /* Pointer to I/O device identifiler for the session */ -}; - - - -/* TJpgDec API functions */ -JRESULT jd_prepare (JDEC*, UINT(*)(JDEC*,BYTE*,UINT), void*, UINT, void*); -JRESULT jd_decomp (JDEC*, UINT(*)(JDEC*,void*,JRECT*), BYTE); - - -#ifdef __cplusplus -} -#endif - -#endif /* _TJPGDEC */ diff --git a/target/tjpgd.c b/target/tjpgd.c deleted file mode 100755 index 5a983c4c7f..0000000000 --- a/target/tjpgd.c +++ /dev/null @@ -1,970 +0,0 @@ -/*----------------------------------------------------------------------------/ -/ TJpgDec - Tiny JPEG Decompressor R0.01b (C)ChaN, 2012 -/-----------------------------------------------------------------------------/ -/ The TJpgDec is a generic JPEG decompressor module for tiny embedded systems. -/ This is a free software that opened for education, research and commercial -/ developments under license policy of following terms. -/ -/ Copyright (C) 2012, ChaN, all right reserved. -/ -/ * The TJpgDec module is a free software and there is NO WARRANTY. -/ * No restriction on use. You can use, modify and redistribute it for -/ personal, non-profit or commercial products UNDER YOUR RESPONSIBILITY. -/ * Redistributions of source code must retain the above copyright notice. -/ -/-----------------------------------------------------------------------------/ -/ Oct 04,'11 R0.01 First release. -/ Feb 19,'12 R0.01a Fixed decompression fails when scan starts with an escape seq. -/ Sep 03,'12 R0.01b Added JD_TBLCLIP option. -/----------------------------------------------------------------------------*/ - -#include "tjpgd.h" - -#define SUPPORT_JPEG 1 - -#ifdef SUPPORT_JPEG -/*-----------------------------------------------*/ -/* Zigzag-order to raster-order conversion table */ -/*-----------------------------------------------*/ - -#define ZIG(n) Zig[n] - -static -const BYTE Zig[64] = { /* Zigzag-order to raster-order conversion table */ - 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, - 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, - 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, - 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63 -}; - - - -/*-------------------------------------------------*/ -/* Input scale factor of Arai algorithm */ -/* (scaled up 16 bits for fixed point operations) */ -/*-------------------------------------------------*/ - -#define IPSF(n) Ipsf[n] - -static -const WORD Ipsf[64] = { /* See also aa_idct.png */ - (WORD)(1.00000*8192), (WORD)(1.38704*8192), (WORD)(1.30656*8192), (WORD)(1.17588*8192), (WORD)(1.00000*8192), (WORD)(0.78570*8192), (WORD)(0.54120*8192), (WORD)(0.27590*8192), - (WORD)(1.38704*8192), (WORD)(1.92388*8192), (WORD)(1.81226*8192), (WORD)(1.63099*8192), (WORD)(1.38704*8192), (WORD)(1.08979*8192), (WORD)(0.75066*8192), (WORD)(0.38268*8192), - (WORD)(1.30656*8192), (WORD)(1.81226*8192), (WORD)(1.70711*8192), (WORD)(1.53636*8192), (WORD)(1.30656*8192), (WORD)(1.02656*8192), (WORD)(0.70711*8192), (WORD)(0.36048*8192), - (WORD)(1.17588*8192), (WORD)(1.63099*8192), (WORD)(1.53636*8192), (WORD)(1.38268*8192), (WORD)(1.17588*8192), (WORD)(0.92388*8192), (WORD)(0.63638*8192), (WORD)(0.32442*8192), - (WORD)(1.00000*8192), (WORD)(1.38704*8192), (WORD)(1.30656*8192), (WORD)(1.17588*8192), (WORD)(1.00000*8192), (WORD)(0.78570*8192), (WORD)(0.54120*8192), (WORD)(0.27590*8192), - (WORD)(0.78570*8192), (WORD)(1.08979*8192), (WORD)(1.02656*8192), (WORD)(0.92388*8192), (WORD)(0.78570*8192), (WORD)(0.61732*8192), (WORD)(0.42522*8192), (WORD)(0.21677*8192), - (WORD)(0.54120*8192), (WORD)(0.75066*8192), (WORD)(0.70711*8192), (WORD)(0.63638*8192), (WORD)(0.54120*8192), (WORD)(0.42522*8192), (WORD)(0.29290*8192), (WORD)(0.14932*8192), - (WORD)(0.27590*8192), (WORD)(0.38268*8192), (WORD)(0.36048*8192), (WORD)(0.32442*8192), (WORD)(0.27590*8192), (WORD)(0.21678*8192), (WORD)(0.14932*8192), (WORD)(0.07612*8192) -}; - - - -/*---------------------------------------------*/ -/* Conversion table for fast clipping process */ -/*---------------------------------------------*/ - -#if JD_TBLCLIP - -#define BYTECLIP(v) Clip8[(UINT)(v) & 0x3FF] - -static -const BYTE Clip8[1024] = { - /* 0..255 */ - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, - 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, - 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, - 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, - 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, - 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, - 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, - /* 256..511 */ - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - /* -512..-257 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - /* -256..-1 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -}; - -#else /* JD_TBLCLIP */ - -inline -BYTE BYTECLIP ( - INT val -) -{ - if (val < 0) val = 0; - if (val > 255) val = 255; - - return (BYTE)val; -} - -#endif - - - -/*-----------------------------------------------------------------------*/ -/* Allocate a memory block from memory pool */ -/*-----------------------------------------------------------------------*/ - -static -void* alloc_pool ( /* Pointer to allocated memory block (NULL:no memory available) */ - JDEC* jd, /* Pointer to the decompressor object */ - UINT nd /* Number of bytes to allocate */ -) -{ - char *rp = 0; - - - nd = (nd + 3) & ~3; /* Align block size to the word boundary */ - - if (jd->sz_pool >= nd) { - jd->sz_pool -= nd; - rp = (char*)jd->pool; /* Get start of available memory pool */ - jd->pool = (void*)(rp + nd); /* Allocate requierd bytes */ - } - - return (void*)rp; /* Return allocated memory block (NULL:no memory to allocate) */ -} - - - - -/*-----------------------------------------------------------------------*/ -/* Create de-quantization and prescaling tables with a DQT segment */ -/*-----------------------------------------------------------------------*/ - -static -UINT create_qt_tbl ( /* 0:OK, !0:Failed */ - JDEC* jd, /* Pointer to the decompressor object */ - const BYTE* data, /* Pointer to the quantizer tables */ - UINT ndata /* Size of input data */ -) -{ - UINT i; - BYTE d, z; - LONG *pb; - - - while (ndata) { /* Process all tables in the segment */ - if (ndata < 65) return JDR_FMT1; /* Err: table size is unaligned */ - ndata -= 65; - d = *data++; /* Get table property */ - if (d & 0xF0) return JDR_FMT1; /* Err: not 8-bit resolution */ - i = d & 3; /* Get table ID */ - pb = alloc_pool(jd, 64 * sizeof (LONG));/* Allocate a memory block for the table */ - if (!pb) return JDR_MEM1; /* Err: not enough memory */ - jd->qttbl[i] = pb; /* Register the table */ - for (i = 0; i < 64; i++) { /* Load the table */ - z = ZIG(i); /* Zigzag-order to raster-order conversion */ - pb[z] = (LONG)((DWORD)*data++ * IPSF(z)); /* Apply scale factor of Arai algorithm to the de-quantizers */ - } - } - - return JDR_OK; -} - - - - -/*-----------------------------------------------------------------------*/ -/* Create huffman code tables with a DHT segment */ -/*-----------------------------------------------------------------------*/ - -static -UINT create_huffman_tbl ( /* 0:OK, !0:Failed */ - JDEC* jd, /* Pointer to the decompressor object */ - const BYTE* data, /* Pointer to the packed huffman tables */ - UINT ndata /* Size of input data */ -) -{ - UINT i, j, b, np, cls, num; - BYTE d, *pb, *pd; - WORD hc, *ph; - - - while (ndata) { /* Process all tables in the segment */ - if (ndata < 17) return JDR_FMT1; /* Err: wrong data size */ - ndata -= 17; - d = *data++; /* Get table number and class */ - cls = (d >> 4); num = d & 0x0F; /* class = dc(0)/ac(1), table number = 0/1 */ - if (d & 0xEE) return JDR_FMT1; /* Err: invalid class/number */ - pb = alloc_pool(jd, 16); /* Allocate a memory block for the bit distribution table */ - if (!pb) return JDR_MEM1; /* Err: not enough memory */ - jd->huffbits[num][cls] = pb; - for (np = i = 0; i < 16; i++) { /* Load number of patterns for 1 to 16-bit code */ - pb[i] = b = *data++; - np += b; /* Get sum of code words for each code */ - } - - ph = alloc_pool(jd, np * sizeof (WORD));/* Allocate a memory block for the code word table */ - if (!ph) return JDR_MEM1; /* Err: not enough memory */ - jd->huffcode[num][cls] = ph; - hc = 0; - for (j = i = 0; i < 16; i++) { /* Re-build huffman code word table */ - b = pb[i]; - while (b--) ph[j++] = hc++; - hc <<= 1; - } - - if (ndata < np) return JDR_FMT1; /* Err: wrong data size */ - ndata -= np; - pd = alloc_pool(jd, np); /* Allocate a memory block for the decoded data */ - if (!pd) return JDR_MEM1; /* Err: not enough memory */ - jd->huffdata[num][cls] = pd; - for (i = 0; i < np; i++) { /* Load decoded data corresponds to each code ward */ - d = *data++; - if (!cls && d > 11) return JDR_FMT1; - *pd++ = d; - } - } - - return JDR_OK; -} - - - - -/*-----------------------------------------------------------------------*/ -/* Extract N bits from input stream */ -/*-----------------------------------------------------------------------*/ - -static -INT bitext ( /* >=0: extracted data, <0: error code */ - JDEC* jd, /* Pointer to the decompressor object */ - UINT nbit /* Number of bits to extract (1 to 11) */ -) -{ - BYTE msk, s, *dp; - UINT dc, v, f; - - - msk = jd->dmsk; dc = jd->dctr; dp = jd->dptr; /* Bit mask, number of data available, read ptr */ - s = *dp; v = f = 0; - do { - if (!msk) { /* Next byte? */ - if (!dc) { /* No input data is available, re-fill input buffer */ - dp = jd->inbuf; /* Top of input buffer */ - dc = jd->infunc(jd, dp, JD_SZBUF); - if (!dc) return 0 - (INT)JDR_INP; /* Err: read error or wrong stream termination */ - } else { - dp++; /* Next data ptr */ - } - dc--; /* Decrement number of available bytes */ - if (f) { /* In flag sequence? */ - f = 0; /* Exit flag sequence */ - if (*dp != 0) return 0 - (INT)JDR_FMT1; /* Err: unexpected flag is detected (may be collapted data) */ - *dp = s = 0xFF; /* The flag is a data 0xFF */ - } else { - s = *dp; /* Get next data byte */ - if (s == 0xFF) { /* Is start of flag sequence? */ - f = 1; continue; /* Enter flag sequence */ - } - } - msk = 0x80; /* Read from MSB */ - } - v <<= 1; /* Get a bit */ - if (s & msk) v++; - msk >>= 1; - nbit--; - } while (nbit); - jd->dmsk = msk; jd->dctr = dc; jd->dptr = dp; - - return (INT)v; -} - - - - -/*-----------------------------------------------------------------------*/ -/* Extract a huffman decoded data from input stream */ -/*-----------------------------------------------------------------------*/ - -static -INT huffext ( /* >=0: decoded data, <0: error code */ - JDEC* jd, /* Pointer to the decompressor object */ - const BYTE* hbits, /* Pointer to the bit distribution table */ - const WORD* hcode, /* Pointer to the code word table */ - const BYTE* hdata /* Pointer to the data table */ -) -{ - BYTE msk, s, *dp; - UINT dc, v, f, bl, nd; - - - msk = jd->dmsk; dc = jd->dctr; dp = jd->dptr; /* Bit mask, number of data available, read ptr */ - s = *dp; v = f = 0; - bl = 16; /* Max code length */ - do { - if (!msk) { /* Next byte? */ - if (!dc) { /* No input data is available, re-fill input buffer */ - dp = jd->inbuf; /* Top of input buffer */ - dc = jd->infunc(jd, dp, JD_SZBUF); - if (!dc) return 0 - (INT)JDR_INP; /* Err: read error or wrong stream termination */ - } else { - dp++; /* Next data ptr */ - } - dc--; /* Decrement number of available bytes */ - if (f) { /* In flag sequence? */ - f = 0; /* Exit flag sequence */ - if (*dp != 0) - return 0 - (INT)JDR_FMT1; /* Err: unexpected flag is detected (may be collapted data) */ - *dp = s = 0xFF; /* The flag is a data 0xFF */ - } else { - s = *dp; /* Get next data byte */ - if (s == 0xFF) { /* Is start of flag sequence? */ - f = 1; continue; /* Enter flag sequence, get trailing byte */ - } - } - msk = 0x80; /* Read from MSB */ - } - v <<= 1; /* Get a bit */ - if (s & msk) v++; - msk >>= 1; - - for (nd = *hbits++; nd; nd--) { /* Search the code word in this bit length */ - if (v == *hcode++) { /* Matched? */ - jd->dmsk = msk; jd->dctr = dc; jd->dptr = dp; - return *hdata; /* Return the decoded data */ - } - hdata++; - } - bl--; - } while (bl); - - return 0 - (INT)JDR_FMT1; /* Err: code not found (may be collapted data) */ -} - - - - -/*-----------------------------------------------------------------------*/ -/* Apply Inverse-DCT in Arai Algorithm (see also aa_idct.png) */ -/*-----------------------------------------------------------------------*/ - -static -void block_idct ( - LONG* src, /* Input block data (de-quantized and pre-scaled for Arai Algorithm) */ - BYTE* dst /* Pointer to the destination to store the block as byte array */ -) -{ - const LONG M13 = (LONG)(1.41421*4096), M2 = (LONG)(1.08239*4096), M4 = (LONG)(2.61313*4096), M5 = (LONG)(1.84776*4096); - LONG v0, v1, v2, v3, v4, v5, v6, v7; - LONG t10, t11, t12, t13; - UINT i; - - /* Process columns */ - for (i = 0; i < 8; i++) { - v0 = src[8 * 0]; /* Get even elements */ - v1 = src[8 * 2]; - v2 = src[8 * 4]; - v3 = src[8 * 6]; - - t10 = v0 + v2; /* Process the even elements */ - t12 = v0 - v2; - t11 = (v1 - v3) * M13 >> 12; - v3 += v1; - t11 -= v3; - v0 = t10 + v3; - v3 = t10 - v3; - v1 = t11 + t12; - v2 = t12 - t11; - - v4 = src[8 * 7]; /* Get odd elements */ - v5 = src[8 * 1]; - v6 = src[8 * 5]; - v7 = src[8 * 3]; - - t10 = v5 - v4; /* Process the odd elements */ - t11 = v5 + v4; - t12 = v6 - v7; - v7 += v6; - v5 = (t11 - v7) * M13 >> 12; - v7 += t11; - t13 = (t10 + t12) * M5 >> 12; - v4 = t13 - (t10 * M2 >> 12); - v6 = t13 - (t12 * M4 >> 12) - v7; - v5 -= v6; - v4 -= v5; - - src[8 * 0] = v0 + v7; /* Write-back transformed values */ - src[8 * 7] = v0 - v7; - src[8 * 1] = v1 + v6; - src[8 * 6] = v1 - v6; - src[8 * 2] = v2 + v5; - src[8 * 5] = v2 - v5; - src[8 * 3] = v3 + v4; - src[8 * 4] = v3 - v4; - - src++; /* Next column */ - } - - /* Process rows */ - src -= 8; - for (i = 0; i < 8; i++) { - v0 = src[0] + (128L << 8); /* Get even elements (remove DC offset (-128) here) */ - v1 = src[2]; - v2 = src[4]; - v3 = src[6]; - - t10 = v0 + v2; /* Process the even elements */ - t12 = v0 - v2; - t11 = (v1 - v3) * M13 >> 12; - v3 += v1; - t11 -= v3; - v0 = t10 + v3; - v3 = t10 - v3; - v1 = t11 + t12; - v2 = t12 - t11; - - v4 = src[7]; /* Get odd elements */ - v5 = src[1]; - v6 = src[5]; - v7 = src[3]; - - t10 = v5 - v4; /* Process the odd elements */ - t11 = v5 + v4; - t12 = v6 - v7; - v7 += v6; - v5 = (t11 - v7) * M13 >> 12; - v7 += t11; - t13 = (t10 + t12) * M5 >> 12; - v4 = t13 - (t10 * M2 >> 12); - v6 = t13 - (t12 * M4 >> 12) - v7; - v5 -= v6; - v4 -= v5; - - dst[0] = BYTECLIP((v0 + v7) >> 8); /* Descale the transformed values 8 bits and output */ - dst[7] = BYTECLIP((v0 - v7) >> 8); - dst[1] = BYTECLIP((v1 + v6) >> 8); - dst[6] = BYTECLIP((v1 - v6) >> 8); - dst[2] = BYTECLIP((v2 + v5) >> 8); - dst[5] = BYTECLIP((v2 - v5) >> 8); - dst[3] = BYTECLIP((v3 + v4) >> 8); - dst[4] = BYTECLIP((v3 - v4) >> 8); - dst += 8; - - src += 8; /* Next row */ - } -} - - - - -/*-----------------------------------------------------------------------*/ -/* Load all blocks in the MCU into working buffer */ -/*-----------------------------------------------------------------------*/ - -static -JRESULT mcu_load ( - JDEC* jd /* Pointer to the decompressor object */ -) -{ - LONG *tmp = (LONG*)jd->workbuf; /* Block working buffer for de-quantize and IDCT */ - UINT blk, nby, nbc, i, z, id, cmp; - INT b, d, e; - BYTE *bp; - const BYTE *hb, *hd; - const WORD *hc; - const LONG *dqf; - - - nby = jd->msx * jd->msy; /* Number of Y blocks (1, 2 or 4) */ - nbc = 2; /* Number of C blocks (2) */ - bp = jd->mcubuf; /* Pointer to the first block */ - - for (blk = 0; blk < nby + nbc; blk++) { - cmp = (blk < nby) ? 0 : blk - nby + 1; /* Component number 0:Y, 1:Cb, 2:Cr */ - id = cmp ? 1 : 0; /* Huffman table ID of the component */ - - /* Extract a DC element from input stream */ - hb = jd->huffbits[id][0]; /* Huffman table for the DC element */ - hc = jd->huffcode[id][0]; - hd = jd->huffdata[id][0]; - b = huffext(jd, hb, hc, hd); /* Extract a huffman coded data (bit length) */ - if (b < 0) return 0 - b; /* Err: invalid code or input */ - d = jd->dcv[cmp]; /* DC value of previous block */ - if (b) { /* If there is any difference from previous block */ - e = bitext(jd, b); /* Extract data bits */ - if (e < 0) return 0 - e; /* Err: input */ - b = 1 << (b - 1); /* MSB position */ - if (!(e & b)) e -= (b << 1) - 1; /* Restore sign if needed */ - d += e; /* Get current value */ - jd->dcv[cmp] = (SHORT)d; /* Save current DC value for next block */ - } - dqf = jd->qttbl[jd->qtid[cmp]]; /* De-quantizer table ID for this component */ - tmp[0] = d * dqf[0] >> 8; /* De-quantize, apply scale factor of Arai algorithm and descale 8 bits */ - - /* Extract following 63 AC elements from input stream */ - for (i = 1; i < 64; i++) tmp[i] = 0; /* Clear rest of elements */ - hb = jd->huffbits[id][1]; /* Huffman table for the AC elements */ - hc = jd->huffcode[id][1]; - hd = jd->huffdata[id][1]; - i = 1; /* Top of the AC elements */ - do { - b = huffext(jd, hb, hc, hd); /* Extract a huffman coded value (zero runs and bit length) */ - if (b == 0) break; /* EOB? */ - if (b < 0) return 0 - b; /* Err: invalid code or input error */ - z = (UINT)b >> 4; /* Number of leading zero elements */ - if (z) { - i += z; /* Skip zero elements */ - if (i >= 64) return JDR_FMT1; /* Too long zero run */ - } - if (b &= 0x0F) { /* Bit length */ - d = bitext(jd, b); /* Extract data bits */ - if (d < 0) return 0 - d; /* Err: input device */ - b = 1 << (b - 1); /* MSB position */ - if (!(d & b)) d -= (b << 1) - 1;/* Restore negative value if needed */ - z = ZIG(i); /* Zigzag-order to raster-order converted index */ - tmp[z] = d * dqf[z] >> 8; /* De-quantize, apply scale factor of Arai algorithm and descale 8 bits */ - } - } while (++i < 64); /* Next AC element */ - - if (JD_USE_SCALE && jd->scale == 3) - *bp = (*tmp / 256) + 128; /* If scale ratio is 1/8, IDCT can be ommited and only DC element is used */ - else - block_idct(tmp, bp); /* Apply IDCT and store the block to the MCU buffer */ - - bp += 64; /* Next block */ - } - - return JDR_OK; /* All blocks have been loaded successfully */ -} - - - - -/*-----------------------------------------------------------------------*/ -/* Output an MCU: Convert YCrCb to RGB and output it in RGB form */ -/*-----------------------------------------------------------------------*/ - -static -JRESULT mcu_output ( - JDEC* jd, /* Pointer to the decompressor object */ - UINT (*outfunc)(JDEC*, void*, JRECT*), /* RGB output function */ - UINT x, /* MCU position in the image (left of the MCU) */ - UINT y /* MCU position in the image (top of the MCU) */ -) -{ - const INT CVACC = (sizeof (INT) > 2) ? 1024 : 128; - UINT ix, iy, mx, my, rx, ry; - INT yy, cb, cr; - BYTE *py, *pc, *rgb24; - JRECT rect; - - - mx = jd->msx * 8; my = jd->msy * 8; /* MCU size (pixel) */ - rx = (x + mx <= jd->width) ? mx : jd->width - x; /* Output rectangular size (it may be clipped at right/bottom end) */ - ry = (y + my <= jd->height) ? my : jd->height - y; - if (JD_USE_SCALE) { - rx >>= jd->scale; ry >>= jd->scale; - if (!rx || !ry) return JDR_OK; /* Skip this MCU if all pixel is to be rounded off */ - x >>= jd->scale; y >>= jd->scale; - } - rect.left = x; rect.right = x + rx - 1; /* Rectangular area in the frame buffer */ - rect.top = y; rect.bottom = y + ry - 1; - - - if (!JD_USE_SCALE || jd->scale != 3) { /* Not for 1/8 scaling */ - - /* Build an RGB MCU from discrete comopnents */ - rgb24 = (BYTE*)jd->workbuf; - for (iy = 0; iy < my; iy++) { - pc = jd->mcubuf; - py = pc + iy * 8; - if (my == 16) { /* Double block height? */ - pc += 64 * 4 + (iy >> 1) * 8; - if (iy >= 8) py += 64; - } else { /* Single block height */ - pc += mx * 8 + iy * 8; - } - for (ix = 0; ix < mx; ix++) { - cb = pc[0] - 128; /* Get Cb/Cr component and restore right level */ - cr = pc[64] - 128; - if (mx == 16) { /* Double block width? */ - if (ix == 8) py += 64 - 8; /* Jump to next block if double block heigt */ - pc += ix & 1; /* Increase chroma pointer every two pixels */ - } else { /* Single block width */ - pc++; /* Increase chroma pointer every pixel */ - } - yy = *py++; /* Get Y component */ - - /* Convert YCbCr to RGB */ - *rgb24++ = /* R */ BYTECLIP(yy + ((INT)(1.402 * CVACC) * cr) / CVACC); - *rgb24++ = /* G */ BYTECLIP(yy - ((INT)(0.344 * CVACC) * cb + (INT)(0.714 * CVACC) * cr) / CVACC); - *rgb24++ = /* B */ BYTECLIP(yy + ((INT)(1.772 * CVACC) * cb) / CVACC); - } - } - - /* Descale the MCU rectangular if needed */ - if (JD_USE_SCALE && jd->scale) { - UINT x, y, r, g, b, s, w, a; - BYTE *op; - - /* Get averaged RGB value of each square correcponds to a pixel */ - s = jd->scale * 2; /* Bumber of shifts for averaging */ - w = 1 << jd->scale; /* Width of square */ - a = (mx - w) * 3; /* Bytes to skip for next line in the square */ - op = (BYTE*)jd->workbuf; - for (iy = 0; iy < my; iy += w) { - for (ix = 0; ix < mx; ix += w) { - rgb24 = (BYTE*)jd->workbuf + (iy * mx + ix) * 3; - r = g = b = 0; - for (y = 0; y < w; y++) { /* Accumulate RGB value in the square */ - for (x = 0; x < w; x++) { - r += *rgb24++; - g += *rgb24++; - b += *rgb24++; - } - rgb24 += a; - } /* Put the averaged RGB value as a pixel */ - *op++ = (BYTE)(r >> s); - *op++ = (BYTE)(g >> s); - *op++ = (BYTE)(b >> s); - } - } - } - - } else { /* For only 1/8 scaling (left-top pixel in each block are the DC value of the block) */ - - /* Build a 1/8 descaled RGB MCU from discrete comopnents */ - rgb24 = (BYTE*)jd->workbuf; - pc = jd->mcubuf + mx * my; - cb = pc[0] - 128; /* Get Cb/Cr component and restore right level */ - cr = pc[64] - 128; - for (iy = 0; iy < my; iy += 8) { - py = jd->mcubuf; - if (iy == 8) py += 64 * 2; - for (ix = 0; ix < mx; ix += 8) { - yy = *py; /* Get Y component */ - py += 64; - - /* Convert YCbCr to RGB */ - *rgb24++ = /* R */ BYTECLIP(yy + ((INT)(1.402 * CVACC) * cr / CVACC)); - *rgb24++ = /* G */ BYTECLIP(yy - ((INT)(0.344 * CVACC) * cb + (INT)(0.714 * CVACC) * cr) / CVACC); - *rgb24++ = /* B */ BYTECLIP(yy + ((INT)(1.772 * CVACC) * cb / CVACC)); - } - } - } - - /* Squeeze up pixel table if a part of MCU is to be truncated */ - mx >>= jd->scale; - if (rx < mx) { - BYTE *s, *d; - UINT x, y; - - s = d = (BYTE*)jd->workbuf; - for (y = 0; y < ry; y++) { - for (x = 0; x < rx; x++) { /* Copy effective pixels */ - *d++ = *s++; - *d++ = *s++; - *d++ = *s++; - } - s += (mx - rx) * 3; /* Skip truncated pixels */ - } - } - - /* Convert RGB888 to RGB565 if needed */ - if (JD_FORMAT == 1) { - BYTE *s = (BYTE*)jd->workbuf; - WORD w, *d = (WORD*)s; - UINT n = rx * ry; - - do { - w = (*s++ & 0xF8) << 8; /* RRRRR----------- */ - w |= (*s++ & 0xFC) << 3; /* -----GGGGGG----- */ - w |= *s++ >> 3; /* -----------BBBBB */ - *d++ = w; - } while (--n); - } - - /* Output the RGB rectangular */ - return outfunc(jd, jd->workbuf, &rect) ? JDR_OK : JDR_INTR; -} - - - - -/*-----------------------------------------------------------------------*/ -/* Process restart interval */ -/*-----------------------------------------------------------------------*/ - -static -JRESULT restart ( - JDEC* jd, /* Pointer to the decompressor object */ - WORD rstn /* Expected restert sequense number */ -) -{ - UINT i, dc; - WORD d; - BYTE *dp; - - - /* Discard padding bits and get two bytes from the input stream */ - dp = jd->dptr; dc = jd->dctr; - d = 0; - for (i = 0; i < 2; i++) { - if (!dc) { /* No input data is available, re-fill input buffer */ - dp = jd->inbuf; - dc = jd->infunc(jd, dp, JD_SZBUF); - if (!dc) return JDR_INP; - } else { - dp++; - } - dc--; - d = (d << 8) | *dp; /* Get a byte */ - } - jd->dptr = dp; jd->dctr = dc; jd->dmsk = 0; - - /* Check the marker */ - if ((d & 0xFFD8) != 0xFFD0 || (d & 7) != (rstn & 7)) - return JDR_FMT1; /* Err: expected RSTn marker is not detected (may be collapted data) */ - - /* Reset DC offset */ - jd->dcv[2] = jd->dcv[1] = jd->dcv[0] = 0; - - return JDR_OK; -} - - - - -/*-----------------------------------------------------------------------*/ -/* Analyze the JPEG image and Initialize decompressor object */ -/*-----------------------------------------------------------------------*/ - -#define LDB_WORD(ptr) (WORD)(((WORD)*((BYTE*)(ptr))<<8)|(WORD)*(BYTE*)((ptr)+1)) - - -JRESULT jd_prepare ( - JDEC* jd, /* Blank decompressor object */ - UINT (*infunc)(JDEC*, BYTE*, UINT), /* JPEG strem input function */ - void* pool, /* Working buffer for the decompression session */ - UINT sz_pool, /* Size of working buffer */ - void* dev /* I/O device identifier for the session */ -) -{ - BYTE *seg, b; - WORD marker; - DWORD ofs; - UINT n, i, j, len; - JRESULT rc; - - - if (!pool) return JDR_PAR; - - jd->pool = pool; /* Work memroy */ - jd->sz_pool = sz_pool; /* Size of given work memory */ - jd->infunc = infunc; /* Stream input function */ - jd->device = dev; /* I/O device identifier */ - jd->nrst = 0; /* No restart interval (default) */ - - for (i = 0; i < 2; i++) { /* Nulls pointers */ - for (j = 0; j < 2; j++) { - jd->huffbits[i][j] = 0; - jd->huffcode[i][j] = 0; - jd->huffdata[i][j] = 0; - } - } - for (i = 0; i < 4; i++) jd->qttbl[i] = 0; - - jd->inbuf = seg = alloc_pool(jd, JD_SZBUF); /* Allocate stream input buffer */ - if (!seg) return JDR_MEM1; - - if (jd->infunc(jd, seg, 2) != 2) return JDR_INP;/* Check SOI marker */ - if (LDB_WORD(seg) != 0xFFD8) return JDR_FMT1; /* Err: SOI is not detected */ - ofs = 2; - - for (;;) { - /* Get a JPEG marker */ - if (jd->infunc(jd, seg, 4) != 4) return JDR_INP; - marker = LDB_WORD(seg); /* Marker */ - len = LDB_WORD(seg + 2); /* Length field */ - if (len <= 2 || (marker >> 8) != 0xFF) return JDR_FMT1; - len -= 2; /* Content size excluding length field */ - ofs += 4 + len; /* Number of bytes loaded */ - - switch (marker & 0xFF) { - case 0xC0: /* SOF0 (baseline JPEG) */ - /* Load segment data */ - if (len > JD_SZBUF) return JDR_MEM2; - if (jd->infunc(jd, seg, len) != len) return JDR_INP; - - jd->width = LDB_WORD(seg+3); /* Image width in unit of pixel */ - jd->height = LDB_WORD(seg+1); /* Image height in unit of pixel */ - if (seg[5] != 3) return JDR_FMT3; /* Err: Supports only Y/Cb/Cr format */ - - /* Check three image components */ - for (i = 0; i < 3; i++) { - b = seg[7 + 3 * i]; /* Get sampling factor */ - if (!i) { /* Y component */ - if (b != 0x11 && b != 0x22 && b != 0x21)/* Check sampling factor */ - return JDR_FMT3; /* Err: Supports only 4:4:4, 4:2:0 or 4:2:2 */ - jd->msx = b >> 4; jd->msy = b & 15; /* Size of MCU [blocks] */ - } else { /* Cb/Cr component */ - if (b != 0x11) return JDR_FMT3; /* Err: Sampling factor of Cr/Cb must be 1 */ - } - b = seg[8 + 3 * i]; /* Get dequantizer table ID for this component */ - if (b > 3) return JDR_FMT3; /* Err: Invalid ID */ - jd->qtid[i] = b; - } - break; - - case 0xDD: /* DRI */ - /* Load segment data */ - if (len > JD_SZBUF) return JDR_MEM2; - if (jd->infunc(jd, seg, len) != len) return JDR_INP; - - /* Get restart interval (MCUs) */ - jd->nrst = LDB_WORD(seg); - break; - - case 0xC4: /* DHT */ - /* Load segment data */ - if (len > JD_SZBUF) return JDR_MEM2; - if (jd->infunc(jd, seg, len) != len) return JDR_INP; - - /* Create huffman tables */ - rc = create_huffman_tbl(jd, seg, len); - if (rc) return rc; - break; - - case 0xDB: /* DQT */ - /* Load segment data */ - if (len > JD_SZBUF) return JDR_MEM2; - if (jd->infunc(jd, seg, len) != len) return JDR_INP; - - /* Create de-quantizer tables */ - rc = create_qt_tbl(jd, seg, len); - if (rc) return rc; - break; - - case 0xDA: /* SOS */ - /* Load segment data */ - if (len > JD_SZBUF) return JDR_MEM2; - if (jd->infunc(jd, seg, len) != len) return JDR_INP; - - if (!jd->width || !jd->height) return JDR_FMT1; /* Err: Invalid image size */ - - if (seg[0] != 3) return JDR_FMT3; /* Err: Supports only three color components format */ - - /* Check if all tables corresponding to each components have been loaded */ - for (i = 0; i < 3; i++) { - b = seg[2 + 2 * i]; /* Get huffman table ID */ - if (b != 0x00 && b != 0x11) return JDR_FMT3; /* Err: Different table number for DC/AC element */ - b = i ? 1 : 0; - if (!jd->huffbits[b][0] || !jd->huffbits[b][1]) /* Check huffman table for this component */ - return JDR_FMT1; /* Err: Huffman table not loaded */ - if (!jd->qttbl[jd->qtid[i]]) return JDR_FMT1; /* Err: Dequantizer table not loaded */ - } - - /* Allocate working buffer for MCU and RGB */ - n = jd->msy * jd->msx; /* Number of Y blocks in the MCU */ - if (!n) return JDR_FMT1; /* Err: SOF0 has not been loaded */ - len = n * 64 * 2 + 64; /* Allocate buffer for IDCT and RGB output */ - if (len < 256) len = 256; /* but at least 256 byte is required for IDCT */ - jd->workbuf = alloc_pool(jd, len); /* and it may occupy a part of following MCU working buffer for RGB output */ - if (!jd->workbuf) return JDR_MEM1; /* Err: not enough memory */ - jd->mcubuf = alloc_pool(jd, (n + 2) * 64); /* Allocate MCU working buffer */ - if (!jd->mcubuf) return JDR_MEM1; /* Err: not enough memory */ - - /* Pre-load the JPEG data to extract it from the bit stream */ - jd->dptr = seg; jd->dctr = 0; jd->dmsk = 0; /* Prepare to read bit stream */ - if (ofs %= JD_SZBUF) { /* Align read offset to JD_SZBUF */ - jd->dctr = jd->infunc(jd, seg + ofs, JD_SZBUF - (UINT)ofs); - jd->dptr = seg + ofs - 1; - } - - return JDR_OK; /* Initialization succeeded. Ready to decompress the JPEG image. */ - - case 0xC1: /* SOF1 */ - case 0xC2: /* SOF2 */ - case 0xC3: /* SOF3 */ - case 0xC5: /* SOF5 */ - case 0xC6: /* SOF6 */ - case 0xC7: /* SOF7 */ - case 0xC9: /* SOF9 */ - case 0xCA: /* SOF10 */ - case 0xCB: /* SOF11 */ - case 0xCD: /* SOF13 */ - case 0xCE: /* SOF14 */ - case 0xCF: /* SOF15 */ - case 0xD9: /* EOI */ - return JDR_FMT3; /* Unsuppoted JPEG standard (may be progressive JPEG) */ - - default: /* Unknown segment (comment, exif or etc..) */ - /* Skip segment data */ - if (jd->infunc(jd, 0, len) != len) /* Null pointer specifies to skip bytes of stream */ - return JDR_INP; - } - } -} - - - - -/*-----------------------------------------------------------------------*/ -/* Start to decompress the JPEG picture */ -/*-----------------------------------------------------------------------*/ - -JRESULT jd_decomp ( - JDEC* jd, /* Initialized decompression object */ - UINT (*outfunc)(JDEC*, void*, JRECT*), /* RGB output function */ - BYTE scale /* Output de-scaling factor (0 to 3) */ -) -{ - UINT x, y, mx, my; - WORD rst, rsc; - JRESULT rc; - - - if (scale > (JD_USE_SCALE ? 3 : 0)) return JDR_PAR; - jd->scale = scale; - - mx = jd->msx * 8; my = jd->msy * 8; /* Size of the MCU (pixel) */ - - jd->dcv[2] = jd->dcv[1] = jd->dcv[0] = 0; /* Initialize DC values */ - rst = rsc = 0; - - rc = JDR_OK; - for (y = 0; y < jd->height; y += my) { /* Vertical loop of MCUs */ - for (x = 0; x < jd->width; x += mx) { /* Horizontal loop of MCUs */ - if (jd->nrst && rst++ == jd->nrst) { /* Process restart interval if enabled */ - rc = restart(jd, rsc++); - if (rc != JDR_OK) return rc; - rst = 1; - } - rc = mcu_load(jd); /* Load an MCU (decompress huffman coded stream and apply IDCT) */ - if (rc != JDR_OK) return rc; - rc = mcu_output(jd, outfunc, x, y); /* Output the MCU (color space conversion, scaling and output) */ - if (rc != JDR_OK) return rc; - } - } - - return rc; -} -#endif//SUPPORT_JPEG - - diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index a8c3d16b75..1e68ddcc1e 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,4 +1,5 @@ idf_component_register(SRC_DIRS . PRIV_INCLUDE_DIRS . - PRIV_REQUIRES test_utils esp32-camera nvs_flash + PRIV_REQUIRES test_utils esp32-camera nvs_flash mbedtls esp_timer EMBED_TXTFILES pictures/testimg.jpeg pictures/test_outside.jpeg pictures/test_inside.jpeg) +target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format") diff --git a/test/test_camera.c b/test/test_camera.c index 41a89b022f..c78dd1474b 100644 --- a/test/test_camera.c +++ b/test/test_camera.c @@ -7,6 +7,7 @@ #include #include "esp_log.h" #include "driver/i2c.h" +#include "esp_timer.h" #include "esp_camera.h" @@ -18,6 +19,8 @@ #define BOARD_CAMERA_MODEL_ESP32_S3_EYE 1 #endif +#define portTICK_RATE_MS portTICK_PERIOD_MS + // WROVER-KIT PIN Map #if BOARD_WROVER_KIT @@ -382,7 +385,7 @@ static void print_rgb888_img(uint8_t *img, int width, int height) static void tjpgd_decode_rgb565(uint8_t *mjpegbuffer, uint32_t size, uint8_t *outbuffer) { - jpg2rgb565(mjpegbuffer, size, outbuffer, JPG_SCALE_NONE); + jpg2rgb565(mjpegbuffer, size, outbuffer, JPEG_IMAGE_SCALE_0); } static void tjpgd_decode_rgb888(uint8_t *mjpegbuffer, uint32_t size, uint8_t *outbuffer)