C

Monitor: Verifying the Rendering Output

// Copyright (C) 2025 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial
// This file is part of the Qt Safe Renderer module

#include "goldencrccalculator.h"
#include <QtSafeRenderer/qsafelayoutresourcereader.h>
#include <QtSafeRenderer/qsaferenderer.h>
#include <QtSafeRenderer/qsafelayout.h>
#include <QtSafeRenderer/qsafewindow.h>
#include <QtSafeRenderer/qsafesize.h>
#include <QtSafeRenderer/qsafepoint.h>
#include <QtSafeRenderer/qsafestring.h>
#include <QtSafeRenderer/qsafeexceptionhandler.h>
#include <QtSafeRenderer/qsafeexceptionhandlerinterface.h>
#include "offscreensurface.h"

#include <QtSafePlatformAdaptation/outputverifier.h>
#include <QtSafePlatformAdaptation/verifyregion.h>
#include <QtSafeOutputVerifierAdaptation/calcmisr.h>

#include <cstring>
#include <new>
#include <cstdio>
#include <cstdlib>

class GoldenCrcCalculatorImpl {
public:
    GoldenCrcCalculatorImpl(SafeRenderer::QSafeLayout *layoutPtr)
        : m_layout(layoutPtr)
        , m_surface(m_layout->size())
        , m_renderer(&m_surface, m_layout)
        , m_misrCalculator(*m_surface.framebuffer(), m_layout->size())
    {
        SafeRenderer::QSafeExceptionHandler::installExceptionHandler(&m_exceptionHandler);
    }

    qint32 calculateItemCRCById(quint32 itemId, quint32 stateId, quint32* crcOut)
    {
        qint32 returnValue = -1;
        if (crcOut != NULL) {
            /* Clear then render the item; custom exception handler will abort on errors */
            (void)m_renderer.clearBitmap(itemId, false);
            SafeRenderer::QSafeRect dirtyRect = m_renderer.drawBitmap(itemId);
            SafeRenderer::VerifyRegion verifyRegion(itemId, stateId, dirtyRect);
            m_misrCalculator.getCRC(verifyRegion);
            *crcOut = verifyRegion.crc();
            returnValue = 0;
        }

        return returnValue;
    }

private:
    SafeRenderer::QSafeLayout* m_layout;
    OffscreenSurface m_surface;
    SafeRenderer::QSafeRenderer m_renderer;
    SafeRenderer::MISRCalculator m_misrCalculator;

    class ExceptionHandler : public SafeRenderer::QSafeExceptionHandlerInterface {
    public:
        virtual bool onException(const SafeRenderer::QSafeException &exception) {
            std::fprintf(stderr, "GoldenCrcCalculator fatal error: %s\n", exception.what());
            std::abort();
        }
    };
    ExceptionHandler m_exceptionHandler;
};

// C API Implementation
extern "C" {

SafeLayoutHandle qsrCreateLayoutFromResource(const qchar* resourcePath)
{
    SafeLayoutHandle layoutHandle = NULL;
    if (resourcePath != NULL) {
        // Use QSafeRenderer allocator to allocate the memory from the memory pool.
        SafeRenderer::quint8 *implMem = SafeRenderer::allocAlignedFromMemoryPool(sizeof(SafeRenderer::QSafeLayoutResourceReader));
        SafeRenderer::QSafeLayoutResourceReader *layoutReader = new (implMem) SafeRenderer::QSafeLayoutResourceReader(resourcePath);
        layoutHandle = layoutReader;
    }
    return layoutHandle;
}

void qsrDestroyLayout(SafeLayoutHandle layoutHandle)
{
    if (layoutHandle != NULL) {
        SafeRenderer::QSafeLayoutResourceReader *layoutReader = static_cast<SafeRenderer::QSafeLayoutResourceReader *>(layoutHandle);
        layoutReader->~QSafeLayoutResourceReader();
        // The default allocator in QSR does not support dealloc, but this is
        // used here in case the allocator is customized.
        SafeRenderer::deallocFromMemoryPool(reinterpret_cast<SafeRenderer::quint8*>(layoutReader));
    }
}

qint32 qsrSetTextForItem(quint32 itemId, const qchar *text, SafeLayoutHandle layoutHandle)
{
    qint32 returnValue = -1;
    if (layoutHandle != NULL) {
        SafeRenderer::QSafeLayout *layoutPtr = static_cast<SafeRenderer::QSafeLayout *>(layoutHandle);
        layoutPtr->setTextForItem(itemId, text);
        returnValue = 0;
    }
    return returnValue;
}

GoldenCrcCalculatorHandle qsrGoldenCrcCalculatorCreate(SafeLayoutHandle layoutHandle)
{
    GoldenCrcCalculatorHandle crcCalc = NULL;
    if (layoutHandle != NULL) {
        // Use QSafeRenderer allocator to allocate the memory from the memory pool.
        SafeRenderer::QSafeLayout *layoutPtr = static_cast<SafeRenderer::QSafeLayout *>(layoutHandle);
        SafeRenderer::quint8* implMem = SafeRenderer::allocAlignedFromMemoryPool(sizeof(GoldenCrcCalculatorImpl));
        crcCalc = new (implMem) GoldenCrcCalculatorImpl(layoutPtr);
    }
    return crcCalc;
}

void qsrGoldenCrcCalculatorDestroy(GoldenCrcCalculatorHandle handle)
{
    if (handle) {
        GoldenCrcCalculatorImpl* impl = static_cast<GoldenCrcCalculatorImpl*>(handle);
        impl->~GoldenCrcCalculatorImpl();
        // The default allocator in QSR does not support dealloc, but this is
        // used here in case the allocator is customized.
        SafeRenderer::deallocFromMemoryPool(reinterpret_cast<SafeRenderer::quint8*>(impl));
    }
}

qint32 qsrCalculateItemCrc(GoldenCrcCalculatorHandle handle,
                                            const quint32 itemId,
                                            const quint32 stateId,
                                            quint32* crcOut) {
    qint32 returnValue = -1;
    if (handle != NULL) {
        GoldenCrcCalculatorImpl* impl = static_cast<GoldenCrcCalculatorImpl*>(handle);
        returnValue = impl->calculateItemCRCById(itemId, stateId, crcOut);
    }
    return returnValue;
}

} // extern "C"