You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在paint事件中绘制无锯齿渐变色轮?解决旋转绘制的不规则问题

解决方案:替代rotate()的色轮实现

为什么不推荐使用rotate()?

  • 精度问题:多次调用rotate()会累积浮点计算误差,导致色轮边缘的渐变出现错位、不规则;
  • 性能问题:原代码中3590次色相循环+255次饱和度循环的嵌套迭代,计算量巨大,动态修改时会明显卡顿。

最优方案1:逐像素计算HSV值(精度最高)

直接计算每个像素的色相(对应角度)、饱和度(对应半径),生成QImage后转QPixmap,无旋转误差且计算效率更高。

修改后的.cpp核心代码:

#include "colorwheel.h"
#include <QPainter>
#include <cmath>

ColorWheel::ColorWheel(QWidget *parent)
    : QWidget(parent)
{
    setFixedSize(400,400);
    m_wheelRect = rect().marginsRemoved(QMargins(18,18,18,18));
    m_wheel = wheel(width(),height());
}

ColorWheel::~ColorWheel()
{
}

void ColorWheel::paintEvent(QPaintEvent *)
{
    QPainter painter(this);
    // 开启抗锯齿和平滑像素变换,提升绘制效果
    painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
    painter.setPen(QPen(Qt::black, 3));
    painter.drawEllipse(m_wheelRect);
    painter.drawPixmap(m_wheelRect, m_wheel);
}

QPixmap ColorWheel::wheel(int width, int height)
{
    QImage image(width, height, QImage::Format_ARGB32_Premultiplied);
    image.fill(Qt::transparent);

    const int centerX = width / 2;
    const int centerY = height / 2;
    const qreal maxRadius = qMin(centerX, centerY);

    // 遍历所有像素计算HSV值
    for (int y = 0; y < height; ++y) {
        for (int x = 0; x < width; ++x) {
            qreal dx = x - centerX;
            qreal dy = y - centerY;
            qreal radius = qSqrt(dx*dx + dy*dy);

            // 超出色轮范围的像素设为透明
            if (radius > maxRadius) {
                image.setPixelColor(x, y, Qt::transparent);
                continue;
            }

            // 计算角度(转换为0-360度,顺时针从右侧开始)
            qreal angle = qAtan2(dy, dx) * 180 / M_PI;
            if (angle < 0) angle += 360;

            // 饱和度:半径从0到maxRadius对应0到255
            int saturation = qRound((radius / maxRadius) * 255);
            // 色相:角度对应0-359
            int hue = qRound(angle);
            // 明度固定为255
            QColor clr = QColor::fromHsv(hue, saturation, 255);
            image.setPixelColor(x, y, clr);
        }
    }

    return QPixmap::fromImage(image);
}

最优方案2:使用Qt渐变组合(性能最优)

利用QConicalGradient控制色相的角度变化,QRadialGradient控制饱和度的径向变化,结合绘图组合模式实现,借助Qt硬件加速,性能远超手动迭代。

修改后的wheel函数:

QPixmap ColorWheel::wheel(int width, int height)
{
    QPixmap wheel(width, height);
    wheel.fill(Qt::transparent);

    const int centerX = width / 2;
    const int centerY = height / 2;
    const qreal maxRadius = qMin(centerX, centerY);

    QPainter painter(&wheel);
    painter.setRenderHint(QPainter::Antialiasing);

    // 锥形渐变:实现色相随角度变化
    QConicalGradient hueGradient(centerX, centerY, 0);
    for (int hue = 0; hue < 360; hue += 5) {
        hueGradient.setColorAt(hue / 360.0, QColor::fromHsv(hue, 255, 255));
    }
    hueGradient.setColorAt(1.0, QColor::fromHsv(0, 255, 255)); // 闭合渐变避免首尾色差

    // 径向渐变:实现饱和度从中心0到边缘255
    QRadialGradient saturationGradient(centerX, centerY, maxRadius);
    saturationGradient.setColorAt(0, QColor(0, 0, 0, 0));    // 中心饱和度0(透明)
    saturationGradient.setColorAt(1, QColor(0, 0, 0, 255));  // 边缘饱和度255(不透明)

    // 先绘制色相渐变的色轮
    painter.setBrush(hueGradient);
    painter.drawEllipse(QRectF(centerX - maxRadius, centerY - maxRadius, maxRadius*2, maxRadius*2));

    // 叠加饱和度渐变,用Multiply模式实现饱和度从中心到边缘的过渡
    painter.setCompositionMode(QPainter::CompositionMode_Multiply);
    painter.setBrush(saturationGradient);
    painter.drawEllipse(QRectF(centerX - maxRadius, centerY - maxRadius, maxRadius*2, maxRadius*2));

    return wheel;
}

效果说明

两种方案都能解决原代码的渐变不规则问题:

  • 方案1精度最高,完全匹配HSV色轮的数学定义;
  • 方案2性能更优,适合需要频繁动态修改的场景。

内容的提问来源于stack exchange,提问作者BanAnn

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 18:42:03