【问题标题】:QPainter how draw texture with special colorQPainter如何用特殊颜色绘制纹理
【发布时间】:2018-08-22 22:03:06
【问题描述】:

我有一些带有 alpha 的黑色图案,并且有一些我想用图案画线的点。

我发现QBrush可以通过纹理来构建,但我不知道如何用不同的颜色绘制它。

这个答案在 C# 代码中显示了 here 的一种方式,但我不知道如何使用 ColorMatrix 更改图案颜色。

【问题讨论】:

  • 我浏览了文档。 QBrush 的,但没有提到像颜色矩阵这样的东西。 QBrush::setMatrix() 是关于局部画笔变换(即关于坐标而不是颜色)。但是,它是QPixmap,在QBrush::setTexture() 中使用。所以,你可以根据需要复制并修改它,不是吗。
  • 如果我遗漏了什么(我也是一个学习者),你可以请here的作者详细说明这个颜色矩阵的想法。即使他提供了 C# 中的示例,您也可以通知我,我很乐意提供帮助(出于好奇)。 ;-)
  • @Scheff 我没有足够的声誉在他的回答中添加评论......所以我问了这个问题。
  • 对不起,我没有考虑。自己做:Comment for Creating different brush patterns in c#
  • @Scheff 谢谢,我看到了他的最新评论,并浏览了文档。 Qt 图形中,QT 中似乎没有这样的ColorMatrixImageAttributes 类。

标签: qt drawing textures


【解决方案1】:

使用 5×5 颜色矩阵对图像的 RGBA 值进行修改让我想起了homogeneous coordinates 在计算机图形学中的常用转换。如果您将 RGBA 值想象为 4 维颜色/阿尔法空间,那么使用转换矩阵进行颜色转换听起来并不那么具有革命性。 (并不是说你误会了——这给我留下了深刻的印象,我迫不及待地想立即尝试一下。)因此,我不奇怪为什么需要一个 5×5 矩阵,尽管只有 4 个颜色分量。 (例如,如果要转换颜色值,则第 5 维会起作用。)

我必须承认,我首先将我从计算机动画中获得的知识应用于这个问题,然后将我的方法与 MSDN Using a Color Matrix to Transform a Single Color 上描述的方法进行了比较。然后我意识到与我的相比,原始论文使用转置向量和矩阵。这只是数学

(vTMT)T = v ' = M v

如果我没记错的话。

实际上,这意味着当我尝试复制示例时,我必须使用矩阵行作为列。 ColorMatrix Guide。 (这对我来说感觉有点对,因为它与我们描述 3d 空间中的变换完全一样,即平移是变换矩阵的最后一列。)

示例代码:

colorMatrix.h:

#ifndef COLOR_MATRIX_H
#define COLOR_MATRIX_H

#include <algorithm>

struct ColorMatrix {
  float values[5][5];
  ColorMatrix() { }
  ColorMatrix(const float(&values)[25])
  {
    std::copy(std::begin(values), std::end(values), (float*)this->values);
  }
  float (&operator[](unsigned i))[5] { return values[i]; }
  const float(&operator[](unsigned i) const)[5] { return values[i]; }
};

struct ColorVector {
  float values[5];
  ColorVector(const float(&values)[5])
  {
    std::copy(std::begin(values), std::end(values), (float*)this->values);
  }
  float& operator[](size_t i) { return values[i]; }
  const float& operator[](size_t i) const { return values[i]; }
};

#endif // COLOR_MATRIX_H

colorMatrix.cc:

#include <algorithm>

#include <QtWidgets>

#include "colorMatrix.h"
#include "QColorMatrixView.h"

ColorVector operator*(const ColorMatrix &m, const ColorVector &v)
{
  return ColorVector({
    m[0][0] * v[0] + m[0][1] * v[1] + m[0][2] * v[2] + m[0][3] * v[3] + m[0][4] * v[4],
    m[1][0] * v[0] + m[1][1] * v[1] + m[1][2] * v[2] + m[1][3] * v[3] + m[1][4] * v[4],
    m[2][0] * v[0] + m[2][1] * v[1] + m[2][2] * v[2] + m[2][3] * v[3] + m[2][4] * v[4],
    m[3][0] * v[0] + m[3][1] * v[1] + m[3][2] * v[2] + m[3][3] * v[3] + m[3][4] * v[4],
    m[4][0] * v[0] + m[4][1] * v[1] + m[4][2] * v[2] + m[4][3] * v[3] + m[4][4] * v[4]
  });
}

