C
Qt Quick Ultralite imagedecoder Example
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial
#include "stmimagedecoder.h"
#include "stm32_mcu_specific.h"
#include "buffer_config.h"
#include <platforminterface/imagedecoder.h>
#include <platforminterface/log.h>
extern "C" {
#include "jpeg_utils.h"
}
void JPEG_InputHandler(JPEG_HandleTypeDef *hjpeg, Qul::PlatformInterface::ImageDecoder::RequestDataCallback &callback);
uint32_t JPEG_OutputHandler(JPEG_HandleTypeDef *hjpeg);
bool jpegInformation(Qul::PlatformInterface::ImageDecoder::RequestDataCallback &callback,
int16_t *width,
int16_t *height,
Qul::PixelFormat *actualPixelFormat,
Qul::PixelFormat optimalOpaquePixelFormat,
Qul::PixelFormat optimalAlphaPixelFormat);
StmImageDecoder::StmImageDecoder() {}
StmImageDecoder::~StmImageDecoder() {}
extern JPEG_HandleTypeDef JPEG_Handle;
JPEG_YCbCrToRGB_Convert_Function pConvert_Function = NULL;
bool StmImageDecoder::imageInformation(RequestDataCallback &callback,
int16_t *width,
int16_t *height,
Qul::PixelFormat *actualPixelFormat,
Qul::PixelFormat optimalOpaquePixelFormat,
Qul::PixelFormat optimalAlphaPixelFormat)
{
return jpegInformation(callback, width, height, actualPixelFormat, optimalOpaquePixelFormat, optimalAlphaPixelFormat);
}
uint32_t MCU_TotalNb = 0;
uint32_t MCU_BlockIndex;
__IO uint32_t Jpeg_HWDecodingEnd = 0;
uint32_t JPEG_OUT_Read_BufferIndex = 0;
uint32_t JPEG_OUT_Write_BufferIndex = 0;
__IO uint32_t Output_Is_Paused = 0;
uint32_t JPEG_IN_Read_BufferIndex = 0;
uint32_t JPEG_IN_Write_BufferIndex = 0;
__IO uint32_t Input_Is_Paused = 0;
uint8_t *FrameBufferAddress;
uint32_t fileOffset = 0;
#define DECODING_TIMEOUT 9000
static void resetDecoderContext(void)
{
uint16_t i;
fileOffset = 0;
pConvert_Function = NULL;
MCU_TotalNb = 0;
MCU_BlockIndex = 0;
Jpeg_HWDecodingEnd = 0;
JPEG_OUT_Read_BufferIndex = 0;
JPEG_OUT_Write_BufferIndex = 0;
Output_Is_Paused = 0;
JPEG_IN_Read_BufferIndex = 0;
JPEG_IN_Write_BufferIndex = 0;
Input_Is_Paused = 0;
for (i = 0; i < NB_OUTPUT_DATA_BUFFERS; i++) {
Jpeg_OUT_BufferTab[i].State = JPEG_BUFFER_EMPTY;
Jpeg_OUT_BufferTab[i].DataBufferSize = 0;
}
}
int StmImageDecoder::decodeImage(RequestDataCallback &callback,
unsigned char *outbuffer,
uint32_t outbufferSize,
Qul::PixelFormat pixelFormat,
uint32_t requiredBytesPerLine)
{
QUL_UNUSED(outbufferSize);
QUL_UNUSED(pixelFormat);
QUL_UNUSED(requiredBytesPerLine);
/*##-6- JPEG decoding with DMA (Not Blocking ) Method ################*/
uint32_t i;
FrameBufferAddress = outbuffer;
resetDecoderContext();
uint32_t jpegDecodingStart = HAL_GetTick();
/* Read from JPG file and fill input buffers */
for (i = 0; i < NB_INPUT_DATA_BUFFERS && fileOffset < callback.totalAvailableDataSize(); i++) {
Jpeg_IN_BufferTab[i].DataBufferSize = callback.readData(Jpeg_IN_BufferTab[i].DataBuffer,
fileOffset,
CHUNK_SIZE_IN);
if (Jpeg_IN_BufferTab[i].DataBufferSize % 4 != 0)
Qul::PlatformInterface::log("size is not 4 bytes\n");
if (uint32_t(Jpeg_IN_BufferTab[i].DataBuffer) % 4 != 0)
Qul::PlatformInterface::log("buffer is not 4 bytes aligned\n");
if (Jpeg_IN_BufferTab[i].DataBufferSize > 0) {
Jpeg_IN_BufferTab[i].State = JPEG_BUFFER_FULL;
fileOffset += Jpeg_IN_BufferTab[i].DataBufferSize;
} else {
Qul::PlatformInterface::log("Read fail\n");
return -1;
}
}
/* Start JPEG decoding with DMA method */
HAL_StatusTypeDef status;
#ifdef USE_DMA_BASED_JPEG_DECODING
//Using DMA based jpeg decoding
status = HAL_JPEG_Decode_DMA(&JPEG_Handle,
Jpeg_IN_BufferTab[0].DataBuffer,
Jpeg_IN_BufferTab[0].DataBufferSize,
Jpeg_OUT_BufferTab[0].DataBuffer,
CHUNK_SIZE_OUT);
#else
//Using Interrupt based jpeg decoding
status = HAL_JPEG_Decode_IT(&JPEG_Handle,
Jpeg_IN_BufferTab[0].DataBuffer,
Jpeg_IN_BufferTab[0].DataBufferSize,
Jpeg_OUT_BufferTab[0].DataBuffer,
CHUNK_SIZE_OUT);
#endif
if (status != HAL_OK) {
Qul::PlatformInterface::log("HAL_JPEG_Decode_DMA failed\n");
return 1;
}
/*##-7- Wait till end of JPEG decoding and perfom Input/Output Processing in BackGround #*/
uint32_t JpegProcessing_End = 0;
do {
JPEG_InputHandler(&JPEG_Handle, callback);
JpegProcessing_End = JPEG_OutputHandler(&JPEG_Handle);
} while (JpegProcessing_End == 0 && ((HAL_GetTick() - jpegDecodingStart) < DECODING_TIMEOUT));
while (!Jpeg_HWDecodingEnd && ((HAL_GetTick() - jpegDecodingStart) < DECODING_TIMEOUT)) {
#if (PLATFORM_OS == baremetal)
//Sleep until HAL_JPEG_DecodeCpltCallback
__WFI();
#else
//FreeRTOS based platforms can wait on semaphore to yield to other tasks.
//Semaphore can be given up in HAL_JPEG_DecodeCpltCallback
#endif
}
if (!Jpeg_HWDecodingEnd && (HAL_GetTick() - jpegDecodingStart) >= DECODING_TIMEOUT) {
Qul::PlatformInterface::log("JPEG image decoding timed out\n");
return -1;
}
Jpeg_HWDecodingEnd = 0;
return 0;
}
/**
* @brief JPEG Ouput Data BackGround Postprocessing .
* @param hjpeg: JPEG handle pointer
* @retval 1 : if JPEG processing has finished, 0 : if JPEG processing still ongoing
*/
uint32_t JPEG_OutputHandler(JPEG_HandleTypeDef *hjpeg)
{
uint32_t ConvertedDataCount; // Looks like not being used by the STM32 implementation for decoding
if (Jpeg_OUT_BufferTab[JPEG_OUT_Read_BufferIndex].State == JPEG_BUFFER_FULL) {
MCU_BlockIndex += pConvert_Function(Jpeg_OUT_BufferTab[JPEG_OUT_Read_BufferIndex].DataBuffer,
FrameBufferAddress,
MCU_BlockIndex,
Jpeg_OUT_BufferTab[JPEG_OUT_Read_BufferIndex].DataBufferSize,
&ConvertedDataCount);
Jpeg_OUT_BufferTab[JPEG_OUT_Read_BufferIndex].State = JPEG_BUFFER_EMPTY;
Jpeg_OUT_BufferTab[JPEG_OUT_Read_BufferIndex].DataBufferSize = 0;
JPEG_OUT_Read_BufferIndex++;
if (JPEG_OUT_Read_BufferIndex >= NB_OUTPUT_DATA_BUFFERS) {
JPEG_OUT_Read_BufferIndex = 0;
}
if (MCU_BlockIndex == MCU_TotalNb) {
return 1;
}
} else if ((Output_Is_Paused == 1) && (JPEG_OUT_Write_BufferIndex == JPEG_OUT_Read_BufferIndex)
&& (Jpeg_OUT_BufferTab[JPEG_OUT_Read_BufferIndex].State == JPEG_BUFFER_EMPTY)) {
Output_Is_Paused = 0;
HAL_JPEG_Resume(hjpeg, JPEG_PAUSE_RESUME_OUTPUT);
}
return 0;
}
/**
* @brief JPEG Input Data BackGround processing .
* @param hjpeg: JPEG handle pointer
* @retval None
*/
void JPEG_InputHandler(JPEG_HandleTypeDef *hjpeg, Qul::PlatformInterface::ImageDecoder::RequestDataCallback &callback)
{
if (Jpeg_IN_BufferTab[JPEG_IN_Write_BufferIndex].State == JPEG_BUFFER_EMPTY) {
if (fileOffset == callback.totalAvailableDataSize())
return; // No more data to read
Jpeg_IN_BufferTab[JPEG_IN_Write_BufferIndex].DataBufferSize
= callback.readData(Jpeg_IN_BufferTab[JPEG_IN_Write_BufferIndex].DataBuffer, fileOffset, CHUNK_SIZE_IN);
if (Jpeg_IN_BufferTab[JPEG_IN_Write_BufferIndex].DataBufferSize > 0) {
fileOffset += Jpeg_IN_BufferTab[JPEG_IN_Write_BufferIndex].DataBufferSize;
Jpeg_IN_BufferTab[JPEG_IN_Write_BufferIndex].State = JPEG_BUFFER_FULL;
} else {
Qul::PlatformInterface::log("EOF reached, (or read failed)\n");
return;
}
if ((Input_Is_Paused == 1) && (JPEG_IN_Write_BufferIndex == JPEG_IN_Read_BufferIndex)) {
Input_Is_Paused = 0;
HAL_JPEG_ConfigInputBuffer(hjpeg,
Jpeg_IN_BufferTab[JPEG_IN_Read_BufferIndex].DataBuffer,
Jpeg_IN_BufferTab[JPEG_IN_Read_BufferIndex].DataBufferSize);
HAL_StatusTypeDef status = HAL_JPEG_Resume(hjpeg, JPEG_PAUSE_RESUME_INPUT);
if (status != HAL_OK)
Qul::PlatformInterface::log("HAL_JPEG_Resume failed\n");
}
JPEG_IN_Write_BufferIndex++;
if (JPEG_IN_Write_BufferIndex >= NB_INPUT_DATA_BUFFERS) {
JPEG_IN_Write_BufferIndex = 0;
}
}
}
extern "C" {
/**
* @brief JPEG Info ready callback
* @param hjpeg: JPEG handle pointer
* @param pInfo: JPEG Info Struct pointer
* @retval None
*/
void HAL_JPEG_InfoReadyCallback(JPEG_HandleTypeDef *hjpeg, JPEG_ConfTypeDef *pInfo)
{
QUL_UNUSED(hjpeg);
if (JPEG_GetDecodeColorConvertFunc(pInfo, &pConvert_Function, &MCU_TotalNb) != HAL_OK) {
return;
}
}
/**
* @brief JPEG Get Data callback
* @param hjpeg: JPEG handle pointer
* @param NbDecodedData: Number of decoded (consummed) bytes from input buffer
* @retval None
*/
void HAL_JPEG_GetDataCallback(JPEG_HandleTypeDef *hjpeg, uint32_t NbDecodedData)
{
if (NbDecodedData == Jpeg_IN_BufferTab[JPEG_IN_Read_BufferIndex].DataBufferSize) {
Jpeg_IN_BufferTab[JPEG_IN_Read_BufferIndex].State = JPEG_BUFFER_EMPTY;
Jpeg_IN_BufferTab[JPEG_IN_Read_BufferIndex].DataBufferSize = 0;
JPEG_IN_Read_BufferIndex++;
if (JPEG_IN_Read_BufferIndex >= NB_INPUT_DATA_BUFFERS) {
JPEG_IN_Read_BufferIndex = 0;
}
if (Jpeg_IN_BufferTab[JPEG_IN_Read_BufferIndex].State == JPEG_BUFFER_EMPTY) {
HAL_JPEG_Pause(hjpeg, JPEG_PAUSE_RESUME_INPUT);
Input_Is_Paused = 1;
} else {
HAL_JPEG_ConfigInputBuffer(hjpeg,
Jpeg_IN_BufferTab[JPEG_IN_Read_BufferIndex].DataBuffer,
Jpeg_IN_BufferTab[JPEG_IN_Read_BufferIndex].DataBufferSize);
}
} else {
HAL_JPEG_ConfigInputBuffer(hjpeg,
Jpeg_IN_BufferTab[JPEG_IN_Read_BufferIndex].DataBuffer + NbDecodedData,
Jpeg_IN_BufferTab[JPEG_IN_Read_BufferIndex].DataBufferSize - NbDecodedData);
}
}
/**
* @brief JPEG Data Ready callback
* @param hjpeg: JPEG handle pointer
* @param pDataOut: pointer to the output data buffer
* @param OutDataLength: length of output buffer in bytes
* @retval None
*/
void HAL_JPEG_DataReadyCallback(JPEG_HandleTypeDef *hjpeg, uint8_t *pDataOut, uint32_t OutDataLength)
{
QUL_UNUSED(pDataOut);
Jpeg_OUT_BufferTab[JPEG_OUT_Write_BufferIndex].State = JPEG_BUFFER_FULL;
Jpeg_OUT_BufferTab[JPEG_OUT_Write_BufferIndex].DataBufferSize = OutDataLength;
JPEG_OUT_Write_BufferIndex++;
if (JPEG_OUT_Write_BufferIndex >= NB_OUTPUT_DATA_BUFFERS) {
JPEG_OUT_Write_BufferIndex = 0;
}
if (Jpeg_OUT_BufferTab[JPEG_OUT_Write_BufferIndex].State != JPEG_BUFFER_EMPTY) {
HAL_JPEG_Pause(hjpeg, JPEG_PAUSE_RESUME_OUTPUT);
Output_Is_Paused = 1;
}
HAL_JPEG_ConfigOutputBuffer(hjpeg, Jpeg_OUT_BufferTab[JPEG_OUT_Write_BufferIndex].DataBuffer, CHUNK_SIZE_OUT);
}
void HAL_JPEG_ErrorCallback(JPEG_HandleTypeDef *hjpeg)
{
uint32_t error = HAL_JPEG_GetError(hjpeg);
Qul::PlatformInterface::log("HAL_JPEG_ErrorCallback: %x\n", error);
if (error & HAL_JPEG_ERROR_DMA)
Qul::PlatformInterface::log("HAL_JPEG_ERROR_DMA\n");
if (error & HAL_JPEG_ERROR_HUFF_TABLE)
Qul::PlatformInterface::log("HAL_JPEG_ERROR_HUFF_TABLE\n");
if (error & HAL_JPEG_ERROR_QUANT_TABLE)
Qul::PlatformInterface::log("HAL_JPEG_ERROR_QUANT_TABLE\n");
if (error & HAL_JPEG_ERROR_TIMEOUT)
Qul::PlatformInterface::log("HAL_JPEG_ERROR_TIMEOUT\n");
if (error & HAL_DMA_ERROR_TIMEOUT)
Qul::PlatformInterface::log("HAL_DMA_ERROR_TIMEOUT\n");
}
void HAL_JPEG_DecodeCpltCallback(JPEG_HandleTypeDef *hjpeg)
{
QUL_UNUSED(hjpeg);
Jpeg_HWDecodingEnd = 1;
}
}