123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283 | // Copyright (C) 2016 The Qt Company Ltd.// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only#include"qwindowsdirect2dcontext.h"#include"qwindowsdirect2dbitmap.h"#include"qwindowsdirect2dwindow.h"#include"qwindowsdirect2ddevicecontext.h"#include"qwindowsdirect2dhelpers.h"#include"qwindowsdirect2dplatformpixmap.h"#include <d3d11.h>#include <d2d1_1.h>#include <dxgi1_2.h>usingMicrosoft::WRL::ComPtr; QT_BEGIN_NAMESPACE QWindowsDirect2DWindow::QWindowsDirect2DWindow(QWindow *window,const QWindowsWindowData &data):QWindowsWindow(window, data),m_directRendering(!(data.flags &Qt::FramelessWindowHint && window->format().hasAlpha())){if(window->type() ==Qt::Desktop)return;// No further handling for Qt::Desktopif(m_directRendering)setupSwapChain(); HRESULT hr =QWindowsDirect2DContext::instance()->d2dDevice()->CreateDeviceContext( D2D1_DEVICE_CONTEXT_OPTIONS_NONE, m_deviceContext.GetAddressOf());if(FAILED(hr))qWarning("%s: Couldn't create Direct2D Device context: %#lx", __FUNCTION__, hr);}QWindowsDirect2DWindow::~QWindowsDirect2DWindow(){}voidQWindowsDirect2DWindow::setWindowFlags(Qt::WindowFlags flags){ m_directRendering = !(flags &Qt::FramelessWindowHint &&window()->format().hasAlpha());if(!m_directRendering) m_swapChain.Reset();// No need for the swap chain; release from memoryelse if(!m_swapChain)setupSwapChain();QWindowsWindow::setWindowFlags(flags);} QPixmap *QWindowsDirect2DWindow::pixmap(){setupBitmap();return m_pixmap.data();}voidQWindowsDirect2DWindow::flush(QWindowsDirect2DBitmap *bitmap,const QRegion ®ion,const QPoint &offset){ QSize size;if(m_directRendering) { DXGI_SWAP_CHAIN_DESC1 desc; HRESULT hr = m_swapChain->GetDesc1(&desc); QRect geom =geometry();if(FAILED(hr) || (desc.Width !=UINT(geom.width()) || (desc.Height !=UINT(geom.height())))) {resizeSwapChain(geom.size()); m_swapChain->GetDesc1(&desc);} size.setWidth(int(desc.Width)); size.setHeight(int(desc.Height));}else{ size =geometry().size();}setupBitmap();if(!m_bitmap)return;if(bitmap != m_bitmap.data()) { m_bitmap->deviceContext()->begin(); ID2D1DeviceContext *dc = m_bitmap->deviceContext()->get();if(!m_needsFullFlush) { QRegion clipped = region; clipped &=QRect(QPoint(), size);for(const QRect &rect : clipped) { QRectF rectF(rect); dc->DrawBitmap(bitmap->bitmap(),to_d2d_rect_f(rectF),1.0, D2D1_INTERPOLATION_MODE_LINEAR,to_d2d_rect_f(rectF.translated(offset.x(), offset.y())));}}else{ QRectF rectF(QPoint(), size); dc->DrawBitmap(bitmap->bitmap(),to_d2d_rect_f(rectF),1.0, D2D1_INTERPOLATION_MODE_LINEAR,to_d2d_rect_f(rectF.translated(offset.x(), offset.y()))); m_needsFullFlush =false;} m_bitmap->deviceContext()->end();}}voidQWindowsDirect2DWindow::present(const QRegion ®ion){if(m_directRendering) { m_swapChain->Present(0,0);return;} ComPtr<IDXGISurface> bitmapSurface; HRESULT hr = m_bitmap->bitmap()->GetSurface(&bitmapSurface);Q_ASSERT(SUCCEEDED(hr)); ComPtr<IDXGISurface1> dxgiSurface; hr = bitmapSurface.As(&dxgiSurface);Q_ASSERT(SUCCEEDED(hr)); HDC hdc; hr = dxgiSurface->GetDC(FALSE, &hdc);if(FAILED(hr)) {qErrnoWarning(hr,"Failed to get DC for presenting the surface");return;}const QRect bounds =window()->geometry();const SIZE size = { bounds.width(), bounds.height() };const POINT ptDst = { bounds.x(), bounds.y() };const POINT ptSrc = {0,0};const BLENDFUNCTION blend = { AC_SRC_OVER,0,BYTE(255.0*opacity()), AC_SRC_ALPHA };const QRect r = region.boundingRect();const RECT dirty = { r.left(), r.top(), r.left() + r.width(), r.top() + r.height() }; UPDATELAYEREDWINDOWINFO info = {sizeof(UPDATELAYEREDWINDOWINFO),nullptr,&ptDst, &size, hdc, &ptSrc,0, &blend, ULW_ALPHA, &dirty };if(!UpdateLayeredWindowIndirect(handle(), &info))qErrnoWarning(int(GetLastError()),"Failed to update the layered window"); hr = dxgiSurface->ReleaseDC(nullptr);if(FAILED(hr))qErrnoWarning(hr,"Failed to release the DC for presentation");}voidQWindowsDirect2DWindow::setupSwapChain(){ DXGI_SWAP_CHAIN_DESC1 desc = {}; desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; desc.SampleDesc.Count =1; desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; desc.BufferCount =1; desc.SwapEffect = DXGI_SWAP_EFFECT_SEQUENTIAL; HRESULT hr =QWindowsDirect2DContext::instance()->dxgiFactory()->CreateSwapChainForHwnd(QWindowsDirect2DContext::instance()->d3dDevice(),// [in] IUnknown *pDevicehandle(),// [in] HWND hWnd&desc,// [in] const DXGI_SWAP_CHAIN_DESC1 *pDescnullptr,// [in] const DXGI_SWAP_CHAIN_FULLSCREEN_DESC *pFullscreenDescnullptr,// [in] IDXGIOutput *pRestrictToOutput m_swapChain.ReleaseAndGetAddressOf());// [out] IDXGISwapChain1 **ppSwapChainif(FAILED(hr))qWarning("%s: Could not create swap chain: %#lx", __FUNCTION__, hr); m_needsFullFlush =true;}voidQWindowsDirect2DWindow::resizeSwapChain(const QSize &size){ m_pixmap.reset(); m_bitmap.reset(); m_deviceContext->SetTarget(nullptr); m_needsFullFlush =true;if(!m_swapChain)return; HRESULT hr = m_swapChain->ResizeBuffers(0,UINT(size.width()),UINT(size.height()), DXGI_FORMAT_UNKNOWN,0);if(FAILED(hr))qWarning("%s: Could not resize swap chain: %#lx", __FUNCTION__, hr);} QSharedPointer<QWindowsDirect2DBitmap>QWindowsDirect2DWindow::copyBackBuffer()const{const QSharedPointer<QWindowsDirect2DBitmap> null_result;if(!m_bitmap)return null_result; D2D1_PIXEL_FORMAT format = m_bitmap->bitmap()->GetPixelFormat(); D2D1_SIZE_U size = m_bitmap->bitmap()->GetPixelSize(); FLOAT dpiX, dpiY; m_bitmap->bitmap()->GetDpi(&dpiX, &dpiY); D2D1_BITMAP_PROPERTIES1 properties = { format,// D2D1_PIXEL_FORMAT pixelFormat; dpiX,// FLOAT dpiX; dpiY,// FLOAT dpiY; D2D1_BITMAP_OPTIONS_TARGET,// D2D1_BITMAP_OPTIONS bitmapOptions;nullptr// _Field_size_opt_(1) ID2D1ColorContext *colorContext;}; ComPtr<ID2D1Bitmap1> copy; HRESULT hr = m_deviceContext.Get()->CreateBitmap(size,nullptr,0, properties, ©);if(FAILED(hr)) {qWarning("%s: Could not create staging bitmap: %#lx", __FUNCTION__, hr);return null_result;} hr = copy.Get()->CopyFromBitmap(nullptr, m_bitmap->bitmap(),nullptr);if(FAILED(hr)) {qWarning("%s: Could not copy from bitmap! %#lx", __FUNCTION__, hr);return null_result;}return QSharedPointer<QWindowsDirect2DBitmap>(newQWindowsDirect2DBitmap(copy.Get(),nullptr));}voidQWindowsDirect2DWindow::setupBitmap(){if(m_bitmap)return;if(!m_deviceContext)return;if(m_directRendering && !m_swapChain)return; HRESULT hr; ComPtr<IDXGISurface1> backBufferSurface;if(m_directRendering) { hr = m_swapChain->GetBuffer(0,IID_PPV_ARGS(&backBufferSurface));if(FAILED(hr)) {qWarning("%s: Could not query backbuffer for DXGI Surface: %#lx", __FUNCTION__, hr);return;}}else{const QRect rect =geometry(); CD3D11_TEXTURE2D_DESC backBufferDesc(DXGI_FORMAT_B8G8R8A8_UNORM,UINT(rect.width()),UINT(rect.height()),1,1); backBufferDesc.BindFlags = D3D11_BIND_RENDER_TARGET; backBufferDesc.MiscFlags = D3D11_RESOURCE_MISC_GDI_COMPATIBLE; ComPtr<ID3D11Texture2D> backBufferTexture; HRESULT hr =QWindowsDirect2DContext::instance()->d3dDevice()->CreateTexture2D(&backBufferDesc,nullptr, &backBufferTexture);if(FAILED(hr)) {qErrnoWarning(hr,"Failed to create backing texture for indirect rendering");return;} hr = backBufferTexture.As(&backBufferSurface);if(FAILED(hr)) {qErrnoWarning(hr,"Failed to cast back buffer surface to DXGI surface");return;}} ComPtr<ID2D1Bitmap1> backBufferBitmap; hr = m_deviceContext->CreateBitmapFromDxgiSurface(backBufferSurface.Get(),nullptr, backBufferBitmap.GetAddressOf());if(FAILED(hr)) {qWarning("%s: Could not create Direct2D Bitmap from DXGI Surface: %#lx", __FUNCTION__, hr);return;} m_bitmap.reset(newQWindowsDirect2DBitmap(backBufferBitmap.Get(), m_deviceContext.Get()));QWindowsDirect2DPaintEngine::Flags flags =QWindowsDirect2DPaintEngine::NoFlag;if(!m_directRendering) flags |=QWindowsDirect2DPaintEngine::TranslucentTopLevelWindow;auto*pp =newQWindowsDirect2DPlatformPixmap(QPlatformPixmap::PixmapType, flags, m_bitmap.data()); m_pixmap.reset(newQPixmap(pp));} QT_END_NAMESPACE
|