C

Qt Quick Ultralite traveo_t2g_effects Example

// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial
#include "needletrail.h"

#include <qul/image.h>
#include <qul/private/texture.h>
#include <qul/private/rasterbuffer.h>

#include <cstring>
#include <memory>

#ifdef QUL_TVIIC_PLATFORM
#include <sm_util.h>
#include <pe_matrix.h>
#include "cygfx.h"
#else
#include <QImage>
#include <QPainter>
#include <QVector>
#include "desktop.h"
#endif

#include <qul/private/unicodestring.h>
#include <qul/private/image_p.h>

Qul::PlatformInterface::Rect NeedleTrailDelegate::boundingRect(Qul::PlatformInterface::Size size) const
{
    return Qul::PlatformInterface::Rect(0, 0, size.width(), size.height());
}

Qul::PlatformInterface::Transform computeRotationTransform(float cx, float cy, float w, float h, float angle, bool flip)
{
    Qul::PlatformInterface::Transform transform;
    transform.translate(0, -h * 0.5f);
    if (flip)
        transform.scale(-1, 1);
    transform.rotate(angle);
    transform.translate(w * 0.5f, h * 0.5f);
    transform.translate(cx, cy);
    return transform;
}

void NeedleTrailDelegate::rotationChanged()
{
    const float angle = rotation.value();
    if (m_initialized) {
        m_trailAngle += 0.02 * (angle - m_trailAngle);
    } else {
        m_trailAngle = angle;
    }
    update();
}

void NeedleTrailDelegate::paint(Qul::PlatformInterface::DrawingDevice *device,
                                const Qul::PlatformInterface::Rect &clip,
                                const Qul::PlatformInterface::Transform &offset,
                                Qul::PlatformInterface::Size size,
                                float opacity) const
{
    const float angle = rotation.value();

    if (!m_initialized) {
#ifdef QUL_TVIIC_PLATFORM
        if (mask.value().textureCount() != 1) {
            Qul::PlatformInterface::log("TVII effects: needle trail: Unexpected texture count %i\r\n",
                                        mask.value().textureCount());
            std::terminate();
        }

        auto texture = mask.value().texture();
        CyGfx::loadSurface(&m_trailMask, texture);
#endif
        m_trailAngle = angle;
        m_initialized = true;
    }

    float angleDelta = std::min(1.0f, 0.025f * std::abs(angle - m_trailAngle));
    float s = 1.0 - angleDelta;

    float steepness = 1.5; // HW supports no more than 3.99
    float maxAlpha = 0.8;

    float is = steepness * s;
    float ss = maxAlpha + (steepness - maxAlpha) * s;

    auto transform = computeRotationTransform(0, 0, size.width(), size.height(), angle, m_trailAngle < angle);

    transform = transform * offset;

#ifdef QUL_TVIIC_PLATFORM
    void **platformContext = static_cast<void **>(device->platformContext());
    CYGFX_BE_CONTEXT ctx = static_cast<CYGFX_BE_CONTEXT>(platformContext[0]);

    CYGFX_ERROR ret = CYGFX_OK;

    float colorMatrix[5][4] = {
        {0, 0, 0, 0},
        {0, 0, 0, 0},
        {0, 0, 0, 0},
        {0, 0.8, 0.8, ss * opacity},
        {0, 0, 0, -is * opacity},
    };

    Mat3x3 matrix{transform.m11(),
                  transform.m12(),
                  transform.m13(),
                  transform.m21(),
                  transform.m22(),
                  transform.m23(),
                  transform.dx(),
                  transform.dy(),
                  transform.m33()};

    UTIL_SUCCESS(ret, CyGfx_CmSetColorMatrix(ctx, CYGFX_CM_CMATRIX_FORMAT_5X4, &colorMatrix[0][0]));
    bool hasClipping = false;
    if (clip != Qul::PlatformInterface::Rect(0, 0, device->width(), device->height())) {
        // Blend only in clip area
        UTIL_SUCCESS(ret,
                     CyGfx_BeSetSurfAttribute(ctx, CYGFX_BE_TARGET_STORE, CYGFX_BE_SURF_ATTR_USE_CLIPPING, CYGFX_TRUE));
        UTIL_SUCCESS(ret,
                     CyGfx_BeActiveArea(ctx, CYGFX_BE_TARGET_STORE, clip.x(), clip.y(), clip.width(), clip.height()));
    }

    UTIL_SUCCESS(ret, CyGfx_BeBindSurface(ctx, CYGFX_BE_TARGET_SRC, m_trailMask));
    UTIL_SUCCESS(ret, CyGfx_BeSetGeoMatrix(ctx, CYGFX_BE_TARGET_SRC, CYGFX_BE_GEO_MATRIX_FORMAT_3X3, matrix));
    UTIL_SUCCESS(ret, CyGfx_BeBlt(ctx, 0, 0));
    UTIL_SUCCESS(ret, CyGfx_CmSetColorMatrix(ctx, CYGFX_CM_CMATRIX_FORMAT_4X3, nullptr));
    UTIL_SUCCESS(ret, CyGfx_BeBindSurface(ctx, CYGFX_BE_TARGET_MASK, nullptr));

    if (hasClipping)
        UTIL_SUCCESS(ret,
                     CyGfx_BeSetSurfAttribute(ctx, CYGFX_BE_TARGET_STORE, CYGFX_BE_SURF_ATTR_USE_CLIPPING, CYGFX_FALSE));
#else
    QPainter *p = static_cast<QPainter *>(device->platformContext());

    QVector<QRgb> colorTable(256);
    for (int i = 0; i < 256; ++i) {
        int gb = qRound(0.8 * i);
        int alpha = qBound(0, qRound(ss * i - is * 255), 255);
        colorTable[i] = qRgba(0, gb, gb, alpha);
    }

    if (mask.value().textureCount() != 1) {
        Qul::PlatformInterface::log("TVII effects: needle trail: Unexpected texture count %i\r\n",
                                    mask.value().textureCount());
        std::terminate();
    }

    auto texture = mask.value().texture();
    assert(texture.bitsPerPixel() == 8);
    QImage image(texture.data(), texture.width(), texture.height(), texture.bytesPerLine(), QImage::Format_Indexed8);
    image.setColorTable(colorTable);

    p->save();
    p->setClipRect(QRect(clip.x(), clip.y(), clip.width(), clip.height()));
    p->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
    p->setTransform(toQTransform(transform));
    p->setOpacity(opacity);
    p->drawImage(QPoint(), image);
    p->restore();
#endif
}