| /**************************************************************************** |
| ** |
| ** 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 <QtGui/qwindow.h> |
| #include <QtGui/qpainter.h> |
| #include <QtFontDatabaseSupport/private/qwindowsnativeimage_p.h> |
| #include <private/qhighdpiscaling_p.h> |
| #include <private/qimage_p.h> |
| |
| #include <QtCore/qdebug.h> |
| |
| 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<const QWindow *>(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), nullptr, &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(); |
| auto *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 nullptr; |
| } |
| |
| QImage QWindowsBackingStore::toImage() const |
| { |
| if (m_image.isNull()) { |
| qCWarning(lcQpaBackingStore) <<__FUNCTION__ << "Image is null."; |
| return QImage(); |
| } |
| return m_image.data()->image(); |
| } |
| |
| QT_END_NAMESPACE |