C

Qt Quick Ultralite traveo_t2g_effects Example

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

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

#include <platforminterface/log.h>

#include <cstring>
#include <memory>

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

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

Qul::PlatformInterface::Rect GaussianBlurDelegate::boundingRect(Qul::PlatformInterface::Size) const
{
    int padding = int(ceil(blurValue()));

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

    auto texture = source.value().texture();

    return Qul::PlatformInterface::Rect(-padding,
                                        -padding,
                                        texture.width() + 2 * padding,
                                        texture.height() + 2 * padding);
}

#ifndef QUL_TVIIC_PLATFORM
QImage blurred(const QImage &source, int padding, float sigma)
{
    if (sigma <= 0)
        return source;

    QLabel label;
    label.setPixmap(QPixmap::fromImage(source));
    label.resize(source.size());
    QGraphicsBlurEffect *effect = new QGraphicsBlurEffect;
    // approximate the same blurriness as on TRAVEO™ T2G
    effect->setBlurRadius(sigma * 2 + 1);
    effect->setBlurHints(QGraphicsBlurEffect::QualityHint);
    label.setGraphicsEffect(effect);
    QImage image(source.width() + 2 * padding, source.height() + 2 * padding, QImage::Format_ARGB32_Premultiplied);
    image.fill(0x0);
    QWidget widget;
    widget.resize(image.size());
    label.setParent(&widget);
    label.move(padding, padding);
    widget.render(&image, QPoint(), QRegion(), QWidget::DrawChildren);
    label.setParent(nullptr);
    return image;
}
#endif

void GaussianBlurDelegate::blurChanged()
{
    m_blurDirty = true;
    update();
}

void GaussianBlurDelegate::paint(Qul::PlatformInterface::DrawingDevice *device,
                                 const Qul::PlatformInterface::Rect &clip,
                                 const Qul::PlatformInterface::Transform &transform,
                                 Qul::PlatformInterface::Size,
                                 float opacity) const
{
    float sigma = blurValue();

    // Values near 1.64f trigger a bug in the TRAVEO™ T2G Graphics Driver
    // (versions 1.2.1 and 2e.2.0) where the image doesn't get shown at all. As
    // a workaround, round the values to the second decimal place, and skip the
    // value that triggers the bug.

    int sigmaPercent = int(sigma * 100);
    if (sigmaPercent == 164)
        ++sigmaPercent;
    sigma = sigmaPercent * 0.01f;

    auto texture = source.value().texture();
    if (!m_initialized) {
#ifdef QUL_TVIIC_PLATFORM
        CyGfx::loadSurface(&m_source, texture);
#endif
        m_initialized = true;
    }

    int padding = int(ceil(sigma));
    const Qul::PlatformInterface::Transform adjusted = Qul::PlatformInterface::Transform::fromTranslation(-padding,
                                                                                                          -padding)
                                                       * transform;

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

    CYGFX_ERROR ret = CYGFX_OK;

    if (m_blurDirty && sigma > 0) {
        m_index = (m_index + 1) % 3;

        auto &surface = m_blurred[m_index];
        surface.Delete();
        surface.CreateBuffer(texture.width() + padding, texture.height() + padding);

        UTIL_SUCCESS(ret, CyGfx_BeBindSurface(drawCtx, CYGFX_BE_TARGET_STORE | CYGFX_BE_TARGET_DST, surface));

        UTIL_SUCCESS(ret, CyGfx_BeBindSurface(drawCtx, CYGFX_BE_TARGET_SRC, m_source));
        utFilterSetGaussFilter(drawCtx, CYGFX_CM_FILTER_CHANNEL_RGBA, sigma);

        UTIL_SUCCESS(ret, CyGfx_BeBlt(drawCtx, padding, padding));
        UTIL_SUCCESS(ret, CyGfx_BeFinish(drawCtx));
        utFilterFree(drawCtx);

        m_blurDirty = false;
    }

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

    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, sigma > 0 ? m_blurred[m_index] : m_source));
    if (!Qul::Private::qFuzzyCompare(opacity, 1.0f)) {
        UTIL_SUCCESS(ret,
                     CyGfx_BeSetSurfAttribute(ctx,
                                              CYGFX_BE_TARGET_SRC,
                                              CYGFX_BE_SURF_ATTR_COLOR,
                                              CYGFX_SM_COLOR_TO_RGBA(255, 255, 255, int(255 * opacity))));
        UTIL_SUCCESS(ret, CyGfx_BeSetSurfAttribute(ctx, CYGFX_BE_TARGET_SRC, CYGFX_BE_SURF_ATTR_ALPHAMULTI, CYGFX_TRUE));
    }

    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_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());

    QImage image(texture.data(),
                 texture.width(),
                 texture.height(),
                 texture.bytesPerLine(),
                 toQImageFormat(texture.format()));
    QImage blurredImage = blurred(image, padding, sigma);

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