QPainter如何绘制具有特殊颜色的纹理

时间:2018-08-21 01:45:57

标签: qt drawing textures

我有一些带有alpha字母的黑色图案,并且有一些要与图案画线的点。

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

此答案显示了C#代码中的here方法,但是我不知道如何使用ColorMatrix更改图案颜色。

1 个答案:

答案 0 :(得分:1)

使用5×5彩色矩阵修改图像的RGBA值使我想起homogeneous coordinates的转变,它在计算机图形学中的用法很常见。如果您将RGBA值想象为4维颜色/ alpha空间,则使用变换矩阵进行颜色变换听起来并不那么革命性。 (并不是您误会我的意思-这给我留下了深刻的印象,我无法抗拒立即尝试一下。)因此,我并不奇怪为什么只需要4种颜色分量就需要5×5的矩阵。 (例如,如果要转换颜色值,则第5维就起作用了。)

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

v T M T T = v ' = M v

如果我没记错的话。

实际上,这意味着当我尝试重现例如图2的样本时,必须使用矩阵行作为列。 ColorMatrix Guide。 (这对我来说是正确的,因为这正像我们描述3d空间中的变换一样,即翻译是变换矩阵的最后一列。)

Snapshot of colorMatrix program

示例代码:

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(如果没有猫照片文件的话):

Moritz the cat

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

$ qmake-qt5 colorMatrix.pro

$ make

$ ./colorMatrix

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

Snapshot of Enhanced Demo on github