const ColorMatrix Identity({
  1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
  0.0f, 1.0f, 0.0f, 0.0f, 0.0f,
  0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
  0.0f, 0.0f, 0.0f, 1.0f, 0.0f,
  0.0f, 0.0f, 0.0f, 0.0f, 1.0f
});

template <typename T>
T clamp(T value, T min, T max)
{
  return value < min ? min
    : value > max ? max
    : value;
}

QRgb transform(const ColorMatrix &mat, const QRgb &color)
{
  ColorVector vec({
    qRed(color) / 255.0f, qGreen(color) / 255.0f, qBlue(color) / 255.0f, qAlpha(color) / 255.0f, 1.0f });
  vec = mat * vec;
  if (vec[4] != 0.0f) {
    vec[0] /= vec[4]; vec[1] /= vec[4]; vec[2] /= vec[4]; vec[3] /= vec[4]; // vec[4] = 1.0f;
  }
  return qRgba(
    clamp<int>(255 * vec[0], 0, 255),
    clamp<int>(255 * vec[1], 0, 255),
    clamp<int>(255 * vec[2], 0, 255),
    clamp<int>(255 * vec[3], 0, 255));
}

QImage transform(const ColorMatrix &mat, const QImage &qImg)
{
  const int w = qImg.width(), h = qImg.height();
  QImage qImgDst(w, h, qImg.format());
  for (int y = 0; y < h; ++y) for (int x = 0; x < w; ++x) {
    qImgDst.setPixel(x, y, transform(mat, qImg.pixel(x, y)));
  }
  return qImgDst;
}

QImage open(QWidget *pQParent)
{
  return QImage(
    QFileDialog::getOpenFileName(pQParent,
      QString::fromUtf8("Open Image File"),
      QString()));
}

void update(
  QLabel &qLblViewResult,
  const QColorMatrixView &qEditColMat, const QLabel &qLblViewOrig)
{
  ColorMatrix colMat = qEditColMat.values();
  const QPixmap *pQPixmap = qLblViewOrig.pixmap();
  const QImage qImg = pQPixmap ? pQPixmap->toImage() : QImage();
  qLblViewResult.setPixmap(
    QPixmap::fromImage(transform(colMat, qImg)));
}

int main(int argc, char **argv)
{
  QApplication app(argc, argv);
  // setup GUI
  QWidget qWin;
  qWin.setWindowTitle(QString::fromUtf8("Qt Color Matrix Demo"));
  QGridLayout qGrid;
  QVBoxLayout qVBoxColMat;
  QLabel qLblColMat(QString::fromUtf8("Color Matrix:"));
  qVBoxColMat.addWidget(&qLblColMat, 0);
  QColorMatrixView qEditColMat;
  qEditColMat.setValues(Identity);
  qVBoxColMat.addWidget(&qEditColMat);
  QPushButton qBtnReset(QString::fromUtf8("Identity"));
  qVBoxColMat.addWidget(&qBtnReset);
  QPushButton qBtnGray(QString::fromUtf8("Grayscale"));
  qVBoxColMat.addWidget(&qBtnGray);
  qVBoxColMat.addStretch(1);
  qGrid.addLayout(&qVBoxColMat, 0, 0, 2, 1);
  QLabel qLblX(QString::fromUtf8(" \xc3\x97 "));
  qGrid.addWidget(&qLblX, 0, 1);
  QLabel qLblViewOrig;
  qGrid.addWidget(&qLblViewOrig, 0, 2);
  QPushButton qBtnLoad(QString::fromUtf8("Open..."));
  qGrid.addWidget(&qBtnLoad, 1, 2);
  QLabel qLblEq(QString::fromUtf8(" = "));
  qGrid.addWidget(&qLblEq, 0, 3);
  QLabel qLblViewResult;
  qGrid.addWidget(&qLblViewResult, 0, 4);
  qWin.setLayout(&qGrid);
  qWin.show();
  // install signal handlers
  QObject::connect(&qEditColMat, &QColorMatrixView::editingFinished,
    [&]() { update(qLblViewResult, qEditColMat, qLblViewOrig); });
  QObject::connect(&qBtnReset, &QPushButton::clicked,
    [&]() {
      qEditColMat.setValues(Identity);
      update(qLblViewResult, qEditColMat, qLblViewOrig);
    });
  QObject::connect(&qBtnGray, &QPushButton::clicked,
    [&]() {
      qEditColMat.setValues(ColorMatrix({
        0.33f, 0.59f, 0.11f, 0.0f, 0.0f,
        0.33f, 0.59f, 0.11f, 0.0f, 0.0f,
        0.33f, 0.59f, 0.11f, 0.0f, 0.0f,
        0.00f, 0.00f, 0.00f, 1.0f, 0.0f,
        0.00f, 0.00f, 0.00f, 0.0f, 1.0f
      }));
      update(qLblViewResult, qEditColMat, qLblViewOrig);
    });
  QObject::connect(&qBtnLoad, &QPushButton::clicked,
    [&]() {
      qLblViewOrig.setPixmap(QPixmap::fromImage(open(&qBtnLoad)));
      update(qLblViewResult, qEditColMat, qLblViewOrig);
    });
  // initial contents
  {
    QImage qImg("colorMatrixDefault.jpg");
    qLblViewOrig.setPixmap(QPixmap::fromImage(qImg));
    update(qLblViewResult, qEditColMat, qLblViewOrig);
  }
  // runtime loop
  return app.exec();
}

