summaryrefslogtreecommitdiffstats
path: root/canvas/source/directx
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:54:39 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:54:39 +0000
commit267c6f2ac71f92999e969232431ba04678e7437e (patch)
tree358c9467650e1d0a1d7227a21dac2e3d08b622b2 /canvas/source/directx
parentInitial commit. (diff)
downloadlibreoffice-267c6f2ac71f92999e969232431ba04678e7437e.tar.xz
libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.zip
Adding upstream version 4:24.2.0.upstream/4%24.2.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'canvas/source/directx')
-rw-r--r--canvas/source/directx/directx9canvas.component26
-rw-r--r--canvas/source/directx/dx_9rm.cxx1201
-rw-r--r--canvas/source/directx/dx_bitmap.cxx204
-rw-r--r--canvas/source/directx/dx_bitmap.hxx82
-rw-r--r--canvas/source/directx/dx_bitmapcanvashelper.cxx221
-rw-r--r--canvas/source/directx/dx_bitmapcanvashelper.hxx126
-rw-r--r--canvas/source/directx/dx_bitmapprovider.hxx34
-rw-r--r--canvas/source/directx/dx_canvas.cxx256
-rw-r--r--canvas/source/directx/dx_canvas.hxx173
-rw-r--r--canvas/source/directx/dx_canvasbitmap.cxx256
-rw-r--r--canvas/source/directx/dx_canvasbitmap.hxx91
-rw-r--r--canvas/source/directx/dx_canvascustomsprite.cxx106
-rw-r--r--canvas/source/directx/dx_canvascustomsprite.hxx130
-rw-r--r--canvas/source/directx/dx_canvasfont.cxx162
-rw-r--r--canvas/source/directx/dx_canvasfont.hxx92
-rw-r--r--canvas/source/directx/dx_canvashelper.cxx814
-rw-r--r--canvas/source/directx/dx_canvashelper.hxx252
-rw-r--r--canvas/source/directx/dx_canvashelper_texturefill.cxx608
-rw-r--r--canvas/source/directx/dx_config.cxx152
-rw-r--r--canvas/source/directx/dx_config.hxx80
-rw-r--r--canvas/source/directx/dx_devicehelper.cxx199
-rw-r--r--canvas/source/directx/dx_devicehelper.hxx114
-rw-r--r--canvas/source/directx/dx_gdiplususer.cxx74
-rw-r--r--canvas/source/directx/dx_gdiplususer.hxx45
-rw-r--r--canvas/source/directx/dx_graphicsprovider.hxx46
-rw-r--r--canvas/source/directx/dx_ibitmap.hxx62
-rw-r--r--canvas/source/directx/dx_impltools.cxx629
-rw-r--r--canvas/source/directx/dx_impltools.hxx124
-rw-r--r--canvas/source/directx/dx_linepolypolygon.cxx59
-rw-r--r--canvas/source/directx/dx_linepolypolygon.hxx47
-rw-r--r--canvas/source/directx/dx_rendermodule.hxx80
-rw-r--r--canvas/source/directx/dx_sprite.hxx45
-rw-r--r--canvas/source/directx/dx_spritecanvas.cxx192
-rw-r--r--canvas/source/directx/dx_spritecanvas.hxx155
-rw-r--r--canvas/source/directx/dx_spritecanvashelper.cxx352
-rw-r--r--canvas/source/directx/dx_spritecanvashelper.hxx152
-rw-r--r--canvas/source/directx/dx_spritedevicehelper.cxx221
-rw-r--r--canvas/source/directx/dx_spritedevicehelper.hxx98
-rw-r--r--canvas/source/directx/dx_spritehelper.cxx199
-rw-r--r--canvas/source/directx/dx_spritehelper.hxx102
-rw-r--r--canvas/source/directx/dx_surfacebitmap.cxx654
-rw-r--r--canvas/source/directx/dx_surfacebitmap.hxx135
-rw-r--r--canvas/source/directx/dx_surfacegraphics.cxx77
-rw-r--r--canvas/source/directx/dx_surfacegraphics.hxx36
-rw-r--r--canvas/source/directx/dx_textlayout.cxx253
-rw-r--r--canvas/source/directx/dx_textlayout.hxx107
-rw-r--r--canvas/source/directx/dx_textlayout_drawhelper.cxx317
-rw-r--r--canvas/source/directx/dx_textlayout_drawhelper.hxx78
-rw-r--r--canvas/source/directx/dx_vcltools.cxx307
-rw-r--r--canvas/source/directx/dx_vcltools.hxx47
-rw-r--r--canvas/source/directx/dx_winstuff.hxx71
-rw-r--r--canvas/source/directx/gdipluscanvas.component30
52 files changed, 10173 insertions, 0 deletions
diff --git a/canvas/source/directx/directx9canvas.component b/canvas/source/directx/directx9canvas.component
new file mode 100644
index 0000000000..ac75940d2c
--- /dev/null
+++ b/canvas/source/directx/directx9canvas.component
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ -->
+
+<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@"
+ xmlns="http://openoffice.org/2010/uno-components">
+ <implementation name="com.sun.star.comp.rendering.SpriteCanvas.DX9"
+ constructor="canvas_directx9_SpriteCanvas_get_implementation">
+ <service name="com.sun.star.rendering.SpriteCanvas.DX9"/>
+ </implementation>
+</component>
diff --git a/canvas/source/directx/dx_9rm.cxx b/canvas/source/directx/dx_9rm.cxx
new file mode 100644
index 0000000000..93738b3455
--- /dev/null
+++ b/canvas/source/directx/dx_9rm.cxx
@@ -0,0 +1,1201 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+#include <memory>
+#include <string.h>
+
+#include <basegfx/numeric/ftools.hxx>
+#include <basegfx/point/b2ipoint.hxx>
+#include <basegfx/range/b2irectangle.hxx>
+#include <basegfx/vector/b2dsize.hxx>
+#include <basegfx/vector/b2isize.hxx>
+#include <com/sun/star/lang/NoSupportException.hpp>
+#include <osl/thread.hxx>
+#include <osl/time.h>
+#include <comphelper/diagnose_ex.hxx>
+#include <vcl/syschild.hxx>
+#include <vcl/sysdata.hxx>
+#include <vcl/window.hxx>
+
+#include <canvas/elapsedtime.hxx>
+#include <canvas/canvastools.hxx>
+#include <rendering/icolorbuffer.hxx>
+#include <rendering/irendermodule.hxx>
+#include <rendering/isurface.hxx>
+
+#include "dx_config.hxx"
+#include "dx_impltools.hxx"
+#include "dx_rendermodule.hxx"
+
+#define MIN_TEXTURE_SIZE (32)
+//#define FAKE_MAX_NUMBER_TEXTURES (2)
+//#define FAKE_MAX_TEXTURE_SIZE (4096)
+
+#define VERTEX_BUFFER_SIZE (341*3) // 1023, the size of the internal
+ // vertex buffer (must be divisible
+ // by 3, as each triangle primitive
+ // has 3 vertices)
+
+
+using namespace ::com::sun::star;
+
+
+// 'dxcanvas' namespace
+
+
+namespace dxcanvas
+{
+ namespace
+ {
+ class DXRenderModule;
+
+
+ // DXSurface
+
+
+ /** ISurface implementation.
+
+ @attention holds the DXRenderModule via non-refcounted
+ reference! This is safe with current state of affairs, since
+ the canvas::PageManager holds surface and render module via
+ shared_ptr (and makes sure all surfaces are deleted before its
+ render module member goes out of scope).
+ */
+ class DXSurface : public canvas::ISurface
+ {
+ public:
+ DXSurface( DXRenderModule& rRenderModule,
+ const ::basegfx::B2ISize& rSize );
+ ~DXSurface() override;
+
+ virtual bool selectTexture() override;
+ virtual bool isValid() override;
+ virtual bool update( const ::basegfx::B2IPoint& rDestPos,
+ const ::basegfx::B2IRange& rSourceRect,
+ ::canvas::IColorBuffer& rSource ) override;
+ virtual ::basegfx::B2ISize getSize();
+
+ private:
+ /// Guard local methods against concurrent access to RenderModule
+ class ImplRenderModuleGuard
+ {
+ public:
+ /// make noncopyable
+ ImplRenderModuleGuard(const ImplRenderModuleGuard&) = delete;
+ const ImplRenderModuleGuard& operator=(const ImplRenderModuleGuard&) = delete;
+
+ explicit ImplRenderModuleGuard( DXRenderModule& rRenderModule );
+ ~ImplRenderModuleGuard();
+
+ private:
+ DXRenderModule& mrRenderModule;
+ };
+
+ DXRenderModule& mrRenderModule;
+ sal::systools::COMReference<IDirect3DTexture9> mpTexture;
+
+ ::basegfx::B2ISize maSize;
+ };
+
+
+ // DXRenderModule
+
+
+ /// Default implementation of IDXRenderModule
+ class DXRenderModule final: public IDXRenderModule
+ {
+ public:
+ explicit DXRenderModule( const vcl::Window& rWindow );
+ ~DXRenderModule() override;
+
+ virtual void lock() const override { maMutex.acquire(); }
+ virtual void unlock() const override { maMutex.release(); }
+
+ virtual sal::systools::COMReference<IDirect3DSurface9>
+ createSystemMemorySurface(const ::basegfx::B2ISize& rSize) override;
+ virtual void disposing() override;
+ virtual HWND getHWND() const override { return mhWnd; }
+ virtual void screenShot() override;
+
+ virtual bool flip( const ::basegfx::B2IRectangle& rUpdateArea,
+ const ::basegfx::B2IRectangle& rCurrWindowArea ) override;
+
+ virtual void resize( const ::basegfx::B2IRange& rect ) override;
+ virtual ::basegfx::B2IVector getPageSize() override;
+ virtual std::shared_ptr<canvas::ISurface> createSurface( const ::basegfx::B2IVector& surfaceSize ) override;
+ virtual void beginPrimitive( PrimitiveType eType ) override;
+ virtual void endPrimitive() override;
+ virtual void pushVertex( const ::canvas::Vertex& vertex ) override;
+ virtual bool isError() override;
+
+ sal::systools::COMReference<IDirect3DDevice9> getDevice() { return mpDevice; }
+
+ void flushVertexCache();
+ void commitVertexCache();
+
+ private:
+
+ bool create( const vcl::Window& rWindow );
+ bool createDevice();
+ bool verifyDevice( const UINT nAdapter );
+ UINT getAdapterFromWindow();
+
+ /** This object represents the DirectX state machine. In order
+ to serialize access to DirectX's global state, a global
+ mutex is required.
+ */
+ static ::osl::Mutex maMutex;
+
+ HWND mhWnd;
+ sal::systools::COMReference<IDirect3DDevice9> mpDevice;
+ sal::systools::COMReference<IDirect3D9> mpDirect3D9;
+ sal::systools::COMReference<IDirect3DSwapChain9> mpSwapChain;
+ sal::systools::COMReference<IDirect3DVertexBuffer9> mpVertexBuffer;
+ std::shared_ptr<canvas::ISurface> mpTexture;
+ VclPtr<SystemChildWindow> mpWindow;
+ ::basegfx::B2ISize maSize;
+ typedef std::vector<canvas::Vertex> vertexCache_t;
+ vertexCache_t maVertexCache;
+ std::size_t mnCount;
+ int mnBeginSceneCount;
+ bool mbCanUseDynamicTextures;
+ bool mbError;
+ PrimitiveType meType;
+ ::basegfx::B2IVector maPageSize;
+ D3DPRESENT_PARAMETERS mad3dpp;
+
+ bool isDisposed() const { return (mhWnd==nullptr); }
+
+ struct dxvertex
+ {
+ float x,y,z,rhw;
+ DWORD diffuse;
+ float u,v;
+ };
+
+ std::size_t maNumVertices;
+ std::size_t maWriteIndex;
+ std::size_t maReadIndex;
+ };
+
+ ::osl::Mutex DXRenderModule::maMutex;
+
+
+ // DXSurface::ImplRenderModuleGuard
+
+
+ DXSurface::ImplRenderModuleGuard::ImplRenderModuleGuard(
+ DXRenderModule& rRenderModule ) :
+ mrRenderModule( rRenderModule )
+ {
+ mrRenderModule.lock();
+ }
+
+ DXSurface::ImplRenderModuleGuard::~ImplRenderModuleGuard()
+ {
+ mrRenderModule.unlock();
+ }
+
+#ifdef FAKE_MAX_NUMBER_TEXTURES
+ static sal_uInt32 gNumSurfaces = 0;
+#endif
+
+ // DXSurface::DXSurface
+
+
+ DXSurface::DXSurface( DXRenderModule& rRenderModule,
+ const ::basegfx::B2ISize& rSize ) :
+ mrRenderModule(rRenderModule),
+ mpTexture(nullptr)
+ {
+ ImplRenderModuleGuard aGuard( mrRenderModule );
+
+#ifdef FAKE_MAX_NUMBER_TEXTURES
+ ++gNumSurfaces;
+ if(gNumSurfaces >= FAKE_MAX_NUMBER_TEXTURES)
+ return;
+#endif
+
+#ifdef FAKE_MAX_TEXTURE_SIZE
+ if(rSize.getWidth() > FAKE_MAX_TEXTURE_SIZE)
+ return;
+ if(rSize.getHeight() > FAKE_MAX_TEXTURE_SIZE)
+ return;
+#endif
+
+ ENSURE_ARG_OR_THROW(rSize.getWidth() > 0 && rSize.getHeight() > 0,
+ "DXSurface::DXSurface(): request for zero-sized surface");
+
+ sal::systools::COMReference<IDirect3DDevice9> pDevice(rRenderModule.getDevice());
+
+ IDirect3DTexture9 *pTexture(nullptr);
+ if(FAILED(pDevice->CreateTexture(
+ rSize.getWidth(),
+ rSize.getHeight(),
+ 1,0,D3DFMT_A8R8G8B8,
+ D3DPOOL_MANAGED,
+ &pTexture,nullptr)))
+ return;
+
+ mpTexture = sal::systools::COMReference<IDirect3DTexture9>(pTexture, false);
+ maSize = rSize;
+ }
+
+
+ // DXSurface::~DXSurface
+
+
+ DXSurface::~DXSurface()
+ {
+ ImplRenderModuleGuard aGuard( mrRenderModule );
+
+#ifdef FAKE_MAX_NUMBER_TEXTURES
+ gNumSurfaces--;
+#endif
+ }
+
+
+ // DXSurface::selectTexture
+
+
+ bool DXSurface::selectTexture()
+ {
+ ImplRenderModuleGuard aGuard( mrRenderModule );
+ mrRenderModule.flushVertexCache();
+ sal::systools::COMReference<IDirect3DDevice9> pDevice(mrRenderModule.getDevice());
+
+ if( FAILED(pDevice->SetTexture(0,mpTexture.get())) )
+ return false;
+
+ return true;
+ }
+
+
+ // DXSurface::isValid
+
+
+ bool DXSurface::isValid()
+ {
+ ImplRenderModuleGuard aGuard( mrRenderModule );
+
+ if(!(mpTexture.is()))
+ return false;
+ return true;
+ }
+
+
+ // DXSurface::update
+
+
+ bool DXSurface::update( const ::basegfx::B2IPoint& rDestPos,
+ const ::basegfx::B2IRange& rSourceRect,
+ ::canvas::IColorBuffer& rSource )
+ {
+ ImplRenderModuleGuard aGuard( mrRenderModule );
+
+ // can't update if surface is not valid, that means
+ // either not existent nor restored...
+ if(!(isValid()))
+ return false;
+
+ D3DLOCKED_RECT aLockedRect;
+ RECT rect;
+ rect.left = std::max(sal_Int32(0),rDestPos.getX());
+ rect.top = std::max(sal_Int32(0),rDestPos.getY());
+ // to avoid interpolation artifacts from other textures,
+ // the surface manager allocates one pixel gap between
+ // them. Clear that to transparent.
+ rect.right = std::min(maSize.getWidth(),
+ rect.left + sal_Int32(rSourceRect.getWidth()+1));
+ rect.bottom = std::min(maSize.getHeight(),
+ rect.top + sal_Int32(rSourceRect.getHeight()+1));
+ const bool bClearRightColumn( rect.right < maSize.getWidth() );
+ const bool bClearBottomRow( rect.bottom < maSize.getHeight() );
+
+ if(SUCCEEDED(mpTexture->LockRect(0,&aLockedRect,&rect,D3DLOCK_NOSYSLOCK)))
+ {
+ if(sal_uInt8* pImage = rSource.lock())
+ {
+ switch( rSource.getFormat() )
+ {
+ case ::canvas::IColorBuffer::Format::A8R8G8B8:
+ {
+ const std::size_t nSourceBytesPerPixel(4);
+ const std::size_t nSourcePitchInBytes(rSource.getStride());
+ pImage += rSourceRect.getMinY()*nSourcePitchInBytes;
+ pImage += rSourceRect.getMinX()*nSourceBytesPerPixel;
+
+ // calculate the destination memory address
+ sal_uInt8 *pDst = static_cast<sal_uInt8*>(aLockedRect.pBits);
+
+ const sal_uInt32 nNumBytesToCopy(
+ static_cast<sal_uInt32>(
+ rSourceRect.getWidth())*
+ nSourceBytesPerPixel);
+ const sal_uInt64 nNumLines(rSourceRect.getHeight());
+
+ for(sal_uInt64 i=0; i<nNumLines; ++i)
+ {
+ memcpy(pDst,pImage,nNumBytesToCopy);
+
+ if( bClearRightColumn )
+ {
+ // to avoid interpolation artifacts
+ // from other textures, the surface
+ // manager allocates one pixel gap
+ // between them. Clear that to
+ // transparent.
+ pDst[nNumBytesToCopy] =
+ pDst[nNumBytesToCopy+1] =
+ pDst[nNumBytesToCopy+2] =
+ pDst[nNumBytesToCopy+3] = 0x00;
+ }
+ pDst += aLockedRect.Pitch;
+ pImage += nSourcePitchInBytes;
+ }
+
+ if( bClearBottomRow )
+ memset(pDst, 0, nNumBytesToCopy+4);
+ }
+ break;
+
+ case ::canvas::IColorBuffer::Format::X8R8G8B8:
+ {
+ const std::size_t nSourceBytesPerPixel(4);
+ const std::size_t nSourcePitchInBytes(rSource.getStride());
+ pImage += rSourceRect.getMinY()*nSourcePitchInBytes;
+ pImage += rSourceRect.getMinX()*nSourceBytesPerPixel;
+
+ // calculate the destination memory address
+ sal_uInt8 *pDst = static_cast<sal_uInt8*>(aLockedRect.pBits);
+
+ const sal_Int32 nNumLines(
+ sal::static_int_cast<sal_Int32>(rSourceRect.getHeight()));
+ const sal_Int32 nNumColumns(
+ sal::static_int_cast<sal_Int32>(rSourceRect.getWidth()));
+ for(sal_Int32 i=0; i<nNumLines; ++i)
+ {
+ sal_uInt32 *pSrc32 = reinterpret_cast<sal_uInt32 *>(pImage);
+ sal_uInt32 *pDst32 = reinterpret_cast<sal_uInt32 *>(pDst);
+ for(sal_Int32 j=0; j<nNumColumns; ++j)
+ pDst32[j] = 0xFF000000 | pSrc32[j];
+
+ if( bClearRightColumn )
+ pDst32[nNumColumns] = 0xFF000000;
+
+ pDst += aLockedRect.Pitch;
+ pImage += nSourcePitchInBytes;
+ }
+
+ if( bClearBottomRow )
+ memset(pDst, 0, 4*(nNumColumns+1));
+ }
+ break;
+
+ default:
+ ENSURE_OR_RETURN_FALSE(false,
+ "DXSurface::update(): Unknown/unimplemented buffer format" );
+ break;
+ }
+
+ rSource.unlock();
+ }
+
+ return SUCCEEDED(mpTexture->UnlockRect(0));
+ }
+
+ return true;
+ }
+
+ ::basegfx::B2ISize DXSurface::getSize()
+ {
+ return maSize;
+ }
+
+ DXRenderModule::DXRenderModule( const vcl::Window& rWindow ) :
+ mhWnd(nullptr),
+ mpDevice(),
+ mpDirect3D9(),
+ mpSwapChain(),
+ mpVertexBuffer(),
+ mpTexture(),
+ maVertexCache(),
+ mnCount(0),
+ mnBeginSceneCount(0),
+ mbCanUseDynamicTextures(false),
+ mbError( false ),
+ meType( PrimitiveType::Unknown ),
+ mad3dpp(),
+ maNumVertices( VERTEX_BUFFER_SIZE ),
+ maWriteIndex(0),
+ maReadIndex(0)
+ {
+ // TODO(P2): get rid of those fine-grained locking
+ ::osl::MutexGuard aGuard( maMutex );
+
+ if(!(create(rWindow)))
+ {
+ throw lang::NoSupportException( "Could not create DirectX device!" );
+ }
+
+ // allocate a single texture surface which can be used later.
+ // we also use this to calibrate the page size.
+ basegfx::B2IVector aPageSize(maPageSize);
+ while(true)
+ {
+ mpTexture = std::make_shared<DXSurface>(*this, basegfx::B2ISize(aPageSize.getX(), aPageSize.getY()));
+ if(mpTexture->isValid())
+ break;
+
+ aPageSize.setX(aPageSize.getX()>>1);
+ aPageSize.setY(aPageSize.getY()>>1);
+ if((aPageSize.getX() < MIN_TEXTURE_SIZE) ||
+ (aPageSize.getY() < MIN_TEXTURE_SIZE))
+ {
+ throw lang::NoSupportException(
+ "Could not create DirectX device - insufficient texture space!" );
+ }
+ }
+ maPageSize=aPageSize;
+
+ IDirect3DVertexBuffer9 *pVB(nullptr);
+ if( FAILED(mpDevice->CreateVertexBuffer(sizeof(dxvertex)*maNumVertices,
+ D3DUSAGE_DYNAMIC|D3DUSAGE_WRITEONLY,
+ D3DFVF_XYZRHW|D3DFVF_DIFFUSE|D3DFVF_TEX1,
+ D3DPOOL_DEFAULT,
+ &pVB,
+ nullptr)) )
+ {
+ throw lang::NoSupportException(
+ "Could not create DirectX device - out of memory!" );
+ }
+
+ mpVertexBuffer = sal::systools::COMReference<IDirect3DVertexBuffer9>(pVB, false);
+ }
+
+
+ // DXRenderModule::~DXRenderModule
+
+
+ DXRenderModule::~DXRenderModule()
+ {
+ disposing();
+ }
+
+
+ // DXRenderModule::disposing
+
+
+ void DXRenderModule::disposing()
+ {
+ if(!mhWnd)
+ return;
+
+ mpTexture.reset();
+ mpWindow.disposeAndClear();
+ mhWnd=nullptr;
+
+ // refrain from releasing the DX9 objects. We're the only
+ // ones holding references to them, and it might be
+ // dangerous to destroy the DX9 device, before all other
+ // objects are dead.
+ }
+
+
+ // DXRenderModule::create
+
+
+ bool DXRenderModule::create( const vcl::Window& rWindow )
+ {
+ // TODO(P2): get rid of those fine-grained locking
+ ::osl::MutexGuard aGuard( maMutex );
+
+ // TODO(F2): since we would like to share precious hardware
+ // resources, the direct3d9 object should be global. each new
+ // request for a canvas should only create a new swapchain.
+ mpDirect3D9 = sal::systools::COMReference<IDirect3D9>(
+ Direct3DCreate9(D3D_SDK_VERSION), false);
+ if(!mpDirect3D9.is())
+ return false;
+
+ maVertexCache.reserve( 1024 );
+
+ mpWindow.disposeAndClear();
+ mpWindow.reset( VclPtr<SystemChildWindow>::Create(
+ const_cast<vcl::Window *>(&rWindow), 0) );
+
+ // system child window must not receive mouse events
+ mpWindow->SetMouseTransparent( true );
+
+ // parent should receive paint messages as well
+ mpWindow->SetParentClipMode(ParentClipMode::NoClip);
+
+ // the system child window must not clear its background
+ mpWindow->EnableEraseBackground( false );
+
+ mpWindow->SetControlForeground();
+ mpWindow->SetControlBackground();
+
+ const SystemEnvData *pData = mpWindow->GetSystemData();
+ const HWND hwnd(reinterpret_cast<HWND>(pData->hWnd));
+ mhWnd = hwnd;
+
+ ENSURE_OR_THROW( IsWindow( mhWnd ),
+ "DXRenderModule::create() No valid HWND given." );
+
+ // retrieve position and size of the parent window
+ const ::Size &rSizePixel(rWindow.GetSizePixel());
+
+ // remember the size of the parent window, since we
+ // need to use this for our child window.
+ maSize.setWidth(sal_Int32(rSizePixel.Width()));
+ maSize.setHeight(sal_Int32(rSizePixel.Height()));
+
+ // let the child window cover the same size as the parent window.
+ mpWindow->setPosSizePixel(0, 0, maSize.getWidth(),maSize.getHeight());
+
+ // create a device from the direct3d9 object.
+ if(!(createDevice()))
+ {
+ mpWindow.disposeAndClear();
+ return false;
+ }
+
+ mpWindow->Show();
+
+ return true;
+ }
+
+
+ // DXRenderModule::verifyDevice
+
+
+ bool DXRenderModule::verifyDevice( const UINT nAdapter )
+ {
+ ENSURE_OR_THROW( mpDirect3D9.is(),
+ "DXRenderModule::verifyDevice() No valid device." );
+
+ // ask direct3d9 about the capabilities of hardware devices on a specific adapter.
+ // here we decide if the underlying hardware of the machine 'is good enough'.
+ // since we only need a tiny little fraction of what could be used, this
+ // is basically a no-op.
+ D3DCAPS9 aCaps;
+ if(FAILED(mpDirect3D9->GetDeviceCaps(nAdapter,D3DDEVTYPE_HAL,&aCaps)))
+ return false;
+ if(!(aCaps.MaxTextureWidth))
+ return false;
+ if(!(aCaps.MaxTextureHeight))
+ return false;
+ maPageSize = ::basegfx::B2IVector(aCaps.MaxTextureWidth,aCaps.MaxTextureHeight);
+
+ // check device against allow & denylist entries
+ D3DADAPTER_IDENTIFIER9 aIdent;
+ if(FAILED(mpDirect3D9->GetAdapterIdentifier(nAdapter,0,&aIdent)))
+ return false;
+
+ DXCanvasItem aConfigItem;
+ DXCanvasItem::DeviceInfo aInfo;
+ aInfo.nVendorId = aIdent.VendorId;
+ aInfo.nDeviceId = aIdent.DeviceId;
+ aInfo.nDeviceSubSysId = aIdent.SubSysId;
+ aInfo.nDeviceRevision = aIdent.Revision;
+
+ aInfo.nDriverId = HIWORD(aIdent.DriverVersion.HighPart);
+ aInfo.nDriverVersion = LOWORD(aIdent.DriverVersion.HighPart);
+ aInfo.nDriverSubVersion = HIWORD(aIdent.DriverVersion.LowPart);
+ aInfo.nDriverBuildId = LOWORD(aIdent.DriverVersion.LowPart);
+
+ if( !aConfigItem.isDeviceUsable(aInfo) )
+ return false;
+
+ if( aConfigItem.isDenylistCurrentDevice() )
+ {
+ aConfigItem.denylistDevice(aInfo);
+ return false;
+ }
+
+ aConfigItem.adaptMaxTextureSize(maPageSize);
+
+ mbCanUseDynamicTextures = (aCaps.Caps2 & D3DCAPS2_DYNAMICTEXTURES) != 0;
+
+ return true;
+ }
+
+
+ // DXRenderModule::createDevice
+
+
+ bool DXRenderModule::createDevice()
+ {
+ // we expect that the caller provides us with a valid HWND
+ ENSURE_OR_THROW( IsWindow(mhWnd),
+ "DXRenderModule::createDevice() No valid HWND given." );
+
+ // we expect that the caller already created the direct3d9 object.
+ ENSURE_OR_THROW( mpDirect3D9.is(),
+ "DXRenderModule::createDevice() no direct3d?." );
+
+ // find the adapter identifier from the window.
+ const UINT aAdapter(getAdapterFromWindow());
+ if(aAdapter == static_cast<UINT>(-1))
+ return false;
+
+ // verify that device possibly works
+ if( !verifyDevice(aAdapter) )
+ return false;
+
+ // query the display mode from the selected adapter.
+ // we'll later request the backbuffer format to be same
+ // same as the display format.
+ D3DDISPLAYMODE d3ddm;
+ mpDirect3D9->GetAdapterDisplayMode(aAdapter,&d3ddm);
+
+ // we need to use D3DSWAPEFFECT_COPY here since the canvas-api has
+ // basically nothing to do with efficient resource handling. it tries
+ // to avoid drawing whenever possible, which is simply not the most
+ // efficient way we could leverage the hardware in this case. it would
+ // be far better to redraw the backbuffer each time we would like to
+ // display the content of the backbuffer, but we need to face reality
+ // here and follow how the canvas was designed.
+
+ // Strictly speaking, we don't need a full screen worth of
+ // backbuffer here. We could also scale dynamically with
+ // the current window size, but this will make it
+ // necessary to temporarily have two buffers while copying
+ // from the old to the new one. What's more, at the time
+ // we need a larger buffer, DX might not have sufficient
+ // resources available, and we're then left with too small
+ // a back buffer, and no way of falling back to a
+ // different canvas implementation.
+ ZeroMemory( &mad3dpp, sizeof(mad3dpp) );
+ mad3dpp.BackBufferWidth = std::max(maSize.getWidth(), sal_Int32(d3ddm.Width));
+ mad3dpp.BackBufferHeight = std::max(maSize.getHeight(), sal_Int32(d3ddm.Height));
+ mad3dpp.BackBufferCount = 1;
+ mad3dpp.Windowed = TRUE;
+ mad3dpp.SwapEffect = D3DSWAPEFFECT_COPY;
+ mad3dpp.BackBufferFormat = d3ddm.Format;
+ mad3dpp.EnableAutoDepthStencil = FALSE;
+ mad3dpp.hDeviceWindow = mhWnd;
+ mad3dpp.PresentationInterval = D3DPRESENT_INTERVAL_ONE;
+
+ // now create the device, first try hardware vertex processing,
+ // then software vertex processing. if both queries fail, we give up
+ // and indicate failure.
+ IDirect3DDevice9 *pDevice(nullptr);
+ if(FAILED(mpDirect3D9->CreateDevice(aAdapter,
+ D3DDEVTYPE_HAL,
+ mhWnd,
+ D3DCREATE_HARDWARE_VERTEXPROCESSING|
+ D3DCREATE_MULTITHREADED|D3DCREATE_FPU_PRESERVE,
+ &mad3dpp,
+ &pDevice)))
+ if(FAILED(mpDirect3D9->CreateDevice(aAdapter,
+ D3DDEVTYPE_HAL,
+ mhWnd,
+ D3DCREATE_SOFTWARE_VERTEXPROCESSING|
+ D3DCREATE_MULTITHREADED|D3DCREATE_FPU_PRESERVE,
+ &mad3dpp,
+ &pDevice)))
+ return false;
+
+ // got it, store it in a safe place...
+ mpDevice = sal::systools::COMReference<IDirect3DDevice9>(pDevice, false);
+
+ // After CreateDevice, the first swap chain already exists, so just get it...
+ IDirect3DSwapChain9 *pSwapChain(nullptr);
+ pDevice->GetSwapChain(0,&pSwapChain);
+ mpSwapChain = sal::systools::COMReference<IDirect3DSwapChain9>(pSwapChain, false);
+ if( !mpSwapChain.is() )
+ return false;
+
+ // clear the render target [which is the backbuffer in this case].
+ // we are forced to do this once, and furthermore right now.
+ // please note that this is only possible since we created the
+ // backbuffer with copy semantics [the content is preserved after
+ // calls to Present()], which is an unnecessarily expensive operation.
+ LPDIRECT3DSURFACE9 pBackBuffer = nullptr;
+ mpSwapChain->GetBackBuffer(0,D3DBACKBUFFER_TYPE_MONO,&pBackBuffer);
+ mpDevice->SetRenderTarget( 0, pBackBuffer );
+ mpDevice->Clear(0,nullptr,D3DCLEAR_TARGET,0,1.0f,0);
+ pBackBuffer->Release();
+
+ return true;
+ }
+
+
+ // DXRenderModule::createSystemMemorySurface
+
+
+ sal::systools::COMReference<IDirect3DSurface9> DXRenderModule::createSystemMemorySurface(const ::basegfx::B2ISize& rSize)
+ {
+ if(isDisposed())
+ return sal::systools::COMReference<IDirect3DSurface9>(nullptr);
+
+ // please note that D3DFMT_X8R8G8B8 is the only format we're
+ // able to choose here, since GetDC() doesn't support any
+ // other 32bit-format.
+ IDirect3DSurface9 *pSurface(nullptr);
+ if( FAILED(mpDevice->CreateOffscreenPlainSurface(
+ rSize.getWidth(),
+ rSize.getHeight(),
+ D3DFMT_X8R8G8B8,
+ D3DPOOL_SYSTEMMEM,
+ &pSurface,
+ nullptr)) )
+ {
+ throw lang::NoSupportException(
+ "Could not create offscreen surface - out of mem!" );
+ }
+
+ return sal::systools::COMReference<IDirect3DSurface9>(pSurface, false);
+ }
+
+
+ // DXRenderModule::flip
+
+
+ bool DXRenderModule::flip( const ::basegfx::B2IRectangle& rUpdateArea,
+ const ::basegfx::B2IRectangle& /*rCurrWindowArea*/ )
+ {
+ // TODO(P2): get rid of those fine-grained locking
+ ::osl::MutexGuard aGuard( maMutex );
+
+ if(isDisposed() || !mpSwapChain.is())
+ return false;
+
+ flushVertexCache();
+
+ // TODO(P2): Might be faster to actually pass update area here
+ RECT aRect =
+ {
+ rUpdateArea.getMinX(),
+ rUpdateArea.getMinY(),
+ rUpdateArea.getMaxX(),
+ rUpdateArea.getMaxY()
+ };
+ HRESULT hr(mpSwapChain->Present(&aRect,&aRect,nullptr,nullptr,0));
+ if(FAILED(hr))
+ {
+ if(hr != D3DERR_DEVICELOST)
+ return false;
+
+ // interestingly enough, sometimes the Reset() below
+ // *still* causes DeviceLost errors. So, cycle until
+ // DX was kind enough to really reset the device...
+ do
+ {
+ mpVertexBuffer.clear();
+ hr = mpDevice->Reset(&mad3dpp);
+ if(SUCCEEDED(hr))
+ {
+ IDirect3DVertexBuffer9 *pVB(nullptr);
+ if( FAILED(mpDevice->CreateVertexBuffer(sizeof(dxvertex)*maNumVertices,
+ D3DUSAGE_DYNAMIC|D3DUSAGE_WRITEONLY,
+ D3DFVF_XYZRHW|D3DFVF_DIFFUSE|D3DFVF_TEX1,
+ D3DPOOL_DEFAULT,
+ &pVB,
+ nullptr)) )
+ {
+ throw lang::NoSupportException(
+ "Could not create DirectX device - out of memory!" );
+ }
+ mpVertexBuffer = sal::systools::COMReference<IDirect3DVertexBuffer9>(pVB, false);
+
+ // retry after the restore
+ if(SUCCEEDED(mpSwapChain->Present(&aRect,&aRect,nullptr,nullptr,0)))
+ return true;
+ }
+
+ osl::Thread::wait(std::chrono::seconds(1));
+ }
+ while(hr == D3DERR_DEVICELOST);
+
+ return false;
+ }
+
+ return true;
+ }
+
+
+ // DXRenderModule::screenShot
+
+
+ void DXRenderModule::screenShot()
+ {
+ }
+
+
+ // DXRenderModule::resize
+
+
+ void DXRenderModule::resize( const ::basegfx::B2IRange& rect )
+ {
+ // TODO(P2): get rid of those fine-grained locking
+ ::osl::MutexGuard aGuard( maMutex );
+
+ if(isDisposed())
+ return;
+
+ // don't do anything if the size didn't change.
+ if(maSize.getWidth() == static_cast<sal_Int32>(rect.getWidth()) &&
+ maSize.getHeight() == static_cast<sal_Int32>(rect.getHeight()))
+ return;
+
+ // TODO(Q2): use numeric cast to prevent overflow
+ maSize.setWidth(sal_Int32(rect.getWidth()));
+ maSize.setHeight(sal_Int32(rect.getHeight()));
+
+ mpWindow->setPosSizePixel(0, 0, maSize.getWidth(), maSize.getHeight());
+
+ // resize back buffer, if necessary
+
+
+ // don't attempt to create anything if the
+ // requested size is NULL.
+ if(!(maSize.getWidth()))
+ return;
+ if(!(maSize.getHeight()))
+ return;
+
+ // backbuffer too small (might happen, if window is
+ // maximized across multiple monitors)
+ if( sal_Int32(mad3dpp.BackBufferWidth) < maSize.getWidth() ||
+ sal_Int32(mad3dpp.BackBufferHeight) < maSize.getHeight() )
+ {
+ mad3dpp.BackBufferWidth = maSize.getWidth();
+ mad3dpp.BackBufferHeight = maSize.getHeight();
+
+ // clear before, save resources
+ mpSwapChain.clear();
+
+ IDirect3DSwapChain9 *pSwapChain(nullptr);
+ if(FAILED(mpDevice->CreateAdditionalSwapChain(&mad3dpp,&pSwapChain)))
+ return;
+ mpSwapChain = sal::systools::COMReference<IDirect3DSwapChain9>(pSwapChain, false);
+
+ // clear the render target [which is the backbuffer in this case].
+ // we are forced to do this once, and furthermore right now.
+ // please note that this is only possible since we created the
+ // backbuffer with copy semantics [the content is preserved after
+ // calls to Present()], which is an unnecessarily expensive operation.
+ LPDIRECT3DSURFACE9 pBackBuffer = nullptr;
+ mpSwapChain->GetBackBuffer(0,D3DBACKBUFFER_TYPE_MONO,&pBackBuffer);
+ mpDevice->SetRenderTarget( 0, pBackBuffer );
+ mpDevice->Clear(0,nullptr,D3DCLEAR_TARGET,0,1.0f,0);
+ pBackBuffer->Release();
+ }
+ }
+
+
+ // DXRenderModule::getPageSize
+
+
+ ::basegfx::B2IVector DXRenderModule::getPageSize()
+ {
+ // TODO(P2): get rid of those fine-grained locking
+ ::osl::MutexGuard aGuard( maMutex );
+ return maPageSize;
+ }
+
+
+ // DXRenderModule::createSurface
+
+
+ std::shared_ptr<canvas::ISurface> DXRenderModule::createSurface( const ::basegfx::B2IVector& surfaceSize )
+ {
+ // TODO(P2): get rid of those fine-grained locking
+ ::osl::MutexGuard aGuard( maMutex );
+
+ if(isDisposed())
+ return std::shared_ptr<canvas::ISurface>();
+
+ const ::basegfx::B2IVector& rPageSize( getPageSize() );
+ ::basegfx::B2ISize aSize(surfaceSize);
+ if(!(aSize.getWidth()))
+ aSize.setWidth(rPageSize.getX());
+ if(!(aSize.getHeight()))
+ aSize.setHeight(rPageSize.getY());
+
+ if(mpTexture.use_count() == 1)
+ return mpTexture;
+
+ return std::make_shared<DXSurface>(*this,aSize);
+ }
+
+
+ // DXRenderModule::beginPrimitive
+
+
+ void DXRenderModule::beginPrimitive( PrimitiveType eType )
+ {
+ // TODO(P2): get rid of those fine-grained locking
+ ::osl::MutexGuard aGuard( maMutex );
+
+ if(isDisposed())
+ return;
+
+ ENSURE_OR_THROW( !mnBeginSceneCount,
+ "DXRenderModule::beginPrimitive(): nested call" );
+
+ ++mnBeginSceneCount;
+ meType=eType;
+ mnCount=0;
+ }
+
+
+ // DXRenderModule::endPrimitive
+
+
+ void DXRenderModule::endPrimitive()
+ {
+ // TODO(P2): get rid of those fine-grained locking
+ ::osl::MutexGuard aGuard( maMutex );
+
+ if(isDisposed())
+ return;
+
+ --mnBeginSceneCount;
+ meType = PrimitiveType::Unknown;
+ mnCount = 0;
+ }
+
+
+ // DXRenderModule::pushVertex
+
+
+ void DXRenderModule::pushVertex( const ::canvas::Vertex& vertex )
+ {
+ // TODO(P2): get rid of those fine-grained locking
+ ::osl::MutexGuard aGuard( maMutex );
+
+ if(isDisposed())
+ return;
+
+ switch(meType)
+ {
+ case PrimitiveType::Triangle:
+ {
+ maVertexCache.push_back(vertex);
+ ++mnCount;
+ mnCount &= 3;
+ break;
+ }
+
+ case PrimitiveType::Quad:
+ {
+ if(mnCount == 3)
+ {
+ const std::size_t size(maVertexCache.size());
+ ::canvas::Vertex v0(maVertexCache[size-1]);
+ ::canvas::Vertex v2(maVertexCache[size-3]);
+ maVertexCache.push_back(v0);
+ maVertexCache.push_back(vertex);
+ maVertexCache.push_back(v2);
+ mnCount=0;
+ }
+ else
+ {
+ maVertexCache.push_back(vertex);
+ ++mnCount;
+ }
+ break;
+ }
+
+ default:
+ SAL_WARN("canvas.directx", "DXRenderModule::pushVertex(): unexpected primitive type");
+ break;
+ }
+ }
+
+
+ // DXRenderModule::isError
+
+
+ bool DXRenderModule::isError()
+ {
+ // TODO(P2): get rid of those fine-grained locking
+ ::osl::MutexGuard aGuard( maMutex );
+
+ return mbError;
+ }
+
+
+ // DXRenderModule::getAdapterFromWindow
+
+
+ UINT DXRenderModule::getAdapterFromWindow()
+ {
+ HMONITOR hMonitor(MonitorFromWindow(mhWnd, MONITOR_DEFAULTTONEAREST));
+ UINT aAdapterCount(mpDirect3D9->GetAdapterCount());
+ for(UINT i=0; i<aAdapterCount; ++i)
+ if(hMonitor == mpDirect3D9->GetAdapterMonitor(i))
+ return i;
+ return static_cast<UINT>(-1);
+ }
+
+
+ // DXRenderModule::commitVertexCache
+
+
+ void DXRenderModule::commitVertexCache()
+ {
+ if(maReadIndex != maWriteIndex)
+ {
+ const std::size_t nVertexStride = sizeof(dxvertex);
+ const unsigned int nNumVertices = maWriteIndex-maReadIndex;
+ const unsigned int nNumPrimitives = nNumVertices / 3;
+
+ if(FAILED(mpDevice->SetStreamSource(0,mpVertexBuffer.get(),0,nVertexStride)))
+ return;
+
+ if(FAILED(mpDevice->SetFVF(D3DFVF_XYZRHW|D3DFVF_DIFFUSE|D3DFVF_TEX1)))
+ return;
+
+ if(FAILED(mpDevice->BeginScene()))
+ return;
+
+ mbError |= FAILED(mpDevice->DrawPrimitive(D3DPT_TRIANGLELIST,maReadIndex,nNumPrimitives));
+ mbError |= FAILED(mpDevice->EndScene());
+
+ maReadIndex += nNumVertices;
+ }
+ }
+
+
+ // DXRenderModule::flushVertexCache
+
+
+ void DXRenderModule::flushVertexCache()
+ {
+ if(maVertexCache.empty())
+ return;
+
+ mbError=true;
+
+ if( FAILED(mpDevice->SetRenderState(D3DRS_LIGHTING,FALSE)))
+ return;
+
+ // enable texture alpha blending
+ if( FAILED(mpDevice->SetRenderState(D3DRS_ALPHABLENDENABLE,TRUE)))
+ return;
+
+ mpDevice->SetSamplerState(0,D3DSAMP_MAGFILTER,D3DTEXF_LINEAR);
+ mpDevice->SetSamplerState(0,D3DSAMP_MINFILTER,D3DTEXF_LINEAR);
+ mpDevice->SetSamplerState(0,D3DSAMP_ADDRESSU ,D3DTADDRESS_CLAMP );
+ mpDevice->SetSamplerState(0,D3DSAMP_ADDRESSV ,D3DTADDRESS_CLAMP );
+
+ // configure the fixed-function pipeline.
+ // the only 'feature' we need here is to modulate the alpha-channels
+ // from the texture and the interpolated diffuse color. the result
+ // will then be blended with the backbuffer.
+ // fragment color = texture color * diffuse.alpha.
+ mpDevice->SetTextureStageState(0,D3DTSS_ALPHAOP,D3DTOP_MODULATE);
+ mpDevice->SetTextureStageState(0,D3DTSS_ALPHAARG1,D3DTA_TEXTURE);
+ mpDevice->SetTextureStageState(0,D3DTSS_ALPHAARG2,D3DTA_DIFFUSE);
+
+ // normal combination of object...
+ if( FAILED(mpDevice->SetRenderState(D3DRS_SRCBLEND,D3DBLEND_SRCALPHA)) )
+ return;
+
+ // ..and background color
+ if( FAILED(mpDevice->SetRenderState(D3DRS_DESTBLEND,D3DBLEND_INVSRCALPHA)) )
+ return;
+
+ // disable backface culling; this enables us to mirror sprites
+ // by simply reverting the triangles, which, with enabled
+ // culling, would be invisible otherwise
+ if( FAILED(mpDevice->SetRenderState(D3DRS_CULLMODE,D3DCULL_NONE)) )
+ return;
+
+ mbError=false;
+
+ std::size_t nSize(maVertexCache.size());
+ const std::size_t nVertexStride = sizeof(dxvertex);
+
+ const ::basegfx::B2IVector aPageSize(getPageSize());
+ const float nHalfPixelSizeX(0.5f/aPageSize.getX());
+ const float nHalfPixelSizeY(0.5f/aPageSize.getY());
+ vertexCache_t::const_iterator it(maVertexCache.begin());
+
+ while( nSize )
+ {
+ DWORD dwLockFlags(D3DLOCK_NOOVERWRITE);
+
+ // Check to see if there's space for the current set of
+ // vertices in the buffer.
+ if( maNumVertices - maWriteIndex < nSize )
+ {
+ commitVertexCache();
+ dwLockFlags = D3DLOCK_DISCARD;
+ maWriteIndex = 0;
+ maReadIndex = 0;
+ }
+
+ dxvertex *vertices(nullptr);
+ const std::size_t nNumVertices(
+ std::min(maNumVertices - maWriteIndex,
+ nSize));
+ if(FAILED(mpVertexBuffer->Lock(maWriteIndex*nVertexStride,
+ nNumVertices*nVertexStride,
+ reinterpret_cast<void **>(&vertices),
+ dwLockFlags)))
+ return;
+
+ std::size_t nIndex(0);
+ while( nIndex < nNumVertices )
+ {
+ dxvertex &dest = vertices[nIndex++];
+ dest.x=it->x;
+ dest.y=it->y;
+ dest.z=it->z;
+ dest.rhw=1;
+ const sal_uInt32 alpha(static_cast<sal_uInt32>(it->a*255.0f));
+ dest.diffuse=D3DCOLOR_ARGB(alpha,255,255,255);
+ dest.u=static_cast<float>(it->u + nHalfPixelSizeX);
+ dest.v=static_cast<float>(it->v + nHalfPixelSizeY);
+ ++it;
+ }
+
+ mpVertexBuffer->Unlock();
+
+ // Advance to the next position in the vertex buffer.
+ maWriteIndex += nNumVertices;
+ nSize -= nNumVertices;
+
+ commitVertexCache();
+ }
+
+ maVertexCache.clear();
+ }
+ }
+
+
+ // createRenderModule
+
+
+ IDXRenderModuleSharedPtr createRenderModule( const vcl::Window& rParent )
+ {
+ return std::make_shared<DXRenderModule>(rParent);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/directx/dx_bitmap.cxx b/canvas/source/directx/dx_bitmap.cxx
new file mode 100644
index 0000000000..9e5f2348fa
--- /dev/null
+++ b/canvas/source/directx/dx_bitmap.cxx
@@ -0,0 +1,204 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <memory>
+
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/range/b2irange.hxx>
+#include <comphelper/diagnose_ex.hxx>
+
+#include "dx_bitmap.hxx"
+#include "dx_graphicsprovider.hxx"
+#include "dx_impltools.hxx"
+
+using namespace ::com::sun::star;
+
+namespace dxcanvas
+{
+
+ // DXBitmap::DXBitmap
+
+
+ DXBitmap::DXBitmap( const BitmapSharedPtr& rBitmap,
+ bool bWithAlpha ) :
+ mpGdiPlusUser( GDIPlusUser::createInstance() ),
+ maSize(rBitmap->GetWidth(),rBitmap->GetHeight()),
+ mpBitmap(rBitmap),
+ mpGraphics(tools::createGraphicsFromBitmap(mpBitmap)),
+ mbAlpha(bWithAlpha)
+ {
+ }
+
+ DXBitmap::DXBitmap( const ::basegfx::B2ISize& rSize,
+ bool bWithAlpha ) :
+ mpGdiPlusUser( GDIPlusUser::createInstance() ),
+ maSize(rSize),
+ mpBitmap(),
+ mpGraphics(),
+ mbAlpha(bWithAlpha)
+ {
+ // create container for pixel data
+ if(mbAlpha)
+ {
+ mpBitmap = std::make_shared<Gdiplus::Bitmap>(
+ maSize.getWidth(),
+ maSize.getHeight(),
+ PixelFormat32bppARGB);
+ }
+ else
+ {
+ mpBitmap = std::make_shared<Gdiplus::Bitmap>(
+ maSize.getWidth(),
+ maSize.getHeight(),
+ PixelFormat24bppRGB);
+ }
+
+ mpGraphics = tools::createGraphicsFromBitmap(mpBitmap);
+ }
+
+ BitmapSharedPtr DXBitmap::getBitmap() const
+ {
+ return mpBitmap;
+ }
+
+ GraphicsSharedPtr DXBitmap::getGraphics()
+ {
+ return mpGraphics;
+ }
+
+ ::basegfx::B2ISize DXBitmap::getSize() const
+ {
+ return maSize;
+ }
+
+ bool DXBitmap::hasAlpha() const
+ {
+ return mbAlpha;
+ }
+
+ uno::Sequence< sal_Int8 > DXBitmap::getData( rendering::IntegerBitmapLayout& /*bitmapLayout*/,
+ const geometry::IntegerRectangle2D& rect )
+ {
+ uno::Sequence< sal_Int8 > aRes( (rect.X2-rect.X1)*(rect.Y2-rect.Y1)*4 ); // TODO(F1): Be format-agnostic here
+
+ const Gdiplus::Rect aRect( tools::gdiPlusRectFromIntegerRectangle2D( rect ) );
+
+ Gdiplus::BitmapData aBmpData;
+ aBmpData.Width = rect.X2-rect.X1;
+ aBmpData.Height = rect.Y2-rect.Y1;
+ aBmpData.Stride = 4*aBmpData.Width;
+ aBmpData.PixelFormat = PixelFormat32bppARGB;
+ aBmpData.Scan0 = aRes.getArray();
+
+ // TODO(F1): Support more pixel formats natively
+
+ // read data from bitmap
+ if( Gdiplus::Ok != mpBitmap->LockBits( &aRect,
+ Gdiplus::ImageLockModeRead | Gdiplus::ImageLockModeUserInputBuf,
+ PixelFormat32bppARGB, // TODO(F1): Adapt to
+ // Graphics native
+ // format/change
+ // getMemoryLayout
+ &aBmpData ) )
+ {
+ // failed to lock, bail out
+ return uno::Sequence< sal_Int8 >();
+ }
+
+ mpBitmap->UnlockBits( &aBmpData );
+
+ return aRes;
+ }
+
+ void DXBitmap::setData( const uno::Sequence< sal_Int8 >& data,
+ const rendering::IntegerBitmapLayout& /*bitmapLayout*/,
+ const geometry::IntegerRectangle2D& rect )
+ {
+ const Gdiplus::Rect aRect( tools::gdiPlusRectFromIntegerRectangle2D( rect ) );
+
+ Gdiplus::BitmapData aBmpData;
+ aBmpData.Width = rect.X2-rect.X1;
+ aBmpData.Height = rect.Y2-rect.Y1;
+ aBmpData.Stride = 4*aBmpData.Width;
+ aBmpData.PixelFormat = PixelFormat32bppARGB;
+ aBmpData.Scan0 = const_cast<sal_Int8 *>(data.getConstArray());
+ // const_cast is safe, "Gdiplus::ImageLockModeWrite
+ // | Gdiplus::ImageLockModeUserInputBuf makes the data go from
+ // BitmapData into Bitmap", says Thorsten
+
+ // TODO(F1): Support more pixel formats natively
+
+ if( Gdiplus::Ok != mpBitmap->LockBits( &aRect,
+ Gdiplus::ImageLockModeWrite | Gdiplus::ImageLockModeUserInputBuf,
+ PixelFormat32bppARGB, // TODO: Adapt to
+ // Graphics native
+ // format/change
+ // getMemoryLayout
+ &aBmpData ) )
+ {
+ throw uno::RuntimeException("Internal error while writing BitmapData into Bitmap");
+ }
+
+ // commit data to bitmap
+ mpBitmap->UnlockBits( &aBmpData );
+ }
+
+ void DXBitmap::setPixel( const uno::Sequence< sal_Int8 >& color,
+ const rendering::IntegerBitmapLayout& /*bitmapLayout*/,
+ const geometry::IntegerPoint2D& pos )
+ {
+ const geometry::IntegerSize2D aSize( maSize.getWidth(),maSize.getHeight() );
+
+ ENSURE_ARG_OR_THROW( pos.X >= 0 && pos.X < aSize.Width,
+ "CanvasHelper::setPixel: X coordinate out of bounds" );
+ ENSURE_ARG_OR_THROW( pos.Y >= 0 && pos.Y < aSize.Height,
+ "CanvasHelper::setPixel: Y coordinate out of bounds" );
+ ENSURE_ARG_OR_THROW( color.getLength() > 3,
+ "CanvasHelper::setPixel: not enough color components" );
+
+ if( Gdiplus::Ok != mpBitmap->SetPixel( pos.X, pos.Y,
+ Gdiplus::Color( tools::sequenceToArgb( color ))))
+ {
+ throw uno::RuntimeException("SetPixel called with invalid x,y points or color");
+ }
+ }
+
+ uno::Sequence< sal_Int8 > DXBitmap::getPixel( rendering::IntegerBitmapLayout& /*bitmapLayout*/,
+ const geometry::IntegerPoint2D& pos )
+ {
+ const geometry::IntegerSize2D aSize( maSize.getWidth(),maSize.getHeight() );
+
+ ENSURE_ARG_OR_THROW( pos.X >= 0 && pos.X < aSize.Width,
+ "CanvasHelper::getPixel: X coordinate out of bounds" );
+ ENSURE_ARG_OR_THROW( pos.Y >= 0 && pos.Y < aSize.Height,
+ "CanvasHelper::getPixel: Y coordinate out of bounds" );
+
+ Gdiplus::Color aColor;
+
+ if( Gdiplus::Ok != mpBitmap->GetPixel( pos.X, pos.Y, &aColor ) )
+ return uno::Sequence< sal_Int8 >();
+
+ return tools::argbToIntSequence(aColor.GetValue());
+ }
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/directx/dx_bitmap.hxx b/canvas/source/directx/dx_bitmap.hxx
new file mode 100644
index 0000000000..b27da5cfa6
--- /dev/null
+++ b/canvas/source/directx/dx_bitmap.hxx
@@ -0,0 +1,82 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/rendering/XCanvas.hpp>
+#include <com/sun/star/rendering/XIntegerBitmap.hpp>
+#include <basegfx/vector/b2ivector.hxx>
+#include <basegfx/point/b2dpoint.hxx>
+#include <basegfx/range/b2drange.hxx>
+#include <memory>
+#include "dx_winstuff.hxx"
+#include "dx_ibitmap.hxx"
+#include "dx_graphicsprovider.hxx"
+#include "dx_gdiplususer.hxx"
+
+namespace dxcanvas
+{
+ class DXBitmap : public IBitmap
+ {
+ public:
+ DXBitmap( const BitmapSharedPtr& rBitmap, bool bWithAlpha );
+ DXBitmap( const ::basegfx::B2ISize& rSize, bool bWithAlpha );
+
+ virtual GraphicsSharedPtr getGraphics() override;
+
+ virtual BitmapSharedPtr getBitmap() const override;
+ virtual ::basegfx::B2ISize getSize() const override;
+ virtual bool hasAlpha() const override;
+
+ css::uno::Sequence< sal_Int8 > getData(
+ css::rendering::IntegerBitmapLayout& bitmapLayout,
+ const css::geometry::IntegerRectangle2D& rect ) override;
+
+ void setData(
+ const css::uno::Sequence< sal_Int8 >& data,
+ const css::rendering::IntegerBitmapLayout& bitmapLayout,
+ const css::geometry::IntegerRectangle2D& rect ) override;
+
+ void setPixel(
+ const css::uno::Sequence< sal_Int8 >& color,
+ const css::rendering::IntegerBitmapLayout& bitmapLayout,
+ const css::geometry::IntegerPoint2D& pos ) override;
+
+ css::uno::Sequence< sal_Int8 > getPixel(
+ css::rendering::IntegerBitmapLayout& bitmapLayout,
+ const css::geometry::IntegerPoint2D& pos ) override;
+
+ private:
+ // Refcounted global GDI+ state container
+ GDIPlusUserSharedPtr mpGdiPlusUser;
+
+ // size of this image in pixels [integral unit]
+ ::basegfx::B2ISize maSize;
+
+ BitmapSharedPtr mpBitmap;
+ GraphicsSharedPtr mpGraphics;
+
+ // true if the bitmap contains an alpha channel
+ bool mbAlpha;
+ };
+
+ typedef std::shared_ptr< DXBitmap > DXBitmapSharedPtr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/directx/dx_bitmapcanvashelper.cxx b/canvas/source/directx/dx_bitmapcanvashelper.cxx
new file mode 100644
index 0000000000..f82fa0ac3a
--- /dev/null
+++ b/canvas/source/directx/dx_bitmapcanvashelper.cxx
@@ -0,0 +1,221 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+#include <algorithm>
+
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/point/b2dpoint.hxx>
+#include <basegfx/utils/canvastools.hxx>
+#include <com/sun/star/rendering/CompositeOperation.hpp>
+#include <com/sun/star/rendering/PathCapType.hpp>
+#include <com/sun/star/rendering/PathJoinType.hpp>
+#include <com/sun/star/rendering/RepaintResult.hpp>
+#include <com/sun/star/rendering/TexturingMode.hpp>
+#include <rtl/math.hxx>
+#include <comphelper/diagnose_ex.hxx>
+
+#include <canvas/canvastools.hxx>
+
+#include "dx_bitmapcanvashelper.hxx"
+#include "dx_canvasfont.hxx"
+#include "dx_impltools.hxx"
+#include "dx_spritecanvas.hxx"
+#include "dx_textlayout.hxx"
+
+
+using namespace ::com::sun::star;
+
+namespace dxcanvas
+{
+ BitmapCanvasHelper::BitmapCanvasHelper() :
+ mpTarget()
+ {}
+
+ void BitmapCanvasHelper::disposing()
+ {
+ mpTarget.reset();
+ CanvasHelper::disposing();
+ }
+
+ void BitmapCanvasHelper::setTarget( const IBitmapSharedPtr& rTarget )
+ {
+ ENSURE_OR_THROW( rTarget,
+ "BitmapCanvasHelper::setTarget(): Invalid target" );
+ ENSURE_OR_THROW( !mpTarget,
+ "BitmapCanvasHelper::setTarget(): target set, old target would be overwritten" );
+
+ mpTarget = rTarget;
+ CanvasHelper::setTarget(rTarget);
+ }
+
+ void BitmapCanvasHelper::setTarget( const IBitmapSharedPtr& rTarget,
+ const ::basegfx::B2ISize& rOutputOffset )
+ {
+ ENSURE_OR_THROW( rTarget,
+ "BitmapCanvasHelper::setTarget(): invalid target" );
+ ENSURE_OR_THROW( !mpTarget,
+ "BitmapCanvasHelper::setTarget(): target set, old target would be overwritten" );
+
+ mpTarget = rTarget;
+ CanvasHelper::setTarget(rTarget,rOutputOffset);
+ }
+
+ void BitmapCanvasHelper::clear()
+ {
+ if( needOutput() )
+ {
+ GraphicsSharedPtr pGraphics( mpTarget->getGraphics() );
+
+ Gdiplus::Color aClearColor = hasAlpha() ?
+ Gdiplus::Color( 0,255,255,255 ) : Gdiplus::Color(Gdiplus::ARGB(Gdiplus::Color::White));
+
+ ENSURE_OR_THROW(
+ Gdiplus::Ok == pGraphics->SetCompositingMode(
+ Gdiplus::CompositingModeSourceCopy ), // force set, don't blend
+ "BitmapCanvasHelper::clear(): GDI+ SetCompositingMode call failed" );
+ ENSURE_OR_THROW(
+ Gdiplus::Ok == pGraphics->Clear( aClearColor ),
+ "BitmapCanvasHelper::clear(): GDI+ Clear call failed" );
+ }
+ }
+
+ uno::Reference< rendering::XCachedPrimitive > BitmapCanvasHelper::drawTextLayout( const rendering::XCanvas* /*pCanvas*/,
+ const uno::Reference< rendering::XTextLayout >& xLayoutetText,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState )
+ {
+ ENSURE_OR_THROW( xLayoutetText.is(),
+ "BitmapCanvasHelper::drawTextLayout: layout is NULL");
+
+ if( needOutput() )
+ {
+ TextLayout* pTextLayout =
+ dynamic_cast< TextLayout* >( xLayoutetText.get() );
+
+ ENSURE_OR_THROW( pTextLayout,
+ "BitmapCanvasHelper::drawTextLayout(): TextLayout not compatible with this canvas" );
+
+ pTextLayout->draw( mpTarget->getGraphics(),
+ viewState,
+ renderState,
+ maOutputOffset,
+ mpDevice,
+ mpTarget->hasAlpha() );
+ }
+
+ return uno::Reference< rendering::XCachedPrimitive >(nullptr);
+ }
+
+ void BitmapCanvasHelper::copyRect( const rendering::XCanvas* /*pCanvas*/,
+ const uno::Reference< rendering::XBitmapCanvas >& /*sourceCanvas*/,
+ const geometry::RealRectangle2D& /*sourceRect*/,
+ const rendering::ViewState& /*sourceViewState*/,
+ const rendering::RenderState& /*sourceRenderState*/,
+ const geometry::RealRectangle2D& /*destRect*/,
+ const rendering::ViewState& /*destViewState*/,
+ const rendering::RenderState& /*destRenderState*/ )
+ {
+ // TODO(F2): copyRect NYI
+ }
+
+ geometry::IntegerSize2D BitmapCanvasHelper::getSize()
+ {
+ if( !mpTarget )
+ return geometry::IntegerSize2D(1, 1);
+ return basegfx::unotools::integerSize2DFromB2ISize(mpTarget->getSize());
+ }
+
+ uno::Reference< rendering::XBitmap > BitmapCanvasHelper::getScaledBitmap( const geometry::RealSize2D& /*newSize*/,
+ bool /*beFast*/ )
+ {
+ // TODO(F1):
+ return uno::Reference< rendering::XBitmap >();
+ }
+
+ uno::Sequence< sal_Int8 > BitmapCanvasHelper::getData( rendering::IntegerBitmapLayout& bitmapLayout,
+ const geometry::IntegerRectangle2D& rect )
+ {
+ SAL_INFO( "canvas.directx", "::dxcanvas::BitmapCanvasHelper::getData()" );
+
+ ENSURE_OR_THROW( mpTarget,
+ "::dxcanvas::BitmapCanvasHelper::getData(): disposed" );
+
+ bitmapLayout = getMemoryLayout();
+ return mpTarget->getData(bitmapLayout,rect);
+ }
+
+ void BitmapCanvasHelper::setData( const uno::Sequence< sal_Int8 >& data,
+ const rendering::IntegerBitmapLayout& bitmapLayout,
+ const geometry::IntegerRectangle2D& rect )
+ {
+ SAL_INFO( "canvas.directx", "::dxcanvas::BitmapCanvasHelper::setData()" );
+
+ ENSURE_OR_THROW( mpTarget,
+ "::dxcanvas::BitmapCanvasHelper::setData(): disposed" );
+
+ mpTarget->setData(data,bitmapLayout,rect);
+ }
+
+ void BitmapCanvasHelper::setPixel( const uno::Sequence< sal_Int8 >& color,
+ const rendering::IntegerBitmapLayout& bitmapLayout,
+ const geometry::IntegerPoint2D& pos )
+ {
+ SAL_INFO( "canvas.directx", "::dxcanvas::BitmapCanvasHelper::setPixel()" );
+
+ ENSURE_OR_THROW( mpTarget,
+ "::dxcanvas::BitmapCanvasHelper::setPixel(): disposed" );
+
+ mpTarget->setPixel(color,bitmapLayout,pos);
+ }
+
+ uno::Sequence< sal_Int8 > BitmapCanvasHelper::getPixel( rendering::IntegerBitmapLayout& bitmapLayout,
+ const geometry::IntegerPoint2D& pos )
+ {
+ SAL_INFO( "canvas.directx", "::dxcanvas::BitmapCanvasHelper::getPixel()" );
+
+ ENSURE_OR_THROW( mpTarget,
+ "::dxcanvas::BitmapCanvasHelper::getPixel(): disposed" );
+
+ bitmapLayout = getMemoryLayout();
+ return mpTarget->getPixel(bitmapLayout,pos);
+ }
+
+ uno::Reference< rendering::XBitmapPalette > BitmapCanvasHelper::getPalette()
+ {
+ // TODO(F1): Palette bitmaps NYI
+ return uno::Reference< rendering::XBitmapPalette >();
+ }
+
+ rendering::IntegerBitmapLayout BitmapCanvasHelper::getMemoryLayout()
+ {
+ if( !mpTarget )
+ return rendering::IntegerBitmapLayout(); // we're disposed
+
+ return ::canvas::tools::getStdMemoryLayout(getSize());
+ }
+ bool BitmapCanvasHelper::hasAlpha() const
+ {
+ return mpTarget && mpTarget->hasAlpha();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/directx/dx_bitmapcanvashelper.hxx b/canvas/source/directx/dx_bitmapcanvashelper.hxx
new file mode 100644
index 0000000000..46f970493f
--- /dev/null
+++ b/canvas/source/directx/dx_bitmapcanvashelper.hxx
@@ -0,0 +1,126 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/rendering/XCanvas.hpp>
+#include <com/sun/star/rendering/XBitmapCanvas.hpp>
+
+#include <basegfx/vector/b2isize.hxx>
+#include <basegfx/vector/b2dsize.hxx>
+
+#include "dx_graphicsprovider.hxx"
+#include "dx_ibitmap.hxx"
+#include "dx_gdiplususer.hxx"
+#include "dx_impltools.hxx"
+#include "dx_canvashelper.hxx"
+
+
+namespace dxcanvas
+{
+ /** Helper class for basic canvas functionality. Also offers
+ optional backbuffer painting, when providing it with a second
+ HDC to render into.
+ */
+ class BitmapCanvasHelper : public CanvasHelper
+ {
+ public:
+ BitmapCanvasHelper();
+
+ /// Release all references
+ void disposing();
+
+ /** Set the target for rendering operations
+
+ @param rTarget
+ Render target
+ */
+ void setTarget( const IBitmapSharedPtr& rTarget );
+
+ /** Set the target for rendering operations
+
+ @param rTarget
+ Render target
+
+ @param rOutputOffset
+ Output offset in pixel
+ */
+ void setTarget( const IBitmapSharedPtr& rTarget,
+ const ::basegfx::B2ISize& rOutputOffset );
+
+
+ // CanvasHelper functionality is implementation-inherited. yuck.
+ // =============================================================
+ void clear();
+
+ css::uno::Reference< css::rendering::XCachedPrimitive >
+ drawTextLayout( const css::rendering::XCanvas* pCanvas,
+ const css::uno::Reference<
+ css::rendering::XTextLayout >& laidOutText,
+ const css::rendering::ViewState& viewState,
+ const css::rendering::RenderState& renderState );
+
+ // BitmapCanvasHelper functionality
+ // ================================
+
+ void copyRect( const css::rendering::XCanvas* pCanvas,
+ const css::uno::Reference<
+ css::rendering::XBitmapCanvas >& sourceCanvas,
+ const css::geometry::RealRectangle2D& sourceRect,
+ const css::rendering::ViewState& sourceViewState,
+ const css::rendering::RenderState& sourceRenderState,
+ const css::geometry::RealRectangle2D& destRect,
+ const css::rendering::ViewState& destViewState,
+ const css::rendering::RenderState& destRenderState );
+
+ css::geometry::IntegerSize2D getSize();
+
+ css::uno::Reference< css::rendering::XBitmap >
+ getScaledBitmap( const css::geometry::RealSize2D& newSize,
+ bool beFast );
+
+ css::uno::Sequence< sal_Int8 >
+ getData( css::rendering::IntegerBitmapLayout& bitmapLayout,
+ const css::geometry::IntegerRectangle2D& rect );
+
+ void setData( const css::uno::Sequence< sal_Int8 >& data,
+ const css::rendering::IntegerBitmapLayout& bitmapLayout,
+ const css::geometry::IntegerRectangle2D& rect );
+
+ void setPixel( const css::uno::Sequence< sal_Int8 >& color,
+ const css::rendering::IntegerBitmapLayout& bitmapLayout,
+ const css::geometry::IntegerPoint2D& pos );
+
+ css::uno::Sequence< sal_Int8 >
+ getPixel( css::rendering::IntegerBitmapLayout& bitmapLayout,
+ const css::geometry::IntegerPoint2D& pos );
+
+ css::uno::Reference< css::rendering::XBitmapPalette > getPalette();
+
+ css::rendering::IntegerBitmapLayout getMemoryLayout();
+
+ bool hasAlpha() const;
+
+ protected:
+ /// Render target
+ IBitmapSharedPtr mpTarget;
+ };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/directx/dx_bitmapprovider.hxx b/canvas/source/directx/dx_bitmapprovider.hxx
new file mode 100644
index 0000000000..ad83abee0f
--- /dev/null
+++ b/canvas/source/directx/dx_bitmapprovider.hxx
@@ -0,0 +1,34 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "dx_ibitmap.hxx"
+#include <memory>
+
+namespace dxcanvas
+{
+struct BitmapProvider
+{
+ virtual ~BitmapProvider() {}
+ virtual IBitmapSharedPtr getBitmap() const = 0;
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/directx/dx_canvas.cxx b/canvas/source/directx/dx_canvas.cxx
new file mode 100644
index 0000000000..c19ea1ea90
--- /dev/null
+++ b/canvas/source/directx/dx_canvas.cxx
@@ -0,0 +1,256 @@
+// /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <memory>
+#include <utility>
+
+#include <sal/log.hxx>
+
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/numeric/ftools.hxx>
+#include <basegfx/point/b2dpoint.hxx>
+#include <basegfx/utils/canvastools.hxx>
+#include <com/sun/star/awt/XSystemDependentWindowPeer.hpp>
+#include <com/sun/star/awt/XWindow.hpp>
+#include <com/sun/star/lang/NoSupportException.hpp>
+#include <com/sun/star/lang/XSingleServiceFactory.hpp>
+#include <com/sun/star/registry/XRegistryKey.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <cppuhelper/supportsservice.hxx>
+#include <osl/mutex.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <vcl/sysdata.hxx>
+#include <vcl/skia/SkiaHelper.hxx>
+#include <vcl/window.hxx>
+
+#include <canvas/canvastools.hxx>
+
+#include "dx_canvas.hxx"
+#include "dx_graphicsprovider.hxx"
+#include "dx_winstuff.hxx"
+
+using namespace ::com::sun::star;
+
+namespace dxcanvas
+{
+ namespace {
+
+ /// Actual canonical implementation of the GraphicsProvider interface
+ class GraphicsProviderImpl : public GraphicsProvider
+ {
+ GraphicsSharedPtr mpGraphics;
+ public:
+ explicit GraphicsProviderImpl( GraphicsSharedPtr && pGraphics ) : mpGraphics( std::move(pGraphics) ) {}
+ virtual GraphicsSharedPtr getGraphics() override { return mpGraphics; }
+ };
+
+ }
+
+ Canvas::Canvas( const uno::Sequence< uno::Any >& aArguments,
+ const uno::Reference< uno::XComponentContext >& rxContext ) :
+ maArguments(aArguments),
+ mxComponentContext( rxContext )
+ {
+ }
+
+ void Canvas::initialize()
+ {
+ // #i64742# Only perform initialization when not in probe mode
+ if( maArguments.getLength() == 0 )
+ return;
+
+ assert( !SkiaHelper::isVCLSkiaEnabled() );
+
+ SAL_INFO("canvas.directx", "Canvas::initialize called" );
+
+ // At index 1, we expect a HWND handle here, containing a
+ // pointer to a valid window, on which to output
+ // At index 2, we expect the current window bound rect
+ ENSURE_ARG_OR_THROW( maArguments.getLength() >= 5 &&
+ maArguments[4].getValueTypeClass() == uno::TypeClass_SEQUENCE,
+ "Canvas::initialize: wrong number of arguments, or wrong types" );
+
+ uno::Sequence<sal_Int8> aSeq;
+ maArguments[4] >>= aSeq;
+
+ const SystemGraphicsData* pSysData=reinterpret_cast<const SystemGraphicsData*>(aSeq.getConstArray());
+ if( !pSysData || !pSysData->hDC )
+ throw lang::NoSupportException("Passed SystemGraphicsData or HDC invalid!");
+
+ sal_Int64 nPtr = 0;
+ maArguments[0] >>= nPtr;
+ OutputDevice* pOutDev = reinterpret_cast<OutputDevice*>(nPtr);
+ ENSURE_ARG_OR_THROW( pOutDev != nullptr,"Canvas::initialize: invalid OutDev pointer" );
+
+ // setup helper
+ maDeviceHelper.init( pSysData->hDC, pOutDev, *this );
+ maCanvasHelper.setDevice( *this );
+ maCanvasHelper.setTarget(
+ std::make_shared<GraphicsProviderImpl>(
+ GraphicsSharedPtr(Gdiplus::Graphics::FromHDC(pSysData->hDC))));
+
+ maArguments.realloc(0);
+ }
+
+ void Canvas::disposeThis()
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ mxComponentContext.clear();
+
+ // forward to parent
+ CanvasBaseT::disposeThis();
+ }
+
+ OUString SAL_CALL Canvas::getServiceName( )
+ {
+ return "com.sun.star.rendering.Canvas.GDI+";
+ }
+
+ // XServiceInfo
+ css::uno::Sequence<OUString> Canvas::getSupportedServiceNames( )
+ {
+ return { "com.sun.star.rendering.Canvas.GDI+" };
+ }
+ OUString Canvas::getImplementationName( )
+ {
+ return "com.sun.star.comp.rendering.Canvas.GDI+";
+ }
+ sal_Bool Canvas::supportsService( const OUString& sServiceName )
+ {
+ return cppu::supportsService(this, sServiceName);
+ }
+
+ BitmapCanvas::BitmapCanvas( const uno::Sequence< uno::Any >& aArguments,
+ const uno::Reference< uno::XComponentContext >& rxContext ) :
+ maArguments(aArguments),
+ mxComponentContext( rxContext ),
+ mpTarget()
+ {
+ }
+
+ void BitmapCanvas::initialize()
+ {
+ // #i64742# Only perform initialization when not in probe mode
+ if( maArguments.getLength() == 0 )
+ return;
+
+ SAL_INFO("canvas.directx", "BitmapCanvas::initialize called" );
+
+ // At index 1, we expect a HWND handle here, containing a
+ // pointer to a valid window, on which to output
+ // At index 2, we expect the current window bound rect
+ ENSURE_ARG_OR_THROW( maArguments.getLength() >= 5 &&
+ maArguments[4].getValueTypeClass() == uno::TypeClass_SEQUENCE,
+ "Canvas::initialize: wrong number of arguments, or wrong types" );
+
+ uno::Sequence<sal_Int8> aSeq;
+ maArguments[4] >>= aSeq;
+
+ const SystemGraphicsData* pSysData=reinterpret_cast<const SystemGraphicsData*>(aSeq.getConstArray());
+ if( !pSysData || !pSysData->hDC )
+ throw lang::NoSupportException( "Passed SystemGraphicsData or HDC invalid!");
+
+ sal_Int64 nPtr = 0;
+ maArguments[0] >>= nPtr;
+ OutputDevice* pOutDev = reinterpret_cast<OutputDevice*>(nPtr);
+ ENSURE_ARG_OR_THROW( pOutDev != nullptr,"Canvas::initialize: invalid OutDev pointer" );
+
+ // setup helper
+ maDeviceHelper.init( pSysData->hDC, pOutDev, *this );
+ maCanvasHelper.setDevice( *this );
+
+ // check whether we can actually provide a BitmapCanvas
+ // here. for this, check whether the HDC has a bitmap
+ // selected.
+ HBITMAP hBmp;
+ hBmp=static_cast<HBITMAP>(GetCurrentObject(pSysData->hDC, OBJ_BITMAP));
+ if( !hBmp || GetObjectType(pSysData->hDC) != OBJ_MEMDC )
+ {
+ throw lang::NoSupportException( "Passed HDC is no mem DC/has no bitmap selected!");
+ }
+
+ mpTarget = std::make_shared<DXBitmap>(
+ BitmapSharedPtr(
+ Gdiplus::Bitmap::FromHBITMAP(
+ hBmp, nullptr) ),
+ false );
+
+ maCanvasHelper.setTarget( mpTarget );
+
+ maArguments.realloc(0);
+ }
+
+ void BitmapCanvas::disposeThis()
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ mpTarget.reset();
+ mxComponentContext.clear();
+
+ // forward to parent
+ BitmapCanvasBaseT::disposeThis();
+ }
+
+ OUString SAL_CALL BitmapCanvas::getServiceName( )
+ {
+ return "com.sun.star.rendering.BitmapCanvas.GDI+";
+ }
+
+ // XServiceInfo
+ css::uno::Sequence<OUString> BitmapCanvas::getSupportedServiceNames( )
+ {
+ return { "com.sun.star.rendering.BitmapCanvas.GDI+" };
+ }
+ OUString BitmapCanvas::getImplementationName( )
+ {
+ return "com.sun.star.comp.rendering.BitmapCanvas.GDI+";
+ }
+ sal_Bool BitmapCanvas::supportsService( const OUString& sServiceName )
+ {
+ return cppu::supportsService(this, sServiceName);
+ }
+
+ IBitmapSharedPtr BitmapCanvas::getBitmap() const
+ {
+ return mpTarget;
+ }
+
+ extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+ canvas_gdiplus_Canvas_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& args)
+ {
+ rtl::Reference<Canvas> xCanvas(new Canvas(args, context));
+ xCanvas->initialize();
+ return cppu::acquire(xCanvas.get());
+ }
+
+ extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+ canvas_gdiplus_BitmapCanvas_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& args)
+ {
+ rtl::Reference<BitmapCanvas> xCanvas(new BitmapCanvas(args, context));
+ xCanvas->initialize();
+ return cppu::acquire(xCanvas.get());
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/directx/dx_canvas.hxx b/canvas/source/directx/dx_canvas.hxx
new file mode 100644
index 0000000000..5a00e07fa4
--- /dev/null
+++ b/canvas/source/directx/dx_canvas.hxx
@@ -0,0 +1,173 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <rtl/ref.hxx>
+
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/lang/XServiceName.hpp>
+#include <com/sun/star/util/XUpdatable.hpp>
+#include <com/sun/star/rendering/XBitmapCanvas.hpp>
+#include <com/sun/star/rendering/XIntegerBitmap.hpp>
+#include <com/sun/star/rendering/XGraphicDevice.hpp>
+#include <com/sun/star/rendering/XBufferController.hpp>
+
+#include <cppuhelper/compbase.hxx>
+#include <comphelper/uno3.hxx>
+
+#include <base/integerbitmapbase.hxx>
+#include <base/basemutexhelper.hxx>
+#include <base/graphicdevicebase.hxx>
+#include <base/canvasbase.hxx>
+#include <base/bitmapcanvasbase.hxx>
+
+#include "dx_bitmapprovider.hxx"
+#include "dx_canvashelper.hxx"
+#include "dx_bitmapcanvashelper.hxx"
+#include "dx_impltools.hxx"
+#include "dx_devicehelper.hxx"
+
+
+namespace dxcanvas
+{
+ typedef ::cppu::WeakComponentImplHelper< css::rendering::XCanvas,
+ css::rendering::XGraphicDevice,
+ css::lang::XMultiServiceFactory,
+ css::util::XUpdatable,
+ css::beans::XPropertySet,
+ css::lang::XServiceName,
+ css::lang::XServiceInfo> GraphicDeviceBase1_Base;
+ typedef ::canvas::GraphicDeviceBase< ::canvas::BaseMutexHelper< GraphicDeviceBase1_Base >,
+ DeviceHelper,
+ ::osl::MutexGuard,
+ ::cppu::OWeakObject > CanvasBase1_Base;
+ typedef ::canvas::CanvasBase< CanvasBase1_Base,
+ CanvasHelper,
+ ::osl::MutexGuard,
+ ::cppu::OWeakObject > CanvasBaseT;
+
+ /** Product of this component's factory.
+
+ The Canvas object combines the actual Window canvas with
+ the XGraphicDevice interface. This is because there's a
+ one-to-one relation between them, anyway, since each window
+ can have exactly one canvas and one associated
+ XGraphicDevice. And to avoid messing around with circular
+ references, this is implemented as one single object.
+ */
+ class Canvas : public CanvasBaseT
+ {
+ public:
+ Canvas( const css::uno::Sequence<
+ css::uno::Any >& aArguments,
+ const css::uno::Reference<
+ css::uno::XComponentContext >& rxContext );
+
+ void initialize();
+
+ /// Dispose all internal references
+ virtual void disposeThis() override;
+
+ // Forwarding the XComponent implementation to the
+ // cppu::ImplHelper templated base
+ // Classname Base doing refcounting Base implementing the XComponent interface
+ // | | |
+ // V V V
+ DECLARE_UNO3_XCOMPONENT_AGG_DEFAULTS( Canvas, GraphicDeviceBase1_Base, ::cppu::WeakComponentImplHelperBase )
+
+ // XServiceName
+ virtual OUString SAL_CALL getServiceName( ) override;
+
+ // XServiceInfo
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames( ) override;
+ virtual OUString SAL_CALL getImplementationName( ) override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ) override;
+
+ private:
+ css::uno::Sequence< css::uno::Any > maArguments;
+ css::uno::Reference< css::uno::XComponentContext > mxComponentContext;
+ };
+
+ typedef ::cppu::WeakComponentImplHelper< css::rendering::XBitmapCanvas,
+ css::rendering::XIntegerBitmap,
+ css::rendering::XGraphicDevice,
+ css::lang::XMultiServiceFactory,
+ css::util::XUpdatable,
+ css::beans::XPropertySet,
+ css::lang::XServiceName,
+ css::lang::XServiceInfo > GraphicDeviceBase2_Base;
+ typedef ::canvas::GraphicDeviceBase< ::canvas::BaseMutexHelper< GraphicDeviceBase2_Base >,
+ DeviceHelper,
+ ::osl::MutexGuard,
+ ::cppu::OWeakObject > CanvasBase2_Base;
+ typedef ::canvas::IntegerBitmapBase<
+ canvas::BitmapCanvasBase2<
+ CanvasBase2_Base,
+ BitmapCanvasHelper,
+ ::osl::MutexGuard,
+ ::cppu::OWeakObject> > BitmapCanvasBaseT;
+
+ /** Product of this component's factory.
+
+ The Canvas object combines the actual Window canvas with
+ the XGraphicDevice interface. This is because there's a
+ one-to-one relation between them, anyway, since each window
+ can have exactly one canvas and one associated
+ XGraphicDevice. And to avoid messing around with circular
+ references, this is implemented as one single object.
+ */
+ class BitmapCanvas : public BitmapCanvasBaseT, public BitmapProvider
+ {
+ public:
+ BitmapCanvas( const css::uno::Sequence< css::uno::Any >& aArguments,
+ const css::uno::Reference< css::uno::XComponentContext >& rxContext );
+
+ void initialize();
+
+ /// Dispose all internal references
+ virtual void disposeThis() override;
+
+ // Forwarding the XComponent implementation to the
+ // cppu::ImplHelper templated base
+ // Classname Base doing refcounting Base implementing the XComponent interface
+ // | | |
+ // V V V
+ DECLARE_UNO3_XCOMPONENT_AGG_DEFAULTS( BitmapCanvas, GraphicDeviceBase2_Base, ::cppu::WeakComponentImplHelperBase )
+
+ // XServiceName
+ virtual OUString SAL_CALL getServiceName( ) override;
+
+ // XServiceInfo
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames( ) override;
+ virtual OUString SAL_CALL getImplementationName( ) override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ) override;
+
+ // BitmapProvider
+ virtual IBitmapSharedPtr getBitmap() const override;
+
+ private:
+ css::uno::Sequence< css::uno::Any > maArguments;
+ css::uno::Reference< css::uno::XComponentContext > mxComponentContext;
+ IBitmapSharedPtr mpTarget;
+ };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/directx/dx_canvasbitmap.cxx b/canvas/source/directx/dx_canvasbitmap.cxx
new file mode 100644
index 0000000000..fb06288ada
--- /dev/null
+++ b/canvas/source/directx/dx_canvasbitmap.cxx
@@ -0,0 +1,256 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <memory>
+
+#include <cppuhelper/supportsservice.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <vcl/bitmapex.hxx>
+
+#include <canvas/canvastools.hxx>
+
+#include "dx_canvasbitmap.hxx"
+#include "dx_impltools.hxx"
+
+
+using namespace ::com::sun::star;
+
+namespace dxcanvas
+{
+ CanvasBitmap::CanvasBitmap( const IBitmapSharedPtr& rBitmap,
+ const DeviceRef& rDevice ) :
+ mpDevice( rDevice ),
+ mpBitmap( rBitmap )
+ {
+ ENSURE_OR_THROW( mpDevice.is() && mpBitmap,
+ "CanvasBitmap::CanvasBitmap(): Invalid surface or device" );
+
+ maCanvasHelper.setDevice( *mpDevice );
+ maCanvasHelper.setTarget( mpBitmap );
+ }
+
+ void CanvasBitmap::disposeThis()
+ {
+ mpBitmap.reset();
+ mpDevice.clear();
+
+ // forward to parent
+ CanvasBitmap_Base::disposeThis();
+ }
+
+ namespace {
+
+ struct AlphaDIB
+ {
+ BITMAPINFOHEADER bmiHeader;
+ RGBQUAD bmiColors[256];
+ AlphaDIB()
+ : bmiHeader({0,0,0,1,8,BI_RGB,0,0,0,0,0})
+ {
+ for (size_t i = 0; i < 256; ++i)
+ {
+ // this here fills palette with grey level colors, starting
+ // from 0,0,0 up to 255,255,255
+ BYTE const b(i);
+ bmiColors[i] = { b,b,b,b };
+ }
+ }
+ };
+
+ }
+
+ uno::Any SAL_CALL CanvasBitmap::getFastPropertyValue( sal_Int32 nHandle )
+ {
+ uno::Any aRes;
+ // 0 ... get BitmapEx
+ // 1 ... get Pixbuf with bitmap RGB content
+ // 2 ... get Pixbuf with bitmap alpha mask
+ switch( nHandle )
+ {
+ // sorry, no BitmapEx here...
+ case 0:
+ aRes <<= reinterpret_cast<sal_Int64>( nullptr );
+ break;
+
+ case 1:
+ {
+ if(!mpBitmap->hasAlpha())
+ {
+ HBITMAP aHBmp;
+ mpBitmap->getBitmap()->GetHBITMAP(Gdiplus::Color(), &aHBmp );
+
+ uno::Sequence< uno::Any > args{ uno::Any(reinterpret_cast<sal_Int64>(aHBmp)) };
+ aRes <<= args;
+ }
+ else
+ {
+ // need to copy&convert the bitmap, since dx
+ // canvas uses inline alpha channel
+ HDC hScreenDC=GetDC(nullptr);
+ const basegfx::B2ISize aSize = mpBitmap->getSize();
+ HBITMAP hBmpBitmap = CreateCompatibleBitmap( hScreenDC,
+ aSize.getWidth(),
+ aSize.getHeight() );
+ if( !hBmpBitmap )
+ return aRes;
+
+ BITMAPINFOHEADER aBIH;
+
+ aBIH.biSize = sizeof( BITMAPINFOHEADER );
+ aBIH.biWidth = aSize.getWidth();
+ aBIH.biHeight = -aSize.getHeight();
+ aBIH.biPlanes = 1;
+ aBIH.biBitCount = 32;
+ aBIH.biCompression = BI_RGB; // expects pixel in
+ // bbggrrxx format
+ // (little endian)
+ aBIH.biSizeImage = 0;
+ aBIH.biXPelsPerMeter = 0;
+ aBIH.biYPelsPerMeter = 0;
+ aBIH.biClrUsed = 0;
+ aBIH.biClrImportant = 0;
+
+ Gdiplus::BitmapData aBmpData;
+ aBmpData.Width = aSize.getWidth();
+ aBmpData.Height = aSize.getHeight();
+ aBmpData.Stride = 4*aBmpData.Width;
+ aBmpData.PixelFormat = PixelFormat32bppARGB;
+ aBmpData.Scan0 = nullptr;
+ const Gdiplus::Rect aRect( 0,0,aSize.getWidth(),aSize.getHeight() );
+ BitmapSharedPtr pGDIPlusBitmap=mpBitmap->getBitmap();
+ if( Gdiplus::Ok != pGDIPlusBitmap->LockBits( &aRect,
+ Gdiplus::ImageLockModeRead,
+ PixelFormat32bppARGB, // outputs ARGB (big endian)
+ &aBmpData ) )
+ {
+ // failed to lock, bail out
+ return aRes;
+ }
+
+ // now aBmpData.Scan0 contains our bits - push
+ // them into HBITMAP, ignoring alpha
+ SetDIBits( hScreenDC, hBmpBitmap, 0, aSize.getHeight(), aBmpData.Scan0, reinterpret_cast<PBITMAPINFO>(&aBIH), DIB_RGB_COLORS );
+
+ pGDIPlusBitmap->UnlockBits( &aBmpData );
+
+ uno::Sequence< uno::Any > args{ uno::Any(reinterpret_cast<sal_Int64>(hBmpBitmap)) };
+ aRes <<= args;
+ }
+ }
+ break;
+
+ case 2:
+ {
+ if(!mpBitmap->hasAlpha())
+ {
+ return aRes;
+ }
+ else
+ {
+ static AlphaDIB aDIB;
+
+ // need to copy&convert the bitmap, since dx
+ // canvas uses inline alpha channel
+ HDC hScreenDC=GetDC(nullptr);
+ const basegfx::B2ISize aSize = mpBitmap->getSize();
+ HBITMAP hBmpBitmap = CreateCompatibleBitmap( hScreenDC, aSize.getWidth(), aSize.getHeight() );
+ if( !hBmpBitmap )
+ return aRes;
+
+ aDIB.bmiHeader.biSize = sizeof( BITMAPINFOHEADER );
+ aDIB.bmiHeader.biWidth = aSize.getWidth();
+ aDIB.bmiHeader.biHeight = -aSize.getHeight();
+ aDIB.bmiHeader.biPlanes = 1;
+ aDIB.bmiHeader.biBitCount = 8;
+ aDIB.bmiHeader.biCompression = BI_RGB;
+ aDIB.bmiHeader.biSizeImage = 0;
+ aDIB.bmiHeader.biXPelsPerMeter = 0;
+ aDIB.bmiHeader.biYPelsPerMeter = 0;
+ aDIB.bmiHeader.biClrUsed = 0;
+ aDIB.bmiHeader.biClrImportant = 0;
+
+ Gdiplus::BitmapData aBmpData;
+ aBmpData.Width = aSize.getWidth();
+ aBmpData.Height = aSize.getHeight();
+ aBmpData.Stride = 4*aBmpData.Width;
+ aBmpData.PixelFormat = PixelFormat32bppARGB;
+ aBmpData.Scan0 = nullptr;
+ const Gdiplus::Rect aRect( 0,0,aSize.getWidth(),aSize.getHeight() );
+ BitmapSharedPtr pGDIPlusBitmap=mpBitmap->getBitmap();
+ if( Gdiplus::Ok != pGDIPlusBitmap->LockBits( &aRect,
+ Gdiplus::ImageLockModeRead,
+ PixelFormat32bppARGB, // outputs ARGB (big endian)
+ &aBmpData ) )
+ {
+ // failed to lock, bail out
+ return aRes;
+ }
+
+ // copy only alpha channel to pAlphaBits
+ const sal_Int32 nScanWidth((aSize.getWidth() + 3) & ~3);
+ std::unique_ptr<sal_uInt8[]> pAlphaBits( new sal_uInt8[nScanWidth*aSize.getHeight()] );
+ const sal_uInt8* pInBits=static_cast<sal_uInt8*>(aBmpData.Scan0);
+ pInBits+=3;
+ for( sal_Int32 y=0; y<aSize.getHeight(); ++y )
+ {
+ sal_uInt8* pOutBits=pAlphaBits.get()+y*nScanWidth;
+ for( sal_Int32 x=0; x<aSize.getWidth(); ++x )
+ {
+ *pOutBits++ = *pInBits;
+ pInBits += 4;
+ }
+ }
+
+ pGDIPlusBitmap->UnlockBits( &aBmpData );
+
+ // set bits to newly create HBITMAP
+ SetDIBits( hScreenDC, hBmpBitmap, 0,
+ aSize.getHeight(), pAlphaBits.get(),
+ reinterpret_cast<PBITMAPINFO>(&aDIB), DIB_RGB_COLORS );
+
+ uno::Sequence< uno::Any > args{ uno::Any(reinterpret_cast<sal_Int64>(hBmpBitmap)) };
+ aRes <<= args;
+ }
+ }
+ break;
+ }
+
+ return aRes;
+ }
+
+ OUString SAL_CALL CanvasBitmap::getImplementationName( )
+ {
+ return "DXCanvas.CanvasBitmap";
+ }
+
+ sal_Bool SAL_CALL CanvasBitmap::supportsService( const OUString& ServiceName )
+ {
+ return cppu::supportsService( this, ServiceName );
+ }
+
+ uno::Sequence< OUString > SAL_CALL CanvasBitmap::getSupportedServiceNames( )
+ {
+ return { "com.sun.star.rendering.CanvasBitmap" };
+ }
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/directx/dx_canvasbitmap.hxx b/canvas/source/directx/dx_canvasbitmap.hxx
new file mode 100644
index 0000000000..b1725b447d
--- /dev/null
+++ b/canvas/source/directx/dx_canvasbitmap.hxx
@@ -0,0 +1,91 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/rendering/XBitmapCanvas.hpp>
+#include <com/sun/star/rendering/XIntegerBitmap.hpp>
+#include <com/sun/star/beans/XFastPropertySet.hpp>
+#include <basegfx/vector/b2isize.hxx>
+#include <cppuhelper/compbase.hxx>
+#include <base/basemutexhelper.hxx>
+#include <base/bitmapcanvasbase.hxx>
+#include <base/integerbitmapbase.hxx>
+
+#include "dx_bitmapprovider.hxx"
+#include "dx_bitmapcanvashelper.hxx"
+#include "dx_devicehelper.hxx"
+#include "dx_impltools.hxx"
+#include "dx_ibitmap.hxx"
+
+
+/* Definition of CanvasBitmap class */
+
+namespace dxcanvas
+{
+ typedef ::cppu::WeakComponentImplHelper< css::rendering::XBitmapCanvas,
+ css::rendering::XIntegerBitmap,
+ css::lang::XServiceInfo,
+ css::beans::XFastPropertySet > CanvasBitmapBase_Base;
+ typedef ::canvas::IntegerBitmapBase<
+ canvas::BitmapCanvasBase2<
+ ::canvas::BaseMutexHelper< CanvasBitmapBase_Base >,
+ BitmapCanvasHelper,
+ ::osl::MutexGuard,
+ ::cppu::OWeakObject> > CanvasBitmap_Base;
+
+ class CanvasBitmap : public CanvasBitmap_Base, public BitmapProvider
+ {
+ public:
+ /** Create a canvas bitmap for the given surface
+
+ @param rSurface
+ Surface to create API object for.
+
+ @param rDevice
+ Reference device, with which bitmap should be compatible
+ */
+ CanvasBitmap( const IBitmapSharedPtr& rSurface,
+ const DeviceRef& rDevice );
+
+ /// Dispose all internal references
+ virtual void disposeThis() override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName( ) override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+
+ // BitmapProvider
+ virtual IBitmapSharedPtr getBitmap() const override { return mpBitmap; }
+
+ virtual css::uno::Any SAL_CALL getFastPropertyValue(sal_Int32 nHandle) override;
+ virtual void SAL_CALL setFastPropertyValue(sal_Int32, const css::uno::Any&) override {}
+
+ private:
+ /** MUST hold here, too, since CanvasHelper only contains a
+ raw pointer (without refcounting)
+ */
+ DeviceRef mpDevice;
+ IBitmapSharedPtr mpBitmap;
+ };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/directx/dx_canvascustomsprite.cxx b/canvas/source/directx/dx_canvascustomsprite.cxx
new file mode 100644
index 0000000000..b9e79fdc06
--- /dev/null
+++ b/canvas/source/directx/dx_canvascustomsprite.cxx
@@ -0,0 +1,106 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <memory>
+
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/point/b2dpoint.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <rtl/math.hxx>
+#include <comphelper/diagnose_ex.hxx>
+
+#include <canvas/canvastools.hxx>
+
+#include "dx_canvascustomsprite.hxx"
+#include "dx_impltools.hxx"
+#include "dx_spritecanvas.hxx"
+
+using namespace ::com::sun::star;
+
+namespace dxcanvas
+{
+ CanvasCustomSprite::CanvasCustomSprite( const css::geometry::RealSize2D& rSpriteSize,
+ const SpriteCanvasRef& rRefDevice,
+ const IDXRenderModuleSharedPtr& rRenderModule,
+ const std::shared_ptr<canvas::ISurfaceProxyManager>& rSurfaceProxy,
+ bool bShowSpriteBounds ) :
+ mpSpriteCanvas( rRefDevice ),
+ mpSurface()
+ {
+ ENSURE_OR_THROW( rRefDevice,
+ "CanvasCustomSprite::CanvasCustomSprite(): Invalid sprite canvas" );
+
+ mpSurface = std::make_shared<DXSurfaceBitmap>(
+ ::basegfx::B2ISize(
+ ::canvas::tools::roundUp( rSpriteSize.Width ),
+ ::canvas::tools::roundUp( rSpriteSize.Height )),
+ rSurfaceProxy,
+ rRenderModule,
+ true);
+
+ maCanvasHelper.setDevice( *rRefDevice );
+ maCanvasHelper.setTarget( mpSurface );
+
+ maSpriteHelper.init( rSpriteSize,
+ rRefDevice,
+ rRenderModule,
+ mpSurface,
+ bShowSpriteBounds );
+
+ // clear sprite to 100% transparent
+ maCanvasHelper.clear();
+ }
+
+ void CanvasCustomSprite::disposeThis()
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ mpSurface.reset();
+ mpSpriteCanvas.clear();
+
+ // forward to parent
+ CanvasCustomSpriteBaseT::disposeThis();
+ }
+
+ OUString SAL_CALL CanvasCustomSprite::getImplementationName()
+ {
+ return "DXCanvas.CanvasCustomSprite";
+ }
+
+ sal_Bool SAL_CALL CanvasCustomSprite::supportsService( const OUString& ServiceName )
+ {
+ return cppu::supportsService( this, ServiceName );
+ }
+
+ uno::Sequence< OUString > SAL_CALL CanvasCustomSprite::getSupportedServiceNames()
+ {
+ return { "com.sun.star.rendering.CanvasCustomSprite" };
+ }
+
+ void CanvasCustomSprite::redraw() const
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ maSpriteHelper.redraw( mbSurfaceDirty );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/directx/dx_canvascustomsprite.hxx b/canvas/source/directx/dx_canvascustomsprite.hxx
new file mode 100644
index 0000000000..2411a15273
--- /dev/null
+++ b/canvas/source/directx/dx_canvascustomsprite.hxx
@@ -0,0 +1,130 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <cppuhelper/compbase.hxx>
+#include <comphelper/uno3.hxx>
+
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/rendering/XCustomSprite.hpp>
+#include <com/sun/star/rendering/XIntegerBitmap.hpp>
+#include <com/sun/star/rendering/XPolyPolygon2D.hpp>
+
+#include <basegfx/point/b2dpoint.hxx>
+#include <basegfx/vector/b2isize.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+
+#include <base/basemutexhelper.hxx>
+#include <base/canvascustomspritebase.hxx>
+
+#include "dx_sprite.hxx"
+#include "dx_surfacebitmap.hxx"
+#include "dx_bitmapcanvashelper.hxx"
+#include "dx_spritehelper.hxx"
+#include "dx_spritecanvas.hxx"
+
+
+namespace dxcanvas
+{
+ typedef ::cppu::WeakComponentImplHelper< css::rendering::XCustomSprite,
+ css::rendering::XBitmapCanvas,
+ css::rendering::XIntegerBitmap,
+ css::lang::XServiceInfo > CanvasCustomSpriteBase_Base;
+ /** Mixin Sprite
+
+ Have to mixin the Sprite interface before deriving from
+ ::canvas::CanvasCustomSpriteBase, as this template should
+ already implement some of those interface methods.
+
+ The reason why this appears kinda convoluted is the fact that
+ we cannot specify non-IDL types as WeakComponentImplHelper
+ template args, and furthermore, don't want to derive
+ ::canvas::CanvasCustomSpriteBase directly from
+ ::canvas::Sprite (because derivees of
+ ::canvas::CanvasCustomSpriteBase have to explicitly forward
+ the XInterface methods (e.g. via DECLARE_UNO3_AGG_DEFAULTS)
+ anyway). Basically, ::canvas::CanvasCustomSpriteBase should
+ remain a base class that provides implementation, not to
+ enforce any specific interface on its derivees.
+ */
+ class CanvasCustomSpriteSpriteBase_Base : public ::canvas::BaseMutexHelper< CanvasCustomSpriteBase_Base >,
+ public Sprite
+ {
+ };
+
+ typedef ::canvas::CanvasCustomSpriteBase< CanvasCustomSpriteSpriteBase_Base,
+ SpriteHelper,
+ BitmapCanvasHelper,
+ ::osl::MutexGuard,
+ ::cppu::OWeakObject > CanvasCustomSpriteBaseT;
+
+ /* Definition of CanvasCustomSprite class */
+
+ class CanvasCustomSprite : public CanvasCustomSpriteBaseT
+ {
+ public:
+ /** Create a custom sprite
+
+ @param rSpriteSize
+ Size of the sprite in pixel
+
+ @param rRefDevice
+ Associated output device
+
+ @param rSpriteCanvas
+ Target canvas
+
+ @param rDevice
+ Target DX device
+ */
+ CanvasCustomSprite( const css::geometry::RealSize2D& rSpriteSize,
+ const SpriteCanvasRef& rRefDevice,
+ const IDXRenderModuleSharedPtr& rRenderModule,
+ const std::shared_ptr<canvas::ISurfaceProxyManager>& rSurfaceProxy,
+ bool bShowSpriteBounds );
+
+ virtual void disposeThis() override;
+
+ // Forwarding the XComponent implementation to the
+ // cppu::ImplHelper templated base
+ // Classname Base doing refcount Base implementing the XComponent interface
+ // | | |
+ // V V V
+ DECLARE_UNO3_XCOMPONENT_AGG_DEFAULTS( CanvasCustomSprite, CanvasCustomSpriteBase_Base, ::cppu::WeakComponentImplHelperBase )
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ // Sprite
+ virtual void redraw() const override;
+
+ private:
+ /** MUST hold here, too, since BitmapCanvasHelper only contains a
+ raw pointer (without refcounting)
+ */
+ SpriteCanvasRef mpSpriteCanvas;
+ DXSurfaceBitmapSharedPtr mpSurface;
+ };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/directx/dx_canvasfont.cxx b/canvas/source/directx/dx_canvasfont.cxx
new file mode 100644
index 0000000000..aeb3070525
--- /dev/null
+++ b/canvas/source/directx/dx_canvasfont.cxx
@@ -0,0 +1,162 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <memory>
+
+#include <o3tl/char16_t2wchar_t.hxx>
+
+#include <com/sun/star/rendering/PanoseWeight.hpp>
+#include <com/sun/star/rendering/XSpriteCanvas.hpp>
+#include <cppuhelper/supportsservice.hxx>
+
+#include "dx_canvasfont.hxx"
+#include "dx_spritecanvas.hxx"
+#include "dx_textlayout.hxx"
+#include "dx_winstuff.hxx"
+
+using namespace ::com::sun::star;
+
+namespace dxcanvas
+{
+ namespace
+ {
+ INT calcFontStyle( const rendering::FontRequest& rFontRequest )
+ {
+ INT nFontStyle( Gdiplus::FontStyleRegular );
+
+ if( rFontRequest.FontDescription.FontDescription.Weight > rendering::PanoseWeight::BOOK )
+ nFontStyle = Gdiplus::FontStyleBold;
+
+ return nFontStyle;
+ }
+ }
+
+ CanvasFont::CanvasFont( const rendering::FontRequest& rFontRequest,
+ const uno::Sequence< beans::PropertyValue >& extraFontProperties,
+ const geometry::Matrix2D& fontMatrix ) :
+ CanvasFont_Base( m_aMutex ),
+ mpGdiPlusUser( GDIPlusUser::createInstance() ),
+ mpFontFamily(),
+ mpFont(),
+ maFontRequest( rFontRequest ),
+ mnEmphasisMark(0),
+ maFontMatrix( fontMatrix )
+ {
+ mpFontFamily = std::make_shared<Gdiplus::FontFamily>(o3tl::toW(rFontRequest.FontDescription.FamilyName.getStr()),nullptr);
+ if( !mpFontFamily->IsAvailable() )
+ mpFontFamily = std::make_shared<Gdiplus::FontFamily>(L"Arial",nullptr);
+
+ mpFont = std::make_shared<Gdiplus::Font>( mpFontFamily.get(),
+ static_cast<Gdiplus::REAL>(rFontRequest.CellSize),
+ calcFontStyle( rFontRequest ),
+ Gdiplus::UnitWorld );
+
+ ::canvas::tools::extractExtraFontProperties(extraFontProperties, mnEmphasisMark);
+ }
+
+ void SAL_CALL CanvasFont::disposing()
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ mpFont.reset();
+ mpFontFamily.reset();
+ mpGdiPlusUser.reset();
+ }
+
+ uno::Reference< rendering::XTextLayout > SAL_CALL CanvasFont::createTextLayout( const rendering::StringContext& aText,
+ sal_Int8 nDirection,
+ sal_Int64 nRandomSeed )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ return new TextLayout( aText, nDirection, nRandomSeed, ImplRef( this ) );
+ }
+
+ uno::Sequence< double > SAL_CALL CanvasFont::getAvailableSizes( )
+ {
+ // TODO
+ return uno::Sequence< double >();
+ }
+
+ uno::Sequence< beans::PropertyValue > SAL_CALL CanvasFont::getExtraFontProperties( )
+ {
+ // TODO
+ return uno::Sequence< beans::PropertyValue >();
+ }
+
+ rendering::FontRequest SAL_CALL CanvasFont::getFontRequest( )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ return maFontRequest;
+ }
+
+ rendering::FontMetrics SAL_CALL CanvasFont::getFontMetrics( )
+ {
+ // TODO
+ return rendering::FontMetrics();
+ }
+
+ OUString SAL_CALL CanvasFont::getImplementationName()
+ {
+ return "DXCanvas::CanvasFont";
+ }
+
+ sal_Bool SAL_CALL CanvasFont::supportsService( const OUString& ServiceName )
+ {
+ return cppu::supportsService( this, ServiceName );
+ }
+
+ uno::Sequence< OUString > SAL_CALL CanvasFont::getSupportedServiceNames()
+ {
+ return { "com.sun.star.rendering.CanvasFont" };
+ }
+
+ double CanvasFont::getCellAscent() const
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ return mpFontFamily->GetCellAscent(0); // TODO(F1): rFontRequest.styleName
+ }
+
+ double CanvasFont::getEmHeight() const
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ return mpFontFamily->GetEmHeight(0); // TODO(F1): rFontRequest.styleName
+ }
+
+ FontSharedPtr CanvasFont::getFont() const
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ return mpFont;
+ }
+
+ const css::geometry::Matrix2D& CanvasFont::getFontMatrix() const
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ return maFontMatrix;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/directx/dx_canvasfont.hxx b/canvas/source/directx/dx_canvasfont.hxx
new file mode 100644
index 0000000000..c5f369d00f
--- /dev/null
+++ b/canvas/source/directx/dx_canvasfont.hxx
@@ -0,0 +1,92 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <cppuhelper/compbase.hxx>
+#include <cppuhelper/basemutex.hxx>
+
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/rendering/XCanvas.hpp>
+#include <com/sun/star/rendering/XCanvasFont.hpp>
+
+#include <rtl/ref.hxx>
+
+#include <memory>
+
+#include "dx_winstuff.hxx"
+#include "dx_gdiplususer.hxx"
+
+
+/* Definition of CanvasFont class */
+
+namespace dxcanvas
+{
+ typedef std::shared_ptr< Gdiplus::Font > FontSharedPtr;
+ typedef std::shared_ptr< Gdiplus::FontFamily > FontFamilySharedPtr;
+
+ typedef ::cppu::WeakComponentImplHelper< css::rendering::XCanvasFont,
+ css::lang::XServiceInfo > CanvasFont_Base;
+
+ class CanvasFont : public ::cppu::BaseMutex,
+ public CanvasFont_Base
+ {
+ public:
+ typedef rtl::Reference<CanvasFont> ImplRef;
+ /// make noncopyable
+ CanvasFont(const CanvasFont&) = delete;
+ const CanvasFont& operator=(const CanvasFont&) = delete;
+
+ CanvasFont( const css::rendering::FontRequest& fontRequest,
+ const css::uno::Sequence< css::beans::PropertyValue >& extraFontProperties,
+ const css::geometry::Matrix2D& fontMatrix );
+
+ /// Dispose all internal references
+ virtual void SAL_CALL disposing() override;
+
+ // XCanvasFont
+ virtual css::uno::Reference< css::rendering::XTextLayout > SAL_CALL createTextLayout( const css::rendering::StringContext& aText, sal_Int8 nDirection, sal_Int64 nRandomSeed ) override;
+ virtual css::rendering::FontRequest SAL_CALL getFontRequest( ) override;
+ virtual css::rendering::FontMetrics SAL_CALL getFontMetrics( ) override;
+ virtual css::uno::Sequence< double > SAL_CALL getAvailableSizes( ) override;
+ virtual css::uno::Sequence< css::beans::PropertyValue > SAL_CALL getExtraFontProperties( ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ double getCellAscent() const;
+ double getEmHeight() const;
+ FontSharedPtr getFont() const;
+ const css::geometry::Matrix2D& getFontMatrix() const;
+ sal_uInt32 getEmphasisMark() const { return mnEmphasisMark; }
+
+ private:
+ GDIPlusUserSharedPtr mpGdiPlusUser;
+ FontFamilySharedPtr mpFontFamily;
+ FontSharedPtr mpFont;
+ css::rendering::FontRequest maFontRequest;
+ sal_uInt32 mnEmphasisMark;
+ css::geometry::Matrix2D maFontMatrix;
+ };
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/directx/dx_canvashelper.cxx b/canvas/source/directx/dx_canvashelper.cxx
new file mode 100644
index 0000000000..1184886784
--- /dev/null
+++ b/canvas/source/directx/dx_canvashelper.cxx
@@ -0,0 +1,814 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+#include <algorithm>
+
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <basegfx/point/b2dpoint.hxx>
+#include <basegfx/utils/canvastools.hxx>
+#include <com/sun/star/rendering/CompositeOperation.hpp>
+#include <com/sun/star/rendering/PathCapType.hpp>
+#include <com/sun/star/rendering/PathJoinType.hpp>
+#include <com/sun/star/rendering/RepaintResult.hpp>
+#include <com/sun/star/rendering/TexturingMode.hpp>
+#include <comphelper/sequence.hxx>
+#include <o3tl/char16_t2wchar_t.hxx>
+#include <rtl/math.hxx>
+#include <comphelper/diagnose_ex.hxx>
+
+#include <canvas/canvastools.hxx>
+
+#include "dx_canvasfont.hxx"
+#include "dx_canvashelper.hxx"
+#include "dx_impltools.hxx"
+#include "dx_spritecanvas.hxx"
+#include "dx_textlayout.hxx"
+#include "dx_vcltools.hxx"
+
+using namespace ::com::sun::star;
+
+namespace dxcanvas
+{
+ namespace
+ {
+ Gdiplus::LineCap gdiLineCapFromCap( sal_Int8 nCapType )
+ {
+ switch( nCapType )
+ {
+ case rendering::PathCapType::BUTT:
+ return Gdiplus::LineCapFlat;
+
+ case rendering::PathCapType::ROUND:
+ return Gdiplus::LineCapRound;
+
+ case rendering::PathCapType::SQUARE:
+ return Gdiplus::LineCapSquare;
+
+ default:
+ ENSURE_OR_THROW( false,
+ "gdiLineCapFromCap(): Unexpected cap type" );
+ }
+
+ return Gdiplus::LineCapFlat;
+ }
+
+ Gdiplus::DashCap gdiDashCapFromCap( sal_Int8 nCapType )
+ {
+ switch( nCapType )
+ {
+ case rendering::PathCapType::BUTT:
+ return Gdiplus::DashCapFlat;
+
+ case rendering::PathCapType::ROUND:
+ return Gdiplus::DashCapRound;
+
+ // Gdiplus does not know square, using flat would make short
+ // dashes disappear, so use triangle as the closest one.
+ case rendering::PathCapType::SQUARE:
+ return Gdiplus::DashCapTriangle;
+
+ default:
+ ENSURE_OR_THROW( false,
+ "gdiDashCapFromCap(): Unexpected cap type" );
+ }
+
+ return Gdiplus::DashCapFlat;
+ }
+
+ Gdiplus::LineJoin gdiJoinFromJoin( sal_Int8 nJoinType )
+ {
+ switch( nJoinType )
+ {
+ case rendering::PathJoinType::NONE:
+ SAL_WARN( "canvas.directx", "gdiJoinFromJoin(): Join NONE not possible, mapping to BEVEL (closest to NONE)" );
+ return Gdiplus::LineJoinBevel;
+
+ case rendering::PathJoinType::MITER:
+ // in GDI+ fallback to Bevel, if miter limit is exceeded, is not done
+ // by Gdiplus::LineJoinMiter but by Gdiplus::LineJoinMiterClipped
+ return Gdiplus::LineJoinMiterClipped;
+
+ case rendering::PathJoinType::ROUND:
+ return Gdiplus::LineJoinRound;
+
+ case rendering::PathJoinType::BEVEL:
+ return Gdiplus::LineJoinBevel;
+
+ default:
+ ENSURE_OR_THROW( false,
+ "gdiJoinFromJoin(): Unexpected join type" );
+ }
+
+ return Gdiplus::LineJoinMiter;
+ }
+ }
+
+ CanvasHelper::CanvasHelper() :
+ mpGdiPlusUser( GDIPlusUser::createInstance() ),
+ mpDevice( nullptr ),
+ mpGraphicsProvider(),
+ maOutputOffset()
+ {
+ }
+
+ void CanvasHelper::disposing()
+ {
+ mpGraphicsProvider.reset();
+ mpDevice = nullptr;
+ mpGdiPlusUser.reset();
+ }
+
+ void CanvasHelper::setDevice( rendering::XGraphicDevice& rDevice )
+ {
+ mpDevice = &rDevice;
+ }
+
+ void CanvasHelper::setTarget( const GraphicsProviderSharedPtr& rTarget )
+ {
+ ENSURE_OR_THROW( rTarget,
+ "CanvasHelper::setTarget(): Invalid target" );
+ ENSURE_OR_THROW( !mpGraphicsProvider,
+ "CanvasHelper::setTarget(): target set, old target would be overwritten" );
+
+ mpGraphicsProvider = rTarget;
+ }
+
+ void CanvasHelper::setTarget( const GraphicsProviderSharedPtr& rTarget,
+ const ::basegfx::B2ISize& rOutputOffset )
+ {
+ ENSURE_OR_THROW( rTarget,
+ "CanvasHelper::setTarget(): invalid target" );
+ ENSURE_OR_THROW( !mpGraphicsProvider,
+ "CanvasHelper::setTarget(): target set, old target would be overwritten" );
+
+ mpGraphicsProvider = rTarget;
+ maOutputOffset = rOutputOffset;
+ }
+
+ void CanvasHelper::clear()
+ {
+ if( needOutput() )
+ {
+ GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );
+ Gdiplus::Color aClearColor{Gdiplus::ARGB(Gdiplus::Color::White)};
+
+ ENSURE_OR_THROW(
+ Gdiplus::Ok == pGraphics->SetCompositingMode(
+ Gdiplus::CompositingModeSourceCopy ), // force set, don't blend
+ "CanvasHelper::clear(): GDI+ SetCompositingMode call failed" );
+ ENSURE_OR_THROW(
+ Gdiplus::Ok == pGraphics->Clear( aClearColor ),
+ "CanvasHelper::clear(): GDI+ Clear call failed" );
+ }
+ }
+
+ void CanvasHelper::drawPoint( const rendering::XCanvas* /*pCanvas*/,
+ const geometry::RealPoint2D& aPoint,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState )
+ {
+ if( needOutput() )
+ {
+ GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );
+
+ setupGraphicsState( pGraphics, viewState, renderState );
+
+ Gdiplus::SolidBrush aBrush(
+ Gdiplus::Color(
+ tools::sequenceToArgb(renderState.DeviceColor)) );
+
+ // determine size of one-by-one device pixel ellipse
+ Gdiplus::Matrix aMatrix;
+ pGraphics->GetTransform(&aMatrix);
+ aMatrix.Invert();
+ Gdiplus::PointF vector(1, 1);
+ aMatrix.TransformVectors(&vector);
+
+ // paint a one-by-one circle, with the given point
+ // in the middle (rounded to float)
+ ENSURE_OR_THROW(
+ Gdiplus::Ok == pGraphics->FillEllipse( &aBrush,
+ // disambiguate call
+ Gdiplus::REAL(aPoint.X),
+ Gdiplus::REAL(aPoint.Y),
+ Gdiplus::REAL(vector.X),
+ Gdiplus::REAL(vector.Y) ),
+ "CanvasHelper::drawPoint(): GDI+ call failed" );
+ }
+ }
+
+ void CanvasHelper::drawLine( const rendering::XCanvas* /*pCanvas*/,
+ const geometry::RealPoint2D& aStartPoint,
+ const geometry::RealPoint2D& aEndPoint,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState )
+ {
+ if( needOutput() )
+ {
+ GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );
+
+ setupGraphicsState( pGraphics, viewState, renderState );
+
+ Gdiplus::Pen aPen(
+ Gdiplus::Color(
+ tools::sequenceToArgb(renderState.DeviceColor)),
+ Gdiplus::REAL(0.0) );
+
+ // #122683# Switched precedence of pixel offset
+ // mode. Seemingly, polygon stroking needs
+ // PixelOffsetModeNone to achieve visually pleasing
+ // results, whereas all other operations (e.g. polygon
+ // fills, bitmaps) look better with PixelOffsetModeHalf.
+ const Gdiplus::PixelOffsetMode aOldMode(
+ pGraphics->GetPixelOffsetMode() );
+ pGraphics->SetPixelOffsetMode( Gdiplus::PixelOffsetModeNone );
+
+ Gdiplus::Status hr = pGraphics->DrawLine( &aPen,
+ Gdiplus::REAL(aStartPoint.X), // disambiguate call
+ Gdiplus::REAL(aStartPoint.Y),
+ Gdiplus::REAL(aEndPoint.X),
+ Gdiplus::REAL(aEndPoint.Y) );
+ pGraphics->SetPixelOffsetMode( aOldMode );
+
+ ENSURE_OR_THROW(
+ Gdiplus::Ok == hr,
+ "CanvasHelper::drawLine(): GDI+ call failed" );
+ }
+ }
+
+ void CanvasHelper::drawBezier( const rendering::XCanvas* /*pCanvas*/,
+ const geometry::RealBezierSegment2D& aBezierSegment,
+ const geometry::RealPoint2D& aEndPoint,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState )
+ {
+ if( needOutput() )
+ {
+ GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );
+
+ setupGraphicsState( pGraphics, viewState, renderState );
+
+ Gdiplus::Pen aPen(
+ Gdiplus::Color(
+ tools::sequenceToArgb(renderState.DeviceColor)),
+ Gdiplus::REAL(0.0) );
+
+ // #122683# Switched precedence of pixel offset
+ // mode. Seemingly, polygon stroking needs
+ // PixelOffsetModeNone to achieve visually pleasing
+ // results, whereas all other operations (e.g. polygon
+ // fills, bitmaps) look better with PixelOffsetModeHalf.
+ const Gdiplus::PixelOffsetMode aOldMode(
+ pGraphics->GetPixelOffsetMode() );
+ pGraphics->SetPixelOffsetMode( Gdiplus::PixelOffsetModeNone );
+
+ Gdiplus::Status hr = pGraphics->DrawBezier( &aPen,
+ Gdiplus::REAL(aBezierSegment.Px), // disambiguate call
+ Gdiplus::REAL(aBezierSegment.Py),
+ Gdiplus::REAL(aBezierSegment.C1x),
+ Gdiplus::REAL(aBezierSegment.C1y),
+ Gdiplus::REAL(aEndPoint.X),
+ Gdiplus::REAL(aEndPoint.Y),
+ Gdiplus::REAL(aBezierSegment.C2x),
+ Gdiplus::REAL(aBezierSegment.C2y) );
+
+ pGraphics->SetPixelOffsetMode( aOldMode );
+
+ ENSURE_OR_THROW(
+ Gdiplus::Ok == hr,
+ "CanvasHelper::drawBezier(): GDI+ call failed" );
+ }
+ }
+
+ uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawPolyPolygon( const rendering::XCanvas* /*pCanvas*/,
+ const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState )
+ {
+ ENSURE_OR_THROW( xPolyPolygon.is(),
+ "CanvasHelper::drawPolyPolygon: polygon is NULL");
+
+ if( needOutput() )
+ {
+ GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );
+
+ setupGraphicsState( pGraphics, viewState, renderState );
+
+ Gdiplus::Pen aPen(
+ Gdiplus::Color(
+ tools::sequenceToArgb(renderState.DeviceColor)),
+ Gdiplus::REAL(0.0) );
+
+ // #122683# Switched precedence of pixel offset
+ // mode. Seemingly, polygon stroking needs
+ // PixelOffsetModeNone to achieve visually pleasing
+ // results, whereas all other operations (e.g. polygon
+ // fills, bitmaps) look better with PixelOffsetModeHalf.
+ const Gdiplus::PixelOffsetMode aOldMode(
+ pGraphics->GetPixelOffsetMode() );
+ pGraphics->SetPixelOffsetMode( Gdiplus::PixelOffsetModeNone );
+
+ GraphicsPathSharedPtr pPath( tools::graphicsPathFromXPolyPolygon2D( xPolyPolygon ) );
+
+ // TODO(E1): Return value
+ Gdiplus::Status hr = pGraphics->DrawPath( &aPen, pPath.get() );
+
+ pGraphics->SetPixelOffsetMode( aOldMode );
+
+ ENSURE_OR_THROW(
+ Gdiplus::Ok == hr,
+ "CanvasHelper::drawPolyPolygon(): GDI+ call failed" );
+ }
+
+ // TODO(P1): Provide caching here.
+ return uno::Reference< rendering::XCachedPrimitive >(nullptr);
+ }
+
+ uno::Reference< rendering::XCachedPrimitive > CanvasHelper::strokePolyPolygon( const rendering::XCanvas* /*pCanvas*/,
+ const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState,
+ const rendering::StrokeAttributes& strokeAttributes )
+ {
+ ENSURE_OR_THROW( xPolyPolygon.is(),
+ "CanvasHelper::drawPolyPolygon: polygon is NULL");
+
+ if( needOutput() )
+ {
+ GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );
+
+ setupGraphicsState( pGraphics, viewState, renderState );
+
+
+ // Setup stroke pen
+
+
+ Gdiplus::Pen aPen(
+ Gdiplus::Color(
+ tools::sequenceToArgb(renderState.DeviceColor)),
+ static_cast< Gdiplus::REAL >(strokeAttributes.StrokeWidth) );
+
+ // #122683# Switched precedence of pixel offset
+ // mode. Seemingly, polygon stroking needs
+ // PixelOffsetModeNone to achieve visually pleasing
+ // results, whereas all other operations (e.g. polygon
+ // fills, bitmaps) look better with PixelOffsetModeHalf.
+ const Gdiplus::PixelOffsetMode aOldMode(
+ pGraphics->GetPixelOffsetMode() );
+ pGraphics->SetPixelOffsetMode( Gdiplus::PixelOffsetModeNone );
+
+ const bool bIsMiter(rendering::PathJoinType::MITER == strokeAttributes.JoinType);
+ const bool bIsNone(rendering::PathJoinType::NONE == strokeAttributes.JoinType);
+
+ if(bIsMiter)
+ aPen.SetMiterLimit( static_cast< Gdiplus::REAL >(strokeAttributes.MiterLimit) );
+
+ const std::vector< Gdiplus::REAL >& rDashArray(
+ ::comphelper::sequenceToContainer< std::vector< Gdiplus::REAL >, double >(
+ strokeAttributes.DashArray ) );
+ if( !rDashArray.empty() )
+ {
+ aPen.SetDashPattern( rDashArray.data(),
+ rDashArray.size() );
+ }
+ aPen.SetLineCap( gdiLineCapFromCap(strokeAttributes.StartCapType),
+ gdiLineCapFromCap(strokeAttributes.EndCapType),
+ gdiDashCapFromCap(strokeAttributes.StartCapType));
+ if(!bIsNone)
+ aPen.SetLineJoin( gdiJoinFromJoin(strokeAttributes.JoinType) );
+
+ GraphicsPathSharedPtr pPath( tools::graphicsPathFromXPolyPolygon2D( xPolyPolygon, bIsNone ) );
+
+ // TODO(E1): Return value
+ Gdiplus::Status hr = pGraphics->DrawPath( &aPen, pPath.get() );
+
+ pGraphics->SetPixelOffsetMode( aOldMode );
+
+ ENSURE_OR_THROW(
+ Gdiplus::Ok == hr,
+ "CanvasHelper::strokePolyPolygon(): GDI+ call failed" );
+ }
+
+ // TODO(P1): Provide caching here.
+ return uno::Reference< rendering::XCachedPrimitive >(nullptr);
+ }
+
+ uno::Reference< rendering::XCachedPrimitive > CanvasHelper::strokeTexturedPolyPolygon( const rendering::XCanvas* /*pCanvas*/,
+ const uno::Reference< rendering::XPolyPolygon2D >& /*xPolyPolygon*/,
+ const rendering::ViewState& /*viewState*/,
+ const rendering::RenderState& /*renderState*/,
+ const uno::Sequence< rendering::Texture >& /*textures*/,
+ const rendering::StrokeAttributes& /*strokeAttributes*/ )
+ {
+ // TODO
+ return uno::Reference< rendering::XCachedPrimitive >(nullptr);
+ }
+
+ uno::Reference< rendering::XCachedPrimitive > CanvasHelper::strokeTextureMappedPolyPolygon( const rendering::XCanvas* /*pCanvas*/,
+ const uno::Reference< rendering::XPolyPolygon2D >& /*xPolyPolygon*/,
+ const rendering::ViewState& /*viewState*/,
+ const rendering::RenderState& /*renderState*/,
+ const uno::Sequence< rendering::Texture >& /*textures*/,
+ const uno::Reference< geometry::XMapping2D >& /*xMapping*/,
+ const rendering::StrokeAttributes& /*strokeAttributes*/ )
+ {
+ // TODO
+ return uno::Reference< rendering::XCachedPrimitive >(nullptr);
+ }
+
+ uno::Reference< rendering::XPolyPolygon2D > CanvasHelper::queryStrokeShapes( const rendering::XCanvas* /*pCanvas*/,
+ const uno::Reference< rendering::XPolyPolygon2D >& /*xPolyPolygon*/,
+ const rendering::ViewState& /*viewState*/,
+ const rendering::RenderState& /*renderState*/,
+ const rendering::StrokeAttributes& /*strokeAttributes*/ )
+ {
+ // TODO
+ return uno::Reference< rendering::XPolyPolygon2D >(nullptr);
+ }
+
+ uno::Reference< rendering::XCachedPrimitive > CanvasHelper::fillPolyPolygon( const rendering::XCanvas* /*pCanvas*/,
+ const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState )
+ {
+ ENSURE_OR_THROW( xPolyPolygon.is(),
+ "CanvasHelper::fillPolyPolygon: polygon is NULL");
+
+ if( needOutput() )
+ {
+ GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );
+
+ setupGraphicsState( pGraphics, viewState, renderState );
+
+ Gdiplus::SolidBrush aBrush(
+ tools::sequenceToArgb(renderState.DeviceColor));
+
+ GraphicsPathSharedPtr pPath( tools::graphicsPathFromXPolyPolygon2D( xPolyPolygon ) );
+
+ // TODO(F1): FillRule
+ ENSURE_OR_THROW( Gdiplus::Ok == pGraphics->FillPath( &aBrush, pPath.get() ),
+ "CanvasHelper::fillPolyPolygon(): GDI+ call failed " );
+ }
+
+ // TODO(P1): Provide caching here.
+ return uno::Reference< rendering::XCachedPrimitive >(nullptr);
+ }
+
+ uno::Reference< rendering::XCachedPrimitive > CanvasHelper::fillTextureMappedPolyPolygon( const rendering::XCanvas* /*pCanvas*/,
+ const uno::Reference< rendering::XPolyPolygon2D >& /*xPolyPolygon*/,
+ const rendering::ViewState& /*viewState*/,
+ const rendering::RenderState& /*renderState*/,
+ const uno::Sequence< rendering::Texture >& /*textures*/,
+ const uno::Reference< geometry::XMapping2D >& /*xMapping*/ )
+ {
+ // TODO
+ return uno::Reference< rendering::XCachedPrimitive >(nullptr);
+ }
+
+ uno::Reference< rendering::XCanvasFont > CanvasHelper::createFont( const rendering::XCanvas* /*pCanvas*/,
+ const rendering::FontRequest& fontRequest,
+ const uno::Sequence< beans::PropertyValue >& extraFontProperties,
+ const geometry::Matrix2D& fontMatrix )
+ {
+ if( needOutput() )
+ {
+ return uno::Reference< rendering::XCanvasFont >(
+ new CanvasFont(fontRequest, extraFontProperties, fontMatrix ) );
+ }
+
+ return uno::Reference< rendering::XCanvasFont >();
+ }
+
+ uno::Sequence< rendering::FontInfo > CanvasHelper::queryAvailableFonts( const rendering::XCanvas* /*pCanvas*/,
+ const rendering::FontInfo& /*aFilter*/,
+ const uno::Sequence< beans::PropertyValue >& /*aFontProperties*/ )
+ {
+ // TODO
+ return uno::Sequence< rendering::FontInfo >();
+ }
+
+ uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawText( const rendering::XCanvas* /*pCanvas*/,
+ const rendering::StringContext& text,
+ const uno::Reference< rendering::XCanvasFont >& xFont,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState,
+ sal_Int8 /*textDirection*/ )
+ {
+ ENSURE_OR_THROW( xFont.is(),
+ "CanvasHelper::drawText: font is NULL");
+
+ if( needOutput() )
+ {
+ GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );
+
+ setupGraphicsState( pGraphics, viewState, renderState );
+
+ Gdiplus::SolidBrush aBrush(
+ Gdiplus::Color(
+ tools::sequenceToArgb(renderState.DeviceColor)));
+
+ CanvasFont::ImplRef pFont(
+ tools::canvasFontFromXFont(xFont) );
+
+ // Move glyphs up, such that output happens at the font
+ // baseline.
+ Gdiplus::PointF aPoint( 0.0,
+ static_cast<Gdiplus::REAL>(-(pFont->getFont()->GetSize()*
+ pFont->getCellAscent() /
+ pFont->getEmHeight())) );
+
+ // TODO(F1): According to
+ // http://support.microsoft.com/default.aspx?scid=kb;EN-US;Q307208,
+ // we might have to revert to GDI and ExTextOut here,
+ // since GDI+ takes the scalability a little bit too
+ // far...
+
+ // TODO(F2): Proper layout (BiDi, CTL)! IMHO must use
+ // DrawDriverString here, and perform layouting myself...
+ ENSURE_OR_THROW(
+ Gdiplus::Ok == pGraphics->DrawString( o3tl::toW(text.Text.copy( text.StartPosition,
+ text.Length ).getStr()),
+ text.Length,
+ pFont->getFont().get(),
+ aPoint,
+ &aBrush ),
+ "CanvasHelper::drawText(): GDI+ call failed" );
+ }
+
+ return uno::Reference< rendering::XCachedPrimitive >(nullptr);
+ }
+
+ uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawTextLayout( const rendering::XCanvas* /*pCanvas*/,
+ const uno::Reference< rendering::XTextLayout >& xLayoutetText,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState )
+ {
+ ENSURE_OR_THROW( xLayoutetText.is(),
+ "CanvasHelper::drawTextLayout: layout is NULL");
+
+ if( needOutput() )
+ {
+ TextLayout* pTextLayout =
+ dynamic_cast< TextLayout* >( xLayoutetText.get() );
+
+ ENSURE_OR_THROW( pTextLayout,
+ "CanvasHelper::drawTextLayout(): TextLayout not compatible with this canvas" );
+
+ pTextLayout->draw( mpGraphicsProvider->getGraphics(),
+ viewState,
+ renderState,
+ maOutputOffset,
+ mpDevice,
+ false );
+ }
+
+ return uno::Reference< rendering::XCachedPrimitive >(nullptr);
+ }
+
+ uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawBitmap( const rendering::XCanvas* /*pCanvas*/,
+ const uno::Reference< rendering::XBitmap >& xBitmap,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState )
+ {
+ ENSURE_OR_THROW( xBitmap.is(),
+ "CanvasHelper::drawBitmap: bitmap is NULL");
+
+ if( needOutput() )
+ {
+ // check whether one of our own objects - need to retrieve
+ // bitmap _before_ calling
+ // GraphicsProvider::getGraphics(), to avoid locking our
+ // own surface.
+ BitmapSharedPtr pGdiBitmap;
+ BitmapProvider* pBitmap = dynamic_cast< BitmapProvider* >(xBitmap.get());
+ if( pBitmap )
+ {
+ IBitmapSharedPtr pDXBitmap( pBitmap->getBitmap() );
+ if( pDXBitmap )
+ pGdiBitmap = pDXBitmap->getBitmap();
+ }
+
+ GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );
+ setupGraphicsState( pGraphics, viewState, renderState );
+
+ if( pGdiBitmap )
+ tools::drawGdiPlusBitmap(pGraphics,pGdiBitmap);
+ else
+ tools::drawVCLBitmapFromXBitmap(pGraphics,
+ xBitmap);
+ }
+
+ // TODO(P1): Provide caching here.
+ return uno::Reference< rendering::XCachedPrimitive >(nullptr);
+ }
+
+ uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawBitmapModulated( const rendering::XCanvas* pCanvas,
+ const uno::Reference< rendering::XBitmap >& xBitmap,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState )
+ {
+ ENSURE_OR_THROW( xBitmap.is(),
+ "CanvasHelper::drawBitmap: bitmap is NULL");
+
+ // no color set -> this is equivalent to a plain drawBitmap(), then
+ if( renderState.DeviceColor.getLength() < 3 )
+ return drawBitmap( pCanvas, xBitmap, viewState, renderState );
+
+ if( needOutput() )
+ {
+ GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );
+
+ setupGraphicsState( pGraphics, viewState, renderState );
+
+ BitmapSharedPtr pBitmap( tools::bitmapFromXBitmap( xBitmap ) );
+ Gdiplus::Rect aRect( 0, 0,
+ pBitmap->GetWidth(),
+ pBitmap->GetHeight() );
+
+ // Setup an ImageAttributes with an alpha-modulating
+ // color matrix.
+ rendering::ARGBColor aARGBColor(
+ mpDevice->getDeviceColorSpace()->convertToARGB(renderState.DeviceColor)[0]);
+
+ Gdiplus::ImageAttributes aImgAttr;
+ tools::setModulateImageAttributes( aImgAttr,
+ aARGBColor.Red,
+ aARGBColor.Green,
+ aARGBColor.Blue,
+ aARGBColor.Alpha );
+
+ ENSURE_OR_THROW(
+ Gdiplus::Ok == pGraphics->DrawImage( pBitmap.get(),
+ aRect,
+ 0, 0,
+ pBitmap->GetWidth(),
+ pBitmap->GetHeight(),
+ Gdiplus::UnitPixel,
+ &aImgAttr ),
+ "CanvasHelper::drawBitmapModulated(): GDI+ call failed" );
+ }
+
+ // TODO(P1): Provide caching here.
+ return uno::Reference< rendering::XCachedPrimitive >(nullptr);
+ }
+
+ uno::Reference< rendering::XGraphicDevice > CanvasHelper::getDevice()
+ {
+ return uno::Reference< rendering::XGraphicDevice >(mpDevice);
+ }
+
+ // private helper
+
+
+ Gdiplus::CompositingMode CanvasHelper::calcCompositingMode( sal_Int8 nMode )
+ {
+ Gdiplus::CompositingMode aRet( Gdiplus::CompositingModeSourceOver );
+
+ switch( nMode )
+ {
+ case rendering::CompositeOperation::OVER:
+ case rendering::CompositeOperation::CLEAR:
+ aRet = Gdiplus::CompositingModeSourceOver;
+ break;
+
+ case rendering::CompositeOperation::SOURCE:
+ aRet = Gdiplus::CompositingModeSourceCopy;
+ break;
+
+ case rendering::CompositeOperation::DESTINATION:
+ case rendering::CompositeOperation::UNDER:
+ case rendering::CompositeOperation::INSIDE:
+ case rendering::CompositeOperation::INSIDE_REVERSE:
+ case rendering::CompositeOperation::OUTSIDE:
+ case rendering::CompositeOperation::OUTSIDE_REVERSE:
+ case rendering::CompositeOperation::ATOP:
+ case rendering::CompositeOperation::ATOP_REVERSE:
+ case rendering::CompositeOperation::XOR:
+ case rendering::CompositeOperation::ADD:
+ case rendering::CompositeOperation::SATURATE:
+ // TODO(F2): Problem, because GDI+ only knows about two compositing modes
+ aRet = Gdiplus::CompositingModeSourceOver;
+ break;
+
+ default:
+ ENSURE_OR_THROW( false, "CanvasHelper::calcCompositingMode: unexpected mode" );
+ break;
+ }
+
+ return aRet;
+ }
+
+ void CanvasHelper::setupGraphicsState( GraphicsSharedPtr const & rGraphics,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState )
+ {
+ ENSURE_OR_THROW( needOutput(),
+ "CanvasHelper::setupGraphicsState: primary graphics invalid" );
+ ENSURE_OR_THROW( mpDevice,
+ "CanvasHelper::setupGraphicsState: reference device invalid" );
+
+ // setup view transform first. Clipping e.g. depends on it
+ ::basegfx::B2DHomMatrix aTransform;
+ ::canvas::tools::getViewStateTransform(aTransform, viewState);
+
+ // add output offset
+ if( !maOutputOffset.equalZero() )
+ {
+ const basegfx::B2DHomMatrix aOutputOffset(basegfx::utils::createTranslateB2DHomMatrix(
+ maOutputOffset.getWidth(), maOutputOffset.getHeight()));
+ aTransform = aOutputOffset * aTransform;
+ }
+
+ Gdiplus::Matrix aMatrix;
+ tools::gdiPlusMatrixFromB2DHomMatrix( aMatrix, aTransform );
+
+ ENSURE_OR_THROW(
+ Gdiplus::Ok == rGraphics->SetTransform( &aMatrix ),
+ "CanvasHelper::setupGraphicsState(): Failed to set GDI+ transformation" );
+
+ // setup view and render state clipping
+ ENSURE_OR_THROW(
+ Gdiplus::Ok == rGraphics->ResetClip(),
+ "CanvasHelper::setupGraphicsState(): Failed to reset GDI+ clip" );
+
+ if( viewState.Clip.is() )
+ {
+ GraphicsPathSharedPtr aClipPath( tools::graphicsPathFromXPolyPolygon2D( viewState.Clip ) );
+
+ // TODO(P3): Cache clip. SetClip( GraphicsPath ) performs abyssmally on GDI+.
+ // Try SetClip( Rect ) or similar for simple clip paths (need some support in
+ // LinePolyPolygon, then)
+ ENSURE_OR_THROW(
+ Gdiplus::Ok == rGraphics->SetClip( aClipPath.get(),
+ Gdiplus::CombineModeIntersect ),
+ "CanvasHelper::setupGraphicsState(): Cannot set GDI+ clip" );
+ }
+
+ // setup overall transform only now. View clip above was relative to
+ // view transform
+ ::canvas::tools::mergeViewAndRenderTransform(aTransform,
+ viewState,
+ renderState);
+
+ // add output offset
+ if( !maOutputOffset.equalZero() )
+ {
+ const basegfx::B2DHomMatrix aOutputOffset(basegfx::utils::createTranslateB2DHomMatrix(
+ maOutputOffset.getWidth(), maOutputOffset.getHeight()));
+ aTransform = aOutputOffset * aTransform;
+ }
+
+ tools::gdiPlusMatrixFromB2DHomMatrix( aMatrix, aTransform );
+
+ ENSURE_OR_THROW(
+ Gdiplus::Ok == rGraphics->SetTransform( &aMatrix ),
+ "CanvasHelper::setupGraphicsState(): Cannot set GDI+ transformation" );
+
+ if( renderState.Clip.is() )
+ {
+ GraphicsPathSharedPtr aClipPath( tools::graphicsPathFromXPolyPolygon2D( renderState.Clip ) );
+
+ // TODO(P3): Cache clip. SetClip( GraphicsPath ) performs abyssmally on GDI+.
+ // Try SetClip( Rect ) or similar for simple clip paths (need some support in
+ // LinePolyPolygon, then)
+ ENSURE_OR_THROW(
+ Gdiplus::Ok == rGraphics->SetClip( aClipPath.get(),
+ Gdiplus::CombineModeIntersect ),
+ "CanvasHelper::setupGraphicsState(): Cannot set GDI+ clip" );
+ }
+
+ // setup compositing
+ const Gdiplus::CompositingMode eCompositing( calcCompositingMode( renderState.CompositeOperation ) );
+ ENSURE_OR_THROW(
+ Gdiplus::Ok == rGraphics->SetCompositingMode( eCompositing ),
+ "CanvasHelper::setupGraphicsState(): Cannot set GDI* compositing mode)" );
+ }
+
+ void CanvasHelper::flush() const
+ {
+ if( needOutput() )
+ mpGraphicsProvider->getGraphics()->Flush( Gdiplus::FlushIntentionSync );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/directx/dx_canvashelper.hxx b/canvas/source/directx/dx_canvashelper.hxx
new file mode 100644
index 0000000000..54731a08eb
--- /dev/null
+++ b/canvas/source/directx/dx_canvashelper.hxx
@@ -0,0 +1,252 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/rendering/XCanvas.hpp>
+#include <com/sun/star/rendering/XBitmapCanvas.hpp>
+#include <com/sun/star/rendering/XIntegerBitmap.hpp>
+
+#include <basegfx/vector/b2isize.hxx>
+#include <basegfx/vector/b2dsize.hxx>
+
+#include "dx_graphicsprovider.hxx"
+#include "dx_gdiplususer.hxx"
+#include "dx_impltools.hxx"
+
+
+namespace dxcanvas
+{
+ /** Helper class for basic canvas functionality. Also offers
+ optional backbuffer painting, when providing it with a second
+ HDC to render into.
+ */
+ class CanvasHelper
+ {
+ public:
+ CanvasHelper();
+
+ /// make noncopyable
+ CanvasHelper(const CanvasHelper&) = delete;
+ const CanvasHelper& operator=(const CanvasHelper&) = delete;
+
+ /// Release all references
+ void disposing();
+
+ /** Initialize canvas helper
+
+ This method late-initializes the canvas helper, providing
+ it with the necessary device and output objects. Note that
+ the CanvasHelper does <em>not</em> take ownership of the
+ passed rDevice reference, nor does it perform any
+ reference counting. Thus, to prevent the reference counted
+ SpriteCanvas object from deletion, the user of this class
+ is responsible for holding ref-counted references itself!
+
+ @param rDevice
+ Reference device this canvas is associated with
+
+ */
+ void setDevice( css::rendering::XGraphicDevice& rDevice );
+
+ /** Set the target for rendering operations
+
+ @param rTarget
+ Render target
+ */
+ void setTarget( const GraphicsProviderSharedPtr& rTarget );
+
+ /** Set the target for rendering operations
+
+ @param rTarget
+ Render target
+
+ @param rOutputOffset
+ Output offset in pixel
+ */
+ void setTarget( const GraphicsProviderSharedPtr& rTarget,
+ const ::basegfx::B2ISize& rOutputOffset );
+
+
+ // CanvasHelper functionality
+ // ==========================
+
+ // XCanvas (only providing, not implementing the
+ // interface. Also note subtle method parameter differences)
+ void clear();
+ void drawPoint( const css::rendering::XCanvas* pCanvas,
+ const css::geometry::RealPoint2D& aPoint,
+ const css::rendering::ViewState& viewState,
+ const css::rendering::RenderState& renderState );
+ void drawLine( const css::rendering::XCanvas* pCanvas,
+ const css::geometry::RealPoint2D& aStartPoint,
+ const css::geometry::RealPoint2D& aEndPoint,
+ const css::rendering::ViewState& viewState,
+ const css::rendering::RenderState& renderState );
+ void drawBezier( const css::rendering::XCanvas* pCanvas,
+ const css::geometry::RealBezierSegment2D& aBezierSegment,
+ const css::geometry::RealPoint2D& aEndPoint,
+ const css::rendering::ViewState& viewState,
+ const css::rendering::RenderState& renderState );
+ css::uno::Reference< css::rendering::XCachedPrimitive >
+ drawPolyPolygon( const css::rendering::XCanvas* pCanvas,
+ const css::uno::Reference<
+ css::rendering::XPolyPolygon2D >& xPolyPolygon,
+ const css::rendering::ViewState& viewState,
+ const css::rendering::RenderState& renderState );
+ css::uno::Reference< css::rendering::XCachedPrimitive >
+ strokePolyPolygon( const css::rendering::XCanvas* pCanvas,
+ const css::uno::Reference<
+ css::rendering::XPolyPolygon2D >& xPolyPolygon,
+ const css::rendering::ViewState& viewState,
+ const css::rendering::RenderState& renderState,
+ const css::rendering::StrokeAttributes& strokeAttributes );
+ css::uno::Reference< css::rendering::XCachedPrimitive >
+ strokeTexturedPolyPolygon( const css::rendering::XCanvas* pCanvas,
+ const css::uno::Reference<
+ css::rendering::XPolyPolygon2D >& xPolyPolygon,
+ const css::rendering::ViewState& viewState,
+ const css::rendering::RenderState& renderState,
+ const css::uno::Sequence<
+ css::rendering::Texture >& textures,
+ const css::rendering::StrokeAttributes& strokeAttributes );
+ css::uno::Reference< css::rendering::XCachedPrimitive >
+ strokeTextureMappedPolyPolygon( const css::rendering::XCanvas* pCanvas,
+ const css::uno::Reference<
+ css::rendering::XPolyPolygon2D >& xPolyPolygon,
+ const css::rendering::ViewState& viewState,
+ const css::rendering::RenderState& renderState,
+ const css::uno::Sequence<
+ css::rendering::Texture >& textures,
+ const css::uno::Reference<
+ css::geometry::XMapping2D >& xMapping,
+ const css::rendering::StrokeAttributes& strokeAttributes );
+ css::uno::Reference< css::rendering::XPolyPolygon2D >
+ queryStrokeShapes( const css::rendering::XCanvas* pCanvas,
+ const css::uno::Reference<
+ css::rendering::XPolyPolygon2D >& xPolyPolygon,
+ const css::rendering::ViewState& viewState,
+ const css::rendering::RenderState& renderState,
+ const css::rendering::StrokeAttributes& strokeAttributes );
+ css::uno::Reference< css::rendering::XCachedPrimitive >
+ fillPolyPolygon( const css::rendering::XCanvas* pCanvas,
+ const css::uno::Reference<
+ css::rendering::XPolyPolygon2D >& xPolyPolygon,
+ const css::rendering::ViewState& viewState,
+ const css::rendering::RenderState& renderState );
+ css::uno::Reference< css::rendering::XCachedPrimitive >
+ fillTexturedPolyPolygon( const css::rendering::XCanvas* pCanvas,
+ const css::uno::Reference<
+ css::rendering::XPolyPolygon2D >& xPolyPolygon,
+ const css::rendering::ViewState& viewState,
+ const css::rendering::RenderState& renderState,
+ const css::uno::Sequence<
+ css::rendering::Texture >& textures );
+ css::uno::Reference< css::rendering::XCachedPrimitive >
+ fillTextureMappedPolyPolygon( const css::rendering::XCanvas* pCanvas,
+ const css::uno::Reference<
+ css::rendering::XPolyPolygon2D >& xPolyPolygon,
+ const css::rendering::ViewState& viewState,
+ const css::rendering::RenderState& renderState,
+ const css::uno::Sequence<
+ css::rendering::Texture >& textures,
+ const css::uno::Reference<
+ css::geometry::XMapping2D >& xMapping );
+
+ css::uno::Reference< css::rendering::XCanvasFont >
+ createFont( const css::rendering::XCanvas* pCanvas,
+ const css::rendering::FontRequest& fontRequest,
+ const css::uno::Sequence<
+ css::beans::PropertyValue >& extraFontProperties,
+ const css::geometry::Matrix2D& fontMatrix );
+
+ css::uno::Sequence< css::rendering::FontInfo >
+ queryAvailableFonts( const css::rendering::XCanvas* pCanvas,
+ const css::rendering::FontInfo& aFilter,
+ const css::uno::Sequence<
+ css::beans::PropertyValue >& aFontProperties );
+
+ css::uno::Reference< css::rendering::XCachedPrimitive >
+ drawText( const css::rendering::XCanvas* pCanvas,
+ const css::rendering::StringContext& text,
+ const css::uno::Reference<
+ css::rendering::XCanvasFont >& xFont,
+ const css::rendering::ViewState& viewState,
+ const css::rendering::RenderState& renderState,
+ sal_Int8 textDirection );
+
+ css::uno::Reference< css::rendering::XCachedPrimitive >
+ drawTextLayout( const css::rendering::XCanvas* pCanvas,
+ const css::uno::Reference<
+ css::rendering::XTextLayout >& laidOutText,
+ const css::rendering::ViewState& viewState,
+ const css::rendering::RenderState& renderState );
+
+ css::uno::Reference< css::rendering::XCachedPrimitive >
+ drawBitmap( const css::rendering::XCanvas* pCanvas,
+ const css::uno::Reference<
+ css::rendering::XBitmap >& xBitmap,
+ const css::rendering::ViewState& viewState,
+ const css::rendering::RenderState& renderState );
+ css::uno::Reference< css::rendering::XCachedPrimitive >
+ drawBitmapModulated( const css::rendering::XCanvas* pCanvas,
+ const css::uno::Reference<
+ css::rendering::XBitmap >& xBitmap,
+ const css::rendering::ViewState& viewState,
+ const css::rendering::RenderState& renderState );
+ css::uno::Reference< css::rendering::XGraphicDevice >
+ getDevice();
+
+ // Flush drawing queue to screen
+ void flush() const;
+
+ /** Called from XCanvas base classes, to notify that content
+ is _about_ to change
+ */
+ void modifying() {}
+
+ protected:
+ /// Refcounted global GDI+ state container
+ GDIPlusUserSharedPtr mpGdiPlusUser;
+
+ /** Phyical output device
+
+ Deliberately not a refcounted reference, because of
+ potential circular references for spritecanvas.
+ */
+ css::rendering::XGraphicDevice* mpDevice;
+
+ /// Provides the Gdiplus::Graphics to render into
+ GraphicsProviderSharedPtr mpGraphicsProvider;
+
+ bool needOutput() const { return bool(mpGraphicsProvider); };
+
+ // returns transparency of color
+ void setupGraphicsState( GraphicsSharedPtr const & rGraphics,
+ const css::rendering::ViewState& viewState,
+ const css::rendering::RenderState& renderState );
+
+ Gdiplus::CompositingMode calcCompositingMode( sal_Int8 nMode );
+
+ /// Current (transformation-independent) output buffer offset
+ ::basegfx::B2ISize maOutputOffset;
+ };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/directx/dx_canvashelper_texturefill.cxx b/canvas/source/directx/dx_canvashelper_texturefill.cxx
new file mode 100644
index 0000000000..c64773539e
--- /dev/null
+++ b/canvas/source/directx/dx_canvashelper_texturefill.cxx
@@ -0,0 +1,608 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <cstdlib>
+#include <memory>
+#include <tuple>
+
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <basegfx/numeric/ftools.hxx>
+#include <basegfx/point/b2dpoint.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/range/b2drectangle.hxx>
+#include <basegfx/utils/canvastools.hxx>
+#include <basegfx/utils/keystoplerp.hxx>
+#include <basegfx/utils/lerp.hxx>
+#include <basegfx/utils/tools.hxx>
+#include <com/sun/star/rendering/TexturingMode.hpp>
+#include <rtl/math.hxx>
+#include <comphelper/diagnose_ex.hxx>
+
+#include <parametricpolypolygon.hxx>
+
+#include "dx_canvashelper.hxx"
+#include "dx_impltools.hxx"
+#include "dx_spritecanvas.hxx"
+
+
+using namespace ::com::sun::star;
+
+namespace dxcanvas
+{
+ namespace
+ {
+ typedef std::shared_ptr< Gdiplus::PathGradientBrush > PathGradientBrushSharedPtr;
+
+ bool fillLinearGradient( GraphicsSharedPtr const & rGraphics,
+ const ::canvas::ParametricPolyPolygon::Values& /*rValues*/,
+ const std::vector< Gdiplus::Color >& rColors,
+ const std::vector< Gdiplus::REAL >& rStops,
+ const GraphicsPathSharedPtr& rFillPath,
+ const rendering::Texture& texture )
+ {
+ // setup a linear gradient with given colors
+
+
+ Gdiplus::LinearGradientBrush aBrush(
+ Gdiplus::PointF(0.0f,
+ 0.5f),
+ Gdiplus::PointF(1.0f,
+ 0.5f),
+ rColors[0],
+ rColors[1] );
+
+ aBrush.SetInterpolationColors(rColors.data(),
+ rStops.data(),
+ rColors.size());
+
+ // render background color, as LinearGradientBrush does not
+ // properly support the WrapModeClamp repeat mode
+ Gdiplus::SolidBrush aBackgroundBrush( rColors[0] );
+ rGraphics->FillPath( &aBackgroundBrush, rFillPath.get() );
+
+ // TODO(F2): This does not yet support other repeat modes
+ // except clamp, and probably also no multi-texturing
+
+ // calculate parallelogram of gradient in object space, extend
+ // top and bottom of it such that they cover the whole fill
+ // path bound area
+ ::basegfx::B2DHomMatrix aTextureTransform;
+ ::basegfx::unotools::homMatrixFromAffineMatrix( aTextureTransform,
+ texture.AffineTransform );
+
+ ::basegfx::B2DPoint aLeftTop( 0.0, 0.0 );
+ ::basegfx::B2DPoint aLeftBottom( 0.0, 1.0 );
+ ::basegfx::B2DPoint aRightTop( 1.0, 0.0 );
+ ::basegfx::B2DPoint aRightBottom( 1.0, 1.0 );
+
+ aLeftTop *= aTextureTransform;
+ aLeftBottom *= aTextureTransform;
+ aRightTop *= aTextureTransform;
+ aRightBottom*= aTextureTransform;
+
+ Gdiplus::RectF aBounds;
+ rFillPath->GetBounds( &aBounds );
+
+ // now, we potentially have to enlarge our gradient area
+ // atop and below the transformed [0,1]x[0,1] unit rect,
+ // for the gradient to fill the complete bound rect.
+ ::basegfx::utils::infiniteLineFromParallelogram( aLeftTop,
+ aLeftBottom,
+ aRightTop,
+ aRightBottom,
+ tools::b2dRangeFromGdiPlusRectF( aBounds ) );
+
+ // calc length of bound rect diagonal
+ const double nDiagonalLength(
+ hypot( aBounds.Width,
+ aBounds.Height ) );
+
+ // generate a path which covers the 'right' side of the
+ // gradient, extending two times the bound rect diagonal to
+ // the right (and thus covering the whole half plane 'right'
+ // of the gradient). Take the middle of the gradient as the
+ // 'left' side of the polygon, to not fall victim to rounding
+ // errors at the edge.
+ ::basegfx::B2DVector aDirection( aLeftTop - aLeftBottom );
+ aDirection = ::basegfx::getNormalizedPerpendicular( aDirection );
+ aDirection *= nDiagonalLength;
+
+ const ::basegfx::B2DPoint aHalfPlaneLeftTop( (aLeftTop + aRightTop) * 0.5 );
+ const ::basegfx::B2DPoint aHalfPlaneLeftBottom( (aLeftBottom + aRightBottom) * 0.5 );
+ const ::basegfx::B2DPoint aHalfPlaneRightTop( aRightTop + aDirection );
+ const ::basegfx::B2DPoint aHalfPlaneRightBottom( aRightBottom + aDirection );
+
+ Gdiplus::GraphicsPath aSolidFillPath;
+ aSolidFillPath.AddLine( static_cast<Gdiplus::REAL>(aHalfPlaneLeftTop.getX()),
+ static_cast<Gdiplus::REAL>(aHalfPlaneLeftTop.getY()),
+ static_cast<Gdiplus::REAL>(aHalfPlaneRightTop.getX()),
+ static_cast<Gdiplus::REAL>(aHalfPlaneRightTop.getY()) );
+ aSolidFillPath.AddLine( static_cast<Gdiplus::REAL>(aHalfPlaneRightBottom.getX()),
+ static_cast<Gdiplus::REAL>(aHalfPlaneRightBottom.getY()),
+ static_cast<Gdiplus::REAL>(aHalfPlaneLeftBottom.getX()),
+ static_cast<Gdiplus::REAL>(aHalfPlaneLeftBottom.getY()) );
+ aSolidFillPath.CloseFigure();
+
+ // limit output to fill path, we've just generated a path that
+ // might be substantially larger
+ if( Gdiplus::Ok != rGraphics->SetClip( rFillPath.get(),
+ Gdiplus::CombineModeIntersect ) )
+ {
+ return false;
+ }
+
+ Gdiplus::SolidBrush aBackgroundBrush2( rColors.back() );
+ rGraphics->FillPath( &aBackgroundBrush2, &aSolidFillPath );
+
+ // generate clip polygon from the extended parallelogram
+ // (exploit the feature that distinct lines in a figure are
+ // automatically closed by a straight line)
+ Gdiplus::GraphicsPath aClipPath;
+ aClipPath.AddLine( static_cast<Gdiplus::REAL>(aLeftTop.getX()),
+ static_cast<Gdiplus::REAL>(aLeftTop.getY()),
+ static_cast<Gdiplus::REAL>(aRightTop.getX()),
+ static_cast<Gdiplus::REAL>(aRightTop.getY()) );
+ aClipPath.AddLine( static_cast<Gdiplus::REAL>(aRightBottom.getX()),
+ static_cast<Gdiplus::REAL>(aRightBottom.getY()),
+ static_cast<Gdiplus::REAL>(aLeftBottom.getX()),
+ static_cast<Gdiplus::REAL>(aLeftBottom.getY()) );
+ aClipPath.CloseFigure();
+
+ // limit output to a _single_ strip of the gradient (have to
+ // clip here, since GDI+ wrapmode clamp does not work here)
+ if( Gdiplus::Ok != rGraphics->SetClip( &aClipPath,
+ Gdiplus::CombineModeIntersect ) )
+ {
+ return false;
+ }
+
+ // now, finally, output the gradient
+ Gdiplus::Matrix aMatrix;
+ tools::gdiPlusMatrixFromAffineMatrix2D( aMatrix,
+ texture.AffineTransform );
+ aBrush.SetTransform( &aMatrix );
+
+ rGraphics->FillRectangle( &aBrush, aBounds );
+
+ return true;
+ }
+
+ int numColorSteps( const Gdiplus::Color& rColor1, const Gdiplus::Color& rColor2 )
+ {
+ return std::max(
+ std::abs( rColor1.GetRed() - rColor2.GetRed() ),
+ std::max(
+ std::abs( rColor1.GetGreen() - rColor2.GetGreen() ),
+ std::abs( rColor1.GetBlue() - rColor2.GetBlue() ) ) );
+ }
+
+ bool fillPolygonalGradient( const ::canvas::ParametricPolyPolygon::Values& rValues,
+ const std::vector< Gdiplus::Color >& rColors,
+ const std::vector< Gdiplus::REAL >& rStops,
+ GraphicsSharedPtr const & rGraphics,
+ const GraphicsPathSharedPtr& rPath,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState,
+ const rendering::Texture& texture )
+ {
+ // copy original fill path object, might have to change it
+ // below
+ GraphicsPathSharedPtr pFillPath( rPath );
+ const ::basegfx::B2DPolygon& rGradientPoly( rValues.maGradientPoly );
+
+ PathGradientBrushSharedPtr pGradientBrush;
+
+ // fill background uniformly with end color
+ Gdiplus::SolidBrush aBackgroundBrush( rColors[0] );
+ rGraphics->FillPath( &aBackgroundBrush, pFillPath.get() );
+
+ Gdiplus::Matrix aMatrix;
+ // scale focus according to aspect ratio: for wider-than-tall
+ // bounds (nAspectRatio > 1.0), the focus must have non-zero
+ // width. Specifically, a bound rect twice as wide as tall has
+ // a focus of half its width.
+ if( !::rtl::math::approxEqual(rValues.mnAspectRatio,
+ 1.0) )
+ {
+ // KLUDGE 1:
+
+ // And here comes the greatest shortcoming of the GDI+
+ // gradients ever: SetFocusScales completely ignores
+ // transformations, both when set at the PathGradientBrush
+ // and for the world coordinate system. Thus, to correctly
+ // display anisotrophic path gradients, we have to render
+ // them by hand. WTF.
+
+ // TODO(F2): This does not yet support other repeat modes
+ // except clamp, and probably also no multi-texturing
+
+ // limit output to to-be-filled polygon
+ if( Gdiplus::Ok != rGraphics->SetClip( pFillPath.get(),
+ Gdiplus::CombineModeIntersect ) )
+ {
+ return false;
+ }
+
+ // disable anti-aliasing, if any
+ const Gdiplus::SmoothingMode eOldAAMode( rGraphics->GetSmoothingMode() );
+ rGraphics->SetSmoothingMode( Gdiplus::SmoothingModeHighSpeed );
+
+
+ // determine number of steps to use
+
+
+ // TODO(Q2): Unify step calculations with VCL canvas
+ int nColorSteps = 0;
+ for( size_t i=0; i<rColors.size()-1; ++i )
+ nColorSteps += numColorSteps(rColors[i],rColors[i+1]);
+ ::basegfx::B2DHomMatrix aTotalTransform;
+ const int nStepCount=
+ ::canvas::tools::calcGradientStepCount(aTotalTransform,
+ viewState,
+ renderState,
+ texture,
+ nColorSteps);
+
+ ::basegfx::B2DHomMatrix aTextureTransform;
+ ::basegfx::unotools::homMatrixFromAffineMatrix( aTextureTransform,
+ texture.AffineTransform );
+ // determine overall transformation for inner polygon (might
+ // have to be prefixed by anisotrophic scaling)
+ ::basegfx::B2DHomMatrix aInnerPolygonTransformMatrix;
+
+ // For performance reasons, we create a temporary VCL polygon
+ // here, keep it all the way and only change the vertex values
+ // in the loop below (as ::Polygon is a pimpl class, creating
+ // one every loop turn would really stress the mem allocator)
+ ::basegfx::B2DPolygon aOuterPoly( rGradientPoly );
+ ::basegfx::B2DPolygon aInnerPoly;
+
+ // subdivide polygon _before_ rendering, would otherwise have
+ // to be performed on every loop turn.
+ if( aOuterPoly.areControlPointsUsed() )
+ aOuterPoly = ::basegfx::utils::adaptiveSubdivideByAngle(aOuterPoly);
+
+ aInnerPoly = aOuterPoly;
+ aOuterPoly.transform(aTextureTransform);
+
+
+ // apply scaling (possibly anisotrophic) to inner polygon
+
+
+ // scale inner polygon according to aspect ratio: for
+ // wider-than-tall bounds (nAspectRatio > 1.0), the inner
+ // polygon, representing the gradient focus, must have
+ // non-zero width. Specifically, a bound rect twice as wide as
+ // tall has a focus polygon of half its width.
+ const double nAspectRatio( rValues.mnAspectRatio );
+ if( nAspectRatio > 1.0 )
+ {
+ // width > height case
+ aInnerPolygonTransformMatrix.scale( 1.0 - 1.0/nAspectRatio,
+ 0.0 );
+ }
+ else if( nAspectRatio < 1.0 )
+ {
+ // width < height case
+ aInnerPolygonTransformMatrix.scale( 0.0,
+ 1.0 - nAspectRatio );
+ }
+ else
+ {
+ // isotrophic case
+ aInnerPolygonTransformMatrix.scale( 0.0, 0.0 );
+ }
+
+ // and finally, add texture transform to it.
+ aInnerPolygonTransformMatrix *= aTextureTransform;
+
+ // apply final matrix to polygon
+ aInnerPoly.transform( aInnerPolygonTransformMatrix );
+
+ Gdiplus::GraphicsPath aCurrPath;
+ Gdiplus::SolidBrush aFillBrush( rColors[0] );
+ const sal_uInt32 nNumPoints( aOuterPoly.count() );
+ basegfx::utils::KeyStopLerp aLerper(rValues.maStops);
+ for( int i=1; i<nStepCount; ++i )
+ {
+ std::ptrdiff_t nIndex;
+ double fAlpha;
+ const double fT( i/double(nStepCount) );
+ std::tie(nIndex,fAlpha)=aLerper.lerp(fT);
+
+ const Gdiplus::Color aFillColor(
+ static_cast<BYTE>( basegfx::utils::lerp(rColors[nIndex].GetRed(),rColors[nIndex+1].GetRed(),fAlpha) ),
+ static_cast<BYTE>( basegfx::utils::lerp(rColors[nIndex].GetGreen(),rColors[nIndex+1].GetGreen(),fAlpha) ),
+ static_cast<BYTE>( basegfx::utils::lerp(rColors[nIndex].GetBlue(),rColors[nIndex+1].GetBlue(),fAlpha) ) );
+
+ aFillBrush.SetColor( aFillColor );
+ aCurrPath.Reset(); aCurrPath.StartFigure();
+ for( unsigned int p=1; p<nNumPoints; ++p )
+ {
+ const ::basegfx::B2DPoint& rOuterPoint1( aOuterPoly.getB2DPoint(p-1) );
+ const ::basegfx::B2DPoint& rInnerPoint1( aInnerPoly.getB2DPoint(p-1) );
+ const ::basegfx::B2DPoint& rOuterPoint2( aOuterPoly.getB2DPoint(p) );
+ const ::basegfx::B2DPoint& rInnerPoint2( aInnerPoly.getB2DPoint(p) );
+
+ aCurrPath.AddLine(
+ Gdiplus::REAL(fT*rInnerPoint1.getX() + (1-fT)*rOuterPoint1.getX()),
+ Gdiplus::REAL(fT*rInnerPoint1.getY() + (1-fT)*rOuterPoint1.getY()),
+ Gdiplus::REAL(fT*rInnerPoint2.getX() + (1-fT)*rOuterPoint2.getX()),
+ Gdiplus::REAL(fT*rInnerPoint2.getY() + (1-fT)*rOuterPoint2.getY()));
+ }
+ aCurrPath.CloseFigure();
+
+ rGraphics->FillPath( &aFillBrush, &aCurrPath );
+ }
+
+ // reset to old anti-alias mode
+ rGraphics->SetSmoothingMode( eOldAAMode );
+ }
+ else
+ {
+ // KLUDGE 2:
+
+ // We're generating a PathGradientBrush from scratch here,
+ // and put in a transformed GraphicsPath (transformed with
+ // the texture transform). This is because the
+ // straight-forward approach to store a Brush pointer at
+ // this class and set a texture transform via
+ // PathGradientBrush::SetTransform() is spoiled by MS: it
+ // seems that _either_ the texture transform, _or_ the
+ // transform at the Graphics can be set, but not both. If
+ // one sets both, only the translational components of the
+ // texture is respected.
+
+ tools::gdiPlusMatrixFromAffineMatrix2D( aMatrix,
+ texture.AffineTransform );
+ GraphicsPathSharedPtr pGradientPath(
+ tools::graphicsPathFromB2DPolygon( rValues.maGradientPoly ));
+ pGradientPath->Transform( &aMatrix );
+
+ pGradientBrush
+ = std::make_shared<Gdiplus::PathGradientBrush>( pGradientPath.get() );
+ pGradientBrush->SetInterpolationColors( rColors.data(),
+ rStops.data(),
+ rStops.size() );
+
+ // explicitly setup center point. Since the center of GDI+
+ // gradients are by default the _centroid_ of the path
+ // (i.e. the weighted sum of edge points), it will not
+ // necessarily coincide with our notion of center.
+ Gdiplus::PointF aCenterPoint(0, 0);
+ aMatrix.TransformPoints( &aCenterPoint );
+ pGradientBrush->SetCenterPoint( aCenterPoint );
+
+ const bool bTileX( texture.RepeatModeX != rendering::TexturingMode::CLAMP );
+ const bool bTileY( texture.RepeatModeY != rendering::TexturingMode::CLAMP );
+
+ if( bTileX && bTileY )
+ pGradientBrush->SetWrapMode( Gdiplus::WrapModeTile );
+ else
+ {
+ OSL_ENSURE( bTileY == bTileX,
+ "ParametricPolyPolygon::fillPolygonalGradient(): Cannot have repeat x and repeat y differ!" );
+
+ pGradientBrush->SetWrapMode( Gdiplus::WrapModeClamp );
+ }
+
+ // render actual gradient
+ rGraphics->FillPath( pGradientBrush.get(), pFillPath.get() );
+ }
+
+#if OSL_DEBUG_LEVEL > 0
+ Gdiplus::Pen aPen( Gdiplus::Color( 255, 255, 0, 0 ),
+ 0.0001f );
+
+ rGraphics->DrawRectangle( &aPen,
+ Gdiplus::RectF( 0.0f, 0.0f,
+ 1.0f, 1.0f ) );
+#endif
+
+ return true;
+ }
+
+ bool fillGradient( const ::canvas::ParametricPolyPolygon::Values& rValues,
+ const std::vector< Gdiplus::Color >& rColors,
+ const std::vector< Gdiplus::REAL >& rStops,
+ GraphicsSharedPtr const & rGraphics,
+ const GraphicsPathSharedPtr& rPath,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState,
+ const rendering::Texture& texture )
+ {
+ switch( rValues.meType )
+ {
+ case ::canvas::ParametricPolyPolygon::GradientType::Linear:
+ fillLinearGradient( rGraphics,
+ rValues,
+ rColors,
+ rStops,
+ rPath,
+ texture );
+ break;
+
+ case ::canvas::ParametricPolyPolygon::GradientType::Elliptical:
+ case ::canvas::ParametricPolyPolygon::GradientType::Rectangular:
+ fillPolygonalGradient( rValues,
+ rColors,
+ rStops,
+ rGraphics,
+ rPath,
+ viewState,
+ renderState,
+ texture );
+ break;
+
+ default:
+ ENSURE_OR_THROW( false,
+ "CanvasHelper::fillGradient(): Unexpected case" );
+ }
+
+ return true;
+ }
+
+ void fillBitmap( const uno::Reference< rendering::XBitmap >& xBitmap,
+ GraphicsSharedPtr const & rGraphics,
+ const GraphicsPathSharedPtr& rPath,
+ const rendering::Texture& rTexture )
+ {
+ OSL_ENSURE( rTexture.RepeatModeX ==
+ rTexture.RepeatModeY,
+ "CanvasHelper::fillBitmap(): GDI+ cannot handle differing X/Y repeat mode." );
+
+ const bool bClamp( rTexture.RepeatModeX == rendering::TexturingMode::NONE &&
+ rTexture.RepeatModeY == rendering::TexturingMode::NONE );
+
+ const geometry::IntegerSize2D aBmpSize( xBitmap->getSize() );
+ ENSURE_ARG_OR_THROW( aBmpSize.Width != 0 &&
+ aBmpSize.Height != 0,
+ "CanvasHelper::fillBitmap(): zero-sized texture bitmap" );
+
+ // TODO(P3): Detect case that path is rectangle and
+ // bitmap is just scaled into that. Then, we can
+ // render directly, without generating a temporary
+ // GDI+ bitmap (this is significant, because drawing
+ // layer presents background object bitmap in that
+ // way!)
+ BitmapSharedPtr pBitmap(
+ tools::bitmapFromXBitmap( xBitmap ) );
+
+ TextureBrushSharedPtr pBrush;
+ if( ::rtl::math::approxEqual( rTexture.Alpha,
+ 1.0 ) )
+ {
+ pBrush = std::make_shared<Gdiplus::TextureBrush>(
+ pBitmap.get(),
+ bClamp ? Gdiplus::WrapModeClamp : Gdiplus::WrapModeTile );
+ }
+ else
+ {
+ Gdiplus::ImageAttributes aImgAttr;
+
+ tools::setModulateImageAttributes( aImgAttr,
+ 1.0,
+ 1.0,
+ 1.0,
+ rTexture.Alpha );
+
+ Gdiplus::Rect aRect(0,0,
+ aBmpSize.Width,
+ aBmpSize.Height);
+ pBrush = std::make_shared<Gdiplus::TextureBrush>(
+ pBitmap.get(),
+ aRect,
+ &aImgAttr );
+
+ pBrush->SetWrapMode(
+ bClamp ? Gdiplus::WrapModeClamp : Gdiplus::WrapModeTile );
+ }
+
+ Gdiplus::Matrix aTextureTransform;
+ tools::gdiPlusMatrixFromAffineMatrix2D( aTextureTransform,
+ rTexture.AffineTransform );
+
+ // scale down bitmap to [0,1]x[0,1] rect, as required
+ // from the XCanvas interface.
+ pBrush->MultiplyTransform( &aTextureTransform );
+ pBrush->ScaleTransform( static_cast< Gdiplus::REAL >(1.0/aBmpSize.Width),
+ static_cast< Gdiplus::REAL >(1.0/aBmpSize.Height) );
+
+ // TODO(F1): FillRule
+ ENSURE_OR_THROW(
+ Gdiplus::Ok == rGraphics->FillPath( pBrush.get(),
+ rPath.get() ),
+ "CanvasHelper::fillTexturedPolyPolygon(): GDI+ call failed" );
+ }
+ }
+
+
+ uno::Reference< rendering::XCachedPrimitive > CanvasHelper::fillTexturedPolyPolygon( const rendering::XCanvas* /*pCanvas*/,
+ const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState,
+ const uno::Sequence< rendering::Texture >& textures )
+ {
+ ENSURE_OR_THROW( xPolyPolygon.is(),
+ "CanvasHelper::fillTexturedPolyPolygon: polygon is NULL");
+ ENSURE_OR_THROW( textures.getLength(),
+ "CanvasHelper::fillTexturedPolyPolygon: empty texture sequence");
+
+ if( needOutput() )
+ {
+ GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );
+
+ setupGraphicsState( pGraphics, viewState, renderState );
+
+ // TODO(F1): Multi-texturing
+ if( textures[0].Gradient.is() )
+ {
+ // try to cast XParametricPolyPolygon2D reference to
+ // our implementation class.
+ ::canvas::ParametricPolyPolygon* pGradient =
+ dynamic_cast< ::canvas::ParametricPolyPolygon* >( textures[0].Gradient.get() );
+
+ if( pGradient )
+ {
+ const ::canvas::ParametricPolyPolygon::Values& rValues(
+ pGradient->getValues() );
+
+ OSL_ASSERT(rValues.maColors.getLength() == rValues.maStops.getLength()
+ && rValues.maColors.getLength() > 1);
+
+ std::vector< Gdiplus::Color > aColors(rValues.maColors.getLength());
+ std::transform(&rValues.maColors[0],
+ &rValues.maColors[0]+rValues.maColors.getLength(),
+ aColors.begin(),
+ [](const uno::Sequence< double >& aDoubleSequence) { return tools::sequenceToArgb(aDoubleSequence); } );
+ std::vector< Gdiplus::REAL > aStops;
+ comphelper::sequenceToContainer(aStops,rValues.maStops);
+
+ // TODO(E1): Return value
+ // TODO(F1): FillRule
+ fillGradient( rValues,
+ aColors,
+ aStops,
+ pGraphics,
+ tools::graphicsPathFromXPolyPolygon2D( xPolyPolygon ),
+ viewState,
+ renderState,
+ textures[0] );
+ }
+ }
+ else if( textures[0].Bitmap.is() )
+ {
+ // TODO(E1): Return value
+ // TODO(F1): FillRule
+ fillBitmap( textures[0].Bitmap,
+ pGraphics,
+ tools::graphicsPathFromXPolyPolygon2D( xPolyPolygon ),
+ textures[0] );
+ }
+ }
+
+ // TODO(P1): Provide caching here.
+ return uno::Reference< rendering::XCachedPrimitive >(nullptr);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/directx/dx_config.cxx b/canvas/source/directx/dx_config.cxx
new file mode 100644
index 0000000000..c1d9020845
--- /dev/null
+++ b/canvas/source/directx/dx_config.cxx
@@ -0,0 +1,152 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+#include <basegfx/vector/b2ivector.hxx>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <comphelper/anytostring.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <osl/diagnose.h>
+#include <comphelper/diagnose_ex.hxx>
+
+#include "dx_config.hxx"
+
+using namespace com::sun::star;
+
+namespace dxcanvas
+{
+ DXCanvasItem::DXCanvasItem() :
+ ConfigItem(
+ "Office.Canvas/DXCanvas",
+ ConfigItemMode::NONE ),
+ maValues(),
+ maMaxTextureSize(),
+ mbDenylistCurrentDevice(false),
+ mbValuesDirty(false)
+ {
+ try
+ {
+ uno::Sequence< uno::Any > aProps( GetProperties( { "DeviceDenylist" } ));
+ uno::Sequence< sal_Int32 > aValues;
+
+ if( aProps.getLength() > 0 &&
+ (aProps[0] >>= aValues) )
+ {
+ const sal_Int32* pValues = aValues.getConstArray();
+ const sal_Int32 nNumEntries( aValues.getLength()*sizeof(sal_Int32)/sizeof(DeviceInfo) );
+ for( sal_Int32 i=0; i<nNumEntries; ++i )
+ {
+ DeviceInfo aInfo;
+ aInfo.nVendorId = *pValues++;
+ aInfo.nDeviceId = *pValues++;
+ aInfo.nDeviceSubSysId = *pValues++;
+ aInfo.nDeviceRevision = *pValues++;
+ aInfo.nDriverId = *pValues++;
+ aInfo.nDriverVersion = *pValues++;
+ aInfo.nDriverSubVersion = *pValues++;
+ aInfo.nDriverBuildId = *pValues++;
+ maValues.insert(aInfo);
+ }
+ }
+
+ aProps = GetProperties( { "DenylistCurrentDevice" } );
+ if( aProps.getLength() > 0 )
+ aProps[0] >>= mbDenylistCurrentDevice;
+
+ aProps = GetProperties( { "MaxTextureSize" } );
+ if( aProps.getLength() > 0 )
+ maMaxTextureSize = aProps[0].get<sal_Int32>();
+ else
+ maMaxTextureSize.reset();
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "canvas", "" );
+ }
+ }
+
+ DXCanvasItem::~DXCanvasItem()
+ {
+ if( !mbValuesDirty )
+ return;
+
+ try
+ {
+ uno::Sequence< sal_Int32 > aValues( sizeof(DeviceInfo)/sizeof(sal_Int32)*maValues.size() );
+
+ sal_Int32* pValues = aValues.getArray();
+ for( const auto& rValueSet : maValues )
+ {
+ const DeviceInfo& rInfo( rValueSet );
+ *pValues++ = rInfo.nVendorId;
+ *pValues++ = rInfo.nDeviceId;
+ *pValues++ = rInfo.nDeviceSubSysId;
+ *pValues++ = rInfo.nDeviceRevision;
+ *pValues++ = rInfo.nDriverId;
+ *pValues++ = rInfo.nDriverVersion;
+ *pValues++ = rInfo.nDriverSubVersion;
+ *pValues++ = rInfo.nDriverBuildId;
+ }
+
+ PutProperties({"DeviceDenylist"}, {css::uno::Any(aValues)});
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "canvas", "" );
+ }
+ }
+
+ void DXCanvasItem::Notify( const css::uno::Sequence<OUString>& ) {}
+ void DXCanvasItem::ImplCommit() {}
+
+ bool DXCanvasItem::isDeviceUsable( const DeviceInfo& rDeviceInfo ) const
+ {
+ return maValues.find(rDeviceInfo) == maValues.end();
+ }
+
+ bool DXCanvasItem::isDenylistCurrentDevice() const
+ {
+ return mbDenylistCurrentDevice;
+ }
+
+ void DXCanvasItem::denylistDevice( const DeviceInfo& rDeviceInfo )
+ {
+ mbValuesDirty = true;
+ maValues.insert(rDeviceInfo);
+ }
+
+ void DXCanvasItem::adaptMaxTextureSize( basegfx::B2IVector& io_maxTextureSize ) const
+ {
+ if( maMaxTextureSize )
+ {
+ io_maxTextureSize.setX(
+ std::min( *maMaxTextureSize,
+ io_maxTextureSize.getX() ));
+ io_maxTextureSize.setY(
+ std::min( *maMaxTextureSize,
+ io_maxTextureSize.getY() ));
+ }
+ }
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/directx/dx_config.hxx b/canvas/source/directx/dx_config.hxx
new file mode 100644
index 0000000000..14a77d19da
--- /dev/null
+++ b/canvas/source/directx/dx_config.hxx
@@ -0,0 +1,80 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <unotools/configitem.hxx>
+#include <optional>
+#include <set>
+
+namespace basegfx { class B2IVector; }
+
+namespace dxcanvas
+{
+ /** Provide DX canvas config data
+ */
+ class DXCanvasItem : public ::utl::ConfigItem
+ {
+ public:
+ DXCanvasItem();
+
+ struct DeviceInfo
+ {
+ sal_Int32 nVendorId;
+ sal_Int32 nDeviceId;
+ sal_Int32 nDeviceSubSysId;
+ sal_Int32 nDeviceRevision;
+
+ sal_Int32 nDriverId;
+ sal_Int32 nDriverVersion;
+ sal_Int32 nDriverSubVersion;
+ sal_Int32 nDriverBuildId;
+
+ bool operator<( const DeviceInfo& rRHS ) const
+ {
+ return nVendorId != rRHS.nVendorId ? nVendorId < rRHS.nVendorId :
+ (nDeviceId != rRHS.nDeviceId ? nDeviceId < rRHS.nDeviceId :
+ (nDeviceSubSysId != rRHS.nDeviceSubSysId ? nDeviceSubSysId < rRHS.nDeviceSubSysId :
+ (nDeviceRevision != rRHS.nDeviceRevision ? nDeviceRevision < rRHS.nDeviceRevision :
+ (nDriverId != rRHS.nDriverId ? nDriverId < rRHS.nDriverId :
+ (nDriverVersion != rRHS.nDriverVersion ? nDriverVersion < rRHS.nDriverVersion :
+ (nDriverSubVersion != rRHS.nDriverSubVersion ? nDriverSubVersion < rRHS.nDriverSubVersion :
+ (nDriverBuildId != rRHS.nDriverBuildId && nDriverBuildId < rRHS.nDriverBuildId)))))));
+ }
+ };
+
+ ~DXCanvasItem() override;
+
+ bool isDeviceUsable( const DeviceInfo& rDeviceInfo ) const;
+ bool isDenylistCurrentDevice() const;
+ void denylistDevice( const DeviceInfo& rDeviceInfo );
+ void adaptMaxTextureSize( basegfx::B2IVector& io_maxTextureSize ) const;
+ virtual void Notify( const css::uno::Sequence<OUString>& aPropertyNames) override;
+
+ private:
+ virtual void ImplCommit() override;
+ typedef std::set< DeviceInfo > ValueSet;
+ ValueSet maValues;
+ std::optional<sal_Int32> maMaxTextureSize;
+ bool mbDenylistCurrentDevice;
+ bool mbValuesDirty;
+ };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/directx/dx_devicehelper.cxx b/canvas/source/directx/dx_devicehelper.cxx
new file mode 100644
index 0000000000..dada6238e0
--- /dev/null
+++ b/canvas/source/directx/dx_devicehelper.cxx
@@ -0,0 +1,199 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <memory>
+
+#include <basegfx/utils/canvastools.hxx>
+#include <com/sun/star/lang/NoSupportException.hpp>
+#include <comphelper/diagnose_ex.hxx>
+#include <vcl/canvastools.hxx>
+#include <vcl/outdev.hxx>
+#include <vcl/sysdata.hxx>
+
+#include <canvas/canvastools.hxx>
+
+#include "dx_canvasbitmap.hxx"
+#include "dx_devicehelper.hxx"
+#include "dx_linepolypolygon.hxx"
+#include "dx_spritecanvas.hxx"
+#include "dx_winstuff.hxx"
+
+using namespace ::com::sun::star;
+
+namespace dxcanvas
+{
+ DeviceHelper::DeviceHelper() :
+ mpDevice( nullptr ),
+ mnHDC(nullptr),
+ mpOutDev(nullptr)
+ {
+ }
+
+ DeviceHelper::~DeviceHelper()
+ {
+ }
+
+ void DeviceHelper::init( HDC hdc, OutputDevice* pOutDev,
+ rendering::XGraphicDevice& rDevice )
+ {
+ mnHDC = hdc;
+ mpDevice = &rDevice;
+ mpOutDev = pOutDev;
+ }
+
+ void DeviceHelper::disposing()
+ {
+ // release all references
+ mnHDC = nullptr;
+ mpDevice = nullptr;
+ mpOutDev = nullptr;
+ }
+
+ geometry::RealSize2D DeviceHelper::getPhysicalResolution()
+ {
+ if( !mpDevice )
+ return ::canvas::tools::createInfiniteSize2D(); // we're disposed
+
+ HDC hDC = getHDC();
+ ENSURE_OR_THROW( hDC,
+ "DeviceHelper::getPhysicalResolution(): cannot retrieve HDC from window" );
+
+ const int nHorzRes( GetDeviceCaps( hDC,
+ LOGPIXELSX ) );
+ const int nVertRes( GetDeviceCaps( hDC,
+ LOGPIXELSY ) );
+
+ return geometry::RealSize2D( nHorzRes*25.4,
+ nVertRes*25.4 );
+ }
+
+ geometry::RealSize2D DeviceHelper::getPhysicalSize()
+ {
+ if( !mpDevice )
+ return ::canvas::tools::createInfiniteSize2D(); // we're disposed
+
+ HDC hDC=getHDC();
+ ENSURE_OR_THROW( hDC,
+ "DeviceHelper::getPhysicalSize(): cannot retrieve HDC from window" );
+
+ const int nHorzSize( GetDeviceCaps( hDC,
+ HORZSIZE ) );
+ const int nVertSize( GetDeviceCaps( hDC,
+ VERTSIZE ) );
+
+ return geometry::RealSize2D( nHorzSize,
+ nVertSize );
+ }
+
+ uno::Reference< rendering::XLinePolyPolygon2D > DeviceHelper::createCompatibleLinePolyPolygon(
+ const uno::Reference< rendering::XGraphicDevice >& /*rDevice*/,
+ const uno::Sequence< uno::Sequence< geometry::RealPoint2D > >& points )
+ {
+ if( !mpDevice )
+ return uno::Reference< rendering::XLinePolyPolygon2D >(); // we're disposed
+
+ return uno::Reference< rendering::XLinePolyPolygon2D >(
+ new LinePolyPolygon(
+ ::basegfx::unotools::polyPolygonFromPoint2DSequenceSequence( points ) ) );
+ }
+
+ uno::Reference< rendering::XBezierPolyPolygon2D > DeviceHelper::createCompatibleBezierPolyPolygon(
+ const uno::Reference< rendering::XGraphicDevice >& /*rDevice*/,
+ const uno::Sequence< uno::Sequence< geometry::RealBezierSegment2D > >& points )
+ {
+ if( !mpDevice )
+ return uno::Reference< rendering::XBezierPolyPolygon2D >(); // we're disposed
+
+ return uno::Reference< rendering::XBezierPolyPolygon2D >(
+ new LinePolyPolygon(
+ ::basegfx::unotools::polyPolygonFromBezier2DSequenceSequence( points ) ) );
+ }
+
+ uno::Reference< rendering::XBitmap > DeviceHelper::createCompatibleBitmap(
+ const uno::Reference< rendering::XGraphicDevice >& /*rDevice*/,
+ const geometry::IntegerSize2D& size )
+ {
+ if( !mpDevice )
+ return uno::Reference< rendering::XBitmap >(); // we're disposed
+
+ DXBitmapSharedPtr pBitmap = std::make_shared<DXBitmap>(
+ ::basegfx::unotools::b2ISizeFromIntegerSize2D(size),
+ false);
+
+ // create a 24bit RGB system memory surface
+ return uno::Reference< rendering::XBitmap >(new CanvasBitmap(pBitmap,mpDevice));
+ }
+
+ uno::Reference< rendering::XVolatileBitmap > DeviceHelper::createVolatileBitmap(
+ const uno::Reference< rendering::XGraphicDevice >& /*rDevice*/,
+ const geometry::IntegerSize2D& /*size*/ )
+ {
+ return uno::Reference< rendering::XVolatileBitmap >();
+ }
+
+ uno::Reference< rendering::XBitmap > DeviceHelper::createCompatibleAlphaBitmap(
+ const uno::Reference< rendering::XGraphicDevice >& /*rDevice*/,
+ const geometry::IntegerSize2D& size )
+ {
+ if( !mpDevice )
+ return uno::Reference< rendering::XBitmap >(); // we're disposed
+
+ DXBitmapSharedPtr pBitmap = std::make_shared<DXBitmap>(
+ ::basegfx::unotools::b2ISizeFromIntegerSize2D(size),
+ true);
+
+ // create a 32bit ARGB system memory surface
+ return uno::Reference< rendering::XBitmap >(new CanvasBitmap(pBitmap,mpDevice));
+ }
+
+ uno::Reference< rendering::XVolatileBitmap > DeviceHelper::createVolatileAlphaBitmap(
+ const uno::Reference< rendering::XGraphicDevice >& /*rDevice*/,
+ const geometry::IntegerSize2D& /*size*/ )
+ {
+ return uno::Reference< rendering::XVolatileBitmap >();
+ }
+
+ uno::Any DeviceHelper::isAccelerated() const
+ {
+ return css::uno::Any(false);
+ }
+
+ uno::Any DeviceHelper::getDeviceHandle() const
+ {
+ return uno::Any( reinterpret_cast< sal_Int64 >(mpOutDev.get()) );
+ }
+
+ uno::Any DeviceHelper::getSurfaceHandle() const
+ {
+ // TODO(F1): expose DirectDraw object
+ //return mpBackBuffer->getBitmap().get();
+ return uno::Any();
+ }
+
+ uno::Reference<rendering::XColorSpace> DeviceHelper::getColorSpace() const
+ {
+ // always the same
+ static uno::Reference<rendering::XColorSpace> theSpace = vcl::unotools::createStandardColorSpace();
+ return theSpace;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/directx/dx_devicehelper.hxx b/canvas/source/directx/dx_devicehelper.hxx
new file mode 100644
index 0000000000..d5fa62b07c
--- /dev/null
+++ b/canvas/source/directx/dx_devicehelper.hxx
@@ -0,0 +1,114 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/awt/Rectangle.hpp>
+#include <com/sun/star/rendering/XGraphicDevice.hpp>
+#include <com/sun/star/rendering/XBufferController.hpp>
+
+#include "dx_rendermodule.hxx"
+#include "dx_bitmap.hxx"
+
+#include <rendering/isurfaceproxymanager.hxx>
+#include <vcl/vclptr.hxx>
+
+class OutputDevice;
+/* Definition of DeviceHelper class */
+
+namespace dxcanvas
+{
+ class DeviceHelper
+ {
+ public:
+ DeviceHelper();
+ ~DeviceHelper();
+
+ /// make noncopyable
+ DeviceHelper(const DeviceHelper&) = delete;
+ const DeviceHelper& operator=(const DeviceHelper&) = delete;
+
+ /** Init the device helper
+
+ @param hdc
+ private or class dc of the output device. is only stored,
+ not release
+
+ @param rDevice
+ Ref back to owning UNO device
+ */
+ void init( HDC hdc, OutputDevice* pOutputDev,
+ css::rendering::XGraphicDevice& rDevice );
+
+ /// Dispose all internal references
+ void disposing();
+
+ // XWindowGraphicDevice
+ css::geometry::RealSize2D getPhysicalResolution();
+ css::geometry::RealSize2D getPhysicalSize();
+ css::uno::Reference< css::rendering::XLinePolyPolygon2D > createCompatibleLinePolyPolygon(
+ const css::uno::Reference< css::rendering::XGraphicDevice >& rDevice,
+ const css::uno::Sequence< css::uno::Sequence< css::geometry::RealPoint2D > >& points );
+ css::uno::Reference< css::rendering::XBezierPolyPolygon2D > createCompatibleBezierPolyPolygon(
+ const css::uno::Reference< css::rendering::XGraphicDevice >& rDevice,
+ const css::uno::Sequence< css::uno::Sequence< css::geometry::RealBezierSegment2D > >& points );
+ css::uno::Reference< css::rendering::XBitmap > createCompatibleBitmap(
+ const css::uno::Reference< css::rendering::XGraphicDevice >& rDevice,
+ const css::geometry::IntegerSize2D& size );
+ css::uno::Reference< css::rendering::XVolatileBitmap > createVolatileBitmap(
+ const css::uno::Reference< css::rendering::XGraphicDevice >& rDevice,
+ const css::geometry::IntegerSize2D& size );
+ css::uno::Reference< css::rendering::XBitmap > createCompatibleAlphaBitmap(
+ const css::uno::Reference< css::rendering::XGraphicDevice >& rDevice,
+ const css::geometry::IntegerSize2D& size );
+ css::uno::Reference< css::rendering::XVolatileBitmap > createVolatileAlphaBitmap(
+ const css::uno::Reference< css::rendering::XGraphicDevice >& rDevice,
+ const css::geometry::IntegerSize2D& size );
+
+ css::uno::Any isAccelerated() const;
+ css::uno::Any getDeviceHandle() const;
+ css::uno::Any getSurfaceHandle() const;
+ css::uno::Reference<
+ css::rendering::XColorSpace > getColorSpace() const;
+
+ /** called when DumpScreenContent property is enabled on
+ XGraphicDevice, and writes out bitmaps of current screen.
+ */
+ void dumpScreenContent() const {}
+
+ protected:
+ HDC getHDC() const { return mnHDC; }
+ css::rendering::XGraphicDevice* getDevice() const { return mpDevice; }
+
+ private:
+ /** Phyical output device
+
+ Deliberately not a refcounted reference, because of
+ potential circular references for canvas. Needed to
+ create bitmaps
+ */
+ css::rendering::XGraphicDevice* mpDevice;
+ HDC mnHDC;
+ VclPtr<OutputDevice> mpOutDev;
+ };
+
+ typedef ::rtl::Reference< css::rendering::XGraphicDevice > DeviceRef;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/directx/dx_gdiplususer.cxx b/canvas/source/directx/dx_gdiplususer.cxx
new file mode 100644
index 0000000000..85c143cff4
--- /dev/null
+++ b/canvas/source/directx/dx_gdiplususer.cxx
@@ -0,0 +1,74 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <osl/mutex.hxx>
+
+#include "dx_gdiplususer.hxx"
+#include "dx_winstuff.hxx"
+
+
+namespace dxcanvas
+{
+ namespace
+ {
+ ::osl::Mutex* p_gdiPlusUsageCountMutex( osl::Mutex::getGlobalMutex() );
+ int n_gdiPlusUsageCount( 0 );
+
+ ULONG_PTR a_GdiPlusToken; // GDI+ handle. Owned by this object
+ }
+
+ GDIPlusUser::GDIPlusUserSharedPtr GDIPlusUser::createInstance()
+ {
+ return GDIPlusUserSharedPtr( new GDIPlusUser() );
+ }
+
+ GDIPlusUser::~GDIPlusUser()
+ {
+ ::osl::MutexGuard aGuard( *p_gdiPlusUsageCountMutex );
+
+ --n_gdiPlusUsageCount;
+
+ if( n_gdiPlusUsageCount == 0 )
+ Gdiplus::GdiplusShutdown( a_GdiPlusToken );
+ }
+
+ GDIPlusUser::GDIPlusUser()
+ {
+ ::osl::MutexGuard aGuard( *p_gdiPlusUsageCountMutex );
+
+ if( n_gdiPlusUsageCount == 0 )
+ {
+ // Setup GDI+
+
+ // No extras here, simply taking GdiplusStartupInput's
+ // default constructor
+ Gdiplus::GdiplusStartupInput gdiPlusStartupInput;
+
+ Gdiplus::GdiplusStartup( &a_GdiPlusToken,
+ &gdiPlusStartupInput,
+ nullptr );
+ }
+
+ ++n_gdiPlusUsageCount;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/directx/dx_gdiplususer.hxx b/canvas/source/directx/dx_gdiplususer.hxx
new file mode 100644
index 0000000000..4b0a0f52aa
--- /dev/null
+++ b/canvas/source/directx/dx_gdiplususer.hxx
@@ -0,0 +1,45 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+#include <memory>
+
+/* Definition of GDIPlusUser class */
+
+namespace dxcanvas
+{
+ class GDIPlusUser
+ {
+ public:
+ typedef std::shared_ptr< GDIPlusUser > GDIPlusUserSharedPtr;
+
+ static GDIPlusUserSharedPtr createInstance();
+ ~GDIPlusUser();
+
+ private:
+ GDIPlusUser(); // create us via factory method
+ };
+
+ typedef GDIPlusUser::GDIPlusUserSharedPtr GDIPlusUserSharedPtr;
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/directx/dx_graphicsprovider.hxx b/canvas/source/directx/dx_graphicsprovider.hxx
new file mode 100644
index 0000000000..2d6d618842
--- /dev/null
+++ b/canvas/source/directx/dx_graphicsprovider.hxx
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "dx_winstuff.hxx"
+#include <memory>
+
+namespace Gdiplus{ class Graphics; }
+
+namespace dxcanvas
+{
+ /** Provider of a Gdiplus::Graphics. Interface
+ */
+ class GraphicsProvider
+ {
+ public:
+ GraphicsProvider() = default;
+ virtual ~GraphicsProvider() {}
+ /// make noncopyable
+ GraphicsProvider(const GraphicsProvider&) = delete;
+ GraphicsProvider& operator=(const GraphicsProvider&) = delete;
+
+ virtual GraphicsSharedPtr getGraphics() = 0;
+ };
+
+ typedef std::shared_ptr< GraphicsProvider > GraphicsProviderSharedPtr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/directx/dx_ibitmap.hxx b/canvas/source/directx/dx_ibitmap.hxx
new file mode 100644
index 0000000000..9669f67674
--- /dev/null
+++ b/canvas/source/directx/dx_ibitmap.hxx
@@ -0,0 +1,62 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/rendering/XCanvas.hpp>
+#include <com/sun/star/rendering/XIntegerBitmap.hpp>
+#include <basegfx/vector/b2isize.hxx>
+#include <basegfx/vector/b2ivector.hxx>
+#include <basegfx/point/b2dpoint.hxx>
+#include <basegfx/range/b2drange.hxx>
+#include <memory>
+#include "dx_graphicsprovider.hxx"
+
+namespace dxcanvas
+{
+ /// Interface for internal canvas bitmap objects
+ struct IBitmap : public GraphicsProvider
+ {
+ virtual BitmapSharedPtr getBitmap() const = 0;
+ virtual ::basegfx::B2ISize getSize() const = 0;
+ virtual bool hasAlpha() const = 0;
+
+ virtual css::uno::Sequence< sal_Int8 > getData(
+ css::rendering::IntegerBitmapLayout& bitmapLayout,
+ const css::geometry::IntegerRectangle2D& rect ) = 0;
+
+ virtual void setData(
+ const css::uno::Sequence< sal_Int8 >& data,
+ const css::rendering::IntegerBitmapLayout& bitmapLayout,
+ const css::geometry::IntegerRectangle2D& rect ) = 0;
+
+ virtual void setPixel(
+ const css::uno::Sequence< sal_Int8 >& color,
+ const css::rendering::IntegerBitmapLayout& bitmapLayout,
+ const css::geometry::IntegerPoint2D& pos ) = 0;
+
+ virtual css::uno::Sequence< sal_Int8 > getPixel(
+ css::rendering::IntegerBitmapLayout& bitmapLayout,
+ const css::geometry::IntegerPoint2D& pos ) = 0;
+ };
+
+ typedef std::shared_ptr<IBitmap> IBitmapSharedPtr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/directx/dx_impltools.cxx b/canvas/source/directx/dx_impltools.cxx
new file mode 100644
index 0000000000..0364ebcbdd
--- /dev/null
+++ b/canvas/source/directx/dx_impltools.cxx
@@ -0,0 +1,629 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <algorithm>
+#include <memory>
+#include <vector>
+
+
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/numeric/ftools.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolypolygon.hxx>
+#include <basegfx/range/b2drectangle.hxx>
+#include <basegfx/range/b2irectangle.hxx>
+#include <basegfx/utils/canvastools.hxx>
+#include <com/sun/star/geometry/IntegerRectangle2D.hpp>
+#include <com/sun/star/geometry/RealPoint2D.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XUnoTunnel.hpp>
+#include <comphelper/diagnose_ex.hxx>
+
+#include <canvas/canvastools.hxx>
+#include <verifyinput.hxx>
+
+#include "dx_canvas.hxx"
+#include "dx_canvasbitmap.hxx"
+#include "dx_canvasfont.hxx"
+#include "dx_impltools.hxx"
+#include "dx_linepolypolygon.hxx"
+#include "dx_spritecanvas.hxx"
+#include "dx_vcltools.hxx"
+
+
+using namespace ::com::sun::star;
+
+
+namespace dxcanvas::tools
+{
+ ::basegfx::B2DPolyPolygon polyPolygonFromXPolyPolygon2D( const uno::Reference< rendering::XPolyPolygon2D >& xPoly )
+ {
+ LinePolyPolygon* pPolyImpl = dynamic_cast< LinePolyPolygon* >( xPoly.get() );
+
+ if( pPolyImpl )
+ {
+ return pPolyImpl->getPolyPolygon();
+ }
+ else
+ {
+ const sal_Int32 nPolys( xPoly->getNumberOfPolygons() );
+
+ // not a known implementation object - try data source
+ // interfaces
+ uno::Reference< rendering::XBezierPolyPolygon2D > xBezierPoly(
+ xPoly,
+ uno::UNO_QUERY );
+
+ if( xBezierPoly.is() )
+ {
+ return ::basegfx::unotools::polyPolygonFromBezier2DSequenceSequence(
+ xBezierPoly->getBezierSegments( 0,
+ nPolys,
+ 0,
+ -1 ) );
+ }
+ else
+ {
+ uno::Reference< rendering::XLinePolyPolygon2D > xLinePoly(
+ xPoly,
+ uno::UNO_QUERY );
+
+ // no implementation class and no data provider
+ // found - contract violation.
+ ENSURE_ARG_OR_THROW( xLinePoly.is(),
+ "VCLCanvas::polyPolygonFromXPolyPolygon2D(): Invalid input "
+ "poly-polygon, cannot retrieve vertex data" );
+
+ return ::basegfx::unotools::polyPolygonFromPoint2DSequenceSequence(
+ xLinePoly->getPoints( 0,
+ nPolys,
+ 0,
+ -1 ) );
+ }
+ }
+ }
+
+ void setupGraphics( Gdiplus::Graphics& rGraphics )
+ {
+ // setup graphics with (somewhat arbitrary) defaults
+ //rGraphics.SetCompositingQuality( Gdiplus::CompositingQualityHighQuality );
+ rGraphics.SetCompositingQuality( Gdiplus::CompositingQualityHighSpeed );
+ //rGraphics.SetInterpolationMode( Gdiplus::InterpolationModeHighQualityBilinear ); // with prefiltering for shrinks
+ rGraphics.SetInterpolationMode( Gdiplus::InterpolationModeBilinear );
+
+ // #122683# Switched precedence of pixel offset
+ // mode. Seemingly, polygon stroking needs
+ // PixelOffsetModeNone to achieve visually pleasing
+ // results, whereas all other operations (e.g. polygon
+ // fills, bitmaps) look better with PixelOffsetModeHalf.
+ rGraphics.SetPixelOffsetMode( Gdiplus::PixelOffsetModeHalf ); // Pixel center at (0.5, 0.5) etc.
+ //rGraphics.SetPixelOffsetMode( Gdiplus::PixelOffsetModeNone );
+
+ //rGraphics.SetSmoothingMode( Gdiplus::SmoothingModeHighSpeed ); // no line/curve antialiasing
+ //rGraphics.SetSmoothingMode( Gdiplus::SmoothingModeHighQuality );
+ rGraphics.SetSmoothingMode( Gdiplus::SmoothingModeAntiAlias );
+ //rGraphics.SetTextRenderingHint( Gdiplus::TextRenderingHintAntiAlias );
+ rGraphics.SetTextRenderingHint( Gdiplus::TextRenderingHintSystemDefault );
+ rGraphics.SetPageUnit(Gdiplus::UnitPixel);
+ }
+
+ Gdiplus::Graphics* createGraphicsFromHDC(HDC aHDC)
+ {
+ Gdiplus::Graphics* pRet = new Gdiplus::Graphics(aHDC);
+ setupGraphics( *pRet );
+ return pRet;
+ }
+
+ GraphicsSharedPtr createGraphicsFromBitmap(const BitmapSharedPtr& rBitmap)
+ {
+ GraphicsSharedPtr pRet(Gdiplus::Graphics::FromImage(rBitmap.get()));
+ if( pRet )
+ setupGraphics( *pRet );
+ return pRet;
+ }
+
+ void gdiPlusMatrixFromB2DHomMatrix( Gdiplus::Matrix& rGdiplusMatrix, const ::basegfx::B2DHomMatrix& rMatrix )
+ {
+ rGdiplusMatrix.SetElements( static_cast<Gdiplus::REAL>(rMatrix.get(0,0)),
+ static_cast<Gdiplus::REAL>(rMatrix.get(1,0)),
+ static_cast<Gdiplus::REAL>(rMatrix.get(0,1)),
+ static_cast<Gdiplus::REAL>(rMatrix.get(1,1)),
+ static_cast<Gdiplus::REAL>(rMatrix.get(0,2)),
+ static_cast<Gdiplus::REAL>(rMatrix.get(1,2)) );
+ }
+
+ void gdiPlusMatrixFromAffineMatrix2D( Gdiplus::Matrix& rGdiplusMatrix,
+ const geometry::AffineMatrix2D& rMatrix )
+ {
+ rGdiplusMatrix.SetElements( static_cast<Gdiplus::REAL>(rMatrix.m00),
+ static_cast<Gdiplus::REAL>(rMatrix.m10),
+ static_cast<Gdiplus::REAL>(rMatrix.m01),
+ static_cast<Gdiplus::REAL>(rMatrix.m11),
+ static_cast<Gdiplus::REAL>(rMatrix.m02),
+ static_cast<Gdiplus::REAL>(rMatrix.m12) );
+ }
+
+ namespace
+ {
+ // TODO(P2): Check whether this gets inlined. If not, make functor
+ // out of it
+ Gdiplus::PointF implGdiPlusPointFromRealPoint2D( const css::geometry::RealPoint2D& rPoint )
+ {
+ return Gdiplus::PointF( static_cast<Gdiplus::REAL>(rPoint.X),
+ static_cast<Gdiplus::REAL>(rPoint.Y) );
+ }
+
+ void graphicsPathFromB2DPolygon( GraphicsPathSharedPtr const & rOutput,
+ std::vector< Gdiplus::PointF >& rPoints,
+ const ::basegfx::B2DPolygon& rPoly,
+ bool bNoLineJoin)
+ {
+ const sal_uInt32 nPoints( rPoly.count() );
+
+ if( nPoints < 2 )
+ return;
+
+ rOutput->StartFigure();
+
+ const bool bClosedPolygon( rPoly.isClosed() );
+
+ if( rPoly.areControlPointsUsed() )
+ {
+ // control points used -> for now, add all
+ // segments as curves to GraphicsPath
+
+ // If the polygon is closed, we need to add the
+ // first point, thus, one more (can't simply
+ // GraphicsPath::CloseFigure() it, since the last
+ // point cannot have any control points for GDI+)
+ rPoints.resize( 3*nPoints + (bClosedPolygon ? 1 : 0) );
+
+ sal_uInt32 nCurrOutput=0;
+ for( sal_uInt32 nCurrPoint=0; nCurrPoint<nPoints; ++nCurrPoint )
+ {
+ const ::basegfx::B2DPoint& rPoint( rPoly.getB2DPoint( nCurrPoint ) );
+ rPoints[nCurrOutput++] = Gdiplus::PointF( static_cast<Gdiplus::REAL>(rPoint.getX()),
+ static_cast<Gdiplus::REAL>(rPoint.getY()) );
+
+ const ::basegfx::B2DPoint& rControlPointA( rPoly.getNextControlPoint( nCurrPoint ) );
+ rPoints[nCurrOutput++] = Gdiplus::PointF( static_cast<Gdiplus::REAL>(rControlPointA.getX()),
+ static_cast<Gdiplus::REAL>(rControlPointA.getY()) );
+
+ const ::basegfx::B2DPoint& rControlPointB( rPoly.getPrevControlPoint( (nCurrPoint + 1) % nPoints) );
+ rPoints[nCurrOutput++] = Gdiplus::PointF( static_cast<Gdiplus::REAL>(rControlPointB.getX()),
+ static_cast<Gdiplus::REAL>(rControlPointB.getY()) );
+ }
+
+ if( bClosedPolygon )
+ {
+ // add first point again (to be able to pass
+ // control points for the last point, see
+ // above)
+ const ::basegfx::B2DPoint& rPoint( rPoly.getB2DPoint(0) );
+ rPoints[nCurrOutput++] = Gdiplus::PointF( static_cast<Gdiplus::REAL>(rPoint.getX()),
+ static_cast<Gdiplus::REAL>(rPoint.getY()) );
+
+ if(bNoLineJoin && nCurrOutput > 7)
+ {
+ for(sal_uInt32 a(3); a < nCurrOutput; a+=3)
+ {
+ rOutput->StartFigure();
+ rOutput->AddBezier(rPoints[a - 3], rPoints[a - 2], rPoints[a - 1], rPoints[a]);
+ }
+ }
+ else
+ {
+ rOutput->AddBeziers( rPoints.data(), nCurrOutput );
+ }
+ }
+ else
+ {
+ // GraphicsPath expects 3(n-1)+1 points (i.e. the
+ // last point must not have any trailing control
+ // points after it).
+ // Therefore, simply don't pass the last two
+ // points here.
+ if( nCurrOutput > 3 )
+ {
+ if(bNoLineJoin && nCurrOutput > 7)
+ {
+ for(sal_uInt32 a(3); a < nCurrOutput; a+=3)
+ {
+ rOutput->StartFigure();
+ rOutput->AddBezier(rPoints[a - 3], rPoints[a - 2], rPoints[a - 1], rPoints[a]);
+ }
+ }
+ else
+ {
+ rOutput->AddBeziers( rPoints.data(), nCurrOutput-2 );
+ }
+ }
+ }
+ }
+ else
+ {
+ // no control points -> no curves, simply add
+ // straight lines to GraphicsPath
+ rPoints.resize( nPoints );
+
+ for( sal_uInt32 nCurrPoint=0; nCurrPoint<nPoints; ++nCurrPoint )
+ {
+ const ::basegfx::B2DPoint& rPoint( rPoly.getB2DPoint( nCurrPoint ) );
+ rPoints[nCurrPoint] = Gdiplus::PointF( static_cast<Gdiplus::REAL>(rPoint.getX()),
+ static_cast<Gdiplus::REAL>(rPoint.getY()) );
+ }
+
+ if(bNoLineJoin && nPoints > 2)
+ {
+ for(sal_uInt32 a(1); a < nPoints; a++)
+ {
+ rOutput->StartFigure();
+ rOutput->AddLine(rPoints[a - 1], rPoints[a]);
+ }
+
+ if(bClosedPolygon)
+ {
+ rOutput->StartFigure();
+ rOutput->AddLine(rPoints[nPoints - 1], rPoints[0]);
+ }
+ }
+ else
+ {
+ rOutput->AddLines( rPoints.data(), nPoints );
+ }
+ }
+
+ if( bClosedPolygon && !bNoLineJoin )
+ rOutput->CloseFigure();
+ }
+ }
+
+ Gdiplus::Rect gdiPlusRectFromIntegerRectangle2D( const geometry::IntegerRectangle2D& rRect )
+ {
+ return Gdiplus::Rect( rRect.X1,
+ rRect.Y1,
+ rRect.X2 - rRect.X1,
+ rRect.Y2 - rRect.Y1 );
+ }
+
+ Gdiplus::RectF gdiPlusRectFFromRectangle2D( const geometry::RealRectangle2D& rRect )
+ {
+ return Gdiplus::RectF( static_cast<Gdiplus::REAL>(rRect.X1),
+ static_cast<Gdiplus::REAL>(rRect.Y1),
+ static_cast<Gdiplus::REAL>(rRect.X2 - rRect.X1),
+ static_cast<Gdiplus::REAL>(rRect.Y2 - rRect.Y1) );
+ }
+
+ RECT gdiRectFromB2IRect( const ::basegfx::B2IRange& rRect )
+ {
+ RECT aRect = {rRect.getMinX(),
+ rRect.getMinY(),
+ rRect.getMaxX(),
+ rRect.getMaxY()};
+
+ return aRect;
+ }
+
+ geometry::RealPoint2D realPoint2DFromGdiPlusPointF( const Gdiplus::PointF& rPoint )
+ {
+ return geometry::RealPoint2D( rPoint.X, rPoint.Y );
+ }
+
+ geometry::RealRectangle2D realRectangle2DFromGdiPlusRectF( const Gdiplus::RectF& rRect )
+ {
+ return geometry::RealRectangle2D( rRect.X, rRect.Y,
+ rRect.X + rRect.Width,
+ rRect.Y + rRect.Height );
+ }
+
+ ::basegfx::B2DPoint b2dPointFromGdiPlusPointF( const Gdiplus::PointF& rPoint )
+ {
+ return ::basegfx::B2DPoint( rPoint.X, rPoint.Y );
+ }
+
+ ::basegfx::B2DRange b2dRangeFromGdiPlusRectF( const Gdiplus::RectF& rRect )
+ {
+ return ::basegfx::B2DRange( rRect.X, rRect.Y,
+ rRect.X + rRect.Width,
+ rRect.Y + rRect.Height );
+ }
+
+ uno::Sequence< sal_Int8 > argbToIntSequence( Gdiplus::ARGB rColor )
+ {
+ // TODO(F1): handle color space conversions, when defined on canvas/graphicDevice
+ return
+ {
+ static_cast<sal_Int8>((rColor >> 16) & 0xFF), // red
+ static_cast<sal_Int8>((rColor >> 8) & 0xFF), // green
+ static_cast<sal_Int8>(rColor & 0xFF), // blue
+ static_cast<sal_Int8>((rColor >> 24) & 0xFF) // alpha
+ };
+ }
+
+ Gdiplus::ARGB sequenceToArgb( const uno::Sequence< sal_Int8 >& rColor )
+ {
+ ENSURE_OR_THROW( rColor.getLength() > 2,
+ "sequenceToArgb: need at least three channels" );
+
+ // TODO(F1): handle color space conversions, when defined on canvas/graphicDevice
+ Gdiplus::ARGB aColor;
+
+ aColor = (static_cast<sal_uInt8>(rColor[0]) << 16) | (static_cast<sal_uInt8>(rColor[1]) << 8) | static_cast<sal_uInt8>(rColor[2]);
+
+ if( rColor.getLength() > 3 )
+ aColor |= static_cast<sal_uInt8>(rColor[3]) << 24;
+
+ return aColor;
+ }
+
+ Gdiplus::ARGB sequenceToArgb( const uno::Sequence< double >& rColor )
+ {
+ ENSURE_OR_THROW( rColor.getLength() > 2,
+ "sequenceToColor: need at least three channels" );
+
+ // TODO(F1): handle color space conversions, when defined on canvas/graphicDevice
+ Gdiplus::ARGB aColor;
+
+ ::canvas::tools::verifyRange(rColor[0],0.0,1.0);
+ ::canvas::tools::verifyRange(rColor[1],0.0,1.0);
+ ::canvas::tools::verifyRange(rColor[2],0.0,1.0);
+
+ aColor =
+ (static_cast<sal_uInt8>( ::basegfx::fround( 255*rColor[0] ) ) << 16) |
+ (static_cast<sal_uInt8>( ::basegfx::fround( 255*rColor[1] ) ) << 8) |
+ static_cast<sal_uInt8>( ::basegfx::fround( 255*rColor[2] ) );
+
+ if( rColor.getLength() > 3 )
+ {
+ ::canvas::tools::verifyRange(rColor[3],0.0,1.0);
+ aColor |= static_cast<sal_uInt8>( ::basegfx::fround( 255*rColor[3] ) ) << 24;
+ }
+
+ return aColor;
+ }
+
+ GraphicsPathSharedPtr graphicsPathFromRealPoint2DSequence( const uno::Sequence< uno::Sequence< geometry::RealPoint2D > >& points )
+ {
+ GraphicsPathSharedPtr pRes = std::make_shared<Gdiplus::GraphicsPath>();
+ std::vector< Gdiplus::PointF > aPoints;
+
+ for( uno::Sequence< geometry::RealPoint2D > const & seqPoints : points )
+ {
+ const sal_Int32 nCurrSize( seqPoints.getLength() );
+ if( nCurrSize )
+ {
+ aPoints.resize( nCurrSize );
+
+ // TODO(F1): Closed/open polygons
+
+ // convert from RealPoint2D array to Gdiplus::PointF array
+ std::transform( seqPoints.getConstArray(),
+ seqPoints.getConstArray()+nCurrSize,
+ aPoints.begin(),
+ implGdiPlusPointFromRealPoint2D );
+
+ pRes->AddLines( aPoints.data(), nCurrSize );
+ }
+ }
+
+ return pRes;
+ }
+
+ GraphicsPathSharedPtr graphicsPathFromB2DPolygon( const ::basegfx::B2DPolygon& rPoly, bool bNoLineJoin )
+ {
+ GraphicsPathSharedPtr pRes = std::make_shared<Gdiplus::GraphicsPath>();
+ std::vector< Gdiplus::PointF > aPoints;
+
+ graphicsPathFromB2DPolygon( pRes, aPoints, rPoly, bNoLineJoin );
+
+ return pRes;
+ }
+
+ GraphicsPathSharedPtr graphicsPathFromB2DPolyPolygon( const ::basegfx::B2DPolyPolygon& rPoly, bool bNoLineJoin )
+ {
+ GraphicsPathSharedPtr pRes = std::make_shared<Gdiplus::GraphicsPath>();
+ std::vector< Gdiplus::PointF > aPoints;
+
+ const sal_uInt32 nPolies( rPoly.count() );
+ for( sal_uInt32 nCurrPoly=0; nCurrPoly<nPolies; ++nCurrPoly )
+ {
+ graphicsPathFromB2DPolygon( pRes,
+ aPoints,
+ rPoly.getB2DPolygon( nCurrPoly ),
+ bNoLineJoin);
+ }
+
+ return pRes;
+ }
+
+ GraphicsPathSharedPtr graphicsPathFromXPolyPolygon2D( const uno::Reference< rendering::XPolyPolygon2D >& xPoly, bool bNoLineJoin )
+ {
+ LinePolyPolygon* pPolyImpl = dynamic_cast< LinePolyPolygon* >( xPoly.get() );
+
+ if( pPolyImpl )
+ {
+ return pPolyImpl->getGraphicsPath( bNoLineJoin );
+ }
+ else
+ {
+ return tools::graphicsPathFromB2DPolyPolygon(
+ polyPolygonFromXPolyPolygon2D( xPoly ), bNoLineJoin );
+ }
+ }
+
+ bool drawGdiPlusBitmap( const GraphicsSharedPtr& rGraphics,
+ const BitmapSharedPtr& rBitmap )
+ {
+ Gdiplus::PointF aPoint;
+ return (Gdiplus::Ok == rGraphics->DrawImage( rBitmap.get(),
+ aPoint ) );
+ }
+
+ bool drawDIBits( const std::shared_ptr<Gdiplus::Graphics>& rGraphics,
+ const BITMAPINFO& rBI,
+ const void* pBits )
+ {
+ BitmapSharedPtr pBitmap(
+ Gdiplus::Bitmap::FromBITMAPINFO( &rBI,
+ const_cast<void*>(pBits) ) );
+
+ return drawGdiPlusBitmap( rGraphics,
+ pBitmap );
+ }
+
+ bool drawRGBABits( const std::shared_ptr<Gdiplus::Graphics>& rGraphics,
+ const RawRGBABitmap& rRawRGBAData )
+ {
+ BitmapSharedPtr pBitmap = std::make_shared<Gdiplus::Bitmap>( rRawRGBAData.mnWidth,
+ rRawRGBAData.mnHeight,
+ PixelFormat32bppARGB );
+
+ Gdiplus::BitmapData aBmpData;
+ aBmpData.Width = rRawRGBAData.mnWidth;
+ aBmpData.Height = rRawRGBAData.mnHeight;
+ aBmpData.Stride = 4*aBmpData.Width; // bottom-up format
+ aBmpData.PixelFormat = PixelFormat32bppARGB;
+ aBmpData.Scan0 = const_cast<sal_uInt8*>(rRawRGBAData.maBitmapData.data());
+
+ const Gdiplus::Rect aRect( 0,0,aBmpData.Width,aBmpData.Height );
+ if( Gdiplus::Ok != pBitmap->LockBits( &aRect,
+ Gdiplus::ImageLockModeWrite | Gdiplus::ImageLockModeUserInputBuf,
+ PixelFormat32bppARGB,
+ &aBmpData ) )
+ {
+ return false;
+ }
+
+ // commit data to bitmap
+ pBitmap->UnlockBits( &aBmpData );
+
+ return drawGdiPlusBitmap( rGraphics,
+ pBitmap );
+ }
+
+ BitmapSharedPtr bitmapFromXBitmap( const uno::Reference< rendering::XBitmap >& xBitmap )
+ {
+ BitmapProvider* pBitmapProvider = dynamic_cast< BitmapProvider* >(xBitmap.get());
+
+ if( pBitmapProvider )
+ {
+ IBitmapSharedPtr pBitmap( pBitmapProvider->getBitmap() );
+ return pBitmap->getBitmap();
+ }
+ else
+ {
+ // not a native CanvasBitmap, extract VCL bitmap and
+ // render into GDI+ bitmap of similar size
+ // =================================================
+
+ const geometry::IntegerSize2D aBmpSize( xBitmap->getSize() );
+ BitmapSharedPtr pBitmap;
+
+ if( xBitmap->hasAlpha() )
+ {
+ // TODO(P2): At least for the alpha bitmap case, it
+ // would be possible to generate the corresponding
+ // bitmap directly
+ pBitmap = std::make_shared<Gdiplus::Bitmap>( aBmpSize.Width,
+ aBmpSize.Height,
+ PixelFormat32bppARGB );
+ }
+ else
+ {
+ // TODO(F2): Might be wise to create bitmap compatible
+ // to the VCL bitmap. Also, check whether the VCL
+ // bitmap's system handles can be used to create the
+ // GDI+ bitmap (currently, it does not seem so).
+ pBitmap = std::make_shared<Gdiplus::Bitmap>( aBmpSize.Width,
+ aBmpSize.Height,
+ PixelFormat24bppRGB );
+ }
+
+ GraphicsSharedPtr pGraphics(createGraphicsFromBitmap(pBitmap));
+ tools::setupGraphics(*pGraphics);
+ if( !drawVCLBitmapFromXBitmap(
+ pGraphics,
+ xBitmap) )
+ {
+ pBitmap.reset();
+ }
+
+ return pBitmap;
+ }
+ }
+
+ CanvasFont::ImplRef canvasFontFromXFont( const uno::Reference< rendering::XCanvasFont >& xFont )
+ {
+ CanvasFont* pCanvasFont = dynamic_cast< CanvasFont* >(xFont.get());
+
+ ENSURE_ARG_OR_THROW( pCanvasFont,
+ "canvasFontFromXFont(): Invalid XFont (or incompatible font for this XCanvas)" );
+
+ return CanvasFont::ImplRef( pCanvasFont );
+ }
+
+ void setModulateImageAttributes( Gdiplus::ImageAttributes& o_rAttr,
+ double nRedModulation,
+ double nGreenModulation,
+ double nBlueModulation,
+ double nAlphaModulation )
+ {
+ // This gets rather verbose, but we have to setup a color
+ // transformation matrix, in order to incorporate the global
+ // alpha value mfAlpha into the bitmap rendering.
+ Gdiplus::ColorMatrix aColorMatrix;
+
+ aColorMatrix.m[0][0] = static_cast<Gdiplus::REAL>(nRedModulation);
+ aColorMatrix.m[0][1] = 0.0;
+ aColorMatrix.m[0][2] = 0.0;
+ aColorMatrix.m[0][3] = 0.0;
+ aColorMatrix.m[0][4] = 0.0;
+
+ aColorMatrix.m[1][0] = 0.0;
+ aColorMatrix.m[1][1] = static_cast<Gdiplus::REAL>(nGreenModulation);
+ aColorMatrix.m[1][2] = 0.0;
+ aColorMatrix.m[1][3] = 0.0;
+ aColorMatrix.m[1][4] = 0.0;
+
+ aColorMatrix.m[2][0] = 0.0;
+ aColorMatrix.m[2][1] = 0.0;
+ aColorMatrix.m[2][2] = static_cast<Gdiplus::REAL>(nBlueModulation);
+ aColorMatrix.m[2][3] = 0.0;
+ aColorMatrix.m[2][4] = 0.0;
+
+ aColorMatrix.m[3][0] = 0.0;
+ aColorMatrix.m[3][1] = 0.0;
+ aColorMatrix.m[3][2] = 0.0;
+ aColorMatrix.m[3][3] = static_cast<Gdiplus::REAL>(nAlphaModulation);
+ aColorMatrix.m[3][4] = 0.0;
+
+ aColorMatrix.m[4][0] = 0.0;
+ aColorMatrix.m[4][1] = 0.0;
+ aColorMatrix.m[4][2] = 0.0;
+ aColorMatrix.m[4][3] = 0.0;
+ aColorMatrix.m[4][4] = 1.0;
+
+ o_rAttr.SetColorMatrix( &aColorMatrix );
+ }
+
+} // namespace dxcanvas::tools
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/directx/dx_impltools.hxx b/canvas/source/directx/dx_impltools.hxx
new file mode 100644
index 0000000000..2c6b85ce83
--- /dev/null
+++ b/canvas/source/directx/dx_impltools.hxx
@@ -0,0 +1,124 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/util/TriState.hpp>
+
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolypolygon.hxx>
+#include <basegfx/numeric/ftools.hxx>
+
+#include <memory>
+#include "dx_canvasfont.hxx"
+
+namespace basegfx
+{
+ class B2DPoint;
+ class B2DRange;
+ class B2DHomMatrix;
+ class B2IRange;
+ class B2DPolyPolygon;
+};
+
+namespace com::sun::star::geometry
+{
+ struct IntegerRectangle2D;
+ struct RealPoint2D;
+}
+
+namespace com::sun::star::rendering
+{
+ class XBitmap;
+ class XPolyPolygon2D;
+ class XCanvasFont;
+}
+
+
+namespace dxcanvas::tools
+{
+ struct RawRGBABitmap;
+
+ ::basegfx::B2DPolyPolygon
+ polyPolygonFromXPolyPolygon2D( const css::uno::Reference< css::rendering::XPolyPolygon2D >& );
+
+ Gdiplus::Graphics* createGraphicsFromHDC(HDC);
+ GraphicsSharedPtr createGraphicsFromBitmap(const BitmapSharedPtr&);
+
+ void setupGraphics( Gdiplus::Graphics& rGraphics );
+
+ void gdiPlusMatrixFromB2DHomMatrix( Gdiplus::Matrix& rGdiplusMatrix,
+ const ::basegfx::B2DHomMatrix& rMatrix );
+ void gdiPlusMatrixFromAffineMatrix2D( Gdiplus::Matrix& rGdiplusMatrix,
+ const css::geometry::AffineMatrix2D& rMatrix );
+
+ Gdiplus::PointF gdiPlusPointFFromRealPoint2D( const css::geometry::RealPoint2D& );
+ Gdiplus::RectF gdiPlusRectFFromRectangle2D( const css::geometry::RealRectangle2D& );
+ Gdiplus::Rect gdiPlusRectFromIntegerRectangle2D( const css::geometry::IntegerRectangle2D& );
+ RECT gdiRectFromB2IRect( const ::basegfx::B2IRange& );
+
+ css::geometry::RealPoint2D realPoint2DFromGdiPlusPointF( const Gdiplus::PointF& );
+ css::geometry::RealRectangle2D realRectangle2DFromGdiPlusRectF( const Gdiplus::RectF& );
+
+ ::basegfx::B2DPoint b2dPointFromGdiPlusPointF( const Gdiplus::PointF& );
+ ::basegfx::B2DRange b2dRangeFromGdiPlusRectF( const Gdiplus::RectF& );
+
+ css::uno::Sequence< sal_Int8 > argbToIntSequence( Gdiplus::ARGB rColor );
+ Gdiplus::ARGB sequenceToArgb( const css::uno::Sequence< sal_Int8 >& rColor );
+ Gdiplus::ARGB sequenceToArgb( const css::uno::Sequence< double >& rColor );
+
+ GraphicsPathSharedPtr graphicsPathFromRealPoint2DSequence( const css::uno::Sequence<
+ css::uno::Sequence< css::geometry::RealPoint2D > >& );
+
+ GraphicsPathSharedPtr graphicsPathFromB2DPolygon(
+ const ::basegfx::B2DPolygon& rPoly,
+ bool bNoLineJoin = false);
+
+ GraphicsPathSharedPtr graphicsPathFromB2DPolyPolygon(
+ const ::basegfx::B2DPolyPolygon& rPoly,
+ bool bNoLineJoin = false);
+
+ GraphicsPathSharedPtr graphicsPathFromXPolyPolygon2D(
+ const css::uno::Reference< css::rendering::XPolyPolygon2D >&,
+ bool bNoLineJoin = false );
+
+ bool drawGdiPlusBitmap( const GraphicsSharedPtr& rGraphics,
+ const BitmapSharedPtr& rBitmap );
+ bool drawDIBits( const std::shared_ptr< Gdiplus::Graphics >& rGraphics,
+ const BITMAPINFO& rBI,
+ const void* pBits );
+
+ bool drawRGBABits( const std::shared_ptr< Gdiplus::Graphics >& rGraphics,
+ const RawRGBABitmap& rRawRGBAData );
+
+ BitmapSharedPtr bitmapFromXBitmap( const css::uno::Reference< css::rendering::XBitmap >& xBitmap );
+
+ CanvasFont::ImplRef canvasFontFromXFont( const css::uno::Reference< css::rendering::XCanvasFont >& xFont );
+
+ void setModulateImageAttributes( Gdiplus::ImageAttributes& o_rAttr,
+ double nRedModulation,
+ double nGreenModulation,
+ double nBlueModulation,
+ double nAlphaModulation );
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/directx/dx_linepolypolygon.cxx b/canvas/source/directx/dx_linepolypolygon.cxx
new file mode 100644
index 0000000000..53ec2953e7
--- /dev/null
+++ b/canvas/source/directx/dx_linepolypolygon.cxx
@@ -0,0 +1,59 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <basegfx/utils/canvastools.hxx>
+
+#include "dx_linepolypolygon.hxx"
+
+
+using namespace ::com::sun::star;
+
+namespace dxcanvas
+{
+ LinePolyPolygon::LinePolyPolygon( const ::basegfx::B2DPolyPolygon& rPolyPoly ) :
+ ::basegfx::unotools::UnoPolyPolygon( rPolyPoly ),
+ mpGdiPlusUser( GDIPlusUser::createInstance() ),
+ mpPath()
+ {
+ }
+
+ GraphicsPathSharedPtr LinePolyPolygon::getGraphicsPath( bool bNoLineJoin ) const
+ {
+ // generate GraphicsPath only on demand (gets deleted as soon
+ // as any of the modifying methods above touches the
+ // B2DPolyPolygon).
+ if( !mpPath )
+ {
+ mpPath = tools::graphicsPathFromB2DPolyPolygon( getPolyPolygonUnsafe(), bNoLineJoin );
+ mpPath->SetFillMode( const_cast<LinePolyPolygon*>(this)->getFillRule() == rendering::FillRule_EVEN_ODD ?
+ Gdiplus::FillModeAlternate : Gdiplus::FillModeWinding );
+ }
+
+ return mpPath;
+ }
+
+ void LinePolyPolygon::modifying() const
+ {
+ mpPath.reset();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/directx/dx_linepolypolygon.hxx b/canvas/source/directx/dx_linepolypolygon.hxx
new file mode 100644
index 0000000000..eaec483a02
--- /dev/null
+++ b/canvas/source/directx/dx_linepolypolygon.hxx
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <canvas/canvastools.hxx>
+#include <basegfx/utils/unopolypolygon.hxx>
+
+#include "dx_gdiplususer.hxx"
+#include "dx_impltools.hxx"
+
+
+namespace dxcanvas
+{
+ class LinePolyPolygon : public ::basegfx::unotools::UnoPolyPolygon
+ {
+ public:
+ explicit LinePolyPolygon( const ::basegfx::B2DPolyPolygon& );
+
+ GraphicsPathSharedPtr getGraphicsPath( bool bNoLineJoin = false) const;
+
+ private:
+ // overridden, to clear mpPath
+ virtual void modifying() const override;
+
+ GDIPlusUserSharedPtr mpGdiPlusUser;
+ mutable GraphicsPathSharedPtr mpPath;
+ };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/directx/dx_rendermodule.hxx b/canvas/source/directx/dx_rendermodule.hxx
new file mode 100644
index 0000000000..4b13937967
--- /dev/null
+++ b/canvas/source/directx/dx_rendermodule.hxx
@@ -0,0 +1,80 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <basegfx/vector/b2ivector.hxx>
+#include <basegfx/range/b2irectangle.hxx>
+#include <rendering/irendermodule.hxx>
+#include <memory>
+#include "dx_winstuff.hxx"
+
+namespace vcl { class Window; }
+namespace basegfx
+{
+ class B2IRange;
+}
+
+namespace dxcanvas
+{
+ /// Specialization of IRenderModule for DirectX
+ struct IDXRenderModule : public canvas::IRenderModule
+ {
+ /** Flip front- and backbuffer, update only given area
+
+ Note: Both update area and offset are ignored for
+ fullscreen canvas, that uses page flipping (cannot, by
+ definition, do anything else there except displaying the
+ full backbuffer instead of the front buffer)
+
+ @param rUpdateArea
+ Area to copy from backbuffer to front
+
+ @param rCurrWindowArea
+ Current area of VCL window (coordinates relative to VCL
+ HWND)
+ */
+ virtual bool flip( const ::basegfx::B2IRectangle& rUpdateArea,
+ const ::basegfx::B2IRectangle& rCurrWindowArea ) = 0;
+
+ /** Resize backbuffer area for this render module
+ */
+ virtual void resize( const ::basegfx::B2IRange& rect ) = 0;
+
+ /// Write a snapshot of the screen to disk
+ virtual void screenShot() = 0;
+
+ virtual sal::systools::COMReference<surface_type>
+ createSystemMemorySurface(
+ const ::basegfx::B2ISize& rSize) = 0;
+
+ virtual void disposing() = 0;
+ virtual HWND getHWND() const = 0;
+ };
+
+ typedef std::shared_ptr< IDXRenderModule > IDXRenderModuleSharedPtr;
+
+
+ /** Factory method, to create an IRenderModule instance for the
+ given VCL window instance
+ */
+ IDXRenderModuleSharedPtr createRenderModule( const vcl::Window& rParent );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/directx/dx_sprite.hxx b/canvas/source/directx/dx_sprite.hxx
new file mode 100644
index 0000000000..c1f75e1b1e
--- /dev/null
+++ b/canvas/source/directx/dx_sprite.hxx
@@ -0,0 +1,45 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <base/sprite.hxx>
+
+namespace dxcanvas
+{
+ /** Specialization of ::canvas::Sprite interface, to also provide
+ redraw methods.
+ */
+ class Sprite : public ::canvas::Sprite
+ {
+ public:
+
+ /** Redraw sprite using the hardware
+
+ This method will silently fail, if the previous
+ restoreTextures() call failed.
+ */
+ virtual void redraw() const = 0;
+
+ protected:
+ ~Sprite() {}
+ };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/directx/dx_spritecanvas.cxx b/canvas/source/directx/dx_spritecanvas.cxx
new file mode 100644
index 0000000000..f4fe39203f
--- /dev/null
+++ b/canvas/source/directx/dx_spritecanvas.cxx
@@ -0,0 +1,192 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/numeric/ftools.hxx>
+#include <basegfx/point/b2dpoint.hxx>
+#include <basegfx/utils/canvastools.hxx>
+#include <com/sun/star/lang/NoSupportException.hpp>
+#include <com/sun/star/lang/XSingleServiceFactory.hpp>
+#include <com/sun/star/registry/XRegistryKey.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <cppuhelper/supportsservice.hxx>
+#include <osl/mutex.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <comphelper/diagnose_ex.hxx>
+
+#include <canvas/canvastools.hxx>
+
+#include "dx_spritecanvas.hxx"
+#include "dx_winstuff.hxx"
+
+
+using namespace ::com::sun::star;
+
+namespace dxcanvas
+{
+ SpriteCanvas::SpriteCanvas( const uno::Sequence< uno::Any >& aArguments,
+ const uno::Reference< uno::XComponentContext >& rxContext ) :
+ maArguments(aArguments),
+ mxComponentContext( rxContext )
+ {
+ }
+
+ void SpriteCanvas::initialize()
+ {
+ // #i64742# Only call initialize when not in probe mode
+ if( maArguments.getLength() == 0 )
+ return;
+
+ SAL_INFO("canvas.directx", "SpriteCanvas::initialize called" );
+
+ /* aArguments:
+ 0: ptr to creating instance (Window or VirtualDevice)
+ 1: SystemEnvData as a streamed Any (or empty for VirtualDevice)
+ 2: current bounds of creating instance
+ 3: bool, denoting always on top state for Window (always false for VirtualDevice)
+ 4: XWindow for creating Window (or empty for VirtualDevice)
+ 5: SystemGraphicsData as a streamed Any
+ */
+ ENSURE_ARG_OR_THROW( maArguments.getLength() >= 4 &&
+ maArguments[3].getValueTypeClass() == uno::TypeClass_INTERFACE,
+ "VCLSpriteCanvas::initialize: wrong number of arguments, or wrong types" );
+
+ uno::Reference< awt::XWindow > xParentWindow;
+ maArguments[3] >>= xParentWindow;
+ auto pParentWindow = VCLUnoHelper::GetWindow(xParentWindow);
+ if( !pParentWindow )
+ throw lang::NoSupportException( "Parent window not VCL window, or canvas out-of-process!" );
+
+ awt::Rectangle aRect;
+ maArguments[1] >>= aRect;
+
+ bool bIsFullscreen( false );
+ maArguments[2] >>= bIsFullscreen;
+
+ // setup helper
+ maDeviceHelper.init( *pParentWindow,
+ *this,
+ aRect,
+ bIsFullscreen );
+ maCanvasHelper.init( *this,
+ maRedrawManager,
+ maDeviceHelper.getRenderModule(),
+ maDeviceHelper.getSurfaceProxy(),
+ maDeviceHelper.getBackBuffer(),
+ ::basegfx::B2ISize() );
+ maArguments.realloc(0);
+ }
+
+ void SpriteCanvas::disposeThis()
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ mxComponentContext.clear();
+
+ // forward to parent
+ SpriteCanvasBaseT::disposeThis();
+ }
+
+ sal_Bool SAL_CALL SpriteCanvas::showBuffer( sal_Bool bUpdateAll )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ // avoid repaints on hidden window (hidden: not mapped to
+ // screen). Return failure, since the screen really has _not_
+ // been updated (caller should try again later)
+ return mbIsVisible && SpriteCanvasBaseT::showBuffer( bUpdateAll );
+ }
+
+ sal_Bool SAL_CALL SpriteCanvas::switchBuffer( sal_Bool bUpdateAll )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ // avoid repaints on hidden window (hidden: not mapped to
+ // screen). Return failure, since the screen really has _not_
+ // been updated (caller should try again later)
+ return mbIsVisible && SpriteCanvasBaseT::switchBuffer( bUpdateAll );
+ }
+
+ sal_Bool SAL_CALL SpriteCanvas::updateScreen( sal_Bool bUpdateAll )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ // avoid repaints on hidden window (hidden: not mapped to
+ // screen). Return failure, since the screen really has _not_
+ // been updated (caller should try again later)
+ return mbIsVisible && maCanvasHelper.updateScreen(
+ ::basegfx::unotools::b2IRectangleFromAwtRectangle(maBounds),
+ bUpdateAll,
+ mbSurfaceDirty );
+ }
+
+ OUString SAL_CALL SpriteCanvas::getServiceName( )
+ {
+ return "com.sun.star.rendering.SpriteCanvas.DX9";
+ }
+
+ // XServiceInfo
+ css::uno::Sequence<OUString> SpriteCanvas::getSupportedServiceNames( )
+ {
+ return { "com.sun.star.rendering.SpriteCanvas.DX9" };
+ }
+ OUString SpriteCanvas::getImplementationName( )
+ {
+ return "com.sun.star.comp.rendering.SpriteCanvas.DX9";
+ }
+ sal_Bool SpriteCanvas::supportsService( const OUString& sServiceName )
+ {
+ return cppu::supportsService(this, sServiceName);
+ }
+
+ const IDXRenderModuleSharedPtr& SpriteCanvas::getRenderModule() const
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ return maDeviceHelper.getRenderModule();
+ }
+
+ const DXSurfaceBitmapSharedPtr& SpriteCanvas::getBackBuffer() const
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ return maDeviceHelper.getBackBuffer();
+ }
+
+ IBitmapSharedPtr SpriteCanvas::getBitmap() const
+ {
+ return maDeviceHelper.getBackBuffer();
+ }
+
+ extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+ canvas_directx9_SpriteCanvas_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& args)
+ {
+ rtl::Reference<SpriteCanvas> xCanvas(new SpriteCanvas(args, context));
+ xCanvas->initialize();
+ return cppu::acquire(xCanvas.get());
+ }
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/directx/dx_spritecanvas.hxx b/canvas/source/directx/dx_spritecanvas.hxx
new file mode 100644
index 0000000000..081337f72c
--- /dev/null
+++ b/canvas/source/directx/dx_spritecanvas.hxx
@@ -0,0 +1,155 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <rtl/ref.hxx>
+
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/lang/XServiceName.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/awt/XWindowListener.hpp>
+#include <com/sun/star/awt/XTopWindow.hpp>
+#include <com/sun/star/util/XUpdatable.hpp>
+#include <com/sun/star/rendering/XSpriteCanvas.hpp>
+#include <com/sun/star/rendering/XIntegerBitmap.hpp>
+#include <com/sun/star/rendering/XGraphicDevice.hpp>
+#include <com/sun/star/rendering/XBufferController.hpp>
+
+#include <cppuhelper/compbase.hxx>
+#include <comphelper/uno3.hxx>
+
+#include <base/spritecanvasbase.hxx>
+#include <base/spritesurface.hxx>
+#include <base/disambiguationhelper.hxx>
+#include <base/bufferedgraphicdevicebase.hxx>
+
+#include "dx_bitmapprovider.hxx"
+#include "dx_spritecanvashelper.hxx"
+#include "dx_surfacebitmap.hxx"
+#include "dx_impltools.hxx"
+#include "dx_spritedevicehelper.hxx"
+
+
+namespace dxcanvas
+{
+ typedef ::cppu::WeakComponentImplHelper< css::rendering::XSpriteCanvas,
+ css::rendering::XIntegerBitmap,
+ css::rendering::XGraphicDevice,
+ css::lang::XMultiServiceFactory,
+ css::rendering::XBufferController,
+ css::awt::XWindowListener,
+ css::util::XUpdatable,
+ css::beans::XPropertySet,
+ css::lang::XServiceName,
+ css::lang::XServiceInfo> WindowGraphicDeviceBase_Base;
+ typedef ::canvas::BufferedGraphicDeviceBase< ::canvas::DisambiguationHelper< WindowGraphicDeviceBase_Base >,
+ SpriteDeviceHelper,
+ ::osl::MutexGuard,
+ ::cppu::OWeakObject > SpriteCanvasBase_Base;
+ /** Mixin SpriteSurface
+
+ Have to mixin the SpriteSurface before deriving from
+ ::canvas::SpriteCanvasBase, as this template should already
+ implement some of those interface methods.
+
+ The reason why this appears kinda convoluted is the fact that
+ we cannot specify non-IDL types as WeakComponentImplHelper
+ template args, and furthermore, don't want to derive
+ ::canvas::SpriteCanvasBase directly from
+ ::canvas::SpriteSurface (because derivees of
+ ::canvas::SpriteCanvasBase have to explicitly forward the
+ XInterface methods (e.g. via DECLARE_UNO3_AGG_DEFAULTS)
+ anyway). Basically, ::canvas::CanvasCustomSpriteBase should
+ remain a base class that provides implementation, not to
+ enforce any specific interface on its derivees.
+ */
+ class SpriteCanvasBaseSpriteSurface_Base : public SpriteCanvasBase_Base,
+ public ::canvas::SpriteSurface
+ {
+ };
+
+ typedef ::canvas::SpriteCanvasBase< SpriteCanvasBaseSpriteSurface_Base,
+ SpriteCanvasHelper,
+ ::osl::MutexGuard,
+ ::cppu::OWeakObject > SpriteCanvasBaseT;
+
+ /** Product of this component's factory.
+
+ The SpriteCanvas object combines the actual Window canvas with
+ the XGraphicDevice interface. This is because there's a
+ one-to-one relation between them, anyway, since each window
+ can have exactly one canvas and one associated
+ XGraphicDevice. And to avoid messing around with circular
+ references, this is implemented as one single object.
+ */
+ class SpriteCanvas : public SpriteCanvasBaseT, public BitmapProvider
+ {
+ public:
+ SpriteCanvas( const css::uno::Sequence<
+ css::uno::Any >& aArguments,
+ const css::uno::Reference<
+ css::uno::XComponentContext >& rxContext );
+
+ void initialize();
+
+ /// Dispose all internal references
+ virtual void disposeThis() override;
+
+ // Forwarding the XComponent implementation to the
+ // cppu::ImplHelper templated base
+ // Classname Base doing refcounting Base implementing the XComponent interface
+ // | | |
+ // V V V
+ DECLARE_UNO3_XCOMPONENT_AGG_DEFAULTS( SpriteCanvas, WindowGraphicDeviceBase_Base, ::cppu::WeakComponentImplHelperBase )
+
+ // XBufferController (partial)
+ virtual sal_Bool SAL_CALL showBuffer( sal_Bool bUpdateAll ) override;
+ virtual sal_Bool SAL_CALL switchBuffer( sal_Bool bUpdateAll ) override;
+
+ // XSpriteCanvas (partial)
+ virtual sal_Bool SAL_CALL updateScreen( sal_Bool bUpdateAll ) override;
+
+ // XServiceName
+ virtual OUString SAL_CALL getServiceName( ) override;
+
+ // XServiceInfo
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames( ) override;
+ virtual OUString SAL_CALL getImplementationName( ) override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ) override;
+
+ /// Retrieve rendermodule object for this Canvas
+ const IDXRenderModuleSharedPtr& getRenderModule() const;
+
+ /// Get backbuffer for this canvas
+ const DXSurfaceBitmapSharedPtr& getBackBuffer() const;
+
+ // BitmapProvider
+ virtual IBitmapSharedPtr getBitmap() const override;
+
+ private:
+ css::uno::Sequence< css::uno::Any > maArguments;
+ css::uno::Reference< css::uno::XComponentContext > mxComponentContext;
+ };
+
+ typedef ::rtl::Reference< SpriteCanvas > SpriteCanvasRef;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/directx/dx_spritecanvashelper.cxx b/canvas/source/directx/dx_spritecanvashelper.cxx
new file mode 100644
index 0000000000..d676fa2a80
--- /dev/null
+++ b/canvas/source/directx/dx_spritecanvashelper.cxx
@@ -0,0 +1,352 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <boost/cast.hpp>
+
+#include <basegfx/range/b2drectangle.hxx>
+#include <basegfx/utils/canvastools.hxx>
+#include <comphelper/scopeguard.hxx>
+#include <comphelper/diagnose_ex.hxx>
+
+#include <canvas/canvastools.hxx>
+
+#include "dx_canvascustomsprite.hxx"
+#include "dx_spritecanvashelper.hxx"
+
+using namespace ::com::sun::star;
+
+namespace dxcanvas
+{
+ namespace
+ {
+ void repaintBackground( const ::basegfx::B2DRange& rUpdateArea,
+ const ::basegfx::B2IRange& rOutputArea,
+ const DXSurfaceBitmapSharedPtr& rBackBuffer )
+ {
+ // TODO(E1): Use numeric_cast to catch overflow here
+ ::basegfx::B2IRange aActualArea( 0, 0,
+ static_cast<sal_Int32>(rOutputArea.getWidth()),
+ static_cast<sal_Int32>(rOutputArea.getHeight()) );
+ aActualArea.intersect( fround( rUpdateArea ) );
+
+ // repaint the given area of the screen with background content
+ rBackBuffer->draw(aActualArea);
+ }
+
+ void spriteRedraw( const ::canvas::Sprite::Reference& rSprite )
+ {
+ // downcast to derived dxcanvas::Sprite interface, which
+ // provides the actual redraw methods.
+ ::boost::polymorphic_downcast< Sprite* >(
+ rSprite.get() )->redraw();
+ }
+ }
+
+ SpriteCanvasHelper::SpriteCanvasHelper() :
+ mpSpriteSurface( nullptr ),
+ mpRedrawManager( nullptr ),
+ mpRenderModule(),
+ mpSurfaceProxy(),
+ mpBackBuffer(),
+ maUpdateRect(),
+ maScrapRect(),
+ mbShowSpriteBounds( false )
+ {
+#if OSL_DEBUG_LEVEL > 0
+ // inverse default for verbose debug mode
+ mbShowSpriteBounds = true;
+#endif
+ }
+
+ void SpriteCanvasHelper::init( SpriteCanvas& rParent,
+ ::canvas::SpriteRedrawManager& rManager,
+ const IDXRenderModuleSharedPtr& rRenderModule,
+ const std::shared_ptr<canvas::ISurfaceProxyManager>& rSurfaceProxy,
+ const DXSurfaceBitmapSharedPtr& rBackBuffer,
+ const ::basegfx::B2ISize& rOutputOffset )
+ {
+ // init base
+ setDevice( rParent );
+ setTarget( rBackBuffer, rOutputOffset );
+
+ mpSpriteSurface = &rParent;
+ mpRedrawManager = &rManager;
+ mpRenderModule = rRenderModule;
+ mpSurfaceProxy = rSurfaceProxy;
+ mpBackBuffer = rBackBuffer;
+ }
+
+ void SpriteCanvasHelper::disposing()
+ {
+ if(mpRenderModule)
+ mpRenderModule->disposing();
+
+ mpBackBuffer.reset();
+ mpRenderModule.reset();
+ mpRedrawManager = nullptr;
+ mpSpriteSurface = nullptr;
+
+ // forward to base
+ CanvasHelper::disposing();
+ }
+
+ uno::Reference< rendering::XAnimatedSprite > SpriteCanvasHelper::createSpriteFromAnimation(
+ const uno::Reference< rendering::XAnimation >& /*animation*/ )
+ {
+ return uno::Reference< rendering::XAnimatedSprite >();
+ }
+
+ uno::Reference< rendering::XAnimatedSprite > SpriteCanvasHelper::createSpriteFromBitmaps(
+ const uno::Sequence< uno::Reference< rendering::XBitmap > >& /*animationBitmaps*/,
+ sal_Int8 /*interpolationMode*/ )
+ {
+ return uno::Reference< rendering::XAnimatedSprite >();
+ }
+
+ uno::Reference< rendering::XCustomSprite > SpriteCanvasHelper::createCustomSprite( const geometry::RealSize2D& spriteSize )
+ {
+ if( !mpRedrawManager )
+ return uno::Reference< rendering::XCustomSprite >(); // we're disposed
+
+ return uno::Reference< rendering::XCustomSprite >(
+ new CanvasCustomSprite( spriteSize,
+ mpSpriteSurface,
+ mpRenderModule,
+ mpSurfaceProxy,
+ mbShowSpriteBounds ) );
+ }
+
+ uno::Reference< rendering::XSprite > SpriteCanvasHelper::createClonedSprite( const uno::Reference< rendering::XSprite >& /*original*/ )
+ {
+ return uno::Reference< rendering::XSprite >();
+ }
+
+ bool SpriteCanvasHelper::updateScreen( const ::basegfx::B2IRectangle& rCurrArea,
+ bool bUpdateAll,
+ bool& io_bSurfaceDirty )
+ {
+ if( !mpRedrawManager ||
+ !mpRenderModule ||
+ !mpBackBuffer )
+ {
+ return false; // disposed, or otherwise dysfunctional
+ }
+
+ // store current output area (need to tunnel that to the
+ // background, scroll, opaque and general sprite repaint
+ // routines)
+ maScrapRect = rCurrArea;
+
+ // clear area that needs to be blitted to screen beforehand
+ maUpdateRect.reset();
+
+ // TODO(P1): Might be worthwhile to track areas of background
+ // changes, too.
+
+ // TODO(P2): Might be worthwhile to use page-flipping only if
+ // a certain percentage of screen area has changed - and
+ // compose directly to the front buffer otherwise.
+ if( !bUpdateAll && !io_bSurfaceDirty )
+ {
+ // background has not changed, so we're free to optimize
+ // repaint to areas where a sprite has changed
+
+ // process each independent area of overlapping sprites
+ // separately.
+ mpRedrawManager->forEachSpriteArea( *this );
+
+ // flip primary surface to screen
+ // ==============================
+
+ // perform buffer flipping
+ mpRenderModule->flip( maUpdateRect,
+ rCurrArea );
+ }
+ else
+ {
+ // limit update to parent window area (ignored for fullscreen)
+ // TODO(E1): Use numeric_cast to catch overflow here
+ const ::basegfx::B2IRectangle aUpdateArea( 0,0,
+ static_cast<sal_Int32>(rCurrArea.getWidth()),
+ static_cast<sal_Int32>(rCurrArea.getHeight()) );
+
+ // background has changed, or called requested full
+ // update, or we're performing double buffering via page
+ // flipping, so we currently have no choice but repaint
+ // everything
+
+ // repaint the whole screen with background content
+ mpBackBuffer->draw(aUpdateArea);
+
+ // redraw sprites
+ mpRedrawManager->forEachSprite( &spriteRedraw );
+
+ // flip primary surface to screen
+ // ==============================
+
+ // perform buffer flipping
+ mpRenderModule->flip( aUpdateArea,
+ rCurrArea );
+ }
+
+ // change record vector must be cleared, for the next turn of
+ // rendering and sprite changing
+ mpRedrawManager->clearChangeRecords();
+
+ io_bSurfaceDirty = false;
+
+ return true;
+ }
+
+ void SpriteCanvasHelper::backgroundPaint( const ::basegfx::B2DRange& rUpdateRect )
+ {
+ ENSURE_OR_THROW( mpRenderModule &&
+ mpBackBuffer,
+ "SpriteCanvasHelper::backgroundPaint(): NULL device pointer " );
+
+ repaintBackground( rUpdateRect,
+ maScrapRect,
+ mpBackBuffer );
+ }
+
+ void SpriteCanvasHelper::scrollUpdate( const ::basegfx::B2DRange& /*rMoveStart*/,
+ const ::basegfx::B2DRange& rMoveEnd,
+ const ::canvas::SpriteRedrawManager::UpdateArea& rUpdateArea )
+ {
+ ENSURE_OR_THROW( mpRenderModule &&
+ mpBackBuffer,
+ "SpriteCanvasHelper::scrollUpdate(): NULL device pointer " );
+
+ // round rectangles to integer pixel. Note: have to be
+ // extremely careful here, to avoid off-by-one errors for
+ // the destination area: otherwise, the next scroll update
+ // would copy pixel that are not supposed to be part of
+ // the sprite.
+ const ::basegfx::B2IRange& rDestRect(
+ ::canvas::tools::spritePixelAreaFromB2DRange( rMoveEnd ) );
+
+ // not much sense in really implementing scrollUpdate here,
+ // since outputting a sprite only partially would result in
+ // expensive clipping. Furthermore, we cannot currently render
+ // 3D directly to the front buffer, thus, would have to blit
+ // the full sprite area, anyway. But at least optimized in the
+ // sense that unnecessary background paints behind the sprites
+ // are avoided.
+ for( const auto& rComponent : rUpdateArea.maComponentList )
+ {
+ const ::canvas::Sprite::Reference& rSprite( rComponent.second.getSprite() );
+
+ if( rSprite.is() )
+ {
+ // downcast to derived dxcanvas::Sprite interface, which
+ // provides the actual redraw methods.
+ ::boost::polymorphic_downcast< Sprite* >( rSprite.get() )->redraw();
+ }
+ }
+
+ // repaint uncovered areas from backbuffer - take the
+ // _rounded_ rectangles from above, to have the update
+ // consistent with the scroll above.
+ std::vector< ::basegfx::B2DRange > aUncoveredAreas;
+ ::basegfx::computeSetDifference( aUncoveredAreas,
+ rUpdateArea.maTotalBounds,
+ ::basegfx::B2DRange( rDestRect ) );
+ for( const auto& rUncoveredArea : aUncoveredAreas )
+ repaintBackground( rUncoveredArea, maScrapRect, mpBackBuffer );
+
+ // TODO(E1): Use numeric_cast to catch overflow here
+ ::basegfx::B2IRange aActualArea( 0, 0,
+ static_cast<sal_Int32>(maScrapRect.getWidth()),
+ static_cast<sal_Int32>(maScrapRect.getHeight()) );
+ aActualArea.intersect( fround( rUpdateArea.maTotalBounds ) );
+
+ // add given update area to the 'blit to foreground' rect
+ maUpdateRect.expand( aActualArea );
+ }
+
+ void SpriteCanvasHelper::opaqueUpdate( const ::basegfx::B2DRange& rTotalArea,
+ const std::vector< ::canvas::Sprite::Reference >& rSortedUpdateSprites )
+ {
+ ENSURE_OR_THROW( mpRenderModule &&
+ mpBackBuffer,
+ "SpriteCanvasHelper::opaqueUpdate(): NULL device pointer " );
+
+ // TODO(P2): optimize this by truly rendering to the front
+ // buffer. Currently, we've the 3D device only for the back
+ // buffer.
+ for( const auto& rSprite : rSortedUpdateSprites )
+ {
+ if( rSprite.is() )
+ {
+ // downcast to derived dxcanvas::Sprite interface, which
+ // provides the actual redraw methods.
+ ::boost::polymorphic_downcast< Sprite* >( rSprite.get() )->redraw();
+ }
+ }
+
+ // TODO(E1): Use numeric_cast to catch overflow here
+ ::basegfx::B2IRange aActualArea( 0, 0,
+ static_cast<sal_Int32>(maScrapRect.getWidth()),
+ static_cast<sal_Int32>(maScrapRect.getHeight()) );
+ aActualArea.intersect( fround( rTotalArea ) );
+
+ // add given update area to the 'blit to foreground' rect
+ maUpdateRect.expand( aActualArea );
+ }
+
+ void SpriteCanvasHelper::genericUpdate( const ::basegfx::B2DRange& rTotalArea,
+ const std::vector< ::canvas::Sprite::Reference >& rSortedUpdateSprites )
+ {
+ ENSURE_OR_THROW( mpRenderModule &&
+ mpBackBuffer,
+ "SpriteCanvasHelper::genericUpdate(): NULL device pointer " );
+
+ // paint background
+ // ================
+
+ // TODO(E1): Use numeric_cast to catch overflow here
+ ::basegfx::B2IRange aActualArea( 0, 0,
+ static_cast<sal_Int32>(maScrapRect.getWidth()),
+ static_cast<sal_Int32>(maScrapRect.getHeight()) );
+ aActualArea.intersect( fround( rTotalArea ) );
+
+ // repaint the given area of the screen with background content
+ mpBackBuffer->draw(aActualArea);
+
+ // paint sprite
+ // ============
+
+ for( const auto& rSprite : rSortedUpdateSprites )
+ {
+ if( rSprite.is() )
+ {
+ // downcast to derived dxcanvas::Sprite interface, which
+ // provides the actual redraw methods.
+ ::boost::polymorphic_downcast< Sprite* >( rSprite.get() )->redraw();
+ }
+ }
+
+ // add given update area to the 'blit to foreground' rect
+ maUpdateRect.expand( aActualArea );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/directx/dx_spritecanvashelper.hxx b/canvas/source/directx/dx_spritecanvashelper.hxx
new file mode 100644
index 0000000000..5c2d98ba81
--- /dev/null
+++ b/canvas/source/directx/dx_spritecanvashelper.hxx
@@ -0,0 +1,152 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/rendering/XSpriteCanvas.hpp>
+#include <com/sun/star/rendering/XIntegerBitmap.hpp>
+
+#include <spriteredrawmanager.hxx>
+#include <rendering/isurfaceproxy.hxx>
+#include <rendering/isurfaceproxymanager.hxx>
+
+#include "dx_bitmapcanvashelper.hxx"
+#include "dx_impltools.hxx"
+#include "dx_rendermodule.hxx"
+#include "dx_surfacebitmap.hxx"
+
+#include <basegfx/range/b2irectangle.hxx>
+
+namespace dxcanvas
+{
+ class SpriteCanvas;
+
+ class SpriteCanvasHelper : public BitmapCanvasHelper
+ {
+ public:
+ SpriteCanvasHelper();
+
+ void init( SpriteCanvas& rParent,
+ ::canvas::SpriteRedrawManager& rManager,
+ const IDXRenderModuleSharedPtr& rRenderModule,
+ const std::shared_ptr<canvas::ISurfaceProxyManager>& rSurfaceProxy,
+ const DXSurfaceBitmapSharedPtr& rBackBuffer,
+ const ::basegfx::B2ISize& rOutputOffset );
+
+ /// Dispose all internal references
+ void disposing();
+
+ // XSpriteCanvas
+ css::uno::Reference<
+ css::rendering::XAnimatedSprite > createSpriteFromAnimation(
+ const css::uno::Reference< css::rendering::XAnimation >& animation );
+
+ css::uno::Reference<
+ css::rendering::XAnimatedSprite > createSpriteFromBitmaps(
+ const css::uno::Sequence<
+ css::uno::Reference<
+ css::rendering::XBitmap > >& animationBitmaps,
+ sal_Int8 interpolationMode );
+
+ css::uno::Reference<
+ css::rendering::XCustomSprite > createCustomSprite(
+ const css::geometry::RealSize2D& spriteSize );
+
+ css::uno::Reference<
+ css::rendering::XSprite > createClonedSprite(
+ const css::uno::Reference< css::rendering::XSprite >& original );
+
+ /** Actually perform the screen update
+
+ @param rCurrArea
+ Current window area in absolute screen coordinates
+
+ @param bUpdateAll
+ sal_True, if everything must be updated, not only changed
+ sprites
+
+ @param io_bSurfaceDirty
+ In/out parameter, whether backbuffer surface is dirty (if
+ yes, we're performing a full update, anyway)
+ */
+ bool updateScreen( const ::basegfx::B2IRectangle& rCurrArea,
+ bool bUpdateAll,
+ bool& io_bSurfaceDirty );
+
+
+ // SpriteRedrawManager functor calls
+
+
+ /** Gets called for simple background repaints
+ */
+ void backgroundPaint( const ::basegfx::B2DRange& rUpdateRect );
+
+ /** Gets called when area can be handled by scrolling.
+
+ Called method must copy screen content from rMoveStart to
+ rMoveEnd, and restore the background in the uncovered
+ areas.
+
+ @param rMoveStart
+ Source rect of the scroll
+
+ @param rMoveEnd
+ Dest rect of the scroll
+
+ @param rUpdateArea
+ All info necessary, should rMoveStart be partially or
+ fully outside the outdev
+ */
+ void scrollUpdate( const ::basegfx::B2DRange& rMoveStart,
+ const ::basegfx::B2DRange& rMoveEnd,
+ const ::canvas::SpriteRedrawManager::UpdateArea& rUpdateArea );
+
+ void opaqueUpdate( const ::basegfx::B2DRange& rTotalArea,
+ const std::vector< ::canvas::Sprite::Reference >& rSortedUpdateSprites );
+
+ void genericUpdate( const ::basegfx::B2DRange& rTotalArea,
+ const std::vector< ::canvas::Sprite::Reference >& rSortedUpdateSprites );
+
+ private:
+ /// For generating sprites
+ SpriteCanvas* mpSpriteSurface;
+
+ /// Set from the SpriteCanvas: instance coordinating sprite redraw
+ ::canvas::SpriteRedrawManager* mpRedrawManager;
+
+ /// DX device, handling all low-level rendering
+ IDXRenderModuleSharedPtr mpRenderModule;
+
+ std::shared_ptr<canvas::ISurfaceProxyManager> mpSurfaceProxy;
+
+ /// Backbuffer, contains the static canvas render output
+ DXSurfaceBitmapSharedPtr mpBackBuffer;
+
+ /// Completely temporary rect storage (used by sprite repaint)
+ mutable ::basegfx::B2IRange maUpdateRect;
+
+ /// Completely temporary rect storage (used by sprite repaint)
+ mutable ::basegfx::B2IRange maScrapRect;
+
+ /// When true, show small bound rects around each sprite
+ bool mbShowSpriteBounds;
+ };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/directx/dx_spritedevicehelper.cxx b/canvas/source/directx/dx_spritedevicehelper.cxx
new file mode 100644
index 0000000000..622246bc2a
--- /dev/null
+++ b/canvas/source/directx/dx_spritedevicehelper.cxx
@@ -0,0 +1,221 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <memory>
+
+#include <sal/log.hxx>
+
+#include <basegfx/utils/canvastools.hxx>
+#include <canvas/canvastools.hxx>
+#include <com/sun/star/lang/NoSupportException.hpp>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <vcl/sysdata.hxx>
+#include <vcl/window.hxx>
+
+#include "dx_canvasbitmap.hxx"
+#include "dx_linepolypolygon.hxx"
+#include "dx_spritecanvas.hxx"
+#include "dx_spritedevicehelper.hxx"
+#include "dx_winstuff.hxx"
+
+
+using namespace ::com::sun::star;
+
+namespace dxcanvas
+{
+ SpriteDeviceHelper::SpriteDeviceHelper() :
+ DeviceHelper(),
+ mpSpriteCanvas( nullptr ),
+ mpBackBuffer(),
+ mpSurfaceProxyManager(),
+ mpRenderModule()
+ {
+ }
+
+ void SpriteDeviceHelper::init( vcl::Window& rWindow,
+ SpriteCanvas& rSpriteCanvas,
+ const awt::Rectangle& rRect,
+ bool /*bFullscreen*/ )
+ {
+ // #i60490# ensure backbuffer has sensible minimal size
+ const sal_Int32 w( std::max(sal_Int32(1),sal_Int32(rRect.Width)));
+ const sal_Int32 h( std::max(sal_Int32(1),sal_Int32(rRect.Height)));
+
+ rSpriteCanvas.setWindow(
+ uno::Reference<awt::XWindow2>(
+ VCLUnoHelper::GetInterface(&rWindow),
+ uno::UNO_QUERY_THROW) );
+
+ const SystemEnvData *pData = rWindow.GetSystemData();
+ const HWND hWnd = reinterpret_cast<HWND>(pData->hWnd);
+ if( !IsWindow( hWnd ) )
+ throw lang::NoSupportException( "Passed window has invalid system window, or canvas out-of-process!" );
+
+ mpSpriteCanvas = &rSpriteCanvas;
+
+ try
+ {
+ // setup directx rendermodule
+ mpRenderModule = createRenderModule( rWindow );
+ }
+ catch (...) {
+
+ throw lang::NoSupportException( "Could not create DirectX device!",
+ rSpriteCanvas.getXWeak() );
+ }
+
+ // create the surfaceproxy manager
+ mpSurfaceProxyManager = ::canvas::createSurfaceProxyManager( mpRenderModule );
+
+ // #i60490# ensure backbuffer has sensible minimal size
+ mpBackBuffer = std::make_shared<DXSurfaceBitmap>(
+ basegfx::B2ISize(w,h),
+ mpSurfaceProxyManager,
+ mpRenderModule,
+ false);
+
+ // Assumes: SystemChildWindow() has CS_OWNDC
+ DeviceHelper::init(GetDC(mpRenderModule->getHWND()),rWindow.GetOutDev(), rSpriteCanvas);
+ }
+
+ void SpriteDeviceHelper::disposing()
+ {
+ // release all references
+ mpBackBuffer.reset();
+ mpSurfaceProxyManager.reset();
+ mpRenderModule.reset();
+ mpSpriteCanvas = nullptr;
+
+ DeviceHelper::disposing();
+ }
+
+ uno::Reference< rendering::XBitmap > SpriteDeviceHelper::createCompatibleBitmap(
+ const uno::Reference< rendering::XGraphicDevice >& /*rDevice*/,
+ const geometry::IntegerSize2D& size )
+ {
+ if( !getDevice() )
+ return uno::Reference< rendering::XBitmap >(); // we're disposed
+
+ DXSurfaceBitmapSharedPtr pBitmap = std::make_shared<DXSurfaceBitmap>(
+ ::basegfx::unotools::b2ISizeFromIntegerSize2D(size),
+ mpSurfaceProxyManager,
+ mpRenderModule,
+ false);
+
+ // create a 24bit RGB system memory surface
+ return uno::Reference< rendering::XBitmap >(new CanvasBitmap(pBitmap,getDevice()));
+ }
+
+ uno::Reference< rendering::XVolatileBitmap > SpriteDeviceHelper::createVolatileBitmap(
+ const uno::Reference< rendering::XGraphicDevice >& /*rDevice*/,
+ const geometry::IntegerSize2D& /*size*/ )
+ {
+ return uno::Reference< rendering::XVolatileBitmap >();
+ }
+
+ uno::Reference< rendering::XBitmap > SpriteDeviceHelper::createCompatibleAlphaBitmap(
+ const uno::Reference< rendering::XGraphicDevice >& /*rDevice*/,
+ const geometry::IntegerSize2D& size )
+ {
+ if( !getDevice() )
+ return uno::Reference< rendering::XBitmap >(); // we're disposed
+
+ DXSurfaceBitmapSharedPtr pBitmap = std::make_shared<DXSurfaceBitmap>(
+ ::basegfx::unotools::b2ISizeFromIntegerSize2D(size),
+ mpSurfaceProxyManager,
+ mpRenderModule,
+ true);
+
+ // create a 32bit ARGB system memory surface
+ return uno::Reference< rendering::XBitmap >(new CanvasBitmap(pBitmap,getDevice()));
+ }
+
+ uno::Reference< rendering::XVolatileBitmap > SpriteDeviceHelper::createVolatileAlphaBitmap(
+ const uno::Reference< rendering::XGraphicDevice >& /*rDevice*/,
+ const geometry::IntegerSize2D& /*size*/ )
+ {
+ return uno::Reference< rendering::XVolatileBitmap >();
+ }
+
+ void SpriteDeviceHelper::destroyBuffers()
+ {
+ // TODO(F3): implement XBufferStrategy interface. For now, we
+ // _always_ will have exactly one backbuffer
+ }
+
+ bool SpriteDeviceHelper::showBuffer( bool, bool )
+ {
+ SAL_WARN("canvas.directx", "Not supposed to be called, handled by SpriteCanvas");
+ return false;
+ }
+
+ bool SpriteDeviceHelper::switchBuffer( bool, bool )
+ {
+ SAL_WARN("canvas.directx", "Not supposed to be called, handled by SpriteCanvas");
+ return false;
+ }
+
+ uno::Any SpriteDeviceHelper::isAccelerated() const
+ {
+ return css::uno::Any(true);
+ }
+
+ void SpriteDeviceHelper::notifySizeUpdate( const awt::Rectangle& rBounds )
+ {
+ // #i60490# ensure backbuffer has sensible minimal size
+ const sal_Int32 x(rBounds.X);
+ const sal_Int32 y(rBounds.Y);
+ const sal_Int32 w(std::max(sal_Int32(1),sal_Int32(rBounds.Width)));
+ const sal_Int32 h(std::max(sal_Int32(1),sal_Int32(rBounds.Height)));
+
+ if( mpRenderModule )
+ mpRenderModule->resize(::basegfx::B2IRange(x,y,x+w,y+h));
+
+ resizeBackBuffer(::basegfx::B2ISize(w,h));
+ }
+
+ void SpriteDeviceHelper::resizeBackBuffer( const ::basegfx::B2ISize& rNewSize )
+ {
+ // disposed?
+ if(!mpBackBuffer)
+ return;
+
+ mpBackBuffer->resize(rNewSize);
+ mpBackBuffer->clear();
+ }
+
+ HWND SpriteDeviceHelper::getHwnd() const
+ {
+ if( mpRenderModule )
+ return mpRenderModule->getHWND();
+ else
+ return nullptr;
+ }
+
+ void SpriteDeviceHelper::dumpScreenContent() const
+ {
+ if( mpRenderModule )
+ mpRenderModule->screenShot();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/directx/dx_spritedevicehelper.hxx b/canvas/source/directx/dx_spritedevicehelper.hxx
new file mode 100644
index 0000000000..ad74177706
--- /dev/null
+++ b/canvas/source/directx/dx_spritedevicehelper.hxx
@@ -0,0 +1,98 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/awt/Rectangle.hpp>
+#include <com/sun/star/rendering/XGraphicDevice.hpp>
+#include <com/sun/star/rendering/XBufferController.hpp>
+
+#include "dx_rendermodule.hxx"
+#include "dx_surfacebitmap.hxx"
+#include "dx_devicehelper.hxx"
+
+#include <rendering/isurfaceproxymanager.hxx>
+
+
+namespace dxcanvas
+{
+ class SpriteCanvas;
+
+ class SpriteDeviceHelper : public DeviceHelper
+ {
+ public:
+ SpriteDeviceHelper();
+
+ void init( vcl::Window& rWindow,
+ SpriteCanvas& rSpriteCanvas,
+ const css::awt::Rectangle& rRect,
+ bool bFullscreen );
+
+ /// Dispose all internal references
+ void disposing();
+
+ // partial override XWindowGraphicDevice
+ css::uno::Reference< css::rendering::XBitmap > createCompatibleBitmap(
+ const css::uno::Reference< css::rendering::XGraphicDevice >& rDevice,
+ const css::geometry::IntegerSize2D& size );
+ css::uno::Reference< css::rendering::XVolatileBitmap > createVolatileBitmap(
+ const css::uno::Reference< css::rendering::XGraphicDevice >& rDevice,
+ const css::geometry::IntegerSize2D& size );
+ css::uno::Reference< css::rendering::XBitmap > createCompatibleAlphaBitmap(
+ const css::uno::Reference< css::rendering::XGraphicDevice >& rDevice,
+ const css::geometry::IntegerSize2D& size );
+ css::uno::Reference< css::rendering::XVolatileBitmap > createVolatileAlphaBitmap(
+ const css::uno::Reference< css::rendering::XGraphicDevice >& rDevice,
+ const css::geometry::IntegerSize2D& size );
+
+ void destroyBuffers( );
+ bool showBuffer( bool bIsVisible, bool bUpdateAll );
+ bool switchBuffer( bool bIsVisible, bool bUpdateAll );
+
+ const IDXRenderModuleSharedPtr& getRenderModule() const { return mpRenderModule; }
+ const DXSurfaceBitmapSharedPtr& getBackBuffer() const { return mpBackBuffer; }
+ const std::shared_ptr<canvas::ISurfaceProxyManager> &getSurfaceProxy() const { return mpSurfaceProxyManager; }
+
+ css::uno::Any isAccelerated() const;
+
+ void notifySizeUpdate( const css::awt::Rectangle& rBounds );
+
+ /** called when DumpScreenContent property is enabled on
+ XGraphicDevice, and writes out bitmaps of current screen.
+ */
+ void dumpScreenContent() const;
+
+ private:
+ void resizeBackBuffer( const ::basegfx::B2ISize& rNewSize );
+ HWND getHwnd() const;
+
+ /// Pointer to sprite canvas (owner of this helper), needed to create bitmaps
+ SpriteCanvas* mpSpriteCanvas;
+
+ DXSurfaceBitmapSharedPtr mpBackBuffer;
+
+ /// Instance passing out HW textures
+ std::shared_ptr<canvas::ISurfaceProxyManager> mpSurfaceProxyManager;
+
+ /// Our encapsulation interface to DirectX
+ IDXRenderModuleSharedPtr mpRenderModule;
+ };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/directx/dx_spritehelper.cxx b/canvas/source/directx/dx_spritehelper.cxx
new file mode 100644
index 0000000000..3cb211886a
--- /dev/null
+++ b/canvas/source/directx/dx_spritehelper.cxx
@@ -0,0 +1,199 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/numeric/ftools.hxx>
+#include <basegfx/point/b2dpoint.hxx>
+#include <basegfx/polygon/b2dpolygoncutandtouch.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolygontriangulator.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <basegfx/utils/canvastools.hxx>
+#include <rtl/math.hxx>
+#include <comphelper/diagnose_ex.hxx>
+
+#include <canvas/canvastools.hxx>
+
+#include "dx_canvascustomsprite.hxx"
+#include "dx_impltools.hxx"
+#include "dx_spritehelper.hxx"
+
+using namespace ::com::sun::star;
+
+namespace dxcanvas
+{
+ SpriteHelper::SpriteHelper() :
+ mpSpriteCanvas(),
+ mpBitmap(),
+ mbTextureDirty( true ),
+ mbShowSpriteBounds( false )
+ {
+ }
+
+ void SpriteHelper::init( const geometry::RealSize2D& rSpriteSize,
+ const SpriteCanvasRef& rSpriteCanvas,
+ const IDXRenderModuleSharedPtr& rRenderModule,
+ const DXSurfaceBitmapSharedPtr& rBitmap,
+ bool bShowSpriteBounds )
+ {
+ ENSURE_OR_THROW( rSpriteCanvas &&
+ rRenderModule &&
+ rBitmap,
+ "SpriteHelper::init(): Invalid device, sprite canvas or surface" );
+
+ mpSpriteCanvas = rSpriteCanvas;
+ mpBitmap = rBitmap;
+ mbTextureDirty = true;
+ mbShowSpriteBounds = bShowSpriteBounds;
+
+ // also init base class
+ CanvasCustomSpriteHelper::init( rSpriteSize,
+ rSpriteCanvas );
+ }
+
+ void SpriteHelper::disposing()
+ {
+ mpBitmap.reset();
+ mpSpriteCanvas.clear();
+
+ // forward to parent
+ CanvasCustomSpriteHelper::disposing();
+ }
+
+ ::basegfx::B2DPolyPolygon SpriteHelper::polyPolygonFromXPolyPolygon2D( uno::Reference< rendering::XPolyPolygon2D >& xPoly ) const
+ {
+ return tools::polyPolygonFromXPolyPolygon2D( xPoly );
+ }
+
+ bool SpriteHelper::needRedraw() const
+ {
+ if( !mpBitmap ||
+ !mpSpriteCanvas )
+ {
+ return false; // we're disposed, no redraw necessary
+ }
+
+ if( !isActive() ||
+ ::basegfx::fTools::equalZero( getAlpha() ) )
+ {
+ return false; // sprite is invisible
+ }
+
+ return true;
+ }
+
+ void SpriteHelper::redraw( bool& io_bSurfaceDirty ) const
+ {
+ if( !mpBitmap ||
+ !mpSpriteCanvas )
+ {
+ return; // we're disposed
+ }
+
+ const ::basegfx::B2DPoint& rPos( getPosPixel() );
+ const double fAlpha( getAlpha() );
+
+ if( isActive() &&
+ !::basegfx::fTools::equalZero( fAlpha ) )
+ {
+
+ // TODO(Q2): For the time being, Device does not take a target
+ // surface, but always unconditionally renders to the
+ // background buffer.
+
+ // log output pos in device pixel
+ SAL_INFO("canvas.directx", "SpriteHelper::redraw(): output pos is (" <<
+ rPos.getX() << "," << rPos.getY() << ")" );
+
+ const ::basegfx::B2DVector& rSize( getSizePixel() );
+ const ::basegfx::B2DHomMatrix& rTransform( getTransformation() );
+ const uno::Reference< rendering::XPolyPolygon2D >& xClip( getClip() );
+
+ mbTextureDirty = false;
+ io_bSurfaceDirty = false; // state taken, and processed.
+
+ ::basegfx::B2DPolyPolygon aClipPath; // empty for no clip
+ bool bIsClipRectangular( false ); // false, if no
+ // clip, or clip
+ // is complex
+
+ // setup and apply clip (if any)
+ // =================================
+
+ if( xClip.is() )
+ {
+ aClipPath = tools::polyPolygonFromXPolyPolygon2D( xClip );
+
+ const sal_Int32 nNumClipPolygons( aClipPath.count() );
+ if( nNumClipPolygons )
+ {
+ // TODO(P2): hold rectangle attribute directly
+ // at the XPolyPolygon2D
+
+ // check whether the clip is rectangular
+ if( nNumClipPolygons == 1 )
+ if( ::basegfx::utils::isRectangle( aClipPath.getB2DPolygon( 0 ) ) )
+ bIsClipRectangular = true;
+ }
+ }
+
+ const ::basegfx::B2DRectangle aSourceRect( 0.0,
+ 0.0,
+ rSize.getX(),
+ rSize.getY() );
+
+ // draw simple rectangular area if no clip is set.
+ if( !aClipPath.count() )
+ {
+ mpBitmap->draw(fAlpha,rPos,rTransform);
+ }
+ else if( bIsClipRectangular )
+ {
+ // apply a simple rect clip
+ // ========================
+
+ ::basegfx::B2DRectangle aClipBounds(
+ ::basegfx::utils::getRange( aClipPath ) );
+ aClipBounds.intersect( aSourceRect );
+
+ mpBitmap->draw(fAlpha,rPos,aClipBounds,rTransform);
+ }
+ else
+ {
+ // apply clip the hard way
+ // =======================
+
+ mpBitmap->draw(fAlpha,rPos,aClipPath,rTransform);
+ }
+
+ if( mbShowSpriteBounds )
+ {
+ if( aClipPath.count() )
+ {
+ // TODO(F2): Re-enable debug output
+ }
+ }
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/directx/dx_spritehelper.hxx b/canvas/source/directx/dx_spritehelper.hxx
new file mode 100644
index 0000000000..86e6dbaf6c
--- /dev/null
+++ b/canvas/source/directx/dx_spritehelper.hxx
@@ -0,0 +1,102 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/rendering/XCustomSprite.hpp>
+
+#include <base/canvascustomspritehelper.hxx>
+
+#include <basegfx/point/b2dpoint.hxx>
+#include <basegfx/vector/b2isize.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+
+#include "dx_spritecanvas.hxx"
+#include "dx_surfacebitmap.hxx"
+
+namespace dxcanvas
+{
+ /* Definition of SpriteHelper class */
+
+ /** Helper class for canvas sprites.
+
+ This class implements all sprite-related functionality, like
+ that available on the XSprite interface.
+ */
+ class SpriteHelper : public ::canvas::CanvasCustomSpriteHelper
+ {
+ public:
+ /** Create sprite helper
+ */
+ SpriteHelper();
+
+ /** Late-init the sprite helper
+
+ @param rSpriteSize
+ Size of the sprite
+
+ @param rSpriteCanvas
+ Sprite canvas this sprite is part of. Object stores
+ ref-counted reference to it, thus, don't forget to pass on
+ disposing()!
+
+ @param rRenderModule
+ rendermodule to use
+
+ @param rSpriteSurface
+ The surface of the sprite (not the DX texture, but the
+ persistent target of content rendering)
+
+ @param bShowSpriteBounds
+ When true, little debug bound rects for sprites are shown
+ */
+ void init( const css::geometry::RealSize2D& rSpriteSize,
+ const SpriteCanvasRef& rSpriteCanvas,
+ const IDXRenderModuleSharedPtr& rRenderModule,
+ const DXSurfaceBitmapSharedPtr& rBitmap,
+ bool bShowSpriteBounds );
+
+ void disposing();
+
+ /** Repaint sprite content via hardware to associated sprite
+ canvas
+
+ @param io_bSurfaceDirty
+ Input/output parameter, whether the sprite content is
+ dirty or not. If texture was updated, set to false
+
+ */
+ void redraw( bool& io_bSurfaceDirty ) const;
+
+ private:
+ virtual ::basegfx::B2DPolyPolygon polyPolygonFromXPolyPolygon2D(
+ css::uno::Reference< css::rendering::XPolyPolygon2D >& xPoly ) const override;
+
+ /// Returns true, if the sprite _really_ needs redraw
+ bool needRedraw() const;
+
+ SpriteCanvasRef mpSpriteCanvas;
+
+ DXSurfaceBitmapSharedPtr mpBitmap;
+ mutable bool mbTextureDirty; // when true, texture needs update
+ bool mbShowSpriteBounds; // when true, debug bound rect for sprites is shown
+ };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/directx/dx_surfacebitmap.cxx b/canvas/source/directx/dx_surfacebitmap.cxx
new file mode 100644
index 0000000000..5a3992eea2
--- /dev/null
+++ b/canvas/source/directx/dx_surfacebitmap.cxx
@@ -0,0 +1,654 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <memory>
+#include <string.h>
+
+#include <com/sun/star/rendering/ColorComponentTag.hpp>
+
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/range/b2irange.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <rendering/icolorbuffer.hxx>
+
+#include "dx_graphicsprovider.hxx"
+#include "dx_impltools.hxx"
+#include "dx_surfacebitmap.hxx"
+#include "dx_surfacegraphics.hxx"
+
+using namespace ::com::sun::star;
+
+namespace dxcanvas
+{
+ namespace
+ {
+
+ // DXColorBuffer
+
+
+ struct DXColorBuffer : public canvas::IColorBuffer
+ {
+ public:
+ DXColorBuffer( const sal::systools::COMReference<surface_type>& rSurface,
+ const ::basegfx::B2ISize& rSize )
+ : maSize(rSize)
+ , maLockedRect{}
+ , mpSurface(rSurface)
+ {
+ }
+
+ // implementation of the 'IColorBuffer' interface
+ public:
+
+ virtual sal_uInt8* lock() const override;
+ virtual void unlock() const override;
+ virtual sal_uInt32 getWidth() const override;
+ virtual sal_uInt32 getHeight() const override;
+ virtual sal_uInt32 getStride() const override;
+ virtual Format getFormat() const override;
+
+ private:
+
+ ::basegfx::B2ISize maSize;
+ mutable D3DLOCKED_RECT maLockedRect;
+ sal::systools::COMReference<surface_type> mpSurface;
+ };
+
+ sal_uInt8* DXColorBuffer::lock() const
+ {
+ if(SUCCEEDED(mpSurface->LockRect(&maLockedRect,nullptr,D3DLOCK_NOSYSLOCK|D3DLOCK_READONLY)))
+ return static_cast<sal_uInt8 *>(maLockedRect.pBits);
+ return nullptr;
+ }
+
+ void DXColorBuffer::unlock() const
+ {
+ mpSurface->UnlockRect();
+ }
+
+ sal_uInt32 DXColorBuffer::getWidth() const
+ {
+ return maSize.getWidth();
+ }
+
+ sal_uInt32 DXColorBuffer::getHeight() const
+ {
+ return maSize.getHeight();
+ }
+
+ sal_uInt32 DXColorBuffer::getStride() const
+ {
+ return maLockedRect.Pitch;
+ }
+
+ canvas::IColorBuffer::Format DXColorBuffer::getFormat() const
+ {
+ return canvas::IColorBuffer::Format::X8R8G8B8;
+ }
+
+
+ // GDIColorBuffer
+
+
+ struct GDIColorBuffer : public canvas::IColorBuffer
+ {
+ public:
+
+ GDIColorBuffer( const BitmapSharedPtr& rSurface,
+ const ::basegfx::B2ISize& rSize )
+ : maSize(rSize)
+ , aBmpData{}
+ , mpGDIPlusBitmap(rSurface)
+ {
+ }
+
+ // implementation of the 'IColorBuffer' interface
+ public:
+
+ virtual sal_uInt8* lock() const override;
+ virtual void unlock() const override;
+ virtual sal_uInt32 getWidth() const override;
+ virtual sal_uInt32 getHeight() const override;
+ virtual sal_uInt32 getStride() const override;
+ virtual Format getFormat() const override;
+
+ private:
+
+ ::basegfx::B2ISize maSize;
+ mutable Gdiplus::BitmapData aBmpData;
+ BitmapSharedPtr mpGDIPlusBitmap;
+ };
+
+ sal_uInt8* GDIColorBuffer::lock() const
+ {
+ aBmpData.Width = maSize.getWidth();
+ aBmpData.Height = maSize.getHeight();
+ aBmpData.Stride = 4*aBmpData.Width;
+ aBmpData.PixelFormat = PixelFormat32bppARGB;
+ aBmpData.Scan0 = nullptr;
+ const Gdiplus::Rect aRect( 0,0,aBmpData.Width,aBmpData.Height );
+ if( Gdiplus::Ok != mpGDIPlusBitmap->LockBits( &aRect,
+ Gdiplus::ImageLockModeRead,
+ PixelFormat32bppARGB,
+ &aBmpData ) )
+ {
+ return nullptr;
+ }
+
+ return static_cast<sal_uInt8*>(aBmpData.Scan0);
+ }
+
+ void GDIColorBuffer::unlock() const
+ {
+ mpGDIPlusBitmap->UnlockBits( &aBmpData );
+ }
+
+ sal_uInt32 GDIColorBuffer::getWidth() const
+ {
+ return maSize.getWidth();
+ }
+
+ sal_uInt32 GDIColorBuffer::getHeight() const
+ {
+ return maSize.getHeight();
+ }
+
+ sal_uInt32 GDIColorBuffer::getStride() const
+ {
+ return aBmpData.Stride;
+ }
+
+ canvas::IColorBuffer::Format GDIColorBuffer::getFormat() const
+ {
+ return canvas::IColorBuffer::Format::A8R8G8B8;
+ }
+ }
+
+
+ // DXSurfaceBitmap::DXSurfaceBitmap
+
+
+ DXSurfaceBitmap::DXSurfaceBitmap( const ::basegfx::B2ISize& rSize,
+ const std::shared_ptr<canvas::ISurfaceProxyManager>& rMgr,
+ const IDXRenderModuleSharedPtr& rRenderModule,
+ bool bWithAlpha ) :
+ mpGdiPlusUser( GDIPlusUser::createInstance() ),
+ maSize(rSize),
+ mpRenderModule(rRenderModule),
+ mpSurfaceManager(rMgr),
+ mpSurfaceProxy(),
+ mpSurface(),
+ mpGDIPlusBitmap(),
+ mpGraphics(),
+ mpColorBuffer(),
+ mbIsSurfaceDirty(true),
+ mbAlpha(bWithAlpha)
+ {
+ init();
+ }
+
+
+ // DXSurfaceBitmap::getSize
+
+
+ ::basegfx::B2ISize DXSurfaceBitmap::getSize() const
+ {
+ return maSize;
+ }
+
+
+ // DXSurfaceBitmap::init
+
+
+ void DXSurfaceBitmap::init()
+ {
+ // create container for pixel data
+ if(mbAlpha)
+ {
+ mpGDIPlusBitmap = std::make_shared<Gdiplus::Bitmap>(
+ maSize.getWidth(),
+ maSize.getHeight(),
+ PixelFormat32bppARGB
+ );
+ mpGraphics = tools::createGraphicsFromBitmap(mpGDIPlusBitmap);
+
+ // create the colorbuffer object, which is basically a simple
+ // wrapper around the directx surface. the colorbuffer is the
+ // interface which is used by the surfaceproxy to support any
+ // kind of underlying structure for the pixel data container.
+ mpColorBuffer = std::make_shared<GDIColorBuffer>(mpGDIPlusBitmap, maSize);
+ }
+ else
+ {
+ mpSurface = mpRenderModule->createSystemMemorySurface(maSize);
+
+ // create the colorbuffer object, which is basically a simple
+ // wrapper around the directx surface. the colorbuffer is the
+ // interface which is used by the surfaceproxy to support any
+ // kind of underlying structure for the pixel data container.
+ mpColorBuffer = std::make_shared<DXColorBuffer>(mpSurface, maSize);
+ }
+
+ // create a (possibly hardware accelerated) mirror surface.
+ mpSurfaceProxy = mpSurfaceManager->createSurfaceProxy(mpColorBuffer);
+ }
+
+
+ // DXSurfaceBitmap::resize
+
+
+ bool DXSurfaceBitmap::resize(const ::basegfx::B2ISize& rSize)
+ {
+ if(maSize != rSize)
+ {
+ maSize = rSize;
+ init();
+ }
+
+ return true;
+ }
+
+
+ // DXSurfaceBitmap::clear
+
+
+ void DXSurfaceBitmap::clear()
+ {
+ GraphicsSharedPtr pGraphics(getGraphics());
+ Gdiplus::Color transColor(255,0,0,0);
+ pGraphics->SetCompositingMode( Gdiplus::CompositingModeSourceCopy );
+ pGraphics->Clear( transColor );
+ }
+
+
+ // DXSurfaceBitmap::hasAlpha
+
+
+ bool DXSurfaceBitmap::hasAlpha() const
+ {
+ return mbAlpha;
+ }
+
+
+ // DXSurfaceBitmap::getGraphics
+
+
+ GraphicsSharedPtr DXSurfaceBitmap::getGraphics()
+ {
+ // since clients will most probably draw directly
+ // to the GDI+ bitmap, we need to mark it as dirty
+ // to ensure that the corresponding dxsurface will
+ // be updated.
+ mbIsSurfaceDirty = true;
+
+ if(hasAlpha())
+ return mpGraphics;
+ else
+ return createSurfaceGraphics(mpSurface);
+ }
+
+
+ // DXSurfaceBitmap::getBitmap
+
+
+ BitmapSharedPtr DXSurfaceBitmap::getBitmap() const
+ {
+ if(hasAlpha())
+ return mpGDIPlusBitmap;
+
+ BitmapSharedPtr pResult;
+
+ D3DLOCKED_RECT aLockedRect;
+ if(SUCCEEDED(mpSurface->LockRect(&aLockedRect,nullptr,D3DLOCK_NOSYSLOCK|D3DLOCK_READONLY)))
+ {
+ // decide about the format we pass the gdi+, the directx surface is always
+ // 32bit, either with or without alpha component.
+ Gdiplus::PixelFormat nFormat = hasAlpha() ? PixelFormat32bppARGB : PixelFormat32bppRGB;
+
+ // construct a gdi+ bitmap from the raw pixel data.
+ pResult = std::make_shared<Gdiplus::Bitmap>(maSize.getWidth(), maSize.getHeight(),
+ aLockedRect.Pitch,
+ nFormat,
+ static_cast<BYTE *>(aLockedRect.pBits) );
+
+ mpSurface->UnlockRect();
+ }
+
+ return pResult;
+ }
+
+
+ // DXSurfaceBitmap::draw
+
+
+ bool DXSurfaceBitmap::draw( double fAlpha,
+ const ::basegfx::B2DPoint& rPos,
+ const ::basegfx::B2DPolyPolygon& rClipPoly,
+ const ::basegfx::B2DHomMatrix& rTransform )
+ {
+ if( mbIsSurfaceDirty )
+ {
+ mpSurfaceProxy->setColorBufferDirty();
+ mbIsSurfaceDirty = false;
+ }
+
+ return mpSurfaceProxy->draw( fAlpha, rPos, rClipPoly, rTransform );
+ }
+
+
+ // DXSurfaceBitmap::draw
+
+
+ bool DXSurfaceBitmap::draw( double fAlpha,
+ const ::basegfx::B2DPoint& rPos,
+ const ::basegfx::B2DRange& rArea,
+ const ::basegfx::B2DHomMatrix& rTransform )
+ {
+ if( mbIsSurfaceDirty )
+ {
+ mpSurfaceProxy->setColorBufferDirty();
+ mbIsSurfaceDirty = false;
+ }
+
+ return mpSurfaceProxy->draw( fAlpha, rPos, rArea, rTransform );
+ }
+
+
+ // DXSurfaceBitmap::draw
+
+
+ bool DXSurfaceBitmap::draw( double fAlpha,
+ const ::basegfx::B2DPoint& rPos,
+ const ::basegfx::B2DHomMatrix& rTransform )
+ {
+ if( mbIsSurfaceDirty )
+ {
+ mpSurfaceProxy->setColorBufferDirty();
+ mbIsSurfaceDirty = false;
+ }
+
+ return mpSurfaceProxy->draw( fAlpha, rPos, rTransform );
+ }
+
+
+ // DXSurfaceBitmap::draw
+
+
+ bool DXSurfaceBitmap::draw( const ::basegfx::B2IRange& rArea )
+ {
+ if( mbIsSurfaceDirty )
+ {
+ mpSurfaceProxy->setColorBufferDirty();
+ mbIsSurfaceDirty = false;
+ }
+
+ const double fAlpha(1.0);
+ const ::basegfx::B2DHomMatrix aTransform;
+ const ::basegfx::B2DRange aIEEEArea( rArea );
+ return mpSurfaceProxy->draw(fAlpha,
+ ::basegfx::B2DPoint(),
+ aIEEEArea,
+ aTransform);
+ }
+
+
+ // DXSurfaceBitmap::getData
+
+
+ uno::Sequence< sal_Int8 > DXSurfaceBitmap::getData( rendering::IntegerBitmapLayout& rBitmapLayout,
+ const geometry::IntegerRectangle2D& rect )
+ {
+ if(hasAlpha())
+ {
+ uno::Sequence< sal_Int8 > aRes( (rect.X2-rect.X1)*(rect.Y2-rect.Y1)*4 ); // TODO(F1): Be format-agnostic here
+
+ const Gdiplus::Rect aRect( tools::gdiPlusRectFromIntegerRectangle2D( rect ) );
+
+ Gdiplus::BitmapData aBmpData;
+ aBmpData.Width = rect.X2-rect.X1;
+ aBmpData.Height = rect.Y2-rect.Y1;
+ aBmpData.Stride = 4*aBmpData.Width;
+ aBmpData.PixelFormat = PixelFormat32bppARGB;
+ aBmpData.Scan0 = aRes.getArray();
+
+ // TODO(F1): Support more pixel formats natively
+
+ // read data from bitmap
+ if( Gdiplus::Ok != mpGDIPlusBitmap->LockBits( &aRect,
+ Gdiplus::ImageLockModeRead | Gdiplus::ImageLockModeUserInputBuf,
+ PixelFormat32bppARGB, // TODO(F1): Adapt to
+ // Graphics native
+ // format/change
+ // getMemoryLayout
+ &aBmpData ) )
+ {
+ // failed to lock, bail out
+ return uno::Sequence< sal_Int8 >();
+ }
+
+ mpGDIPlusBitmap->UnlockBits( &aBmpData );
+
+ return aRes;
+ }
+ else
+ {
+ sal_uInt32 nWidth = rect.X2-rect.X1;
+ sal_uInt32 nHeight = rect.Y2-rect.Y1;
+
+ uno::Sequence< sal_Int8 > aRes(nWidth*nHeight*4);
+
+ D3DLOCKED_RECT aLockedRect;
+ if(FAILED(mpSurface->LockRect(&aLockedRect,nullptr,D3DLOCK_NOSYSLOCK|D3DLOCK_READONLY)))
+ return uno::Sequence< sal_Int8 >();
+ D3DSURFACE_DESC aDesc;
+ if(FAILED(mpSurface->GetDesc(&aDesc)))
+ return uno::Sequence< sal_Int8 >();
+
+ assert(aDesc.Format == D3DFMT_A8R8G8B8 || aDesc.Format == D3DFMT_X8R8G8B8);
+
+ sal_uInt8 *pSrc = (static_cast<BYTE *>(aLockedRect.pBits)+(rect.Y1*aLockedRect.Pitch))+rect.X1;
+ sal_uInt8 *pDst = reinterpret_cast<sal_uInt8 *>(aRes.getArray());
+ sal_uInt32 nSegmentSizeInBytes = nWidth*4;
+ for(sal_uInt32 y=0; y<nHeight; ++y)
+ {
+ memcpy(pDst,pSrc,nSegmentSizeInBytes);
+ pDst += nSegmentSizeInBytes;
+ pSrc += aLockedRect.Pitch;
+ }
+
+ if(rBitmapLayout.ColorSpace->getComponentTags().getArray()[0] == rendering::ColorComponentTag::RGB_RED &&
+ rBitmapLayout.ColorSpace->getComponentTags().getArray()[2] == rendering::ColorComponentTag::RGB_BLUE)
+ {
+ pDst = reinterpret_cast<sal_uInt8 *>(aRes.getArray());
+ for(sal_uInt32 y=0; y<nHeight; ++y)
+ {
+ sal_uInt8* pPixel = pDst;
+ for(sal_uInt32 n = 0; n<nWidth; n++)
+ {
+ sal_uInt8 nB = pPixel[0];
+ pPixel[0] = pPixel[2];
+ pPixel[2] = nB;
+ pPixel += 4;
+ }
+ pDst += nSegmentSizeInBytes;
+ }
+ }
+
+ mpSurface->UnlockRect();
+ return aRes;
+ }
+ }
+
+
+ // DXSurfaceBitmap::setData
+
+
+ void DXSurfaceBitmap::setData( const uno::Sequence< sal_Int8 >& data,
+ const rendering::IntegerBitmapLayout& /*bitmapLayout*/,
+ const geometry::IntegerRectangle2D& rect )
+ {
+ if(hasAlpha())
+ {
+ const Gdiplus::Rect aRect( tools::gdiPlusRectFromIntegerRectangle2D( rect ) );
+
+ Gdiplus::BitmapData aBmpData;
+ aBmpData.Width = rect.X2-rect.X1;
+ aBmpData.Height = rect.Y2-rect.Y1;
+ aBmpData.Stride = 4*aBmpData.Width;
+ aBmpData.PixelFormat = PixelFormat32bppARGB;
+ aBmpData.Scan0 = const_cast<sal_Int8 *>(data.getConstArray());
+
+ // TODO(F1): Support more pixel formats natively
+
+ if( Gdiplus::Ok != mpGDIPlusBitmap->LockBits( &aRect,
+ Gdiplus::ImageLockModeWrite | Gdiplus::ImageLockModeUserInputBuf,
+ PixelFormat32bppARGB, // TODO: Adapt to
+ // Graphics native
+ // format/change
+ // getMemoryLayout
+ &aBmpData ) )
+ {
+ throw uno::RuntimeException("GDIPlus method call was unsuccessful, problem with locking bitmap aRect object");
+ }
+
+ // commit data to bitmap
+ mpGDIPlusBitmap->UnlockBits( &aBmpData );
+ }
+ else
+ {
+ sal_uInt32 nWidth = rect.X2-rect.X1;
+ sal_uInt32 nHeight = rect.Y2-rect.Y1;
+
+ // lock the directx surface to receive the pointer to the surface memory.
+ D3DLOCKED_RECT aLockedRect;
+ if(FAILED(mpSurface->LockRect(&aLockedRect,nullptr,D3DLOCK_NOSYSLOCK|D3DLOCK_READONLY)))
+ throw uno::RuntimeException("failed to lock directx surface to surface memory");
+
+ sal_uInt8 const *pSrc = reinterpret_cast<sal_uInt8 const *>(data.getConstArray());
+ sal_uInt8 *pDst = (static_cast<BYTE *>(aLockedRect.pBits)+(rect.Y1*aLockedRect.Pitch))+rect.X1;
+ sal_uInt32 nSegmentSizeInBytes = nWidth<<4;
+ for(sal_uInt32 y=0; y<nHeight; ++y)
+ {
+ memcpy(pDst,pSrc,nSegmentSizeInBytes);
+ pSrc += nSegmentSizeInBytes;
+ pDst += aLockedRect.Pitch;
+ }
+
+ mpSurface->UnlockRect();
+ }
+
+ mbIsSurfaceDirty = true;
+ }
+
+
+ // DXSurfaceBitmap::setPixel
+
+
+ void DXSurfaceBitmap::setPixel( const uno::Sequence< sal_Int8 >& color,
+ const rendering::IntegerBitmapLayout& /*bitmapLayout*/,
+ const geometry::IntegerPoint2D& pos )
+ {
+ if(hasAlpha())
+ {
+ const geometry::IntegerSize2D aSize( maSize.getWidth(), maSize.getHeight() );
+
+ ENSURE_ARG_OR_THROW( pos.X >= 0 && pos.X < aSize.Width,
+ "CanvasHelper::setPixel: X coordinate out of bounds" );
+ ENSURE_ARG_OR_THROW( pos.Y >= 0 && pos.Y < aSize.Height,
+ "CanvasHelper::setPixel: Y coordinate out of bounds" );
+ ENSURE_ARG_OR_THROW( color.getLength() > 3,
+ "CanvasHelper::setPixel: not enough color components" );
+
+ if( Gdiplus::Ok != mpGDIPlusBitmap->SetPixel( pos.X, pos.Y,
+ Gdiplus::Color( tools::sequenceToArgb( color ))))
+ {
+ throw uno::RuntimeException("Problem with setting the color of bitmap object");
+ }
+ }
+ else
+ {
+ ENSURE_ARG_OR_THROW( pos.X >= 0 && pos.X < maSize.getWidth(),
+ "CanvasHelper::setPixel: X coordinate out of bounds" );
+ ENSURE_ARG_OR_THROW( pos.Y >= 0 && pos.Y < maSize.getHeight(),
+ "CanvasHelper::setPixel: Y coordinate out of bounds" );
+ ENSURE_ARG_OR_THROW( color.getLength() > 3,
+ "CanvasHelper::setPixel: not enough color components" );
+
+ Gdiplus::Color aColor(tools::sequenceToArgb(color));
+
+ // lock the directx surface to receive the pointer to the surface memory.
+ D3DLOCKED_RECT aLockedRect;
+ if(FAILED(mpSurface->LockRect(&aLockedRect,nullptr,D3DLOCK_NOSYSLOCK|D3DLOCK_READONLY)))
+ throw uno::RuntimeException("cannot lock the directx surface to surface memory");
+
+ sal_uInt32 *pDst = reinterpret_cast<sal_uInt32 *>((static_cast<BYTE *>(aLockedRect.pBits)+(pos.Y*aLockedRect.Pitch))+pos.X);
+ *pDst = aColor.GetValue();
+ mpSurface->UnlockRect();
+ }
+
+ mbIsSurfaceDirty = true;
+ }
+
+
+ // DXSurfaceBitmap::getPixel
+
+
+ uno::Sequence< sal_Int8 > DXSurfaceBitmap::getPixel( rendering::IntegerBitmapLayout& /*bitmapLayout*/,
+ const geometry::IntegerPoint2D& pos )
+ {
+ if(hasAlpha())
+ {
+ const geometry::IntegerSize2D aSize(maSize.getWidth(), maSize.getHeight());
+
+ ENSURE_ARG_OR_THROW( pos.X >= 0 && pos.X < aSize.Width,
+ "CanvasHelper::getPixel: X coordinate out of bounds" );
+ ENSURE_ARG_OR_THROW( pos.Y >= 0 && pos.Y < aSize.Height,
+ "CanvasHelper::getPixel: Y coordinate out of bounds" );
+
+ Gdiplus::Color aColor;
+
+ if( Gdiplus::Ok != mpGDIPlusBitmap->GetPixel( pos.X, pos.Y, &aColor ) )
+ return uno::Sequence< sal_Int8 >();
+
+ return tools::argbToIntSequence(aColor.GetValue());
+ }
+ else
+ {
+ ENSURE_ARG_OR_THROW( pos.X >= 0 && pos.X < maSize.getWidth(),
+ "CanvasHelper::getPixel: X coordinate out of bounds" );
+ ENSURE_ARG_OR_THROW( pos.Y >= 0 && pos.Y < maSize.getHeight(),
+ "CanvasHelper::getPixel: Y coordinate out of bounds" );
+
+ // lock the directx surface to receive the pointer to the surface memory.
+ D3DLOCKED_RECT aLockedRect;
+ if(FAILED(mpSurface->LockRect(&aLockedRect,nullptr,D3DLOCK_NOSYSLOCK|D3DLOCK_READONLY)))
+ throw uno::RuntimeException("failed to lock directX surface to surface memory");
+
+ sal_uInt32 *pDst = reinterpret_cast<sal_uInt32 *>((static_cast<BYTE *>(aLockedRect.pBits)+(pos.Y*aLockedRect.Pitch))+pos.X);
+ Gdiplus::Color aColor(*pDst);
+ mpSurface->UnlockRect();
+
+ return tools::argbToIntSequence(aColor.GetValue());
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/directx/dx_surfacebitmap.hxx b/canvas/source/directx/dx_surfacebitmap.hxx
new file mode 100644
index 0000000000..f39ebb5b97
--- /dev/null
+++ b/canvas/source/directx/dx_surfacebitmap.hxx
@@ -0,0 +1,135 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <rendering/isurfaceproxy.hxx>
+#include <rendering/isurfaceproxymanager.hxx>
+#include "dx_ibitmap.hxx"
+#include "dx_canvasfont.hxx"
+#include "dx_gdiplususer.hxx"
+#include "dx_rendermodule.hxx"
+
+namespace dxcanvas
+{
+ class DXSurfaceBitmap : public IBitmap
+ {
+ public:
+ DXSurfaceBitmap( const ::basegfx::B2ISize& rSize,
+ const std::shared_ptr<canvas::ISurfaceProxyManager>& rMgr,
+ const IDXRenderModuleSharedPtr& rRenderModule,
+ bool bWithAlpha );
+
+ bool resize(const ::basegfx::B2ISize& rSize);
+ void clear();
+
+ virtual GraphicsSharedPtr getGraphics() override;
+
+ virtual BitmapSharedPtr getBitmap() const override;
+ virtual ::basegfx::B2ISize getSize() const override;
+ virtual bool hasAlpha() const override;
+
+ sal::systools::COMReference<surface_type> getSurface() const { return mpSurface; }
+
+ bool draw( double fAlpha,
+ const ::basegfx::B2DPoint& rPos,
+ const ::basegfx::B2DHomMatrix& rTransform );
+
+ bool draw( const ::basegfx::B2IRange& rArea );
+
+ bool draw( double fAlpha,
+ const ::basegfx::B2DPoint& rPos,
+ const ::basegfx::B2DRange& rArea,
+ const ::basegfx::B2DHomMatrix& rTransform );
+
+ bool draw( double fAlpha,
+ const ::basegfx::B2DPoint& rPos,
+ const ::basegfx::B2DPolyPolygon& rClipPoly,
+ const ::basegfx::B2DHomMatrix& rTransform );
+
+ virtual css::uno::Sequence< sal_Int8 > getData(
+ css::rendering::IntegerBitmapLayout& bitmapLayout,
+ const css::geometry::IntegerRectangle2D& rect ) override;
+
+ virtual void setData(
+ const css::uno::Sequence< sal_Int8 >& data,
+ const css::rendering::IntegerBitmapLayout& bitmapLayout,
+ const css::geometry::IntegerRectangle2D& rect ) override;
+
+ virtual void setPixel(
+ const css::uno::Sequence< sal_Int8 >& color,
+ const css::rendering::IntegerBitmapLayout& bitmapLayout,
+ const css::geometry::IntegerPoint2D& pos ) override;
+
+ virtual css::uno::Sequence< sal_Int8 > getPixel(
+ css::rendering::IntegerBitmapLayout& bitmapLayout,
+ const css::geometry::IntegerPoint2D& pos ) override;
+
+ private:
+ void init();
+
+ // Refcounted global GDI+ state container
+ GDIPlusUserSharedPtr mpGdiPlusUser;
+
+ // size of this image in pixels [integral unit]
+ ::basegfx::B2ISize maSize;
+
+ // pointer to the rendermodule, needed to create surfaces
+ // which are used as container for the actual pixel data.
+ // generally we could use any kind of storage, but GDI+
+ // is not willing to render antialiased fonts unless we
+ // use this special kind of container, don't ask me why...
+ IDXRenderModuleSharedPtr mpRenderModule;
+
+ // pointer to the surface manager, needed in case clients
+ // want to resize the bitmap.
+ std::shared_ptr<canvas::ISurfaceProxyManager> mpSurfaceManager;
+
+ // access point to the surface proxy which handles
+ // the hardware-dependent rendering stuff.
+ std::shared_ptr< canvas::ISurfaceProxy > mpSurfaceProxy;
+
+ // container for pixel data, we need to use a directx
+ // surface since GDI+ sucks...
+ sal::systools::COMReference<surface_type> mpSurface;
+
+ // since GDI+ does not work correctly in case we
+ // run on a 16bit display [don't ask me why] we need
+ // to occasionally render to a native GDI+ bitmap.
+ BitmapSharedPtr mpGDIPlusBitmap;
+ // Graphics for the mpGDIPlusBitmap
+ GraphicsSharedPtr mpGraphics;
+
+ // internal implementation of the iColorBuffer interface
+ std::shared_ptr<canvas::IColorBuffer> mpColorBuffer;
+
+ // indicates whether the associated surface needs
+ // to refresh its contents or not. in other words,
+ // this flag is set iff both representations are
+ // out of sync.
+ mutable bool mbIsSurfaceDirty;
+
+ // true if the bitmap contains an alpha channel
+ bool mbAlpha;
+ };
+
+ typedef std::shared_ptr< DXSurfaceBitmap > DXSurfaceBitmapSharedPtr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/directx/dx_surfacegraphics.cxx b/canvas/source/directx/dx_surfacegraphics.cxx
new file mode 100644
index 0000000000..075f2fc153
--- /dev/null
+++ b/canvas/source/directx/dx_surfacegraphics.cxx
@@ -0,0 +1,77 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include "dx_impltools.hxx"
+#include "dx_surfacegraphics.hxx"
+
+using namespace ::com::sun::star;
+
+namespace dxcanvas
+{
+ namespace
+ {
+ struct GraphicsDeleter
+ {
+ sal::systools::COMReference<surface_type> mpSurface;
+ HDC maHDC;
+
+ GraphicsDeleter(const sal::systools::COMReference<surface_type>& rSurface, HDC hdc) :
+ mpSurface(rSurface),
+ maHDC(hdc)
+ {}
+
+ void operator()( Gdiplus::Graphics* pGraphics )
+ {
+ if(!pGraphics)
+ return;
+
+ pGraphics->Flush(Gdiplus::FlushIntentionSync);
+ delete pGraphics;
+
+ if(mpSurface.is())
+ mpSurface->ReleaseDC( maHDC );
+ }
+ };
+ }
+
+ GraphicsSharedPtr createSurfaceGraphics(const sal::systools::COMReference<surface_type>& rSurface )
+ {
+ GraphicsSharedPtr pRet;
+ HDC aHDC;
+ if( SUCCEEDED(rSurface->GetDC( &aHDC )) )
+ {
+ Gdiplus::Graphics* pGraphics = Gdiplus::Graphics::FromHDC( aHDC );
+ if(pGraphics)
+ {
+ tools::setupGraphics( *pGraphics );
+ pRet.reset(pGraphics,
+ GraphicsDeleter(rSurface, aHDC));
+ return pRet;
+ }
+ else
+ rSurface->ReleaseDC( aHDC );
+ }
+
+ throw uno::RuntimeException("could not get the DC to rSurface");
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/directx/dx_surfacegraphics.hxx b/canvas/source/directx/dx_surfacegraphics.hxx
new file mode 100644
index 0000000000..4260383e2e
--- /dev/null
+++ b/canvas/source/directx/dx_surfacegraphics.hxx
@@ -0,0 +1,36 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "dx_graphicsprovider.hxx"
+
+namespace dxcanvas
+{
+/** Container providing a Gdiplus::Graphics for a Surface
+
+ This wrapper class transparently handles allocation and
+ release of surface resources the RAII way (the
+ GraphicsSharedPtr returned has a deleter that does all the
+ necessary DX cleanup work).
+*/
+GraphicsSharedPtr createSurfaceGraphics(const sal::systools::COMReference<surface_type>& rSurface);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/directx/dx_textlayout.cxx b/canvas/source/directx/dx_textlayout.cxx
new file mode 100644
index 0000000000..e64dde5966
--- /dev/null
+++ b/canvas/source/directx/dx_textlayout.cxx
@@ -0,0 +1,253 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/numeric/ftools.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+#include "dx_bitmap.hxx"
+#include "dx_spritecanvas.hxx"
+#include "dx_textlayout.hxx"
+#include "dx_textlayout_drawhelper.hxx"
+
+using namespace ::com::sun::star;
+
+namespace dxcanvas
+{
+ TextLayout::TextLayout( const rendering::StringContext& aText,
+ sal_Int8 nDirection,
+ sal_Int64 /*nRandomSeed*/,
+ const CanvasFont::ImplRef& rFont ) :
+ TextLayout_Base( m_aMutex ),
+ maText( aText ),
+ maLogicalAdvancements(),
+ mpFont( rFont ),
+ mnTextDirection( nDirection )
+ {
+ }
+
+ TextLayout::~TextLayout()
+ {
+ }
+
+ void SAL_CALL TextLayout::disposing()
+ {
+ mpFont.clear();
+ }
+
+ // XTextLayout
+ uno::Sequence< uno::Reference< rendering::XPolyPolygon2D > > SAL_CALL TextLayout::queryTextShapes( )
+ {
+ // TODO
+ return uno::Sequence< uno::Reference< rendering::XPolyPolygon2D > >();
+ }
+
+ uno::Sequence< geometry::RealRectangle2D > SAL_CALL TextLayout::queryInkMeasures( )
+ {
+ // TODO
+ return uno::Sequence< geometry::RealRectangle2D >();
+ }
+
+ uno::Sequence< geometry::RealRectangle2D > SAL_CALL TextLayout::queryMeasures( )
+ {
+ // TODO
+ return uno::Sequence< geometry::RealRectangle2D >();
+ }
+
+ uno::Sequence< double > SAL_CALL TextLayout::queryLogicalAdvancements( )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ return maLogicalAdvancements;
+ }
+
+ void SAL_CALL TextLayout::applyLogicalAdvancements( const uno::Sequence< double >& aAdvancements )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ if( aAdvancements.getLength() != maText.Length )
+ {
+ SAL_WARN("canvas.directx", "TextLayout::applyLogicalAdvancements(): mismatching number of advancements" );
+ throw lang::IllegalArgumentException();
+ }
+
+ maLogicalAdvancements = aAdvancements;
+ }
+
+ uno::Sequence< sal_Bool > SAL_CALL TextLayout::queryKashidaPositions( )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ return maKashidaPositions;
+ }
+
+ void SAL_CALL TextLayout::applyKashidaPositions( const uno::Sequence< sal_Bool >& aPositions )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ if( aPositions.hasElements() && aPositions.getLength() != maText.Length )
+ {
+ SAL_WARN("canvas.directx", "TextLayout::applyKashidaPositions(): mismatching number of positions" );
+ throw lang::IllegalArgumentException("mismatching number of positions", getXWeak(), 1);
+ }
+
+ maKashidaPositions = aPositions;
+ }
+
+ geometry::RealRectangle2D SAL_CALL TextLayout::queryTextBounds( )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ uno::Reference< rendering::XGraphicDevice > xGraphicDevice;
+ ::dxcanvas::TextLayoutDrawHelper aDrawHelper(xGraphicDevice);
+
+ // render text
+ const geometry::RealRectangle2D aBounds(
+ aDrawHelper.queryTextBounds(
+ maText,
+ maLogicalAdvancements,
+ mpFont,
+ mpFont->getFontMatrix()));
+
+ return aBounds;
+ }
+
+ double SAL_CALL TextLayout::justify( double /*nSize*/ )
+ {
+ // TODO
+ return 0.0;
+ }
+
+ double SAL_CALL TextLayout::combinedJustify( const uno::Sequence< uno::Reference< rendering::XTextLayout > >& /*aNextLayouts*/,
+ double /*nSize*/ )
+ {
+ // TODO
+ return 0.0;
+ }
+
+ rendering::TextHit SAL_CALL TextLayout::getTextHit( const geometry::RealPoint2D& /*aHitPoint*/ )
+ {
+ // TODO
+ return rendering::TextHit();
+ }
+
+ rendering::Caret SAL_CALL TextLayout::getCaret( sal_Int32 /*nInsertionIndex*/,
+ sal_Bool /*bExcludeLigatures*/ )
+ {
+ // TODO
+ return rendering::Caret();
+ }
+
+ sal_Int32 SAL_CALL TextLayout::getNextInsertionIndex( sal_Int32 /*nStartIndex*/,
+ sal_Int32 /*nCaretAdvancement*/,
+ sal_Bool /*bExcludeLigatures*/ )
+ {
+ // TODO
+ return 0;
+ }
+
+ uno::Reference< rendering::XPolyPolygon2D > SAL_CALL TextLayout::queryVisualHighlighting( sal_Int32 /*nStartIndex*/,
+ sal_Int32 /*nEndIndex*/ )
+ {
+ // TODO
+ return uno::Reference< rendering::XPolyPolygon2D >();
+ }
+
+ uno::Reference< rendering::XPolyPolygon2D > SAL_CALL TextLayout::queryLogicalHighlighting( sal_Int32 /*nStartIndex*/,
+ sal_Int32 /*nEndIndex*/ )
+ {
+ // TODO
+ return uno::Reference< rendering::XPolyPolygon2D >();
+ }
+
+ double SAL_CALL TextLayout::getBaselineOffset( )
+ {
+ // TODO
+ return 0.0;
+ }
+
+ sal_Int8 SAL_CALL TextLayout::getMainTextDirection( )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ return mnTextDirection;
+ }
+
+ uno::Reference< rendering::XCanvasFont > SAL_CALL TextLayout::getFont( )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ return mpFont;
+ }
+
+ rendering::StringContext SAL_CALL TextLayout::getText( )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ return maText;
+ }
+
+ bool TextLayout::draw( const GraphicsSharedPtr& rGraphics,
+ const rendering::ViewState& rViewState,
+ const rendering::RenderState& rRenderState,
+ const ::basegfx::B2ISize& rOutputOffset,
+ const uno::Reference< rendering::XGraphicDevice >& xGraphicDevice,
+ bool bAlphaSurface ) const
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ ::dxcanvas::TextLayoutDrawHelper aDrawHelper(xGraphicDevice);
+
+ // render text
+ aDrawHelper.drawText(
+ rGraphics,
+ rViewState,
+ rRenderState,
+ rOutputOffset,
+ maText,
+ maLogicalAdvancements,
+ maKashidaPositions,
+ mpFont,
+ mpFont->getFontMatrix(),
+ bAlphaSurface,
+ mnTextDirection != 0);
+
+ return true;
+ }
+
+ OUString SAL_CALL TextLayout::getImplementationName()
+ {
+ return "DXCanvas::TextLayout";
+ }
+
+ sal_Bool SAL_CALL TextLayout::supportsService( const OUString& ServiceName )
+ {
+ return cppu::supportsService( this, ServiceName );
+ }
+
+ uno::Sequence< OUString > SAL_CALL TextLayout::getSupportedServiceNames()
+ {
+ return { "com.sun.star.rendering.TextLayout" };
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/directx/dx_textlayout.hxx b/canvas/source/directx/dx_textlayout.hxx
new file mode 100644
index 0000000000..f0ae523e7b
--- /dev/null
+++ b/canvas/source/directx/dx_textlayout.hxx
@@ -0,0 +1,107 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <cppuhelper/compbase.hxx>
+#include <cppuhelper/basemutex.hxx>
+
+#include <com/sun/star/lang/XServiceInfo.hpp>
+
+#include <com/sun/star/rendering/XTextLayout.hpp>
+
+#include <basegfx/vector/b2isize.hxx>
+
+#include "dx_canvasfont.hxx"
+#include "dx_ibitmap.hxx"
+#include "dx_winstuff.hxx"
+#include "dx_gdiplususer.hxx"
+
+
+/* Definition of TextLayout class */
+namespace dxcanvas
+{
+ typedef ::cppu::WeakComponentImplHelper< css::rendering::XTextLayout,
+ css::lang::XServiceInfo > TextLayout_Base;
+
+ class TextLayout : public ::cppu::BaseMutex,
+ public TextLayout_Base
+ {
+ public:
+ TextLayout( const css::rendering::StringContext& aText,
+ sal_Int8 nDirection,
+ sal_Int64 nRandomSeed,
+ const CanvasFont::ImplRef& rFont );
+ /// make noncopyable
+ TextLayout(const TextLayout&) = delete;
+ const TextLayout& operator=(const TextLayout&) = delete;
+
+ /// Dispose all internal references
+ virtual void SAL_CALL disposing() override;
+
+ // XTextLayout
+ virtual css::uno::Sequence< css::uno::Reference< css::rendering::XPolyPolygon2D > > SAL_CALL queryTextShapes( ) override;
+ virtual css::uno::Sequence< css::geometry::RealRectangle2D > SAL_CALL queryInkMeasures( ) override;
+ virtual css::uno::Sequence< css::geometry::RealRectangle2D > SAL_CALL queryMeasures( ) override;
+ virtual css::uno::Sequence< double > SAL_CALL queryLogicalAdvancements( ) override;
+ virtual void SAL_CALL applyLogicalAdvancements( const css::uno::Sequence< double >& aAdvancements ) override;
+ virtual css::uno::Sequence< sal_Bool > SAL_CALL queryKashidaPositions( ) override;
+ virtual void SAL_CALL applyKashidaPositions( const css::uno::Sequence< sal_Bool >& aPositions ) override;
+ virtual css::geometry::RealRectangle2D SAL_CALL queryTextBounds( ) override;
+ virtual double SAL_CALL justify( double nSize ) override;
+ virtual double SAL_CALL combinedJustify( const css::uno::Sequence< css::uno::Reference< css::rendering::XTextLayout > >& aNextLayouts, double nSize ) override;
+ virtual css::rendering::TextHit SAL_CALL getTextHit( const css::geometry::RealPoint2D& aHitPoint ) override;
+ virtual css::rendering::Caret SAL_CALL getCaret( sal_Int32 nInsertionIndex, sal_Bool bExcludeLigatures ) override;
+ virtual sal_Int32 SAL_CALL getNextInsertionIndex( sal_Int32 nStartIndex, sal_Int32 nCaretAdvancement, sal_Bool bExcludeLigatures ) override;
+ virtual css::uno::Reference< css::rendering::XPolyPolygon2D > SAL_CALL queryVisualHighlighting( sal_Int32 nStartIndex, sal_Int32 nEndIndex ) override;
+ virtual css::uno::Reference< css::rendering::XPolyPolygon2D > SAL_CALL queryLogicalHighlighting( sal_Int32 nStartIndex, sal_Int32 nEndIndex ) override;
+ virtual double SAL_CALL getBaselineOffset( ) override;
+ virtual sal_Int8 SAL_CALL getMainTextDirection( ) override;
+ virtual css::uno::Reference< css::rendering::XCanvasFont > SAL_CALL getFont( ) override;
+ virtual css::rendering::StringContext SAL_CALL getText( ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ bool draw( const GraphicsSharedPtr& rGraphics,
+ const css::rendering::ViewState& rViewState,
+ const css::rendering::RenderState& rRenderState,
+ const ::basegfx::B2ISize& rOutputOffset,
+ const css::uno::Reference<
+ css::rendering::XGraphicDevice >& xGraphicDevice,
+ bool bAlphaSurface ) const;
+
+ protected:
+ ~TextLayout() override; // we're a ref-counted UNO class. _We_ destroy ourselves.
+
+ private:
+ // NOTE: no need for GDIPlusUserSharedPtr, mpFont implicitly has one already
+
+ css::rendering::StringContext maText;
+ css::uno::Sequence< double > maLogicalAdvancements;
+ css::uno::Sequence< sal_Bool > maKashidaPositions;
+ CanvasFont::ImplRef mpFont;
+ sal_Int8 mnTextDirection;
+ };
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/directx/dx_textlayout_drawhelper.cxx b/canvas/source/directx/dx_textlayout_drawhelper.cxx
new file mode 100644
index 0000000000..3a01af375a
--- /dev/null
+++ b/canvas/source/directx/dx_textlayout_drawhelper.cxx
@@ -0,0 +1,317 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <memory>
+
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/polygon/b2dpolypolygon.hxx>
+#include <basegfx/utils/canvastools.hxx>
+#include <com/sun/star/rendering/FontRequest.hpp>
+#include <com/sun/star/rendering/PanoseProportion.hpp>
+#include <com/sun/star/rendering/XCanvasFont.hpp>
+#include <comphelper/scopeguard.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <rtl/math.hxx>
+#include <tools/color.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <tools/poly.hxx>
+#include <vcl/canvastools.hxx>
+#include <vcl/kernarray.hxx>
+#include <vcl/metric.hxx>
+#include <vcl/sysdata.hxx>
+#include <vcl/virdev.hxx>
+
+#include <canvas/canvastools.hxx>
+
+#include "dx_bitmap.hxx"
+#include "dx_canvasfont.hxx"
+#include "dx_impltools.hxx"
+#include "dx_textlayout_drawhelper.hxx"
+
+using namespace ::com::sun::star;
+
+
+namespace dxcanvas
+{
+ TextLayoutDrawHelper::TextLayoutDrawHelper(
+ const uno::Reference< rendering::XGraphicDevice >& xGraphicDevice ) :
+ mxGraphicDevice(xGraphicDevice)
+ {
+ }
+
+ TextLayoutDrawHelper::~TextLayoutDrawHelper()
+ {
+ }
+
+ void TextLayoutDrawHelper::drawText(
+ const std::shared_ptr<Gdiplus::Graphics>& rGraphics,
+ const css::rendering::ViewState& rViewState,
+ const css::rendering::RenderState& rRenderState,
+ const ::basegfx::B2ISize& rOutputOffset,
+ const css::rendering::StringContext& rText,
+ const css::uno::Sequence< double >& rLogicalAdvancements,
+ const css::uno::Sequence< sal_Bool >& rKashidaPositions,
+ const css::uno::Reference<
+ css::rendering::XCanvasFont >& rCanvasFont,
+ const css::geometry::Matrix2D& rFontMatrix,
+ bool bAlphaSurface,
+ bool bIsRTL)
+ {
+ HDC hdc = rGraphics->GetHDC();
+
+ // issue a ReleaseHDC() when leaving the scope
+ const ::comphelper::ScopeGuard aGuard(
+ [&rGraphics, &hdc]() mutable { rGraphics->ReleaseHDC(hdc); } );
+
+ SystemGraphicsData aSystemGraphicsData;
+ aSystemGraphicsData.nSize = sizeof(SystemGraphicsData);
+ aSystemGraphicsData.hDC = reinterpret_cast< ::HDC >(hdc);
+ ScopedVclPtrInstance<VirtualDevice> xVirtualDevice(aSystemGraphicsData, Size(1, 1), DeviceFormat::WITHOUT_ALPHA);
+
+ // disable font antialiasing - GDI does not handle alpha
+ // surfaces properly.
+ if( bAlphaSurface )
+ xVirtualDevice->SetAntialiasing(AntialiasingFlags::DisableText);
+
+ if(rText.Length)
+ {
+ bool test = mxGraphicDevice.is();
+ ENSURE_OR_THROW( test,
+ "TextLayoutDrawHelper::drawText(): Invalid GraphicDevice" );
+
+ // set text color. Make sure to remove transparence part first.
+ Color aColor( COL_WHITE );
+
+ if( rRenderState.DeviceColor.getLength() > 2 )
+ aColor = vcl::unotools::doubleSequenceToColor(
+ rRenderState.DeviceColor,
+ mxGraphicDevice->getDeviceColorSpace());
+ aColor.SetAlpha(255);
+ xVirtualDevice->SetTextColor(aColor);
+
+ // create the font
+ const css::rendering::FontRequest& rFontRequest = rCanvasFont->getFontRequest();
+ vcl::Font aFont(
+ rFontRequest.FontDescription.FamilyName,
+ rFontRequest.FontDescription.StyleName,
+ Size( 0, ::basegfx::fround(rFontRequest.CellSize)));
+
+ aFont.SetAlignment( ALIGN_BASELINE );
+ aFont.SetCharSet( (rFontRequest.FontDescription.IsSymbolFont==css::util::TriState_YES) ? RTL_TEXTENCODING_SYMBOL : RTL_TEXTENCODING_UNICODE );
+ aFont.SetVertical( rFontRequest.FontDescription.IsVertical==css::util::TriState_YES );
+ aFont.SetWeight( static_cast<FontWeight>(rFontRequest.FontDescription.FontDescription.Weight) );
+ aFont.SetItalic( (rFontRequest.FontDescription.FontDescription.Letterform<=8) ? ITALIC_NONE : ITALIC_NORMAL );
+ aFont.SetPitch(
+ rFontRequest.FontDescription.FontDescription.Proportion == rendering::PanoseProportion::MONO_SPACED
+ ? PITCH_FIXED : PITCH_VARIABLE);
+
+ aFont.SetLanguage(LanguageTag::convertToLanguageType(rFontRequest.Locale));
+
+ // setup font color
+ aFont.SetColor( aColor );
+ aFont.SetFillColor( aColor );
+
+ CanvasFont::ImplRef pFont(tools::canvasFontFromXFont(rCanvasFont));
+ if (pFont.is() && pFont->getEmphasisMark())
+ aFont.SetEmphasisMark(FontEmphasisMark(pFont->getEmphasisMark()));
+
+ // adjust to stretched font
+ if(!::rtl::math::approxEqual(rFontMatrix.m00, rFontMatrix.m11))
+ {
+ const Size aSize = xVirtualDevice->GetFontMetric( aFont ).GetFontSize();
+ const double fDividend( rFontMatrix.m10 + rFontMatrix.m11 );
+ double fStretch = rFontMatrix.m00 + rFontMatrix.m01;
+
+ if( !::basegfx::fTools::equalZero( fDividend) )
+ fStretch /= fDividend;
+
+ const sal_Int32 nNewWidth = ::basegfx::fround( aSize.Width() * fStretch );
+
+ aFont.SetAverageFontWidth( nNewWidth );
+ }
+
+ // set font
+ xVirtualDevice->SetFont(aFont);
+
+ // create world transformation matrix
+ ::basegfx::B2DHomMatrix aWorldTransform;
+ ::canvas::tools::mergeViewAndRenderTransform(aWorldTransform, rViewState, rRenderState);
+
+ if(!rOutputOffset.equalZero())
+ {
+ aWorldTransform.translate(rOutputOffset.getWidth(), rOutputOffset.getHeight());
+ }
+
+ // set ViewState clipping
+ if(rViewState.Clip.is())
+ {
+ ::basegfx::B2DPolyPolygon aClipPoly(dxcanvas::tools::polyPolygonFromXPolyPolygon2D(rViewState.Clip));
+ ::basegfx::B2DHomMatrix aMatrix;
+ ::basegfx::unotools::homMatrixFromAffineMatrix(aMatrix, rViewState.AffineTransform );
+
+ if(!rOutputOffset.equalZero())
+ {
+ aMatrix.translate(rOutputOffset.getWidth(), rOutputOffset.getHeight());
+ }
+
+ aClipPoly.transform(aMatrix);
+ const vcl::Region& rClipRegion = vcl::Region(::tools::PolyPolygon(aClipPoly));
+ xVirtualDevice->IntersectClipRegion(rClipRegion);
+ }
+
+ if(rRenderState.Clip.is())
+ {
+ ::basegfx::B2DPolyPolygon aClipPoly(dxcanvas::tools::polyPolygonFromXPolyPolygon2D(rRenderState.Clip));
+ aClipPoly.transform(aWorldTransform);
+ const vcl::Region& rClipRegion = vcl::Region(::tools::PolyPolygon(aClipPoly));
+ xVirtualDevice->IntersectClipRegion(rClipRegion);
+ }
+
+ // set world transform
+ XFORM aXForm;
+ aXForm.eM11 = static_cast<FLOAT>(aWorldTransform.get(0, 0));
+ aXForm.eM12 = static_cast<FLOAT>(aWorldTransform.get(1, 0));
+ aXForm.eM21 = static_cast<FLOAT>(aWorldTransform.get(0, 1));
+ aXForm.eM22 = static_cast<FLOAT>(aWorldTransform.get(1, 1));
+ aXForm.eDx = static_cast<FLOAT>(aWorldTransform.get(0, 2));
+ aXForm.eDy = static_cast<FLOAT>(aWorldTransform.get(1, 2));
+
+ // TODO(F3): This is NOT supported on 95/98/ME!
+ SetGraphicsMode(hdc, GM_ADVANCED);
+ SetTextAlign(hdc, TA_BASELINE);
+ SetWorldTransform(hdc, &aXForm);
+
+ // use an empty StartPosition for text rendering
+ const Point aEmptyPoint(0, 0);
+
+ // create the String
+ const OUString aText(rText.Text);
+
+ if( rLogicalAdvancements.getLength() )
+ {
+ // create the DXArray
+ const sal_Int32 nLen( rLogicalAdvancements.getLength() );
+ KernArray DXArray;
+ DXArray.reserve(nLen);
+ for( sal_Int32 i=0; i<nLen; ++i )
+ DXArray.push_back(basegfx::fround(rLogicalAdvancements[i]));
+
+ std::span<const sal_Bool> aKashidaArray(rKashidaPositions.getConstArray(), rKashidaPositions.getLength());
+
+ // draw the String
+ xVirtualDevice->DrawTextArray( aEmptyPoint,
+ aText,
+ DXArray,
+ aKashidaArray,
+ rText.StartPosition,
+ rText.Length,
+ bIsRTL ? SalLayoutFlags::BiDiRtl : SalLayoutFlags::NONE);
+ }
+ else
+ {
+ // draw the String
+ xVirtualDevice->DrawText( aEmptyPoint,
+ aText,
+ rText.StartPosition,
+ rText.Length );
+ }
+ }
+ }
+
+ geometry::RealRectangle2D TextLayoutDrawHelper::queryTextBounds( const rendering::StringContext& rText,
+ const uno::Sequence< double >& rLogicalAdvancements,
+ const uno::Reference< rendering::XCanvasFont >& rCanvasFont,
+ const geometry::Matrix2D& rFontMatrix )
+ {
+ if(!(rText.Length))
+ return geometry::RealRectangle2D();
+
+ // TODO(F1): Fetching default screen DC here, will yield wrong
+ // metrics when e.g. formatting for a printer!
+ SystemGraphicsData aSystemGraphicsData;
+ aSystemGraphicsData.nSize = sizeof(SystemGraphicsData);
+ aSystemGraphicsData.hDC = reinterpret_cast< ::HDC >(GetDC( nullptr ));
+ ScopedVclPtrInstance<VirtualDevice> xVirtualDevice(aSystemGraphicsData, Size(1, 1), DeviceFormat::WITHOUT_ALPHA);
+
+ // create the font
+ const css::rendering::FontRequest& rFontRequest = rCanvasFont->getFontRequest();
+ vcl::Font aFont(
+ rFontRequest.FontDescription.FamilyName,
+ rFontRequest.FontDescription.StyleName,
+ Size( 0, ::basegfx::fround(rFontRequest.CellSize)));
+
+ aFont.SetAlignment( ALIGN_BASELINE );
+ aFont.SetCharSet( (rFontRequest.FontDescription.IsSymbolFont==css::util::TriState_YES) ? RTL_TEXTENCODING_SYMBOL : RTL_TEXTENCODING_UNICODE );
+ aFont.SetVertical( rFontRequest.FontDescription.IsVertical==css::util::TriState_YES );
+ aFont.SetWeight( static_cast<FontWeight>(rFontRequest.FontDescription.FontDescription.Weight) );
+ aFont.SetItalic( (rFontRequest.FontDescription.FontDescription.Letterform<=8) ? ITALIC_NONE : ITALIC_NORMAL );
+ aFont.SetPitch(
+ rFontRequest.FontDescription.FontDescription.Proportion == rendering::PanoseProportion::MONO_SPACED
+ ? PITCH_FIXED : PITCH_VARIABLE);
+
+ // adjust to stretched font
+ if(!::rtl::math::approxEqual(rFontMatrix.m00, rFontMatrix.m11))
+ {
+ const Size aSize = xVirtualDevice->GetFontMetric( aFont ).GetFontSize();
+ const double fDividend( rFontMatrix.m10 + rFontMatrix.m11 );
+ double fStretch = rFontMatrix.m00 + rFontMatrix.m01;
+
+ if( !::basegfx::fTools::equalZero( fDividend) )
+ fStretch /= fDividend;
+
+ const sal_Int32 nNewWidth = ::basegfx::fround( aSize.Width() * fStretch );
+
+ aFont.SetAverageFontWidth( nNewWidth );
+ }
+
+ CanvasFont::ImplRef pFont(tools::canvasFontFromXFont(rCanvasFont));
+ if (pFont.is() && pFont->getEmphasisMark())
+ aFont.SetEmphasisMark(FontEmphasisMark(pFont->getEmphasisMark()));
+
+ // set font
+ xVirtualDevice->SetFont(aFont);
+
+ // need metrics for Y offset, the XCanvas always renders
+ // relative to baseline
+ const ::FontMetric& aMetric( xVirtualDevice->GetFontMetric() );
+
+ const sal_Int32 nAboveBaseline( -aMetric.GetInternalLeading() - aMetric.GetAscent() );
+ const sal_Int32 nBelowBaseline( aMetric.GetDescent() );
+
+ if( rLogicalAdvancements.getLength() )
+ {
+ return geometry::RealRectangle2D( 0, nAboveBaseline,
+ rLogicalAdvancements[ rLogicalAdvancements.getLength()-1 ],
+ nBelowBaseline );
+ }
+ else
+ {
+ return geometry::RealRectangle2D( 0, nAboveBaseline,
+ xVirtualDevice->GetTextWidth(
+ rText.Text,
+ ::canvas::tools::numeric_cast<sal_uInt16>(rText.StartPosition),
+ ::canvas::tools::numeric_cast<sal_uInt16>(rText.Length) ),
+ nBelowBaseline );
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/directx/dx_textlayout_drawhelper.hxx b/canvas/source/directx/dx_textlayout_drawhelper.hxx
new file mode 100644
index 0000000000..8e9383a8ae
--- /dev/null
+++ b/canvas/source/directx/dx_textlayout_drawhelper.hxx
@@ -0,0 +1,78 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <memory>
+
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/rendering/StringContext.hpp>
+#include <com/sun/star/rendering/XCanvasFont.hpp>
+#include <com/sun/star/geometry/Matrix2D.hpp>
+#include <com/sun/star/rendering/XGraphicDevice.hpp>
+#include <com/sun/star/rendering/ViewState.hpp>
+#include <com/sun/star/rendering/RenderState.hpp>
+
+#include <basegfx/vector/b2isize.hxx>
+
+namespace com::sun::star::rendering { class XCanvasFont; }
+
+namespace Gdiplus { class Graphics; }
+
+namespace dxcanvas
+{
+ struct Bitmap;
+ class TextLayoutDrawHelper
+ {
+ public:
+ explicit TextLayoutDrawHelper(
+ const css::uno::Reference< css::rendering::XGraphicDevice >& xGraphicDevice);
+ ~TextLayoutDrawHelper();
+
+ // draw text
+ void drawText( const std::shared_ptr<Gdiplus::Graphics>& rGraphics,
+ const css::rendering::ViewState& rViewState,
+ const css::rendering::RenderState& rRenderState,
+ const ::basegfx::B2ISize& rOutputOffset,
+ const css::rendering::StringContext& rText,
+ const css::uno::Sequence< double >& rLogicalAdvancements,
+ const css::uno::Sequence< sal_Bool >& rKashidaPositions,
+ const css::uno::Reference<
+ css::rendering::XCanvasFont >& rCanvasFont,
+ const css::geometry::Matrix2D& rFontMatrix,
+ bool bAlphaSurface,
+ bool bIsRTL);
+
+ css::geometry::RealRectangle2D queryTextBounds(
+ const css::rendering::StringContext& rText,
+ const css::uno::Sequence< double >& rLogicalAdvancements,
+ const css::uno::Reference<
+ css::rendering::XCanvasFont >& rCanvasFont,
+ const css::geometry::Matrix2D& rFontMatrix );
+
+#ifdef DBG_UTIL
+ void test();
+#endif
+
+ protected:
+ css::uno::Reference< css::rendering::XGraphicDevice > mxGraphicDevice;
+ };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/directx/dx_vcltools.cxx b/canvas/source/directx/dx_vcltools.cxx
new file mode 100644
index 0000000000..31b05be58e
--- /dev/null
+++ b/canvas/source/directx/dx_vcltools.cxx
@@ -0,0 +1,307 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <basegfx/numeric/ftools.hxx>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/rendering/XIntegerBitmap.hpp>
+#include <comphelper/diagnose_ex.hxx>
+#include <vcl/bitmap.hxx>
+#include <vcl/bitmapex.hxx>
+#include <vcl/BitmapReadAccess.hxx>
+#include <vcl/canvastools.hxx>
+
+#include "dx_impltools.hxx"
+#include "dx_vcltools.hxx"
+
+using namespace ::com::sun::star;
+
+namespace dxcanvas::tools
+{
+ namespace
+ {
+ /// Calc number of colors in given BitmapInfoHeader
+ sal_Int32 calcDIBColorCount( const BITMAPINFOHEADER& rBIH )
+ {
+ if( rBIH.biSize != sizeof( BITMAPCOREHEADER ) )
+ {
+ if( rBIH.biBitCount <= 8 )
+ {
+ if( rBIH.biClrUsed )
+ return rBIH.biClrUsed;
+ else
+ return 1 << rBIH.biBitCount;
+ }
+ }
+ else
+ {
+ BITMAPCOREHEADER const * pCoreHeader = reinterpret_cast<BITMAPCOREHEADER const *>(&rBIH);
+
+ if( pCoreHeader->bcBitCount <= 8 )
+ return 1 << pCoreHeader->bcBitCount;
+ }
+
+ return 0; // nothing known
+ }
+
+ /// Draw DI bits to given Graphics
+ bool drawDIBits( const std::shared_ptr< Gdiplus::Graphics >& rGraphics,
+ const void* hDIB )
+ {
+ bool bRet( false );
+
+ const BITMAPINFO* pBI = static_cast<BITMAPINFO*>(GlobalLock( const_cast<void *>(hDIB) ));
+
+ if( pBI )
+ {
+ const BYTE* pBits = reinterpret_cast<BYTE const *>(pBI) + pBI->bmiHeader.biSize +
+ calcDIBColorCount( pBI->bmiHeader ) * sizeof( RGBQUAD );
+
+ // forward to outsourced GDI+ rendering method
+ // (header clashes)
+ bRet = tools::drawDIBits( rGraphics, *pBI, pBits );
+
+ GlobalUnlock( const_cast<void *>(hDIB) );
+ }
+
+ return bRet;
+ }
+
+ /** Draw VCL bitmap to given Graphics
+
+ @param rBmp
+ Reference to bitmap. Might get modified, in such a way
+ that it will hold a DIB after a successful function call.
+ */
+ bool drawVCLBitmap( const std::shared_ptr< Gdiplus::Graphics >& rGraphics,
+ ::Bitmap& rBmp )
+ {
+ BitmapSystemData aBmpSysData;
+
+ if( !rBmp.GetSystemData( aBmpSysData ) ||
+ !aBmpSysData.pDIB )
+ {
+ // first of all, ensure that Bitmap contains a DIB, by
+ // acquiring a read access
+ BitmapScopedReadAccess pReadAcc(rBmp);
+
+ // TODO(P2): Acquiring a read access can actually
+ // force a read from VRAM, thus, avoiding this
+ // step somehow will increase performance
+ // here.
+ if( pReadAcc )
+ {
+ // try again: now, WinSalBitmap must have
+ // generated a DIB
+ if( rBmp.GetSystemData( aBmpSysData ) &&
+ aBmpSysData.pDIB )
+ {
+ return drawDIBits( rGraphics,
+ aBmpSysData.pDIB );
+ }
+ }
+ }
+ else
+ {
+ return drawDIBits( rGraphics,
+ aBmpSysData.pDIB );
+ }
+
+ // failed to generate DIBits from vcl bitmap
+ return false;
+ }
+
+ /** Create a chunk of raw RGBA data GDI+ Bitmap from VCL BitmapEx
+ */
+ RawRGBABitmap bitmapFromVCLBitmapEx( const ::BitmapEx& rBmpEx )
+ {
+ // TODO(P2): Avoid temporary bitmap generation, maybe
+ // even ensure that created DIBs are copied back to
+ // BmpEx (currently, every AcquireReadAccess() will
+ // make the local bitmap copy unique, effectively
+ // duplicating the memory used)
+
+ ENSURE_OR_THROW( rBmpEx.IsAlpha(),
+ "::dxcanvas::tools::bitmapFromVCLBitmapEx(): "
+ "BmpEx has no alpha" );
+
+ // convert transparent bitmap to 32bit RGBA
+ // ========================================
+
+ const ::Size aBmpSize( rBmpEx.GetSizePixel() );
+
+ RawRGBABitmap aBmpData;
+ aBmpData.mnWidth = aBmpSize.Width();
+ aBmpData.mnHeight = aBmpSize.Height();
+ aBmpData.maBitmapData.resize(4*aBmpData.mnWidth*aBmpData.mnHeight);
+
+ Bitmap aBitmap( rBmpEx.GetBitmap() );
+
+ BitmapScopedReadAccess pReadAccess( aBitmap );
+
+ const sal_Int32 nWidth( aBmpSize.Width() );
+ const sal_Int32 nHeight( aBmpSize.Height() );
+
+ ENSURE_OR_THROW( pReadAccess.get() != nullptr,
+ "::dxcanvas::tools::bitmapFromVCLBitmapEx(): "
+ "Unable to acquire read access to bitmap" );
+
+ Bitmap aAlpha( rBmpEx.GetAlphaMask().GetBitmap() );
+
+ BitmapScopedReadAccess pAlphaReadAccess( aAlpha );
+
+ // By convention, the access buffer always has
+ // one of the following formats:
+
+ // ScanlineFormat::N1BitMsbPal
+ // ScanlineFormat::N8BitPal
+ // ScanlineFormat::N24BitTcBgr
+ // ScanlineFormat::N32BitTcMask
+
+ // and is always ScanlineFormat::BottomUp
+
+ // This is the way
+ // WinSalBitmap::AcquireBuffer() sets up the
+ // buffer
+
+ ENSURE_OR_THROW( pAlphaReadAccess.get() != nullptr,
+ "::dxcanvas::tools::bitmapFromVCLBitmapEx(): "
+ "Unable to acquire read access to alpha" );
+
+ ENSURE_OR_THROW( pAlphaReadAccess->GetScanlineFormat() == ScanlineFormat::N8BitPal,
+ "::dxcanvas::tools::bitmapFromVCLBitmapEx(): "
+ "Unsupported alpha scanline format" );
+
+ BitmapColor aCol;
+ sal_uInt8* pCurrOutput(aBmpData.maBitmapData.data());
+ int x, y;
+
+ for( y=0; y<nHeight; ++y )
+ {
+ switch( pReadAccess->GetScanlineFormat() )
+ {
+ case ScanlineFormat::N8BitPal:
+ {
+ Scanline pScan = pReadAccess->GetScanline( y );
+ Scanline pAScan = pAlphaReadAccess->GetScanline( y );
+
+ for( x=0; x<nWidth; ++x )
+ {
+ aCol = pReadAccess->GetPaletteColor( *pScan++ );
+
+ *pCurrOutput++ = aCol.GetBlue();
+ *pCurrOutput++ = aCol.GetGreen();
+ *pCurrOutput++ = aCol.GetRed();
+ *pCurrOutput++ = static_cast<BYTE>(*pAScan++);
+ }
+ }
+ break;
+
+ case ScanlineFormat::N24BitTcBgr:
+ {
+ Scanline pScan = pReadAccess->GetScanline( y );
+ Scanline pAScan = pAlphaReadAccess->GetScanline( y );
+
+ for( x=0; x<nWidth; ++x )
+ {
+ // store as RGBA
+ *pCurrOutput++ = *pScan++;
+ *pCurrOutput++ = *pScan++;
+ *pCurrOutput++ = *pScan++;
+ *pCurrOutput++ = static_cast<BYTE>(*pAScan++);
+ }
+ }
+ break;
+
+ // TODO(P2): Might be advantageous
+ // to hand-formulate the following
+ // formats, too.
+ case ScanlineFormat::N1BitMsbPal:
+ case ScanlineFormat::N32BitTcMask:
+ {
+ Scanline pAScan = pAlphaReadAccess->GetScanline( y );
+
+ // using fallback for those
+ // seldom formats
+ for( x=0; x<nWidth; ++x )
+ {
+ // yes. x and y are swapped on Get/SetPixel
+ aCol = pReadAccess->GetColor(y,x);
+
+ *pCurrOutput++ = aCol.GetBlue();
+ *pCurrOutput++ = aCol.GetGreen();
+ *pCurrOutput++ = aCol.GetRed();
+ *pCurrOutput++ = static_cast<BYTE>(*pAScan++);
+ }
+ }
+ break;
+
+ case ScanlineFormat::N24BitTcRgb:
+ case ScanlineFormat::N32BitTcAbgr:
+ case ScanlineFormat::N32BitTcArgb:
+ case ScanlineFormat::N32BitTcBgra:
+ case ScanlineFormat::N32BitTcRgba:
+ default:
+ ENSURE_OR_THROW( false,
+ "::dxcanvas::tools::bitmapFromVCLBitmapEx(): "
+ "Unexpected scanline format - has "
+ "WinSalBitmap::AcquireBuffer() changed?" );
+ }
+ }
+
+ return aBmpData;
+ }
+
+ bool drawVCLBitmapEx( const std::shared_ptr< Gdiplus::Graphics >& rGraphics,
+ const ::BitmapEx& rBmpEx )
+ {
+ if( !rBmpEx.IsAlpha() )
+ {
+ Bitmap aBmp( rBmpEx.GetBitmap() );
+ return drawVCLBitmap( rGraphics, aBmp );
+ }
+ else
+ {
+ return drawRGBABits( rGraphics,
+ bitmapFromVCLBitmapEx( rBmpEx ) );
+ }
+ }
+ }
+
+ bool drawVCLBitmapFromXBitmap( const std::shared_ptr< Gdiplus::Graphics >& rGraphics,
+ const uno::Reference< rendering::XBitmap >& xBitmap )
+ {
+ // TODO(F2): add support for floating point bitmap formats
+ uno::Reference< rendering::XIntegerReadOnlyBitmap > xIntBmp(
+ xBitmap, uno::UNO_QUERY );
+
+ if( !xIntBmp.is() )
+ return false;
+
+ ::BitmapEx aBmpEx = vcl::unotools::bitmapExFromXBitmap( xIntBmp );
+ if( aBmpEx.IsEmpty() )
+ return false;
+
+ return drawVCLBitmapEx( rGraphics, aBmpEx );
+ }
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/directx/dx_vcltools.hxx b/canvas/source/directx/dx_vcltools.hxx
new file mode 100644
index 0000000000..433afa618e
--- /dev/null
+++ b/canvas/source/directx/dx_vcltools.hxx
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/rendering/XBitmap.hpp>
+#include <memory>
+#include <vector>
+
+namespace Gdiplus { class Graphics; }
+
+namespace dxcanvas::tools
+{
+ /** Raw RGBA bitmap data,
+ contiguous in memory
+ */
+ struct RawRGBABitmap
+ {
+ sal_Int32 mnWidth;
+ sal_Int32 mnHeight;
+ std::vector<sal_uInt8> maBitmapData;
+ };
+
+ bool drawVCLBitmapFromXBitmap( const std::shared_ptr< Gdiplus::Graphics >& rGraphics,
+ const css::uno::Reference<
+ css::rendering::XBitmap >& xBitmap );
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/directx/dx_winstuff.hxx b/canvas/source/directx/dx_winstuff.hxx
new file mode 100644
index 0000000000..4cd0007fa9
--- /dev/null
+++ b/canvas/source/directx/dx_winstuff.hxx
@@ -0,0 +1,71 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <algorithm>
+#include <memory>
+
+#include <basegfx/numeric/ftools.hxx>
+
+#include <prewin.h>
+
+// Enabling Direct3D Debug Information Further more, with registry key
+// \\HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Direct3D\D3D9Debugging\\EnableCreationStack
+// set to 1, sets a backtrace each time an object is created to the
+// following global variable: LPCWSTR CreationCallStack
+#if OSL_DEBUG_LEVEL > 0
+# define D3D_DEBUG_INFO
+#endif
+
+#include <d3d9.h>
+
+typedef IDirect3DSurface9 surface_type;
+
+
+#ifndef max
+#define max(a,b) (((a) > (b)) ? (a) : (b))
+#endif
+#ifndef min
+#define min(a,b) (((a) < (b)) ? (a) : (b))
+#endif
+
+#include <gdiplus.h>
+
+#undef max
+#undef min
+
+
+namespace dxcanvas
+{
+ // some shared pointer typedefs to Gdiplus objects
+ typedef std::shared_ptr< Gdiplus::Graphics > GraphicsSharedPtr;
+ typedef std::shared_ptr< Gdiplus::GraphicsPath > GraphicsPathSharedPtr;
+ typedef std::shared_ptr< Gdiplus::Bitmap > BitmapSharedPtr;
+ typedef std::shared_ptr< Gdiplus::Font > FontSharedPtr;
+ typedef std::shared_ptr< Gdiplus::TextureBrush > TextureBrushSharedPtr;
+}
+
+#include <systools/win32/comtools.hxx> // for COMReference; must be inside prewin...postwin
+// Attention! All DirectX factory methods return the created interfaces pre-acquired, into a raw
+// pointer. Do not call AddRef on them when constructing COMReference!
+
+#include <postwin.h>
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/directx/gdipluscanvas.component b/canvas/source/directx/gdipluscanvas.component
new file mode 100644
index 0000000000..0ee68a1ad7
--- /dev/null
+++ b/canvas/source/directx/gdipluscanvas.component
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ -->
+
+<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@"
+ xmlns="http://openoffice.org/2010/uno-components">
+ <implementation name="com.sun.star.comp.rendering.BitmapCanvas.GDI+"
+ constructor="canvas_gdiplus_BitmapCanvas_get_implementation">
+ <service name="com.sun.star.rendering.BitmapCanvas.GDI+"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.rendering.Canvas.GDI+"
+ constructor="canvas_gdiplus_Canvas_get_implementation">
+ <service name="com.sun.star.rendering.Canvas.GDI+"/>
+ </implementation>
+</component>