C

Qt Quick Ultralite imagedecoder Example

// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial

#include "rh850_jpeg.h"
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include <string.h>
#include "r_jcua_api.h"
#include <platforminterface/printf.h>
#include <platforminterface/imagedecoder.h>
#include <platform/platform.h>

static volatile char jpegEventGroup = 0x0;

static unsigned char inputSplitBuffer[INPUT_SPLIT_BUFFER_SIZE] __attribute__((aligned(8)));

static const char *toString(int eventId)
{
    static const char *toStr[] = {"R_JCUA_CB_FACTOR_DECODE_COMPLETE",
                                  "R_JCUA_CB_FACTOR_DECODE_INPUT_PAUSED",
                                  "R_JCUA_CB_FACTOR_DECODE_OUTPUT_PAUSED",
                                  "R_JCUA_CB_FACTOR_DECODE_SIZEOVER",
                                  "R_JCUA_CB_FACTOR_DECODE_ERRORED",
                                  "R_JCUA_CB_FACTOR_FATAL_ERROR",
                                  "R_JCUA_CB_FACTOR_HEADER_TIMEOUT",
                                  "R_JCUA_CB_FACTOR_DECODE_TIMEOUT"};

    return toStr[eventId];
}

//This callback is called from interrupt context of JCUA interrupt
void jpegDecoderCallback(uint32_t unit, r_jcua_CallbackReason_t factor, uint32_t param)
{
    (void) unit;
    (void) param;

    switch (factor) {
    case R_JCUA_CB_FACTOR_DECODE_COMPLETE:
        SET_EVENT_BIT(jpegEventGroup, CALLBACK_FACTOR_DECODE_COMPLETE_EVENT_ID);
        break;
    case R_JCUA_CB_FACTOR_DECODE_INPUT_PAUSED:
        SET_EVENT_BIT(jpegEventGroup, CALLBACK_FACTOR_DECODE_INPUT_PAUSED_EVENT_ID);
        break;
    case R_JCUA_CB_FACTOR_DECODE_OUTPUT_PAUSED:
        SET_EVENT_BIT(jpegEventGroup, CALLBACK_FACTOR_DECODE_OUTPUT_PAUSED_EVENT_ID);
        break;
    case R_JCUA_CB_FACTOR_DECODE_SIZEOVER:
        SET_EVENT_BIT(jpegEventGroup, CALLBACK_FACTOR_DECODE_SIZEOVER_EVENT_ID);
        break;
    case R_JCUA_CB_FACTOR_DECODE_ERRORED:
        SET_EVENT_BIT(jpegEventGroup, CALLBACK_FACTOR_DECODE_ERRORED_EVENT_ID);
        break;
    case R_JCUA_CB_FACTOR_FATAL_ERROR:
        SET_EVENT_BIT(jpegEventGroup, CALLBACK_FACTOR_FATAL_ERROR_EVENT_ID);
        break;
    case R_JCUA_CB_FACTOR_HEADER_TIMEOUT:
        SET_EVENT_BIT(jpegEventGroup, CALLBACK_FACTOR_HEADER_TIMEOUT_EVENT_ID);
        break;
    case R_JCUA_CB_FACTOR_DECODE_TIMEOUT:
        SET_EVENT_BIT(jpegEventGroup, CALLBACK_FACTOR_DECODE_TIMEOUT_EVENT_ID);
        break;
    case R_JCUA_CB_FACTOR_NONE:
        break;
    default:
        DEBUG_PRINTF("Error: Invalid Callback factor\r\n");
        break;
    }
}

const uint16_t decodeBuffPixelLines()
{
    return DECODE_BUFFER_PIXEL_LINES;
}

r_jcua_Error_t jpegDecode_Init()
{
    r_jcua_Error_t ret = R_JCUA_ERR_OK;

    ret = R_JCUA_Init(JCUA_HW_UNIT);
    if (ret != R_JCUA_ERR_OK)
        return ret;

    ret = R_JCUA_DecoderOpen(JCUA_HW_UNIT, jpegDecoderCallback, 0);
    return ret;
}

