0

I want to render a SVG icon in a QPixmap by choosing the drawing color.

This is my code using PyQt:

def svg_to_pixmap(svg_filename: str, width: int, height: int, color: QColor) -> QPixmap:
    renderer = QSvgRenderer(svg_filename)
    pixmap = QPixmap(width, height)
    pixmap.fill(Qt.GlobalColor.transparent)
    painter = QPainter(pixmap)
    painter.setPen(QPen(color))
    renderer.render(painter)
    painter.end()
    return pixmap

And a test code to display the pixmap:

app = QApplication([])
pixmap = svg_to_pixmap("test.svg", 512, 512, Qt.GlobalColor.red)
label = QLabel()
label.setStyleSheet("background-color: yellow;")
label.setPixmap(pixmap)
label.show()
app.exec()

The issue is that painter.setPen has no effect as expected (the drawing remains black). The background is transparent as expected and we can see the label background color behind. An example of SVG file to test here

My configuration: Ubuntu22.10, X11, PyQt6.3.1

Stephen Quan
  • 21,481
  • 4
  • 88
  • 75
kleio12345
  • 13
  • 3

1 Answers1

1

The answer is not simple. But, as long as you are completely sure that you only want to use the shape (or mask) of the original SVG, then it is feasible.

The reason for which setting the pen doesn't change the result is that SVG is a vector image format: it usually explicitly tells the colors of anything it wants to draw, so, setting the painter pen is almost useless, unless the SVG content is so simple to not specify it, which is clearly not the case: icons normally define colors of their shapes, and for good reasons.

The problem comes when trying to understand compositing and the possible alternatives that QPainter provides.

To be honest, even after trying to put efforts in understanding the results and associating them with the QPainter.CompositionMode enums, I still need to testing and checking in order to understand the proper mode I need.

What you probably need is the CompositionMode_SourceIn, which is cryptically explained in the documentation:

The output is the source, where the alpha is reduced by that of the destination.

As far as I can understand, the source is what is going to be painted, while the destination is what was previously painted (consider the source as a brush painting, and the destination as the canvas on which you paint).

With that in mind, we need to use the original svg as the destination and fill the whole pixmap with the color we want. Since only the alpha of the destination will be used (the visible part of the svg), we will be practically doing some sort of "stencil".

def svg_to_pixmap(svg_filename: str, width: int, height: int, color: QColor) -> QPixmap:
    renderer = QSvgRenderer(svg_filename)
    pixmap = QPixmap(width, height)
    pixmap.fill(Qt.GlobalColor.transparent)
    painter = QPainter(pixmap)
    renderer.render(painter) # this is the destination, and only its alpha is used!
    painter.setCompositionMode(
        painter.CompositionMode.CompositionMode_SourceIn)
    painter.fillRect(pixmap.rect(), color)
    painter.end()
    return pixmap

Which seems to give the wanted result:

result of the code above

musicamante
  • 41,230
  • 6
  • 33
  • 58