C

Qt Quick Ultralite imagedecoder Example

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

#include "rh850imagedecoder.h"
#include "rh850_jpeg.h"

#include <platforminterface/memoryallocator.h>
#include <platform/platform.h>

bool jpegInformation(Qul::PlatformInterface::ImageDecoder::RequestDataCallback &callback,
                     int16_t *width,
                     int16_t *height,
                     Qul::PixelFormat *actualPixelFormat,
                     Qul::PixelFormat optimalOpaquePixelFormat,
                     Qul::PixelFormat optimalAlphaPixelFormat);

void Rh850ImageDecoder::adjustForMCUSize(r_jcua_JpegFormat_t format, r_jcua_ImageInfo_t &jpegInfo)
{
    switch (format) {
    case R_JCUA_JPEG_FORMAT_YCBCR420:
        //MCU size: 16x16
        jpegInfo.DecodeWidth = (m_width % 16) ? 16 * ((m_width / 16) + 1) : m_width;
        jpegInfo.DecodeHeight = (m_height % 16) ? 16 * ((m_height / 16) + 1) : m_height;
        break;

    case R_JCUA_JPEG_FORMAT_YCBCR411:
        //MCU size: 8x32
        jpegInfo.DecodeWidth = (m_width % 8) ? 8 * ((m_width / 8) + 1) : m_width;
        jpegInfo.DecodeHeight = (m_height % 32) ? 32 * ((m_height / 32) + 1) : m_height;
        break;

    case R_JCUA_JPEG_FORMAT_YCBCR422:
        //MCU size: 8x16
        jpegInfo.DecodeWidth = (m_width % 8) ? 8 * ((m_width / 8) + 1) : m_width;
        jpegInfo.DecodeHeight = (m_height % 16) ? 16 * ((m_height / 16) + 1) : m_height;
        break;

    case R_JCUA_JPEG_FORMAT_YCBCR444:
        //MCU size: 8x8
        jpegInfo.DecodeWidth = (m_width % 8) ? 8 * ((m_width / 8) + 1) : m_width;
        jpegInfo.DecodeHeight = (m_height % 8) ? 8 * ((m_height / 8) + 1) : m_height;
        break;

    default:
        Qul::PlatformInterface::log("Decode Error: Invalid chroma-subsampling format\r\n");
        break;
    }
}

Rh850ImageDecoder::Rh850ImageDecoder() {}
Rh850ImageDecoder::~Rh850ImageDecoder() {}

bool Rh850ImageDecoder::imageInformation(RequestDataCallback &callback,
                                         int16_t *width,
                                         int16_t *height,
                                         Qul::PixelFormat *actualPixelFormat,
                                         Qul::PixelFormat optimalOpaquePixelFormat,
                                         Qul::PixelFormat optimalAlphaPixelFormat)
{
    bool ret = jpegInformation(callback,
                               width,
                               height,
                               actualPixelFormat,
                               optimalOpaquePixelFormat,
                               optimalAlphaPixelFormat);

    *actualPixelFormat = Qul::PixelFormat_ARGB32;
    m_width = *width;
    m_height = *height;

    return ret;
}

int Rh850ImageDecoder::decodeImage(RequestDataCallback &callback,
                                   unsigned char *outBuffer,
                                   uint32_t outBufferSize,
                                   Qul::PixelFormat pixelFormat,
                                   uint32_t requiredBytesPerLine)
{
    const uint8_t decodeBuffer_bpp = 4;

    r_jcua_ImageInfo_t jpegInfo;

    if (pixelFormat != Qul::PixelFormat_ARGB32) {
        Qul::PlatformInterface::log("Decode Error: PixelFormat not supported\r\n");
        return 1;
    }

    jpegInfo.Width = m_width;
    jpegInfo.Height = m_height;

    jpegInfo.EncodedFormat = CHROMA_SUBSAMPLING;
    //Adjust width and height for Minimum Coded Unit(MCU) size
    adjustForMCUSize(jpegInfo.EncodedFormat, jpegInfo);

    jpegInfo.StridePixel = jpegInfo.DecodeWidth;
    jpegInfo.Stride = jpegInfo.DecodeWidth * decodeBuffer_bpp;

#ifdef USE_OUTPUT_SPLIT_MODE
    /* if JPEG Decoder is configured for division mode, use a image block with pixel lines which is multiples of 16
     * for storing decoded output. JPEG decoder resues this image block to avoid allocating large process buffer.
     */
    uint32_t processBufferSize = jpegInfo.DecodeWidth * decodeBuffPixelLines() * decodeBuffer_bpp;

    /* For images with YCrCb420 subsampling, the buffer must be atleast TWICE of this block size,
     * hence the factor 2.
     */
    if (jpegInfo.EncodedFormat == R_JCUA_JPEG_FORMAT_YCBCR420)
        processBufferSize *= 2;

#else
    uint32_t processBufferSize = jpegInfo.DecodeWidth * jpegInfo.DecodeHeight * decodeBuffer_bpp;
#endif

    /* Allocate the process buffer from VRAM.
     * Renesas JCUA hardware does not allow decoder output framebuffer from internal RAM.
     */
    Qul::PlatformInterface::MemoryAllocator *allocator = Qul::Platform::getPlatformInstance()->memoryAllocator(
        Qul::PlatformInterface::MemoryAllocator::Custom);
    if (!allocator) {
        Qul::PlatformInterface::log("Decode Error: NULL Allocator\r\n");
        return 1;
    }

    /*Renesas JCUA driver requires 8 byte alignment for buffers*/
    const size_t alignment = 8;
    const size_t offset = alignment - 1;
    unsigned char *addr = static_cast<unsigned char *>(allocator->allocate(processBufferSize + offset));

    if (!addr) {
        Qul::PlatformInterface::log("Decode Error: allocation failed\r\n");
        return 1;
    }

    void *processBufferAligned = (void *) ((size_t) addr);
    size_t space = processBufferSize + offset;
    Qul::PlatformInterface::stdalign(alignment, processBufferSize, processBufferAligned, space);

#ifdef RH850_JPEG_DEBUG_LOGS
    Qul::PlatformInterface::log("ProcessWidth: %d, ProcessHeight %d\r\n", jpegInfo.DecodeWidth, jpegInfo.DecodeHeight);
    Qul::PlatformInterface::log("PixelFormat: %d, OutBufferSize %d, AllocatedBffer 0x%x, Req Bpl: %d, Size: %d\r\n",
                                pixelFormat,
                                outBufferSize,
                                processBufferAligned,
                                requiredBytesPerLine,
                                processBufferSize);
#endif

    r_jcua_Error_t ret
        = jpegDecode_Start(callback, outBuffer, outBufferSize, processBufferAligned, requiredBytesPerLine, jpegInfo);

    allocator->free(addr);

    if (ret != R_JCUA_ERR_OK) {
        r_jcua_ErrorDetail_t errorCode;
        R_JCUA_ErrorInfoGet(JCUA_HW_UNIT, &errorCode);
        Qul::PlatformInterface::log("Decoder failed with error Code 0x%X\r\n", errorCode);
        return 1;
    }

    return 0;
}