0
votes

I'm trying to create a chroma key for a qwebview in Qt5. This means I need to make a specific color be transparent (other widgets should be visible through webview's pixels with that color). I've found that it can be done using QPainter::CompositionMode operations, but can't make it work.

For example, I need to make all black pixels of a webview be transparent (the source color should be changed in runtime).

I've reimplemented QWebView::paintEvent in my class (get a part of a code from Qt sources), but don't know what to do next

WebView::paintEvent(QPaintEvent *event) {
    if (!page()) return;
    QWebFrame *frame = page()->mainFrame();
    QPainter painter(this);
    painter.setRenderHints(renderHints());
    painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
    frame->render(&painter, event->region());

}

I found a way how to make any source color be white with the following code:

QWebFrame *frame = page()->mainFrame();
QImage source_image(size(), QImage::Format_ARGB32_Premultiplied);
QImage result_image(size(), QImage::Format_ARGB32_Premultiplied);

QPainter imagePainter(&source_image);
imagePainter.setRenderHints(renderHints());
frame->render(&imagePainter, event->region());
imagePainter.end();

QImage mask = source_image.createMaskFromColor(qRgb(0x00,0x00,0x00)); // Source color

QPainter resultPainter(&result_image);
resultPainter.drawImage(source_image.rect(), source_image);
resultPainter.setCompositionMode(QPainter::CompositionMode_Screen);
resultPainter.drawImage(source_image.rect(), mask);

QPainter painter(this);
painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
painter.drawImage(0, 0, result_image);

But I don't know how to convert a white color to transparent.

1

1 Answers

0
votes

I found a solution, but it consumes a lot of CPU.

First it's required to set

setStyleSheet("QWebView { background: transparent }");
setAttribute(Qt::WA_OpaquePaintEvent, true);

somewhere in WebView's constructor (I just forgot to mention that in the first message). Then reimplement paintEvent:

void WebView::paintEvent(QPaintEvent *event)
{
    if (!page())
        return;

    QWebFrame *frame = page()->mainFrame();
    QPainter painter(this);
    QColor chroma_color(0, 0, 0); // A color that should be transparent
    float opacity_level = 0.9; // WebView opacity

    m_render_pixmap.fill(Qt::transparent);

    QPainter pixmapPainter(&m_render_pixmap);
    pixmapPainter.setRenderHints(renderHints());
    frame->render(&pixmapPainter, event->region());
    pixmapPainter.end();
    m_render_pixmap.setMask(m_render_pixmap.createMaskFromColor(
        chroma_color, Qt::MaskInColor));

    painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
    painter.setOpacity(opacity_level);
    painter.drawPixmap(QPoint(event->rect().left(), event->rect().top()), m_render_pixmap, event->rect());
    painter.end();
}

m_render_pixmap is an instance of QPixmap. I don't want to recreate it every time paintEvent is called. I just recreate it on resizeEvent

void WebView::resizeEvent(QResizeEvent *event)
{
    QWebView::resizeEvent(event);
    m_render_pixmap = QPixmap(size());
}

The code above work great but in my case I want to render a video widget below a webview. So WebView::paintEvent calls about 25 times per second and each call takes about 20-25 ms in windowed mode on my PC. And it takes about 100% of one of CPU cores in a fullscreen mode.