QColorMatrixView.h:

#ifndef Q_COLOR_MATRIX_VIEW_H
#define Q_COLOR_MATRIX_VIEW_H

#include <QLineEdit>
#include <QGridLayout>
#include <QWidget>

#include "colorMatrix.h"

class QColorMatrixView: public QWidget {
  Q_OBJECT
  private:
    QGridLayout _qGrid;
    QLineEdit _qEdit[5][5];
  signals:
    void editingFinished();
  public:
    QColorMatrixView(QWidget *pQParent = nullptr);
    virtual ~QColorMatrixView() = default;
    QColorMatrixView(const QColorMatrixView&) = delete;
    QColorMatrixView& operator=(const QColorMatrixView&) = delete;
    ColorMatrix values() const;
    void setValues(const ColorMatrix &mat);
};

#endif // Q_COLOR_MATRIX_VIEW_H

QColorMatrixView.cc:

#include "QColorMatrixView.h"

QColorMatrixView::QColorMatrixView(QWidget *pQParent):
  QWidget(pQParent)
{
  QFontMetrics qFontMetrics(font());
  const int w = qFontMetrics.boundingRect(QString("-000.000")).width() + 10;
  for (int r = 0; r < 5; ++r) {
    for (int c = 0; c < 5; ++c) {
      QLineEdit &qEdit = _qEdit[r][c];
      _qGrid.addWidget(&qEdit, r, c);
      qEdit.setFixedWidth(w);
      QObject::connect(&qEdit, &QLineEdit::editingFinished,
        [this, r, c]() {
        _qEdit[r][c].setText(
          QString::number(_qEdit[r][c].text().toFloat(), 'f', 3));
        editingFinished();
      });
    }
  }
  setLayout(&_qGrid);
}

ColorMatrix QColorMatrixView::values() const
{
  ColorMatrix mat;
  for (int r = 0; r < 5; ++r) for (int c = 0; c < 5; ++c) {
    mat[r][c] = _qEdit[r][c].text().toFloat();
  }
  return mat;
}

void QColorMatrixView::setValues(const ColorMatrix &mat)
{
  for (int r = 0; r < 5; ++r) for (int c = 0; c < 5; ++c) {
    _qEdit[r][c].setText(QString::number(mat[r][c], 'f', 3));
  }
}

moc_colorMatrix.cc(考虑 moc 生成的源):

#include "moc_QColorMatrixView.cpp"

colorMatrix.pro(qmake 项目文件):

SOURCES = colorMatrix.cc QColorMatrixView.cc
HEADERS = colorMatrix.h QColorMatrixView.h
SOURCES += moc_colorMatrix.cc
MOC_DIR = .

QT += widgets

如果手头没有(猫)照片文件,则默认示例图像colorMatrixDefault.jpg

虽然我已经在 VS2013 中开发和测试了应用程序,但我也在 cygwin 上构建和测试了以确保 qmake 项目是完整和独立的:

$ qmake-qt5 colorMatrix.pro

$ make

$ ./colorMatrix

此示例代码的增强版本可以在 github Qt Color Matrix Demo 上找到。

【讨论】:

  • @Shun 我稍微扩展了示例并将其存储在 github 上。该链接已添加到我的答案末尾(如果您有兴趣)。
猜你喜欢
  • 1970-01-01
  • 2018-05-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-09-10
  • 2012-07-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多