Qt drawing icons using color and alpha-map
Asked Answered
H

4

13

I would like to draw icons (only one color) in different colors. To do so, I would like to import a single alpha-texture and then combine this with a given color in the application.

The result should be, that nothing is drawn on to the background, when the alpha-map has an opacity of 0 and the used color should be drawn, when the opacity is 1.

One soulution should be hidden somewhere in QPainter, since you can manually set the Composition-Mode (QPainter::setCompositionMode). But I don't get it to work like I want.

Does somebody have an idea?

Thanks in advance.

EDIT: Here is a little graphic explaining what I would like to do. I want to use a Alpha-Map like shown in the graphic and then use a color layer to create my icon. Important is, that the background stays transparent.

enter image description here

Hamulus answered 24/7, 2014 at 20:50 Comment(1)
"I don't get it to work like I want." If you claim that you have code that doesn't work, you should post the code - in fact, you should post a self contained example that lets us compile it.Unintelligent
M
14

You can do thos using QPainter like this:

QColor color;
// set color value

// load gray-scale image (an alpha map)
QPixmap pixmap = QPixmap(":/images/earth.png");

// initialize painter to draw on a pixmap and set composition mode
QPainter painter(&pixmap);
painter.setCompositionMode(QPainter::CompositionMode_SourceIn);

painter.setBrush(color);
painter.setPen(color);

painter.drawRect(pixmap.rect());

// Here is our new colored icon!
QIcon icon = QIcon(pixmap);

Here is gray-scale image and two colored icons which i get using the code above (QPixmap.save() method): icons

Mckoy answered 13/5, 2016 at 14:57 Comment(1)
For some reason I had to replace the set brush, pen and drawRect lines with painter.fillRect(pixmap.rect(), color) (in python). Less code and works, so that is nice. I also have painter.end() but not sure if that is needed.Quartering
T
2

The DestinationIn composition mode will do the trick.

  1. Draw the color layer using the default composition mode of SourceOver.

  2. Draw the alpha layer using the DestinationIn composition mode.

For example:

screenshot

// https://github.com/KubaO/stackoverflown/tree/master/questions/alpha-mask-24943711
#include <QtWidgets>

QImage icon(int size) {
   QImage image{size, size, QImage::Format_ARGB32_Premultiplied};
   image.fill(Qt::transparent);
   QPainter p(&image);
   p.setRenderHint(QPainter::Antialiasing);
   p.setPen(Qt::NoPen);
   p.translate(image.rect().center());
   p.scale(image.width()/2.2, image.height()/2.2);
   p.setBrush(Qt::white);
   p.drawEllipse(QRectF{-.5, -.5, 1., 1.});
   p.setCompositionMode(QPainter::CompositionMode_DestinationIn);
   p.setBrush(Qt::transparent);
   p.drawEllipse(QRectF{-.3, -.3, .6, .6});
   for (auto angle : {0., 100., 150.}) {
      p.save();
      p.rotate(angle);
      p.drawRect(QRectF{-.1, 0, .2, -1.});
      p.restore();
   }
   return image;
}

QImage checkers(int size) {
   QImage img{size*2, size*2, QImage::Format_ARGB32_Premultiplied};
   QPainter p(&img);
   p.fillRect(0, 0, size, size, Qt::darkGray);
   p.fillRect(size, size, size, 2*size, Qt::darkGray);
   p.fillRect(size, 0, size, size, Qt::lightGray);
   p.fillRect(0, size, size, size, Qt::lightGray);
   return img;
}

void drawColorIcon(QPainter & p, QColor color, const QImage & alpha)
{
  p.save();
  p.setCompositionMode(QPainter::CompositionMode_SourceOver);
  p.fillRect(QRect{0, 0, alpha.width(), alpha.height()}, color);
  p.setCompositionMode(QPainter::CompositionMode_DestinationIn);
  p.drawImage(0, 0, alpha);
  p.restore();
}

QImage drawColorIconProof(QColor color, const QImage & alpha) {
   QImage result{alpha.size(), alpha.format()};
   QPainter p(&result);
   drawColorIcon(p, color, alpha);
   p.setCompositionMode(QPainter::CompositionMode_DestinationAtop);
   p.fillRect(alpha.rect(), {checkers(10)});
   return result;
}

int main(int argc, char *argv[])
{
   QApplication app{argc, argv};
   QLabel label;
   label.setPixmap(QPixmap::fromImage(drawColorIconProof("orangered", icon(200))));
   label.show();
   return app.exec();
}
Trauner answered 25/7, 2014 at 10:34 Comment(0)
H
0

I found a solution. However, instead of using a transparent graphic for the alpha map like I've used in the first post, I had to use a black/white-graphic where black pixels are transparent and white pixels are not (=rendered).

// ++++ in constructor ++++ 
QImage alphaMap = QImage(fileName);

QColor color;

// ++++ in paint Event ++++

QPainter painter(this);
painter.setRenderHints(QPainter::RenderHint::Antialiasing);
painter.setRenderHints(QPainter::RenderHint::HighQualityAntialiasing);

// draw icon
QImage renderedIcon(alphaMap);  
// fill with color
renderedIcon.fill(color);                   
// set alpha-map, black pixels -> opacity of 0, white pixels -> opacity 1
renderedIcon.setAlphaChannel(alphaMap);     

painter.drawImage(this->rect(), renderedIcon);  // draw image to QWidget
Hamulus answered 25/7, 2014 at 10:34 Comment(2)
Since QWidget::rect() always starts at 0,0, you can simply have painter.drawImage(0, 0, renderedIcon). You also don't necessarily need a custom widget for this - you could pass a QPixmap::fromQImage to a label. Of course if you need to render many such icons, it certainly saves memory to have a custom widget do the job.Unintelligent
Hi, I also do some other stuff with the Icon like changing the color when hovering etc. Thats why I think a custom widget is usefule here. ;) Thanks for the hint with rect(). I didn't realy looked into the documentation and thought it also determins the size. ;DHamulus
B
-1

I do not quite understand the problem, but may be you can use QGraphicsColorizeEffect class? QGraphicsColorizeEffect

Behold answered 24/7, 2014 at 22:15 Comment(1)
Hi, I already thought that I didn't made a good job in explaining my problem. I will add a graphic which should help understanding me. :) I will try the QGraphicsColorizeEffect.Hamulus

© 2022 - 2024 — McMap. All rights reserved.