How to draw an SVG with a custom opacity
-
@additional-pumpkin Why do not you set opacity inside your svg file? Just curious.
-
@JoeCFD For 2 reasons. First the svg file can be supplied by the user. Second by setting it in the svg file you get a different result than what I want.
This is what the original svg looks like
This is what happens after changing the opacity in the svg file
This is what I want to happen (ignore the fact that the image is larger)
But changing the file isn't really an option anyway since the user can supply it.
-
Also this SVG will normally be drawn with a normal opacity. Only when the user interacts with it and drags it it will leave behind a ghost svg which is what i want to implement by making in transparent.
Here's an example of it being done (by rust-chessground)
And this is what it looks like in my app right now
I want the piece that is being dragged to leave behind a 'ghost' piece on the square it was dragged from. This would be very simple to implement if I could set an custom opacity while rendering the svg with QSvgRenderer, but since QSvgRenderer has no such functionality how can I do this?
-
@additional-pumpkin I understand your problem now. Maybe take a look at the source code of SVG and figure out how to avoid overlaps(no mix, but only reset of pixel color). Then create a customized class to inherit SVG class and override the generation part of image from svg without overlaps.
-
@JoeCFD I'm sorry but I don't understand...
Which svg class? QSvgRenderer? QSvgGenerator? What SVG source code? Do you mean the xml in the svg file? How do I "override the generation part of image from svg without overlaps"? -
@additional-pumpkin said in How to draw an SVG with a custom opacity:
QSvgRenderer
line 378
https://code.qt.io/cgit/qt/qtsvg.git/tree/src/svg/qsvgrenderer.cpp?h=dev
QSvgRenderer::render(); /* I use this one, not sure which one you use. Can you show some code? */I guess you need to dig it deeper.
-
Here's my code for drawing the pieces
(pieces[] is an array of QSvgRenderers)void BoardView::draw_pieces(QPaintEvent *event) { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); for (chessops::Square sq = chessops::A1; sq <= chessops::H8; ++sq) { int x = sq % 8; int y = 7 - sq / 8; QPointF pos(x * m_square_size, y * m_square_size); chessops::Piece piece = m_board.get_piece_at(sq); if (m_board.is_check() && chessops::type_of(piece) == chessops::PieceType::KING && chessops::color_of(piece) == m_board.get_stm()) { QRadialGradient check_indicator(QPointF(pos.x() + m_square_size/2, pos.y() + m_square_size/2), m_square_size/2); check_indicator.setColorAt(0, Qt::red); check_indicator.setColorAt(1, Qt::transparent); painter.fillRect(QRectF(pos, QSizeF(m_square_size, m_square_size)), check_indicator); } if (sq == m_drag_start) { painter.fillRect(QRectF(pos, QSizeF(m_square_size, m_square_size)), QBrush(QColor(20, 85, 30, 255/2))); // continue; } if(piece != chessops::PIECE_NONE) { pieces[m_board.get_piece_at(sq)].render(&painter, QRectF(pos, QSizeF(m_square_size, m_square_size))); } if(m_board.is_piece_attack(m_drag_start, sq)) { if (piece == chessops::Piece::PIECE_NONE) { QPointF circle_pos = pos; circle_pos.setX(circle_pos.x() + m_square_size/4 + m_square_size/8); circle_pos.setY(circle_pos.y() + m_square_size/4 + m_square_size/8); painter.setPen(Qt::transparent); painter.setBrush(QColor(20, 85, 30, 128)); painter.drawEllipse(QRectF(circle_pos, QSizeF(m_square_size/4, m_square_size/4))); } else { QPointF circle_pos = pos; circle_pos.setX(circle_pos.x() - m_square_size/16); circle_pos.setY(circle_pos.y() - m_square_size/16); QPainterPath path; QPainterPath bounds; path.addRect(QRectF(pos, QSizeF(m_square_size, m_square_size))); path.addEllipse(QRectF(circle_pos, QSizeF(m_square_size + m_square_size/8, m_square_size + m_square_size/8))); bounds.addRect(QRectF(pos, QSizeF(m_square_size/4, m_square_size/4))); bounds.addRect(QRectF(QPointF(pos.x() + m_square_size - m_square_size/4, pos.y()), QSizeF(m_square_size/4, m_square_size/4))); bounds.addRect(QRectF(QPointF(pos.x(), pos.y() + m_square_size - m_square_size/4), QSizeF(m_square_size/4, m_square_size/4))); bounds.addRect(QRectF(QPointF(pos.x() + m_square_size - m_square_size/4, pos.y() + m_square_size - m_square_size/4), QSizeF(m_square_size/4, m_square_size/4))); painter.setPen(Qt::transparent); painter.setBrush(QColor(20, 85, 30, 128)); // painter.drawPath(path); painter.drawPath(path.intersected(bounds)); // painter.fillPath(bounds, QColor(Qt::red)); } } if (m_debug_overlay) { if (m_board.debug_is_enemy_attack(sq)) { painter.setPen(QPen(Qt::red, 10)); painter.setBrush(Qt::transparent); painter.drawEllipse(QRectF(pos, QSizeF(m_square_size, m_square_size))); } } } if (m_drag_start != chessops::SQUARE_NONE && m_board.get_piece_at(m_drag_start) != chessops::PIECE_NONE) pieces[m_board.get_piece_at(m_drag_start)].render(&painter, QRectF(m_dragged_piece_pos, QSizeF(m_square_size, m_square_size))); }```
-
@additional-pumpkin I think you call the func in the line 413. Go deep from there and make your func to get rid of overlaps.
-
@additional-pumpkin making a 3D board may have more fun.
-
@additional-pumpkin Unless the pieces change in some way you shouldn't be rendering them directly from an SVG every time you draw a piece. SVG is a complicated format and that's very inefficient, especially since you're gonna draw the same type of piece multiple times (e.g. the pawns). Draw each piece once to a pixmap and store it. When you need to draw the piece draw the stored pixmap instead. Additional benefit is that drawing a pixmap with a regular painter will let you set opacity on the painter. It's gonna be much more performant and will solve your opacity problem at the same time.
-
@Chris-Kawa Thank you so much!! This works great!
-
A additional-pumpkin has marked this topic as solved on