r_jcua_Error_t jpegDecode_Start(Qul::PlatformInterface::ImageDecoder::RequestDataCallback &callback,
                                void *outputBufferAddr,
                                uint32_t outputBufferSize,
                                void *processBuffer,
                                uint32_t outputBufferStride,
                                r_jcua_ImageInfo_t jpegInfo)
{
    r_jcua_Error_t ret = R_JCUA_ERR_OK;
    r_jcua_FrameBuffer_t fb;
    r_jcua_DecodeSetting_t decodeOptions;

    bool useInputSplitMode = false;

    uint32_t inputBlockCount = 0;
    uint32_t outputBlockCount = 0;
    volatile uint32_t nDecodedImageBlock = 0;
    uint32_t inputSplitBufferOffset = 0;

    const unsigned char *inputBuffer = callback.rawData();
    if (!inputBuffer) {
        /* The images are not in the addressable memory.
         * Use input split mode and retrieve the image chunk by chunk.
         */
        useInputSplitMode = true;
    } else {
        if ((uint32_t) inputBuffer % 8 != 0) {
            DEBUG_PRINTF("Error: inputBuffer is not multiple of 8\r\n");
            return R_JCUA_ERR_NG;
        }
    }

    if ((uint32_t) outputBufferAddr % 8 != 0) {
        DEBUG_PRINTF("Error: outputBufferAddr is not multiple of 8\r\n");
        return R_JCUA_ERR_NG;
    }

    if (outputBufferSize % 8 != 0) {
        DEBUG_PRINTF("Error: outputBufferSize is not multiple of 8\r\n");
        return R_JCUA_ERR_NG;
    }

    if ((uint32_t) processBuffer % 8 != 0) {
        DEBUG_PRINTF("Error: Process framebuffer is not multiple of 8\r\n");
        return R_JCUA_ERR_NG;
    }

    fb.Address = processBuffer;
    fb.Size = (jpegInfo.DecodeWidth) * (jpegInfo.DecodeHeight) * 4;
    fb.Stride = jpegInfo.Stride;
    fb.Format = R_JCUA_OUTPUT_FORMAT_ARGB8888;
    fb.Swap = R_JCUA_SWAP_LONG;

    decodeOptions.ImgInfo = &jpegInfo;

    decodeOptions.OptionFlag = R_JCUA_DECODE_OPTION_DIVISION_MODE;

    if (useInputSplitMode) {
        decodeOptions.DivisionMode.InputBuffer.IsEnable = true;
        decodeOptions.DivisionMode.InputBuffer.IsInitAddress = true;
        decodeOptions.DivisionMode.InputBuffer.RestartAddress = (uint32_t *) inputSplitBuffer;
        decodeOptions.DivisionMode.InputBuffer.DataCount = INPUT_SPLIT_BUFFER_SIZE;
    } else {
        decodeOptions.DivisionMode.InputBuffer.IsEnable = false;
    }

    //Configure Output buffer for division mode
#ifdef USE_OUTPUT_SPLIT_MODE
    decodeOptions.DivisionMode.OutputBuffer.IsEnable = true;
    decodeOptions.DivisionMode.OutputBuffer.IsInitAddress = true;
    decodeOptions.DivisionMode.OutputBuffer.RestartAddress = (uint32_t *) processBuffer;
    decodeOptions.DivisionMode.OutputBuffer.DataCount = decodeBuffPixelLines();
#else
    decodeOptions.DivisionMode.OutputBuffer.IsEnable = false;
#endif

    jpegEventGroup = 0;
    if (useInputSplitMode) {
        inputSplitBufferOffset += callback.readData(inputSplitBuffer, inputSplitBufferOffset, INPUT_SPLIT_BUFFER_SIZE);
        ret = R_JCUA_DecoderStart(JCUA_HW_UNIT, inputSplitBuffer, &fb, &decodeOptions);
    } else {
        ret = R_JCUA_DecoderStart(JCUA_HW_UNIT, inputBuffer, &fb, &decodeOptions);
    }

    if (ret != R_JCUA_ERR_OK) {
        DEBUG_PRINTF("Decoder Error Code %d\r\n", ret);
        return ret;
    }

    while (1) {
        if (jpegEventGroup) {
            if (EVENT(jpegEventGroup, CALLBACK_FACTOR_DECODE_COMPLETE_EVENT_ID)) {
                int remainingLines = jpegInfo.Height - nDecodedImageBlock * decodeBuffPixelLines();
                //clear event bit
                CLEAR_EVENT_BIT(jpegEventGroup, CALLBACK_FACTOR_DECODE_COMPLETE_EVENT_ID);
#ifdef USE_OUTPUT_SPLIT_MODE
                DEBUG_PRINTF("RemainingLines:%d\r\n", remainingLines);
                if (remainingLines > 0) {
                    memcpy(static_cast<unsigned char *>(outputBufferAddr)
                               + nDecodedImageBlock * decodeBuffPixelLines() * outputBufferStride,
                           (uint8_t *) (processBuffer),
                           outputBufferStride * remainingLines);
                }
#else
                memcpy(outputBufferAddr, processBuffer, outputBufferSize);
#endif
                DEBUG_PRINTF("Decode_Complete_Callback\r\n");
                return R_JCUA_ERR_OK;
            }

            if (EVENT(jpegEventGroup, CALLBACK_FACTOR_DECODE_INPUT_PAUSED_EVENT_ID)) {
                if (useInputSplitMode)
                    inputSplitBufferOffset += callback.readData(inputSplitBuffer,
                                                                inputSplitBufferOffset,
                                                                INPUT_SPLIT_BUFFER_SIZE);

                //clear event bit
                CLEAR_EVENT_BIT(jpegEventGroup, CALLBACK_FACTOR_DECODE_INPUT_PAUSED_EVENT_ID);
                inputBlockCount++;
                DEBUG_PRINTF("Input Paused %d\r\n", inputBlockCount);

                R_JCUA_DecoderContinue(JCUA_HW_UNIT, R_JCUA_PAUSE_FACTOR_INPUT_PAUSED);
            }

            if (EVENT(jpegEventGroup, CALLBACK_FACTOR_DECODE_OUTPUT_PAUSED_EVENT_ID)) {
#ifdef USE_OUTPUT_SPLIT_MODE
                volatile size_t copyLength = outputBufferStride * decodeBuffPixelLines();

                // if image is encoded with YCrCb420, then for first decoded image block 0,
                // the decoded data length is twice than all other subsequent blocks.
                if (jpegInfo.EncodedFormat == R_JCUA_JPEG_FORMAT_YCBCR420)
                    copyLength *= (nDecodedImageBlock == 0 ? 2 : 1);

                memcpy(static_cast<unsigned char *>(outputBufferAddr)
                           + nDecodedImageBlock * decodeBuffPixelLines() * outputBufferStride,
                       (uint8_t *) (processBuffer),
                       copyLength);

                //Decoded length is twice for block number 0
                if (nDecodedImageBlock == 0 && jpegInfo.EncodedFormat == R_JCUA_JPEG_FORMAT_YCBCR420)
                    nDecodedImageBlock = nDecodedImageBlock + 2;
                else
                    nDecodedImageBlock = nDecodedImageBlock + 1;
#endif
                //clear event bit
                CLEAR_EVENT_BIT(jpegEventGroup, CALLBACK_FACTOR_DECODE_OUTPUT_PAUSED_EVENT_ID);
                outputBlockCount++;
                DEBUG_PRINTF("Output Paused %d\r\n", outputBlockCount);

                R_JCUA_DecoderContinue(JCUA_HW_UNIT, R_JCUA_PAUSE_FACTOR_OUTPUT_PAUSED);
            }

            if (EVENT(jpegEventGroup, CALLBACK_FACTOR_DECODE_SIZEOVER_EVENT_ID)) {
                DEBUG_PRINTF("Error: SizeOver Error with FrameBuffer size %d\r\n", fb.Size);
                return R_JCUA_ERR_NG;
            }

            if (EVENT(jpegEventGroup, CALLBACK_FACTOR_DECODE_ERRORED_EVENT_ID)) {
                DEBUG_PRINTF("Decoder Callback Error code: %s\r\n", toString(CALLBACK_FACTOR_DECODE_ERRORED_EVENT_ID));
                return R_JCUA_ERR_NG;
            }

            if (EVENT(jpegEventGroup, CALLBACK_FACTOR_FATAL_ERROR_EVENT_ID)) {
                DEBUG_PRINTF("Decoder Callback Error code: %s\r\n", toString(CALLBACK_FACTOR_FATAL_ERROR_EVENT_ID));
                return R_JCUA_ERR_NG;
            }

            if (EVENT(jpegEventGroup, CALLBACK_FACTOR_HEADER_TIMEOUT_EVENT_ID)) {
                DEBUG_PRINTF("Decoder Callback Error code: %s\r\n", toString(CALLBACK_FACTOR_HEADER_TIMEOUT_EVENT_ID));
                return R_JCUA_ERR_NG;
            }

            if (EVENT(jpegEventGroup, CALLBACK_FACTOR_DECODE_TIMEOUT_EVENT_ID)) {
                DEBUG_PRINTF("Decoder Callback Error code: %s\r\n", toString(CALLBACK_FACTOR_DECODE_TIMEOUT_EVENT_ID));
                return R_JCUA_ERR_NG;
            }
        } else {
            //enter sleep power mode
            __HALT();
        }
    }
}