QPainter how draw texture with special color
QPainter how draw texture with special color
I have some patterns which are black with alpha and have some points that I want to draw line with patterns.
I find QBrush can be constructed by texture, but I don't know how to draw it with difference colors.
This answer show a way here in C# code, but I don't know how to change patterns color with ColorMatrix.
QBrush
QBrush::setMatrix()
QPixmap
QBrush::setTexture()
In case that I missed something (I'm still a learner as well), you may ask the author of here to elaborate this color matrix idea. Even if he provides the sample in C#, you may notify me and I would be glad to help (out of curiosity). ;-)
– Scheff
Aug 21 at 7:06
@Scheff I have not enough reputation to add a comment in his answer...so I ask this question.
– Shun
Aug 21 at 7:15
Sorry, I didn't consider. Have it done by myself: Comment for Creating different brush patterns in c#
– Scheff
Aug 21 at 9:12
@Scheff thanks, i see his newest comment, and scrolled through the doc. of Qt graphics , it seems that have not such
ColorMatrix and ImageAttributes class in QT.– Shun
Aug 21 at 9:47
ColorMatrix
ImageAttributes
1 Answer
1
The modification of RGBA values of an image using a 5×5 color matrix reminds me to the transformation of homogeneous coordinates how it is often used in computer graphics. If you imagine the RGBA values as 4-dimensional color/alpha space the transformation of colors using transformation matrices doesn't sound that revolutionary. (Not that you got me wrong – this impressed me much, and I couldn't resist to try this out immediately.) Hence, I didn't wonder why a 5×5 matrix is needed though there are only 4 color components. (E.g. if a translation of color values is intended the 5th dimension cames into play.)
I must admit that I first applied my knowledge from Computer Animation to this problem and compared my approach to the one described on MSDN Using a Color Matrix to Transform a Single Color afterwards. Then I realized that the original paper uses transposed vectors and matrices compared to mine. This is just mathematics as
(vTMT)T = v' = M v
if I remember right.
Practically, it means I have to use matrix rows as columns when I try to reproduce the samples of e.g. the ColorMatrix Guide. (This feels somehow right to me as it is exactly as we describe transformations in 3d space i.e. translation is the last column of the transformation matrix.)

The sample code:
colorMatrix.h:
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:
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(" xc3x97 "));
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:
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:
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 (to consider moc generated sources):
moc_colorMatrix.cc
#include "moc_QColorMatrixView.cpp"
colorMatrix.pro (the qmake project file):
colorMatrix.pro
SOURCES = colorMatrix.cc QColorMatrixView.cc
HEADERS = colorMatrix.h QColorMatrixView.h
SOURCES += moc_colorMatrix.cc
MOC_DIR = .
QT += widgets
and the default sample image colorMatrixDefault.jpg if no (cat) photo file is at hand:
colorMatrixDefault.jpg

Although, I've developed and tested the application in VS2013, I built and tested also on cygwin to ensure that the qmake project is complete and self-standing:
$ qmake-qt5 colorMatrix.pro
$ make
$ ./colorMatrix
An enhanced version of this sample code can be found on github Qt Color Matrix Demo.

awesome work! thanks!
– Shun
Aug 22 at 1:57
@Shun I've extended the sample a bit and stored it on github. The link is added to the end of my answer (in case you're interested).
– Scheff
Aug 22 at 14:19
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.
I scrolled through the doc. of
QBrushbut there was nothing mentioned like a color matrix. There isQBrush::setMatrix()which is about local brush transformation (i.e. concerning coordinates but not colors). However, it's aQPixmapwhich is used inQBrush::setTexture(). So, you may make a copy and modify it according to your needs, can't you.– Scheff
Aug 21 at 7:04