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.





I scrolled through the doc. of QBrush but there was nothing mentioned like a color matrix. There is QBrush::setMatrix() which is about local brush transformation (i.e. concerning coordinates but not colors). However, it's a QPixmap which is used in QBrush::setTexture(). So, you may make a copy and modify it according to your needs, can't you.
– Scheff
Aug 21 at 7:04


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.)



Snapshot of colorMatrix program



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



Moritz the cat



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.



Snapshot of Enhanced Demo on github





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.

Popular posts from this blog

Help:Category

How can temperature be calculated given relative humidity and dew point?

I have a recursive function to validate tree graph and need a return condition