/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the plugins of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 3 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL3 included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 3 requirements ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 2.0 or (at your option) the GNU General ** Public license version 3 or any later version approved by the KDE Free ** Qt Foundation. The licenses are as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-2.0.html and ** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qwindowsbackingstore.h" #include "qwindowswindow.h" #include "qwindowscontext.h" #include #include #include #include #include #include QT_BEGIN_NAMESPACE /*! \class QWindowsBackingStore \brief Backing store for windows. \internal \ingroup qt-lighthouse-win */ QWindowsBackingStore::QWindowsBackingStore(QWindow *window) : QPlatformBackingStore(window), m_alphaNeedsFill(false) { qCDebug(lcQpaBackingStore) << __FUNCTION__ << this << window; } QWindowsBackingStore::~QWindowsBackingStore() { qCDebug(lcQpaBackingStore) << __FUNCTION__ << this; } QPaintDevice *QWindowsBackingStore::paintDevice() { Q_ASSERT(!m_image.isNull()); return &m_image->image(); } void QWindowsBackingStore::flush(QWindow *window, const QRegion ®ion, const QPoint &offset) { Q_ASSERT(window); const QRect br = region.boundingRect(); if (QWindowsContext::verbose > 1) qCDebug(lcQpaBackingStore) << __FUNCTION__ << this << window << offset << br; QWindowsWindow *rw = QWindowsWindow::windowsWindowOf(window); Q_ASSERT(rw); const bool hasAlpha = rw->format().hasAlpha(); const Qt::WindowFlags flags = window->flags(); if ((flags & Qt::FramelessWindowHint) && QWindowsWindow::setWindowLayered(rw->handle(), flags, hasAlpha, rw->opacity()) && hasAlpha) { // Windows with alpha: Use blend function to update. QRect r = QHighDpi::toNativePixels(window->frameGeometry(), window); QPoint frameOffset(QHighDpi::toNativePixels(QPoint(window->frameMargins().left(), window->frameMargins().top()), static_cast(nullptr))); QRect dirtyRect = br.translated(offset + frameOffset); SIZE size = {r.width(), r.height()}; POINT ptDst = {r.x(), r.y()}; POINT ptSrc = {0, 0}; BLENDFUNCTION blend = {AC_SRC_OVER, 0, BYTE(qRound(255.0 * rw->opacity())), AC_SRC_ALPHA}; RECT dirty = {dirtyRect.x(), dirtyRect.y(), dirtyRect.x() + dirtyRect.width(), dirtyRect.y() + dirtyRect.height()}; UPDATELAYEREDWINDOWINFO info = {sizeof(info), NULL, &ptDst, &size, m_image->hdc(), &ptSrc, 0, &blend, ULW_ALPHA, &dirty}; const BOOL result = UpdateLayeredWindowIndirect(rw->handle(), &info); if (!result) qErrnoWarning("UpdateLayeredWindowIndirect failed for ptDst=(%d, %d)," " size=(%dx%d), dirty=(%dx%d %d, %d)", r.x(), r.y(), r.width(), r.height(), dirtyRect.width(), dirtyRect.height(), dirtyRect.x(), dirtyRect.y()); } else { const HDC dc = rw->getDC(); if (!dc) { qErrnoWarning("%s: GetDC failed", __FUNCTION__); return; } if (!BitBlt(dc, br.x(), br.y(), br.width(), br.height(), m_image->hdc(), br.x() + offset.x(), br.y() + offset.y(), SRCCOPY)) { const DWORD lastError = GetLastError(); // QTBUG-35926, QTBUG-29716: may fail after lock screen. if (lastError != ERROR_SUCCESS && lastError != ERROR_INVALID_HANDLE) qErrnoWarning(int(lastError), "%s: BitBlt failed", __FUNCTION__); } rw->releaseDC(); } // Write image for debug purposes. if (QWindowsContext::verbose > 2 && lcQpaBackingStore().isDebugEnabled()) { static int n = 0; const QString fileName = QString::fromLatin1("win%1_%2.png"). arg(rw->winId()).arg(n++); m_image->image().save(fileName); qCDebug(lcQpaBackingStore) << "Wrote " << m_image->image().size() << fileName; } } void QWindowsBackingStore::resize(const QSize &size, const QRegion ®ion) { if (m_image.isNull() || m_image->image().size() != size) { #ifndef QT_NO_DEBUG_OUTPUT if (QWindowsContext::verbose && lcQpaBackingStore().isDebugEnabled()) { qCDebug(lcQpaBackingStore) << __FUNCTION__ << ' ' << window() << ' ' << size << ' ' << region << " from: " << (m_image.isNull() ? QSize() : m_image->image().size()); } #endif QImage::Format format = window()->format().hasAlpha() ? QImage::Format_ARGB32_Premultiplied : QWindowsNativeImage::systemFormat(); // The backingstore composition (enabling render-to-texture widgets) // punches holes in the backingstores using the alpha channel. Hence // the need for a true alpha format. if (QImage::toPixelFormat(format).alphaUsage() == QPixelFormat::UsesAlpha) m_alphaNeedsFill = true; else // upgrade but here we know app painting does not rely on alpha hence no need to fill format = qt_maybeAlphaVersionWithSameDepth(format); QWindowsNativeImage *oldwni = m_image.data(); QWindowsNativeImage *newwni = new QWindowsNativeImage(size.width(), size.height(), format); if (oldwni && !region.isEmpty()) { const QImage &oldimg(oldwni->image()); QImage &newimg(newwni->image()); QRegion staticRegion(region); staticRegion &= QRect(0, 0, oldimg.width(), oldimg.height()); staticRegion &= QRect(0, 0, newimg.width(), newimg.height()); QPainter painter(&newimg); painter.setCompositionMode(QPainter::CompositionMode_Source); for (const QRect &rect : staticRegion) painter.drawImage(rect, oldimg, rect); } m_image.reset(newwni); } } Q_GUI_EXPORT void qt_scrollRectInImage(QImage &img, const QRect &rect, const QPoint &offset); bool QWindowsBackingStore::scroll(const QRegion &area, int dx, int dy) { if (m_image.isNull() || m_image->image().isNull()) return false; const QPoint offset(dx, dy); for (const QRect &rect : area) qt_scrollRectInImage(m_image->image(), rect, offset); return true; } void QWindowsBackingStore::beginPaint(const QRegion ®ion) { if (QWindowsContext::verbose > 1) qCDebug(lcQpaBackingStore) <<__FUNCTION__ << region; if (m_alphaNeedsFill) { QPainter p(&m_image->image()); p.setCompositionMode(QPainter::CompositionMode_Source); const QColor blank = Qt::transparent; for (const QRect &r : region) p.fillRect(r, blank); } } HDC QWindowsBackingStore::getDC() const { if (!m_image.isNull()) return m_image->hdc(); return 0; } QImage QWindowsBackingStore::toImage() const { if (m_image.isNull()) { qCWarning(lcQpaBackingStore) <<__FUNCTION__ << "Image is null."; return QImage(); } return m_image.data()->image(); } QT_END_NAMESPACE