summaryrefslogtreecommitdiffstats
path: root/canvas/source
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
commited5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch)
tree7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /canvas/source
parentInitial commit. (diff)
downloadlibreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.tar.xz
libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.zip
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--canvas/source/cairo/cairo_cachedbitmap.cxx79
-rw-r--r--canvas/source/cairo/cairo_cachedbitmap.hxx58
-rw-r--r--canvas/source/cairo/cairo_canvas.cxx191
-rw-r--r--canvas/source/cairo/cairo_canvas.hxx142
-rw-r--r--canvas/source/cairo/cairo_canvasbitmap.cxx163
-rw-r--r--canvas/source/cairo/cairo_canvasbitmap.hxx126
-rw-r--r--canvas/source/cairo/cairo_canvascustomsprite.cxx151
-rw-r--r--canvas/source/cairo/cairo_canvascustomsprite.hxx145
-rw-r--r--canvas/source/cairo/cairo_canvasfont.cxx156
-rw-r--r--canvas/source/cairo/cairo_canvasfont.hxx83
-rw-r--r--canvas/source/cairo/cairo_canvashelper.cxx2047
-rw-r--r--canvas/source/cairo/cairo_canvashelper.hxx271
-rw-r--r--canvas/source/cairo/cairo_canvashelper_text.cxx300
-rw-r--r--canvas/source/cairo/cairo_devicehelper.cxx257
-rw-r--r--canvas/source/cairo/cairo_devicehelper.hxx122
-rw-r--r--canvas/source/cairo/cairo_repainttarget.hxx48
-rw-r--r--canvas/source/cairo/cairo_sprite.hxx65
-rw-r--r--canvas/source/cairo/cairo_spritecanvas.cxx233
-rw-r--r--canvas/source/cairo/cairo_spritecanvas.hxx159
-rw-r--r--canvas/source/cairo/cairo_spritecanvashelper.cxx518
-rw-r--r--canvas/source/cairo/cairo_spritecanvashelper.hxx140
-rw-r--r--canvas/source/cairo/cairo_spritedevicehelper.cxx153
-rw-r--r--canvas/source/cairo/cairo_spritedevicehelper.hxx77
-rw-r--r--canvas/source/cairo/cairo_spritehelper.cxx195
-rw-r--r--canvas/source/cairo/cairo_spritehelper.hxx102
-rw-r--r--canvas/source/cairo/cairo_surfaceprovider.hxx70
-rw-r--r--canvas/source/cairo/cairo_textlayout.cxx354
-rw-r--r--canvas/source/cairo/cairo_textlayout.hxx106
-rw-r--r--canvas/source/cairo/cairocanvas.component30
-rw-r--r--canvas/source/directx/directx9canvas.component26
-rw-r--r--canvas/source/directx/dx_9rm.cxx1213
-rw-r--r--canvas/source/directx/dx_bitmap.cxx204
-rw-r--r--canvas/source/directx/dx_bitmap.hxx84
-rw-r--r--canvas/source/directx/dx_bitmapcanvashelper.cxx222
-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.cxx258
-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.cxx210
-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.hxx61
-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.cxx193
-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.cxx232
-rw-r--r--canvas/source/directx/dx_textlayout.hxx104
-rw-r--r--canvas/source/directx/dx_textlayout_drawhelper.cxx311
-rw-r--r--canvas/source/directx/dx_textlayout_drawhelper.hxx77
-rw-r--r--canvas/source/directx/dx_vcltools.cxx322
-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
-rw-r--r--canvas/source/factory/canvasfactory.component26
-rw-r--r--canvas/source/factory/cf_service.cxx465
-rw-r--r--canvas/source/opengl/ogl_bitmapcanvashelper.cxx65
-rw-r--r--canvas/source/opengl/ogl_bitmapcanvashelper.hxx71
-rw-r--r--canvas/source/opengl/ogl_buffercontext.hxx35
-rw-r--r--canvas/source/opengl/ogl_canvasbitmap.cxx54
-rw-r--r--canvas/source/opengl/ogl_canvasbitmap.hxx72
-rw-r--r--canvas/source/opengl/ogl_canvascustomsprite.cxx257
-rw-r--r--canvas/source/opengl/ogl_canvascustomsprite.hxx92
-rw-r--r--canvas/source/opengl/ogl_canvasfont.cxx70
-rw-r--r--canvas/source/opengl/ogl_canvasfont.hxx61
-rw-r--r--canvas/source/opengl/ogl_canvashelper.cxx947
-rw-r--r--canvas/source/opengl/ogl_canvashelper.hxx219
-rw-r--r--canvas/source/opengl/ogl_canvastools.cxx148
-rw-r--r--canvas/source/opengl/ogl_canvastools.hxx37
-rw-r--r--canvas/source/opengl/ogl_spritecanvas.cxx167
-rw-r--r--canvas/source/opengl/ogl_spritecanvas.hxx114
-rw-r--r--canvas/source/opengl/ogl_spritedevicehelper.cxx549
-rw-r--r--canvas/source/opengl/ogl_spritedevicehelper.hxx135
-rw-r--r--canvas/source/opengl/ogl_textlayout.cxx180
-rw-r--r--canvas/source/opengl/ogl_textlayout.hxx70
-rw-r--r--canvas/source/opengl/ogl_texturecache.cxx117
-rw-r--r--canvas/source/opengl/ogl_texturecache.hxx60
-rw-r--r--canvas/source/opengl/ogl_tools.hxx27
-rw-r--r--canvas/source/opengl/oglcanvas.component17
-rw-r--r--canvas/source/simplecanvas/simplecanvas.component26
-rw-r--r--canvas/source/simplecanvas/simplecanvasimpl.cxx369
-rw-r--r--canvas/source/tools/cachedprimitivebase.cxx96
-rw-r--r--canvas/source/tools/canvascustomspritehelper.cxx441
-rw-r--r--canvas/source/tools/canvastools.cxx1308
-rw-r--r--canvas/source/tools/elapsedtime.cxx130
-rw-r--r--canvas/source/tools/page.cxx135
-rw-r--r--canvas/source/tools/page.hxx147
-rw-r--r--canvas/source/tools/pagemanager.cxx152
-rw-r--r--canvas/source/tools/pagemanager.hxx76
-rw-r--r--canvas/source/tools/parametricpolypolygon.cxx238
-rw-r--r--canvas/source/tools/propertysethelper.cxx156
-rw-r--r--canvas/source/tools/spriteredrawmanager.cxx483
-rw-r--r--canvas/source/tools/surface.cxx437
-rw-r--r--canvas/source/tools/surface.hxx143
-rw-r--r--canvas/source/tools/surfaceproxy.cxx141
-rw-r--r--canvas/source/tools/surfaceproxy.hxx118
-rw-r--r--canvas/source/tools/surfaceproxymanager.cxx73
-rw-r--r--canvas/source/tools/surfacerect.hxx90
-rw-r--r--canvas/source/tools/verifyinput.cxx709
-rw-r--r--canvas/source/vcl/backbuffer.cxx59
-rw-r--r--canvas/source/vcl/backbuffer.hxx50
-rw-r--r--canvas/source/vcl/bitmapbackbuffer.cxx147
-rw-r--r--canvas/source/vcl/bitmapbackbuffer.hxx93
-rw-r--r--canvas/source/vcl/cachedbitmap.cxx93
-rw-r--r--canvas/source/vcl/cachedbitmap.hxx66
-rw-r--r--canvas/source/vcl/canvas.cxx122
-rw-r--r--canvas/source/vcl/canvas.hxx112
-rw-r--r--canvas/source/vcl/canvasbitmap.cxx122
-rw-r--r--canvas/source/vcl/canvasbitmap.hxx117
-rw-r--r--canvas/source/vcl/canvasbitmaphelper.cxx176
-rw-r--r--canvas/source/vcl/canvasbitmaphelper.hxx106
-rw-r--r--canvas/source/vcl/canvascustomsprite.cxx158
-rw-r--r--canvas/source/vcl/canvascustomsprite.hxx116
-rw-r--r--canvas/source/vcl/canvasfont.cxx177
-rw-r--r--canvas/source/vcl/canvasfont.hxx87
-rw-r--r--canvas/source/vcl/canvashelper.cxx1184
-rw-r--r--canvas/source/vcl/canvashelper.hxx309
-rw-r--r--canvas/source/vcl/canvashelper_texturefill.cxx1088
-rw-r--r--canvas/source/vcl/devicehelper.cxx217
-rw-r--r--canvas/source/vcl/devicehelper.hxx86
-rw-r--r--canvas/source/vcl/impltools.cxx239
-rw-r--r--canvas/source/vcl/impltools.hxx178
-rw-r--r--canvas/source/vcl/outdevholder.hxx50
-rw-r--r--canvas/source/vcl/outdevprovider.hxx50
-rw-r--r--canvas/source/vcl/repainttarget.hxx52
-rw-r--r--canvas/source/vcl/sprite.hxx65
-rw-r--r--canvas/source/vcl/spritecanvas.cxx187
-rw-r--r--canvas/source/vcl/spritecanvas.hxx163
-rw-r--r--canvas/source/vcl/spritecanvashelper.cxx661
-rw-r--r--canvas/source/vcl/spritecanvashelper.hxx169
-rw-r--r--canvas/source/vcl/spritedevicehelper.cxx119
-rw-r--r--canvas/source/vcl/spritedevicehelper.hxx60
-rw-r--r--canvas/source/vcl/spritehelper.cxx231
-rw-r--r--canvas/source/vcl/spritehelper.hxx108
-rw-r--r--canvas/source/vcl/textlayout.cxx428
-rw-r--r--canvas/source/vcl/textlayout.hxx103
-rw-r--r--canvas/source/vcl/vclcanvas.component30
-rw-r--r--canvas/source/vcl/windowoutdevholder.cxx49
-rw-r--r--canvas/source/vcl/windowoutdevholder.hxx56
166 files changed, 33932 insertions, 0 deletions
diff --git a/canvas/source/cairo/cairo_cachedbitmap.cxx b/canvas/source/cairo/cairo_cachedbitmap.cxx
new file mode 100644
index 000000000..111f797e2
--- /dev/null
+++ b/canvas/source/cairo/cairo_cachedbitmap.cxx
@@ -0,0 +1,79 @@
+/* -*- 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 <com/sun/star/rendering/XCanvas.hpp>
+#include <com/sun/star/rendering/RepaintResult.hpp>
+#include <utility>
+#include <tools/diagnose_ex.h>
+
+#include "cairo_cachedbitmap.hxx"
+#include "cairo_repainttarget.hxx"
+
+
+using namespace ::cairo;
+using namespace ::com::sun::star;
+
+namespace cairocanvas
+{
+ CachedBitmap::CachedBitmap( SurfaceSharedPtr pSurface,
+ const rendering::ViewState& rUsedViewState,
+ rendering::RenderState aUsedRenderState,
+ const uno::Reference< rendering::XCanvas >& rTarget ) :
+ CachedPrimitiveBase( rUsedViewState, rTarget ),
+ mpSurface(std::move( pSurface )),
+ maRenderState(std::move( aUsedRenderState ))
+ {}
+
+ void SAL_CALL CachedBitmap::disposing()
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ mpSurface.reset();
+ CachedPrimitiveBase::disposing();
+ }
+
+ ::sal_Int8 CachedBitmap::doRedraw( const rendering::ViewState& rNewState,
+ const rendering::ViewState& /*rOldState*/,
+ const uno::Reference< rendering::XCanvas >& rTargetCanvas,
+ bool bSameViewTransform )
+ {
+ ENSURE_OR_THROW( bSameViewTransform,
+ "CachedBitmap::doRedraw(): base called with changed view transform "
+ "(told otherwise during construction)" );
+
+ RepaintTarget* pTarget = dynamic_cast< RepaintTarget* >(rTargetCanvas.get());
+
+ ENSURE_OR_THROW( pTarget,
+ "CachedBitmap::redraw(): cannot cast target to RepaintTarget" );
+
+ if( !pTarget->repaint( mpSurface,
+ rNewState,
+ maRenderState ) )
+ {
+ // target failed to repaint
+ return rendering::RepaintResult::FAILED;
+ }
+
+ return rendering::RepaintResult::REDRAWN;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/cairo/cairo_cachedbitmap.hxx b/canvas/source/cairo/cairo_cachedbitmap.hxx
new file mode 100644
index 000000000..159b54bc2
--- /dev/null
+++ b/canvas/source/cairo/cairo_cachedbitmap.hxx
@@ -0,0 +1,58 @@
+/* -*- 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/cachedprimitivebase.hxx>
+#include <com/sun/star/rendering/RenderState.hpp>
+
+#include <vcl/cairo.hxx>
+
+/* Definition of CachedBitmap class */
+
+namespace cairocanvas
+{
+ class CachedBitmap : public ::canvas::CachedPrimitiveBase
+ {
+ public:
+
+ /** Create an XCachedPrimitive for given GraphicObject
+ */
+ CachedBitmap( ::cairo::SurfaceSharedPtr pSurface,
+ const css::rendering::ViewState& rUsedViewState,
+ css::rendering::RenderState aUsedRenderState,
+ const css::uno::Reference< css::rendering::XCanvas >& rTarget );
+
+ /// Dispose all internal references
+ virtual void SAL_CALL disposing() override;
+
+ private:
+ virtual ::sal_Int8 doRedraw( const css::rendering::ViewState& rNewState,
+ const css::rendering::ViewState& rOldState,
+ const css::uno::Reference<
+ css::rendering::XCanvas >& rTargetCanvas,
+ bool bSameViewTransform ) override;
+
+
+ ::cairo::SurfaceSharedPtr mpSurface;
+ const css::rendering::RenderState maRenderState;
+ };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/cairo/cairo_canvas.cxx b/canvas/source/cairo/cairo_canvas.cxx
new file mode 100644
index 000000000..622213dee
--- /dev/null
+++ b/canvas/source/cairo/cairo_canvas.cxx
@@ -0,0 +1,191 @@
+/* -*- 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 <com/sun/star/awt/Rectangle.hpp>
+#include <com/sun/star/lang/NoSupportException.hpp>
+#include <osl/mutex.hxx>
+#include <tools/diagnose_ex.h>
+#include <vcl/sysdata.hxx>
+#include <vcl/skia/SkiaHelper.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+#include "cairo_canvas.hxx"
+
+using namespace ::cairo;
+using namespace ::com::sun::star;
+
+namespace cairocanvas
+{
+ Canvas::Canvas( const uno::Sequence< uno::Any >& aArguments,
+ const uno::Reference< uno::XComponentContext >& /*rxContext*/ ) :
+ maArguments(aArguments)
+ {
+ }
+
+ void Canvas::initialize()
+ {
+ // #i64742# Only perform initialization when not in probe mode
+ if( !maArguments.hasElements() )
+ return;
+
+ assert( !SkiaHelper::isVCLSkiaEnabled() );
+
+ /* maArguments:
+ 0: ptr to creating instance (Window or VirtualDevice)
+ 1: current bounds of creating instance
+ 2: bool, denoting always on top state for Window (always false for VirtualDevice)
+ 3: XWindow for creating Window (or empty for VirtualDevice)
+ 4: SystemGraphicsData as a streamed Any
+ */
+ SAL_INFO("canvas.cairo","Canvas created " << this);
+
+ ENSURE_ARG_OR_THROW( maArguments.getLength() >= 5 &&
+ maArguments[0].getValueTypeClass() == uno::TypeClass_HYPER &&
+ maArguments[4].getValueTypeClass() == uno::TypeClass_SEQUENCE,
+ "Canvas::initialize: wrong number of arguments, or wrong types" );
+
+ // We expect a single Any here, containing a pointer to a valid
+ // VCL output device, on which to output (mostly needed for text)
+ sal_Int64 nPtr = 0;
+ maArguments[0] >>= nPtr;
+ OutputDevice* pOutDev = reinterpret_cast<OutputDevice*>(nPtr);
+ ENSURE_ARG_OR_THROW( pOutDev != nullptr,
+ "Canvas::initialize: invalid OutDev pointer" );
+
+ awt::Rectangle aBounds;
+ maArguments[1] >>= aBounds;
+
+ uno::Sequence<sal_Int8> aSeq;
+ maArguments[4] >>= aSeq;
+
+ const SystemGraphicsData* pSysData=reinterpret_cast<const SystemGraphicsData*>(aSeq.getConstArray());
+ if( !pSysData || !pSysData->nSize )
+ throw lang::NoSupportException( "Passed SystemGraphicsData invalid!" );
+
+ bool bHasCairo = pOutDev->SupportsCairo();
+ ENSURE_ARG_OR_THROW(bHasCairo, "SpriteCanvas::SpriteCanvas: No Cairo capability");
+
+ // setup helper
+ maDeviceHelper.init( *this, *pOutDev );
+
+ maCanvasHelper.init( basegfx::B2ISize(aBounds.Width, aBounds.Height), *this, this );
+
+ // forward surface to render on to canvashelper
+ maCanvasHelper.setSurface( maDeviceHelper.getSurface(), false );
+
+ maArguments.realloc(0);
+ }
+
+ Canvas::~Canvas()
+ {
+ SAL_INFO("canvas.cairo", "CairoCanvas destroyed" );
+ }
+
+ void Canvas::disposeThis()
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ // forward to parent
+ CanvasBaseT::disposeThis();
+ }
+
+ OUString SAL_CALL Canvas::getServiceName( )
+ {
+ return "com.sun.star.rendering.Canvas.Cairo";
+ }
+
+ // XServiceInfo
+ sal_Bool Canvas::supportsService(const OUString& sServiceName)
+ {
+ return cppu::supportsService(this, sServiceName);
+
+ }
+ OUString Canvas::getImplementationName()
+ {
+ return "com.sun.star.comp.rendering.Canvas.Cairo";
+ }
+ css::uno::Sequence< OUString > Canvas::getSupportedServiceNames()
+ {
+ return { getServiceName() };
+ }
+
+ bool Canvas::repaint( const SurfaceSharedPtr& pSurface,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState )
+ {
+ return maCanvasHelper.repaint( pSurface, viewState, renderState );
+ }
+
+ SurfaceSharedPtr Canvas::getSurface()
+ {
+ return maDeviceHelper.getSurface();
+ }
+
+ SurfaceSharedPtr Canvas::createSurface( const ::basegfx::B2ISize& rSize, int aContent )
+ {
+ return maDeviceHelper.createSurface( rSize, aContent );
+ }
+
+ SurfaceSharedPtr Canvas::createSurface( ::Bitmap& rBitmap )
+ {
+ SurfaceSharedPtr pSurface;
+
+ BitmapSystemData aData;
+ if( rBitmap.GetSystemData( aData ) ) {
+ const Size& rSize = rBitmap.GetSizePixel();
+
+ pSurface = maDeviceHelper.createSurface( aData, rSize );
+ }
+
+ return pSurface;
+ }
+
+ SurfaceSharedPtr Canvas::changeSurface()
+ {
+ // non-modifiable surface here
+ return SurfaceSharedPtr();
+ }
+
+ OutputDevice* Canvas::getOutputDevice()
+ {
+ return maDeviceHelper.getOutputDevice();
+ }
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_rendering_Canvas_Cairo_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& args)
+{
+ rtl::Reference<cairocanvas::Canvas> p = new cairocanvas::Canvas(args, context);
+ p->acquire();
+ try {
+ p->initialize();
+ } catch (css::uno::Exception&) {
+ p->dispose();
+ p->release();
+ throw;
+ }
+ return static_cast<cppu::OWeakObject*>(p.get());
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/cairo/cairo_canvas.hxx b/canvas/source/cairo/cairo_canvas.hxx
new file mode 100644
index 000000000..0c41a8a5c
--- /dev/null
+++ b/canvas/source/cairo/cairo_canvas.hxx
@@ -0,0 +1,142 @@
+/* -*- 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/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/util/XUpdatable.hpp>
+#include <com/sun/star/rendering/XIntegerBitmap.hpp>
+#include <com/sun/star/rendering/XGraphicDevice.hpp>
+#include <com/sun/star/rendering/XBitmapCanvas.hpp>
+
+#include <cppuhelper/compbase.hxx>
+#include <comphelper/uno3.hxx>
+
+#include <base/basemutexhelper.hxx>
+#include <base/bitmapcanvasbase.hxx>
+#include <base/graphicdevicebase.hxx>
+#include <base/integerbitmapbase.hxx>
+
+#include "cairo_canvashelper.hxx"
+#include "cairo_devicehelper.hxx"
+#include "cairo_repainttarget.hxx"
+#include "cairo_surfaceprovider.hxx"
+
+namespace cairocanvas
+{
+ 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 > GraphicDeviceBase_Base;
+ typedef ::canvas::GraphicDeviceBase< ::canvas::BaseMutexHelper< GraphicDeviceBase_Base >,
+ DeviceHelper,
+ ::osl::MutexGuard,
+ ::cppu::OWeakObject > CanvasBase_Base;
+
+ /** Mixin SurfaceProvider
+
+ Have to mixin the SurfaceProvider before deriving from
+ ::canvas::CanvasBase, 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::CanvasBase directly from
+ SurfaceProvider (because derivees of
+ ::canvas::CanvasBase have to explicitly forward the
+ XInterface methods (e.g. via DECLARE_UNO3_AGG_DEFAULTS)
+ anyway).
+ */
+ class CanvasBaseSurfaceProvider_Base : public CanvasBase_Base,
+ public SurfaceProvider
+ {
+ };
+
+ typedef ::canvas::IntegerBitmapBase<
+ canvas::BitmapCanvasBase2<
+ CanvasBaseSurfaceProvider_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 RepaintTarget
+ {
+ public:
+ Canvas( const css::uno::Sequence< css::uno::Any >& aArguments,
+ const css::uno::Reference< css::uno::XComponentContext >& rxContext );
+
+ void initialize();
+
+ /// For resource tracking
+ virtual ~Canvas() override;
+
+ /// 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, GraphicDeviceBase_Base, ::cppu::WeakComponentImplHelperBase )
+
+ // XServiceName
+ virtual OUString SAL_CALL getServiceName( ) override;
+
+ // XServiceInfo
+ virtual sal_Bool SAL_CALL supportsService(const OUString& sServiceName) override;
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ // RepaintTarget
+ virtual bool repaint( const ::cairo::SurfaceSharedPtr& pSurface,
+ const css::rendering::ViewState& viewState,
+ const css::rendering::RenderState& renderState ) override;
+
+ // SurfaceProvider
+ virtual ::cairo::SurfaceSharedPtr getSurface() override;
+ virtual ::cairo::SurfaceSharedPtr createSurface( const ::basegfx::B2ISize& rSize, int aContent ) override;
+ virtual ::cairo::SurfaceSharedPtr createSurface( ::Bitmap& rBitmap ) override;
+ virtual ::cairo::SurfaceSharedPtr changeSurface() override;
+ virtual OutputDevice* getOutputDevice() override;
+
+ private:
+ css::uno::Sequence< css::uno::Any > maArguments;
+ };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/cairo/cairo_canvasbitmap.cxx b/canvas/source/cairo/cairo_canvasbitmap.cxx
new file mode 100644
index 000000000..fa33b83eb
--- /dev/null
+++ b/canvas/source/cairo/cairo_canvasbitmap.cxx
@@ -0,0 +1,163 @@
+/* -*- 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 <cppuhelper/supportsservice.hxx>
+#include <tools/diagnose_ex.h>
+#include <utility>
+#include <vcl/bitmapex.hxx>
+#include <vcl/BitmapTools.hxx>
+
+#include <cairo.h>
+
+#include "cairo_canvasbitmap.hxx"
+
+using namespace ::cairo;
+using namespace ::com::sun::star;
+
+namespace cairocanvas
+{
+ CanvasBitmap::CanvasBitmap( const ::basegfx::B2ISize& rSize,
+ SurfaceProviderRef rSurfaceProvider,
+ rendering::XGraphicDevice* pDevice,
+ bool bHasAlpha ) :
+ mpSurfaceProvider(std::move( rSurfaceProvider )),
+ maSize(rSize),
+ mbHasAlpha(bHasAlpha)
+ {
+ ENSURE_OR_THROW( mpSurfaceProvider.is(),
+ "CanvasBitmap::CanvasBitmap(): Invalid surface or device" );
+
+ SAL_INFO(
+ "canvas.cairo",
+ "bitmap size: " << rSize.getX() << "x" << rSize.getY());
+
+ mpBufferSurface = mpSurfaceProvider->createSurface( rSize, bHasAlpha ? CAIRO_CONTENT_COLOR_ALPHA : CAIRO_CONTENT_COLOR );
+ mpBufferCairo = mpBufferSurface->getCairo();
+
+ maCanvasHelper.init( rSize, *mpSurfaceProvider, pDevice );
+ maCanvasHelper.setSurface( mpBufferSurface, bHasAlpha );
+
+ // clear bitmap to 100% transparent
+ maCanvasHelper.clear();
+ }
+
+ void CanvasBitmap::disposeThis()
+ {
+ mpSurfaceProvider.clear();
+
+ mpBufferCairo.reset();
+ mpBufferSurface.reset();
+
+ // forward to parent
+ CanvasBitmap_Base::disposeThis();
+ }
+
+ SurfaceSharedPtr CanvasBitmap::getSurface()
+ {
+ return mpBufferSurface;
+ }
+
+ SurfaceSharedPtr CanvasBitmap::createSurface( const ::basegfx::B2ISize& rSize, int aContent )
+ {
+ return mpSurfaceProvider->createSurface(rSize,aContent);
+ }
+
+ SurfaceSharedPtr CanvasBitmap::createSurface( ::Bitmap& rBitmap )
+ {
+ return mpSurfaceProvider->createSurface(rBitmap);
+ }
+
+ SurfaceSharedPtr CanvasBitmap::changeSurface()
+ {
+ // non-modifiable surface here
+ return SurfaceSharedPtr();
+ }
+
+ OutputDevice* CanvasBitmap::getOutputDevice()
+ {
+ return mpSurfaceProvider->getOutputDevice();
+ }
+
+ bool CanvasBitmap::repaint( const SurfaceSharedPtr& pSurface,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState )
+ {
+ return maCanvasHelper.repaint( pSurface, viewState, renderState );
+ }
+
+ uno::Any SAL_CALL CanvasBitmap::getFastPropertyValue( sal_Int32 nHandle )
+ {
+ uno::Any aRV( sal_Int32(0) );
+ // 0 ... get BitmapEx
+ // 1 ... get Pixbuf with bitmap RGB content
+ // 2 ... return nothing (empty Any)
+ switch( nHandle )
+ {
+ case 0:
+ {
+ aRV <<= reinterpret_cast<sal_Int64>( nullptr );
+ if ( !mbHasAlpha )
+ break;
+
+ BitmapEx* pBitmapEx = vcl::bitmap::CreateFromCairoSurface(
+ ::Size( maSize.getX(), maSize.getY() ),
+ getSurface()->getCairoSurface().get());
+ if (pBitmapEx)
+ aRV <<= reinterpret_cast<sal_Int64>( pBitmapEx );
+
+ break;
+ }
+ case 1:
+ {
+ aRV = getOutputDevice()->GetNativeSurfaceHandle(mpBufferSurface, maSize);
+ break;
+ }
+ case 2:
+ {
+ // Always return nothing - for the RGB surface support.
+ // Alpha code paths go via the above case 0.
+ aRV = uno::Any();
+ break;
+ }
+ }
+
+ return aRV;
+ }
+
+ OUString SAL_CALL CanvasBitmap::getImplementationName( )
+ {
+ return "CairoCanvas.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/cairo/cairo_canvasbitmap.hxx b/canvas/source/cairo/cairo_canvasbitmap.hxx
new file mode 100644
index 000000000..8826bc82e
--- /dev/null
+++ b/canvas/source/cairo/cairo_canvasbitmap.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 <cppuhelper/compbase.hxx>
+
+#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 <comphelper/uno3.hxx>
+
+#include <base/bitmapcanvasbase.hxx>
+#include <base/basemutexhelper.hxx>
+#include <base/integerbitmapbase.hxx>
+
+#include "cairo_canvashelper.hxx"
+#include "cairo_repainttarget.hxx"
+
+
+/* Definition of CanvasBitmap class */
+
+namespace cairocanvas
+{
+ typedef ::cppu::WeakComponentImplHelper< css::rendering::XBitmapCanvas,
+ css::rendering::XIntegerBitmap,
+ css::lang::XServiceInfo,
+ css::beans::XFastPropertySet > CanvasBitmapBase_Base;
+ class CanvasBitmapSpriteSurface_Base :
+ public ::canvas::BaseMutexHelper<CanvasBitmapBase_Base>,
+ public SurfaceProvider
+ {
+ };
+
+ typedef ::canvas::IntegerBitmapBase<
+ canvas::BitmapCanvasBase2<
+ CanvasBitmapSpriteSurface_Base,
+ CanvasHelper,
+ ::osl::MutexGuard,
+ ::cppu::OWeakObject> > CanvasBitmap_Base;
+
+ class CanvasBitmap : public CanvasBitmap_Base,
+ public RepaintTarget
+ {
+ public:
+ /** Create a canvas bitmap for the given surface
+
+ @param rSize
+ Size of the bitmap
+
+ @param rDevice
+ Reference device, with which bitmap should be compatible
+ */
+ CanvasBitmap( const ::basegfx::B2ISize& rSize,
+ SurfaceProviderRef rDevice,
+ css::rendering::XGraphicDevice* pDevice,
+ bool bHasAlpha );
+
+ /// 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( CanvasBitmap, CanvasBitmapBase_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;
+
+ // SurfaceProvider
+ virtual ::cairo::SurfaceSharedPtr getSurface() override;
+ virtual ::cairo::SurfaceSharedPtr createSurface( const ::basegfx::B2ISize& rSize, int aContent ) override;
+ virtual ::cairo::SurfaceSharedPtr createSurface( ::Bitmap& rBitmap ) override;
+ virtual ::cairo::SurfaceSharedPtr changeSurface() override;
+ virtual OutputDevice* getOutputDevice() override;
+
+ // RepaintTarget
+ virtual bool repaint( const ::cairo::SurfaceSharedPtr& pSurface,
+ const css::rendering::ViewState& viewState,
+ const css::rendering::RenderState& renderState ) override;
+
+ // XFastPropertySet
+ // used to retrieve BitmapEx pointer or X Pixmap handles for this bitmap
+ // handle values have these meanings:
+ // 0 ... get pointer to BitmapEx
+ // 1 ... get X pixmap handle to rgb content
+ // 2 ... FIXME: leftover? ever called with this? always returns nothing (empty Any)
+ // returned any contains either BitmapEx pointer or array of three Any value
+ // 1st a bool value: true - free the pixmap after used by XFreePixmap, false do nothing, the pixmap is used internally in the canvas
+ // 2nd the pixmap handle (sal_Int64)
+ // 3rd the pixmap depth
+ virtual css::uno::Any SAL_CALL getFastPropertyValue(sal_Int32 nHandle) override;
+ virtual void SAL_CALL setFastPropertyValue(sal_Int32, const css::uno::Any&) override {}
+
+ private:
+ SurfaceProviderRef mpSurfaceProvider;
+ ::cairo::SurfaceSharedPtr mpBufferSurface;
+ ::cairo::CairoSharedPtr mpBufferCairo;
+
+ const ::basegfx::B2ISize maSize;
+ const bool mbHasAlpha;
+ };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/cairo/cairo_canvascustomsprite.cxx b/canvas/source/cairo/cairo_canvascustomsprite.cxx
new file mode 100644
index 000000000..b88507178
--- /dev/null
+++ b/canvas/source/cairo/cairo_canvascustomsprite.cxx
@@ -0,0 +1,151 @@
+/* -*- 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/point/b2dpoint.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <tools/diagnose_ex.h>
+
+#include <canvas/canvastools.hxx>
+#include <cairo.h>
+
+#include "cairo_canvascustomsprite.hxx"
+#include "cairo_spritecanvas.hxx"
+
+
+using namespace ::cairo;
+using namespace ::com::sun::star;
+
+namespace cairocanvas
+{
+ CanvasCustomSprite::CanvasCustomSprite( const css::geometry::RealSize2D& rSpriteSize,
+ const SpriteCanvasRef& rRefDevice ) :
+ mpSpriteCanvas( rRefDevice ),
+ maSize( ::canvas::tools::roundUp( rSpriteSize.Width ),
+ ::canvas::tools::roundUp( rSpriteSize.Height ) )
+ {
+ ENSURE_OR_THROW( rRefDevice,
+ "CanvasCustomSprite::CanvasCustomSprite(): Invalid sprite canvas" );
+
+ SAL_INFO( "canvas.cairo", "sprite size: " << ::canvas::tools::roundUp( rSpriteSize.Width ) << ", " << ::canvas::tools::roundUp( rSpriteSize.Height ));
+
+ mpBufferSurface = mpSpriteCanvas->createSurface( maSize, CAIRO_CONTENT_COLOR_ALPHA );
+
+ maCanvasHelper.init( maSize,
+ *rRefDevice,
+ rRefDevice.get() );
+ maCanvasHelper.setSurface( mpBufferSurface, true );
+
+ maSpriteHelper.init( rSpriteSize,
+ rRefDevice );
+ maSpriteHelper.setSurface( mpBufferSurface );
+
+ // clear sprite to 100% transparent
+ maCanvasHelper.clear();
+ }
+
+ void CanvasCustomSprite::disposeThis()
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ mpSpriteCanvas.clear();
+ mpBufferSurface.reset();
+
+ // forward to parent
+ CanvasCustomSpriteBaseT::disposeThis();
+ }
+
+ void CanvasCustomSprite::redraw( const CairoSharedPtr& pCairo,
+ bool bBufferedUpdate ) const
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ redraw( pCairo, maSpriteHelper.getPosPixel(), bBufferedUpdate );
+ }
+
+ void CanvasCustomSprite::redraw( const CairoSharedPtr& pCairo,
+ const ::basegfx::B2DPoint& rOrigOutputPos,
+ bool bBufferedUpdate ) const
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ maSpriteHelper.redraw( pCairo,
+ rOrigOutputPos,
+ mbSurfaceDirty,
+ bBufferedUpdate );
+
+ mbSurfaceDirty = false;
+ }
+
+ bool CanvasCustomSprite::repaint( const SurfaceSharedPtr& pSurface,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState )
+ {
+ return maCanvasHelper.repaint( pSurface, viewState, renderState );
+ }
+
+ SurfaceSharedPtr CanvasCustomSprite::getSurface()
+ {
+ return mpBufferSurface;
+ }
+
+ SurfaceSharedPtr CanvasCustomSprite::createSurface( const ::basegfx::B2ISize& rSize, int aContent )
+ {
+ return mpSpriteCanvas->createSurface(rSize,aContent);
+ }
+
+ SurfaceSharedPtr CanvasCustomSprite::createSurface( ::Bitmap& rBitmap )
+ {
+ return mpSpriteCanvas->createSurface(rBitmap);
+ }
+
+ SurfaceSharedPtr CanvasCustomSprite::changeSurface()
+ {
+ SAL_INFO( "canvas.cairo", "replacing sprite background surface");
+
+ mpBufferSurface = mpSpriteCanvas->createSurface( maSize, CAIRO_CONTENT_COLOR );
+ maSpriteHelper.setSurface( mpBufferSurface );
+
+ return mpBufferSurface;
+ }
+
+ OutputDevice* CanvasCustomSprite::getOutputDevice()
+ {
+ return mpSpriteCanvas->getOutputDevice();
+ }
+
+ OUString SAL_CALL CanvasCustomSprite::getImplementationName()
+ {
+ return "CairoCanvas.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" };
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/cairo/cairo_canvascustomsprite.hxx b/canvas/source/cairo/cairo_canvascustomsprite.hxx
new file mode 100644
index 000000000..ffe766ab0
--- /dev/null
+++ b/canvas/source/cairo/cairo_canvascustomsprite.hxx
@@ -0,0 +1,145 @@
+/* -*- 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/rendering/XBitmapCanvas.hpp>
+#include <com/sun/star/rendering/XCustomSprite.hpp>
+#include <com/sun/star/rendering/XIntegerBitmap.hpp>
+
+#include <basegfx/point/b2dpoint.hxx>
+
+#include <base/basemutexhelper.hxx>
+#include <base/canvascustomspritebase.hxx>
+
+#include <vcl/cairo.hxx>
+
+#include "cairo_sprite.hxx"
+#include "cairo_canvashelper.hxx"
+#include "cairo_repainttarget.hxx"
+#include "cairo_spritehelper.hxx"
+#include "cairo_spritecanvas.hxx"
+
+
+namespace cairocanvas
+{
+ 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,
+ public SurfaceProvider
+ {
+ };
+
+ typedef ::canvas::CanvasCustomSpriteBase< CanvasCustomSpriteSpriteBase_Base,
+ SpriteHelper,
+ CanvasHelper,
+ ::osl::MutexGuard,
+ ::cppu::OWeakObject > CanvasCustomSpriteBaseT;
+
+ /* Definition of CanvasCustomSprite class */
+
+ class CanvasCustomSprite : public CanvasCustomSpriteBaseT,
+ public RepaintTarget
+ {
+ 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 );
+
+ 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 ::cairo::CairoSharedPtr& pCairo,
+ bool bBufferedUpdate ) const override;
+ virtual void redraw( const ::cairo::CairoSharedPtr& pCairo,
+ const ::basegfx::B2DPoint& rOrigOutputPos,
+ bool bBufferedUpdate ) const override;
+
+ // RepaintTarget
+ virtual bool repaint( const ::cairo::SurfaceSharedPtr& pSurface,
+ const css::rendering::ViewState& viewState,
+ const css::rendering::RenderState& renderState ) override;
+
+ // SurfaceProvider
+ virtual ::cairo::SurfaceSharedPtr getSurface() override;
+ virtual ::cairo::SurfaceSharedPtr createSurface( const ::basegfx::B2ISize& rSize, int aContent ) override;
+ virtual ::cairo::SurfaceSharedPtr createSurface( ::Bitmap& rBitmap ) override;
+ virtual ::cairo::SurfaceSharedPtr changeSurface() override;
+ virtual OutputDevice* getOutputDevice() override;
+
+ private:
+ /** MUST hold here, too, since CanvasHelper only contains a
+ raw pointer (without refcounting)
+ */
+ SpriteCanvasRef mpSpriteCanvas;
+ ::cairo::SurfaceSharedPtr mpBufferSurface;
+ ::basegfx::B2ISize maSize;
+ };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/cairo/cairo_canvasfont.cxx b/canvas/source/cairo/cairo_canvasfont.cxx
new file mode 100644
index 000000000..36c1733ef
--- /dev/null
+++ b/canvas/source/cairo/cairo_canvasfont.cxx
@@ -0,0 +1,156 @@
+/* -*- 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/rendering/PanoseProportion.hpp>
+#include <cppuhelper/supportsservice.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <rtl/math.hxx>
+#include <utility>
+#include <vcl/metric.hxx>
+
+#include "cairo_canvasfont.hxx"
+#include "cairo_textlayout.hxx"
+
+using namespace ::com::sun::star;
+
+namespace cairocanvas
+{
+
+ CanvasFont::CanvasFont( const rendering::FontRequest& rFontRequest,
+ const uno::Sequence< beans::PropertyValue >& /*rExtraFontProperties*/,
+ const geometry::Matrix2D& rFontMatrix,
+ SurfaceProviderRef rDevice ) :
+ CanvasFont_Base( m_aMutex ),
+ maFont( vcl::Font( rFontRequest.FontDescription.FamilyName,
+ rFontRequest.FontDescription.StyleName,
+ Size( 0, ::basegfx::fround(rFontRequest.CellSize) ) ) ),
+ maFontRequest( rFontRequest ),
+ mpRefDevice(std::move( rDevice ))
+ {
+ maFont->SetAlignment( ALIGN_BASELINE );
+ maFont->SetCharSet( (rFontRequest.FontDescription.IsSymbolFont==css::util::TriState_YES) ? RTL_TEXTENCODING_SYMBOL : RTL_TEXTENCODING_UNICODE );
+ maFont->SetVertical( rFontRequest.FontDescription.IsVertical==css::util::TriState_YES );
+
+ // TODO(F2): improve panose->vclenum conversion
+ maFont->SetWeight( static_cast<FontWeight>(rFontRequest.FontDescription.FontDescription.Weight) );
+ maFont->SetItalic( (rFontRequest.FontDescription.FontDescription.Letterform<=8) ? ITALIC_NONE : ITALIC_NORMAL );
+ maFont->SetPitch(
+ rFontRequest.FontDescription.FontDescription.Proportion == rendering::PanoseProportion::MONO_SPACED
+ ? PITCH_FIXED : PITCH_VARIABLE);
+
+ maFont->SetLanguage( LanguageTag::convertToLanguageType( rFontRequest.Locale, false));
+
+ // adjust to stretched/shrunk font
+ if( ::rtl::math::approxEqual( rFontMatrix.m00, rFontMatrix.m11) )
+ return;
+
+ VclPtr<OutputDevice> pOutDev( mpRefDevice->getOutputDevice() );
+
+ if( !pOutDev )
+ return;
+
+ const bool bOldMapState( pOutDev->IsMapModeEnabled() );
+ pOutDev->EnableMapMode(false);
+
+ const Size aSize = pOutDev->GetFontMetric( *maFont ).GetFontSize();
+
+ const double fDividend( rFontMatrix.m10 + rFontMatrix.m11 );
+ double fStretch = rFontMatrix.m00 + rFontMatrix.m01;
+
+ if( !::basegfx::fTools::equalZero( fDividend) )
+ fStretch /= fDividend;
+
+ const tools::Long nNewWidth = ::basegfx::fround( aSize.Width() * fStretch );
+
+ maFont->SetAverageFontWidth( nNewWidth );
+
+ pOutDev->EnableMapMode(bOldMapState);
+ }
+
+ void SAL_CALL CanvasFont::disposing()
+ {
+ SolarMutexGuard aGuard;
+
+ mpRefDevice.clear();
+ }
+
+ uno::Reference< rendering::XTextLayout > SAL_CALL CanvasFont::createTextLayout( const rendering::StringContext& aText, sal_Int8 nDirection, sal_Int64 nRandomSeed )
+ {
+ SolarMutexGuard aGuard;
+
+ if( !mpRefDevice.is() )
+ return uno::Reference< rendering::XTextLayout >(); // we're disposed
+
+ return new TextLayout( aText,
+ nDirection,
+ nRandomSeed,
+ Reference( this ),
+ mpRefDevice );
+ }
+
+ rendering::FontRequest SAL_CALL CanvasFont::getFontRequest( )
+ {
+ SolarMutexGuard aGuard;
+
+ return maFontRequest;
+ }
+
+ rendering::FontMetrics SAL_CALL CanvasFont::getFontMetrics( )
+ {
+ // TODO(F1)
+ return rendering::FontMetrics();
+ }
+
+ uno::Sequence< double > SAL_CALL CanvasFont::getAvailableSizes( )
+ {
+ // TODO(F1)
+ return uno::Sequence< double >();
+ }
+
+ uno::Sequence< beans::PropertyValue > SAL_CALL CanvasFont::getExtraFontProperties( )
+ {
+ // TODO(F1)
+ return uno::Sequence< beans::PropertyValue >();
+ }
+
+ OUString SAL_CALL CanvasFont::getImplementationName()
+ {
+ return "CairoCanvas::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" };
+ }
+
+ vcl::Font const & CanvasFont::getVCLFont() const
+ {
+ return *maFont;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/cairo/cairo_canvasfont.hxx b/canvas/source/cairo/cairo_canvasfont.hxx
new file mode 100644
index 000000000..0ebe9acd8
--- /dev/null
+++ b/canvas/source/cairo/cairo_canvasfont.hxx
@@ -0,0 +1,83 @@
+/* -*- 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/geometry/Matrix2D.hpp>
+#include <com/sun/star/rendering/FontRequest.hpp>
+#include <com/sun/star/rendering/XCanvasFont.hpp>
+
+#include <vcl/font.hxx>
+
+#include <vclwrapper.hxx>
+
+#include "cairo_surfaceprovider.hxx"
+
+
+/* Definition of CanvasFont class */
+
+namespace cairocanvas
+{
+ typedef ::cppu::WeakComponentImplHelper< css::rendering::XCanvasFont,
+ css::lang::XServiceInfo > CanvasFont_Base;
+
+ class CanvasFont : public ::cppu::BaseMutex,
+ public CanvasFont_Base
+ {
+ public:
+ typedef rtl::Reference<CanvasFont> Reference;
+ /// 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& rFontMatrix,
+ SurfaceProviderRef rDevice );
+
+ /// 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;
+
+ vcl::Font const & getVCLFont() const;
+
+ private:
+ ::canvas::vcltools::VCLObject<vcl::Font> maFont;
+ css::rendering::FontRequest maFontRequest;
+ SurfaceProviderRef mpRefDevice;
+ };
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/cairo/cairo_canvashelper.cxx b/canvas/source/cairo/cairo_canvashelper.cxx
new file mode 100644
index 000000000..8f46c5b85
--- /dev/null
+++ b/canvas/source/cairo/cairo_canvashelper.cxx
@@ -0,0 +1,2047 @@
+/* -*- 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 <tuple>
+
+#include <basegfx/point/b2dpoint.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolypolygon.hxx>
+#include <basegfx/utils/canvastools.hxx>
+#include <basegfx/utils/keystoplerp.hxx>
+#include <basegfx/utils/lerp.hxx>
+#include <com/sun/star/rendering/ColorComponentTag.hpp>
+#include <com/sun/star/rendering/ColorSpaceType.hpp>
+#include <com/sun/star/rendering/CompositeOperation.hpp>
+#include <com/sun/star/rendering/IntegerBitmapLayout.hpp>
+#include <com/sun/star/rendering/PathCapType.hpp>
+#include <com/sun/star/rendering/PathJoinType.hpp>
+#include <com/sun/star/rendering/RenderingIntent.hpp>
+#include <com/sun/star/rendering/TexturingMode.hpp>
+#include <com/sun/star/rendering/XIntegerBitmapColorSpace.hpp>
+#include <com/sun/star/util/Endianness.hpp>
+#include <comphelper/sequence.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <rtl/math.hxx>
+#include <tools/diagnose_ex.h>
+#include <vcl/bitmapex.hxx>
+#include <vcl/BitmapTools.hxx>
+#include <vcl/canvastools.hxx>
+#include <vcl/virdev.hxx>
+
+#include <canvas/canvastools.hxx>
+#include <parametricpolypolygon.hxx>
+#include <cairo.h>
+
+#include "cairo_cachedbitmap.hxx"
+#include "cairo_canvasbitmap.hxx"
+#include "cairo_canvashelper.hxx"
+
+using namespace ::cairo;
+using namespace ::com::sun::star;
+
+namespace cairocanvas
+{
+ CanvasHelper::CanvasHelper() :
+ mpSurfaceProvider(nullptr),
+ mpDevice(nullptr),
+ mbHaveAlpha()
+ {
+ }
+
+ void CanvasHelper::disposing()
+ {
+ mpSurface.reset();
+ mpCairo.reset();
+ mpVirtualDevice.disposeAndClear();
+ mpDevice = nullptr;
+ mpSurfaceProvider = nullptr;
+ }
+
+ void CanvasHelper::init( const ::basegfx::B2ISize& rSizePixel,
+ SurfaceProvider& rSurfaceProvider,
+ rendering::XGraphicDevice* pDevice )
+ {
+ maSize = rSizePixel;
+ mpSurfaceProvider = &rSurfaceProvider;
+ mpDevice = pDevice;
+ }
+
+ void CanvasHelper::setSize( const ::basegfx::B2ISize& rSize )
+ {
+ maSize = rSize;
+ }
+
+ void CanvasHelper::setSurface( const SurfaceSharedPtr& pSurface, bool bHasAlpha )
+ {
+ mbHaveAlpha = bHasAlpha;
+ mpVirtualDevice.disposeAndClear();
+ mpSurface = pSurface;
+ mpCairo = pSurface->getCairo();
+ }
+
+ static void setColor( cairo_t* pCairo,
+ const uno::Sequence<double>& rColor )
+ {
+ if( rColor.getLength() > 3 )
+ {
+ cairo_set_source_rgba( pCairo,
+ rColor[0],
+ rColor[1],
+ rColor[2],
+ rColor[3] );
+ }
+ else if( rColor.getLength() == 3 )
+ cairo_set_source_rgb( pCairo,
+ rColor[0],
+ rColor[1],
+ rColor[2] );
+ }
+
+ void CanvasHelper::useStates( const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState,
+ bool bSetColor )
+ {
+ cairo_matrix_t aViewMatrix;
+ cairo_matrix_t aRenderMatrix;
+ cairo_matrix_t aCombinedMatrix;
+
+ cairo_matrix_init( &aViewMatrix,
+ viewState.AffineTransform.m00, viewState.AffineTransform.m10, viewState.AffineTransform.m01,
+ viewState.AffineTransform.m11, viewState.AffineTransform.m02, viewState.AffineTransform.m12);
+ cairo_matrix_init( &aRenderMatrix,
+ renderState.AffineTransform.m00, renderState.AffineTransform.m10, renderState.AffineTransform.m01,
+ renderState.AffineTransform.m11, renderState.AffineTransform.m02, renderState.AffineTransform.m12);
+ cairo_matrix_multiply( &aCombinedMatrix, &aRenderMatrix, &aViewMatrix);
+
+ if( viewState.Clip.is() )
+ {
+ SAL_INFO( "canvas.cairo", "view clip");
+
+ aViewMatrix.x0 = basegfx::fround( aViewMatrix.x0 );
+ aViewMatrix.y0 = basegfx::fround( aViewMatrix.y0 );
+ cairo_set_matrix( mpCairo.get(), &aViewMatrix );
+ doPolyPolygonPath( viewState.Clip, Clip );
+ }
+
+ aCombinedMatrix.x0 = basegfx::fround( aCombinedMatrix.x0 );
+ aCombinedMatrix.y0 = basegfx::fround( aCombinedMatrix.y0 );
+ cairo_set_matrix( mpCairo.get(), &aCombinedMatrix );
+
+ if( renderState.Clip.is() )
+ {
+ SAL_INFO( "canvas.cairo", "render clip BEGIN");
+
+ doPolyPolygonPath( renderState.Clip, Clip );
+ SAL_INFO( "canvas.cairo", "render clip END");
+ }
+
+ if( bSetColor )
+ setColor(mpCairo.get(),renderState.DeviceColor);
+
+ cairo_operator_t compositingMode( CAIRO_OPERATOR_OVER );
+ switch( renderState.CompositeOperation )
+ {
+ case rendering::CompositeOperation::CLEAR:
+ compositingMode = CAIRO_OPERATOR_CLEAR;
+ break;
+ case rendering::CompositeOperation::SOURCE:
+ compositingMode = CAIRO_OPERATOR_SOURCE;
+ break;
+ case rendering::CompositeOperation::DESTINATION:
+ compositingMode = CAIRO_OPERATOR_DEST;
+ break;
+ case rendering::CompositeOperation::OVER:
+ compositingMode = CAIRO_OPERATOR_OVER;
+ break;
+ case rendering::CompositeOperation::UNDER:
+ compositingMode = CAIRO_OPERATOR_DEST;
+ break;
+ case rendering::CompositeOperation::INSIDE:
+ compositingMode = CAIRO_OPERATOR_IN;
+ break;
+ case rendering::CompositeOperation::INSIDE_REVERSE:
+ compositingMode = CAIRO_OPERATOR_OUT;
+ break;
+ case rendering::CompositeOperation::OUTSIDE:
+ compositingMode = CAIRO_OPERATOR_DEST_OVER;
+ break;
+ case rendering::CompositeOperation::OUTSIDE_REVERSE:
+ compositingMode = CAIRO_OPERATOR_DEST_OUT;
+ break;
+ case rendering::CompositeOperation::ATOP:
+ compositingMode = CAIRO_OPERATOR_ATOP;
+ break;
+ case rendering::CompositeOperation::ATOP_REVERSE:
+ compositingMode = CAIRO_OPERATOR_DEST_ATOP;
+ break;
+ case rendering::CompositeOperation::XOR:
+ compositingMode = CAIRO_OPERATOR_XOR;
+ break;
+ case rendering::CompositeOperation::ADD:
+ compositingMode = CAIRO_OPERATOR_ADD;
+ break;
+ case rendering::CompositeOperation::SATURATE:
+ compositingMode = CAIRO_OPERATOR_SATURATE;
+ break;
+ }
+ cairo_set_operator( mpCairo.get(), compositingMode );
+ }
+
+ void CanvasHelper::clear()
+ {
+ SAL_INFO( "canvas.cairo", "clear whole area: " << maSize.getX() << " x " << maSize.getY() );
+
+ if( !mpCairo )
+ return;
+
+ cairo_save( mpCairo.get() );
+
+ cairo_identity_matrix( mpCairo.get() );
+ // this does not really differ from all-zero, as cairo
+ // internally converts to premultiplied alpha. but anyway,
+ // this keeps it consistent with the other canvas impls
+ if( mbHaveAlpha )
+ cairo_set_source_rgba( mpCairo.get(), 1.0, 1.0, 1.0, 0.0 );
+ else
+ cairo_set_source_rgb( mpCairo.get(), 1.0, 1.0, 1.0 );
+ cairo_set_operator( mpCairo.get(), CAIRO_OPERATOR_SOURCE );
+
+ cairo_rectangle( mpCairo.get(), 0, 0, maSize.getX(), maSize.getY() );
+ cairo_fill( mpCairo.get() );
+
+ cairo_restore( mpCairo.get() );
+ }
+
+ void CanvasHelper::drawLine( const rendering::XCanvas* /*pCanvas*/,
+ const geometry::RealPoint2D& aStartPoint,
+ const geometry::RealPoint2D& aEndPoint,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState )
+ {
+ if( !mpCairo )
+ return;
+
+ cairo_save( mpCairo.get() );
+
+ cairo_set_line_width( mpCairo.get(), 1 );
+
+ useStates( viewState, renderState, true );
+
+ cairo_move_to( mpCairo.get(), aStartPoint.X + 0.5, aStartPoint.Y + 0.5 );
+ cairo_line_to( mpCairo.get(), aEndPoint.X + 0.5, aEndPoint.Y + 0.5 );
+ cairo_stroke( mpCairo.get() );
+
+ cairo_restore( mpCairo.get() );
+ }
+
+ void CanvasHelper::drawBezier( const rendering::XCanvas* ,
+ const geometry::RealBezierSegment2D& aBezierSegment,
+ const geometry::RealPoint2D& aEndPoint,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState )
+ {
+ if( !mpCairo )
+ return;
+
+ cairo_save( mpCairo.get() );
+
+ cairo_set_line_width( mpCairo.get(), 1 );
+
+ useStates( viewState, renderState, true );
+
+ cairo_move_to( mpCairo.get(), aBezierSegment.Px + 0.5, aBezierSegment.Py + 0.5 );
+ // tdf#99165 correction of control points not needed here, only hairlines drawn
+ // (see cairo_set_line_width above)
+ cairo_curve_to( mpCairo.get(),
+ aBezierSegment.C1x + 0.5, aBezierSegment.C1y + 0.5,
+ aBezierSegment.C2x + 0.5, aBezierSegment.C2y + 0.5,
+ aEndPoint.X + 0.5, aEndPoint.Y + 0.5 );
+ cairo_stroke( mpCairo.get() );
+
+ cairo_restore( mpCairo.get() );
+ }
+
+constexpr OUStringLiteral PARAMETRICPOLYPOLYGON_IMPLEMENTATION_NAME = u"Canvas::ParametricPolyPolygon";
+
+ /** surfaceFromXBitmap Create a surface from XBitmap
+ * @param xBitmap bitmap image that will be used for the surface
+ * @param bHasAlpha will be set to true if resulting surface has alpha
+ *
+ * This is a helper function for the other surfaceFromXBitmap().
+ * This function tries to create surface from xBitmap by checking if xBitmap is CanvasBitmap or SpriteCanvas.
+ *
+ * @return created surface or NULL
+ **/
+ static SurfaceSharedPtr surfaceFromXBitmap( const uno::Reference< rendering::XBitmap >& xBitmap )
+ {
+ CanvasBitmap* pBitmapImpl = dynamic_cast< CanvasBitmap* >( xBitmap.get() );
+ if( pBitmapImpl )
+ return pBitmapImpl->getSurface();
+
+ SurfaceProvider* pSurfaceProvider = dynamic_cast<SurfaceProvider*>( xBitmap.get() );
+ if( pSurfaceProvider )
+ return pSurfaceProvider->getSurface();
+
+ return SurfaceSharedPtr();
+ }
+
+ static ::BitmapEx bitmapExFromXBitmap( const uno::Reference< rendering::XBitmap >& xBitmap )
+ {
+ // TODO(F1): Add support for floating point bitmap formats
+ uno::Reference<rendering::XIntegerReadOnlyBitmap> xIntBmp(xBitmap,
+ uno::UNO_QUERY_THROW);
+ ::BitmapEx aBmpEx = vcl::unotools::bitmapExFromXBitmap(xIntBmp);
+ if( !aBmpEx.IsEmpty() )
+ return aBmpEx;
+
+ // TODO(F1): extract pixel from XBitmap interface
+ ENSURE_OR_THROW( false,
+ "bitmapExFromXBitmap(): could not extract BitmapEx" );
+
+ return ::BitmapEx();
+ }
+
+ /** surfaceFromXBitmap Create a surface from XBitmap
+ * @param xBitmap bitmap image that will be used for the surface
+ * @param rDevice reference to the device into which we want to draw
+ * @param data will be filled with alpha data, if xBitmap is alpha/transparent image
+ * @param bHasAlpha will be set to true if resulting surface has alpha
+ *
+ * This function tries various methods for creating a surface from xBitmap. It also uses
+ * the helper function surfaceFromXBitmap( xBitmap, bHasAlpha )
+ *
+ * @return created surface or NULL
+ **/
+ static SurfaceSharedPtr surfaceFromXBitmap( const uno::Reference< rendering::XBitmap >& xBitmap, const SurfaceProviderRef& rSurfaceProvider, unsigned char*& data, bool& bHasAlpha )
+ {
+ bHasAlpha = xBitmap->hasAlpha();
+ SurfaceSharedPtr pSurface = surfaceFromXBitmap( xBitmap );
+ if( pSurface )
+ data = nullptr;
+ else
+ {
+ ::BitmapEx aBmpEx = bitmapExFromXBitmap(xBitmap);
+ ::Bitmap aBitmap = aBmpEx.GetBitmap();
+
+ // there's no pixmap for alpha bitmap. we might still
+ // use rgb pixmap and only access alpha pixels the
+ // slow way. now we just speedup rgb bitmaps
+ if( !aBmpEx.IsAlpha() )
+ {
+ pSurface = rSurfaceProvider->createSurface( aBitmap );
+ data = nullptr;
+ bHasAlpha = false;
+ }
+
+ if( !pSurface )
+ {
+ tools::Long nWidth;
+ tools::Long nHeight;
+ vcl::bitmap::CanvasCairoExtractBitmapData(aBmpEx, aBitmap, data, bHasAlpha, nWidth, nHeight);
+
+ pSurface = rSurfaceProvider->getOutputDevice()->CreateSurface(
+ CairoSurfaceSharedPtr(
+ cairo_image_surface_create_for_data(
+ data,
+ bHasAlpha ? CAIRO_FORMAT_ARGB32 : CAIRO_FORMAT_RGB24,
+ nWidth, nHeight, nWidth*4 ),
+ &cairo_surface_destroy) );
+
+ SAL_INFO( "canvas.cairo","image: " << nWidth << " x " << nHeight << " alpha: " << bHasAlpha);
+ }
+ }
+
+ return pSurface;
+ }
+
+ static void addColorStops( cairo_pattern_t* pPattern, const uno::Sequence< uno::Sequence< double > >& rColors, const uno::Sequence< double >& rStops, bool bReverseStops )
+ {
+ int i;
+
+ OSL_ASSERT( rColors.getLength() == rStops.getLength() );
+
+ for( i = 0; i < rColors.getLength(); i++ )
+ {
+ const uno::Sequence< double >& rColor( rColors[i] );
+ float stop = bReverseStops ? 1 - rStops[i] : rStops[i];
+ if( rColor.getLength() == 3 )
+ cairo_pattern_add_color_stop_rgb( pPattern, stop, rColor[0], rColor[1], rColor[2] );
+ else if( rColor.getLength() == 4 )
+ {
+ double alpha = rColor[3];
+ // cairo expects premultiplied alpha
+ cairo_pattern_add_color_stop_rgba( pPattern, stop, rColor[0]*alpha, rColor[1]*alpha, rColor[2]*alpha, alpha );
+ }
+ }
+ }
+
+ static uno::Sequence<double> lerp(const uno::Sequence<double>& rLeft, const uno::Sequence<double>& rRight, double fAlpha)
+ {
+ if( rLeft.getLength() == 3 )
+ {
+ return
+ {
+ basegfx::utils::lerp(rLeft[0],rRight[0],fAlpha),
+ basegfx::utils::lerp(rLeft[1],rRight[1],fAlpha),
+ basegfx::utils::lerp(rLeft[2],rRight[2],fAlpha)
+ };
+ }
+ else if( rLeft.getLength() == 4 )
+ {
+ return
+ {
+ basegfx::utils::lerp(rLeft[0],rRight[0],fAlpha),
+ basegfx::utils::lerp(rLeft[1],rRight[1],fAlpha),
+ basegfx::utils::lerp(rLeft[2],rRight[2],fAlpha),
+ basegfx::utils::lerp(rLeft[3],rRight[3],fAlpha)
+ };
+ }
+
+ return {};
+ }
+
+ static cairo_pattern_t* patternFromParametricPolyPolygon( ::canvas::ParametricPolyPolygon const & rPolygon )
+ {
+ cairo_pattern_t* pPattern = nullptr;
+ const ::canvas::ParametricPolyPolygon::Values aValues = rPolygon.getValues();
+ double x0, x1, y0, y1, cx, cy, r0, r1;
+
+ switch( aValues.meType )
+ {
+ case ::canvas::ParametricPolyPolygon::GradientType::Linear:
+ x0 = 0;
+ y0 = 0;
+ x1 = 1;
+ y1 = 0;
+ pPattern = cairo_pattern_create_linear( x0, y0, x1, y1 );
+ addColorStops( pPattern, aValues.maColors, aValues.maStops, false );
+ break;
+
+ case ::canvas::ParametricPolyPolygon::GradientType::Elliptical:
+ cx = 0;
+ cy = 0;
+ r0 = 0;
+ r1 = 1;
+
+ pPattern = cairo_pattern_create_radial( cx, cy, r0, cy, cy, r1 );
+ addColorStops( pPattern, aValues.maColors, aValues.maStops, true );
+ break;
+ default:
+ break;
+ }
+
+ return pPattern;
+ }
+
+ static void doOperation( Operation aOperation,
+ cairo_t* pCairo,
+ const uno::Sequence< rendering::Texture >* pTextures,
+ const SurfaceProviderRef& pDevice,
+ const basegfx::B2DRange& rBounds )
+ {
+ switch( aOperation )
+ {
+ case Fill:
+ /* TODO: multitexturing */
+ if( pTextures )
+ {
+ const css::rendering::Texture& aTexture ( (*pTextures)[0] );
+ if( aTexture.Bitmap.is() )
+ {
+ unsigned char* data = nullptr;
+ bool bHasAlpha = false;
+ SurfaceSharedPtr pSurface = surfaceFromXBitmap( (*pTextures)[0].Bitmap, pDevice, data, bHasAlpha );
+
+ if( pSurface )
+ {
+ cairo_pattern_t* pPattern;
+
+ cairo_save( pCairo );
+
+ css::geometry::AffineMatrix2D aTransform( aTexture.AffineTransform );
+ cairo_matrix_t aScaleMatrix, aTextureMatrix, aScaledTextureMatrix;
+
+ cairo_matrix_init( &aTextureMatrix,
+ aTransform.m00, aTransform.m10, aTransform.m01,
+ aTransform.m11, aTransform.m02, aTransform.m12);
+
+ geometry::IntegerSize2D aSize = aTexture.Bitmap->getSize();
+
+ cairo_matrix_init_scale( &aScaleMatrix, 1.0/aSize.Width, 1.0/aSize.Height );
+ cairo_matrix_multiply( &aScaledTextureMatrix, &aTextureMatrix, &aScaleMatrix );
+ cairo_matrix_invert( &aScaledTextureMatrix );
+
+ // we don't care about repeat mode yet, so the workaround is disabled for now
+ pPattern = cairo_pattern_create_for_surface( pSurface->getCairoSurface().get() );
+
+ if( aTexture.RepeatModeX == rendering::TexturingMode::REPEAT &&
+ aTexture.RepeatModeY == rendering::TexturingMode::REPEAT )
+ {
+ cairo_pattern_set_extend( pPattern, CAIRO_EXTEND_REPEAT );
+ }
+ else if ( aTexture.RepeatModeX == rendering::TexturingMode::NONE &&
+ aTexture.RepeatModeY == rendering::TexturingMode::NONE )
+ {
+ cairo_pattern_set_extend( pPattern, CAIRO_EXTEND_NONE );
+ }
+ else if ( aTexture.RepeatModeX == rendering::TexturingMode::CLAMP &&
+ aTexture.RepeatModeY == rendering::TexturingMode::CLAMP )
+ {
+ cairo_pattern_set_extend( pPattern, CAIRO_EXTEND_PAD );
+ }
+
+ aScaledTextureMatrix.x0 = basegfx::fround( aScaledTextureMatrix.x0 );
+ aScaledTextureMatrix.y0 = basegfx::fround( aScaledTextureMatrix.y0 );
+
+ double x1, y1, x2, y2;
+ cairo_path_extents(pCairo, &x1, &y1, &x2, &y2);
+ aScaledTextureMatrix.x0 -= (x1 * aScaledTextureMatrix.xx);
+ aScaledTextureMatrix.y0 -= (y1 * aScaledTextureMatrix.yy);
+
+ cairo_pattern_set_matrix( pPattern, &aScaledTextureMatrix );
+
+ cairo_set_source( pCairo, pPattern );
+ if( !bHasAlpha )
+ cairo_set_operator( pCairo, CAIRO_OPERATOR_SOURCE );
+ cairo_fill( pCairo );
+
+ cairo_restore( pCairo );
+
+ cairo_pattern_destroy( pPattern );
+ }
+
+ if( data )
+ free( data );
+ }
+ else if( aTexture.Gradient.is() )
+ {
+ uno::Reference< lang::XServiceInfo > xRef( aTexture.Gradient, uno::UNO_QUERY );
+
+ SAL_INFO( "canvas.cairo", "gradient fill" );
+ if( xRef.is() && xRef->getImplementationName() == PARAMETRICPOLYPOLYGON_IMPLEMENTATION_NAME )
+ {
+ // TODO(Q1): Maybe use dynamic_cast here
+
+ // TODO(E1): Return value
+ // TODO(F1): FillRule
+ SAL_INFO( "canvas.cairo", "known implementation" );
+
+ ::canvas::ParametricPolyPolygon* pPolyImpl = static_cast< ::canvas::ParametricPolyPolygon* >( aTexture.Gradient.get() );
+ css::geometry::AffineMatrix2D aTransform( aTexture.AffineTransform );
+ cairo_matrix_t aTextureMatrix;
+
+ cairo_matrix_init( &aTextureMatrix,
+ aTransform.m00, aTransform.m10, aTransform.m01,
+ aTransform.m11, aTransform.m02, aTransform.m12);
+ if( pPolyImpl->getValues().meType == canvas::ParametricPolyPolygon::GradientType::Rectangular )
+ {
+ // no general path gradient yet in cairo; emulate then
+ cairo_save( pCairo );
+ cairo_clip( pCairo );
+
+ // fill bound rect with start color
+ cairo_rectangle( pCairo, rBounds.getMinX(), rBounds.getMinY(),
+ rBounds.getWidth(), rBounds.getHeight() );
+ setColor(pCairo,pPolyImpl->getValues().maColors[0]);
+ cairo_fill(pCairo);
+
+ cairo_transform( pCairo, &aTextureMatrix );
+
+ // longest line in gradient bound rect
+ const unsigned int nGradientSize(
+ static_cast<unsigned int>(
+ ::basegfx::B2DVector(rBounds.getMinimum() - rBounds.getMaximum()).getLength() + 1.0 ) );
+
+ // typical number for pixel of the same color (strip size)
+ const unsigned int nStripSize( nGradientSize < 50 ? 2 : 4 );
+
+ // use at least three steps, and at utmost the number of color
+ // steps
+ const unsigned int nStepCount(
+ std::max(
+ 3U,
+ std::min(
+ nGradientSize / nStripSize,
+ 128U )) + 1 );
+
+ const uno::Sequence<double>* pColors=&pPolyImpl->getValues().maColors[0];
+ basegfx::utils::KeyStopLerp aLerper(pPolyImpl->getValues().maStops);
+ for( unsigned int i=1; i<nStepCount; ++i )
+ {
+ const double fT( i/double(nStepCount) );
+
+ std::ptrdiff_t nIndex;
+ double fAlpha;
+ std::tie(nIndex,fAlpha)=aLerper.lerp(fT);
+
+ setColor(pCairo, lerp(pColors[nIndex], pColors[nIndex+1], fAlpha));
+ cairo_rectangle( pCairo, -1+fT, -1+fT, 2-2*fT, 2-2*fT );
+ cairo_fill(pCairo);
+ }
+
+ cairo_restore( pCairo );
+ }
+ else
+ {
+ cairo_pattern_t* pPattern = patternFromParametricPolyPolygon( *pPolyImpl );
+
+ if( pPattern )
+ {
+ SAL_INFO( "canvas.cairo", "filling with pattern" );
+
+ cairo_save( pCairo );
+
+ cairo_transform( pCairo, &aTextureMatrix );
+ cairo_set_source( pCairo, pPattern );
+ cairo_fill( pCairo );
+ cairo_restore( pCairo );
+
+ cairo_pattern_destroy( pPattern );
+ }
+ }
+ }
+ }
+ }
+ else
+ cairo_fill( pCairo );
+ SAL_INFO( "canvas.cairo", "fill");
+ break;
+ case Stroke:
+ cairo_stroke( pCairo );
+ SAL_INFO( "canvas.cairo", "stroke");
+ break;
+ case Clip:
+ cairo_clip( pCairo );
+ SAL_INFO( "canvas.cairo", "clip");
+ break;
+ }
+ }
+
+ static void clipNULL( cairo_t *pCairo )
+ {
+ SAL_INFO( "canvas.cairo", "clipNULL");
+ cairo_matrix_t aOrigMatrix, aIdentityMatrix;
+
+ /* we set identity matrix here to overcome bug in cairo 0.9.2
+ where XCreatePixmap is called with zero width and height.
+
+ it also reaches faster path in cairo clipping code.
+ */
+ cairo_matrix_init_identity( &aIdentityMatrix );
+ cairo_get_matrix( pCairo, &aOrigMatrix );
+ cairo_set_matrix( pCairo, &aIdentityMatrix );
+
+ cairo_reset_clip( pCairo );
+ cairo_rectangle( pCairo, 0, 0, 1, 1 );
+ cairo_clip( pCairo );
+ cairo_rectangle( pCairo, 2, 0, 1, 1 );
+ cairo_clip( pCairo );
+
+ /* restore the original matrix */
+ cairo_set_matrix( pCairo, &aOrigMatrix );
+ }
+
+ void doPolyPolygonImplementation( const ::basegfx::B2DPolyPolygon& aPolyPolygon,
+ Operation aOperation,
+ cairo_t* pCairo,
+ const uno::Sequence< rendering::Texture >* pTextures,
+ const SurfaceProviderRef& pDevice,
+ rendering::FillRule eFillrule )
+ {
+ if( pTextures )
+ ENSURE_ARG_OR_THROW( pTextures->hasElements(),
+ "CanvasHelper::fillTexturedPolyPolygon: empty texture sequence");
+
+ bool bOpToDo = false;
+ cairo_matrix_t aOrigMatrix, aIdentityMatrix;
+ double nX, nY, nBX, nBY, nAX, nAY, nLastX(0.0), nLastY(0.0);
+
+ cairo_get_matrix( pCairo, &aOrigMatrix );
+ cairo_matrix_init_identity( &aIdentityMatrix );
+ cairo_set_matrix( pCairo, &aIdentityMatrix );
+
+ cairo_set_fill_rule( pCairo,
+ eFillrule == rendering::FillRule_EVEN_ODD ?
+ CAIRO_FILL_RULE_EVEN_ODD : CAIRO_FILL_RULE_WINDING );
+
+ for( sal_uInt32 nPolygonIndex = 0; nPolygonIndex < aPolyPolygon.count(); nPolygonIndex++ )
+ {
+ const ::basegfx::B2DPolygon& aPolygon( aPolyPolygon.getB2DPolygon( nPolygonIndex ) );
+ const sal_uInt32 nPointCount( aPolygon.count() );
+ // to correctly render closed curves, need to output first
+ // point twice (so output one additional point)
+ const sal_uInt32 nExtendedPointCount( nPointCount +
+ int(aPolygon.isClosed() && aPolygon.areControlPointsUsed()) );
+
+ if( nPointCount > 1)
+ {
+ bool bIsBezier = aPolygon.areControlPointsUsed();
+ ::basegfx::B2DPoint aA, aB, aP;
+
+ for( sal_uInt32 j=0; j < nExtendedPointCount; j++ )
+ {
+ aP = aPolygon.getB2DPoint( j % nPointCount );
+
+ nX = aP.getX();
+ nY = aP.getY();
+ cairo_matrix_transform_point( &aOrigMatrix, &nX, &nY );
+
+ if (!bIsBezier && aOperation == Clip)
+ {
+ nX = basegfx::fround( nX );
+ nY = basegfx::fround( nY );
+ }
+
+ if( aOperation == Stroke )
+ {
+ nX += 0.5;
+ nY += 0.5;
+ }
+
+ if( j==0 )
+ {
+ cairo_move_to( pCairo, nX, nY );
+ SAL_INFO( "canvas.cairo", "move to " << nX << "," << nY );
+ }
+ else
+ {
+ if( bIsBezier )
+ {
+ aA = aPolygon.getNextControlPoint( (j-1) % nPointCount );
+ aB = aPolygon.getPrevControlPoint( j % nPointCount );
+
+ nAX = aA.getX();
+ nAY = aA.getY();
+ nBX = aB.getX();
+ nBY = aB.getY();
+
+ cairo_matrix_transform_point( &aOrigMatrix, &nAX, &nAY );
+ cairo_matrix_transform_point( &aOrigMatrix, &nBX, &nBY );
+
+ if( aOperation == Stroke )
+ {
+ nAX += 0.5;
+ nAY += 0.5;
+ nBX += 0.5;
+ nBY += 0.5;
+ }
+
+ // tdf#99165 if the control points are 'empty', create the mathematical
+ // correct replacement ones to avoid problems with the graphical sub-system
+ // tdf#101026 The 1st attempt to create a mathematically correct replacement control
+ // vector was wrong. Best alternative is one as close as possible which means short.
+ if (basegfx::fTools::equal(nAX, nLastX) && basegfx::fTools::equal(nAY, nLastY))
+ {
+ nAX = nLastX + ((nBX - nLastX) * 0.0005);
+ nAY = nLastY + ((nBY - nLastY) * 0.0005);
+ }
+
+ if(basegfx::fTools::equal(nBX, nX) && basegfx::fTools::equal(nBY, nY))
+ {
+ nBX = nX + ((nAX - nX) * 0.0005);
+ nBY = nY + ((nAY - nY) * 0.0005);
+ }
+
+ cairo_curve_to( pCairo, nAX, nAY, nBX, nBY, nX, nY );
+ }
+ else
+ {
+ cairo_line_to( pCairo, nX, nY );
+ SAL_INFO( "canvas.cairo", "line to " << nX << "," << nY );
+ }
+ bOpToDo = true;
+ }
+
+ nLastX = nX;
+ nLastY = nY;
+ }
+
+ if( aPolygon.isClosed() )
+ cairo_close_path( pCairo );
+
+ }
+ else
+ {
+ SAL_INFO( "canvas.cairo", "empty polygon for op: " << aOperation );
+ if( aOperation == Clip )
+ {
+ clipNULL( pCairo );
+
+ return;
+ }
+ }
+ }
+
+ if( aOperation == Fill && pTextures )
+ {
+ cairo_set_matrix( pCairo, &aOrigMatrix );
+ doOperation( aOperation, pCairo, pTextures, pDevice, aPolyPolygon.getB2DRange() );
+ cairo_set_matrix( pCairo, &aIdentityMatrix );
+ }
+
+ if( bOpToDo && ( aOperation != Fill || !pTextures ) )
+ doOperation( aOperation, pCairo, pTextures, pDevice, aPolyPolygon.getB2DRange() );
+
+ cairo_set_matrix( pCairo, &aOrigMatrix );
+
+ if( aPolyPolygon.count() == 0 && aOperation == Clip )
+ clipNULL( pCairo );
+ }
+
+ void CanvasHelper::doPolyPolygonPath( const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon,
+ Operation aOperation,
+ bool bNoLineJoin,
+ const uno::Sequence< rendering::Texture >* pTextures ) const
+ {
+ const ::basegfx::B2DPolyPolygon& rPolyPoly(
+ ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(xPolyPolygon) );
+
+ cairo_t* pCairo = mpCairo.get();
+
+ if(bNoLineJoin && aOperation == Stroke)
+ {
+ // emulate rendering::PathJoinType::NONE by painting single edges
+ for(sal_uInt32 a(0); a < rPolyPoly.count(); a++)
+ {
+ const basegfx::B2DPolygon& aCandidate(rPolyPoly.getB2DPolygon(a));
+ const sal_uInt32 nPointCount(aCandidate.count());
+
+ if(nPointCount)
+ {
+ const sal_uInt32 nEdgeCount(aCandidate.isClosed() ? nPointCount: nPointCount - 1);
+ basegfx::B2DPolygon aEdge;
+ aEdge.append(aCandidate.getB2DPoint(0));
+ aEdge.append(basegfx::B2DPoint(0.0, 0.0));
+
+ for(sal_uInt32 b(0); b < nEdgeCount; b++)
+ {
+ const sal_uInt32 nNextIndex((b + 1) % nPointCount);
+ aEdge.setB2DPoint(1, aCandidate.getB2DPoint(nNextIndex));
+ aEdge.setNextControlPoint(0, aCandidate.getNextControlPoint(b % nPointCount));
+ aEdge.setPrevControlPoint(1, aCandidate.getPrevControlPoint(nNextIndex));
+
+ doPolyPolygonImplementation( basegfx::B2DPolyPolygon(aEdge),
+ aOperation,
+ pCairo, pTextures,
+ mpSurfaceProvider,
+ xPolyPolygon->getFillRule() );
+
+ // prepare next step
+ aEdge.setB2DPoint(0, aEdge.getB2DPoint(1));
+ }
+ }
+ }
+ }
+ else
+ {
+ doPolyPolygonImplementation( rPolyPoly, aOperation,
+ pCairo, pTextures,
+ mpSurfaceProvider,
+ xPolyPolygon->getFillRule() );
+ }
+ }
+
+ uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawPolyPolygon( const rendering::XCanvas* ,
+ const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState )
+ {
+#ifdef CAIRO_CANVAS_PERF_TRACE
+ struct timespec aTimer;
+ mxDevice->startPerfTrace( &aTimer );
+#endif
+
+ if( mpCairo )
+ {
+ cairo_save( mpCairo.get() );
+
+ cairo_set_line_width( mpCairo.get(), 1 );
+
+ useStates( viewState, renderState, true );
+ doPolyPolygonPath( xPolyPolygon, Stroke );
+
+ cairo_restore( mpCairo.get() );
+ }
+ else
+ SAL_INFO( "canvas.cairo", "CanvasHelper called after it was disposed");
+
+#ifdef CAIRO_CANVAS_PERF_TRACE
+ mxDevice->stopPerfTrace( &aTimer, "drawPolyPolygon" );
+#endif
+
+ return uno::Reference< rendering::XCachedPrimitive >(nullptr);
+ }
+
+ uno::Reference< rendering::XCachedPrimitive > CanvasHelper::strokePolyPolygon( const rendering::XCanvas* ,
+ const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState,
+ const rendering::StrokeAttributes& strokeAttributes )
+ {
+#ifdef CAIRO_CANVAS_PERF_TRACE
+ struct timespec aTimer;
+ mxDevice->startPerfTrace( &aTimer );
+#endif
+
+ if( mpCairo )
+ {
+ cairo_save( mpCairo.get() );
+
+ useStates( viewState, renderState, true );
+
+ cairo_matrix_t aMatrix;
+ cairo_get_matrix( mpCairo.get(), &aMatrix );
+ double scaleFactorX = 1;
+ double scaleFactorY = 0;
+ cairo_matrix_transform_distance( &aMatrix, &scaleFactorX, &scaleFactorY );
+ double scaleFactor = basegfx::B2DVector( scaleFactorX, scaleFactorY ).getLength();
+ cairo_set_line_width( mpCairo.get(), strokeAttributes.StrokeWidth * scaleFactor );
+
+ cairo_set_miter_limit( mpCairo.get(), strokeAttributes.MiterLimit );
+
+ // FIXME: cairo doesn't handle end cap so far (rodo)
+ switch( strokeAttributes.StartCapType )
+ {
+ case rendering::PathCapType::BUTT:
+ cairo_set_line_cap( mpCairo.get(), CAIRO_LINE_CAP_BUTT );
+ break;
+ case rendering::PathCapType::ROUND:
+ cairo_set_line_cap( mpCairo.get(), CAIRO_LINE_CAP_ROUND );
+ break;
+ case rendering::PathCapType::SQUARE:
+ cairo_set_line_cap( mpCairo.get(), CAIRO_LINE_CAP_SQUARE );
+ break;
+ }
+
+ bool bNoLineJoin(false);
+
+ switch( strokeAttributes.JoinType )
+ {
+ case rendering::PathJoinType::NONE:
+ bNoLineJoin = true;
+ [[fallthrough]]; // cairo doesn't have join type NONE so we use MITER as it's pretty close
+ case rendering::PathJoinType::MITER:
+ cairo_set_line_join( mpCairo.get(), CAIRO_LINE_JOIN_MITER );
+ break;
+ case rendering::PathJoinType::ROUND:
+ cairo_set_line_join( mpCairo.get(), CAIRO_LINE_JOIN_ROUND );
+ break;
+ case rendering::PathJoinType::BEVEL:
+ cairo_set_line_join( mpCairo.get(), CAIRO_LINE_JOIN_BEVEL );
+ break;
+ }
+
+ //tdf#103026 If the scaling is 0, then all dashes become zero so
+ //cairo will set the cairo_t status to CAIRO_STATUS_INVALID_DASH
+ //and no further drawing will occur
+ if (strokeAttributes.DashArray.hasElements() && scaleFactor > 0.0)
+ {
+ auto aDashArray(comphelper::sequenceToContainer<std::vector<double>>(strokeAttributes.DashArray));
+ for (auto& rDash : aDashArray)
+ rDash *= scaleFactor;
+ cairo_set_dash(mpCairo.get(), aDashArray.data(), aDashArray.size(), 0);
+ }
+
+ // TODO(rodo) use LineArray of strokeAttributes
+
+ doPolyPolygonPath( xPolyPolygon, Stroke, bNoLineJoin );
+
+ cairo_restore( mpCairo.get() );
+ }
+ else
+ SAL_INFO( "canvas.cairo", "CanvasHelper called after it was disposed");
+
+#ifdef CAIRO_CANVAS_PERF_TRACE
+ mxDevice->stopPerfTrace( &aTimer, "strokePolyPolygon" );
+#endif
+
+ // TODO(P1): Provide caching here.
+ return uno::Reference< rendering::XCachedPrimitive >(nullptr);
+ }
+
+ uno::Reference< rendering::XCachedPrimitive > CanvasHelper::strokeTexturedPolyPolygon( const rendering::XCanvas* ,
+ 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* ,
+ 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* ,
+ 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* ,
+ const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState )
+ {
+#ifdef CAIRO_CANVAS_PERF_TRACE
+ struct timespec aTimer;
+ mxDevice->startPerfTrace( &aTimer );
+#endif
+
+ if( mpCairo )
+ {
+ cairo_save( mpCairo.get() );
+
+ useStates( viewState, renderState, true );
+ doPolyPolygonPath( xPolyPolygon, Fill );
+
+ cairo_restore( mpCairo.get() );
+ }
+ else
+ SAL_INFO( "canvas.cairo", "CanvasHelper called after it was disposed");
+
+#ifdef CAIRO_CANVAS_PERF_TRACE
+ mxDevice->stopPerfTrace( &aTimer, "fillPolyPolygon" );
+#endif
+
+ return uno::Reference< rendering::XCachedPrimitive >(nullptr);
+ }
+
+ uno::Reference< rendering::XCachedPrimitive > CanvasHelper::fillTexturedPolyPolygon( const rendering::XCanvas* ,
+ const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState,
+ const uno::Sequence< rendering::Texture >& textures )
+ {
+ if( mpCairo )
+ {
+ cairo_save( mpCairo.get() );
+
+ useStates( viewState, renderState, true );
+ doPolyPolygonPath( xPolyPolygon, Fill, false, &textures );
+
+ cairo_restore( mpCairo.get() );
+ }
+
+ return uno::Reference< rendering::XCachedPrimitive >(nullptr);
+ }
+
+ uno::Reference< rendering::XCachedPrimitive > CanvasHelper::fillTextureMappedPolyPolygon( const rendering::XCanvas* ,
+ 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::XCachedPrimitive > CanvasHelper::implDrawBitmapSurface( const rendering::XCanvas* pCanvas,
+ const SurfaceSharedPtr& pInputSurface,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState,
+ const geometry::IntegerSize2D& rSize,
+ bool bModulateColors,
+ bool bHasAlpha )
+ {
+ SurfaceSharedPtr pSurface=pInputSurface;
+ uno::Reference< rendering::XCachedPrimitive > rv;
+ geometry::IntegerSize2D aBitmapSize = rSize;
+
+ if( mpCairo )
+ {
+ cairo_save( mpCairo.get() );
+
+ cairo_rectangle( mpCairo.get(), 0, 0, maSize.getX(), maSize.getY() );
+ cairo_clip( mpCairo.get() );
+
+ useStates( viewState, renderState, true );
+
+ cairo_matrix_t aMatrix;
+
+ cairo_get_matrix( mpCairo.get(), &aMatrix );
+ if( ! ::rtl::math::approxEqual( aMatrix.xx, 1 ) &&
+ ! ::rtl::math::approxEqual( aMatrix.yy, 1 ) &&
+ ::rtl::math::approxEqual( aMatrix.x0, 0 ) &&
+ ::rtl::math::approxEqual( aMatrix.y0, 0 ) &&
+ basegfx::fround( rSize.Width * aMatrix.xx ) > 8 &&
+ basegfx::fround( rSize.Height* aMatrix.yy ) > 8 )
+ {
+ double dWidth, dHeight;
+
+ dWidth = basegfx::fround( rSize.Width * aMatrix.xx );
+ dHeight = basegfx::fround( rSize.Height* aMatrix.yy );
+ aBitmapSize.Width = static_cast<sal_Int32>( dWidth );
+ aBitmapSize.Height = static_cast<sal_Int32>( dHeight );
+
+ SurfaceSharedPtr pScaledSurface = mpSurfaceProvider->createSurface(
+ ::basegfx::B2ISize( aBitmapSize.Width, aBitmapSize.Height ),
+ bHasAlpha ? CAIRO_CONTENT_COLOR_ALPHA : CAIRO_CONTENT_COLOR );
+ CairoSharedPtr pCairo = pScaledSurface->getCairo();
+
+ cairo_set_operator( pCairo.get(), CAIRO_OPERATOR_SOURCE );
+ // add 0.5px to size to avoid rounding errors in cairo, leading sometimes to random data on the image right/bottom borders
+ cairo_scale( pCairo.get(), (dWidth+0.5)/rSize.Width, (dHeight+0.5)/rSize.Height );
+ cairo_set_source_surface( pCairo.get(), pSurface->getCairoSurface().get(), 0, 0 );
+ cairo_paint( pCairo.get() );
+
+ pSurface = pScaledSurface;
+
+ aMatrix.xx = aMatrix.yy = 1;
+ cairo_set_matrix( mpCairo.get(), &aMatrix );
+
+ rv.set(
+ new CachedBitmap( pSurface, viewState, renderState,
+ // cast away const, need to
+ // change refcount (as this is
+ // ~invisible to client code,
+ // still logically const)
+ const_cast< rendering::XCanvas* >(pCanvas)) );
+ }
+
+ if( !bHasAlpha && mbHaveAlpha )
+ {
+ double x, y, width, height;
+
+ x = y = 0;
+ width = aBitmapSize.Width;
+ height = aBitmapSize.Height;
+ cairo_matrix_transform_point( &aMatrix, &x, &y );
+ cairo_matrix_transform_distance( &aMatrix, &width, &height );
+
+ // in case the bitmap doesn't have alpha and covers whole area
+ // we try to change surface to plain rgb
+ SAL_INFO( "canvas.cairo","chance to change surface to rgb, " << x << ", " << y << ", " << width << " x " << height << " (" << maSize.getX() << " x " << maSize.getY() << ")" );
+ if( x <= 0 && y <= 0 && x + width >= maSize.getX() && y + height >= maSize.getY() )
+ {
+ SAL_INFO( "canvas.cairo","trying to change surface to rgb");
+ if( mpSurfaceProvider ) {
+ SurfaceSharedPtr pNewSurface = mpSurfaceProvider->changeSurface();
+
+ if( pNewSurface )
+ setSurface( pNewSurface, false );
+
+ // set state to new mpCairo.get()
+ useStates( viewState, renderState, true );
+ // use the possibly modified matrix
+ cairo_set_matrix( mpCairo.get(), &aMatrix );
+ }
+ }
+ }
+
+ cairo_set_source_surface( mpCairo.get(), pSurface->getCairoSurface().get(), 0, 0 );
+ if( !bHasAlpha &&
+ ::rtl::math::approxEqual( aMatrix.xx, 1 ) &&
+ ::rtl::math::approxEqual( aMatrix.yy, 1 ) &&
+ ::rtl::math::approxEqual( aMatrix.x0, 0 ) &&
+ ::rtl::math::approxEqual( aMatrix.y0, 0 ) )
+ cairo_set_operator( mpCairo.get(), CAIRO_OPERATOR_SOURCE );
+ cairo_pattern_set_extend( cairo_get_source(mpCairo.get()), CAIRO_EXTEND_PAD );
+ cairo_rectangle( mpCairo.get(), 0, 0, aBitmapSize.Width, aBitmapSize.Height );
+ cairo_clip( mpCairo.get() );
+
+ // Use cairo_matrix_transform_distance() to determine the scaling, as that works even if
+ // the matrix also has rotation.
+ double fPixelWidth = rSize.Width;
+ double fPixelHeight = rSize.Height;
+ cairo_matrix_transform_distance(&aMatrix, &fPixelWidth, &fPixelHeight);
+ int nPixelWidth = std::round(fPixelWidth);
+ int nPixelHeight = std::round(fPixelHeight);
+ if (std::abs(nPixelWidth) > 0 && std::abs(nPixelHeight) > 0)
+ {
+ // Only render the image if it's at least 1x1 px sized.
+ if (bModulateColors)
+ cairo_paint_with_alpha(mpCairo.get(), renderState.DeviceColor[3]);
+ else
+ {
+ cairo_paint(mpCairo.get());
+ if (cairo_status(mpCairo.get()) != CAIRO_STATUS_SUCCESS)
+ {
+ SAL_WARN("canvas.cairo", "cairo_paint() failed: " << cairo_status_to_string(
+ cairo_status(mpCairo.get())));
+ }
+ }
+ }
+ cairo_restore( mpCairo.get() );
+ }
+ else
+ SAL_INFO( "canvas.cairo", "CanvasHelper called after it was disposed");
+
+ return rv; // uno::Reference< rendering::XCachedPrimitive >(NULL);
+ }
+
+ uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawBitmap( const rendering::XCanvas* pCanvas,
+ const uno::Reference< rendering::XBitmap >& xBitmap,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState )
+ {
+#ifdef CAIRO_CANVAS_PERF_TRACE
+ struct timespec aTimer;
+ mxDevice->startPerfTrace( &aTimer );
+#endif
+
+ uno::Reference< rendering::XCachedPrimitive > rv;
+ unsigned char* data = nullptr;
+ bool bHasAlpha = false;
+ SurfaceSharedPtr pSurface = surfaceFromXBitmap( xBitmap, mpSurfaceProvider, data, bHasAlpha );
+ geometry::IntegerSize2D aSize = xBitmap->getSize();
+
+ if( pSurface )
+ {
+ rv = implDrawBitmapSurface( pCanvas, pSurface, viewState, renderState, aSize, false, bHasAlpha );
+
+ if( data )
+ free( data );
+ }
+ else
+ rv.set(nullptr);
+
+#ifdef CAIRO_CANVAS_PERF_TRACE
+ mxDevice->stopPerfTrace( &aTimer, "drawBitmap" );
+#endif
+
+ return rv;
+ }
+
+ uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawBitmapModulated( const rendering::XCanvas* pCanvas,
+ const uno::Reference< rendering::XBitmap >& xBitmap,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState )
+ {
+#ifdef CAIRO_CANVAS_PERF_TRACE
+ struct timespec aTimer;
+ mxDevice->startPerfTrace( &aTimer );
+#endif
+
+ uno::Reference< rendering::XCachedPrimitive > rv;
+ unsigned char* data = nullptr;
+ bool bHasAlpha = false;
+ SurfaceSharedPtr pSurface = surfaceFromXBitmap( xBitmap, mpSurfaceProvider, data, bHasAlpha );
+ geometry::IntegerSize2D aSize = xBitmap->getSize();
+
+ if( pSurface )
+ {
+ rv = implDrawBitmapSurface( pCanvas, pSurface, viewState, renderState, aSize, true, bHasAlpha );
+
+ if( data )
+ free( data );
+ }
+ else
+ rv.set(nullptr);
+
+#ifdef CAIRO_CANVAS_PERF_TRACE
+ mxDevice->stopPerfTrace( &aTimer, "drawBitmap" );
+#endif
+
+ return rv;
+ }
+
+
+ geometry::IntegerSize2D CanvasHelper::getSize() const
+ {
+ if( !mpSurfaceProvider )
+ return geometry::IntegerSize2D(1, 1); // we're disposed
+
+ return ::basegfx::unotools::integerSize2DFromB2ISize( maSize );
+ }
+
+ uno::Reference< rendering::XBitmap > CanvasHelper::getScaledBitmap( const geometry::RealSize2D& newSize,
+ bool /*beFast*/ )
+ {
+#ifdef CAIRO_CANVAS_PERF_TRACE
+ struct timespec aTimer;
+ mxDevice->startPerfTrace( &aTimer );
+#endif
+
+ if( mpCairo )
+ {
+ return uno::Reference< rendering::XBitmap >( new CanvasBitmap( ::basegfx::B2ISize( ::canvas::tools::roundUp( newSize.Width ),
+ ::canvas::tools::roundUp( newSize.Height ) ),
+ mpSurfaceProvider, mpDevice, false ) );
+ }
+ else
+ SAL_INFO( "canvas.cairo", "CanvasHelper called after it was disposed");
+
+#ifdef CAIRO_CANVAS_PERF_TRACE
+ mxDevice->stopPerfTrace( &aTimer, "getScaledBitmap" );
+#endif
+
+ return uno::Reference< rendering::XBitmap >();
+ }
+
+ uno::Sequence< sal_Int8 > CanvasHelper::getData( rendering::IntegerBitmapLayout& aLayout,
+ const geometry::IntegerRectangle2D& rect )
+ {
+ if( mpCairo )
+ {
+ const sal_Int32 nWidth( rect.X2 - rect.X1 );
+ const sal_Int32 nHeight( rect.Y2 - rect.Y1 );
+ const cairo_format_t eFormat( mbHaveAlpha ? CAIRO_FORMAT_ARGB32 : CAIRO_FORMAT_RGB24 );
+ uno::Sequence< sal_Int8 > aRes( 4*nWidth*nHeight );
+ sal_Int8* pData = aRes.getArray();
+ cairo_surface_t* pImageSurface = cairo_image_surface_create_for_data( reinterpret_cast<unsigned char *>(pData),
+ eFormat,
+ nWidth, nHeight, 4*nWidth );
+ cairo_t* pCairo = cairo_create( pImageSurface );
+ cairo_set_source_surface( pCairo, mpSurface->getCairoSurface().get(), -rect.X1, -rect.Y1);
+ cairo_paint( pCairo );
+ cairo_destroy( pCairo );
+ cairo_surface_destroy( pImageSurface );
+
+ aLayout = impl_getMemoryLayout( nWidth, nHeight );
+
+ return aRes;
+ }
+
+ return uno::Sequence< sal_Int8 >();
+ }
+
+ uno::Sequence< sal_Int8 > CanvasHelper::getPixel( rendering::IntegerBitmapLayout& /*bitmapLayout*/,
+ const geometry::IntegerPoint2D& /*pos*/ )
+ {
+ return uno::Sequence< sal_Int8 >();
+ }
+
+ namespace
+ {
+ class CairoColorSpace : public cppu::WeakImplHelper< css::rendering::XIntegerBitmapColorSpace >
+ {
+ private:
+ uno::Sequence< sal_Int8 > maComponentTags;
+ uno::Sequence< sal_Int32 > maBitCounts;
+
+ virtual ::sal_Int8 SAL_CALL getType( ) override
+ {
+ return rendering::ColorSpaceType::RGB;
+ }
+ virtual uno::Sequence< ::sal_Int8 > SAL_CALL getComponentTags( ) override
+ {
+ return maComponentTags;
+ }
+ virtual ::sal_Int8 SAL_CALL getRenderingIntent( ) override
+ {
+ return rendering::RenderingIntent::PERCEPTUAL;
+ }
+ virtual uno::Sequence< beans::PropertyValue > SAL_CALL getProperties( ) override
+ {
+ return uno::Sequence< beans::PropertyValue >();
+ }
+ virtual uno::Sequence< double > SAL_CALL convertColorSpace( const uno::Sequence< double >& deviceColor,
+ const uno::Reference< rendering::XColorSpace >& targetColorSpace ) override
+ {
+ // TODO(P3): if we know anything about target
+ // colorspace, this can be greatly sped up
+ uno::Sequence<rendering::ARGBColor> aIntermediate(
+ convertToARGB(deviceColor));
+ return targetColorSpace->convertFromARGB(aIntermediate);
+ }
+ virtual uno::Sequence< rendering::RGBColor > SAL_CALL convertToRGB( const uno::Sequence< double >& deviceColor ) override
+ {
+ const double* pIn( deviceColor.getConstArray() );
+ const std::size_t nLen( deviceColor.getLength() );
+ ENSURE_ARG_OR_THROW2(nLen%4==0,
+ "number of channels no multiple of 4",
+ static_cast<rendering::XColorSpace*>(this), 0);
+
+ uno::Sequence< rendering::RGBColor > aRes(nLen/4);
+ rendering::RGBColor* pOut( aRes.getArray() );
+ for( std::size_t i=0; i<nLen; i+=4 )
+ {
+ const double fAlpha(pIn[3]);
+ if( fAlpha == 0.0 )
+ *pOut++ = rendering::RGBColor(0.0, 0.0, 0.0);
+ else
+ *pOut++ = rendering::RGBColor(pIn[2]/fAlpha,pIn[1]/fAlpha,pIn[0]/fAlpha);
+ pIn += 4;
+ }
+ return aRes;
+ }
+ virtual uno::Sequence< rendering::ARGBColor > SAL_CALL convertToARGB( const uno::Sequence< double >& deviceColor ) override
+ {
+ const double* pIn( deviceColor.getConstArray() );
+ const std::size_t nLen( deviceColor.getLength() );
+ ENSURE_ARG_OR_THROW2(nLen%4==0,
+ "number of channels no multiple of 4",
+ static_cast<rendering::XColorSpace*>(this), 0);
+
+ uno::Sequence< rendering::ARGBColor > aRes(nLen/4);
+ rendering::ARGBColor* pOut( aRes.getArray() );
+ for( std::size_t i=0; i<nLen; i+=4 )
+ {
+ const double fAlpha(pIn[3]);
+ if( fAlpha == 0.0 )
+ *pOut++ = rendering::ARGBColor(0.0, 0.0, 0.0, 0.0);
+ else
+ *pOut++ = rendering::ARGBColor(fAlpha,pIn[2]/fAlpha,pIn[1]/fAlpha,pIn[0]/fAlpha);
+ pIn += 4;
+ }
+ return aRes;
+ }
+ virtual uno::Sequence< rendering::ARGBColor > SAL_CALL convertToPARGB( const uno::Sequence< double >& deviceColor ) override
+ {
+ const double* pIn( deviceColor.getConstArray() );
+ const std::size_t nLen( deviceColor.getLength() );
+ ENSURE_ARG_OR_THROW2(nLen%4==0,
+ "number of channels no multiple of 4",
+ static_cast<rendering::XColorSpace*>(this), 0);
+
+ uno::Sequence< rendering::ARGBColor > aRes(nLen/4);
+ rendering::ARGBColor* pOut( aRes.getArray() );
+ for( std::size_t i=0; i<nLen; i+=4 )
+ {
+ *pOut++ = rendering::ARGBColor(pIn[3],pIn[2],pIn[1],pIn[1]);
+ pIn += 4;
+ }
+ return aRes;
+ }
+ virtual uno::Sequence< double > SAL_CALL convertFromRGB( const uno::Sequence< rendering::RGBColor >& rgbColor ) override
+ {
+ const rendering::RGBColor* pIn( rgbColor.getConstArray() );
+ const std::size_t nLen( rgbColor.getLength() );
+
+ uno::Sequence< double > aRes(nLen*4);
+ double* pColors=aRes.getArray();
+ for( std::size_t i=0; i<nLen; ++i )
+ {
+ *pColors++ = pIn->Blue;
+ *pColors++ = pIn->Green;
+ *pColors++ = pIn->Red;
+ *pColors++ = 1.0;
+ ++pIn;
+ }
+ return aRes;
+ }
+ virtual uno::Sequence< double > SAL_CALL convertFromARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor ) override
+ {
+ const rendering::ARGBColor* pIn( rgbColor.getConstArray() );
+ const std::size_t nLen( rgbColor.getLength() );
+
+ uno::Sequence< double > aRes(nLen*4);
+ double* pColors=aRes.getArray();
+ for( std::size_t i=0; i<nLen; ++i )
+ {
+ *pColors++ = pIn->Alpha*pIn->Blue;
+ *pColors++ = pIn->Alpha*pIn->Green;
+ *pColors++ = pIn->Alpha*pIn->Red;
+ *pColors++ = pIn->Alpha;
+ ++pIn;
+ }
+ return aRes;
+ }
+ virtual uno::Sequence< double > SAL_CALL convertFromPARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor ) override
+ {
+ const rendering::ARGBColor* pIn( rgbColor.getConstArray() );
+ const std::size_t nLen( rgbColor.getLength() );
+
+ uno::Sequence< double > aRes(nLen*4);
+ double* pColors=aRes.getArray();
+ for( std::size_t i=0; i<nLen; ++i )
+ {
+ *pColors++ = pIn->Blue;
+ *pColors++ = pIn->Green;
+ *pColors++ = pIn->Red;
+ *pColors++ = pIn->Alpha;
+ ++pIn;
+ }
+ return aRes;
+ }
+
+ // XIntegerBitmapColorSpace
+ virtual ::sal_Int32 SAL_CALL getBitsPerPixel( ) override
+ {
+ return 32;
+ }
+ virtual uno::Sequence< ::sal_Int32 > SAL_CALL getComponentBitCounts( ) override
+ {
+ return maBitCounts;
+ }
+ virtual ::sal_Int8 SAL_CALL getEndianness( ) override
+ {
+ return util::Endianness::LITTLE;
+ }
+ virtual uno::Sequence<double> SAL_CALL convertFromIntegerColorSpace( const uno::Sequence< ::sal_Int8 >& deviceColor,
+ const uno::Reference< rendering::XColorSpace >& targetColorSpace ) override
+ {
+ if( dynamic_cast<CairoColorSpace*>(targetColorSpace.get()) )
+ {
+ const sal_Int8* pIn( deviceColor.getConstArray() );
+ const std::size_t nLen( deviceColor.getLength() );
+ ENSURE_ARG_OR_THROW2(nLen%4==0,
+ "number of channels no multiple of 4",
+ static_cast<rendering::XColorSpace*>(this), 0);
+
+ uno::Sequence<double> aRes(nLen);
+ double* pOut( aRes.getArray() );
+ for( std::size_t i=0; i<nLen; i+=4 )
+ {
+ *pOut++ = vcl::unotools::toDoubleColor(*pIn++);
+ *pOut++ = vcl::unotools::toDoubleColor(*pIn++);
+ *pOut++ = vcl::unotools::toDoubleColor(*pIn++);
+ *pOut++ = vcl::unotools::toDoubleColor(*pIn++);
+ }
+ return aRes;
+ }
+ else
+ {
+ // TODO(P3): if we know anything about target
+ // colorspace, this can be greatly sped up
+ uno::Sequence<rendering::ARGBColor> aIntermediate(
+ convertIntegerToARGB(deviceColor));
+ return targetColorSpace->convertFromARGB(aIntermediate);
+ }
+ }
+ virtual uno::Sequence< ::sal_Int8 > SAL_CALL convertToIntegerColorSpace( const uno::Sequence< ::sal_Int8 >& deviceColor,
+ const uno::Reference< rendering::XIntegerBitmapColorSpace >& targetColorSpace ) override
+ {
+ if( dynamic_cast<CairoColorSpace*>(targetColorSpace.get()) )
+ {
+ // it's us, so simply pass-through the data
+ return deviceColor;
+ }
+ else
+ {
+ // TODO(P3): if we know anything about target
+ // colorspace, this can be greatly sped up
+ uno::Sequence<rendering::ARGBColor> aIntermediate(
+ convertIntegerToARGB(deviceColor));
+ return targetColorSpace->convertIntegerFromARGB(aIntermediate);
+ }
+ }
+ virtual uno::Sequence< rendering::RGBColor > SAL_CALL convertIntegerToRGB( const uno::Sequence< ::sal_Int8 >& deviceColor ) override
+ {
+ const sal_Int8* pIn( deviceColor.getConstArray() );
+ const std::size_t nLen( deviceColor.getLength() );
+ ENSURE_ARG_OR_THROW2(nLen%4==0,
+ "number of channels no multiple of 4",
+ static_cast<rendering::XColorSpace*>(this), 0);
+
+ uno::Sequence< rendering::RGBColor > aRes(nLen/4);
+ rendering::RGBColor* pOut( aRes.getArray() );
+ for( std::size_t i=0; i<nLen; i+=4 )
+ {
+ const double fAlpha(static_cast<sal_uInt8>(pIn[3]));
+ if( fAlpha )
+ *pOut++ = rendering::RGBColor(
+ pIn[2]/fAlpha,
+ pIn[1]/fAlpha,
+ pIn[0]/fAlpha);
+ else
+ *pOut++ = rendering::RGBColor(0,0,0);
+ pIn += 4;
+ }
+ return aRes;
+ }
+
+ virtual uno::Sequence< rendering::ARGBColor > SAL_CALL convertIntegerToARGB( const uno::Sequence< ::sal_Int8 >& deviceColor ) override
+ {
+ const sal_Int8* pIn( deviceColor.getConstArray() );
+ const std::size_t nLen( deviceColor.getLength() );
+ ENSURE_ARG_OR_THROW2(nLen%4==0,
+ "number of channels no multiple of 4",
+ static_cast<rendering::XColorSpace*>(this), 0);
+
+ uno::Sequence< rendering::ARGBColor > aRes(nLen/4);
+ rendering::ARGBColor* pOut( aRes.getArray() );
+ for( std::size_t i=0; i<nLen; i+=4 )
+ {
+ const double fAlpha(static_cast<sal_uInt8>(pIn[3]));
+ if( fAlpha )
+ *pOut++ = rendering::ARGBColor(
+ fAlpha/255.0,
+ pIn[2]/fAlpha,
+ pIn[1]/fAlpha,
+ pIn[0]/fAlpha);
+ else
+ *pOut++ = rendering::ARGBColor(0,0,0,0);
+ pIn += 4;
+ }
+ return aRes;
+ }
+ virtual uno::Sequence< rendering::ARGBColor > SAL_CALL convertIntegerToPARGB( const uno::Sequence< ::sal_Int8 >& deviceColor ) override
+ {
+ const sal_Int8* pIn( deviceColor.getConstArray() );
+ const std::size_t nLen( deviceColor.getLength() );
+ ENSURE_ARG_OR_THROW2(nLen%4==0,
+ "number of channels no multiple of 4",
+ static_cast<rendering::XColorSpace*>(this), 0);
+
+ uno::Sequence< rendering::ARGBColor > aRes(nLen/4);
+ rendering::ARGBColor* pOut( aRes.getArray() );
+ for( std::size_t i=0; i<nLen; i+=4 )
+ {
+ *pOut++ = rendering::ARGBColor(
+ vcl::unotools::toDoubleColor(pIn[3]),
+ vcl::unotools::toDoubleColor(pIn[2]),
+ vcl::unotools::toDoubleColor(pIn[1]),
+ vcl::unotools::toDoubleColor(pIn[0]));
+ pIn += 4;
+ }
+ return aRes;
+ }
+
+ virtual uno::Sequence< ::sal_Int8 > SAL_CALL convertIntegerFromRGB( const uno::Sequence< rendering::RGBColor >& rgbColor ) override
+ {
+ const rendering::RGBColor* pIn( rgbColor.getConstArray() );
+ const std::size_t nLen( rgbColor.getLength() );
+
+ uno::Sequence< sal_Int8 > aRes(nLen*4);
+ sal_Int8* pColors=aRes.getArray();
+ for( std::size_t i=0; i<nLen; ++i )
+ {
+ *pColors++ = vcl::unotools::toByteColor(pIn->Blue);
+ *pColors++ = vcl::unotools::toByteColor(pIn->Green);
+ *pColors++ = vcl::unotools::toByteColor(pIn->Red);
+ *pColors++ = -1;
+ ++pIn;
+ }
+ return aRes;
+ }
+
+ virtual uno::Sequence< ::sal_Int8 > SAL_CALL convertIntegerFromARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor ) override
+ {
+ const rendering::ARGBColor* pIn( rgbColor.getConstArray() );
+ const std::size_t nLen( rgbColor.getLength() );
+
+ uno::Sequence< sal_Int8 > aRes(nLen*4);
+ sal_Int8* pColors=aRes.getArray();
+ for( std::size_t i=0; i<nLen; ++i )
+ {
+ const double fAlpha(pIn->Alpha);
+ *pColors++ = vcl::unotools::toByteColor(fAlpha*pIn->Blue);
+ *pColors++ = vcl::unotools::toByteColor(fAlpha*pIn->Green);
+ *pColors++ = vcl::unotools::toByteColor(fAlpha*pIn->Red);
+ *pColors++ = vcl::unotools::toByteColor(fAlpha);
+ ++pIn;
+ }
+ return aRes;
+ }
+ virtual uno::Sequence< ::sal_Int8 > SAL_CALL convertIntegerFromPARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor ) override
+ {
+ const rendering::ARGBColor* pIn( rgbColor.getConstArray() );
+ const std::size_t nLen( rgbColor.getLength() );
+
+ uno::Sequence< sal_Int8 > aRes(nLen*4);
+ sal_Int8* pColors=aRes.getArray();
+ for( std::size_t i=0; i<nLen; ++i )
+ {
+ *pColors++ = vcl::unotools::toByteColor(pIn->Blue);
+ *pColors++ = vcl::unotools::toByteColor(pIn->Green);
+ *pColors++ = vcl::unotools::toByteColor(pIn->Red);
+ *pColors++ = vcl::unotools::toByteColor(pIn->Alpha);
+ ++pIn;
+ }
+ return aRes;
+ }
+
+ public:
+ CairoColorSpace() :
+ maComponentTags(4),
+ maBitCounts(4)
+ {
+ sal_Int8* pTags = maComponentTags.getArray();
+ sal_Int32* pBitCounts = maBitCounts.getArray();
+ pTags[0] = rendering::ColorComponentTag::RGB_BLUE;
+ pTags[1] = rendering::ColorComponentTag::RGB_GREEN;
+ pTags[2] = rendering::ColorComponentTag::RGB_RED;
+ pTags[3] = rendering::ColorComponentTag::PREMULTIPLIED_ALPHA;
+
+ pBitCounts[0] =
+ pBitCounts[1] =
+ pBitCounts[2] =
+ pBitCounts[3] = 8;
+ }
+ };
+
+ class CairoNoAlphaColorSpace : public cppu::WeakImplHelper< css::rendering::XIntegerBitmapColorSpace >
+ {
+ private:
+ uno::Sequence< sal_Int8 > maComponentTags;
+ uno::Sequence< sal_Int32 > maBitCounts;
+
+ virtual ::sal_Int8 SAL_CALL getType( ) override
+ {
+ return rendering::ColorSpaceType::RGB;
+ }
+ virtual uno::Sequence< ::sal_Int8 > SAL_CALL getComponentTags( ) override
+ {
+ return maComponentTags;
+ }
+ virtual ::sal_Int8 SAL_CALL getRenderingIntent( ) override
+ {
+ return rendering::RenderingIntent::PERCEPTUAL;
+ }
+ virtual uno::Sequence< beans::PropertyValue > SAL_CALL getProperties( ) override
+ {
+ return uno::Sequence< beans::PropertyValue >();
+ }
+ virtual uno::Sequence< double > SAL_CALL convertColorSpace( const uno::Sequence< double >& deviceColor,
+ const uno::Reference< rendering::XColorSpace >& targetColorSpace ) override
+ {
+ // TODO(P3): if we know anything about target
+ // colorspace, this can be greatly sped up
+ uno::Sequence<rendering::ARGBColor> aIntermediate(
+ convertToARGB(deviceColor));
+ return targetColorSpace->convertFromARGB(aIntermediate);
+ }
+ virtual uno::Sequence< rendering::RGBColor > SAL_CALL convertToRGB( const uno::Sequence< double >& deviceColor ) override
+ {
+ const double* pIn( deviceColor.getConstArray() );
+ const std::size_t nLen( deviceColor.getLength() );
+ ENSURE_ARG_OR_THROW2(nLen%4==0,
+ "number of channels no multiple of 4",
+ static_cast<rendering::XColorSpace*>(this), 0);
+
+ uno::Sequence< rendering::RGBColor > aRes(nLen/4);
+ rendering::RGBColor* pOut( aRes.getArray() );
+ for( std::size_t i=0; i<nLen; i+=4 )
+ {
+ *pOut++ = rendering::RGBColor(pIn[2], pIn[1], pIn[0]);
+ pIn += 4;
+ }
+ return aRes;
+ }
+ uno::Sequence< rendering::ARGBColor > impl_convertToARGB( const uno::Sequence< double >& deviceColor )
+ {
+ const double* pIn( deviceColor.getConstArray() );
+ const std::size_t nLen( deviceColor.getLength() );
+ ENSURE_ARG_OR_THROW2(nLen%4==0,
+ "number of channels no multiple of 4",
+ static_cast<rendering::XColorSpace*>(this), 0);
+
+ uno::Sequence< rendering::ARGBColor > aRes(nLen/4);
+ rendering::ARGBColor* pOut( aRes.getArray() );
+ for( std::size_t i=0; i<nLen; i+=4 )
+ {
+ *pOut++ = rendering::ARGBColor(1.0, pIn[2], pIn[1], pIn[0]);
+ pIn += 4;
+ }
+ return aRes;
+ }
+ virtual uno::Sequence< rendering::ARGBColor > SAL_CALL convertToARGB( const uno::Sequence< double >& deviceColor ) override
+ {
+ return impl_convertToARGB( deviceColor );
+ }
+ virtual uno::Sequence< rendering::ARGBColor > SAL_CALL convertToPARGB( const uno::Sequence< double >& deviceColor ) override
+ {
+ return impl_convertToARGB( deviceColor );
+ }
+ virtual uno::Sequence< double > SAL_CALL convertFromRGB( const uno::Sequence< rendering::RGBColor >& rgbColor ) override
+ {
+ const rendering::RGBColor* pIn( rgbColor.getConstArray() );
+ const std::size_t nLen( rgbColor.getLength() );
+
+ uno::Sequence< double > aRes(nLen*4);
+ double* pColors=aRes.getArray();
+ for( std::size_t i=0; i<nLen; ++i )
+ {
+ *pColors++ = pIn->Blue;
+ *pColors++ = pIn->Green;
+ *pColors++ = pIn->Red;
+ *pColors++ = 1.0; // the value does not matter
+ ++pIn;
+ }
+ return aRes;
+ }
+ uno::Sequence< double > impl_convertFromARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor )
+ {
+ const rendering::ARGBColor* pIn( rgbColor.getConstArray() );
+ const std::size_t nLen( rgbColor.getLength() );
+
+ uno::Sequence< double > aRes(nLen*4);
+ double* pColors=aRes.getArray();
+ for( std::size_t i=0; i<nLen; ++i )
+ {
+ *pColors++ = pIn->Blue;
+ *pColors++ = pIn->Green;
+ *pColors++ = pIn->Red;
+ *pColors++ = 1.0; // the value does not matter
+ ++pIn;
+ }
+ return aRes;
+ }
+ virtual uno::Sequence< double > SAL_CALL convertFromARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor ) override
+ {
+ return impl_convertFromARGB( rgbColor );
+ }
+ virtual uno::Sequence< double > SAL_CALL convertFromPARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor ) override
+ {
+ return impl_convertFromARGB( rgbColor );
+ }
+
+ // XIntegerBitmapColorSpace
+ virtual ::sal_Int32 SAL_CALL getBitsPerPixel( ) override
+ {
+ return 32;
+ }
+ virtual uno::Sequence< ::sal_Int32 > SAL_CALL getComponentBitCounts( ) override
+ {
+ return maBitCounts;
+ }
+ virtual ::sal_Int8 SAL_CALL getEndianness( ) override
+ {
+ return util::Endianness::LITTLE;
+ }
+ virtual uno::Sequence<double> SAL_CALL convertFromIntegerColorSpace( const uno::Sequence< ::sal_Int8 >& deviceColor,
+ const uno::Reference< rendering::XColorSpace >& targetColorSpace ) override
+ {
+ if( dynamic_cast<CairoColorSpace*>(targetColorSpace.get()) )
+ {
+ const sal_Int8* pIn( deviceColor.getConstArray() );
+ const std::size_t nLen( deviceColor.getLength() );
+ ENSURE_ARG_OR_THROW2(nLen%4==0,
+ "number of channels no multiple of 4",
+ static_cast<rendering::XColorSpace*>(this), 0);
+
+ uno::Sequence<double> aRes(nLen);
+ double* pOut( aRes.getArray() );
+ for( std::size_t i=0; i<nLen; i+=4 )
+ {
+ *pOut++ = vcl::unotools::toDoubleColor(*pIn++);
+ *pOut++ = vcl::unotools::toDoubleColor(*pIn++);
+ *pOut++ = vcl::unotools::toDoubleColor(*pIn++);
+ *pOut++ = 1.0; pIn++; // the value does not matter
+ }
+ return aRes;
+ }
+ else
+ {
+ // TODO(P3): if we know anything about target
+ // colorspace, this can be greatly sped up
+ uno::Sequence<rendering::ARGBColor> aIntermediate(
+ convertIntegerToARGB(deviceColor));
+ return targetColorSpace->convertFromARGB(aIntermediate);
+ }
+ }
+ virtual uno::Sequence< ::sal_Int8 > SAL_CALL convertToIntegerColorSpace( const uno::Sequence< ::sal_Int8 >& deviceColor,
+ const uno::Reference< rendering::XIntegerBitmapColorSpace >& targetColorSpace ) override
+ {
+ if( dynamic_cast<CairoNoAlphaColorSpace*>(targetColorSpace.get()) )
+ {
+ // it's us, so simply pass-through the data
+ return deviceColor;
+ }
+ else
+ {
+ // TODO(P3): if we know anything about target
+ // colorspace, this can be greatly sped up
+ uno::Sequence<rendering::ARGBColor> aIntermediate(
+ convertIntegerToARGB(deviceColor));
+ return targetColorSpace->convertIntegerFromARGB(aIntermediate);
+ }
+ }
+ virtual uno::Sequence< rendering::RGBColor > SAL_CALL convertIntegerToRGB( const uno::Sequence< ::sal_Int8 >& deviceColor ) override
+ {
+ const sal_Int8* pIn( deviceColor.getConstArray() );
+ const std::size_t nLen( deviceColor.getLength() );
+ ENSURE_ARG_OR_THROW2(nLen%4==0,
+ "number of channels no multiple of 4",
+ static_cast<rendering::XColorSpace*>(this), 0);
+
+ uno::Sequence< rendering::RGBColor > aRes(nLen/4);
+ rendering::RGBColor* pOut( aRes.getArray() );
+ for( std::size_t i=0; i<nLen; i+=4 )
+ {
+ *pOut++ = rendering::RGBColor( pIn[2], pIn[1], pIn[0] );
+ pIn += 4;
+ }
+ return aRes;
+ }
+
+ virtual uno::Sequence< rendering::ARGBColor > SAL_CALL convertIntegerToARGB( const uno::Sequence< ::sal_Int8 >& deviceColor ) override
+ {
+ return impl_convertIntegerToARGB( deviceColor );
+ }
+ virtual uno::Sequence< rendering::ARGBColor > SAL_CALL convertIntegerToPARGB( const uno::Sequence< ::sal_Int8 >& deviceColor ) override
+ {
+ return impl_convertIntegerToARGB( deviceColor );
+ }
+ uno::Sequence< rendering::ARGBColor > impl_convertIntegerToARGB( const uno::Sequence< ::sal_Int8 >& deviceColor )
+ {
+ const sal_Int8* pIn( deviceColor.getConstArray() );
+ const std::size_t nLen( deviceColor.getLength() );
+ ENSURE_ARG_OR_THROW2(nLen%4==0,
+ "number of channels no multiple of 4",
+ static_cast<rendering::XColorSpace*>(this), 0);
+
+ uno::Sequence< rendering::ARGBColor > aRes(nLen/4);
+ rendering::ARGBColor* pOut( aRes.getArray() );
+ for( std::size_t i=0; i<nLen; i+=4 )
+ {
+ *pOut++ = rendering::ARGBColor(
+ 1.0,
+ vcl::unotools::toDoubleColor(pIn[2]),
+ vcl::unotools::toDoubleColor(pIn[1]),
+ vcl::unotools::toDoubleColor(pIn[0]));
+ pIn += 4;
+ }
+ return aRes;
+ }
+
+ virtual uno::Sequence< ::sal_Int8 > SAL_CALL convertIntegerFromRGB( const uno::Sequence< rendering::RGBColor >& rgbColor ) override
+ {
+ const rendering::RGBColor* pIn( rgbColor.getConstArray() );
+ const std::size_t nLen( rgbColor.getLength() );
+
+ uno::Sequence< sal_Int8 > aRes(nLen*4);
+ sal_Int8* pColors=aRes.getArray();
+ for( std::size_t i=0; i<nLen; ++i )
+ {
+ *pColors++ = vcl::unotools::toByteColor(pIn->Blue);
+ *pColors++ = vcl::unotools::toByteColor(pIn->Green);
+ *pColors++ = vcl::unotools::toByteColor(pIn->Red);
+ *pColors++ = -1; // the value does not matter
+ ++pIn;
+ }
+ return aRes;
+ }
+
+ virtual uno::Sequence< ::sal_Int8 > SAL_CALL convertIntegerFromARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor ) override
+ {
+ return impl_convertIntegerFromARGB( rgbColor );
+ }
+ virtual uno::Sequence< ::sal_Int8 > SAL_CALL convertIntegerFromPARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor ) override
+ {
+ return impl_convertIntegerFromARGB( rgbColor );
+ }
+ uno::Sequence< ::sal_Int8 > impl_convertIntegerFromARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor )
+ {
+ const rendering::ARGBColor* pIn( rgbColor.getConstArray() );
+ const std::size_t nLen( rgbColor.getLength() );
+
+ uno::Sequence< sal_Int8 > aRes(nLen*4);
+ sal_Int8* pColors=aRes.getArray();
+ for( std::size_t i=0; i<nLen; ++i )
+ {
+ *pColors++ = vcl::unotools::toByteColor(pIn->Blue);
+ *pColors++ = vcl::unotools::toByteColor(pIn->Green);
+ *pColors++ = vcl::unotools::toByteColor(pIn->Red);
+ *pColors++ = -1; // the value does not matter
+ ++pIn;
+ }
+ return aRes;
+ }
+
+ public:
+ CairoNoAlphaColorSpace() :
+ maComponentTags(3),
+ maBitCounts(3)
+ {
+ sal_Int8* pTags = maComponentTags.getArray();
+ sal_Int32* pBitCounts = maBitCounts.getArray();
+ pTags[0] = rendering::ColorComponentTag::RGB_BLUE;
+ pTags[1] = rendering::ColorComponentTag::RGB_GREEN;
+ pTags[2] = rendering::ColorComponentTag::RGB_RED;
+
+ pBitCounts[0] =
+ pBitCounts[1] =
+ pBitCounts[2] = 8;
+ }
+ };
+
+ uno::Reference<rendering::XIntegerBitmapColorSpace>& GetCairoNoAlphaColorSpace()
+ {
+ static uno::Reference<rendering::XIntegerBitmapColorSpace> SPACE = new CairoNoAlphaColorSpace();
+ return SPACE;
+ };
+
+ uno::Reference<rendering::XIntegerBitmapColorSpace>& GetCairoColorSpace()
+ {
+ static uno::Reference<rendering::XIntegerBitmapColorSpace> SPACE = new CairoColorSpace();
+ return SPACE;
+ };
+
+ }
+
+ rendering::IntegerBitmapLayout CanvasHelper::getMemoryLayout()
+ {
+ if( !mpCairo )
+ return rendering::IntegerBitmapLayout(); // we're disposed
+
+ const geometry::IntegerSize2D aSize(getSize());
+
+ return impl_getMemoryLayout( aSize.Width, aSize.Height );
+ }
+
+ rendering::IntegerBitmapLayout
+ CanvasHelper::impl_getMemoryLayout( const sal_Int32 nWidth, const sal_Int32 nHeight )
+ {
+ rendering::IntegerBitmapLayout aLayout;
+
+ aLayout.ScanLines = nHeight;
+ aLayout.ScanLineBytes = nWidth*4;
+ aLayout.ScanLineStride = aLayout.ScanLineBytes;
+ aLayout.PlaneStride = 0;
+ aLayout.ColorSpace = mbHaveAlpha ? GetCairoColorSpace() : GetCairoNoAlphaColorSpace();
+ aLayout.Palette.clear();
+ aLayout.IsMsbFirst = false;
+
+ return aLayout;
+ }
+
+
+ bool CanvasHelper::repaint( const SurfaceSharedPtr& pSurface,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState )
+ {
+ SAL_INFO( "canvas.cairo", "CanvasHelper::repaint");
+
+ if( !mpCairo )
+ return true;
+
+ cairo_save( mpCairo.get() );
+
+ cairo_rectangle( mpCairo.get(), 0, 0, maSize.getX(), maSize.getY() );
+ cairo_clip( mpCairo.get() );
+
+ useStates( viewState, renderState, true );
+
+ cairo_matrix_t aMatrix;
+
+ cairo_get_matrix( mpCairo.get(), &aMatrix );
+ aMatrix.xx = aMatrix.yy = 1;
+ cairo_set_matrix( mpCairo.get(), &aMatrix );
+
+ cairo_set_source_surface( mpCairo.get(), pSurface->getCairoSurface().get(), 0, 0 );
+ cairo_paint( mpCairo.get() );
+ cairo_restore( mpCairo.get() );
+
+ return true;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/cairo/cairo_canvashelper.hxx b/canvas/source/cairo/cairo_canvashelper.hxx
new file mode 100644
index 000000000..21dbf79d7
--- /dev/null
+++ b/canvas/source/cairo/cairo_canvashelper.hxx
@@ -0,0 +1,271 @@
+/* -*- 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/geometry/IntegerPoint2D.hpp>
+#include <com/sun/star/geometry/IntegerRectangle2D.hpp>
+#include <com/sun/star/rendering/IntegerBitmapLayout.hpp>
+#include <com/sun/star/rendering/XCanvas.hpp>
+
+#include <vcl/vclptr.hxx>
+#include <vcl/virdev.hxx>
+
+#include <vcl/cairo.hxx>
+#include "cairo_surfaceprovider.hxx"
+
+class VirtualDevice;
+
+namespace basegfx {
+ class B2DPolyPolygon;
+}
+
+namespace cairocanvas
+{
+ class SpriteCanvas;
+
+ enum Operation {
+ Stroke,
+ Fill,
+ Clip
+ };
+
+ class CanvasHelper
+ {
+ public:
+ /// make noncopyable
+ CanvasHelper(const CanvasHelper&) = delete;
+ const CanvasHelper& operator=(const CanvasHelper&) = delete;
+
+ CanvasHelper();
+
+ /// Release all references
+ void disposing();
+
+ /** Initialize canvas helper
+
+ This method late-initializes the canvas helper, providing
+ it with the necessary device and size. 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 rSizePixel
+ Size of the output surface in pixel.
+
+ @param rDevice
+ Reference device this canvas is associated with
+
+ */
+ void init( const ::basegfx::B2ISize& rSizePixel,
+ SurfaceProvider& rSurfaceProvider,
+ css::rendering::XGraphicDevice* pDevice );
+
+ void setSize( const ::basegfx::B2ISize& rSize );
+ void setSurface( const ::cairo::SurfaceSharedPtr& pSurface, bool bHasAlpha );
+
+ // CanvasHelper functionality
+ // ==========================
+
+ // XCanvas (only providing, not implementing the
+ // interface. Also note subtle method parameter differences)
+ void clear();
+ 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() { return css::uno::Reference< css::rendering::XGraphicDevice >(mpDevice); }
+
+ // BitmapCanvasHelper functionality
+ // ================================
+
+ css::geometry::IntegerSize2D getSize() const;
+
+ 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 );
+
+ css::uno::Sequence< sal_Int8 >
+ getPixel( css::rendering::IntegerBitmapLayout& bitmapLayout,
+ const css::geometry::IntegerPoint2D& pos );
+
+ css::rendering::IntegerBitmapLayout getMemoryLayout();
+
+ void doPolyPolygonPath( const css::uno::Reference< css::rendering::XPolyPolygon2D >& xPolyPolygon,
+ Operation aOperation,
+ bool bNoLineJoin = false,
+ const css::uno::Sequence< css::rendering::Texture >* pTextures=nullptr ) const;
+
+ css::uno::Reference< css::rendering::XCachedPrimitive > implDrawBitmapSurface(
+ const css::rendering::XCanvas* pCanvas,
+ const ::cairo::SurfaceSharedPtr& pSurface,
+ const css::rendering::ViewState& viewState,
+ const css::rendering::RenderState& renderState,
+ const css::geometry::IntegerSize2D& rSize,
+ bool bModulateColors,
+ bool bHasAlpha );
+
+ bool repaint( const ::cairo::SurfaceSharedPtr& pSurface,
+ const css::rendering::ViewState& viewState,
+ const css::rendering::RenderState& renderState );
+
+ protected:
+ /** Surface provider
+
+ Deliberately not a refcounted reference, because of
+ potential circular references for canvas. Provides us with
+ our output surface and associated functionality.
+ */
+ SurfaceProvider* mpSurfaceProvider;
+
+ /** Phyical output device
+
+ Deliberately not a refcounted reference, because of
+ potential circular references for spritecanvas.
+ */
+ css::rendering::XGraphicDevice* mpDevice;
+
+ private:
+
+ VclPtr<VirtualDevice> mpVirtualDevice;
+
+ void useStates( const css::rendering::ViewState& viewState,
+ const css::rendering::RenderState& renderState,
+ bool setColor );
+
+ css::rendering::IntegerBitmapLayout impl_getMemoryLayout( sal_Int32 nWidth, sal_Int32 nHeight );
+
+ /// When true, content is able to represent alpha
+ bool mbHaveAlpha;
+
+ ::cairo::CairoSharedPtr mpCairo;
+ ::cairo::SurfaceSharedPtr mpSurface;
+ ::basegfx::B2ISize maSize;
+ };
+
+ /// also needed from SpriteHelper
+ void doPolyPolygonImplementation( const ::basegfx::B2DPolyPolygon& aPolyPolygon,
+ Operation aOperation,
+ cairo_t* pCairo,
+ const css::uno::Sequence< css::rendering::Texture >* pTextures,
+ const SurfaceProviderRef& pDevice,
+ css::rendering::FillRule eFillrule );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/cairo/cairo_canvashelper_text.cxx b/canvas/source/cairo/cairo_canvashelper_text.cxx
new file mode 100644
index 000000000..5f78bb71a
--- /dev/null
+++ b/canvas/source/cairo/cairo_canvashelper_text.cxx
@@ -0,0 +1,300 @@
+/* -*- 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 <com/sun/star/rendering/TextDirection.hpp>
+
+#include <rtl/math.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <tools/diagnose_ex.h>
+#include <vcl/canvastools.hxx>
+#include <vcl/metric.hxx>
+#include <vcl/virdev.hxx>
+
+#include <canvas/canvastools.hxx>
+#include <verifyinput.hxx>
+
+#include "cairo_canvasfont.hxx"
+#include "cairo_canvashelper.hxx"
+#include "cairo_textlayout.hxx"
+
+using namespace ::cairo;
+using namespace ::com::sun::star;
+
+namespace cairocanvas
+{
+ uno::Reference< rendering::XCanvasFont > CanvasHelper::createFont( const rendering::XCanvas* ,
+ const rendering::FontRequest& fontRequest,
+ const uno::Sequence< beans::PropertyValue >& extraFontProperties,
+ const geometry::Matrix2D& fontMatrix )
+ {
+ return uno::Reference< rendering::XCanvasFont >( new CanvasFont( fontRequest, extraFontProperties, fontMatrix, mpSurfaceProvider ));
+ }
+
+ uno::Sequence< rendering::FontInfo > CanvasHelper::queryAvailableFonts( const rendering::XCanvas* ,
+ const rendering::FontInfo& /*aFilter*/,
+ const uno::Sequence< beans::PropertyValue >& /*aFontProperties*/ )
+ {
+ // TODO
+ return uno::Sequence< rendering::FontInfo >();
+ }
+
+ static bool
+ setupFontTransform( ::OutputDevice const & rOutDev,
+ ::Point& o_rPoint,
+ vcl::Font& io_rVCLFont,
+ const rendering::ViewState& rViewState,
+ const rendering::RenderState& rRenderState )
+ {
+ ::basegfx::B2DHomMatrix aMatrix;
+
+ ::canvas::tools::mergeViewAndRenderTransform(aMatrix,
+ rViewState,
+ rRenderState);
+
+ ::basegfx::B2DTuple aScale;
+ ::basegfx::B2DTuple aTranslate;
+ double nRotate, nShearX;
+
+ aMatrix.decompose( aScale, aTranslate, nRotate, nShearX );
+
+ // query font metric _before_ tampering with width and height
+ if( !::rtl::math::approxEqual(aScale.getX(), aScale.getY()) )
+ {
+ // retrieve true font width
+ const sal_Int32 nFontWidth( rOutDev.GetFontMetric( io_rVCLFont ).GetAverageFontWidth() );
+
+ const sal_Int32 nScaledFontWidth( ::basegfx::fround(nFontWidth * aScale.getX()) );
+
+ if( !nScaledFontWidth )
+ {
+ // scale is smaller than one pixel - disable text
+ // output altogether
+ return false;
+ }
+
+ io_rVCLFont.SetAverageFontWidth( nScaledFontWidth );
+ }
+
+ if( !::rtl::math::approxEqual(aScale.getY(), 1.0) )
+ {
+ const sal_Int32 nFontHeight( io_rVCLFont.GetFontHeight() );
+ io_rVCLFont.SetFontHeight( ::basegfx::fround(nFontHeight * aScale.getY()) );
+ }
+
+ io_rVCLFont.SetOrientation( Degree10( ::basegfx::fround(-basegfx::rad2deg<10>(fmod(nRotate, 2*M_PI))) ) );
+
+ // TODO(F2): Missing functionality in VCL: shearing
+ o_rPoint.setX( ::basegfx::fround(aTranslate.getX()) );
+ o_rPoint.setY( ::basegfx::fround(aTranslate.getY()) );
+
+ return true;
+ }
+
+ static void
+ setupOutDevState( OutputDevice& rOutDev,
+ const rendering::XCanvas* pOwner,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState )
+ {
+ ::canvas::tools::verifyInput( renderState,
+ __func__,
+ const_cast<rendering::XCanvas*>(pOwner), // only for refcount
+ 2,
+ 3 /* text */ );
+
+ // TODO(P2): Don't change clipping all the time, maintain current clip
+ // state and change only when update is necessary
+ ::canvas::tools::clipOutDev(viewState, renderState, rOutDev);
+
+ Color aColor( COL_WHITE );
+
+ if( renderState.DeviceColor.getLength() > 2 )
+ {
+ aColor = vcl::unotools::stdColorSpaceSequenceToColor( renderState.DeviceColor );
+ }
+
+ // extract alpha, and make color opaque
+ // afterwards. Otherwise, OutputDevice won't draw anything
+ aColor.SetAlpha(255);
+
+ rOutDev.SetTextColor( aColor );
+ }
+
+ namespace {
+
+ class DeviceSettingsGuard
+ {
+ private:
+ VclPtr<OutputDevice> mpVirtualDevice;
+ bool mbMappingWasEnabled;
+ public:
+ DeviceSettingsGuard(OutputDevice *pVirtualDevice)
+ : mpVirtualDevice(pVirtualDevice)
+ , mbMappingWasEnabled(mpVirtualDevice->IsMapModeEnabled())
+ {
+ mpVirtualDevice->Push();
+ mpVirtualDevice->EnableMapMode(false);
+ }
+
+ ~DeviceSettingsGuard()
+ {
+ mpVirtualDevice->EnableMapMode(mbMappingWasEnabled);
+ mpVirtualDevice->Pop();
+ }
+ };
+
+ }
+
+ static bool setupTextOutput( OutputDevice& rOutDev,
+ const rendering::XCanvas* pOwner,
+ ::Point& o_rOutPos,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState,
+ const uno::Reference< rendering::XCanvasFont >& xFont )
+ {
+ setupOutDevState( rOutDev, pOwner, viewState, renderState );
+
+ CanvasFont* pFont = dynamic_cast< CanvasFont* >( xFont.get() );
+
+ ENSURE_ARG_OR_THROW( pFont,
+ "CanvasHelper::setupTextOutput(): Font not compatible with this canvas" );
+
+ vcl::Font aVCLFont = pFont->getVCLFont();
+
+ Color aColor( COL_BLACK );
+
+ if( renderState.DeviceColor.getLength() > 2 )
+ {
+ aColor = vcl::unotools::stdColorSpaceSequenceToColor(renderState.DeviceColor );
+ }
+
+ // setup font color
+ aVCLFont.SetColor( aColor );
+ aVCLFont.SetFillColor( aColor );
+
+ // no need to replicate this for mp2ndOutDev, we're modifying only aVCLFont here.
+ if( !setupFontTransform( rOutDev, o_rOutPos, aVCLFont, viewState, renderState ) )
+ return false;
+
+ rOutDev.SetFont( aVCLFont );
+
+ return true;
+ }
+
+ uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawText( const rendering::XCanvas* pOwner,
+ const rendering::StringContext& text,
+ const uno::Reference< rendering::XCanvasFont >& xFont,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState,
+ sal_Int8 textDirection )
+ {
+#ifdef CAIRO_CANVAS_PERF_TRACE
+ struct timespec aTimer;
+ mxDevice->startPerfTrace( &aTimer );
+#endif
+
+ ENSURE_ARG_OR_THROW( xFont.is(),
+ "CanvasHelper::drawText(): font is NULL");
+
+ if( !mpVirtualDevice )
+ mpVirtualDevice = mpSurface->createVirtualDevice();
+
+ if( mpVirtualDevice )
+ {
+ DeviceSettingsGuard aGuard(mpVirtualDevice.get());
+
+ ::Point aOutpos;
+ if( !setupTextOutput( *mpVirtualDevice, pOwner, aOutpos, viewState, renderState, xFont ) )
+ return uno::Reference< rendering::XCachedPrimitive >(nullptr); // no output necessary
+
+ // change text direction and layout mode
+ vcl::text::ComplexTextLayoutFlags nLayoutMode(vcl::text::ComplexTextLayoutFlags::Default);
+ switch( textDirection )
+ {
+ case rendering::TextDirection::WEAK_LEFT_TO_RIGHT:
+ case rendering::TextDirection::STRONG_LEFT_TO_RIGHT:
+ nLayoutMode |= vcl::text::ComplexTextLayoutFlags::BiDiStrong;
+ nLayoutMode |= vcl::text::ComplexTextLayoutFlags::TextOriginLeft;
+ break;
+
+ case rendering::TextDirection::WEAK_RIGHT_TO_LEFT:
+ nLayoutMode |= vcl::text::ComplexTextLayoutFlags::BiDiRtl;
+ [[fallthrough]];
+ case rendering::TextDirection::STRONG_RIGHT_TO_LEFT:
+ nLayoutMode |= vcl::text::ComplexTextLayoutFlags::BiDiRtl | vcl::text::ComplexTextLayoutFlags::BiDiStrong;
+ nLayoutMode |= vcl::text::ComplexTextLayoutFlags::TextOriginRight;
+ break;
+ }
+
+ // TODO(F2): alpha
+ mpVirtualDevice->SetLayoutMode( nLayoutMode );
+
+ rtl::Reference pTextLayout( new TextLayout(text, textDirection, 0, CanvasFont::Reference(dynamic_cast< CanvasFont* >( xFont.get() )), mpSurfaceProvider) );
+ pTextLayout->draw(*mpVirtualDevice, aOutpos, viewState, renderState);
+ }
+
+ return uno::Reference< rendering::XCachedPrimitive >(nullptr);
+ }
+
+ uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawTextLayout( const rendering::XCanvas* pOwner,
+ const uno::Reference< rendering::XTextLayout >& xLayoutedText,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState )
+ {
+ ENSURE_ARG_OR_THROW( xLayoutedText.is(),
+ "CanvasHelper::drawTextLayout(): layout is NULL");
+
+ TextLayout* pTextLayout = dynamic_cast< TextLayout* >( xLayoutedText.get() );
+
+ if( pTextLayout )
+ {
+ if( !mpVirtualDevice )
+ mpVirtualDevice = mpSurface->createVirtualDevice();
+
+ if( mpVirtualDevice )
+ {
+ DeviceSettingsGuard aGuard(mpVirtualDevice.get());
+
+ // TODO(T3): Race condition. We're taking the font
+ // from xLayoutedText, and then calling draw() at it,
+ // without exclusive access. Move setupTextOutput(),
+ // e.g. to impltools?
+
+ ::Point aOutpos;
+ if( !setupTextOutput( *mpVirtualDevice, pOwner, aOutpos, viewState, renderState, xLayoutedText->getFont() ) )
+ return uno::Reference< rendering::XCachedPrimitive >(nullptr); // no output necessary
+
+ // TODO(F2): What about the offset scalings?
+ pTextLayout->draw(*mpVirtualDevice, aOutpos, viewState, renderState);
+ }
+ }
+ else
+ {
+ ENSURE_ARG_OR_THROW( false,
+ "CanvasHelper::drawTextLayout(): TextLayout not compatible with this canvas" );
+ }
+
+ return uno::Reference< rendering::XCachedPrimitive >(nullptr);
+ }
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/cairo/cairo_devicehelper.cxx b/canvas/source/cairo/cairo_devicehelper.cxx
new file mode 100644
index 000000000..5dacea7ae
--- /dev/null
+++ b/canvas/source/cairo/cairo_devicehelper.cxx
@@ -0,0 +1,257 @@
+/* -*- 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/utils/canvastools.hxx>
+#include <basegfx/utils/unopolypolygon.hxx>
+#include <tools/stream.hxx>
+#include <vcl/bitmapex.hxx>
+#include <vcl/canvastools.hxx>
+#include <vcl/dibtools.hxx>
+
+#include <canvas/canvastools.hxx>
+
+#include "cairo_canvasbitmap.hxx"
+#include "cairo_devicehelper.hxx"
+
+using namespace ::cairo;
+using namespace ::com::sun::star;
+
+namespace cairocanvas
+{
+ DeviceHelper::DeviceHelper() :
+ mpSurfaceProvider( nullptr ),
+ mpRefDevice( nullptr )
+ {
+ }
+
+ void DeviceHelper::implInit( SurfaceProvider& rSurfaceProvider,
+ OutputDevice& rRefDevice )
+ {
+ mpSurfaceProvider = &rSurfaceProvider;
+ mpRefDevice = &rRefDevice;
+
+ // no own surface, this is handled by derived classes
+ }
+
+ void DeviceHelper::init( SurfaceProvider& rSurfaceProvider,
+ OutputDevice& rRefDevice )
+ {
+ implInit(rSurfaceProvider, rRefDevice);
+
+ OutputDevice* pOutDev = getOutputDevice();
+ mpSurface = pOutDev->CreateSurface(pOutDev->GetOutOffXPixel(),
+ pOutDev->GetOutOffYPixel(),
+ pOutDev->GetOutputWidthPixel(),
+ pOutDev->GetOutputHeightPixel());
+ }
+
+ void DeviceHelper::disposing()
+ {
+ // release all references
+ mpSurface.reset();
+ mpRefDevice = nullptr;
+ mpSurfaceProvider = nullptr;
+ }
+
+ void DeviceHelper::setSize( const ::basegfx::B2ISize& rSize )
+ {
+ SAL_INFO(
+ "canvas.cairo",
+ "device size " << rSize.getX() << " x " << rSize.getY());
+
+ if( !mpRefDevice )
+ return; // disposed
+
+ OutputDevice* pOutDev = getOutputDevice();
+
+ // X11 only
+ bool bReuseSurface = mpSurface &&
+ mpSurface->Resize(rSize.getX() + pOutDev->GetOutOffXPixel(),
+ rSize.getY() + pOutDev->GetOutOffYPixel());
+
+ if (!bReuseSurface)
+ {
+ mpSurface = pOutDev->CreateSurface(
+ pOutDev->GetOutOffXPixel(),
+ pOutDev->GetOutOffYPixel(),
+ rSize.getX(), rSize.getY() );
+ }
+ }
+
+ geometry::RealSize2D DeviceHelper::getPhysicalResolution()
+ {
+ // Map a one-by-one millimeter box to pixel
+ const MapMode aOldMapMode( mpRefDevice->GetMapMode() );
+ mpRefDevice->SetMapMode( MapMode(MapUnit::MapMM) );
+ const Size aPixelSize( mpRefDevice->LogicToPixel(Size(1,1)) );
+ mpRefDevice->SetMapMode( aOldMapMode );
+
+ return vcl::unotools::size2DFromSize( aPixelSize );
+ }
+
+ geometry::RealSize2D DeviceHelper::getPhysicalSize()
+ {
+ if( !mpRefDevice )
+ return ::canvas::tools::createInfiniteSize2D(); // we're disposed
+
+ // Map the pixel dimensions of the output window to millimeter
+ const MapMode aOldMapMode( mpRefDevice->GetMapMode() );
+ mpRefDevice->SetMapMode( MapMode(MapUnit::MapMM) );
+ const Size aLogSize( mpRefDevice->PixelToLogic(mpRefDevice->GetOutputSizePixel()) );
+ mpRefDevice->SetMapMode( aOldMapMode );
+
+ return vcl::unotools::size2DFromSize( aLogSize );
+ }
+
+ uno::Reference< rendering::XLinePolyPolygon2D > DeviceHelper::createCompatibleLinePolyPolygon(
+ const uno::Reference< rendering::XGraphicDevice >& ,
+ const uno::Sequence< uno::Sequence< geometry::RealPoint2D > >& points )
+ {
+ // disposed?
+ if( !mpSurfaceProvider )
+ return uno::Reference< rendering::XLinePolyPolygon2D >(); // we're disposed
+
+ return uno::Reference< rendering::XLinePolyPolygon2D >(
+ new ::basegfx::unotools::UnoPolyPolygon(
+ ::basegfx::unotools::polyPolygonFromPoint2DSequenceSequence( points ) ) );
+ }
+
+ uno::Reference< rendering::XBezierPolyPolygon2D > DeviceHelper::createCompatibleBezierPolyPolygon(
+ const uno::Reference< rendering::XGraphicDevice >& ,
+ const uno::Sequence< uno::Sequence< geometry::RealBezierSegment2D > >& points )
+ {
+ // disposed?
+ if( !mpSurfaceProvider )
+ return uno::Reference< rendering::XBezierPolyPolygon2D >(); // we're disposed
+
+ return uno::Reference< rendering::XBezierPolyPolygon2D >(
+ new ::basegfx::unotools::UnoPolyPolygon(
+ ::basegfx::unotools::polyPolygonFromBezier2DSequenceSequence( points ) ) );
+ }
+
+ uno::Reference< rendering::XBitmap > DeviceHelper::createCompatibleBitmap(
+ const uno::Reference< rendering::XGraphicDevice >& rDevice,
+ const geometry::IntegerSize2D& size )
+ {
+ // disposed?
+ if( !mpSurfaceProvider )
+ return uno::Reference< rendering::XBitmap >(); // we're disposed
+
+ return uno::Reference< rendering::XBitmap >(
+ new CanvasBitmap(
+ ::basegfx::unotools::b2ISizeFromIntegerSize2D( size ),
+ SurfaceProviderRef(mpSurfaceProvider),
+ rDevice.get(),
+ false ));
+ }
+
+ uno::Reference< rendering::XVolatileBitmap > DeviceHelper::createVolatileBitmap(
+ const uno::Reference< rendering::XGraphicDevice >& ,
+ 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 )
+ {
+ // disposed?
+ if( !mpSurfaceProvider )
+ return uno::Reference< rendering::XBitmap >(); // we're disposed
+
+ return uno::Reference< rendering::XBitmap >(
+ new CanvasBitmap(
+ ::basegfx::unotools::b2ISizeFromIntegerSize2D( size ),
+ SurfaceProviderRef(mpSurfaceProvider),
+ rDevice.get(),
+ true ));
+ }
+
+ uno::Reference< rendering::XVolatileBitmap > DeviceHelper::createVolatileAlphaBitmap(
+ const uno::Reference< rendering::XGraphicDevice >& ,
+ 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 >(mpRefDevice.get()) );
+ }
+
+ uno::Any DeviceHelper::getSurfaceHandle() const
+ {
+ return uno::Any();
+ }
+
+ uno::Reference<rendering::XColorSpace> const & DeviceHelper::getColorSpace() const
+ {
+ static uno::Reference<rendering::XColorSpace> SPACE = vcl::unotools::createStandardColorSpace();
+ // always the same
+ return SPACE;
+ }
+
+ void DeviceHelper::dumpScreenContent() const
+ {
+ static sal_Int32 nFilePostfixCount(0);
+
+ if( !mpRefDevice )
+ return;
+
+ OUString aFilename = "dbg_frontbuffer" + OUString::number(nFilePostfixCount) + ".bmp";
+
+ SvFileStream aStream( aFilename, StreamMode::STD_READWRITE );
+
+ const ::Point aEmptyPoint;
+ bool bOldMap( mpRefDevice->IsMapModeEnabled() );
+ mpRefDevice->EnableMapMode( false );
+ const ::BitmapEx aTempBitmap(mpRefDevice->GetBitmapEx(aEmptyPoint, mpRefDevice->GetOutputSizePixel()));
+ WriteDIB(aTempBitmap, aStream, false);
+ mpRefDevice->EnableMapMode( bOldMap );
+
+ ++nFilePostfixCount;
+ }
+
+ SurfaceSharedPtr DeviceHelper::createSurface( const ::basegfx::B2ISize& rSize, int aContent )
+ {
+ if( mpSurface )
+ return mpSurface->getSimilar( aContent, rSize.getX(), rSize.getY() );
+
+ return SurfaceSharedPtr();
+ }
+
+ SurfaceSharedPtr DeviceHelper::createSurface( BitmapSystemData const & rData, const Size& rSize )
+ {
+ if (mpRefDevice)
+ return mpRefDevice->CreateBitmapSurface(rData, rSize);
+
+ return SurfaceSharedPtr();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/cairo/cairo_devicehelper.hxx b/canvas/source/cairo/cairo_devicehelper.hxx
new file mode 100644
index 000000000..eede77844
--- /dev/null
+++ b/canvas/source/cairo/cairo_devicehelper.hxx
@@ -0,0 +1,122 @@
+/* -*- 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/XGraphicDevice.hpp>
+
+#include <vcl/outdev.hxx>
+
+#include "cairo_surfaceprovider.hxx"
+
+/* Definition of DeviceHelper class */
+
+namespace cairocanvas
+{
+ class DeviceHelper
+ {
+ public:
+ /// make noncopyable
+ DeviceHelper(const DeviceHelper&) = delete;
+ const DeviceHelper& operator=(const DeviceHelper&) = delete;
+
+ DeviceHelper();
+
+ /** init helper
+
+ @param rCanvas
+ Owning canvas.
+
+ @param rRefDevice
+ Reference output device. Needed for resolution
+ calculations etc.
+ */
+ void init( SurfaceProvider& rSurfaceProvider,
+ OutputDevice& rRefDevice );
+
+ /// 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 > const & getColorSpace() const;
+
+ /** called when DumpScreenContent property is enabled on
+ XGraphicDevice, and writes out bitmaps of current screen.
+ */
+ void dumpScreenContent() const;
+
+ OutputDevice* getOutputDevice() const { return mpRefDevice; }
+ const ::cairo::SurfaceSharedPtr& getSurface() const { return mpSurface; }
+ ::cairo::SurfaceSharedPtr createSurface( const ::basegfx::B2ISize& rSize, int aContent );
+ ::cairo::SurfaceSharedPtr createSurface( BitmapSystemData const & rData, const Size& rSize );
+
+ protected:
+ /** init helper
+
+ @param rCanvas
+ Owning canvas.
+
+ @param rRefDevice
+ Reference output device. Needed for resolution
+ calculations etc.
+ */
+ void implInit( SurfaceProvider& rSurfaceProvider,
+ OutputDevice& rRefDevice );
+ void setSize( const ::basegfx::B2ISize& rSize );
+
+ private:
+ /** Surface provider
+
+ Deliberately not a refcounted reference, because of
+ potential circular references for canvas. Provides us with
+ our output surface and associated functionality.
+ */
+ SurfaceProvider* mpSurfaceProvider;
+
+ VclPtr<OutputDevice> mpRefDevice;
+ ::cairo::SurfaceSharedPtr mpSurface;
+ };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/cairo/cairo_repainttarget.hxx b/canvas/source/cairo/cairo_repainttarget.hxx
new file mode 100644
index 000000000..558ec7921
--- /dev/null
+++ b/canvas/source/cairo/cairo_repainttarget.hxx
@@ -0,0 +1,48 @@
+/* -*- 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/RenderState.hpp>
+#include <com/sun/star/rendering/ViewState.hpp>
+
+#include <vcl/cairo.hxx>
+
+namespace cairocanvas
+{
+ /* Definition of RepaintTarget interface */
+
+ /** Target interface for XCachedPrimitive implementations
+
+ This interface must be implemented on all canvas
+ implementations that hand out XCachedPrimitives
+ */
+ class RepaintTarget
+ {
+ public:
+ virtual ~RepaintTarget() {}
+
+ // call this when a bitmap is repainted
+ virtual bool repaint( const ::cairo::SurfaceSharedPtr& pSurface,
+ const css::rendering::ViewState& viewState,
+ const css::rendering::RenderState& renderState ) = 0;
+ };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/cairo/cairo_sprite.hxx b/canvas/source/cairo/cairo_sprite.hxx
new file mode 100644
index 000000000..513283e30
--- /dev/null
+++ b/canvas/source/cairo/cairo_sprite.hxx
@@ -0,0 +1,65 @@
+/* -*- 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>
+
+#include <vcl/cairo.hxx>
+
+namespace cairocanvas
+{
+ /** Specialization of ::canvas::Sprite interface, to also provide
+ redraw methods.
+ */
+ class Sprite : public ::canvas::Sprite
+ {
+ public:
+
+ /** Redraw sprite at the stored position.
+
+ @param bBufferedUpdate
+ When true, the redraw does <em>not</em> happen directly on
+ the front buffer, but within a VDev. Used to speed up
+ drawing.
+ */
+ virtual void redraw( const ::cairo::CairoSharedPtr& pCairo,
+ bool bBufferedUpdate ) const = 0;
+
+ /** Redraw sprite at the given position.
+
+ @param rPos
+ Output position of the sprite. Overrides the sprite's own
+ output position.
+
+ @param bBufferedUpdate
+ When true, the redraw does <em>not</em> happen directly on
+ the front buffer, but within a VDev. Used to speed up
+ drawing.
+ */
+ virtual void redraw( const ::cairo::CairoSharedPtr& pCairo,
+ const ::basegfx::B2DPoint& rOrigOutputPos,
+ bool bBufferedUpdate ) const = 0;
+
+ protected:
+ ~Sprite() {}
+ };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/cairo/cairo_spritecanvas.cxx b/canvas/source/cairo/cairo_spritecanvas.cxx
new file mode 100644
index 000000000..562157f52
--- /dev/null
+++ b/canvas/source/cairo/cairo_spritecanvas.cxx
@@ -0,0 +1,233 @@
+/* -*- 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/range/b2irange.hxx>
+#include <basegfx/utils/canvastools.hxx>
+#include <com/sun/star/awt/XTopWindow.hpp>
+#include <com/sun/star/awt/Rectangle.hpp>
+#include <com/sun/star/lang/NoSupportException.hpp>
+#include <osl/mutex.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <tools/diagnose_ex.h>
+#include <cppuhelper/supportsservice.hxx>
+
+#include "cairo_spritecanvas.hxx"
+
+using namespace ::cairo;
+using namespace ::com::sun::star;
+
+namespace cairocanvas
+{
+ SpriteCanvas::SpriteCanvas( const uno::Sequence< uno::Any >& aArguments,
+ const uno::Reference< uno::XComponentContext >& /*rxContext*/ ) :
+ maArguments(aArguments)
+ {
+ }
+
+ void SpriteCanvas::initialize()
+ {
+ SAL_INFO("canvas.cairo", "CairoSpriteCanvas created " << this);
+
+ // #i64742# Only call initialize when not in probe mode
+ if( !maArguments.hasElements() )
+ return;
+
+ /* maArguments:
+ 0: ptr to creating instance (Window or VirtualDevice)
+ 1: current bounds of creating instance
+ 2: bool, denoting always on top state for Window (always false for VirtualDevice)
+ 3: XWindow for creating Window (or empty for VirtualDevice)
+ 4: SystemGraphicsData as a streamed Any
+ */
+ ENSURE_ARG_OR_THROW( maArguments.getLength() >= 4 &&
+ maArguments[0].getValueTypeClass() == uno::TypeClass_HYPER &&
+ maArguments[3].getValueTypeClass() == uno::TypeClass_INTERFACE,
+ "CairoSpriteCanvas::initialize: wrong number of arguments, or wrong types" );
+
+ awt::Rectangle aRect;
+ maArguments[1] >>= aRect;
+
+ bool bIsFullscreen( false );
+ maArguments[2] >>= bIsFullscreen;
+
+ uno::Reference< awt::XWindow > xParentWindow;
+ maArguments[3] >>= xParentWindow;
+
+ VclPtr<vcl::Window> pParentWindow = VCLUnoHelper::GetWindow(xParentWindow);
+ if( !pParentWindow )
+ throw lang::NoSupportException(
+ "Parent window not VCL window, or canvas out-of-process!", nullptr);
+
+ bool bHasCairo = pParentWindow->GetOutDev()->SupportsCairo();
+ ENSURE_ARG_OR_THROW(bHasCairo,
+ "CairoSpriteCanvas::SpriteCanvas: No Cairo capability");
+
+ Size aPixelSize( pParentWindow->GetOutputSizePixel() );
+ const ::basegfx::B2ISize aSize( aPixelSize.Width(),
+ aPixelSize.Height() );
+
+ // setup helper
+ maDeviceHelper.init( *pParentWindow,
+ *this,
+ aSize,
+ bIsFullscreen );
+
+ setWindow(uno::Reference<awt::XWindow2>(xParentWindow, uno::UNO_QUERY_THROW));
+
+ maCanvasHelper.init( maRedrawManager,
+ *this,
+ aSize );
+
+ maArguments.realloc(0);
+ }
+
+ void SpriteCanvas::disposeThis()
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ // forward to parent
+ SpriteCanvasBaseT::disposeThis();
+ }
+
+ sal_Bool SAL_CALL SpriteCanvas::showBuffer( sal_Bool bUpdateAll )
+ {
+ return updateScreen( bUpdateAll );
+ }
+
+ sal_Bool SAL_CALL SpriteCanvas::switchBuffer( sal_Bool bUpdateAll )
+ {
+ return updateScreen( 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.Cairo";
+ }
+
+ // XServiceInfo
+ sal_Bool SpriteCanvas::supportsService(const OUString& sServiceName)
+ {
+ return cppu::supportsService(this, sServiceName);
+
+ }
+ OUString SpriteCanvas::getImplementationName()
+ {
+ return "com.sun.star.comp.rendering.Canvas.Cairo";
+ }
+ css::uno::Sequence< OUString > SpriteCanvas::getSupportedServiceNames()
+ {
+ return { getServiceName() };
+ }
+
+ SurfaceSharedPtr SpriteCanvas::getSurface()
+ {
+ return maDeviceHelper.getBufferSurface();
+ }
+
+ SurfaceSharedPtr SpriteCanvas::createSurface( const ::basegfx::B2ISize& rSize, int aContent )
+ {
+ return maDeviceHelper.createSurface( rSize, aContent );
+ }
+
+ SurfaceSharedPtr SpriteCanvas::createSurface( ::Bitmap& rBitmap )
+ {
+ BitmapSystemData aData;
+ if( rBitmap.GetSystemData( aData ) ) {
+ const Size& rSize = rBitmap.GetSizePixel();
+
+ return maDeviceHelper.createSurface( aData, rSize );
+ }
+
+ return SurfaceSharedPtr();
+ }
+
+ SurfaceSharedPtr SpriteCanvas::changeSurface()
+ {
+ // non-modifiable surface here
+ return SurfaceSharedPtr();
+ }
+
+ OutputDevice* SpriteCanvas::getOutputDevice()
+ {
+ return maDeviceHelper.getOutputDevice();
+ }
+
+ SurfaceSharedPtr const & SpriteCanvas::getBufferSurface() const
+ {
+ return maDeviceHelper.getBufferSurface();
+ }
+
+ SurfaceSharedPtr const & SpriteCanvas::getWindowSurface() const
+ {
+ return maDeviceHelper.getWindowSurface();
+ }
+
+ const ::basegfx::B2ISize& SpriteCanvas::getSizePixel() const
+ {
+ return maDeviceHelper.getSizePixel();
+ }
+
+ void SpriteCanvas::setSizePixel( const ::basegfx::B2ISize& rSize )
+ {
+ maCanvasHelper.setSize( rSize );
+ // re-set background surface, in case it needed recreation
+ maCanvasHelper.setSurface( maDeviceHelper.getBufferSurface(),
+ false );
+ }
+
+ void SpriteCanvas::flush()
+ {
+ maDeviceHelper.flush();
+ }
+
+ bool SpriteCanvas::repaint( const SurfaceSharedPtr& pSurface,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState )
+ {
+ return maCanvasHelper.repaint( pSurface, viewState, renderState );
+ }
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_rendering_SpriteCanvas_Cairo_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& args)
+{
+ rtl::Reference<cairocanvas::SpriteCanvas> p = new cairocanvas::SpriteCanvas(args, context);
+ p->acquire();
+ p->initialize();
+ return static_cast<cppu::OWeakObject*>(p.get());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/cairo/cairo_spritecanvas.hxx b/canvas/source/cairo/cairo_spritecanvas.hxx
new file mode 100644
index 000000000..7790e6890
--- /dev/null
+++ b/canvas/source/cairo/cairo_spritecanvas.hxx
@@ -0,0 +1,159 @@
+/* -*- 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/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/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 "cairo_spritedevicehelper.hxx"
+#include "cairo_repainttarget.hxx"
+#include "cairo_surfaceprovider.hxx"
+#include "cairo_spritecanvashelper.hxx"
+
+namespace cairocanvas
+{
+ 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,
+ public SurfaceProvider
+ {
+ };
+
+ 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 RepaintTarget
+ {
+ 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 sal_Bool SAL_CALL supportsService(const OUString& sServiceName) override;
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ // SurfaceProvider
+ virtual ::cairo::SurfaceSharedPtr getSurface() override;
+ virtual ::cairo::SurfaceSharedPtr createSurface( const ::basegfx::B2ISize& rSize, int aContent ) override;
+ virtual ::cairo::SurfaceSharedPtr createSurface( ::Bitmap& rBitmap ) override;
+ virtual ::cairo::SurfaceSharedPtr changeSurface() override;
+ virtual OutputDevice* getOutputDevice() override;
+
+ // RepaintTarget
+ virtual bool repaint( const ::cairo::SurfaceSharedPtr& pSurface,
+ const css::rendering::ViewState& viewState,
+ const css::rendering::RenderState& renderState ) override;
+
+ ::cairo::SurfaceSharedPtr const & getWindowSurface() const;
+ ::cairo::SurfaceSharedPtr const & getBufferSurface() const;
+
+ const ::basegfx::B2ISize& getSizePixel() const;
+ void setSizePixel( const ::basegfx::B2ISize& rSize );
+ void flush();
+
+ private:
+ css::uno::Sequence< css::uno::Any > maArguments;
+ };
+
+ typedef ::rtl::Reference< SpriteCanvas > SpriteCanvasRef;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/cairo/cairo_spritecanvashelper.cxx b/canvas/source/cairo/cairo_spritecanvashelper.cxx
new file mode 100644
index 000000000..992e0a581
--- /dev/null
+++ b/canvas/source/cairo/cairo_spritecanvashelper.cxx
@@ -0,0 +1,518 @@
+/* -*- 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 <boost/cast.hpp>
+
+#include <basegfx/range/b2irange.hxx>
+#include <tools/diagnose_ex.h>
+
+#include <canvas/canvastools.hxx>
+
+#include <cairo.h>
+
+#include "cairo_canvascustomsprite.hxx"
+#include "cairo_spritecanvashelper.hxx"
+
+using namespace ::cairo;
+using namespace ::com::sun::star;
+
+namespace cairocanvas
+{
+ namespace
+ {
+ /** Sprite redraw at original position
+
+ Used to repaint the whole canvas (background and all
+ sprites)
+ */
+ void spriteRedraw( const CairoSharedPtr& pCairo,
+ const ::canvas::Sprite::Reference& rSprite )
+ {
+ // downcast to derived cairocanvas::Sprite interface, which
+ // provides the actual redraw methods.
+ ::boost::polymorphic_downcast< Sprite* >(rSprite.get())->redraw( pCairo, true);
+ }
+
+ void repaintBackground( const CairoSharedPtr& pCairo,
+ const SurfaceSharedPtr& pBackgroundSurface,
+ const ::basegfx::B2DRange& rArea )
+ {
+ cairo_save( pCairo.get() );
+ cairo_rectangle( pCairo.get(), ceil( rArea.getMinX() ), ceil( rArea.getMinY() ),
+ floor( rArea.getWidth() ), floor( rArea.getHeight() ) );
+ cairo_clip( pCairo.get() );
+ cairo_set_source_surface( pCairo.get(), pBackgroundSurface->getCairoSurface().get(), 0, 0 );
+ cairo_set_operator( pCairo.get(), CAIRO_OPERATOR_SOURCE );
+ cairo_paint( pCairo.get() );
+ cairo_restore( pCairo.get() );
+ }
+
+ void opaqueUpdateSpriteArea( const ::canvas::Sprite::Reference& rSprite,
+ const CairoSharedPtr& pCairo,
+ const ::basegfx::B2IRange& rArea )
+ {
+ // clip output to actual update region (otherwise a)
+ // wouldn't save much render time, and b) will clutter
+ // scrolled sprite content outside this area)
+ cairo_save( pCairo.get() );
+ cairo_rectangle( pCairo.get(), rArea.getMinX(), rArea.getMinY(),
+ sal::static_int_cast<sal_Int32>(rArea.getWidth()),
+ sal::static_int_cast<sal_Int32>(rArea.getHeight()) );
+ cairo_clip( pCairo.get() );
+
+ // repaint affected sprite directly to output device (at
+ // the actual screen output position)
+ // rendering directly to device buffer
+ ::boost::polymorphic_downcast< Sprite* >( rSprite.get() )->redraw( pCairo, false );
+
+ cairo_restore( pCairo.get() );
+ }
+ }
+
+ SpriteCanvasHelper::SpriteCanvasHelper() :
+ mpRedrawManager( nullptr ),
+ mpOwningSpriteCanvas( nullptr ),
+ mbCompositingSurfaceDirty(true)
+ {
+ }
+
+ void SpriteCanvasHelper::init( ::canvas::SpriteRedrawManager& rManager,
+ SpriteCanvas& rDevice,
+ const ::basegfx::B2ISize& rSize )
+ {
+ mpRedrawManager = &rManager;
+ mpOwningSpriteCanvas = &rDevice;
+
+ CanvasHelper::init( rSize, rDevice, &rDevice );
+ }
+
+ void SpriteCanvasHelper::disposing()
+ {
+ mpCompositingSurface.reset();
+ mpOwningSpriteCanvas = nullptr;
+ mpRedrawManager = nullptr;
+
+ // forward to base
+ CanvasHelper::disposing();
+ }
+
+ uno::Reference< rendering::XAnimatedSprite > SpriteCanvasHelper::createSpriteFromAnimation(
+ const uno::Reference< rendering::XAnimation >& )
+ {
+ 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,
+ mpOwningSpriteCanvas ) );
+ }
+
+ uno::Reference< rendering::XSprite > SpriteCanvasHelper::createClonedSprite(
+ const uno::Reference< rendering::XSprite >& )
+ {
+ return uno::Reference< rendering::XSprite >();
+ }
+
+ bool SpriteCanvasHelper::updateScreen( const ::basegfx::B2IRange& /*rCurrArea*/,
+ bool bUpdateAll,
+ bool& io_bSurfaceDirty )
+ {
+ if( !mpRedrawManager ||
+ !mpOwningSpriteCanvas ||
+ !mpOwningSpriteCanvas->getWindowSurface() ||
+ !mpOwningSpriteCanvas->getBufferSurface() )
+ {
+ return false; // disposed, or otherwise dysfunctional
+ }
+
+ SAL_INFO("canvas.cairo", "SpriteCanvasHelper::updateScreen called");
+
+ const ::basegfx::B2ISize& rSize = mpOwningSpriteCanvas->getSizePixel();
+
+ // force compositing surface to be available before using it
+ // inside forEachSpriteArea
+ SurfaceSharedPtr pCompositingSurface = getCompositingSurface(rSize);
+ SurfaceSharedPtr pWindowSurface = mpOwningSpriteCanvas->getWindowSurface();
+ CairoSharedPtr pCompositingCairo = pCompositingSurface->getCairo();
+ CairoSharedPtr pWindowCairo = pWindowSurface->getCairo();
+
+ // TODO(P1): Might be worthwhile to track areas of background
+ // changes, too.
+ if( !bUpdateAll && !io_bSurfaceDirty && !mbCompositingSurfaceDirty )
+ {
+ // 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 );
+ }
+ else
+ {
+ SAL_INFO("canvas.cairo", "SpriteCanvasHelper::updateScreen update ALL");
+
+ // background has changed, so we currently have no choice
+ // but repaint everything (or caller requested that)
+
+ cairo_rectangle( pCompositingCairo.get(), 0, 0, rSize.getX(), rSize.getY() );
+ cairo_clip( pCompositingCairo.get() );
+ cairo_save( pCompositingCairo.get() );
+ cairo_set_source_surface( pCompositingCairo.get(),
+ mpOwningSpriteCanvas->getBufferSurface()->getCairoSurface().get(),
+ 0, 0 );
+ cairo_set_operator( pCompositingCairo.get(), CAIRO_OPERATOR_SOURCE );
+ cairo_paint( pCompositingCairo.get() );
+ cairo_restore( pCompositingCairo.get() );
+
+ // repaint all active sprites on top of background into
+ // VDev.
+ mpRedrawManager->forEachSprite(
+ [&pCompositingCairo]( const Sprite::Reference rSprite )
+ { spriteRedraw( pCompositingCairo, rSprite ); }
+ );
+
+ // flush to screen
+ cairo_rectangle( pWindowCairo.get(), 0, 0, rSize.getX(), rSize.getY() );
+ cairo_clip( pWindowCairo.get() );
+ cairo_set_source_surface( pWindowCairo.get(),
+ pCompositingSurface->getCairoSurface().get(),
+ 0, 0 );
+ cairo_set_operator( pWindowCairo.get(), CAIRO_OPERATOR_SOURCE );
+ cairo_paint( pWindowCairo.get() );
+ }
+
+ // change record vector must be cleared, for the next turn of
+ // rendering and sprite changing
+ mpRedrawManager->clearChangeRecords();
+
+ mbCompositingSurfaceDirty = false;
+ io_bSurfaceDirty = false;
+
+ // commit to screen
+ mpOwningSpriteCanvas->flush();
+
+ return true;
+ }
+
+ void SpriteCanvasHelper::backgroundPaint( const ::basegfx::B2DRange& rUpdateRect )
+ {
+ if( mpOwningSpriteCanvas && mpCompositingSurface )
+ repaintBackground( mpCompositingSurface->getCairo(),
+ mpOwningSpriteCanvas->getBufferSurface(),
+ rUpdateRect );
+ }
+
+ void SpriteCanvasHelper::scrollUpdate( const ::basegfx::B2DRange& rMoveStart,
+ const ::basegfx::B2DRange& rMoveEnd,
+ const ::canvas::SpriteRedrawManager::UpdateArea& rUpdateArea )
+ {
+ ENSURE_OR_THROW( mpOwningSpriteCanvas &&
+ mpOwningSpriteCanvas->getBufferSurface(),
+ "SpriteCanvasHelper::scrollUpdate(): NULL device pointer " );
+
+ SAL_INFO("canvas.cairo", "SpriteCanvasHelper::scrollUpdate called");
+
+ const ::basegfx::B2ISize& rSize = mpOwningSpriteCanvas->getSizePixel();
+ const ::basegfx::B2IRange aOutputBounds( 0,0,
+ rSize.getX(),
+ rSize.getY() );
+
+ SurfaceSharedPtr pCompositingSurface = getCompositingSurface(rSize);
+ SurfaceSharedPtr pWindowSurface = mpOwningSpriteCanvas->getWindowSurface();
+ CairoSharedPtr pCompositingCairo = pCompositingSurface->getCairo();
+ CairoSharedPtr pWindowCairo = pWindowSurface->getCairo();
+
+ // 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.
+ ::basegfx::B2IRange aSourceRect(
+ ::canvas::tools::spritePixelAreaFromB2DRange( rMoveStart ) );
+ const ::basegfx::B2IRange& rDestRect(
+ ::canvas::tools::spritePixelAreaFromB2DRange( rMoveEnd ) );
+ ::basegfx::B2IPoint aDestPos( rDestRect.getMinimum() );
+
+ std::vector< ::basegfx::B2IRange > aUnscrollableAreas;
+
+ // TODO(E3): This is plain buggy (but copies the behaviour of
+ // the old Impress slideshow) - the scrolled area might
+ // actually lie _below_ another window!
+
+ // clip to output bounds (cannot properly scroll stuff
+ // _outside_ our screen area)
+ if( !::canvas::tools::clipScrollArea( aSourceRect,
+ aDestPos,
+ aUnscrollableAreas,
+ aOutputBounds ) )
+ {
+ // fully clipped scroll area: cannot simply scroll
+ // then. Perform normal opaque update (can use that, since
+ // one of the preconditions for scrollable update is
+ // opaque sprite content)
+
+ // repaint all affected sprites directly to output device
+ for( const auto& rComponent : rUpdateArea.maComponentList )
+ {
+ const ::canvas::Sprite::Reference& rSprite( rComponent.second.getSprite() );
+ if( rSprite.is() )
+ ::boost::polymorphic_downcast< Sprite* >( rSprite.get() )->redraw(
+ pCompositingCairo, true );
+ }
+ }
+ else
+ {
+ const ::basegfx::B2IVector aSourceUpperLeftPos( aSourceRect.getMinimum() );
+
+ // clip dest area (which must be inside rDestBounds)
+ ::basegfx::B2IRange aDestRect( rDestRect );
+ aDestRect.intersect( aOutputBounds );
+
+ ::basegfx::B2ISize aScrollSize( aDestRect.getWidth(), aDestRect.getHeight() );
+ SurfaceSharedPtr pScrollSurface( getTemporarySurface() );
+ CairoSharedPtr pScrollCairo( pScrollSurface->getCairo() );
+
+ cairo_save( pScrollCairo.get() );
+ // scroll the current content of the compositing surface (and,
+ // thus, of the window) in temp. surface
+ cairo_set_source_surface( pScrollCairo.get(),
+ pCompositingSurface->getCairoSurface().get(),
+ aDestPos.getX() - aSourceUpperLeftPos.getX(),
+ aDestPos.getY() - aSourceUpperLeftPos.getY() );
+ cairo_rectangle( pScrollCairo.get(),
+ aDestPos.getX(), aDestPos.getY(),
+ aScrollSize.getX(), aScrollSize.getY() );
+ cairo_clip( pScrollCairo.get() );
+ cairo_set_operator( pScrollCairo.get(), CAIRO_OPERATOR_SOURCE );
+ cairo_paint( pScrollCairo.get() );
+ cairo_restore( pScrollCairo.get() );
+
+ cairo_save( pCompositingCairo.get() );
+ // copy the scrolled area back onto the compositing surface
+ cairo_set_source_surface( pCompositingCairo.get(),
+ pScrollSurface->getCairoSurface().get(),
+ 0, 0 );
+ cairo_rectangle( pCompositingCairo.get(),
+ aDestPos.getX(), aDestPos.getY(),
+ aScrollSize.getX(), aScrollSize.getY() );
+ cairo_clip( pCompositingCairo.get() );
+ cairo_set_operator( pCompositingCairo.get(), CAIRO_OPERATOR_SOURCE );
+ cairo_paint( pCompositingCairo.get() );
+ cairo_restore( pCompositingCairo.get() );
+
+ const ::canvas::SpriteRedrawManager::SpriteConnectedRanges::ComponentListType::const_iterator
+ aFirst( rUpdateArea.maComponentList.begin() );
+ ::canvas::SpriteRedrawManager::SpriteConnectedRanges::ComponentListType::const_iterator
+ aSecond( aFirst );
+ ++aSecond;
+
+ ENSURE_OR_THROW( aFirst->second.getSprite().is(),
+ "VCLCanvas::scrollUpdate(): no sprite" );
+
+ // repaint uncovered areas from sprite. Need to actually
+ // clip here, since we're only repainting _parts_ of the
+ // sprite
+ for( const auto& rArea : aUnscrollableAreas )
+ opaqueUpdateSpriteArea( aFirst->second.getSprite(),
+ pCompositingCairo, rArea );
+ }
+
+ // 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& rArea : aUncoveredAreas )
+ repaintBackground( pCompositingCairo,
+ mpOwningSpriteCanvas->getBufferSurface(), rArea );
+
+ cairo_rectangle( pWindowCairo.get(), 0, 0, rSize.getX(), rSize.getY() );
+ cairo_clip( pWindowCairo.get() );
+ cairo_set_source_surface( pWindowCairo.get(),
+ pCompositingSurface->getCairoSurface().get(),
+ 0, 0 );
+ cairo_set_operator( pWindowCairo.get(), CAIRO_OPERATOR_SOURCE );
+ cairo_paint( pWindowCairo.get() );
+ }
+
+ void SpriteCanvasHelper::opaqueUpdate( const ::basegfx::B2DRange& rTotalArea,
+ const std::vector< ::canvas::Sprite::Reference >& rSortedUpdateSprites )
+ {
+ ENSURE_OR_THROW( mpOwningSpriteCanvas &&
+ mpOwningSpriteCanvas->getBufferSurface(),
+ "SpriteCanvasHelper::opaqueUpdate(): NULL device pointer " );
+
+ SAL_INFO("canvas.cairo", "SpriteCanvasHelper::opaqueUpdate called");
+
+ const ::basegfx::B2ISize& rDeviceSize = mpOwningSpriteCanvas->getSizePixel();
+
+ SurfaceSharedPtr pCompositingSurface = getCompositingSurface(rDeviceSize);
+ SurfaceSharedPtr pWindowSurface = mpOwningSpriteCanvas->getWindowSurface();
+ CairoSharedPtr pCompositingCairo = pCompositingSurface->getCairo();
+ CairoSharedPtr pWindowCairo = pWindowSurface->getCairo();
+
+ cairo_rectangle( pCompositingCairo.get(), 0, 0, rDeviceSize.getX(), rDeviceSize.getY() );
+ cairo_clip( pCompositingCairo.get() );
+
+ ::basegfx::B2DVector aPos( ceil( rTotalArea.getMinX() ), ceil( rTotalArea.getMinY() ) );
+ ::basegfx::B2DVector aSize( floor( rTotalArea.getMaxX() - aPos.getX() ), floor( rTotalArea.getMaxY() - aPos.getY() ) );
+
+ cairo_rectangle( pCompositingCairo.get(), aPos.getX(), aPos.getY(), aSize.getX(), aSize.getY() );
+ cairo_clip( pCompositingCairo.get() );
+
+ // repaint all affected sprites directly to output device
+ for( const auto& rSprite : rSortedUpdateSprites )
+ {
+ if( rSprite.is() )
+ ::boost::polymorphic_downcast< Sprite* >( rSprite.get() )->redraw(
+ pCompositingCairo, false );
+ }
+
+ // flush to screen
+ cairo_rectangle( pWindowCairo.get(), 0, 0, rDeviceSize.getX(), rDeviceSize.getY() );
+ cairo_clip( pWindowCairo.get() );
+ cairo_rectangle( pWindowCairo.get(), aPos.getX(), aPos.getY(), aSize.getX(), aSize.getY() );
+ cairo_clip( pWindowCairo.get() );
+ cairo_set_source_surface( pWindowCairo.get(),
+ pCompositingSurface->getCairoSurface().get(),
+ 0, 0 );
+ cairo_set_operator( pWindowCairo.get(), CAIRO_OPERATOR_SOURCE );
+ cairo_paint( pWindowCairo.get() );
+ }
+
+ void SpriteCanvasHelper::genericUpdate( const ::basegfx::B2DRange& rRequestedArea,
+ const std::vector< ::canvas::Sprite::Reference >& rSortedUpdateSprites )
+ {
+ // TODO
+ SAL_INFO("canvas.cairo", "SpriteCanvasHelper::genericUpdate called");
+
+ ENSURE_OR_THROW( mpOwningSpriteCanvas &&
+ mpOwningSpriteCanvas->getBufferSurface(),
+ "SpriteCanvasHelper::genericUpdate(): NULL device pointer " );
+
+ // limit size of update VDev to target outdev's size
+ const ::basegfx::B2ISize& rSize = mpOwningSpriteCanvas->getSizePixel();
+
+ SurfaceSharedPtr pCompositingSurface = getCompositingSurface(rSize);
+ SurfaceSharedPtr pWindowSurface = mpOwningSpriteCanvas->getWindowSurface();
+ CairoSharedPtr pCompositingCairo = pCompositingSurface->getCairo();
+ CairoSharedPtr pWindowCairo = pWindowSurface->getCairo();
+
+ // round output position towards zero. Don't want to truncate
+ // a fraction of a sprite pixel... Clip position at origin,
+ // otherwise, truncation of size below might leave visible
+ // areas uncovered by VDev.
+ const Point aOutputPosition(
+ std::max( sal_Int32( 0 ),
+ static_cast< sal_Int32 >(rRequestedArea.getMinX()) ),
+ std::max( sal_Int32( 0 ),
+ static_cast< sal_Int32 >(rRequestedArea.getMinY()) ) );
+ // round output size towards +infty. Don't want to truncate a
+ // fraction of a sprite pixel... Limit size of VDev to output
+ // device's area.
+ const Size aOutputSize(
+ std::min( rSize.getX(),
+ ::canvas::tools::roundUp( rRequestedArea.getMaxX() - aOutputPosition.X()) ),
+ std::min( rSize.getY(),
+ ::canvas::tools::roundUp( rRequestedArea.getMaxY() - aOutputPosition.Y()) ) );
+
+ cairo_rectangle( pCompositingCairo.get(), aOutputPosition.X(), aOutputPosition.Y(), aOutputSize.Width(), aOutputSize.Height() );
+ cairo_clip( pCompositingCairo.get() );
+
+ // paint background
+ cairo_save( pCompositingCairo.get() );
+ cairo_set_source_surface( pCompositingCairo.get(),
+ mpOwningSpriteCanvas->getBufferSurface()->getCairoSurface().get(),
+ 0, 0 );
+ cairo_set_operator( pCompositingCairo.get(), CAIRO_OPERATOR_SOURCE );
+ cairo_paint( pCompositingCairo.get() );
+ cairo_restore( pCompositingCairo.get() );
+
+ // repaint all affected sprites on top of background into
+ // VDev.
+ for( const auto& rSprite : rSortedUpdateSprites )
+ {
+ if( rSprite.is() )
+ ::boost::polymorphic_downcast< Sprite* >( rSprite.get() )->redraw(
+ pCompositingCairo, true );
+ }
+
+ // flush to screen
+ cairo_rectangle( pWindowCairo.get(), aOutputPosition.X(), aOutputPosition.Y(), aOutputSize.Width(), aOutputSize.Height() );
+ cairo_clip( pWindowCairo.get() );
+ cairo_set_source_surface( pWindowCairo.get(),
+ pCompositingSurface->getCairoSurface().get(),
+ 0, 0 );
+ cairo_set_operator( pWindowCairo.get(), CAIRO_OPERATOR_SOURCE );
+ cairo_paint( pWindowCairo.get() );
+ }
+
+ ::cairo::SurfaceSharedPtr const & SpriteCanvasHelper::getCompositingSurface( const ::basegfx::B2ISize& rNeededSize )
+ {
+ if( rNeededSize.getX() > maCompositingSurfaceSize.getX() ||
+ rNeededSize.getY() > maCompositingSurfaceSize.getY() )
+ {
+ // need to give buffer more size
+ mpCompositingSurface.reset();
+ }
+
+ if( !mpCompositingSurface )
+ {
+ mpCompositingSurface = createSurface( rNeededSize );
+ maCompositingSurfaceSize = rNeededSize;
+ mbCompositingSurfaceDirty = true;
+ mpTemporarySurface.reset();
+ }
+
+ return mpCompositingSurface;
+ }
+
+ ::cairo::SurfaceSharedPtr const & SpriteCanvasHelper::getTemporarySurface()
+ {
+ if ( !mpTemporarySurface )
+ mpTemporarySurface = createSurface( maCompositingSurfaceSize );
+ return mpTemporarySurface;
+ }
+
+ ::cairo::SurfaceSharedPtr SpriteCanvasHelper::createSurface( const ::basegfx::B2ISize& rNeededSize ) const
+ {
+ return mpOwningSpriteCanvas->getWindowSurface()->getSimilar(
+ CAIRO_CONTENT_COLOR,
+ rNeededSize.getX(), rNeededSize.getY() );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/cairo/cairo_spritecanvashelper.hxx b/canvas/source/cairo/cairo_spritecanvashelper.hxx
new file mode 100644
index 000000000..2b11f1bcf
--- /dev/null
+++ b/canvas/source/cairo/cairo_spritecanvashelper.hxx
@@ -0,0 +1,140 @@
+/* -*- 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/XAnimatedSprite.hpp>
+#include <com/sun/star/rendering/XAnimation.hpp>
+#include <com/sun/star/rendering/XCustomSprite.hpp>
+
+#include <spriteredrawmanager.hxx>
+
+#include <vcl/cairo.hxx>
+#include "cairo_canvashelper.hxx"
+
+namespace basegfx
+{
+ class B2IRange;
+}
+
+namespace cairocanvas
+{
+ class SpriteCanvas;
+
+ class SpriteCanvasHelper : public CanvasHelper
+ {
+ public:
+ SpriteCanvasHelper();
+
+ void init( ::canvas::SpriteRedrawManager& rManager,
+ SpriteCanvas& rOwningSpriteCanvas,
+ const ::basegfx::B2ISize& rSize );
+
+ /// 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::B2IRange& 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:
+ ::cairo::SurfaceSharedPtr const & getCompositingSurface( const ::basegfx::B2ISize& rNeededSize );
+ ::cairo::SurfaceSharedPtr const & getTemporarySurface();
+ ::cairo::SurfaceSharedPtr createSurface( const ::basegfx::B2ISize& rNeededSize ) const;
+
+ /// Set from the SpriteCanvas: instance coordinating sprite redraw
+ ::canvas::SpriteRedrawManager* mpRedrawManager;
+
+ /// Set from the init method. used to generate sprites
+ SpriteCanvas* mpOwningSpriteCanvas;
+
+ /// a surface used to composite the frontbuffer image
+ ::cairo::SurfaceSharedPtr mpCompositingSurface;
+ ::basegfx::B2ISize maCompositingSurfaceSize;
+ bool mbCompositingSurfaceDirty;
+ /// a temporary surface that is guaranteed to be the same size
+ //as the compositing surface
+ ::cairo::SurfaceSharedPtr mpTemporarySurface;
+ };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/cairo/cairo_spritedevicehelper.cxx b/canvas/source/cairo/cairo_spritedevicehelper.cxx
new file mode 100644
index 000000000..6539cf329
--- /dev/null
+++ b/canvas/source/cairo/cairo_spritedevicehelper.cxx
@@ -0,0 +1,153 @@
+/* -*- 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 <vcl/cairo.hxx>
+
+#include <cairo.h>
+
+#include "cairo_devicehelper.hxx"
+#include "cairo_spritecanvas.hxx"
+
+using namespace ::cairo;
+using namespace ::com::sun::star;
+
+namespace cairocanvas
+{
+
+ SpriteDeviceHelper::SpriteDeviceHelper() :
+ mpSpriteCanvas( nullptr ),
+ mbFullScreen( false )
+ {}
+
+ void SpriteDeviceHelper::init( vcl::Window& rOutputWindow,
+ SpriteCanvas& rSpriteCanvas,
+ const ::basegfx::B2ISize& rSize,
+ bool bFullscreen )
+ {
+ DeviceHelper::init(rSpriteCanvas,
+ *rOutputWindow.GetOutDev());
+
+ mpSpriteCanvas = &rSpriteCanvas;
+ mbFullScreen = bFullscreen;
+
+ setSize( rSize );
+ }
+
+ void SpriteDeviceHelper::disposing()
+ {
+ // release all references
+ mpBufferSurface.reset();
+ mpSpriteCanvas = nullptr;
+ }
+
+ bool SpriteDeviceHelper::showBuffer( bool, bool )
+ {
+ SAL_WARN("canvas.cairo", "showBuffer Not supposed to be called, handled by SpriteCanvas");
+ return false;
+ }
+
+ bool SpriteDeviceHelper::switchBuffer( bool, bool )
+ {
+ SAL_WARN("canvas.cairo", "showBuffer Not supposed to be called, handled by SpriteCanvas");
+ return false;
+ }
+
+ uno::Any SpriteDeviceHelper::isAccelerated() const
+ {
+ return css::uno::Any(true);
+ }
+
+ uno::Any SpriteDeviceHelper::getDeviceHandle() const
+ {
+ return DeviceHelper::getDeviceHandle();
+ }
+
+ uno::Any SpriteDeviceHelper::getSurfaceHandle() const
+ {
+ return DeviceHelper::getSurfaceHandle();
+ }
+
+ void SpriteDeviceHelper::setSize( const ::basegfx::B2ISize& rSize )
+ {
+ SAL_INFO(
+ "canvas.cairo",
+ "device size " << rSize.getX() << " x " << rSize.getY());
+
+ if( !mpSpriteCanvas )
+ return; // disposed
+
+ DeviceHelper::setSize(rSize);
+
+ if( mpBufferSurface && maSize != rSize )
+ mpBufferSurface.reset();
+ if( !mpBufferSurface )
+ mpBufferSurface = getWindowSurface()->getSimilar(
+ CAIRO_CONTENT_COLOR,
+ rSize.getX(), rSize.getY() );
+
+ if( maSize != rSize )
+ maSize = rSize;
+
+ mpSpriteCanvas->setSizePixel( maSize );
+ }
+
+
+ void SpriteDeviceHelper::notifySizeUpdate( const awt::Rectangle& rBounds )
+ {
+ setSize( ::basegfx::B2ISize(rBounds.Width, rBounds.Height) );
+ }
+
+ SurfaceSharedPtr const & SpriteDeviceHelper::getWindowSurface() const
+ {
+ return DeviceHelper::getSurface();
+ }
+
+ SurfaceSharedPtr SpriteDeviceHelper::createSurface( const ::basegfx::B2ISize& rSize, int aContent )
+ {
+ if( mpBufferSurface )
+ return mpBufferSurface->getSimilar( aContent, rSize.getX(), rSize.getY() );
+
+ return SurfaceSharedPtr();
+ }
+
+ SurfaceSharedPtr SpriteDeviceHelper::createSurface( BitmapSystemData const & rData, const Size& rSize )
+ {
+ OutputDevice *pDevice = getOutputDevice();
+ if (pDevice)
+ return pDevice->CreateBitmapSurface(rData, rSize);
+ return SurfaceSharedPtr();
+ }
+
+ /** SpriteDeviceHelper::flush Flush the platform native window
+ *
+ * Flushes the window by using the internally stored mpSysData.
+ *
+ **/
+ void SpriteDeviceHelper::flush()
+ {
+ SurfaceSharedPtr pWinSurface=getWindowSurface();
+ if( pWinSurface )
+ pWinSurface->flush();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/cairo/cairo_spritedevicehelper.hxx b/canvas/source/cairo/cairo_spritedevicehelper.hxx
new file mode 100644
index 000000000..63e9d1303
--- /dev/null
+++ b/canvas/source/cairo/cairo_spritedevicehelper.hxx
@@ -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 .
+ */
+
+#pragma once
+
+#include <com/sun/star/awt/Rectangle.hpp>
+
+#include <vcl/window.hxx>
+#include <vcl/cairo.hxx>
+
+#include "cairo_devicehelper.hxx"
+
+/* Definition of DeviceHelper class */
+
+namespace cairocanvas
+{
+ class SpriteCanvas;
+
+ class SpriteDeviceHelper : public DeviceHelper
+ {
+ public:
+ SpriteDeviceHelper();
+
+ void init( vcl::Window& rOutputWindow,
+ SpriteCanvas& rSpriteCanvas,
+ const ::basegfx::B2ISize& rSize,
+ bool bFullscreen );
+
+ /// Dispose all internal references
+ void disposing();
+
+ // XWindowGraphicDevice
+ bool showBuffer( bool, bool );
+ bool switchBuffer( bool, bool bUpdateAll );
+
+ css::uno::Any isAccelerated() const;
+ css::uno::Any getDeviceHandle() const;
+ css::uno::Any getSurfaceHandle() const;
+
+ void notifySizeUpdate( const css::awt::Rectangle& rBounds );
+ void setSize( const ::basegfx::B2ISize& rSize );
+
+ const ::cairo::SurfaceSharedPtr& getBufferSurface() const { return mpBufferSurface; }
+ ::cairo::SurfaceSharedPtr const & getWindowSurface() const;
+ ::cairo::SurfaceSharedPtr createSurface( const ::basegfx::B2ISize& rSize, int aContent );
+ ::cairo::SurfaceSharedPtr createSurface( BitmapSystemData const & rData, const Size& rSize );
+ const ::basegfx::B2ISize& getSizePixel() const { return maSize; }
+ void flush();
+
+ private:
+ /// Pointer to sprite canvas (owner of this helper), needed to create bitmaps
+ SpriteCanvas* mpSpriteCanvas;
+
+ ::cairo::SurfaceSharedPtr mpBufferSurface;
+
+ ::basegfx::B2ISize maSize;
+ bool mbFullScreen;
+ };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/cairo/cairo_spritehelper.cxx b/canvas/source/cairo/cairo_spritehelper.cxx
new file mode 100644
index 000000000..b3806f0e9
--- /dev/null
+++ b/canvas/source/cairo/cairo_spritehelper.cxx
@@ -0,0 +1,195 @@
+/* -*- 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 <rtl/math.hxx>
+#include <tools/diagnose_ex.h>
+
+#include <cairo.h>
+#include <pixman.h>
+
+#include "cairo_spritehelper.hxx"
+
+using namespace ::cairo;
+using namespace ::com::sun::star;
+
+namespace cairocanvas
+{
+ SpriteHelper::SpriteHelper() :
+ mbTextureDirty(true)
+ {}
+
+ void SpriteHelper::init( const geometry::RealSize2D& rSpriteSize,
+ const SpriteCanvasRef& rSpriteCanvas )
+ {
+ ENSURE_OR_THROW( rSpriteCanvas,
+ "SpriteHelper::init(): Invalid device, sprite canvas or surface" );
+
+ mpSpriteCanvas = rSpriteCanvas;
+ mbTextureDirty = true;
+
+ // also init base class
+ CanvasCustomSpriteHelper::init( rSpriteSize, rSpriteCanvas );
+ }
+
+ void SpriteHelper::setSurface( const SurfaceSharedPtr& pBufferSurface )
+ {
+ mpBufferSurface = pBufferSurface;
+ }
+
+ void SpriteHelper::disposing()
+ {
+ mpBufferSurface.reset();
+ mpSpriteCanvas.clear();
+
+ // forward to parent
+ CanvasCustomSpriteHelper::disposing();
+ }
+
+ void SpriteHelper::redraw( const CairoSharedPtr& pCairo,
+ const ::basegfx::B2DPoint& rPos,
+ bool& /*io_bSurfacesDirty*/,
+ bool /*bBufferedUpdate*/ ) const
+ {
+#ifdef CAIRO_CANVAS_PERF_TRACE
+ struct timespec aTimer;
+ mxDevice->startPerfTrace( &aTimer );
+#endif
+
+ const double fAlpha( getAlpha() );
+ const ::basegfx::B2DHomMatrix aTransform( getTransformation() );
+
+ if( !isActive() || ::basegfx::fTools::equalZero( fAlpha ) )
+ return;
+
+ SAL_INFO( "canvas.cairo", "CanvasCustomSprite::redraw called");
+ if( !pCairo )
+ return;
+
+ basegfx::B2DVector aSize = getSizePixel();
+ cairo_save( pCairo.get() );
+
+ double fX, fY;
+
+ fX = rPos.getX();
+ fY = rPos.getY();
+
+ if( !aTransform.isIdentity() )
+ {
+ cairo_matrix_t aMatrix, aInverseMatrix;
+ cairo_matrix_init( &aMatrix,
+ aTransform.get( 0, 0 ), aTransform.get( 1, 0 ), aTransform.get( 0, 1 ),
+ aTransform.get( 1, 1 ), aTransform.get( 0, 2 ), aTransform.get( 1, 2 ) );
+
+ aMatrix.x0 = basegfx::fround( aMatrix.x0 );
+ aMatrix.y0 = basegfx::fround( aMatrix.y0 );
+
+ cairo_matrix_init( &aInverseMatrix, aMatrix.xx, aMatrix.yx, aMatrix.xy, aMatrix.yy, aMatrix.x0, aMatrix.y0 );
+ cairo_matrix_invert( &aInverseMatrix );
+ cairo_matrix_transform_distance( &aInverseMatrix, &fX, &fY );
+
+ cairo_set_matrix( pCairo.get(), &aMatrix );
+ }
+
+ fX = basegfx::fround( fX );
+ fY = basegfx::fround( fY );
+
+ cairo_matrix_t aOrigMatrix;
+ cairo_get_matrix( pCairo.get(), &aOrigMatrix );
+ cairo_translate( pCairo.get(), fX, fY );
+
+ if( getClip().is() )
+ {
+ const uno::Reference<rendering::XPolyPolygon2D>& rClip( getClip() );
+
+ ::basegfx::B2DPolyPolygon aClipPoly(
+ ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(
+ rClip ));
+
+ doPolyPolygonImplementation( aClipPoly, Clip, pCairo.get(),
+ nullptr, SurfaceProviderRef(mpSpriteCanvas),
+ rClip->getFillRule() );
+ }
+
+ SAL_INFO( "canvas.cairo","aSize " << aSize.getX() << " x " << aSize.getY() << " position: " << fX << "," << fY );
+ cairo_rectangle( pCairo.get(), 0, 0, floor( aSize.getX() ), floor( aSize.getY() ) );
+ cairo_clip( pCairo.get() );
+ cairo_set_matrix( pCairo.get(), &aOrigMatrix );
+
+ cairo_matrix_t aInverseMatrix = aOrigMatrix;
+ bool matrixProblem = false;
+ // tdf#125949: Cairo internally uses the pixman library, and _cairo_matrix_to_pixman_matrix()
+ // checks all matrix components to fix PIXMAN_MAX_INT, which is about 32k. Which means that
+ // if our transformation is large, such as an initial step of some zooming animations,
+ // the conversion will fail. To make things worse, once something in cairo fails, it's treated
+ // as a fatal error, the error status of that cairo_t gets set, and there's no way to reset it
+ // besides recreating the whole cairo_t
+ // (https://lists.cairographics.org/archives/cairo/2006-September/007892.html).
+ // So copy&paste PIXMAN_MAX_INT here, and if our matrix could fail, bail out.
+#define PIXMAN_MAX_INT ((pixman_fixed_1 >> 1) - pixman_fixed_e) /* need to ensure deltas also fit */
+ if(cairo_matrix_invert(&aInverseMatrix) == CAIRO_STATUS_SUCCESS)
+ {
+ if(abs(aOrigMatrix.xx) > PIXMAN_MAX_INT || abs(aOrigMatrix.xx) > PIXMAN_MAX_INT
+ || abs(aOrigMatrix.xy) > PIXMAN_MAX_INT || abs(aOrigMatrix.yx) > PIXMAN_MAX_INT
+ || abs(aOrigMatrix.x0) > PIXMAN_MAX_INT || abs(aOrigMatrix.y0) > PIXMAN_MAX_INT
+ || abs(aInverseMatrix.xx) > PIXMAN_MAX_INT || abs(aInverseMatrix.xx) > PIXMAN_MAX_INT
+ || abs(aInverseMatrix.xy) > PIXMAN_MAX_INT || abs(aInverseMatrix.yx) > PIXMAN_MAX_INT
+ || abs(aInverseMatrix.x0) > PIXMAN_MAX_INT || abs(aInverseMatrix.y0) > PIXMAN_MAX_INT)
+ matrixProblem = true;
+#undef PIXMAN_MAX_INT
+ }
+ else
+ matrixProblem = true;
+ if(matrixProblem)
+ {
+ SAL_WARN( "canvas.cairo", "matrix would overflow PIXMAN_MAX_INT, avoiding drawing" );
+ cairo_restore( pCairo.get() );
+ return;
+ }
+
+ if( isContentFullyOpaque() )
+ cairo_set_operator( pCairo.get(), CAIRO_OPERATOR_SOURCE );
+ cairo_set_source_surface( pCairo.get(),
+ mpBufferSurface->getCairoSurface().get(),
+ fX, fY );
+ if( ::rtl::math::approxEqual( fAlpha, 1.0 ) )
+ cairo_paint( pCairo.get() );
+ else
+ cairo_paint_with_alpha( pCairo.get(), fAlpha );
+
+ cairo_restore( pCairo.get() );
+
+#ifdef CAIRO_CANVAS_PERF_TRACE
+ mxDevice->stopPerfTrace( &aTimer, "sprite redraw" );
+#endif
+ }
+
+ ::basegfx::B2DPolyPolygon SpriteHelper::polyPolygonFromXPolyPolygon2D( uno::Reference< rendering::XPolyPolygon2D >& xPoly ) const
+ {
+ return ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(xPoly);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/cairo/cairo_spritehelper.hxx b/canvas/source/cairo/cairo_spritehelper.hxx
new file mode 100644
index 000000000..df0919e7f
--- /dev/null
+++ b/canvas/source/cairo/cairo_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 <base/canvascustomspritehelper.hxx>
+
+#include <basegfx/point/b2dpoint.hxx>
+
+#include "cairo_spritecanvas.hxx"
+
+
+namespace cairocanvas
+{
+ /* 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 rDevice
+ DX device 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 );
+
+ void disposing();
+
+ void setSurface( const ::cairo::SurfaceSharedPtr& pBufferSurface );
+
+ /** Repaint sprite content to associated sprite canvas
+
+ @param rPos
+ Output position (sprite's own position is disregarded)
+
+ @param io_bSurfacesDirty
+ When true, the referenced sprite surfaces (backBuffer and
+ backBufferMask) have been modified since last call.
+
+ @param bBufferedUpdate
+ When true, the redraw does <em>not</em> happen directly on
+ the front buffer, but within a VDev. Used to speed up
+ drawing.
+ */
+ void redraw( const ::cairo::CairoSharedPtr& pCairo,
+ const ::basegfx::B2DPoint& rPos,
+ bool& bSurfacesDirty,
+ bool bBufferedUpdate ) const;
+
+ private:
+ virtual ::basegfx::B2DPolyPolygon polyPolygonFromXPolyPolygon2D(
+ css::uno::Reference< css::rendering::XPolyPolygon2D >& xPoly ) const override;
+
+
+ SpriteCanvasRef mpSpriteCanvas;
+ ::cairo::SurfaceSharedPtr mpBufferSurface;
+ mutable bool mbTextureDirty; // when true, texture needs update
+ };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/cairo/cairo_surfaceprovider.hxx b/canvas/source/cairo/cairo_surfaceprovider.hxx
new file mode 100644
index 000000000..a5115a507
--- /dev/null
+++ b/canvas/source/cairo/cairo_surfaceprovider.hxx
@@ -0,0 +1,70 @@
+/* -*- 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/XInterface.hpp>
+
+#include <basegfx/vector/b2isize.hxx>
+#include <vcl/cairo.hxx>
+
+class OutputDevice;
+class Bitmap;
+
+namespace cairocanvas
+{
+ /* Definition of RepaintTarget interface */
+
+ /** Target interface for XCachedPrimitive implementations
+
+ This interface must be implemented on all canvas
+ implementations that hand out XCachedPrimitives
+ */
+ class SurfaceProvider : public css::uno::XInterface
+ {
+ public:
+ virtual ~SurfaceProvider() {}
+
+ /** Query surface from this provider
+
+ This should return the default surface to render on.
+ */
+ virtual ::cairo::SurfaceSharedPtr getSurface() = 0;
+
+ /// create new surface in given size
+ virtual ::cairo::SurfaceSharedPtr createSurface( const ::basegfx::B2ISize& rSize,
+ int aContent ) = 0;
+ /// create new surface from given bitmap
+ virtual ::cairo::SurfaceSharedPtr createSurface( ::Bitmap& rBitmap ) = 0;
+
+ /** convert surface from alpha to non-alpha, does not copy content
+ channel. returns new surface on success, NULL otherwise
+ */
+ virtual ::cairo::SurfaceSharedPtr changeSurface() = 0;
+
+ /** Provides the underlying vcl outputdevice this surface renders on
+ */
+ virtual OutputDevice* getOutputDevice() = 0;
+ };
+
+ typedef ::rtl::Reference< SurfaceProvider > SurfaceProviderRef;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/cairo/cairo_textlayout.cxx b/canvas/source/cairo/cairo_textlayout.cxx
new file mode 100644
index 000000000..cbbf02c56
--- /dev/null
+++ b/canvas/source/cairo/cairo_textlayout.cxx
@@ -0,0 +1,354 @@
+/* -*- 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 <math.h>
+
+#include <com/sun/star/rendering/TextDirection.hpp>
+#include <canvas/canvastools.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/numeric/ftools.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <tools/diagnose_ex.h>
+#include <utility>
+#include <vcl/metric.hxx>
+#include <vcl/virdev.hxx>
+
+
+#include "cairo_textlayout.hxx"
+
+using namespace ::cairo;
+using namespace ::com::sun::star;
+
+namespace cairocanvas
+{
+ namespace
+ {
+ void setupLayoutMode( OutputDevice& rOutDev,
+ sal_Int8 nTextDirection )
+ {
+ // TODO(P3): avoid if already correctly set
+ vcl::text::ComplexTextLayoutFlags nLayoutMode = vcl::text::ComplexTextLayoutFlags::Default;
+ switch( nTextDirection )
+ {
+ case rendering::TextDirection::WEAK_LEFT_TO_RIGHT:
+ break;
+ case rendering::TextDirection::STRONG_LEFT_TO_RIGHT:
+ nLayoutMode = vcl::text::ComplexTextLayoutFlags::BiDiStrong;
+ break;
+ case rendering::TextDirection::WEAK_RIGHT_TO_LEFT:
+ nLayoutMode = vcl::text::ComplexTextLayoutFlags::BiDiRtl;
+ break;
+ case rendering::TextDirection::STRONG_RIGHT_TO_LEFT:
+ nLayoutMode = vcl::text::ComplexTextLayoutFlags::BiDiRtl | vcl::text::ComplexTextLayoutFlags::BiDiStrong;
+ break;
+ default:
+ break;
+ }
+
+ // set calculated layout mode. Origin is always the left edge,
+ // as required at the API spec
+ rOutDev.SetLayoutMode( nLayoutMode | vcl::text::ComplexTextLayoutFlags::TextOriginLeft );
+ }
+ }
+
+ TextLayout::TextLayout( rendering::StringContext aText,
+ sal_Int8 nDirection,
+ sal_Int64 /*nRandomSeed*/,
+ CanvasFont::Reference rFont,
+ SurfaceProviderRef rRefDevice ) :
+ TextLayout_Base( m_aMutex ),
+ maText(std::move( aText )),
+ mpFont(std::move( rFont )),
+ mpRefDevice(std::move( rRefDevice )),
+ mnTextDirection( nDirection )
+ {
+ }
+
+ TextLayout::~TextLayout()
+ {
+ }
+
+ void SAL_CALL TextLayout::disposing()
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ mpFont.clear();
+ mpRefDevice.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.cairo", "TextLayout::applyLogicalAdvancements(): mismatching number of advancements" );
+ throw lang::IllegalArgumentException("mismatching number of advancements", static_cast<cppu::OWeakObject*>(this), 1);
+ }
+
+ maLogicalAdvancements = aAdvancements;
+ }
+
+ geometry::RealRectangle2D SAL_CALL TextLayout::queryTextBounds( )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ OutputDevice* pOutDev = mpRefDevice->getOutputDevice();
+ if( !pOutDev )
+ return geometry::RealRectangle2D();
+
+ ScopedVclPtrInstance< VirtualDevice > pVDev( *pOutDev );
+ pVDev->SetFont( mpFont->getVCLFont() );
+
+ // need metrics for Y offset, the XCanvas always renders
+ // relative to baseline
+ const ::FontMetric& aMetric( pVDev->GetFontMetric() );
+
+ setupLayoutMode( *pVDev, mnTextDirection );
+
+ const sal_Int32 nAboveBaseline( -aMetric.GetAscent() );
+ const sal_Int32 nBelowBaseline( aMetric.GetDescent() );
+
+ if( maLogicalAdvancements.hasElements() )
+ {
+ return geometry::RealRectangle2D( 0, nAboveBaseline,
+ maLogicalAdvancements[ maLogicalAdvancements.getLength()-1 ],
+ nBelowBaseline );
+ }
+ else
+ {
+ return geometry::RealRectangle2D( 0, nAboveBaseline,
+ pVDev->GetTextWidth(
+ maText.Text,
+ ::canvas::tools::numeric_cast<sal_uInt16>(maText.StartPosition),
+ ::canvas::tools::numeric_cast<sal_uInt16>(maText.Length) ),
+ nBelowBaseline );
+ }
+ }
+
+ 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;
+ }
+
+ /**
+ * TextLayout::draw
+ *
+ * Cairo-based text rendering. Draw text directly on the cairo surface with cairo fonts.
+ * Avoid using VCL VirtualDevices for that, bypassing VCL DrawText functions, when possible
+ *
+ * Note: some text effects are not rendered due to lacking generic canvas or cairo canvas
+ * implementation. See issues 92657, 92658, 92659, 92660, 97529
+ **/
+ void TextLayout::draw( OutputDevice& rOutDev,
+ const Point& rOutpos,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState ) const
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ setupLayoutMode( rOutDev, mnTextDirection );
+
+ std::vector<sal_Int32> aOffsets(maLogicalAdvancements.getLength());
+
+ if( maLogicalAdvancements.hasElements() )
+ setupTextOffsets( aOffsets.data(), maLogicalAdvancements, viewState, renderState );
+
+ if (maLogicalAdvancements.hasElements())
+ {
+ rOutDev.DrawTextArray( rOutpos, maText.Text, aOffsets,
+ ::canvas::tools::numeric_cast<sal_uInt16>(maText.StartPosition),
+ ::canvas::tools::numeric_cast<sal_uInt16>(maText.Length) );
+ }
+ else
+ {
+ rOutDev.DrawText( rOutpos, maText.Text,
+ ::canvas::tools::numeric_cast<sal_uInt16>(maText.StartPosition),
+ ::canvas::tools::numeric_cast<sal_uInt16>(maText.Length) );
+ }
+ }
+
+ namespace
+ {
+ class OffsetTransformer
+ {
+ public:
+ explicit OffsetTransformer( ::basegfx::B2DHomMatrix aMat ) :
+ maMatrix(std::move( aMat ))
+ {
+ }
+
+ sal_Int32 operator()( const double& rOffset )
+ {
+ // This is an optimization of the normal rMat*[x,0]
+ // transformation of the advancement vector (in x
+ // direction), followed by a length calculation of the
+ // resulting vector: advancement' =
+ // ||rMat*[x,0]||. Since advancements are vectors, we
+ // can ignore translational components, thus if [x,0],
+ // it follows that rMat*[x,0]=[x',0] holds. Thus, we
+ // just have to calc the transformation of the x
+ // component.
+
+ // TODO(F2): Handle non-horizontal advancements!
+ return ::basegfx::fround( hypot(maMatrix.get(0,0)*rOffset,
+ maMatrix.get(1,0)*rOffset) );
+ }
+
+ private:
+ ::basegfx::B2DHomMatrix maMatrix;
+ };
+ }
+
+ void TextLayout::setupTextOffsets( sal_Int32* outputOffsets,
+ const uno::Sequence< double >& inputOffsets,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState ) const
+ {
+ ENSURE_OR_THROW( outputOffsets!=nullptr,
+ "TextLayout::setupTextOffsets offsets NULL" );
+
+ ::basegfx::B2DHomMatrix aMatrix;
+
+ ::canvas::tools::mergeViewAndRenderTransform(aMatrix,
+ viewState,
+ renderState);
+
+ // fill integer offsets
+ std::transform( inputOffsets.begin(),
+ inputOffsets.end(),
+ outputOffsets,
+ OffsetTransformer( aMatrix ) );
+ }
+
+ OUString SAL_CALL TextLayout::getImplementationName()
+ {
+ return "CairoCanvas::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/cairo/cairo_textlayout.hxx b/canvas/source/cairo/cairo_textlayout.hxx
new file mode 100644
index 000000000..fb14e620c
--- /dev/null
+++ b/canvas/source/cairo/cairo_textlayout.hxx
@@ -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 .
+ */
+
+#pragma once
+
+#include <cppuhelper/compbase.hxx>
+#include <cppuhelper/basemutex.hxx>
+
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/rendering/RenderState.hpp>
+#include <com/sun/star/rendering/ViewState.hpp>
+#include <com/sun/star/rendering/XTextLayout.hpp>
+
+#include <vcl/outdev.hxx>
+
+#include "cairo_canvasfont.hxx"
+
+
+/* Definition of TextLayout class */
+
+namespace cairocanvas
+{
+ typedef ::cppu::WeakComponentImplHelper< css::rendering::XTextLayout,
+ css::lang::XServiceInfo > TextLayout_Base;
+
+ class TextLayout : public ::cppu::BaseMutex,
+ public TextLayout_Base
+ {
+ public:
+ /// make noncopyable
+ TextLayout(const TextLayout&) = delete;
+ const TextLayout& operator=(const TextLayout&) = delete;
+
+ TextLayout( css::rendering::StringContext aText,
+ sal_Int8 nDirection,
+ sal_Int64 nRandomSeed,
+ CanvasFont::Reference rFont,
+ SurfaceProviderRef rRefDevice );
+
+ /// 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::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;
+
+ void draw( OutputDevice& rOutDev,
+ const Point& rOutpos,
+ const css::rendering::ViewState& viewState,
+ const css::rendering::RenderState& renderState ) const;
+
+ void setupTextOffsets( sal_Int32* outputOffsets,
+ const css::uno::Sequence< double >& inputOffsets,
+ const css::rendering::ViewState& viewState,
+ const css::rendering::RenderState& renderState ) const;
+
+ protected:
+ virtual ~TextLayout() override; // we're a ref-counted UNO class. _We_ destroy ourselves.
+
+ private:
+ css::rendering::StringContext maText;
+ css::uno::Sequence< double > maLogicalAdvancements;
+ CanvasFont::Reference mpFont;
+ SurfaceProviderRef mpRefDevice;
+ sal_Int8 mnTextDirection;
+ };
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/cairo/cairocanvas.component b/canvas/source/cairo/cairocanvas.component
new file mode 100644
index 000000000..7a201f582
--- /dev/null
+++ b/canvas/source/cairo/cairocanvas.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.Canvas.Cairo"
+ constructor="com_sun_star_comp_rendering_Canvas_Cairo_get_implementation">
+ <service name="com.sun.star.rendering.Canvas.Cairo"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.rendering.SpriteCanvas.Cairo"
+ constructor="com_sun_star_comp_rendering_SpriteCanvas_Cairo_get_implementation">
+ <service name="com.sun.star.rendering.SpriteCanvas.Cairo"/>
+ </implementation>
+</component>
diff --git a/canvas/source/directx/directx9canvas.component b/canvas/source/directx/directx9canvas.component
new file mode 100644
index 000000000..ac75940d2
--- /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 000000000..98c4c14b9
--- /dev/null
+++ b/canvas/source/directx/dx_9rm.cxx
@@ -0,0 +1,1213 @@
+/* -*- 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 <tools/diagnose_ex.h>
+#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::B2IVector 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::B2IVector 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::B2IVector& 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::B2IVector 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),
+ maSize()
+ {
+ ImplRenderModuleGuard aGuard( mrRenderModule );
+
+#ifdef FAKE_MAX_NUMBER_TEXTURES
+ ++gNumSurfaces;
+ if(gNumSurfaces >= FAKE_MAX_NUMBER_TEXTURES)
+ return;
+#endif
+
+#ifdef FAKE_MAX_TEXTURE_SIZE
+ if(rSize.getX() > FAKE_MAX_TEXTURE_SIZE)
+ return;
+ if(rSize.getY() > FAKE_MAX_TEXTURE_SIZE)
+ return;
+#endif
+
+ ENSURE_ARG_OR_THROW(rSize.getX() > 0 && rSize.getY() > 0,
+ "DXSurface::DXSurface(): request for zero-sized surface");
+
+ sal::systools::COMReference<IDirect3DDevice9> pDevice(rRenderModule.getDevice());
+
+ IDirect3DTexture9 *pTexture(nullptr);
+ if(FAILED(pDevice->CreateTexture(
+ rSize.getX(),
+ rSize.getY(),
+ 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.getX(),
+ rect.left + sal_Int32(rSourceRect.getWidth()+1));
+ rect.bottom = std::min(maSize.getY(),
+ rect.top + sal_Int32(rSourceRect.getHeight()+1));
+ const bool bClearRightColumn( rect.right < maSize.getX() );
+ const bool bClearBottomRow( rect.bottom < maSize.getY() );
+
+ 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;
+ }
+
+
+ // DXSurface::getSize
+
+
+ ::basegfx::B2IVector DXSurface::getSize()
+ {
+ return maSize;
+ }
+
+ // DXRenderModule::DXRenderModule
+
+
+ DXRenderModule::DXRenderModule( const vcl::Window& rWindow ) :
+ mhWnd(nullptr),
+ mpDevice(),
+ mpDirect3D9(),
+ mpSwapChain(),
+ mpVertexBuffer(),
+ mpTexture(),
+ maSize(),
+ maVertexCache(),
+ mnCount(0),
+ mnBeginSceneCount(0),
+ mbCanUseDynamicTextures(false),
+ mbError( false ),
+ meType( PrimitiveType::Unknown ),
+ maPageSize(),
+ 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,aPageSize);
+ 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.setX(static_cast<sal_Int32>(rSizePixel.Width()));
+ maSize.setY(static_cast<sal_Int32>(rSizePixel.Height()));
+
+ // let the child window cover the same size as the parent window.
+ mpWindow->setPosSizePixel(0,0,maSize.getX(),maSize.getY());
+
+ // 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.getX(),
+ sal_Int32(d3ddm.Width));
+ mad3dpp.BackBufferHeight = std::max(maSize.getY(),
+ 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::B2IVector& 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.getX(),
+ rSize.getY(),
+ 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.getX() == static_cast<sal_Int32>(rect.getWidth()) &&
+ maSize.getY() == static_cast<sal_Int32>(rect.getHeight()))
+ return;
+
+ // TODO(Q2): use numeric cast to prevent overflow
+ maSize.setX(static_cast<sal_Int32>(rect.getWidth()));
+ maSize.setY(static_cast<sal_Int32>(rect.getHeight()));
+
+ mpWindow->setPosSizePixel(0,0,maSize.getX(),maSize.getY());
+
+ // resize back buffer, if necessary
+
+
+ // don't attempt to create anything if the
+ // requested size is NULL.
+ if(!(maSize.getX()))
+ return;
+ if(!(maSize.getY()))
+ return;
+
+ // backbuffer too small (might happen, if window is
+ // maximized across multiple monitors)
+ if( sal_Int32(mad3dpp.BackBufferWidth) < maSize.getX() ||
+ sal_Int32(mad3dpp.BackBufferHeight) < maSize.getY() )
+ {
+ mad3dpp.BackBufferWidth = maSize.getX();
+ mad3dpp.BackBufferHeight = maSize.getY();
+
+ // 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.getX()))
+ aSize.setX(rPageSize.getX());
+ if(!(aSize.getY()))
+ aSize.setY(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 000000000..2f42170d6
--- /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 <tools/diagnose_ex.h>
+
+#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::B2IVector& 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.getX(),
+ maSize.getY(),
+ PixelFormat32bppARGB);
+ }
+ else
+ {
+ mpBitmap = std::make_shared<Gdiplus::Bitmap>(
+ maSize.getX(),
+ maSize.getY(),
+ PixelFormat24bppRGB);
+ }
+
+ mpGraphics = tools::createGraphicsFromBitmap(mpBitmap);
+ }
+
+ BitmapSharedPtr DXBitmap::getBitmap() const
+ {
+ return mpBitmap;
+ }
+
+ GraphicsSharedPtr DXBitmap::getGraphics()
+ {
+ return mpGraphics;
+ }
+
+ ::basegfx::B2IVector 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();
+ }
+
+ // 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.getX(),maSize.getY() );
+
+ 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();
+ }
+ }
+
+ uno::Sequence< sal_Int8 > DXBitmap::getPixel( rendering::IntegerBitmapLayout& /*bitmapLayout*/,
+ const geometry::IntegerPoint2D& pos )
+ {
+ const geometry::IntegerSize2D aSize( maSize.getX(),maSize.getY() );
+
+ 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 000000000..127ee5fc5
--- /dev/null
+++ b/canvas/source/directx/dx_bitmap.hxx
@@ -0,0 +1,84 @@
+/* -*- 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::B2IVector& rSize,
+ bool bWithAlpha );
+
+ virtual GraphicsSharedPtr getGraphics() override;
+
+ virtual BitmapSharedPtr getBitmap() const override;
+ virtual ::basegfx::B2IVector 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::B2IVector 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 000000000..9733853f5
--- /dev/null
+++ b/canvas/source/directx/dx_bitmapcanvashelper.cxx
@@ -0,0 +1,222 @@
+/* -*- 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 <tools/diagnose_ex.h>
+
+#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 000000000..46f970493
--- /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 000000000..ad83abee0
--- /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 000000000..7f29813c9
--- /dev/null
+++ b/canvas/source/directx/dx_canvas.cxx
@@ -0,0 +1,258 @@
+// /* -*- 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 <tools/diagnose_ex.h>
+#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();
+ xCanvas->acquire();
+ return static_cast<cppu::OWeakObject*>(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();
+ xCanvas->acquire();
+ return static_cast<cppu::OWeakObject*>(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 000000000..5a00e07fa
--- /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 000000000..217d3edd2
--- /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 <tools/diagnose_ex.h>
+#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::B2IVector aSize(mpBitmap->getSize());
+ HBITMAP hBmpBitmap = CreateCompatibleBitmap( hScreenDC,
+ aSize.getX(),
+ aSize.getY() );
+ if( !hBmpBitmap )
+ return aRes;
+
+ BITMAPINFOHEADER aBIH;
+
+ aBIH.biSize = sizeof( BITMAPINFOHEADER );
+ aBIH.biWidth = aSize.getX();
+ aBIH.biHeight = -aSize.getY();
+ 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.getX();
+ aBmpData.Height = aSize.getY();
+ aBmpData.Stride = 4*aBmpData.Width;
+ aBmpData.PixelFormat = PixelFormat32bppARGB;
+ aBmpData.Scan0 = nullptr;
+ const Gdiplus::Rect aRect( 0,0,aSize.getX(),aSize.getY() );
+ 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.getY(), 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::B2IVector aSize(mpBitmap->getSize());
+ HBITMAP hBmpBitmap = CreateCompatibleBitmap( hScreenDC, aSize.getX(), aSize.getY() );
+ if( !hBmpBitmap )
+ return aRes;
+
+ aDIB.bmiHeader.biSize = sizeof( BITMAPINFOHEADER );
+ aDIB.bmiHeader.biWidth = aSize.getX();
+ aDIB.bmiHeader.biHeight = -aSize.getY();
+ 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.getX();
+ aBmpData.Height = aSize.getY();
+ aBmpData.Stride = 4*aBmpData.Width;
+ aBmpData.PixelFormat = PixelFormat32bppARGB;
+ aBmpData.Scan0 = nullptr;
+ const Gdiplus::Rect aRect( 0,0,aSize.getX(),aSize.getY() );
+ 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.getX() + 3) & ~3);
+ std::unique_ptr<sal_uInt8[]> pAlphaBits( new sal_uInt8[nScanWidth*aSize.getY()] );
+ const sal_uInt8* pInBits=static_cast<sal_uInt8*>(aBmpData.Scan0);
+ pInBits+=3;
+ for( sal_Int32 y=0; y<aSize.getY(); ++y )
+ {
+ sal_uInt8* pOutBits=pAlphaBits.get()+y*nScanWidth;
+ for( sal_Int32 x=0; x<aSize.getX(); ++x )
+ {
+ *pOutBits++ = 255-*pInBits;
+ pInBits += 4;
+ }
+ }
+
+ pGDIPlusBitmap->UnlockBits( &aBmpData );
+
+ // set bits to newly create HBITMAP
+ SetDIBits( hScreenDC, hBmpBitmap, 0,
+ aSize.getY(), 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 000000000..b1725b447
--- /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 000000000..a0e58f29d
--- /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 <tools/diagnose_ex.h>
+
+#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::B2IVector(
+ ::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 000000000..2411a1527
--- /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 000000000..aeb307052
--- /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 000000000..c5f369d00
--- /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 000000000..2ca2e09e2
--- /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 <tools/diagnose_ex.h>
+
+#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.getX(), maOutputOffset.getY()));
+ 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.getX(), maOutputOffset.getY()));
+ 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 000000000..54731a08e
--- /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 000000000..0bea70fc8
--- /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 <tools/diagnose_ex.h>
+
+#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 000000000..afb52de30
--- /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 <tools/diagnose_ex.h>
+
+#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 000000000..14a77d19d
--- /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 000000000..a1b116f99
--- /dev/null
+++ b/canvas/source/directx/dx_devicehelper.cxx
@@ -0,0 +1,210 @@
+/* -*- 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 <tools/diagnose_ex.h>
+#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();
+ }
+
+ namespace
+ {
+ struct DeviceColorSpace: public rtl::StaticWithInit<uno::Reference<rendering::XColorSpace>,
+ DeviceColorSpace>
+ {
+ uno::Reference<rendering::XColorSpace> operator()()
+ {
+ return vcl::unotools::createStandardColorSpace();
+ }
+ };
+ }
+
+ uno::Reference<rendering::XColorSpace> DeviceHelper::getColorSpace() const
+ {
+ // always the same
+ return DeviceColorSpace::get();
+ }
+}
+
+/* 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 000000000..d5fa62b07
--- /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 000000000..85c143cff
--- /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 000000000..4b0a0f52a
--- /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 000000000..2d6d61884
--- /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 000000000..6d3f4d6f6
--- /dev/null
+++ b/canvas/source/directx/dx_ibitmap.hxx
@@ -0,0 +1,61 @@
+/* -*- 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_graphicsprovider.hxx"
+
+namespace dxcanvas
+{
+ /// Interface for internal canvas bitmap objects
+ struct IBitmap : public GraphicsProvider
+ {
+ virtual BitmapSharedPtr getBitmap() const = 0;
+ virtual ::basegfx::B2IVector 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 000000000..7f464a851
--- /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 <tools/diagnose_ex.h>
+
+#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 000000000..2c6b85ce8
--- /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 000000000..53ec2953e
--- /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 000000000..eaec483a0
--- /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 000000000..6e8a8fb2a
--- /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::B2IVector& 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 000000000..c1f75e1b1
--- /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 000000000..0a16f2ae9
--- /dev/null
+++ b/canvas/source/directx/dx_spritecanvas.cxx
@@ -0,0 +1,193 @@
+/* -*- 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 <tools/diagnose_ex.h>
+
+#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();
+ xCanvas->acquire();
+ return static_cast<cppu::OWeakObject*>(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 000000000..081337f72
--- /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 000000000..416c66ba5
--- /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 <tools/diagnose_ex.h>
+
+#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 000000000..5c2d98ba8
--- /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 000000000..ee339c8af
--- /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 <tools/diagnose_ex.h>
+#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!",
+ static_cast< ::cppu::OWeakObject* >(&rSpriteCanvas) );
+ }
+
+ // 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 000000000..ad7417770
--- /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 000000000..135c59e3e
--- /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 <tools/diagnose_ex.h>
+
+#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 000000000..86e6dbaf6
--- /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 000000000..b6e4e1248
--- /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 <tools/diagnose_ex.h>
+#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::B2IVector& 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::B2IVector 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.getX();
+ }
+
+ sal_uInt32 DXColorBuffer::getHeight() const
+ {
+ return maSize.getY();
+ }
+
+ 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::B2IVector& 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::B2IVector maSize;
+ mutable Gdiplus::BitmapData aBmpData;
+ BitmapSharedPtr mpGDIPlusBitmap;
+ };
+
+ sal_uInt8* GDIColorBuffer::lock() const
+ {
+ aBmpData.Width = maSize.getX();
+ aBmpData.Height = maSize.getY();
+ 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.getX();
+ }
+
+ sal_uInt32 GDIColorBuffer::getHeight() const
+ {
+ return maSize.getY();
+ }
+
+ 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::B2IVector& 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::B2IVector DXSurfaceBitmap::getSize() const
+ {
+ return maSize;
+ }
+
+
+ // DXSurfaceBitmap::init
+
+
+ void DXSurfaceBitmap::init()
+ {
+ // create container for pixel data
+ if(mbAlpha)
+ {
+ mpGDIPlusBitmap = std::make_shared<Gdiplus::Bitmap>(
+ maSize.getX(),
+ maSize.getY(),
+ 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::B2IVector& 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.getX(),maSize.getY(),
+ 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.getX(),maSize.getY() );
+
+ 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.getX(),
+ "CanvasHelper::setPixel: X coordinate out of bounds" );
+ ENSURE_ARG_OR_THROW( pos.Y >= 0 && pos.Y < maSize.getY(),
+ "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.getX(),maSize.getY() );
+
+ 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.getX(),
+ "CanvasHelper::getPixel: X coordinate out of bounds" );
+ ENSURE_ARG_OR_THROW( pos.Y >= 0 && pos.Y < maSize.getY(),
+ "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 000000000..ec71f4823
--- /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::B2IVector& rSize,
+ const std::shared_ptr<canvas::ISurfaceProxyManager>& rMgr,
+ const IDXRenderModuleSharedPtr& rRenderModule,
+ bool bWithAlpha );
+
+ bool resize( const ::basegfx::B2IVector& rSize );
+ void clear();
+
+ virtual GraphicsSharedPtr getGraphics() override;
+
+ virtual BitmapSharedPtr getBitmap() const override;
+ virtual ::basegfx::B2IVector 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::B2IVector 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 000000000..075f2fc15
--- /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 000000000..4260383e2
--- /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 000000000..452b0d48e
--- /dev/null
+++ b/canvas/source/directx/dx_textlayout.cxx
@@ -0,0 +1,232 @@
+/* -*- 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;
+ }
+
+ 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,
+ 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 000000000..24d37e210
--- /dev/null
+++ b/canvas/source/directx/dx_textlayout.hxx
@@ -0,0 +1,104 @@
+/* -*- 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::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;
+ 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 000000000..20ff8bd44
--- /dev/null
+++ b/canvas/source/directx/dx_textlayout_drawhelper.cxx
@@ -0,0 +1,311 @@
+/* -*- 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 <tools/diagnose_ex.h>
+#include <tools/poly.hxx>
+#include <vcl/canvastools.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::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::DEFAULT);
+
+ // 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.getX(), rOutputOffset.getY());
+ }
+
+ // 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.getX(), rOutputOffset.getY());
+ }
+
+ 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() );
+ std::vector<sal_Int32> DXArray( nLen );
+ for( sal_Int32 i=0; i<nLen; ++i )
+ DXArray[i] = basegfx::fround( rLogicalAdvancements[i] );
+
+ // draw the String
+ xVirtualDevice->DrawTextArray( aEmptyPoint,
+ aText,
+ DXArray,
+ 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::DEFAULT);
+
+ // 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 000000000..0db8fca69
--- /dev/null
+++ b/canvas/source/directx/dx_textlayout_drawhelper.hxx
@@ -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 .
+ */
+
+#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::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 000000000..1544ec916
--- /dev/null
+++ b/canvas/source/directx/dx_vcltools.cxx
@@ -0,0 +1,322 @@
+/* -*- 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 <tools/diagnose_ex.h>
+#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
+ BitmapReadAccess* pReadAcc = rBmp.AcquireReadAccess();
+
+ // 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 );
+ }
+
+ Bitmap::ReleaseAccess( pReadAcc );
+ }
+ }
+ 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() );
+
+ Bitmap::ScopedReadAccess 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.GetAlpha().GetBitmap() );
+
+ Bitmap::ScopedReadAccess 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();
+
+ // our notion of alpha is
+ // different from the rest
+ // of the world's
+ *pCurrOutput++ = 255 - 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++;
+
+ // our notion of alpha is
+ // different from the rest
+ // of the world's
+ *pCurrOutput++ = 255 - 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();
+
+ // our notion of alpha is
+ // different from the rest
+ // of the world's
+ *pCurrOutput++ = 255 - static_cast<BYTE>(*pAScan++);
+ }
+ }
+ break;
+
+ case ScanlineFormat::N1BitLsbPal:
+ 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 000000000..433afa618
--- /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 000000000..4cd0007fa
--- /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 000000000..0ee68a1ad
--- /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>
diff --git a/canvas/source/factory/canvasfactory.component b/canvas/source/factory/canvasfactory.component
new file mode 100644
index 000000000..3f9cabf71
--- /dev/null
+++ b/canvas/source/factory/canvasfactory.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.CanvasFactory"
+ constructor="com_sun_star_comp_rendering_CanvasFactory_get_implementation">
+ <service name="com.sun.star.rendering.CanvasFactory"/>
+ </implementation>
+</component>
diff --git a/canvas/source/factory/cf_service.cxx b/canvas/source/factory/cf_service.cxx
new file mode 100644
index 000000000..a56551832
--- /dev/null
+++ b/canvas/source/factory/cf_service.cxx
@@ -0,0 +1,465 @@
+/* -*- 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 <mutex>
+#include <utility>
+#include <vector>
+
+#include <com/sun/star/configuration/theDefaultProvider.hpp>
+#include <com/sun/star/container/XHierarchicalNameAccess.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XServiceName.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <comphelper/propertysequence.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <o3tl/functional.hxx>
+#include <o3tl/string_view.hxx>
+#include <vcl/skia/SkiaHelper.hxx>
+#include <unotools/configmgr.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+
+
+namespace
+{
+
+class CanvasFactory
+ : public ::cppu::WeakImplHelper< lang::XServiceInfo,
+ lang::XMultiComponentFactory,
+ lang::XMultiServiceFactory >
+{
+ typedef std::pair< OUString, Sequence< OUString > > AvailPair;
+ typedef std::pair< OUString, OUString > CachePair;
+ typedef std::vector< AvailPair > AvailVector;
+ typedef std::vector< CachePair > CacheVector;
+
+
+ mutable std::mutex m_mutex;
+ Reference<XComponentContext> m_xContext;
+ Reference<container::XNameAccess> m_xCanvasConfigNameAccess;
+ AvailVector m_aAvailableImplementations;
+ AvailVector m_aAcceleratedImplementations;
+ AvailVector m_aAAImplementations;
+ mutable CacheVector m_aCachedImplementations;
+ mutable bool m_bCacheHasForcedLastImpl;
+ mutable bool m_bCacheHasUseAcceleratedEntry;
+ mutable bool m_bCacheHasUseAAEntry;
+
+ void checkConfigFlag( bool& r_bFlag,
+ bool& r_CacheFlag,
+ const OUString& nodeName ) const;
+ Reference<XInterface> use(
+ OUString const & serviceName,
+ Sequence<Any> const & args,
+ Reference<XComponentContext> const & xContext ) const;
+ Reference<XInterface> lookupAndUse(
+ OUString const & serviceName, Sequence<Any> const & args,
+ Reference<XComponentContext> const & xContext ) const;
+
+public:
+ virtual ~CanvasFactory() override;
+ explicit CanvasFactory( Reference<XComponentContext> const & xContext );
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( OUString const & serviceName ) override;
+ virtual Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+ // XMultiComponentFactory
+ virtual Sequence<OUString> SAL_CALL getAvailableServiceNames() override;
+ virtual Reference<XInterface> SAL_CALL createInstanceWithContext(
+ OUString const & name,
+ Reference<XComponentContext> const & xContext ) override;
+ virtual Reference<XInterface> SAL_CALL
+ createInstanceWithArgumentsAndContext(
+ OUString const & name,
+ Sequence<Any> const & args,
+ Reference<XComponentContext> const & xContext ) override;
+
+ // XMultiServiceFactory
+ virtual Reference<XInterface> SAL_CALL createInstance(
+ OUString const & name ) override;
+ virtual Reference<XInterface> SAL_CALL createInstanceWithArguments(
+ OUString const & name, Sequence<Any> const & args ) override;
+};
+
+CanvasFactory::CanvasFactory( Reference<XComponentContext> const & xContext ) :
+ m_xContext(xContext),
+ m_bCacheHasForcedLastImpl(),
+ m_bCacheHasUseAcceleratedEntry(),
+ m_bCacheHasUseAAEntry()
+{
+ if (!utl::ConfigManager::IsFuzzing())
+ {
+ try
+ {
+ // read out configuration for preferred services:
+ Reference<lang::XMultiServiceFactory> xConfigProvider(
+ configuration::theDefaultProvider::get( m_xContext ) );
+
+ uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
+ {
+ {"nodepath", uno::Any(OUString("/org.openoffice.Office.Canvas"))}
+ }));
+ m_xCanvasConfigNameAccess.set(
+ xConfigProvider->createInstanceWithArguments(
+ "com.sun.star.configuration.ConfigurationAccess",
+ aArgs ),
+ UNO_QUERY_THROW );
+
+ uno::Sequence<uno::Any> aArgs2(comphelper::InitAnyPropertySequence(
+ {
+ {"nodepath", uno::Any(OUString("/org.openoffice.Office.Canvas/CanvasServiceList"))}
+ }));
+ Reference<container::XNameAccess> xNameAccess(
+ xConfigProvider->createInstanceWithArguments(
+ "com.sun.star.configuration.ConfigurationAccess",
+ aArgs2 ), UNO_QUERY_THROW );
+ Reference<container::XHierarchicalNameAccess> xHierarchicalNameAccess(
+ xNameAccess, UNO_QUERY_THROW);
+
+ Sequence<OUString> serviceNames = xNameAccess->getElementNames();
+ const OUString* pCurr = serviceNames.getConstArray();
+ const OUString* const pEnd = pCurr + serviceNames.getLength();
+ while( pCurr != pEnd )
+ {
+ Reference<container::XNameAccess> xEntryNameAccess(
+ xHierarchicalNameAccess->getByHierarchicalName(*pCurr),
+ UNO_QUERY );
+
+ if( xEntryNameAccess.is() )
+ {
+ Sequence<OUString> implementationList;
+ if( xEntryNameAccess->getByName("PreferredImplementations") >>= implementationList )
+ {
+ m_aAvailableImplementations.emplace_back(*pCurr,implementationList );
+ }
+ if( xEntryNameAccess->getByName("AcceleratedImplementations") >>= implementationList )
+ {
+ m_aAcceleratedImplementations.emplace_back(*pCurr,implementationList );
+ }
+ if( xEntryNameAccess->getByName("AntialiasingImplementations") >>= implementationList )
+ {
+ m_aAAImplementations.emplace_back(*pCurr,implementationList );
+ }
+
+ }
+
+ ++pCurr;
+ }
+ }
+ catch (const RuntimeException &)
+ {
+ throw;
+ }
+ catch (const Exception&)
+ {
+ }
+ }
+
+ if (m_aAvailableImplementations.empty())
+ {
+ // Ugh. Looks like configuration is borked. Fake minimal
+ // setup.
+ m_aAvailableImplementations.emplace_back(OUString("com.sun.star.rendering.Canvas"),
+ Sequence<OUString>{ "com.sun.star.comp.rendering.Canvas.VCL" } );
+
+ m_aAvailableImplementations.emplace_back(OUString("com.sun.star.rendering.SpriteCanvas"),
+ Sequence<OUString>{ "com.sun.star.comp.rendering.SpriteCanvas.VCL" } );
+ }
+}
+
+CanvasFactory::~CanvasFactory()
+{
+}
+
+
+// XServiceInfo
+OUString CanvasFactory::getImplementationName()
+{
+ return "com.sun.star.comp.rendering.CanvasFactory";
+}
+
+sal_Bool CanvasFactory::supportsService( OUString const & serviceName )
+{
+ return cppu::supportsService(this, serviceName);
+}
+
+Sequence<OUString> CanvasFactory::getSupportedServiceNames()
+{
+ return { "com.sun.star.rendering.CanvasFactory" };
+}
+
+// XMultiComponentFactory
+Sequence<OUString> CanvasFactory::getAvailableServiceNames()
+{
+ Sequence<OUString> aServiceNames(m_aAvailableImplementations.size());
+ std::transform(m_aAvailableImplementations.begin(),
+ m_aAvailableImplementations.end(),
+ aServiceNames.getArray(),
+ o3tl::select1st< AvailPair >());
+ return aServiceNames;
+}
+
+Reference<XInterface> CanvasFactory::createInstanceWithContext(
+ OUString const & name, Reference<XComponentContext> const & xContext )
+{
+ return createInstanceWithArgumentsAndContext(
+ name, Sequence<Any>(), xContext );
+}
+
+
+Reference<XInterface> CanvasFactory::use(
+ OUString const & serviceName,
+ Sequence<Any> const & args,
+ Reference<XComponentContext> const & xContext ) const
+{
+ try {
+ return m_xContext->getServiceManager()->createInstanceWithArgumentsAndContext(
+ serviceName, args, xContext);
+ }
+ catch (css::lang::IllegalArgumentException &)
+ {
+ return Reference<XInterface>();
+ }
+ catch (const RuntimeException &)
+ {
+ throw;
+ }
+ catch (const Exception &)
+ {
+ return Reference<XInterface>();
+ }
+}
+
+
+void CanvasFactory::checkConfigFlag( bool& r_bFlag,
+ bool& r_CacheFlag,
+ const OUString& nodeName ) const
+{
+ if( m_xCanvasConfigNameAccess.is() )
+ {
+ m_xCanvasConfigNameAccess->getByName( nodeName ) >>= r_bFlag;
+
+ if( r_CacheFlag != r_bFlag )
+ {
+ // cache is invalid, because of different order of
+ // elements
+ r_CacheFlag = r_bFlag;
+ m_aCachedImplementations.clear();
+ }
+ }
+}
+
+
+Reference<XInterface> CanvasFactory::lookupAndUse(
+ OUString const & serviceName, Sequence<Any> const & args,
+ Reference<XComponentContext> const & xContext ) const
+{
+ std::scoped_lock guard(m_mutex);
+
+ // forcing last entry from impl list, if config flag set
+ bool bForceLastEntry(false);
+ checkConfigFlag( bForceLastEntry,
+ m_bCacheHasForcedLastImpl,
+ "ForceSafeServiceImpl" );
+
+ // use anti-aliasing canvas, if config flag set (or not existing)
+ bool bUseAAEntry(true);
+ checkConfigFlag( bUseAAEntry,
+ m_bCacheHasUseAAEntry,
+ "UseAntialiasingCanvas" );
+
+ // use accelerated canvas, if config flag set (or not existing)
+ bool bUseAcceleratedEntry(true);
+ checkConfigFlag( bUseAcceleratedEntry,
+ m_bCacheHasUseAcceleratedEntry,
+ "UseAcceleratedCanvas" );
+
+ // try to reuse last working implementation for given service name
+ const CacheVector::iterator aEnd(m_aCachedImplementations.end());
+ auto aMatch = std::find_if(
+ m_aCachedImplementations.begin(),
+ aEnd,
+ [&serviceName](CachePair const& cp)
+ { return serviceName == cp.first; }
+ );
+ if( aMatch != aEnd ) {
+ Reference<XInterface> xCanvas( use( aMatch->second, args, xContext ) );
+ if(xCanvas.is())
+ return xCanvas;
+ }
+
+ // lookup in available service list
+ const AvailVector::const_iterator aAvailEnd(m_aAvailableImplementations.end());
+ auto aAvailImplsMatch = std::find_if(
+ m_aAvailableImplementations.begin(),
+ aAvailEnd,
+ [&serviceName](AvailPair const& ap)
+ { return serviceName == ap.first; }
+ );
+ if( aAvailImplsMatch == aAvailEnd ) {
+ return Reference<XInterface>();
+ }
+
+ const AvailVector::const_iterator aAAEnd(m_aAAImplementations.end());
+ auto aAAImplsMatch = std::find_if(
+ m_aAAImplementations.begin(),
+ aAAEnd,
+ [&serviceName](AvailPair const& ap)
+ { return serviceName == ap.first; }
+ );
+ if( aAAImplsMatch == aAAEnd ) {
+ return Reference<XInterface>();
+ }
+
+ const AvailVector::const_iterator aAccelEnd(m_aAcceleratedImplementations.end());
+ auto aAccelImplsMatch = std::find_if(
+ m_aAcceleratedImplementations.begin(),
+ aAccelEnd,
+ [&serviceName](AvailPair const& ap)
+ { return serviceName == ap.first; }
+ );
+ if( aAccelImplsMatch == aAccelEnd ) {
+ return Reference<XInterface>();
+ }
+
+ const Sequence<OUString> aPreferredImpls( aAvailImplsMatch->second );
+ const OUString* pCurrImpl = aPreferredImpls.begin();
+ const OUString* const pEndImpl = aPreferredImpls.end();
+
+ const Sequence<OUString> aAAImpls( aAAImplsMatch->second );
+
+ const Sequence<OUString> aAccelImpls( aAccelImplsMatch->second );
+
+ // force last entry from impl list, if config flag set
+ if (bForceLastEntry && pCurrImpl != pEndImpl)
+ pCurrImpl = pEndImpl-1;
+
+ for(; pCurrImpl != pEndImpl; ++pCurrImpl)
+ {
+ const OUString aCurrName(pCurrImpl->trim());
+
+ // Skia works only with vclcanvas.
+ if( SkiaHelper::isVCLSkiaEnabled() && !aCurrName.endsWith(".VCL"))
+ continue;
+
+ // check whether given canvas service is listed in the
+ // sequence of "accelerated canvas implementations"
+ const bool bIsAcceleratedImpl(
+ std::any_of(aAccelImpls.begin(), aAccelImpls.end(),
+ [&aCurrName](OUString const& src)
+ { return aCurrName == o3tl::trim(src); }
+ ));
+
+ // check whether given canvas service is listed in the
+ // sequence of "antialiasing canvas implementations"
+ const bool bIsAAImpl(
+ std::any_of(aAAImpls.begin(), aAAImpls.end(),
+ [&aCurrName](OUString const& src)
+ { return aCurrName == o3tl::trim(src); }
+ ));
+
+ // try to instantiate canvas *only* if either accel and AA
+ // property match preference, *or*, if there's a mismatch, only
+ // go for a less capable canvas (that effectively let those
+ // pour canvas impls still work as fallbacks, should an
+ // accelerated/AA one fail). Property implies configuration:
+ // http://en.wikipedia.org/wiki/Truth_table#Logical_implication
+ if( (!bIsAAImpl || bUseAAEntry) && (!bIsAcceleratedImpl || bUseAcceleratedEntry) )
+ {
+ Reference<XInterface> xCanvas(use(aCurrName, args, xContext));
+
+ if(xCanvas.is())
+ {
+ if( aMatch != aEnd )
+ {
+ // cache entry exists, replace dysfunctional
+ // implementation name
+ aMatch->second = aCurrName;
+ }
+ else
+ {
+ // new service name, add new cache entry
+ m_aCachedImplementations.emplace_back(serviceName, aCurrName);
+ }
+
+ return xCanvas;
+ }
+ }
+ }
+
+ return Reference<XInterface>();
+}
+
+
+Reference<XInterface> CanvasFactory::createInstanceWithArgumentsAndContext(
+ OUString const & preferredOne, Sequence<Any> const & args,
+ Reference<XComponentContext> const & xContext )
+{
+ Reference<XInterface> xCanvas(lookupAndUse(preferredOne, args, xContext));
+ if (!xCanvas.is())
+ // last resort: try service name directly
+ xCanvas = use(preferredOne, args, xContext);
+
+ if (xCanvas.is())
+ {
+ Reference<lang::XServiceName> xServiceName(xCanvas, uno::UNO_QUERY);
+ SAL_INFO("canvas", "using " << (xServiceName.is() ? xServiceName->getServiceName()
+ : OUString("(unknown)")));
+ }
+ return xCanvas;
+}
+
+// XMultiServiceFactory
+
+Reference<XInterface> CanvasFactory::createInstance( OUString const & name )
+{
+ return createInstanceWithArgumentsAndContext(
+ name, Sequence<Any>(), m_xContext );
+}
+
+
+Reference<XInterface> CanvasFactory::createInstanceWithArguments(
+ OUString const & name, Sequence<Any> const & args )
+{
+ return createInstanceWithArgumentsAndContext(
+ name, args, m_xContext );
+}
+
+} // anon namespace
+
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_rendering_CanvasFactory_get_implementation(css::uno::XComponentContext* context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new CanvasFactory(context));
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/opengl/ogl_bitmapcanvashelper.cxx b/canvas/source/opengl/ogl_bitmapcanvashelper.cxx
new file mode 100644
index 000000000..0b95dfb2a
--- /dev/null
+++ b/canvas/source/opengl/ogl_bitmapcanvashelper.cxx
@@ -0,0 +1,65 @@
+/* -*- 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/.
+ */
+
+#include <sal/config.h>
+
+#include <canvas/canvastools.hxx>
+
+#include "ogl_bitmapcanvashelper.hxx"
+
+
+using namespace ::com::sun::star;
+
+namespace oglcanvas
+{
+ BitmapCanvasHelper::BitmapCanvasHelper()
+ {}
+
+ void BitmapCanvasHelper::disposing()
+ {
+ CanvasHelper::disposing();
+ }
+
+ void BitmapCanvasHelper::init( rendering::XGraphicDevice& rDevice,
+ SpriteDeviceHelper& rDeviceHelper,
+ const geometry::IntegerSize2D& rSize )
+ {
+ maSize = rSize;
+ CanvasHelper::init(rDevice,rDeviceHelper);
+ }
+
+ 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*/ )
+ {
+ // TODO(F2): NYI - and improbable to ever be
+ return uno::Sequence< sal_Int8 >();
+ }
+
+ uno::Sequence< sal_Int8 > BitmapCanvasHelper::getPixel( rendering::IntegerBitmapLayout& /*bitmapLayout*/,
+ const geometry::IntegerPoint2D& /*pos*/ )
+ {
+ // TODO(F2): NYI - and improbable to ever be
+ return uno::Sequence< sal_Int8 >();
+ }
+
+ rendering::IntegerBitmapLayout BitmapCanvasHelper::getMemoryLayout() const
+ {
+ return ::canvas::tools::getStdMemoryLayout(getSize());
+ }
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/opengl/ogl_bitmapcanvashelper.hxx b/canvas/source/opengl/ogl_bitmapcanvashelper.hxx
new file mode 100644
index 000000000..5bdb9713d
--- /dev/null
+++ b/canvas/source/opengl/ogl_bitmapcanvashelper.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/.
+ */
+
+#pragma once
+
+#include <com/sun/star/geometry/IntegerPoint2D.hpp>
+#include <com/sun/star/geometry/IntegerRectangle2D.hpp>
+#include <com/sun/star/rendering/IntegerBitmapLayout.hpp>
+
+#include "ogl_canvashelper.hxx"
+
+namespace oglcanvas
+{
+ /** Helper class for basic canvas functionality. */
+ class BitmapCanvasHelper : public CanvasHelper
+ {
+ public:
+ BitmapCanvasHelper();
+
+ /// 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 init( css::rendering::XGraphicDevice& rDevice,
+ SpriteDeviceHelper& rDeviceHelper,
+ const css::geometry::IntegerSize2D& rSize );
+
+ // BitmapCanvasHelper functionality
+ // ================================
+
+ const css::geometry::IntegerSize2D& getSize() const { return maSize; }
+
+ 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 );
+
+ css::uno::Sequence< sal_Int8 >
+ getPixel( css::rendering::IntegerBitmapLayout& bitmapLayout,
+ const css::geometry::IntegerPoint2D& pos );
+
+ css::rendering::IntegerBitmapLayout getMemoryLayout() const;
+
+ private:
+ css::geometry::IntegerSize2D maSize;
+ };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/opengl/ogl_buffercontext.hxx b/canvas/source/opengl/ogl_buffercontext.hxx
new file mode 100644
index 000000000..d4262d356
--- /dev/null
+++ b/canvas/source/opengl/ogl_buffercontext.hxx
@@ -0,0 +1,35 @@
+/* -*- 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/.
+ */
+
+#pragma once
+
+#include <epoxy/gl.h>
+
+#include <sal/config.h>
+#include <memory>
+
+namespace oglcanvas
+{
+ struct IBufferContext
+ {
+ virtual ~IBufferContext() {}
+
+ /// start render to buffer. changes current framebuffer
+ virtual void startBufferRendering() = 0;
+
+ /// end render to buffer. switches to default framebuffer
+ virtual void endBufferRendering() = 0;
+
+ virtual GLuint getTextureId() = 0;
+ };
+
+ typedef std::shared_ptr<IBufferContext> IBufferContextSharedPtr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/opengl/ogl_canvasbitmap.cxx b/canvas/source/opengl/ogl_canvasbitmap.cxx
new file mode 100644
index 000000000..fa5e53823
--- /dev/null
+++ b/canvas/source/opengl/ogl_canvasbitmap.cxx
@@ -0,0 +1,54 @@
+/* -*- 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/.
+ */
+
+#include <sal/config.h>
+
+#include <tools/diagnose_ex.h>
+
+#include <utility>
+
+#include "ogl_canvasbitmap.hxx"
+
+
+using namespace ::com::sun::star;
+
+namespace oglcanvas
+{
+ CanvasBitmap::CanvasBitmap( const geometry::IntegerSize2D& rSize,
+ SpriteCanvasRef rDevice,
+ SpriteDeviceHelper& rDeviceHelper ) :
+ mpDevice(std::move( rDevice ))
+ {
+ ENSURE_OR_THROW( mpDevice.is(),
+ "CanvasBitmap::CanvasBitmap(): Invalid surface or device" );
+
+ maCanvasHelper.init( *mpDevice, rDeviceHelper, rSize );
+ }
+
+ CanvasBitmap::CanvasBitmap( const CanvasBitmap& rSrc ) :
+ mpDevice( rSrc.mpDevice )
+ {
+ maCanvasHelper = rSrc.maCanvasHelper;
+ }
+
+ void CanvasBitmap::disposeThis()
+ {
+ mpDevice.clear();
+
+ // forward to parent
+ CanvasBitmapBaseT::disposeThis();
+ }
+
+ bool CanvasBitmap::renderRecordedActions() const
+ {
+ return maCanvasHelper.renderRecordedActions();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/opengl/ogl_canvasbitmap.hxx b/canvas/source/opengl/ogl_canvasbitmap.hxx
new file mode 100644
index 000000000..5cc41bb19
--- /dev/null
+++ b/canvas/source/opengl/ogl_canvasbitmap.hxx
@@ -0,0 +1,72 @@
+/* -*- 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/.
+ */
+
+#pragma once
+
+#include <cppuhelper/compbase.hxx>
+
+#include <com/sun/star/rendering/XBitmapCanvas.hpp>
+#include <com/sun/star/rendering/XIntegerBitmap.hpp>
+
+#include <base/integerbitmapbase.hxx>
+#include <base/basemutexhelper.hxx>
+#include <base/bitmapcanvasbase.hxx>
+
+#include "ogl_bitmapcanvashelper.hxx"
+#include "ogl_spritecanvas.hxx"
+
+
+/* Definition of CanvasBitmap class */
+
+namespace oglcanvas
+{
+ typedef ::cppu::WeakComponentImplHelper< css::rendering::XBitmapCanvas,
+ css::rendering::XIntegerBitmap > CanvasBitmapBase_Base;
+ typedef ::canvas::IntegerBitmapBase<
+ canvas::BitmapCanvasBase2<
+ ::canvas::BaseMutexHelper< CanvasBitmapBase_Base >,
+ BitmapCanvasHelper,
+ ::osl::MutexGuard,
+ ::cppu::OWeakObject> > CanvasBitmapBaseT;
+
+ class CanvasBitmap : public CanvasBitmapBaseT
+ {
+ public:
+ /** Create a canvas bitmap for the given surface
+
+ @param rSize
+ Size of the bitmap
+
+ @param rDevice
+ Reference device, with which bitmap should be compatible
+ */
+ CanvasBitmap( const css::geometry::IntegerSize2D& rSize,
+ SpriteCanvasRef rDevice,
+ SpriteDeviceHelper& rDeviceHelper );
+
+ /** Create verbatim copy (including all recorded actions)
+ */
+ CanvasBitmap( const CanvasBitmap& rSrc );
+
+ /// Dispose all internal references
+ virtual void disposeThis() override;
+
+ /** Write out recorded actions
+ */
+ bool renderRecordedActions() const;
+
+ private:
+ /** MUST hold here, too, since CanvasHelper only contains a
+ raw pointer (without refcounting)
+ */
+ SpriteCanvasRef mpDevice;
+ };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/opengl/ogl_canvascustomsprite.cxx b/canvas/source/opengl/ogl_canvascustomsprite.cxx
new file mode 100644
index 000000000..4be2b5db2
--- /dev/null
+++ b/canvas/source/opengl/ogl_canvascustomsprite.cxx
@@ -0,0 +1,257 @@
+/* -*- 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/.
+ */
+
+#include <sal/config.h>
+
+#include <epoxy/gl.h>
+
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/point/b2dpoint.hxx>
+#include <basegfx/polygon/b2dpolypolygon.hxx>
+#include <basegfx/polygon/b2dpolygontriangulator.hxx>
+#include <basegfx/utils/canvastools.hxx>
+#include <canvas/canvastools.hxx>
+#include <o3tl/safeint.hxx>
+#include <verifyinput.hxx>
+#include <tools/diagnose_ex.h>
+
+#include "ogl_canvascustomsprite.hxx"
+#include "ogl_canvastools.hxx"
+#include "ogl_tools.hxx"
+
+using namespace ::com::sun::star;
+
+namespace oglcanvas
+{
+ CanvasCustomSprite::CanvasCustomSprite( const css::geometry::RealSize2D& rSpriteSize,
+ const SpriteCanvasRef& rRefDevice,
+ SpriteDeviceHelper& rDeviceHelper ) :
+ mpSpriteCanvas( rRefDevice ),
+ maSize(rSpriteSize),
+ mfAlpha(0.0),
+ mfPriority(0.0)
+ {
+ ENSURE_OR_THROW( rRefDevice,
+ "CanvasCustomSprite::CanvasCustomSprite(): Invalid sprite canvas" );
+
+ ::canvas::tools::setIdentityAffineMatrix2D(maTransformation);
+ maCanvasHelper.init( *rRefDevice,
+ rDeviceHelper );
+ }
+
+ void CanvasCustomSprite::disposeThis()
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ mpSpriteCanvas.clear();
+
+ // forward to parent
+ CanvasCustomSpriteBaseT::disposeThis();
+ }
+
+ void SAL_CALL CanvasCustomSprite::setAlpha( double alpha )
+ {
+ canvas::tools::verifyRange( alpha, 0.0, 1.0 );
+
+ ::osl::MutexGuard aGuard( m_aMutex );
+ mfAlpha = alpha;
+ }
+
+ void SAL_CALL CanvasCustomSprite::move( const geometry::RealPoint2D& aNewPos,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState )
+ {
+ canvas::tools::verifyArgs(aNewPos, viewState, renderState,
+ __func__,
+ static_cast< ::cppu::OWeakObject* >(this));
+
+ ::osl::MutexGuard aGuard( m_aMutex );
+ ::basegfx::B2DHomMatrix aTransform;
+ ::canvas::tools::mergeViewAndRenderTransform(aTransform,
+ viewState,
+ renderState);
+
+ // convert position to device pixel
+ maPosition = ::basegfx::unotools::b2DPointFromRealPoint2D(aNewPos);
+ maPosition *= aTransform;
+ }
+
+ void SAL_CALL CanvasCustomSprite::transform( const geometry::AffineMatrix2D& aTransformation )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ maTransformation = aTransformation;
+ }
+
+ void SAL_CALL CanvasCustomSprite::clip( const uno::Reference< rendering::XPolyPolygon2D >& xClip )
+ {
+ mxClip = xClip;
+ }
+
+ void SAL_CALL CanvasCustomSprite::setPriority( double nPriority )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ mfPriority = nPriority;
+ }
+
+ void SAL_CALL CanvasCustomSprite::show()
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if( mpSpriteCanvas.is() )
+ mpSpriteCanvas->show(this);
+ }
+
+ void SAL_CALL CanvasCustomSprite::hide()
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if( mpSpriteCanvas.is() )
+ mpSpriteCanvas->hide(this);
+ }
+
+ uno::Reference< rendering::XCanvas > SAL_CALL CanvasCustomSprite::getContentCanvas()
+ {
+ return this;
+ }
+
+ bool CanvasCustomSprite::renderSprite() const
+ {
+ if( ::basegfx::fTools::equalZero( mfAlpha ) )
+ return true;
+
+ TransformationPreserver aPreserver1;
+ const ::basegfx::B2IVector aSpriteSizePixel(
+ ::canvas::tools::roundUp( maSize.Width ),
+ ::canvas::tools::roundUp( maSize.Height ));
+
+ // translate sprite to output position
+ glTranslated(maPosition.getX(), maPosition.getY(), 0);
+
+ {
+ TransformationPreserver aPreserver2;
+
+ // apply sprite content transformation matrix
+ double aGLTransform[] =
+ {
+ maTransformation.m00, maTransformation.m10, 0, 0,
+ maTransformation.m01, maTransformation.m11, 0, 0,
+ 0, 0, 1, 0,
+ maTransformation.m02, maTransformation.m12, 0, 1
+ };
+ glMultMatrixd(aGLTransform);
+
+ IBufferContextSharedPtr pBufferContext;
+ if( mfAlpha != 1.0 || mxClip.is() )
+ {
+ // drafts. Need to render to temp surface before, and then
+ // composite that to screen
+
+ // TODO(P3): buffer texture
+ pBufferContext = maCanvasHelper.getDeviceHelper()->createBufferContext(aSpriteSizePixel);
+ pBufferContext->startBufferRendering();
+ }
+
+ // this ends up in pBufferContext, if that one's "current"
+ if( !maCanvasHelper.renderRecordedActions() )
+ return false;
+
+ if( pBufferContext )
+ {
+ // content ended up in background buffer - compose to
+ // screen now. Calls below switches us back to window
+ // context, and binds to generated, dynamic texture
+ pBufferContext->endBufferRendering();
+ GLuint nTexture = pBufferContext->getTextureId();
+ glBindTexture(GL_TEXTURE_2D, nTexture);
+
+ glEnable(GL_TEXTURE_2D);
+ glTexParameteri(GL_TEXTURE_2D,
+ GL_TEXTURE_MIN_FILTER,
+ GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D,
+ GL_TEXTURE_MAG_FILTER,
+ GL_NEAREST);
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA,
+ GL_ONE_MINUS_SRC_ALPHA);
+
+ // blend against fixed vertex color; texture alpha is multiplied in
+ glColor4f(1,1,1,mfAlpha);
+
+ if( mxClip.is() )
+ {
+ const double fWidth=maSize.Width;
+ const double fHeight=maSize.Height;
+
+ // TODO(P3): buffer triangulation
+ const ::basegfx::triangulator::B2DTriangleVector rTriangulatedPolygon(
+ ::basegfx::triangulator::triangulate(
+ ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(mxClip)));
+
+ glBegin(GL_TRIANGLES);
+ for( size_t i=0; i<rTriangulatedPolygon.size(); i++ )
+ {
+ const::basegfx::triangulator::B2DTriangle& rCandidate(rTriangulatedPolygon[i]);
+ glTexCoord2f(
+ rCandidate.getA().getX()/fWidth,
+ rCandidate.getA().getY()/fHeight);
+ glVertex2d(
+ rCandidate.getA().getX(),
+ rCandidate.getA().getY());
+
+ glTexCoord2f(
+ rCandidate.getB().getX()/fWidth,
+ rCandidate.getB().getY()/fHeight);
+ glVertex2d(
+ rCandidate.getB().getX(),
+ rCandidate.getB().getY());
+
+ glTexCoord2f(
+ rCandidate.getC().getX()/fWidth,
+ rCandidate.getC().getY()/fHeight);
+ glVertex2d(
+ rCandidate.getC().getX(),
+ rCandidate.getC().getY());
+ }
+ glEnd();
+ }
+ else
+ {
+ const double fWidth=maSize.Width/aSpriteSizePixel.getX();
+ const double fHeight=maSize.Height/aSpriteSizePixel.getY();
+
+ glBegin(GL_TRIANGLE_STRIP);
+ glTexCoord2f(0,0); glVertex2d(0,0);
+ glTexCoord2f(0,fHeight); glVertex2d(0, aSpriteSizePixel.getY());
+ glTexCoord2f(fWidth,0); glVertex2d(aSpriteSizePixel.getX(),0);
+ glTexCoord2f(fWidth,fHeight); glVertex2d(aSpriteSizePixel.getX(),aSpriteSizePixel.getY());
+ glEnd();
+ }
+
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glDisable(GL_TEXTURE_2D);
+ }
+ }
+
+ glColor4f(1,0,0,1);
+ glBegin(GL_LINE_STRIP);
+ glVertex2d(-2,-2);
+ glVertex2d(-2,maSize.Height+4);
+ glVertex2d(maSize.Width+4,maSize.Height+4);
+ glVertex2d(maSize.Width+4,-2);
+ glVertex2d(-2,-2);
+ glVertex2d(maSize.Width+4,maSize.Height+4);
+ glEnd();
+
+ std::vector<double> aVec { mfAlpha, mfPriority, o3tl::narrowing<double>(maCanvasHelper.getRecordedActionCount()) };
+ renderOSD( aVec, 10 );
+
+ return true;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/opengl/ogl_canvascustomsprite.hxx b/canvas/source/opengl/ogl_canvascustomsprite.hxx
new file mode 100644
index 000000000..3cd43cf4f
--- /dev/null
+++ b/canvas/source/opengl/ogl_canvascustomsprite.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/.
+ */
+
+#pragma once
+
+#include <cppuhelper/compbase.hxx>
+
+#include <com/sun/star/rendering/XCustomSprite.hpp>
+#include <com/sun/star/rendering/XPolyPolygon2D.hpp>
+
+#include <basegfx/point/b2dpoint.hxx>
+
+#include <base/basemutexhelper.hxx>
+
+#include "ogl_spritecanvas.hxx"
+#include "ogl_canvashelper.hxx"
+
+
+namespace oglcanvas
+{
+ typedef ::cppu::WeakComponentImplHelper< css::rendering::XCustomSprite,
+ css::rendering::XCanvas > CanvasCustomSpriteBase_Base;
+ typedef ::canvas::CanvasBase<
+ ::canvas::BaseMutexHelper< CanvasCustomSpriteBase_Base >,
+ CanvasHelper,
+ ::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,
+ SpriteDeviceHelper& rDeviceHelper );
+
+ virtual void disposeThis() override;
+
+ // XSprite
+ virtual void SAL_CALL setAlpha( double alpha ) override;
+ virtual void SAL_CALL move( const css::geometry::RealPoint2D& aNewPos, const css::rendering::ViewState& viewState, const css::rendering::RenderState& renderState ) override;
+ virtual void SAL_CALL transform( const css::geometry::AffineMatrix2D& aTransformation ) override;
+ virtual void SAL_CALL clip( const css::uno::Reference< css::rendering::XPolyPolygon2D >& aClip ) override;
+ virtual void SAL_CALL setPriority( double nPriority ) override;
+ virtual void SAL_CALL show() override;
+ virtual void SAL_CALL hide() override;
+
+ // XCustomSprite
+ virtual css::uno::Reference< css::rendering::XCanvas > SAL_CALL getContentCanvas() override;
+
+ double getPriority() const { return mfPriority; }
+
+ /// Render sprite content at sprite position
+ bool renderSprite() const;
+
+ private:
+ /** MUST hold here, too, since CanvasHelper only contains a
+ raw pointer (without refcounting)
+ */
+ SpriteCanvasRef mpSpriteCanvas;
+ const css::geometry::RealSize2D maSize;
+
+ css::uno::Reference< css::rendering::XPolyPolygon2D > mxClip;
+ css::geometry::AffineMatrix2D maTransformation;
+ ::basegfx::B2DPoint maPosition;
+ double mfAlpha;
+ double mfPriority;
+ };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/opengl/ogl_canvasfont.cxx b/canvas/source/opengl/ogl_canvasfont.cxx
new file mode 100644
index 000000000..7309b9acd
--- /dev/null
+++ b/canvas/source/opengl/ogl_canvasfont.cxx
@@ -0,0 +1,70 @@
+/* -*- 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/.
+ */
+
+#include <sal/config.h>
+
+#include <com/sun/star/rendering/FontMetrics.hpp>
+#include <canvas/canvastools.hxx>
+#include <utility>
+
+#include "ogl_canvasfont.hxx"
+#include "ogl_textlayout.hxx"
+
+using namespace ::com::sun::star;
+
+namespace oglcanvas
+{
+ CanvasFont::CanvasFont( rendering::FontRequest aFontRequest,
+ const uno::Sequence< beans::PropertyValue >& extraFontProperties,
+ const geometry::Matrix2D& fontMatrix ) :
+ CanvasFontBaseT( m_aMutex ),
+ maFontRequest(std::move( aFontRequest )),
+ mnEmphasisMark(0),
+ maFontMatrix( fontMatrix )
+ {
+ ::canvas::tools::extractExtraFontProperties(extraFontProperties, mnEmphasisMark);
+ }
+
+ 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();
+ }
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/opengl/ogl_canvasfont.hxx b/canvas/source/opengl/ogl_canvasfont.hxx
new file mode 100644
index 000000000..13b0f78a9
--- /dev/null
+++ b/canvas/source/opengl/ogl_canvasfont.hxx
@@ -0,0 +1,61 @@
+/* -*- 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/.
+ */
+
+#pragma once
+
+#include <cppuhelper/compbase.hxx>
+#include <cppuhelper/basemutex.hxx>
+
+#include <com/sun/star/rendering/XCanvasFont.hpp>
+#include <com/sun/star/geometry/Matrix2D.hpp>
+
+#include <rtl/ref.hxx>
+
+
+/* Definition of CanvasFont class */
+
+namespace oglcanvas
+{
+ class SpriteCanvas;
+
+ typedef ::cppu::WeakComponentImplHelper< css::rendering::XCanvasFont > CanvasFontBaseT;
+
+ class CanvasFont : public ::cppu::BaseMutex,
+ public CanvasFontBaseT
+ {
+ public:
+ typedef rtl::Reference<CanvasFont> ImplRef;
+
+ /// make noncopyable
+ CanvasFont(const CanvasFont&) = delete;
+ const CanvasFont& operator=(const CanvasFont&) = delete;
+
+ CanvasFont( css::rendering::FontRequest fontRequest,
+ const css::uno::Sequence< css::beans::PropertyValue >& extraFontProperties,
+ const css::geometry::Matrix2D& fontMatrix );
+
+ // 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;
+
+ const css::geometry::Matrix2D& getFontMatrix() const { return maFontMatrix; }
+
+ sal_uInt32 getEmphasisMark() const { return mnEmphasisMark; }
+
+ private:
+ css::rendering::FontRequest maFontRequest;
+ sal_uInt32 mnEmphasisMark;
+ css::geometry::Matrix2D maFontMatrix;
+ };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/opengl/ogl_canvashelper.cxx b/canvas/source/opengl/ogl_canvashelper.cxx
new file mode 100644
index 000000000..9835cea5b
--- /dev/null
+++ b/canvas/source/opengl/ogl_canvashelper.cxx
@@ -0,0 +1,947 @@
+/* -*- 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/.
+ */
+
+#include <sal/config.h>
+
+#include <memory>
+#include <functional>
+#include <epoxy/gl.h>
+
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/polygon/b2dpolypolygon.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <basegfx/utils/canvastools.hxx>
+#include <com/sun/star/rendering/CompositeOperation.hpp>
+#include <rtl/crc.h>
+#include <rtl/math.hxx>
+#include <tools/diagnose_ex.h>
+#include <vcl/font.hxx>
+#include <vcl/metric.hxx>
+#include <vcl/virdev.hxx>
+
+#include "ogl_canvasbitmap.hxx"
+#include "ogl_canvasfont.hxx"
+#include "ogl_canvastools.hxx"
+#include "ogl_texturecache.hxx"
+#include "ogl_tools.hxx"
+
+#include "ogl_canvashelper.hxx"
+
+using namespace ::com::sun::star;
+using namespace std::placeholders;
+
+namespace oglcanvas
+{
+ /* Concepts:
+ =========
+
+ This OpenGL canvas implementation tries to keep all render
+ output as high-level as possible, i.e. geometry data and
+ externally-provided bitmaps. Therefore, calls at the
+ XCanvas-interfaces are not immediately transformed into colored
+ pixel inside some GL buffer, but are retained simply with their
+ call parameters. Only after XSpriteCanvas::updateScreen() has
+ been called, this all gets transferred to the OpenGL subsystem
+ and converted to a visible scene. The big advantage is, this
+ makes sprite modifications practically zero-overhead, and saves
+ a lot on texture memory (compared to the directx canvas, which
+ immediately dumps every render call into a texture).
+
+ The drawback, of course, is that complex images churn a lot of
+ GPU cycles on every re-rendering.
+
+ For the while, I'll be using immediate mode, i.e. transfer data
+ over and over again to the OpenGL subsystem. Alternatively,
+ there are display lists, which at least keep the data on the
+ server, or even better, vertex buffers, which copy geometry
+ data over en bloc.
+
+ Next todo: put polygon geometry into vertex buffer (LRU cache
+ necessary?) - or, rather, buffer objects! prune entries older
+ than one updateScreen() call)
+
+ Text: http://www.opengl.org/resources/features/fontsurvey/
+ */
+
+ struct CanvasHelper::Action
+ {
+ ::basegfx::B2DHomMatrix maTransform;
+ GLenum meSrcBlendMode;
+ GLenum meDstBlendMode;
+ rendering::ARGBColor maARGBColor;
+ ::basegfx::B2DPolyPolygonVector maPolyPolys;
+
+ std::function< bool (
+ const CanvasHelper&,
+ const ::basegfx::B2DHomMatrix&,
+ GLenum,
+ GLenum,
+ const rendering::ARGBColor&,
+ const ::basegfx::B2DPolyPolygonVector&)> maFunction;
+ };
+
+ namespace
+ {
+ bool lcl_drawLine( const CanvasHelper& /*rHelper*/,
+ const ::basegfx::B2DHomMatrix& rTransform,
+ GLenum eSrcBlend,
+ GLenum eDstBlend,
+ const rendering::ARGBColor& rColor,
+ const geometry::RealPoint2D& rStartPoint,
+ const geometry::RealPoint2D& rEndPoint )
+ {
+ TransformationPreserver aPreserver;
+ setupState(rTransform, eSrcBlend, eDstBlend, rColor);
+
+ glBegin(GL_LINES);
+ glVertex2d(rStartPoint.X, rStartPoint.Y);
+ glVertex2d(rEndPoint.X, rEndPoint.Y);
+ glEnd();
+
+ return true;
+ }
+
+ bool lcl_drawPolyPolygon( const CanvasHelper& /*rHelper*/,
+ const ::basegfx::B2DHomMatrix& rTransform,
+ GLenum eSrcBlend,
+ GLenum eDstBlend,
+ const rendering::ARGBColor& rColor,
+ const ::basegfx::B2DPolyPolygonVector& rPolyPolygons )
+ {
+ TransformationPreserver aPreserver;
+ setupState(rTransform, eSrcBlend, eDstBlend, rColor);
+
+ for( const auto& rPoly : rPolyPolygons )
+ renderPolyPolygon( rPoly );
+
+ return true;
+ }
+
+ bool lcl_fillPolyPolygon( const CanvasHelper& /*rHelper*/,
+ const ::basegfx::B2DHomMatrix& rTransform,
+ GLenum eSrcBlend,
+ GLenum eDstBlend,
+ const rendering::ARGBColor& rColor,
+ const ::basegfx::B2DPolyPolygonVector& rPolyPolygons )
+ {
+ TransformationPreserver aPreserver;
+ setupState(rTransform, eSrcBlend, eDstBlend, rColor);
+
+ for( const auto& rPoly : rPolyPolygons )
+ {
+ glBegin( GL_TRIANGLES );
+ renderComplexPolyPolygon( rPoly );
+ glEnd();
+ }
+
+ return true;
+ }
+
+ bool lcl_fillGradientPolyPolygon( const CanvasHelper& rHelper,
+ const ::basegfx::B2DHomMatrix& rTransform,
+ GLenum eSrcBlend,
+ GLenum eDstBlend,
+ const ::canvas::ParametricPolyPolygon::Values& rValues,
+ const rendering::Texture& rTexture,
+ const ::basegfx::B2DPolyPolygonVector& rPolyPolygons )
+ {
+ TransformationPreserver aPreserver;
+ setupState(rTransform, eSrcBlend, eDstBlend, rendering::ARGBColor());
+
+ // convert to weird canvas textur coordinate system (not
+ // [0,1]^2, but path coordinate system)
+ ::basegfx::B2DHomMatrix aTextureTransform;
+ ::basegfx::unotools::homMatrixFromAffineMatrix( aTextureTransform,
+ rTexture.AffineTransform );
+ ::basegfx::B2DRange aBounds;
+ for( const auto& rPoly : rPolyPolygons )
+ aBounds.expand( ::basegfx::utils::getRange( rPoly ) );
+ aTextureTransform.translate(-aBounds.getMinX(), -aBounds.getMinY());
+ aTextureTransform.scale(1/aBounds.getWidth(), 1/aBounds.getHeight());
+
+ const sal_Int32 nNumCols=rValues.maColors.getLength();
+ uno::Sequence< rendering::ARGBColor > aColors(nNumCols);
+ rendering::ARGBColor* const pColors=aColors.getArray();
+ rendering::ARGBColor* pCurrCol=pColors;
+ for( sal_Int32 i=0; i<nNumCols; ++i )
+ *pCurrCol++ = rHelper.getDevice()->getDeviceColorSpace()->convertToARGB(rValues.maColors[i])[0];
+
+ OSL_ASSERT(nNumCols == rValues.maStops.getLength());
+
+ switch( rValues.meType )
+ {
+ case ::canvas::ParametricPolyPolygon::GradientType::Linear:
+ rHelper.getDeviceHelper()->useLinearGradientShader(pColors,
+ rValues.maStops,
+ aTextureTransform);
+ break;
+
+ case ::canvas::ParametricPolyPolygon::GradientType::Elliptical:
+ rHelper.getDeviceHelper()->useRadialGradientShader(pColors,
+ rValues.maStops,
+ aTextureTransform);
+ break;
+
+ case ::canvas::ParametricPolyPolygon::GradientType::Rectangular:
+ rHelper.getDeviceHelper()->useRectangularGradientShader(pColors,
+ rValues.maStops,
+ aTextureTransform);
+ break;
+
+ default:
+ ENSURE_OR_THROW( false,
+ "CanvasHelper lcl_fillGradientPolyPolygon(): Unexpected case" );
+ }
+
+
+ for( const auto& rPoly : rPolyPolygons )
+ {
+ glBegin(GL_TRIANGLES);
+ renderComplexPolyPolygon( rPoly );
+ glEnd();
+ }
+
+ glUseProgram(0);
+ glLoadIdentity();
+ glMatrixMode(GL_MODELVIEW);
+
+ return true;
+ }
+
+ bool lcl_drawOwnBitmap( const CanvasHelper& /*rHelper*/,
+ const ::basegfx::B2DHomMatrix& rTransform,
+ GLenum eSrcBlend,
+ GLenum eDstBlend,
+ const rendering::ARGBColor& rColor,
+ const CanvasBitmap& rBitmap )
+ {
+ TransformationPreserver aPreserver;
+ setupState(rTransform, eSrcBlend, eDstBlend, rColor);
+
+ return rBitmap.renderRecordedActions();
+ }
+
+ bool lcl_drawGenericBitmap( const CanvasHelper& rHelper,
+ const ::basegfx::B2DHomMatrix& rTransform,
+ GLenum eSrcBlend,
+ GLenum eDstBlend,
+ const rendering::ARGBColor& rColor,
+ const geometry::IntegerSize2D& rPixelSize,
+ const uno::Sequence<sal_Int8>& rPixelData,
+ sal_uInt32 nPixelCrc32 )
+ {
+ TransformationPreserver aPreserver;
+ setupState(rTransform, eSrcBlend, eDstBlend, rColor);
+
+ const unsigned int nTexId=rHelper.getDeviceHelper()->getTextureCache().getTexture(
+ rPixelSize, rPixelData.getConstArray(), nPixelCrc32);
+
+ glBindTexture(GL_TEXTURE_2D, nTexId);
+ glEnable(GL_TEXTURE_2D);
+ glTexParameteri(GL_TEXTURE_2D,
+ GL_TEXTURE_MIN_FILTER,
+ GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D,
+ GL_TEXTURE_MAG_FILTER,
+ GL_NEAREST);
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA,
+ GL_ONE_MINUS_SRC_ALPHA);
+
+ // blend against fixed vertex color; texture alpha is multiplied in
+ glColor4f(1,1,1,1);
+
+ glBegin(GL_TRIANGLE_STRIP);
+ glTexCoord2f(0,0); glVertex2d(0,0);
+ glTexCoord2f(0,1); glVertex2d(0, rPixelSize.Height);
+ glTexCoord2f(1,0); glVertex2d(rPixelSize.Width,0);
+ glTexCoord2f(1,1); glVertex2d(rPixelSize.Width,rPixelSize.Height);
+ glEnd();
+
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glDisable(GL_TEXTURE_2D);
+
+ return true;
+ }
+
+ bool lcl_fillTexturedPolyPolygon( const CanvasHelper& rHelper,
+ const ::basegfx::B2DHomMatrix& rTransform,
+ GLenum eSrcBlend,
+ GLenum eDstBlend,
+ const rendering::Texture& rTexture,
+ const geometry::IntegerSize2D& rPixelSize,
+ const uno::Sequence<sal_Int8>& rPixelData,
+ sal_uInt32 nPixelCrc32,
+ const ::basegfx::B2DPolyPolygonVector& rPolyPolygons )
+ {
+ TransformationPreserver aPreserver;
+ setupState(rTransform, eSrcBlend, eDstBlend, rendering::ARGBColor());
+
+ const unsigned int nTexId=rHelper.getDeviceHelper()->getTextureCache().getTexture(
+ rPixelSize, rPixelData.getConstArray(), nPixelCrc32);
+
+ glBindTexture(GL_TEXTURE_2D, nTexId);
+ glEnable(GL_TEXTURE_2D);
+ glTexParameteri(GL_TEXTURE_2D,
+ GL_TEXTURE_MIN_FILTER,
+ GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D,
+ GL_TEXTURE_MAG_FILTER,
+ GL_NEAREST);
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA,
+ GL_ONE_MINUS_SRC_ALPHA);
+
+ // convert to weird canvas textur coordinate system (not
+ // [0,1]^2, but path coordinate system)
+ ::basegfx::B2DHomMatrix aTextureTransform;
+ ::basegfx::unotools::homMatrixFromAffineMatrix( aTextureTransform,
+ rTexture.AffineTransform );
+ ::basegfx::B2DRange aBounds;
+ for( const auto& rPolyPolygon : rPolyPolygons )
+ aBounds.expand( ::basegfx::utils::getRange( rPolyPolygon ) );
+ aTextureTransform.translate(-aBounds.getMinX(), -aBounds.getMinY());
+ aTextureTransform.scale(1/aBounds.getWidth(), 1/aBounds.getHeight());
+ aTextureTransform.invert();
+
+ glMatrixMode(GL_TEXTURE);
+ double aTexTransform[] =
+ {
+ aTextureTransform.get(0,0), aTextureTransform.get(1,0), 0, 0,
+ aTextureTransform.get(0,1), aTextureTransform.get(1,1), 0, 0,
+ 0, 0, 1, 0,
+ aTextureTransform.get(0,2), aTextureTransform.get(1,2), 0, 1
+ };
+ glLoadMatrixd(aTexTransform);
+
+ // blend against fixed vertex color; texture alpha is multiplied in
+ glColor4f(1,1,1,rTexture.Alpha);
+
+ for( const auto& rPolyPolygon : rPolyPolygons )
+ {
+ glBegin(GL_TRIANGLES);
+ renderComplexPolyPolygon( rPolyPolygon );
+ glEnd();
+ }
+
+ glLoadIdentity();
+ glMatrixMode(GL_MODELVIEW);
+
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glDisable(GL_TEXTURE_2D);
+
+ return true;
+ }
+ }
+
+ CanvasHelper::CanvasHelper() :
+ mpDevice( nullptr ),
+ mpDeviceHelper( nullptr )
+ {}
+
+ CanvasHelper::~CanvasHelper()
+ {}
+
+ CanvasHelper& CanvasHelper::operator=( const CanvasHelper& rSrc )
+ {
+ mpDevice = rSrc.mpDevice;
+ mpDeviceHelper = rSrc.mpDeviceHelper;
+ mpRecordedActions = rSrc.mpRecordedActions;
+ return *this;
+ }
+
+ void CanvasHelper::disposing()
+ {
+ RecordVectorT aThrowaway;
+ mpRecordedActions.swap( aThrowaway );
+ mpDevice = nullptr;
+ mpDeviceHelper = nullptr;
+ }
+
+ void CanvasHelper::init( rendering::XGraphicDevice& rDevice,
+ SpriteDeviceHelper& rDeviceHelper )
+ {
+ mpDevice = &rDevice;
+ mpDeviceHelper = &rDeviceHelper;
+ }
+
+ void CanvasHelper::clear()
+ {
+ mpRecordedActions->clear();
+ }
+
+ void CanvasHelper::drawLine( const rendering::XCanvas* /*pCanvas*/,
+ const geometry::RealPoint2D& aStartPoint,
+ const geometry::RealPoint2D& aEndPoint,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState )
+ {
+ if( mpDevice )
+ {
+ mpRecordedActions->push_back( Action() );
+ Action& rAct=mpRecordedActions->back();
+
+ setupGraphicsState( rAct, viewState, renderState );
+ rAct.maFunction = std::bind(&lcl_drawLine,
+ std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5,
+ aStartPoint, aEndPoint);
+ }
+ }
+
+ void CanvasHelper::drawBezier( const rendering::XCanvas* /*pCanvas*/,
+ const geometry::RealBezierSegment2D& aBezierSegment,
+ const geometry::RealPoint2D& aEndPoint,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState )
+ {
+ if( !mpDevice )
+ return;
+
+ mpRecordedActions->push_back( Action() );
+ Action& rAct=mpRecordedActions->back();
+
+ setupGraphicsState( rAct, viewState, renderState );
+
+ // TODO(F2): subdivide&render whole curve
+ rAct.maFunction = std::bind(&lcl_drawLine,
+ std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5,
+ geometry::RealPoint2D(
+ aBezierSegment.Px,
+ aBezierSegment.Py),
+ aEndPoint);
+ }
+
+ 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( mpDevice )
+ {
+ mpRecordedActions->push_back( Action() );
+ Action& rAct=mpRecordedActions->back();
+
+ setupGraphicsState( rAct, viewState, renderState );
+ rAct.maPolyPolys.push_back(
+ ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(xPolyPolygon));
+ rAct.maPolyPolys.back().makeUnique(); // own copy, for thread safety
+
+ rAct.maFunction = &lcl_drawPolyPolygon;
+ }
+
+ // 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::strokePolyPolygon: polygon is NULL");
+
+ if( mpDevice )
+ {
+ mpRecordedActions->push_back( Action() );
+ Action& rAct=mpRecordedActions->back();
+
+ setupGraphicsState( rAct, viewState, renderState );
+ rAct.maPolyPolys.push_back(
+ ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(xPolyPolygon));
+ rAct.maPolyPolys.back().makeUnique(); // own copy, for thread safety
+
+ // TODO(F3): fallback to drawPolyPolygon currently
+ rAct.maFunction = &lcl_drawPolyPolygon;
+ }
+
+ // 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( mpDevice )
+ {
+ mpRecordedActions->push_back( Action() );
+ Action& rAct=mpRecordedActions->back();
+
+ setupGraphicsState( rAct, viewState, renderState );
+ rAct.maPolyPolys.push_back(
+ ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(xPolyPolygon));
+ rAct.maPolyPolys.back().makeUnique(); // own copy, for thread safety
+
+ rAct.maFunction = &lcl_fillPolyPolygon;
+ }
+
+ // TODO(P1): Provide caching here.
+ return uno::Reference< rendering::XCachedPrimitive >(nullptr);
+ }
+
+ 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::fillPolyPolygon: polygon is NULL");
+
+ if( mpDevice )
+ {
+ mpRecordedActions->push_back( Action() );
+ Action& rAct=mpRecordedActions->back();
+
+ setupGraphicsState( rAct, viewState, renderState );
+ rAct.maPolyPolys.push_back(
+ ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(xPolyPolygon));
+ rAct.maPolyPolys.back().makeUnique(); // own copy, for thread safety
+
+ // 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 )
+ {
+ // copy state from Gradient polypoly locally
+ // (given object might change!)
+ const ::canvas::ParametricPolyPolygon::Values& rValues(
+ pGradient->getValues() );
+
+ rAct.maFunction = std::bind(&lcl_fillGradientPolyPolygon,
+ std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4,
+ rValues,
+ textures[0],
+ std::placeholders::_6);
+ }
+ else
+ {
+ // TODO(F1): The generic case is missing here
+ ENSURE_OR_THROW( false,
+ "CanvasHelper::fillTexturedPolyPolygon(): unknown parametric polygon encountered" );
+ }
+ }
+ else if( textures[0].Bitmap.is() )
+ {
+ // own bitmap?
+ CanvasBitmap* pOwnBitmap=dynamic_cast<CanvasBitmap*>(textures[0].Bitmap.get());
+ if( pOwnBitmap )
+ {
+ // TODO(F2): own texture bitmap
+ }
+ else
+ {
+ // TODO(P3): Highly inefficient - simply copies pixel data
+
+ uno::Reference< rendering::XIntegerReadOnlyBitmap > xIntegerBitmap(
+ textures[0].Bitmap,
+ uno::UNO_QUERY);
+ if( xIntegerBitmap.is() )
+ {
+ const geometry::IntegerSize2D aSize=xIntegerBitmap->getSize();
+ rendering::IntegerBitmapLayout aLayout;
+ uno::Sequence<sal_Int8> aPixelData=
+ xIntegerBitmap->getData(
+ aLayout,
+ geometry::IntegerRectangle2D(0,0,aSize.Width,aSize.Height));
+
+ // force-convert color to ARGB8888 int color space
+ uno::Sequence<sal_Int8> aARGBBytes(
+ aLayout.ColorSpace->convertToIntegerColorSpace(
+ aPixelData,
+ canvas::tools::getStdColorSpace()));
+
+ rAct.maFunction = std::bind(&lcl_fillTexturedPolyPolygon,
+ std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4,
+ textures[0],
+ aSize,
+ aARGBBytes,
+ rtl_crc32(0,
+ aARGBBytes.getConstArray(),
+ aARGBBytes.getLength()),
+ std::placeholders::_6);
+ }
+ // TODO(F1): handle non-integer case
+ }
+ }
+ }
+
+ // 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( mpDevice )
+ 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*/ )
+ {
+ // TODO - but not used from slideshow
+ 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: text is NULL");
+
+ if( mpDevice )
+ {
+ ScopedVclPtrInstance< VirtualDevice > pVDev;
+ pVDev->EnableOutput(false);
+
+ auto pLayoutFont = xLayoutetText->getFont();
+ CanvasFont* pFont=dynamic_cast<CanvasFont*>(pLayoutFont.get());
+ const rendering::StringContext& rTxt=xLayoutetText->getText();
+ if( pFont && rTxt.Length )
+ {
+ // create the font
+ const rendering::FontRequest& rFontRequest = pFont->getFontRequest();
+ const geometry::Matrix2D& rFontMatrix = pFont->getFontMatrix();
+ vcl::Font aFont(
+ rFontRequest.FontDescription.FamilyName,
+ rFontRequest.FontDescription.StyleName,
+ Size( 0, ::basegfx::fround(rFontRequest.CellSize)));
+
+ aFont.SetAlignment( ALIGN_BASELINE );
+ aFont.SetCharSet( (rFontRequest.FontDescription.IsSymbolFont==util::TriState_YES) ? RTL_TEXTENCODING_SYMBOL : RTL_TEXTENCODING_UNICODE );
+ aFont.SetVertical( rFontRequest.FontDescription.IsVertical==util::TriState_YES );
+ aFont.SetWeight( static_cast<FontWeight>(rFontRequest.FontDescription.FontDescription.Weight) );
+ aFont.SetItalic( (rFontRequest.FontDescription.FontDescription.Letterform<=8) ? ITALIC_NONE : ITALIC_NORMAL );
+
+ if (pFont->getEmphasisMark())
+ aFont.SetEmphasisMark(FontEmphasisMark(pFont->getEmphasisMark()));
+
+ // adjust to stretched font
+ if(!::rtl::math::approxEqual(rFontMatrix.m00, rFontMatrix.m11))
+ {
+ const Size aSize = pVDev->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
+ pVDev->SetFont(aFont);
+
+ mpRecordedActions->push_back( Action() );
+ Action& rAct=mpRecordedActions->back();
+
+ setupGraphicsState( rAct, viewState, renderState );
+
+ // handle custom spacing, if there
+ uno::Sequence<double> aLogicalAdvancements=xLayoutetText->queryLogicalAdvancements();
+ if( aLogicalAdvancements.hasElements() )
+ {
+ // create the DXArray
+ const sal_Int32 nLen( aLogicalAdvancements.getLength() );
+ std::vector<sal_Int32> aDXArray(nLen);
+ for( sal_Int32 i=0; i<nLen; ++i )
+ aDXArray[i] = basegfx::fround( aLogicalAdvancements[i] );
+
+ // get the glyphs
+ pVDev->GetTextOutlines(rAct.maPolyPolys,
+ rTxt.Text,
+ 0,
+ rTxt.StartPosition,
+ rTxt.Length,
+ 0,
+ aDXArray);
+ }
+ else
+ {
+ // get the glyphs
+ pVDev->GetTextOutlines(rAct.maPolyPolys,
+ rTxt.Text,
+ 0,
+ rTxt.StartPosition,
+ rTxt.Length );
+ }
+
+ // own copy, for thread safety
+ for( auto& rPoly : rAct.maPolyPolys )
+ rPoly.makeUnique();
+
+ rAct.maFunction = &lcl_fillPolyPolygon;
+ }
+ }
+
+ // TODO
+ 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( mpDevice )
+ {
+ // own bitmap?
+ CanvasBitmap* pOwnBitmap=dynamic_cast<CanvasBitmap*>(xBitmap.get());
+ if( pOwnBitmap )
+ {
+ // insert as transformed copy of bitmap action vector -
+ // during rendering, this gets rendered into a temporary
+ // buffer, and then composited to the front
+ mpRecordedActions->push_back( Action() );
+ Action& rAct=mpRecordedActions->back();
+
+ setupGraphicsState( rAct, viewState, renderState );
+ rAct.maFunction = std::bind(&lcl_drawOwnBitmap,
+ std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5,
+ *pOwnBitmap);
+ }
+ else
+ {
+ // TODO(P3): Highly inefficient - simply copies pixel data
+
+ uno::Reference< rendering::XIntegerReadOnlyBitmap > xIntegerBitmap(
+ xBitmap, uno::UNO_QUERY);
+ if( xIntegerBitmap.is() )
+ {
+ const geometry::IntegerSize2D aSize=xBitmap->getSize();
+ rendering::IntegerBitmapLayout aLayout;
+ uno::Sequence<sal_Int8> aPixelData=
+ xIntegerBitmap->getData(
+ aLayout,
+ geometry::IntegerRectangle2D(0,0,aSize.Width,aSize.Height));
+
+ // force-convert color to ARGB8888 int color space
+ uno::Sequence<sal_Int8> aARGBBytes(
+ aLayout.ColorSpace->convertToIntegerColorSpace(
+ aPixelData,
+ canvas::tools::getStdColorSpace()));
+
+ mpRecordedActions->push_back( Action() );
+ Action& rAct=mpRecordedActions->back();
+
+ setupGraphicsState( rAct, viewState, renderState );
+ rAct.maFunction = std::bind(&lcl_drawGenericBitmap,
+ std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5,
+ aSize, aARGBBytes,
+ rtl_crc32(0,
+ aARGBBytes.getConstArray(),
+ aARGBBytes.getLength()));
+ }
+ // TODO(F1): handle non-integer case
+ }
+ }
+
+ // 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 )
+ {
+ // TODO(F3): remove this wart altogether
+ return drawBitmap(pCanvas, xBitmap, viewState, renderState);
+ }
+
+
+ void CanvasHelper::setupGraphicsState( Action& o_action,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState )
+ {
+ ENSURE_OR_THROW( mpDevice,
+ "CanvasHelper::setupGraphicsState: reference device invalid" );
+
+ // TODO(F3): clipping
+ // TODO(P2): think about caching transformations between canvas calls
+
+ // setup overall transform only now. View clip above was
+ // relative to view transform
+ ::canvas::tools::mergeViewAndRenderTransform(o_action.maTransform,
+ viewState,
+ renderState);
+ // setup compositing - mapping courtesy David Reveman
+ // (glitz_operator.c)
+ switch( renderState.CompositeOperation )
+ {
+ case rendering::CompositeOperation::OVER:
+ o_action.meSrcBlendMode=GL_ONE;
+ o_action.meDstBlendMode=GL_ONE_MINUS_SRC_ALPHA;
+ break;
+ case rendering::CompositeOperation::CLEAR:
+ o_action.meSrcBlendMode=GL_ZERO;
+ o_action.meDstBlendMode=GL_ZERO;
+ break;
+ case rendering::CompositeOperation::SOURCE:
+ o_action.meSrcBlendMode=GL_ONE;
+ o_action.meDstBlendMode=GL_ZERO;
+ break;
+ case rendering::CompositeOperation::UNDER:
+ case rendering::CompositeOperation::DESTINATION:
+ o_action.meSrcBlendMode=GL_ZERO;
+ o_action.meDstBlendMode=GL_ONE;
+ break;
+ case rendering::CompositeOperation::INSIDE:
+ o_action.meSrcBlendMode=GL_DST_ALPHA;
+ o_action.meDstBlendMode=GL_ZERO;
+ break;
+ case rendering::CompositeOperation::INSIDE_REVERSE:
+ o_action.meSrcBlendMode=GL_ONE_MINUS_DST_ALPHA;
+ o_action.meDstBlendMode=GL_ZERO;
+ break;
+ case rendering::CompositeOperation::OUTSIDE:
+ o_action.meSrcBlendMode=GL_ONE_MINUS_DST_ALPHA;
+ o_action.meDstBlendMode=GL_ONE;
+ break;
+ case rendering::CompositeOperation::OUTSIDE_REVERSE:
+ o_action.meSrcBlendMode=GL_ZERO;
+ o_action.meDstBlendMode=GL_ONE_MINUS_SRC_ALPHA;
+ break;
+ case rendering::CompositeOperation::ATOP:
+ o_action.meSrcBlendMode=GL_DST_ALPHA;
+ o_action.meDstBlendMode=GL_ONE_MINUS_SRC_ALPHA;
+ break;
+ case rendering::CompositeOperation::ATOP_REVERSE:
+ o_action.meSrcBlendMode=GL_ONE_MINUS_DST_ALPHA;
+ o_action.meDstBlendMode=GL_SRC_ALPHA;
+ break;
+ case rendering::CompositeOperation::XOR:
+ o_action.meSrcBlendMode=GL_ONE_MINUS_DST_ALPHA;
+ o_action.meDstBlendMode=GL_ONE_MINUS_SRC_ALPHA;
+ break;
+ case rendering::CompositeOperation::ADD:
+ o_action.meSrcBlendMode=GL_ONE;
+ o_action.meDstBlendMode=GL_ONE;
+ break;
+ case rendering::CompositeOperation::SATURATE:
+ o_action.meSrcBlendMode=GL_SRC_ALPHA_SATURATE;
+ o_action.meDstBlendMode=GL_SRC_ALPHA_SATURATE;
+ break;
+
+ default:
+ ENSURE_OR_THROW( false, "CanvasHelper::setupGraphicsState: unexpected mode" );
+ break;
+ }
+
+ if (renderState.DeviceColor.hasElements())
+ o_action.maARGBColor =
+ mpDevice->getDeviceColorSpace()->convertToARGB(renderState.DeviceColor)[0];
+ }
+
+ bool CanvasHelper::renderRecordedActions() const
+ {
+ for( const auto& rRecordedAction : *mpRecordedActions )
+ {
+ if( !rRecordedAction.maFunction( *this,
+ rRecordedAction.maTransform,
+ rRecordedAction.meSrcBlendMode,
+ rRecordedAction.meDstBlendMode,
+ rRecordedAction.maARGBColor,
+ rRecordedAction.maPolyPolys ) )
+ return false;
+ }
+
+ return true;
+ }
+
+ size_t CanvasHelper::getRecordedActionCount() const
+ {
+ return mpRecordedActions->size();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/opengl/ogl_canvashelper.hxx b/canvas/source/opengl/ogl_canvashelper.hxx
new file mode 100644
index 000000000..49513983d
--- /dev/null
+++ b/canvas/source/opengl/ogl_canvashelper.hxx
@@ -0,0 +1,219 @@
+/* -*- 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/.
+ */
+
+#pragma once
+
+#include <com/sun/star/rendering/XCanvas.hpp>
+
+#include <o3tl/cow_wrapper.hxx>
+#include <vector>
+
+namespace oglcanvas
+{
+ class SpriteDeviceHelper;
+
+ /** Helper class for basic canvas functionality. */
+ class CanvasHelper
+ {
+ public:
+ CanvasHelper();
+
+ // outline because of incomplete type Action
+ ~CanvasHelper();
+ CanvasHelper& operator=( const CanvasHelper& );
+
+ /// 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 init( css::rendering::XGraphicDevice& rDevice,
+ SpriteDeviceHelper& rDeviceHelper );
+
+ // CanvasHelper functionality
+ // ==========================
+
+ // XCanvas (only providing, not implementing the
+ // interface. Also note subtle method parameter differences)
+ void clear();
+ 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 >& layoutetText,
+ 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() { return css::uno::Reference< css::rendering::XGraphicDevice >(mpDevice); }
+
+ /** Write out recorded actions
+ */
+ bool renderRecordedActions() const;
+
+ /** Retrieve number of recorded actions
+ */
+ size_t getRecordedActionCount() const;
+
+ SpriteDeviceHelper* getDeviceHelper() const { return mpDeviceHelper; }
+ css::rendering::XGraphicDevice* getDevice() const { return mpDevice; }
+
+ struct Action;
+ typedef o3tl::cow_wrapper< std::vector<Action>,
+ o3tl::ThreadSafeRefCountingPolicy > RecordVectorT;
+
+ private:
+ CanvasHelper( const CanvasHelper& ) = delete;
+
+ void setupGraphicsState( Action& o_action,
+ const css::rendering::ViewState& viewState,
+ const css::rendering::RenderState& renderState );
+
+ /** Phyical output device
+
+ Deliberately not a refcounted reference, because of
+ potential circular references for spritecanvas.
+ */
+ css::rendering::XGraphicDevice* mpDevice;
+
+ /** Internal helper - used for a few global GL objects,
+ e.g. shader programs; and caches
+ */
+ SpriteDeviceHelper* mpDeviceHelper;
+
+ /** Ptr to array of recorded render calls
+
+ Gets shared copy-on-write, when this CanvasHelper is
+ copied (used e.g. for CanvasBitmap)
+ */
+ RecordVectorT mpRecordedActions;
+ };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/opengl/ogl_canvastools.cxx b/canvas/source/opengl/ogl_canvastools.cxx
new file mode 100644
index 000000000..c5fbe0255
--- /dev/null
+++ b/canvas/source/opengl/ogl_canvastools.cxx
@@ -0,0 +1,148 @@
+/* -*- 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/.
+ */
+
+#include <sal/config.h>
+
+#include <epoxy/gl.h>
+
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/polygon/b2dpolygontriangulator.hxx>
+#include <basegfx/polygon/b2dpolypolygon.hxx>
+#include <basegfx/utils/tools.hxx>
+#include <com/sun/star/rendering/ARGBColor.hpp>
+
+#include "ogl_canvastools.hxx"
+
+using namespace ::com::sun::star;
+
+namespace oglcanvas
+{
+ /// triangulates polygon before
+ void renderComplexPolyPolygon( const ::basegfx::B2DPolyPolygon& rPolyPoly )
+ {
+ ::basegfx::B2DPolyPolygon aPolyPoly(rPolyPoly);
+ if( aPolyPoly.areControlPointsUsed() )
+ aPolyPoly = rPolyPoly.getDefaultAdaptiveSubdivision();
+ const ::basegfx::B2DRange& rBounds(aPolyPoly.getB2DRange());
+ const double nWidth=rBounds.getWidth();
+ const double nHeight=rBounds.getHeight();
+ const ::basegfx::triangulator::B2DTriangleVector rTriangulatedPolygon(
+ ::basegfx::triangulator::triangulate(aPolyPoly));
+
+ for( size_t i=0; i<rTriangulatedPolygon.size(); i++ )
+ {
+ const::basegfx::triangulator::B2DTriangle& rCandidate(rTriangulatedPolygon[i]);
+ glTexCoord2f(
+ rCandidate.getA().getX()/nWidth,
+ rCandidate.getA().getY()/nHeight);
+ glVertex2d(
+ rCandidate.getA().getX(),
+ rCandidate.getA().getY());
+
+ glTexCoord2f(
+ rCandidate.getB().getX()/nWidth,
+ rCandidate.getB().getY()/nHeight);
+ glVertex2d(
+ rCandidate.getB().getX(),
+ rCandidate.getB().getY());
+
+ glTexCoord2f(
+ rCandidate.getC().getX()/nWidth,
+ rCandidate.getC().getY()/nHeight);
+ glVertex2d(
+ rCandidate.getC().getX(),
+ rCandidate.getC().getY());
+ }
+ }
+
+ /** only use this for line polygons.
+
+ better not leave triangulation to OpenGL. also, ignores texturing
+ */
+ void renderPolyPolygon( const ::basegfx::B2DPolyPolygon& rPolyPoly )
+ {
+ ::basegfx::B2DPolyPolygon aPolyPoly(rPolyPoly);
+ if( aPolyPoly.areControlPointsUsed() )
+ aPolyPoly = rPolyPoly.getDefaultAdaptiveSubdivision();
+
+ for( sal_uInt32 i=0; i<aPolyPoly.count(); i++ )
+ {
+ glBegin(GL_LINE_STRIP);
+
+ const ::basegfx::B2DPolygon& rPolygon( aPolyPoly.getB2DPolygon(i) );
+
+ const sal_uInt32 nPts=rPolygon.count();
+ const sal_uInt32 nExtPts=nPts + int(rPolygon.isClosed());
+ for( sal_uInt32 j=0; j<nExtPts; j++ )
+ {
+ const ::basegfx::B2DPoint& rPt( rPolygon.getB2DPoint( j % nPts ) );
+ glVertex2d(rPt.getX(), rPt.getY());
+ }
+
+ glEnd();
+ }
+ }
+
+ void setupState( const ::basegfx::B2DHomMatrix& rTransform,
+ GLenum eSrcBlend,
+ GLenum eDstBlend,
+ const rendering::ARGBColor& rColor )
+ {
+ double aGLTransform[] =
+ {
+ rTransform.get(0,0), rTransform.get(1,0), 0, 0,
+ rTransform.get(0,1), rTransform.get(1,1), 0, 0,
+ 0, 0, 1, 0,
+ rTransform.get(0,2), rTransform.get(1,2), 0, 1
+ };
+ glMultMatrixd(aGLTransform);
+
+ glEnable(GL_BLEND);
+ glBlendFunc(eSrcBlend, eDstBlend);
+
+ glColor4d(rColor.Red,
+ rColor.Green,
+ rColor.Blue,
+ rColor.Alpha);
+
+ // GL 1.2:
+ // glBlendEquation( GLenum mode );
+ // glBlendColor( GLclampf red, GLclampf green,GLclampf blue, GLclampf alpha );
+ // glConvolutionFilter1D
+ // glConvolutionFilter2D
+ // glSeparableFilter2D
+ }
+
+ void renderOSD( const std::vector<double>& rNumbers, double scale )
+ {
+ double y=4.0;
+ basegfx::B2DHomMatrix aTmp;
+ basegfx::B2DHomMatrix aScaleShear;
+ aScaleShear.shearX(-0.1);
+ aScaleShear.scale(scale,scale);
+
+ for(double rNumber : rNumbers)
+ {
+ aTmp.identity();
+ aTmp.translate(0,y);
+ y += 1.2*scale;
+
+ basegfx::B2DPolyPolygon aPoly=
+ basegfx::utils::number2PolyPolygon(rNumber,10,3);
+
+ aTmp=aTmp*aScaleShear;
+ aPoly.transform(aTmp);
+
+ glColor4f(0,1,0,1);
+ renderPolyPolygon(aPoly);
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/opengl/ogl_canvastools.hxx b/canvas/source/opengl/ogl_canvastools.hxx
new file mode 100644
index 000000000..8e7029fbe
--- /dev/null
+++ b/canvas/source/opengl/ogl_canvastools.hxx
@@ -0,0 +1,37 @@
+/* -*- 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/.
+ */
+
+#pragma once
+
+#include <sal/config.h>
+#include <vector>
+
+#include <epoxy/gl.h>
+
+namespace com::sun::star::rendering {
+ struct ARGBColor;
+}
+namespace basegfx {
+ class B2DPolyPolygon;
+ class B2DHomMatrix;
+}
+
+namespace oglcanvas
+{
+ void renderComplexPolyPolygon( const ::basegfx::B2DPolyPolygon& rPolyPoly );
+ void renderPolyPolygon( const ::basegfx::B2DPolyPolygon& rPolyPoly );
+ void setupState( const ::basegfx::B2DHomMatrix& rTransform,
+ GLenum eSrcBlend,
+ GLenum eDstBlend,
+ const com::sun::star::rendering::ARGBColor& rColor );
+
+ void renderOSD( const std::vector<double>& rNumbers, double scale );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/opengl/ogl_spritecanvas.cxx b/canvas/source/opengl/ogl_spritecanvas.cxx
new file mode 100644
index 000000000..047627dbf
--- /dev/null
+++ b/canvas/source/opengl/ogl_spritecanvas.cxx
@@ -0,0 +1,167 @@
+/* -*- 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/.
+ */
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+#include <com/sun/star/lang/NoSupportException.hpp>
+#include <osl/mutex.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <tools/diagnose_ex.h>
+#include <vcl/opengl/OpenGLHelper.hxx>
+#include <vcl/window.hxx>
+
+#include "ogl_canvascustomsprite.hxx"
+#include "ogl_spritecanvas.hxx"
+
+using namespace ::com::sun::star;
+
+namespace oglcanvas
+{
+ SpriteCanvas::SpriteCanvas( const uno::Sequence< uno::Any >& aArguments,
+ const uno::Reference< uno::XComponentContext >& /*rxContext*/ ) :
+ maArguments(aArguments)
+ {
+ }
+
+ void SpriteCanvas::initialize()
+ {
+ // Only call initialize when not in probe mode
+ if( !maArguments.hasElements() )
+ return;
+
+ SAL_INFO("canvas.ogl", "SpriteCanvas::initialize called" );
+
+ /* aArguments:
+ 0: ptr to creating instance (Window or VirtualDevice)
+ 1: current bounds of creating instance
+ 2: bool, denoting always on top state for Window (always false for VirtualDevice)
+ 3: XWindow for creating Window (or empty for VirtualDevice)
+ 4: SystemGraphicsData as a streamed Any
+ */
+ ENSURE_ARG_OR_THROW( maArguments.getLength() >= 4 &&
+ maArguments[3].getValueTypeClass() == uno::TypeClass_INTERFACE,
+ "OpenGL SpriteCanvas::initialize: wrong number of arguments, or wrong types" );
+
+ uno::Reference< awt::XWindow > xParentWindow;
+ maArguments[3] >>= xParentWindow;
+ VclPtr<vcl::Window> pParentWindow = VCLUnoHelper::GetWindow(xParentWindow);
+ if( !pParentWindow )
+ throw lang::NoSupportException(
+ "Parent window not VCL window, or canvas out-of-process!", nullptr);
+
+ awt::Rectangle aRect;
+ maArguments[1] >>= aRect;
+
+ // setup helper
+ maDeviceHelper.init( *pParentWindow,
+ *this,
+ aRect );
+ maCanvasHelper.init( *this, maDeviceHelper );
+ maArguments.realloc(0);
+ }
+
+ void SpriteCanvas::disposeThis()
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ // 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 );
+ }
+
+ uno::Reference< rendering::XAnimatedSprite > SAL_CALL SpriteCanvas::createSpriteFromAnimation(
+ const uno::Reference< rendering::XAnimation >& /*animation*/ )
+ {
+ return uno::Reference< rendering::XAnimatedSprite >();
+ }
+
+ uno::Reference< rendering::XAnimatedSprite > SAL_CALL SpriteCanvas::createSpriteFromBitmaps(
+ const uno::Sequence< uno::Reference< rendering::XBitmap > >& /*animationBitmaps*/,
+ ::sal_Int8 /*interpolationMode*/ )
+ {
+ return uno::Reference< rendering::XAnimatedSprite >();
+ }
+
+ uno::Reference< rendering::XCustomSprite > SAL_CALL SpriteCanvas::createCustomSprite(
+ const geometry::RealSize2D& spriteSize )
+ {
+ return uno::Reference< rendering::XCustomSprite >(
+ new CanvasCustomSprite(spriteSize, this, maDeviceHelper) );
+ }
+
+ uno::Reference< rendering::XSprite > SAL_CALL SpriteCanvas::createClonedSprite(
+ const uno::Reference< rendering::XSprite >& /*original*/ )
+ {
+ return uno::Reference< rendering::XSprite >();
+ }
+
+ sal_Bool SAL_CALL SpriteCanvas::updateScreen(sal_Bool bUpdateAll)
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ return maDeviceHelper.showBuffer(mbIsVisible, bUpdateAll);
+ }
+
+ OUString SAL_CALL SpriteCanvas::getServiceName( )
+ {
+ return "com.sun.star.rendering.SpriteCanvas.OGL";
+ }
+
+ void SpriteCanvas::show( const ::rtl::Reference< CanvasCustomSprite >& xSprite )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ maDeviceHelper.show(xSprite);
+ }
+
+ void SpriteCanvas::hide( const ::rtl::Reference< CanvasCustomSprite >& xSprite )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ maDeviceHelper.hide(xSprite);
+ }
+
+ void SpriteCanvas::renderRecordedActions() const
+ {
+ maCanvasHelper.renderRecordedActions();
+ }
+
+}
+
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_rendering_SpriteCanvas_OGL_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& args)
+{
+ if( !OpenGLHelper::supportsOpenGL())
+ return nullptr;
+ rtl::Reference<oglcanvas::SpriteCanvas> p = new oglcanvas::SpriteCanvas(args, context);
+ cppu::acquire(p.get());
+ p->initialize();
+ return static_cast<cppu::OWeakObject*>(p.get());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/opengl/ogl_spritecanvas.hxx b/canvas/source/opengl/ogl_spritecanvas.hxx
new file mode 100644
index 000000000..33948b2d0
--- /dev/null
+++ b/canvas/source/opengl/ogl_spritecanvas.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/.
+ */
+
+#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/awt/XWindowListener.hpp>
+#include <com/sun/star/util/XUpdatable.hpp>
+#include <com/sun/star/rendering/XSpriteCanvas.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/canvasbase.hxx>
+#include <base/disambiguationhelper.hxx>
+#include <base/bufferedgraphicdevicebase.hxx>
+
+#include "ogl_spritedevicehelper.hxx"
+#include "ogl_canvashelper.hxx"
+
+
+namespace oglcanvas
+{
+ class CanvasCustomSprite;
+
+ typedef ::cppu::WeakComponentImplHelper< css::rendering::XSpriteCanvas,
+ css::rendering::XGraphicDevice,
+ css::lang::XMultiServiceFactory,
+ css::rendering::XBufferController,
+ css::awt::XWindowListener,
+ css::util::XUpdatable,
+ css::beans::XPropertySet,
+ css::lang::XServiceName > WindowGraphicDeviceBase_Base;
+ typedef ::canvas::BufferedGraphicDeviceBase<
+ ::canvas::DisambiguationHelper< WindowGraphicDeviceBase_Base >,
+ SpriteDeviceHelper,
+ ::osl::MutexGuard,
+ ::cppu::OWeakObject > SpriteCanvasDeviceBaseT;
+
+ typedef ::canvas::CanvasBase< SpriteCanvasDeviceBaseT,
+ CanvasHelper,
+ ::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:
+ 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
+ virtual css::uno::Reference< css::rendering::XAnimatedSprite > SAL_CALL createSpriteFromAnimation( const css::uno::Reference< css::rendering::XAnimation >& animation ) override;
+ virtual css::uno::Reference< css::rendering::XAnimatedSprite > SAL_CALL createSpriteFromBitmaps( const css::uno::Sequence< css::uno::Reference< css::rendering::XBitmap > >& animationBitmaps, ::sal_Int8 interpolationMode ) override;
+ virtual css::uno::Reference< css::rendering::XCustomSprite > SAL_CALL createCustomSprite( const css::geometry::RealSize2D& spriteSize ) override;
+ virtual css::uno::Reference< css::rendering::XSprite > SAL_CALL createClonedSprite( const css::uno::Reference< css::rendering::XSprite >& original ) override;
+ virtual sal_Bool SAL_CALL updateScreen( sal_Bool bUpdateAll ) override;
+
+ // XServiceName
+ virtual OUString SAL_CALL getServiceName( ) override;
+
+ void show( const ::rtl::Reference< CanvasCustomSprite >& );
+ void hide( const ::rtl::Reference< CanvasCustomSprite >& );
+
+ /** Write out recorded actions
+ */
+ void renderRecordedActions() const;
+
+ private:
+ css::uno::Sequence< css::uno::Any > maArguments;
+ };
+
+ typedef ::rtl::Reference< SpriteCanvas > SpriteCanvasRef;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/opengl/ogl_spritedevicehelper.cxx b/canvas/source/opengl/ogl_spritedevicehelper.cxx
new file mode 100644
index 000000000..8cb0a6934
--- /dev/null
+++ b/canvas/source/opengl/ogl_spritedevicehelper.cxx
@@ -0,0 +1,549 @@
+/* -*- 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/.
+ */
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/utils/canvastools.hxx>
+#include <basegfx/utils/unopolypolygon.hxx>
+#include <com/sun/star/awt/XTopWindow.hpp>
+#include <com/sun/star/rendering/XIntegerBitmapColorSpace.hpp>
+#include <com/sun/star/uno/Reference.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <vcl/canvastools.hxx>
+#include <vcl/opengl/OpenGLHelper.hxx>
+#include <vcl/syschild.hxx>
+
+#include "ogl_spritedevicehelper.hxx"
+#include "ogl_spritecanvas.hxx"
+#include "ogl_canvasbitmap.hxx"
+#include "ogl_canvastools.hxx"
+#include "ogl_canvascustomsprite.hxx"
+#include "ogl_texturecache.hxx"
+
+using namespace ::com::sun::star;
+
+static void initContext()
+{
+ // need the backside for mirror effects
+ glDisable(GL_CULL_FACE);
+
+ // no perspective, we're 2D
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+
+ // misc preferences
+ glEnable(GL_POINT_SMOOTH);
+ glEnable(GL_LINE_SMOOTH);
+ glEnable(GL_POLYGON_SMOOTH);
+ glHint(GL_POINT_SMOOTH_HINT,GL_NICEST);
+ glHint(GL_LINE_SMOOTH_HINT,GL_NICEST);
+ glHint(GL_POLYGON_SMOOTH_HINT,GL_NICEST);
+ glShadeModel(GL_FLAT);
+}
+
+static void initTransformation(const ::Size& rSize)
+{
+ // use whole window
+ glViewport( 0,0,
+ static_cast<GLsizei>(rSize.Width()),
+ static_cast<GLsizei>(rSize.Height()) );
+
+ // model coordinate system is already in device pixel
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+ glTranslated(-1.0, 1.0, 0.0);
+ glScaled( 2.0 / rSize.Width(),
+ -2.0 / rSize.Height(),
+ 1.0 );
+
+ // clear to black
+ glClearColor(0,0,0,0);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+}
+
+namespace oglcanvas
+{
+
+ SpriteDeviceHelper::SpriteDeviceHelper() :
+ mpSpriteCanvas(nullptr),
+ mpTextureCache(std::make_shared<TextureCache>()),
+ mnLinearTwoColorGradientProgram(0),
+ mnLinearMultiColorGradientProgram(0),
+ mnRadialTwoColorGradientProgram(0),
+ mnRadialMultiColorGradientProgram(0),
+ mnRectangularTwoColorGradientProgram(0),
+ mnRectangularMultiColorGradientProgram(0),
+ mxContext(OpenGLContext::Create())
+ {}
+
+ SpriteDeviceHelper::~SpriteDeviceHelper()
+ { mxContext->dispose(); }
+
+ void SpriteDeviceHelper::init( vcl::Window& rWindow,
+ SpriteCanvas& rSpriteCanvas,
+ const awt::Rectangle& rViewArea )
+ {
+ mpSpriteCanvas = &rSpriteCanvas;
+
+ rSpriteCanvas.setWindow(
+ uno::Reference<awt::XWindow2>(
+ VCLUnoHelper::GetInterface(&rWindow),
+ uno::UNO_QUERY_THROW) );
+
+ mxContext->requestLegacyContext();
+ mxContext->init(&rWindow);
+ // init window context
+ initContext();
+
+ mnLinearMultiColorGradientProgram =
+ OpenGLHelper::LoadShaders("dummyVertexShader", "linearMultiColorGradientFragmentShader");
+
+ mnLinearTwoColorGradientProgram =
+ OpenGLHelper::LoadShaders("dummyVertexShader", "linearTwoColorGradientFragmentShader");
+
+ mnRadialMultiColorGradientProgram =
+ OpenGLHelper::LoadShaders("dummyVertexShader", "radialMultiColorGradientFragmentShader");
+
+ mnRadialTwoColorGradientProgram =
+ OpenGLHelper::LoadShaders("dummyVertexShader", "radialTwoColorGradientFragmentShader");
+
+ mnRectangularMultiColorGradientProgram =
+ OpenGLHelper::LoadShaders("dummyVertexShader", "rectangularMultiColorGradientFragmentShader");
+
+ mnRectangularTwoColorGradientProgram =
+ OpenGLHelper::LoadShaders("dummyVertexShader", "rectangularTwoColorGradientFragmentShader");
+
+ mxContext->makeCurrent();
+
+ notifySizeUpdate(rViewArea);
+ // TODO(E3): check for GL_ARB_imaging extension
+ }
+
+ void SpriteDeviceHelper::disposing()
+ {
+ // release all references
+ mpSpriteCanvas = nullptr;
+ mpTextureCache.reset();
+
+ if( mxContext->isInitialized() )
+ {
+ glDeleteProgram( mnRectangularTwoColorGradientProgram );
+ glDeleteProgram( mnRectangularMultiColorGradientProgram );
+ glDeleteProgram( mnRadialTwoColorGradientProgram );
+ glDeleteProgram( mnRadialMultiColorGradientProgram );
+ glDeleteProgram( mnLinearTwoColorGradientProgram );
+ glDeleteProgram( mnLinearMultiColorGradientProgram );
+ }
+ }
+
+ geometry::RealSize2D SpriteDeviceHelper::getPhysicalResolution()
+ {
+ if( !mxContext->isInitialized() )
+ return ::canvas::tools::createInfiniteSize2D(); // we're disposed
+
+ // Map a one-by-one millimeter box to pixel
+ SystemChildWindow* pChildWindow = mxContext->getChildWindow();
+ const MapMode aOldMapMode( pChildWindow->GetMapMode() );
+ pChildWindow->SetMapMode( MapMode(MapUnit::MapMM) );
+ const Size aPixelSize( pChildWindow->LogicToPixel(Size(1,1)) );
+ pChildWindow->SetMapMode( aOldMapMode );
+
+ return vcl::unotools::size2DFromSize( aPixelSize );
+ }
+
+ geometry::RealSize2D SpriteDeviceHelper::getPhysicalSize()
+ {
+ if( !mxContext->isInitialized() )
+ return ::canvas::tools::createInfiniteSize2D(); // we're disposed
+
+ // Map the pixel dimensions of the output window to millimeter
+ SystemChildWindow* pChildWindow = mxContext->getChildWindow();
+ const MapMode aOldMapMode( pChildWindow->GetMapMode() );
+ pChildWindow->SetMapMode( MapMode(MapUnit::MapMM) );
+ const Size aLogSize( pChildWindow->PixelToLogic(pChildWindow->GetOutputSizePixel()) );
+ pChildWindow->SetMapMode( aOldMapMode );
+
+ return vcl::unotools::size2DFromSize( aLogSize );
+ }
+
+ uno::Reference< rendering::XLinePolyPolygon2D > SpriteDeviceHelper::createCompatibleLinePolyPolygon(
+ const uno::Reference< rendering::XGraphicDevice >& /*rDevice*/,
+ const uno::Sequence< uno::Sequence< geometry::RealPoint2D > >& points )
+ {
+ // disposed?
+ if( !mpSpriteCanvas )
+ return uno::Reference< rendering::XLinePolyPolygon2D >(); // we're disposed
+
+ return uno::Reference< rendering::XLinePolyPolygon2D >(
+ new ::basegfx::unotools::UnoPolyPolygon(
+ ::basegfx::unotools::polyPolygonFromPoint2DSequenceSequence( points )));
+ }
+
+ uno::Reference< rendering::XBezierPolyPolygon2D > SpriteDeviceHelper::createCompatibleBezierPolyPolygon(
+ const uno::Reference< rendering::XGraphicDevice >& /*rDevice*/,
+ const uno::Sequence< uno::Sequence< geometry::RealBezierSegment2D > >& points )
+ {
+ // disposed?
+ if( !mpSpriteCanvas )
+ return uno::Reference< rendering::XBezierPolyPolygon2D >(); // we're disposed
+
+ return uno::Reference< rendering::XBezierPolyPolygon2D >(
+ new ::basegfx::unotools::UnoPolyPolygon(
+ ::basegfx::unotools::polyPolygonFromBezier2DSequenceSequence( points ) ) );
+ }
+
+ uno::Reference< rendering::XBitmap > SpriteDeviceHelper::createCompatibleBitmap(
+ const uno::Reference< rendering::XGraphicDevice >& /*rDevice*/,
+ const geometry::IntegerSize2D& size )
+ {
+ // disposed?
+ if( !mpSpriteCanvas )
+ return uno::Reference< rendering::XBitmap >(); // we're disposed
+
+ return uno::Reference< rendering::XBitmap >(
+ new CanvasBitmap( size,
+ mpSpriteCanvas,
+ *this ) );
+ }
+
+ 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 )
+ {
+ // disposed?
+ if( !mpSpriteCanvas )
+ return uno::Reference< rendering::XBitmap >(); // we're disposed
+
+ return uno::Reference< rendering::XBitmap >(
+ new CanvasBitmap( size,
+ mpSpriteCanvas,
+ *this ) );
+ }
+
+ uno::Reference< rendering::XVolatileBitmap > SpriteDeviceHelper::createVolatileAlphaBitmap(
+ const uno::Reference< rendering::XGraphicDevice >& /*rDevice*/,
+ const geometry::IntegerSize2D& /*size*/ )
+ {
+ return uno::Reference< rendering::XVolatileBitmap >();
+ }
+
+ namespace
+ {
+ /** Functor providing a StrictWeakOrdering for XSprites (over
+ priority)
+ */
+ struct SpriteComparator
+ {
+ bool operator()( const ::rtl::Reference<CanvasCustomSprite>& rLHS,
+ const ::rtl::Reference<CanvasCustomSprite>& rRHS ) const
+ {
+ const double nPrioL( rLHS->getPriority() );
+ const double nPrioR( rRHS->getPriority() );
+
+ // if prios are equal, tie-break on ptr value
+ return nPrioL == nPrioR ? rLHS.get() < rRHS.get() : nPrioL < nPrioR;
+ }
+ };
+ }
+
+ bool SpriteDeviceHelper::showBuffer( bool bIsVisible, SAL_UNUSED_PARAMETER bool /*bUpdateAll*/ )
+ {
+ // hidden or disposed?
+ if( !bIsVisible || !mxContext->isInitialized() || !mpSpriteCanvas )
+ return false;
+
+ mxContext->makeCurrent();
+
+ SystemChildWindow* pChildWindow = mxContext->getChildWindow();
+ const ::Size& rOutputSize = pChildWindow->GetSizePixel();
+ initTransformation(rOutputSize);
+
+ // render the actual spritecanvas content
+ mpSpriteCanvas->renderRecordedActions();
+
+ // render all sprites (in order of priority) on top of that
+ std::vector< ::rtl::Reference<CanvasCustomSprite> > aSprites(
+ maActiveSprites.begin(),
+ maActiveSprites.end());
+ std::sort(aSprites.begin(),
+ aSprites.end(),
+ SpriteComparator());
+ for( const auto& rSprite : aSprites )
+ rSprite->renderSprite();
+
+
+ // frame counter, other info
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+ glTranslated(-1.0, 1.0, 0.0);
+ glScaled( 2.0 / rOutputSize.Width(),
+ -2.0 / rOutputSize.Height(),
+ 1.0 );
+
+ const double denominator( maLastUpdate.getElapsedTime() );
+ maLastUpdate.reset();
+
+ const double fps(denominator == 0.0 ? 100.0 : 1.0/denominator);
+ std::vector<double> aVec { fps, static_cast<double>(maActiveSprites.size()),
+ static_cast<double>(mpTextureCache->getCacheSize()),
+ static_cast<double>(mpTextureCache->getCacheMissCount()),
+ static_cast<double>(mpTextureCache->getCacheHitCount()) };
+ renderOSD( aVec, 20 );
+
+ /*
+ * TODO: moggi: fix it!
+ // switch buffer, sync etc.
+ const unx::Window aXWindow=pChildWindow->GetSystemData()->aWindow;
+ unx::glXSwapBuffers(reinterpret_cast<unx::Display*>(mpDisplay),
+ aXWindow);
+ pChildWindow->Show();
+ unx::glXWaitGL();
+ XSync( reinterpret_cast<unx::Display*>(mpDisplay), false );
+ */
+ mxContext->swapBuffers();
+
+ // flush texture cache, such that it does not build up
+ // indefinitely.
+ // TODO: have max cache size/LRU time in config, prune only on
+ // demand
+ mpTextureCache->prune();
+
+ return true;
+ }
+
+ bool SpriteDeviceHelper::switchBuffer( bool bIsVisible, bool bUpdateAll )
+ {
+ // no difference for VCL canvas
+ return showBuffer( bIsVisible, bUpdateAll );
+ }
+
+ uno::Any SpriteDeviceHelper::isAccelerated() const
+ {
+ return css::uno::Any(false);
+ }
+
+ uno::Any SpriteDeviceHelper::getDeviceHandle() const
+ {
+ const SystemChildWindow* pChildWindow = mxContext->getChildWindow();
+ const OutputDevice* pDevice = pChildWindow ? pChildWindow->GetOutDev() : nullptr;
+ return uno::Any(reinterpret_cast<sal_Int64>(pDevice));
+ }
+
+ uno::Any SpriteDeviceHelper::getSurfaceHandle() const
+ {
+ return uno::Any();
+ }
+
+ uno::Reference<rendering::XColorSpace> SpriteDeviceHelper::getColorSpace() const
+ {
+ // always the same
+ return ::canvas::tools::getStdColorSpace();
+ }
+
+ void SpriteDeviceHelper::notifySizeUpdate( const awt::Rectangle& rBounds )
+ {
+ if( mxContext->isInitialized() )
+ {
+ SystemChildWindow* pChildWindow = mxContext->getChildWindow();
+ pChildWindow->setPosSizePixel(
+ 0,0,rBounds.Width,rBounds.Height);
+ }
+ }
+
+ void SpriteDeviceHelper::dumpScreenContent() const
+ {
+ SAL_INFO("canvas.ogl", __func__ );
+ }
+
+ void SpriteDeviceHelper::show( const ::rtl::Reference< CanvasCustomSprite >& xSprite )
+ {
+ maActiveSprites.insert(xSprite);
+ }
+
+ void SpriteDeviceHelper::hide( const ::rtl::Reference< CanvasCustomSprite >& xSprite )
+ {
+ maActiveSprites.erase(xSprite);
+ }
+
+ static void setupUniforms( unsigned int nProgramId,
+ const ::basegfx::B2DHomMatrix& rTexTransform )
+ {
+ const GLint nTransformLocation = glGetUniformLocation(nProgramId,
+ "m_transform" );
+ // OGL is column-major
+ float aTexTransform[] =
+ {
+ float(rTexTransform.get(0,0)), float(rTexTransform.get(1,0)),
+ float(rTexTransform.get(0,1)), float(rTexTransform.get(1,1)),
+ float(rTexTransform.get(0,2)), float(rTexTransform.get(1,2))
+ };
+ glUniformMatrix3x2fv(nTransformLocation,1,false,aTexTransform);
+ }
+
+ static void setupUniforms( unsigned int nProgramId,
+ const rendering::ARGBColor* pColors,
+ const uno::Sequence< double >& rStops,
+ const ::basegfx::B2DHomMatrix& rTexTransform )
+ {
+ glUseProgram(nProgramId);
+
+ GLuint nColorsTexture;
+ glActiveTexture(GL_TEXTURE0);
+ glGenTextures(1, &nColorsTexture);
+ glBindTexture(GL_TEXTURE_1D, nColorsTexture);
+
+ const sal_Int32 nColors=rStops.getLength();
+ glTexImage1D( GL_TEXTURE_1D, 0, GL_RGBA, nColors, 0, GL_RGBA, GL_DOUBLE, pColors );
+ glTexParameteri( GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
+ glTexParameteri( GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
+
+ GLuint nStopsTexture;
+ glActiveTexture(GL_TEXTURE1);
+ glGenTextures(1, &nStopsTexture);
+ glBindTexture(GL_TEXTURE_1D, nStopsTexture);
+
+ glTexImage1D( GL_TEXTURE_1D, 0, GL_ALPHA, nColors, 0, GL_ALPHA, GL_DOUBLE, rStops.getConstArray() );
+ glTexParameteri( GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
+ glTexParameteri( GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
+
+ const GLint nColorArrayLocation = glGetUniformLocation(nProgramId,
+ "t_colorArray4d" );
+ glUniform1i( nColorArrayLocation, 0 ); // unit 0
+
+ const GLint nStopArrayLocation = glGetUniformLocation(nProgramId,
+ "t_stopArray1d" );
+ glUniform1i( nStopArrayLocation, 1 ); // unit 1
+
+ const GLint nNumColorLocation = glGetUniformLocation(nProgramId,
+ "i_nColors" );
+ glUniform1i( nNumColorLocation, nColors-1 );
+
+ setupUniforms(nProgramId,rTexTransform);
+ }
+
+ static void setupUniforms( unsigned int nProgramId,
+ const rendering::ARGBColor& rStartColor,
+ const rendering::ARGBColor& rEndColor,
+ const ::basegfx::B2DHomMatrix& rTexTransform )
+ {
+ glUseProgram(nProgramId);
+
+ const GLint nStartColorLocation = glGetUniformLocation(nProgramId,
+ "v_startColor4d" );
+ glUniform4f(nStartColorLocation,
+ rStartColor.Red,
+ rStartColor.Green,
+ rStartColor.Blue,
+ rStartColor.Alpha);
+
+ const GLint nEndColorLocation = glGetUniformLocation(nProgramId,
+ "v_endColor4d" );
+ glUniform4f(nEndColorLocation,
+ rEndColor.Red,
+ rEndColor.Green,
+ rEndColor.Blue,
+ rEndColor.Alpha);
+
+ setupUniforms(nProgramId,rTexTransform);
+ }
+
+ void SpriteDeviceHelper::useLinearGradientShader( const rendering::ARGBColor* pColors,
+ const uno::Sequence< double >& rStops,
+ const ::basegfx::B2DHomMatrix& rTexTransform )
+ {
+ if( rStops.getLength() > 2 )
+ setupUniforms(mnLinearMultiColorGradientProgram, pColors, rStops, rTexTransform);
+ else
+ setupUniforms(mnLinearTwoColorGradientProgram, pColors[0], pColors[1], rTexTransform);
+ }
+
+ void SpriteDeviceHelper::useRadialGradientShader( const rendering::ARGBColor* pColors,
+ const uno::Sequence< double >& rStops,
+ const ::basegfx::B2DHomMatrix& rTexTransform )
+ {
+ if( rStops.getLength() > 2 )
+ setupUniforms(mnRadialMultiColorGradientProgram, pColors, rStops, rTexTransform);
+ else
+ setupUniforms(mnRadialTwoColorGradientProgram, pColors[0], pColors[1], rTexTransform);
+ }
+
+ void SpriteDeviceHelper::useRectangularGradientShader( const rendering::ARGBColor* pColors,
+ const uno::Sequence< double >& rStops,
+ const ::basegfx::B2DHomMatrix& rTexTransform )
+ {
+ if( rStops.getLength() > 2 )
+ setupUniforms(mnRectangularMultiColorGradientProgram, pColors, rStops, rTexTransform);
+ else
+ setupUniforms(mnRectangularTwoColorGradientProgram, pColors[0], pColors[1], rTexTransform);
+ }
+
+ namespace
+ {
+
+ class BufferContextImpl : public IBufferContext
+ {
+ GLuint mnFramebufferId;
+ GLuint mnDepthId;
+ GLuint mnTextureId;
+
+ virtual void startBufferRendering() override
+ {
+ glBindFramebuffer(GL_FRAMEBUFFER, mnFramebufferId);
+ }
+
+ virtual void endBufferRendering() override
+ {
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+ }
+
+ virtual GLuint getTextureId() override
+ {
+ return mnTextureId;
+ }
+
+ public:
+ explicit BufferContextImpl(const ::basegfx::B2IVector& rSize) :
+ mnFramebufferId(0),
+ mnDepthId(0),
+ mnTextureId(0)
+ {
+ OpenGLHelper::createFramebuffer(rSize.getX(), rSize.getY(), mnFramebufferId,
+ mnDepthId, mnTextureId);
+ }
+
+ virtual ~BufferContextImpl() override
+ {
+ glDeleteTextures(1, &mnTextureId);
+ glDeleteRenderbuffers(1, &mnDepthId);
+ glDeleteFramebuffers(1, &mnFramebufferId);
+ }
+ };
+ }
+
+ IBufferContextSharedPtr SpriteDeviceHelper::createBufferContext(const ::basegfx::B2IVector& rSize) const
+ {
+ return std::make_shared<BufferContextImpl>(rSize);
+ }
+
+ TextureCache& SpriteDeviceHelper::getTextureCache() const
+ {
+ return *mpTextureCache;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/opengl/ogl_spritedevicehelper.hxx b/canvas/source/opengl/ogl_spritedevicehelper.hxx
new file mode 100644
index 000000000..3b9a26262
--- /dev/null
+++ b/canvas/source/opengl/ogl_spritedevicehelper.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/.
+ */
+
+#pragma once
+
+#include <vcl/opengl/OpenGLContext.hxx>
+
+#include <rtl/ref.hxx>
+#include <canvas/elapsedtime.hxx>
+#include <com/sun/star/rendering/XGraphicDevice.hpp>
+
+#include "ogl_buffercontext.hxx"
+
+#include <set>
+
+namespace vcl { class Window; }
+class SystemChildWindow;
+namespace basegfx{ class B2IVector; class B2DHomMatrix; }
+namespace com::sun::star::awt { struct Rectangle; }
+namespace com::sun::star::geometry { struct AffineMatrix2D; }
+
+
+namespace oglcanvas
+{
+ class TextureCache;
+ class SpriteCanvas;
+ class CanvasCustomSprite;
+ class CanvasHelper;
+
+ class SpriteDeviceHelper
+ {
+ public:
+ SpriteDeviceHelper();
+ ~SpriteDeviceHelper();
+
+ /// make noncopyable
+ SpriteDeviceHelper(const SpriteDeviceHelper&) = delete;
+ const SpriteDeviceHelper& operator=(const SpriteDeviceHelper&) = delete;
+
+ void init( vcl::Window& rWindow,
+ SpriteCanvas& rSpriteCanvas,
+ const css::awt::Rectangle& rViewArea );
+
+ /// 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 );
+
+ bool showBuffer( bool bIsVisible, bool bUpdateAll );
+ bool switchBuffer( bool bIsVisible, bool bUpdateAll );
+
+ css::uno::Any isAccelerated() const;
+ css::uno::Any getDeviceHandle() const;
+ css::uno::Any getSurfaceHandle() const;
+ css::uno::Reference<
+ css::rendering::XColorSpace > getColorSpace() 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;
+
+ void show( const ::rtl::Reference< CanvasCustomSprite >& );
+ void hide( const ::rtl::Reference< CanvasCustomSprite >& );
+
+ /// enable linear gradient shader "texture" with given parameters
+ void useLinearGradientShader( const css::rendering::ARGBColor* pColors,
+ const css::uno::Sequence< double >& rStops,
+ const ::basegfx::B2DHomMatrix& rTexTransform );
+ /// enable radial gradient shader "texture" with given parameters
+ void useRadialGradientShader( const css::rendering::ARGBColor* pColors,
+ const css::uno::Sequence< double >& rStops,
+ const ::basegfx::B2DHomMatrix& rTexTransform );
+ /// enable rectangular gradient shader "texture" with given parameters
+ void useRectangularGradientShader( const css::rendering::ARGBColor* pColors,
+ const css::uno::Sequence< double >& rStops,
+ const ::basegfx::B2DHomMatrix& rTexTransform );
+
+ /// create a pbuffer context (for rendering into background surface)
+ IBufferContextSharedPtr createBufferContext(const ::basegfx::B2IVector& rSize) const;
+
+ /// Get instance of internal texture cache
+ TextureCache& getTextureCache() const;
+
+ private:
+ /// Pointer to sprite canvas (owner of this helper), needed to create bitmaps
+ SpriteCanvas* mpSpriteCanvas;
+
+ std::set< ::rtl::Reference< CanvasCustomSprite > > maActiveSprites;
+
+ /// For the frame counter timings
+ ::canvas::tools::ElapsedTime maLastUpdate;
+
+ std::shared_ptr<TextureCache> mpTextureCache;
+
+ unsigned int mnLinearTwoColorGradientProgram;
+ unsigned int mnLinearMultiColorGradientProgram;
+ unsigned int mnRadialTwoColorGradientProgram;
+ unsigned int mnRadialMultiColorGradientProgram;
+ unsigned int mnRectangularTwoColorGradientProgram;
+ unsigned int mnRectangularMultiColorGradientProgram;
+
+ rtl::Reference<OpenGLContext> mxContext;
+ };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/opengl/ogl_textlayout.cxx b/canvas/source/opengl/ogl_textlayout.cxx
new file mode 100644
index 000000000..5eab3d9bc
--- /dev/null
+++ b/canvas/source/opengl/ogl_textlayout.cxx
@@ -0,0 +1,180 @@
+/* -*- 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/.
+ */
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+#include <utility>
+
+#include <tools/diagnose_ex.h>
+
+#include "ogl_textlayout.hxx"
+
+using namespace ::com::sun::star;
+
+namespace oglcanvas
+{
+ TextLayout::TextLayout( rendering::StringContext aText,
+ sal_Int8 nDirection,
+ sal_Int64 /*nRandomSeed*/,
+ CanvasFont::ImplRef rFont ) :
+ TextLayoutBaseT( m_aMutex ),
+ maText(std::move( aText )),
+ mpFont(std::move( rFont )),
+ mnTextDirection( nDirection )
+ {
+ }
+
+ 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_INFO("canvas.ogl", "TextLayout::applyLogicalAdvancements(): mismatching number of advancements");
+ throw lang::IllegalArgumentException();
+ }
+
+ maLogicalAdvancements = aAdvancements;
+ }
+
+ geometry::RealRectangle2D SAL_CALL TextLayout::queryTextBounds( )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ ENSURE_OR_THROW( mpFont,
+ "TextLayout::queryTextBounds(): invalid font" );
+
+ // fake text bounds by either taking the advancement values,
+ // or assuming square glyph boxes (width similar to height)
+ const rendering::FontRequest& rFontRequest( mpFont->getFontRequest() );
+ const double nFontSize( std::max( rFontRequest.CellSize,
+ rFontRequest.ReferenceAdvancement ) );
+ if( maLogicalAdvancements.hasElements() )
+ {
+ return geometry::RealRectangle2D( 0, -nFontSize/2,
+ maLogicalAdvancements[ maLogicalAdvancements.getLength()-1 ],
+ nFontSize/2 );
+ }
+ else
+ {
+ return geometry::RealRectangle2D( 0, -nFontSize/2,
+ nFontSize * maText.Length,
+ nFontSize/2 );
+ }
+ }
+
+ 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;
+ }
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/opengl/ogl_textlayout.hxx b/canvas/source/opengl/ogl_textlayout.hxx
new file mode 100644
index 000000000..ce7b19589
--- /dev/null
+++ b/canvas/source/opengl/ogl_textlayout.hxx
@@ -0,0 +1,70 @@
+/* -*- 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/.
+ */
+
+#pragma once
+
+#include <cppuhelper/compbase.hxx>
+#include <cppuhelper/basemutex.hxx>
+
+#include <com/sun/star/rendering/XTextLayout.hpp>
+
+#include "ogl_canvasfont.hxx"
+
+
+/* Definition of TextLayout class */
+
+namespace oglcanvas
+{
+ typedef ::cppu::WeakComponentImplHelper< css::rendering::XTextLayout > TextLayoutBaseT;
+
+ class TextLayout : public ::cppu::BaseMutex,
+ public TextLayoutBaseT
+ {
+ public:
+ TextLayout( css::rendering::StringContext aText,
+ sal_Int8 nDirection,
+ sal_Int64 nRandomSeed,
+ 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::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;
+
+ private:
+ css::rendering::StringContext maText;
+ css::uno::Sequence< double > maLogicalAdvancements;
+ CanvasFont::ImplRef mpFont;
+ sal_Int8 mnTextDirection;
+ };
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/opengl/ogl_texturecache.cxx b/canvas/source/opengl/ogl_texturecache.cxx
new file mode 100644
index 000000000..43fb7d8e2
--- /dev/null
+++ b/canvas/source/opengl/ogl_texturecache.cxx
@@ -0,0 +1,117 @@
+/* -*- 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/.
+ */
+
+#include <sal/config.h>
+
+#include <epoxy/gl.h>
+
+#include <com/sun/star/geometry/IntegerSize2D.hpp>
+
+#include "ogl_texturecache.hxx"
+
+
+using namespace ::com::sun::star;
+
+namespace oglcanvas
+{
+ TextureCache::TextureCache() :
+ maCache(101),
+ mnMissCount(0),
+ mnHitCount(0)
+ {}
+
+ TextureCache::~TextureCache()
+ {
+ flush();
+ }
+
+ void TextureCache::flush()
+ {
+ // un-bind any texture
+ glBindTexture(GL_TEXTURE_2D, 0);
+
+ // delete all cached textures
+ for( const auto& rCache : maCache )
+ {
+ glDeleteTextures( 1, &rCache.second.nTexture );
+ }
+
+ maCache.clear();
+ mnMissCount = 0;
+ mnHitCount = 0;
+ }
+
+ void TextureCache::prune()
+ {
+ // un-bind any texture
+ glBindTexture(GL_TEXTURE_2D, 0);
+
+ // delete already "old" textures, mark "new" entries "old"
+ const TextureCacheMapT::const_iterator aEnd = maCache.end();
+ for( auto aCurr = maCache.begin(); aCurr != aEnd; /* increment managed in loop */)
+ {
+ if( aCurr->second.bOld )
+ {
+ glDeleteTextures( 1, &aCurr->second.nTexture );
+ aCurr = maCache.erase( aCurr );
+ }
+ else
+ {
+ aCurr->second.bOld = true;
+ ++aCurr;
+ }
+ }
+
+ mnMissCount = 0;
+ mnHitCount = 0;
+ }
+
+ unsigned int TextureCache::getTexture( const geometry::IntegerSize2D& rPixelSize,
+ const sal_Int8* pPixel,
+ sal_uInt32 nPixelCrc32) const
+ {
+ unsigned int nTexture(0);
+
+ // texture already cached?
+ TextureCacheMapT::iterator aCacheEntry;
+ if( (aCacheEntry=maCache.find(nPixelCrc32)) == maCache.end() )
+ {
+ // nope, insert new entry
+ glGenTextures(1, &nTexture);
+ glBindTexture(GL_TEXTURE_2D, nTexture);
+
+ // TODO(E3): handle limited texture sizes -
+ // glGetIntegerv(GL_MAX_TEXTURE_SIZE)
+ glTexImage2D(GL_TEXTURE_2D,
+ 0,
+ 4,
+ rPixelSize.Width,
+ rPixelSize.Height,
+ 0,
+ GL_RGBA,
+ GL_UNSIGNED_INT_8_8_8_8_REV,
+ pPixel);
+
+ maCache[nPixelCrc32].nTexture = nTexture;
+ ++mnMissCount;
+
+ return nTexture;
+ }
+ else
+ {
+ nTexture = aCacheEntry->second.nTexture;
+ aCacheEntry->second.bOld = false;
+ ++mnHitCount;
+ }
+
+ return nTexture;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/opengl/ogl_texturecache.hxx b/canvas/source/opengl/ogl_texturecache.hxx
new file mode 100644
index 000000000..8d0a425ce
--- /dev/null
+++ b/canvas/source/opengl/ogl_texturecache.hxx
@@ -0,0 +1,60 @@
+/* -*- 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/.
+ */
+
+#pragma once
+
+#include <sal/types.h>
+#include <unordered_map>
+
+namespace com::sun::star::geometry { struct IntegerSize2D; }
+
+
+namespace oglcanvas
+{
+ class TextureCache
+ {
+ public:
+ TextureCache();
+ ~TextureCache();
+
+ /// clear whole cache, reset statistic counters
+ void flush();
+
+ /** prune old entries from cache
+
+ Every time this method is called, all cache entries are set
+ to "old". If subsequently not used by getTexture(),
+ they'll be entitled for expunge on the next prune()
+ call. Resets statistic counters.
+ */
+ void prune();
+
+ /// Statistics
+ size_t getCacheSize() const { return maCache.size(); };
+ sal_uInt32 getCacheMissCount() const { return mnMissCount; }
+ sal_uInt32 getCacheHitCount() const { return mnHitCount; }
+
+ unsigned int getTexture( const css::geometry::IntegerSize2D& rPixelSize,
+ const sal_Int8* pPixel,
+ sal_uInt32 nPixelCrc32) const;
+ private:
+ struct CacheEntry
+ {
+ CacheEntry() : nTexture(0), bOld(false) {}
+ unsigned int nTexture;
+ bool bOld;
+ };
+ typedef std::unordered_map<sal_uInt32,CacheEntry> TextureCacheMapT;
+ mutable TextureCacheMapT maCache;
+ mutable sal_uInt32 mnMissCount;
+ mutable sal_uInt32 mnHitCount;
+ };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/opengl/ogl_tools.hxx b/canvas/source/opengl/ogl_tools.hxx
new file mode 100644
index 000000000..8f42f734e
--- /dev/null
+++ b/canvas/source/opengl/ogl_tools.hxx
@@ -0,0 +1,27 @@
+/* -*- 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/.
+ */
+
+#pragma once
+
+#include <sal/config.h>
+#include <epoxy/gl.h>
+
+namespace oglcanvas
+{
+ struct TransformationPreserver
+ {
+ TransformationPreserver()
+ { glPushMatrix(); }
+
+ ~TransformationPreserver()
+ { glPopMatrix(); }
+ };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/opengl/oglcanvas.component b/canvas/source/opengl/oglcanvas.component
new file mode 100644
index 000000000..f6c9af43f
--- /dev/null
+++ b/canvas/source/opengl/oglcanvas.component
@@ -0,0 +1,17 @@
+<?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/.
+ *
+-->
+
+<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.OGL"
+ constructor="com_sun_star_comp_rendering_SpriteCanvas_OGL_get_implementation">
+ <service name="com.sun.star.rendering.SpriteCanvas.OGL"/>
+ </implementation>
+</component>
diff --git a/canvas/source/simplecanvas/simplecanvas.component b/canvas/source/simplecanvas/simplecanvas.component
new file mode 100644
index 000000000..9be11ad47
--- /dev/null
+++ b/canvas/source/simplecanvas/simplecanvas.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.SimpleCanvas"
+ constructor="com_sun_star_comp_rendering_SimpleCanvas">
+ <service name="com.sun.star.rendering.SimpleCanvas"/>
+ </implementation>
+</component>
diff --git a/canvas/source/simplecanvas/simplecanvasimpl.cxx b/canvas/source/simplecanvas/simplecanvasimpl.cxx
new file mode 100644
index 000000000..18d2c0e98
--- /dev/null
+++ b/canvas/source/simplecanvas/simplecanvasimpl.cxx
@@ -0,0 +1,369 @@
+/* -*- 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/matrix/b2dhommatrix.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/lang/XServiceName.hpp>
+#include <com/sun/star/rendering/CompositeOperation.hpp>
+#include <com/sun/star/rendering/PanoseLetterForm.hpp>
+#include <com/sun/star/rendering/PanoseWeight.hpp>
+#include <com/sun/star/rendering/XSimpleCanvas.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <cppuhelper/basemutex.hxx>
+#include <cppuhelper/compbase.hxx>
+#include <o3tl/lazy_update.hxx>
+
+#include <canvas/canvastools.hxx>
+
+
+#include <functional>
+
+using namespace ::com::sun::star;
+using namespace canvas;
+
+namespace
+{
+ uno::Sequence< double > color2Sequence( sal_Int32 nColor )
+ {
+ // TODO(F3): Color management
+ uno::Sequence< double > aRes{
+ static_cast<sal_uInt8>( (nColor&0xFF000000U) >> 24U ) / 255.0,
+ static_cast<sal_uInt8>( (nColor&0x00FF0000U) >> 16U ) / 255.0,
+ static_cast<sal_uInt8>( (nColor&0x0000FF00U) >> 8U ) / 255.0,
+ static_cast<sal_uInt8>( (nColor&0x000000FFU) ) / 255.0
+ };
+ return aRes;
+ }
+
+ uno::Reference< rendering::XPolyPolygon2D > rect2Poly( uno::Reference<rendering::XGraphicDevice> const& xDevice,
+ geometry::RealRectangle2D const& rRect )
+ {
+ uno::Sequence< geometry::RealPoint2D > rectSequence{
+ geometry::RealPoint2D( rRect.X1, rRect.Y1 ),
+ geometry::RealPoint2D( rRect.X2, rRect.Y1 ),
+ geometry::RealPoint2D( rRect.X2, rRect.Y2 ),
+ geometry::RealPoint2D( rRect.X1, rRect.Y2 )
+ };
+ uno::Sequence< uno::Sequence< geometry::RealPoint2D > > sequenceSequence{ rectSequence };
+ uno::Reference< rendering::XPolyPolygon2D > xRes =
+ xDevice->createCompatibleLinePolyPolygon( sequenceSequence );
+ if( xRes.is() )
+ xRes->setClosed( 0, true );
+ return xRes;
+ }
+
+ struct SimpleRenderState
+ {
+ o3tl::LazyUpdate<sal_Int32,
+ uno::Sequence<double>,
+ decltype(&color2Sequence)> m_aPenColor;
+ o3tl::LazyUpdate<sal_Int32,
+ uno::Sequence<double>,
+ decltype(&color2Sequence)> m_aFillColor;
+ o3tl::LazyUpdate<geometry::RealRectangle2D,
+ uno::Reference< rendering::XPolyPolygon2D >,
+ std::function<uno::Reference<rendering::XPolyPolygon2D> (geometry::RealRectangle2D)> > m_aRectClip;
+ geometry::AffineMatrix2D m_aTransformation;
+
+ explicit SimpleRenderState( uno::Reference<rendering::XGraphicDevice> const& xDevice ) :
+ m_aPenColor( &color2Sequence),
+ m_aFillColor( &color2Sequence ),
+ m_aRectClip( [&xDevice](geometry::RealRectangle2D const& rRect) { return rect2Poly(xDevice, rRect); } )
+ {
+ tools::setIdentityAffineMatrix2D( m_aTransformation );
+ }
+ };
+
+
+ typedef ::cppu::WeakComponentImplHelper< css::rendering::XSimpleCanvas,
+ css::lang::XServiceName > SimpleCanvasBase;
+
+ class SimpleCanvasImpl : private cppu::BaseMutex,
+ public SimpleCanvasBase
+ {
+ private:
+ bool isStrokingEnabled() const
+ {
+ return maRenderState.m_aPenColor.getInValue() % 0x100 != 0;
+ }
+
+ rendering::RenderState createStrokingRenderState() const
+ {
+ return rendering::RenderState(maRenderState.m_aTransformation,
+ *maRenderState.m_aRectClip,
+ *maRenderState.m_aPenColor,
+ rendering::CompositeOperation::OVER);
+ }
+
+ bool isFillingEnabled() const
+ {
+ return maRenderState.m_aFillColor.getInValue() % 0x100 != 0;
+ }
+
+ rendering::RenderState createFillingRenderState() const
+ {
+ return rendering::RenderState(maRenderState.m_aTransformation,
+ *maRenderState.m_aRectClip,
+ *maRenderState.m_aFillColor,
+ rendering::CompositeOperation::OVER);
+ }
+
+ static uno::Reference<rendering::XCanvas> grabCanvas( uno::Sequence<uno::Any> const& rArgs )
+ {
+ uno::Reference<rendering::XCanvas> xRet;
+
+ // can't do much without an XCanvas, can't we?
+ if( !rArgs.hasElements() )
+ throw lang::IllegalArgumentException();
+
+ xRet.set( rArgs[0], uno::UNO_QUERY );
+
+ // can't do much without an XCanvas, can't we?
+ if( !xRet.is() )
+ throw lang::IllegalArgumentException();
+
+ return xRet;
+ }
+
+ public:
+ SimpleCanvasImpl( const uno::Sequence< uno::Any >& aArguments,
+ const uno::Reference< uno::XComponentContext >& ) :
+ SimpleCanvasBase( m_aMutex ),
+ mxCanvas( grabCanvas(aArguments) ),
+ maFont([this](rendering::FontRequest const& rFontRequest) {
+ return mxCanvas->createFont(rFontRequest,
+ uno::Sequence< beans::PropertyValue >(),
+ geometry::Matrix2D()); } ),
+ maRenderState( mxCanvas->getDevice() )
+ {
+ tools::initViewState(maViewState);
+ }
+
+
+ private:
+ // Ifc XServiceName
+ virtual OUString SAL_CALL getServiceName( ) override
+ {
+ return "com.sun.star.rendering.SimpleCanvas";
+ }
+
+ // Ifc XSimpleCanvas
+ virtual void SAL_CALL selectFont( const OUString& sFontName,
+ double size,
+ sal_Bool bold,
+ sal_Bool italic ) override
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ maFont->FontDescription.FamilyName = sFontName;
+ maFont->CellSize = size;
+ maFont->FontDescription.FontDescription.Weight =
+ bold ? rendering::PanoseWeight::BOLD : rendering::PanoseWeight::MEDIUM;
+ maFont->FontDescription.FontDescription.Letterform =
+ italic ? rendering::PanoseLetterForm::OBLIQUE_CONTACT : rendering::PanoseLetterForm::ANYTHING;
+ }
+
+ virtual void SAL_CALL setPenColor( ::sal_Int32 nsRgbaColor ) override
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ *(maRenderState.m_aPenColor) = nsRgbaColor;
+ }
+
+ virtual void SAL_CALL setFillColor( ::sal_Int32 nsRgbaColor ) override
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ *(maRenderState.m_aFillColor) = nsRgbaColor;
+ }
+
+ virtual void SAL_CALL setRectClip( const geometry::RealRectangle2D& aRect ) override
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ *(maRenderState.m_aRectClip) = aRect;
+ }
+
+ virtual void SAL_CALL setTransformation( const geometry::AffineMatrix2D& aTransform ) override
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ maRenderState.m_aTransformation = aTransform;
+ }
+
+ virtual void SAL_CALL drawPixel( const geometry::RealPoint2D& aPoint ) override
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ mxCanvas->drawPoint(aPoint,
+ maViewState,
+ createFillingRenderState());
+ }
+
+ virtual void SAL_CALL drawLine( const geometry::RealPoint2D& aStartPoint,
+ const geometry::RealPoint2D& aEndPoint ) override
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ mxCanvas->drawLine(aStartPoint,
+ aEndPoint,
+ maViewState,
+ createStrokingRenderState());
+ }
+
+ virtual void SAL_CALL drawRect( const geometry::RealRectangle2D& aRect ) override
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ uno::Reference< rendering::XPolyPolygon2D > xPoly(
+ rect2Poly( mxCanvas->getDevice(),
+ aRect));
+
+ if( isFillingEnabled() )
+ mxCanvas->drawPolyPolygon(xPoly,
+ maViewState,
+ createFillingRenderState());
+ if( isStrokingEnabled() )
+ mxCanvas->drawPolyPolygon(xPoly,
+ maViewState,
+ createStrokingRenderState());
+ }
+
+ virtual void SAL_CALL drawPolyPolygon( const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon ) override
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ if( isFillingEnabled() )
+ mxCanvas->drawPolyPolygon(xPolyPolygon,
+ maViewState,
+ createFillingRenderState());
+ if( isStrokingEnabled() )
+ mxCanvas->drawPolyPolygon(xPolyPolygon,
+ maViewState,
+ createStrokingRenderState());
+ }
+
+ virtual void SAL_CALL drawText( const rendering::StringContext& aText,
+ const geometry::RealPoint2D& aOutPos,
+ ::sal_Int8 nTextDirection ) override
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ const basegfx::B2DHomMatrix offsetTransform(basegfx::utils::createTranslateB2DHomMatrix(aOutPos.X,aOutPos.Y));
+ rendering::RenderState aRenderState( createStrokingRenderState() );
+ tools::appendToRenderState(aRenderState, offsetTransform);
+
+ mxCanvas->drawText(aText,
+ maFont.getOutValue(),
+ maViewState,
+ aRenderState,
+ nTextDirection);
+ }
+
+ virtual void SAL_CALL drawBitmap( const uno::Reference< rendering::XBitmap >& xBitmap,
+ const geometry::RealPoint2D& aLeftTop ) override
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ const basegfx::B2DHomMatrix offsetTransform(basegfx::utils::createTranslateB2DHomMatrix(aLeftTop.X,aLeftTop.Y));
+ rendering::RenderState aRenderState( createStrokingRenderState() );
+ tools::appendToRenderState(aRenderState, offsetTransform);
+
+ mxCanvas->drawBitmap(xBitmap,maViewState,aRenderState);
+ }
+
+ virtual uno::Reference< rendering::XGraphicDevice > SAL_CALL getDevice( ) override
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ return mxCanvas->getDevice();
+ }
+
+ virtual uno::Reference< rendering::XCanvas > SAL_CALL getCanvas( ) override
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ return mxCanvas;
+ }
+
+ virtual rendering::FontMetrics SAL_CALL getFontMetrics( ) override
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ return maFont.getOutValue()->getFontMetrics();
+ }
+
+ virtual uno::Reference< rendering::XCanvasFont > SAL_CALL getCurrentFont( ) override
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ return maFont.getOutValue();
+ }
+
+ virtual ::sal_Int32 SAL_CALL getCurrentPenColor( ) override
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ return maRenderState.m_aPenColor.getInValue();
+ }
+
+ virtual ::sal_Int32 SAL_CALL getCurrentFillColor( ) override
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ return maRenderState.m_aFillColor.getInValue();
+ }
+
+ virtual geometry::RealRectangle2D SAL_CALL getCurrentClipRect( ) override
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ return maRenderState.m_aRectClip.getInValue();
+ }
+
+ virtual geometry::AffineMatrix2D SAL_CALL getCurrentTransformation( ) override
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ return maRenderState.m_aTransformation;
+ }
+
+ virtual rendering::ViewState SAL_CALL getCurrentViewState( ) override
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ return maViewState;
+ }
+
+ virtual rendering::RenderState SAL_CALL getCurrentRenderState( sal_Bool bUseFillColor ) override
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if( bUseFillColor )
+ return createFillingRenderState();
+ else
+ return createStrokingRenderState();
+ }
+
+
+ typedef o3tl::LazyUpdate<
+ rendering::FontRequest,
+ uno::Reference< rendering::XCanvasFont >,
+ std::function<uno::Reference<rendering::XCanvasFont> (rendering::FontRequest)> > SimpleFont;
+
+ uno::Reference<rendering::XCanvas> mxCanvas;
+ SimpleFont maFont;
+ rendering::ViewState maViewState;
+ SimpleRenderState maRenderState;
+ };
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_rendering_SimpleCanvas(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& args)
+{
+ return cppu::acquire(new SimpleCanvasImpl(args, context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/tools/cachedprimitivebase.cxx b/canvas/source/tools/cachedprimitivebase.cxx
new file mode 100644
index 000000000..652e9c900
--- /dev/null
+++ b/canvas/source/tools/cachedprimitivebase.cxx
@@ -0,0 +1,96 @@
+/* -*- 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/matrix/b2dhommatrix.hxx>
+#include <basegfx/utils/canvastools.hxx>
+#include <com/sun/star/rendering/RepaintResult.hpp>
+#include <cppuhelper/supportsservice.hxx>
+
+#include <base/cachedprimitivebase.hxx>
+#include <utility>
+
+
+using namespace ::com::sun::star;
+
+namespace canvas
+{
+ CachedPrimitiveBase::CachedPrimitiveBase( rendering::ViewState aUsedViewState,
+ uno::Reference< rendering::XCanvas > xTarget ) :
+ CachedPrimitiveBase_Base( m_aMutex ),
+ maUsedViewState(std::move( aUsedViewState )),
+ mxTarget(std::move( xTarget ))
+ {
+ }
+
+ CachedPrimitiveBase::~CachedPrimitiveBase()
+ {
+ }
+
+ void SAL_CALL CachedPrimitiveBase::disposing()
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ maUsedViewState.Clip.clear();
+ mxTarget.clear();
+ }
+
+ sal_Int8 SAL_CALL CachedPrimitiveBase::redraw( const rendering::ViewState& aState )
+ {
+ ::basegfx::B2DHomMatrix aUsedTransformation;
+ ::basegfx::B2DHomMatrix aNewTransformation;
+
+ ::basegfx::unotools::homMatrixFromAffineMatrix( aUsedTransformation,
+ maUsedViewState.AffineTransform );
+ ::basegfx::unotools::homMatrixFromAffineMatrix( aNewTransformation,
+ aState.AffineTransform );
+
+ const bool bSameViewTransforms( aUsedTransformation == aNewTransformation );
+
+ if( !bSameViewTransforms )
+ {
+ // differing transformations, don't try to draft the
+ // output, just plain fail here.
+ return rendering::RepaintResult::FAILED;
+ }
+
+ return doRedraw( aState,
+ maUsedViewState,
+ mxTarget,
+ bSameViewTransforms );
+ }
+
+ OUString SAL_CALL CachedPrimitiveBase::getImplementationName( )
+ {
+ return "canvas::CachedPrimitiveBase";
+ }
+
+ sal_Bool SAL_CALL CachedPrimitiveBase::supportsService( const OUString& ServiceName )
+ {
+ return cppu::supportsService(this, ServiceName);
+ }
+
+ uno::Sequence< OUString > SAL_CALL CachedPrimitiveBase::getSupportedServiceNames( )
+ {
+ return { "com.sun.star.rendering.CachedBitmap" };
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/tools/canvascustomspritehelper.cxx b/canvas/source/tools/canvascustomspritehelper.cxx
new file mode 100644
index 000000000..c56980bf2
--- /dev/null
+++ b/canvas/source/tools/canvascustomspritehelper.cxx
@@ -0,0 +1,441 @@
+/* -*- 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 <com/sun/star/rendering/XPolyPolygon2D.hpp>
+#include <com/sun/star/geometry/RealSize2D.hpp>
+#include <com/sun/star/rendering/XBitmap.hpp>
+#include <com/sun/star/geometry/IntegerSize2D.hpp>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/point/b2dpoint.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <basegfx/utils/canvastools.hxx>
+#include <basegfx/vector/b2dsize.hxx>
+#include <rtl/math.hxx>
+#include <tools/diagnose_ex.h>
+
+#include <base/canvascustomspritehelper.hxx>
+#include <canvas/canvastools.hxx>
+
+using namespace ::com::sun::star;
+
+
+namespace canvas
+{
+ bool CanvasCustomSpriteHelper::updateClipState( const Sprite::Reference& rSprite )
+ {
+ if( !mxClipPoly.is() )
+ {
+ // empty clip polygon -> everything is visible now
+ maCurrClipBounds.reset();
+ mbIsCurrClipRectangle = true;
+ }
+ else
+ {
+ const sal_Int32 nNumClipPolygons( mxClipPoly->getNumberOfPolygons() );
+
+ // clip is not empty - determine actual update area
+ ::basegfx::B2DPolyPolygon aClipPath(
+ polyPolygonFromXPolyPolygon2D( mxClipPoly ) );
+
+ // apply sprite transformation also to clip!
+ aClipPath.transform( maTransform );
+
+ // clip which is about to be set, expressed as a
+ // b2drectangle
+ const ::basegfx::B2DRectangle& rClipBounds(
+ ::basegfx::utils::getRange( aClipPath ) );
+
+ const ::basegfx::B2DRectangle aBounds( 0.0, 0.0,
+ maSize.getX(),
+ maSize.getY() );
+
+ // rectangular area which is actually covered by the sprite.
+ // coordinates are relative to the sprite origin.
+ ::basegfx::B2DRectangle aSpriteRectPixel;
+ ::canvas::tools::calcTransformedRectBounds( aSpriteRectPixel,
+ aBounds,
+ maTransform );
+
+ // aClipBoundsA = new clip bound rect, intersected
+ // with sprite area
+ ::basegfx::B2DRectangle aClipBoundsA(rClipBounds);
+ aClipBoundsA.intersect( aSpriteRectPixel );
+
+ if( nNumClipPolygons != 1 )
+ {
+ // clip cannot be a single rectangle -> cannot
+ // optimize update
+ mbIsCurrClipRectangle = false;
+ maCurrClipBounds = aClipBoundsA;
+ }
+ else
+ {
+ // new clip could be a single rectangle - check
+ // that now:
+ const bool bNewClipIsRect(
+ ::basegfx::utils::isRectangle( aClipPath.getB2DPolygon(0) ) );
+
+ // both new and old clip are truly rectangles
+ // - can now take the optimized path
+ const bool bUseOptimizedUpdate( bNewClipIsRect &&
+ mbIsCurrClipRectangle );
+
+ const ::basegfx::B2DRectangle aOldBounds( maCurrClipBounds );
+
+ // store new current clip type
+ maCurrClipBounds = aClipBoundsA;
+ mbIsCurrClipRectangle = bNewClipIsRect;
+
+ if( mbActive &&
+ bUseOptimizedUpdate )
+ {
+ // aClipBoundsB = maCurrClipBounds, i.e. last
+ // clip, intersected with sprite area
+ std::vector< ::basegfx::B2DRectangle > aClipDifferences;
+
+ // get all rectangles covered by exactly one
+ // of the polygons (aka XOR)
+ ::basegfx::computeSetDifference(aClipDifferences,
+ aClipBoundsA,
+ aOldBounds);
+
+ // aClipDifferences now contains the final
+ // update areas, coordinates are still relative
+ // to the sprite origin. before submitting
+ // this area to 'updateSprite()' we need to
+ // translate this area to the final position,
+ // coordinates need to be relative to the
+ // spritecanvas.
+ for( const auto& rClipDiff : aClipDifferences )
+ {
+ mpSpriteCanvas->updateSprite(
+ rSprite,
+ maPosition,
+ ::basegfx::B2DRectangle(
+ maPosition + rClipDiff.getMinimum(),
+ maPosition + rClipDiff.getMaximum() ) );
+ }
+
+ // update calls all done
+ return true;
+ }
+ }
+ }
+
+ // caller needs to perform update calls
+ return false;
+ }
+
+ CanvasCustomSpriteHelper::CanvasCustomSpriteHelper() :
+ mfPriority(0.0),
+ mfAlpha(0.0),
+ mbActive(false),
+ mbIsCurrClipRectangle(true),
+ mbIsContentFullyOpaque( false ),
+ mbTransformDirty( true )
+ {
+ }
+
+ void CanvasCustomSpriteHelper::init( const geometry::RealSize2D& rSpriteSize,
+ const SpriteSurface::Reference& rOwningSpriteCanvas )
+ {
+ ENSURE_OR_THROW( rOwningSpriteCanvas,
+ "CanvasCustomSpriteHelper::init(): Invalid owning sprite canvas" );
+
+ mpSpriteCanvas = rOwningSpriteCanvas;
+ maSize.setX( std::max( 1.0,
+ ceil( rSpriteSize.Width ) ) ); // round up to nearest int,
+ // enforce sprite to have at
+ // least (1,1) pixel size
+ maSize.setY( std::max( 1.0,
+ ceil( rSpriteSize.Height ) ) );
+ }
+
+ void CanvasCustomSpriteHelper::disposing()
+ {
+ mpSpriteCanvas.clear();
+ }
+
+ void CanvasCustomSpriteHelper::clearingContent( const Sprite::Reference& /*rSprite*/ )
+ {
+ // about to clear content to fully transparent
+ mbIsContentFullyOpaque = false;
+ }
+
+ void CanvasCustomSpriteHelper::checkDrawBitmap( const Sprite::Reference& rSprite,
+ const uno::Reference< rendering::XBitmap >& xBitmap,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState )
+ {
+ // check whether bitmap is non-alpha, and whether its
+ // transformed size covers the whole sprite.
+ if( xBitmap->hasAlpha() )
+ return;
+
+ const geometry::IntegerSize2D& rInputSize(
+ xBitmap->getSize() );
+ const ::basegfx::B2DSize& rOurSize(
+ rSprite->getSizePixel() );
+
+ ::basegfx::B2DHomMatrix aTransform;
+ if( tools::isInside(
+ ::basegfx::B2DRectangle( 0.0,0.0,
+ rOurSize.getX(),
+ rOurSize.getY() ),
+ ::basegfx::B2DRectangle( 0.0,0.0,
+ rInputSize.Width,
+ rInputSize.Height ),
+ ::canvas::tools::mergeViewAndRenderTransform(aTransform,
+ viewState,
+ renderState) ) )
+ {
+ // bitmap is opaque and will fully cover the sprite,
+ // set flag appropriately
+ mbIsContentFullyOpaque = true;
+ }
+ }
+
+ void CanvasCustomSpriteHelper::setAlpha( const Sprite::Reference& rSprite,
+ double alpha )
+ {
+ if( !mpSpriteCanvas )
+ return; // we're disposed
+
+ if( alpha != mfAlpha )
+ {
+ mfAlpha = alpha;
+
+ if( mbActive )
+ {
+ mpSpriteCanvas->updateSprite( rSprite,
+ maPosition,
+ getUpdateArea() );
+ }
+ }
+ }
+
+ void CanvasCustomSpriteHelper::move( const Sprite::Reference& rSprite,
+ const geometry::RealPoint2D& aNewPos,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState )
+ {
+ if( !mpSpriteCanvas )
+ return; // we're disposed
+
+ ::basegfx::B2DHomMatrix aTransform;
+ ::canvas::tools::mergeViewAndRenderTransform(aTransform,
+ viewState,
+ renderState);
+
+ // convert position to device pixel
+ ::basegfx::B2DPoint aPoint(
+ ::basegfx::unotools::b2DPointFromRealPoint2D(aNewPos) );
+ aPoint *= aTransform;
+
+ if( aPoint == maPosition )
+ return;
+
+ const ::basegfx::B2DRectangle& rBounds
+ = getUpdateArea( ::basegfx::B2DRectangle( 0.0, 0.0,
+ maSize.getX(),
+ maSize.getY() ) );
+
+ if( mbActive )
+ {
+ mpSpriteCanvas->moveSprite( rSprite,
+ rBounds.getMinimum(),
+ rBounds.getMinimum() - maPosition + aPoint,
+ rBounds.getRange() );
+ }
+
+ maPosition = aPoint;
+ }
+
+ void CanvasCustomSpriteHelper::transform( const Sprite::Reference& rSprite,
+ const geometry::AffineMatrix2D& aTransformation )
+ {
+ ::basegfx::B2DHomMatrix aMatrix;
+ ::basegfx::unotools::homMatrixFromAffineMatrix(aMatrix,
+ aTransformation);
+
+ if( maTransform == aMatrix )
+ return;
+
+ // retrieve bounds before and after transformation change.
+ const ::basegfx::B2DRectangle& rPrevBounds( getUpdateArea() );
+
+ maTransform = aMatrix;
+
+ if( !updateClipState( rSprite ) &&
+ mbActive )
+ {
+ mpSpriteCanvas->updateSprite( rSprite,
+ maPosition,
+ rPrevBounds );
+ mpSpriteCanvas->updateSprite( rSprite,
+ maPosition,
+ getUpdateArea() );
+ }
+
+ mbTransformDirty = true;
+ }
+
+ void CanvasCustomSpriteHelper::clip( const Sprite::Reference& rSprite,
+ const uno::Reference< rendering::XPolyPolygon2D >& xClip )
+ {
+ // NULL xClip explicitly allowed here (to clear clipping)
+
+ // retrieve bounds before and after clip change.
+ const ::basegfx::B2DRectangle& rPrevBounds( getUpdateArea() );
+
+ mxClipPoly = xClip;
+
+ if( !updateClipState( rSprite ) &&
+ mbActive )
+ {
+ mpSpriteCanvas->updateSprite( rSprite,
+ maPosition,
+ rPrevBounds );
+ mpSpriteCanvas->updateSprite( rSprite,
+ maPosition,
+ getUpdateArea() );
+ }
+ }
+
+ void CanvasCustomSpriteHelper::setPriority( const Sprite::Reference& rSprite,
+ double nPriority )
+ {
+ if( !mpSpriteCanvas )
+ return; // we're disposed
+
+ if( nPriority != mfPriority )
+ {
+ mfPriority = nPriority;
+
+ if( mbActive )
+ {
+ mpSpriteCanvas->updateSprite( rSprite,
+ maPosition,
+ getUpdateArea() );
+ }
+ }
+ }
+
+ void CanvasCustomSpriteHelper::show( const Sprite::Reference& rSprite )
+ {
+ if( !mpSpriteCanvas )
+ return; // we're disposed
+
+ if( mbActive )
+ return;
+
+ mpSpriteCanvas->showSprite( rSprite );
+ mbActive = true;
+
+ // TODO(P1): if clip is the NULL clip (nothing visible),
+ // also save us the update call.
+
+ if( mfAlpha != 0.0 )
+ {
+ mpSpriteCanvas->updateSprite( rSprite,
+ maPosition,
+ getUpdateArea() );
+ }
+ }
+
+ void CanvasCustomSpriteHelper::hide( const Sprite::Reference& rSprite )
+ {
+ if( !mpSpriteCanvas )
+ return; // we're disposed
+
+ if( !mbActive )
+ return;
+
+ mpSpriteCanvas->hideSprite( rSprite );
+ mbActive = false;
+
+ // TODO(P1): if clip is the NULL clip (nothing visible),
+ // also save us the update call.
+
+ if( mfAlpha != 0.0 )
+ {
+ mpSpriteCanvas->updateSprite( rSprite,
+ maPosition,
+ getUpdateArea() );
+ }
+ }
+
+ bool CanvasCustomSpriteHelper::isAreaUpdateOpaque( const ::basegfx::B2DRange& rUpdateArea ) const
+ {
+ if( !mbIsCurrClipRectangle ||
+ !mbIsContentFullyOpaque ||
+ !::rtl::math::approxEqual(mfAlpha, 1.0) )
+ {
+ // sprite either transparent, or clip rect does not
+ // represent exact bounds -> update might not be fully
+ // opaque
+ return false;
+ }
+ else
+ {
+ // make sure sprite rect fully covers update area -
+ // although the update area originates from the sprite,
+ // it's by no means guaranteed that it's limited to this
+ // sprite's update area - after all, other sprites might
+ // have been merged, or this sprite is moving.
+ return getUpdateArea().isInside( rUpdateArea );
+ }
+ }
+
+ ::basegfx::B2DRange CanvasCustomSpriteHelper::getUpdateArea( const ::basegfx::B2DRange& rBounds ) const
+ {
+ // Internal! Only call with locked object mutex!
+ ::basegfx::B2DHomMatrix aTransform( maTransform );
+ aTransform.translate( maPosition.getX(),
+ maPosition.getY() );
+
+ // transform bounds at origin, as the sprite transformation is
+ // formulated that way
+ ::basegfx::B2DRectangle aTransformedBounds;
+ return ::canvas::tools::calcTransformedRectBounds( aTransformedBounds,
+ rBounds,
+ aTransform );
+ }
+
+ ::basegfx::B2DRange CanvasCustomSpriteHelper::getUpdateArea() const
+ {
+ // Internal! Only call with locked object mutex!
+
+ // return effective sprite rect, i.e. take active clip into
+ // account
+ if( maCurrClipBounds.isEmpty() )
+ return getUpdateArea( ::basegfx::B2DRectangle( 0.0, 0.0,
+ maSize.getX(),
+ maSize.getY() ) );
+ else
+ return ::basegfx::B2DRectangle(
+ maPosition + maCurrClipBounds.getMinimum(),
+ maPosition + maCurrClipBounds.getMaximum() );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/tools/canvastools.cxx b/canvas/source/tools/canvastools.cxx
new file mode 100644
index 000000000..048a99407
--- /dev/null
+++ b/canvas/source/tools/canvastools.cxx
@@ -0,0 +1,1308 @@
+/* -*- 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 <limits>
+
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <basegfx/numeric/ftools.hxx>
+#include <basegfx/point/b2dpoint.hxx>
+#include <basegfx/point/b2ipoint.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/range/b2drange.hxx>
+#include <basegfx/range/b2drectangle.hxx>
+#include <basegfx/range/b2irange.hxx>
+#include <basegfx/utils/canvastools.hxx>
+#include <basegfx/vector/b2ivector.hxx>
+#include <com/sun/star/awt/Rectangle.hpp>
+#include <com/sun/star/awt/XWindow2.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/geometry/AffineMatrix2D.hpp>
+#include <com/sun/star/geometry/Matrix2D.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/rendering/ColorComponentTag.hpp>
+#include <com/sun/star/rendering/ColorSpaceType.hpp>
+#include <com/sun/star/rendering/CompositeOperation.hpp>
+#include <com/sun/star/rendering/IntegerBitmapLayout.hpp>
+#include <com/sun/star/rendering/RenderState.hpp>
+#include <com/sun/star/rendering/RenderingIntent.hpp>
+#include <com/sun/star/rendering/ViewState.hpp>
+#include <com/sun/star/rendering/XCanvas.hpp>
+#include <com/sun/star/rendering/XColorSpace.hpp>
+#include <com/sun/star/rendering/XIntegerBitmapColorSpace.hpp>
+#include <com/sun/star/util/Endianness.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <sal/log.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <tools/diagnose_ex.h>
+#include <vcl/canvastools.hxx>
+#include <vcl/window.hxx>
+
+#include <canvas/canvastools.hxx>
+
+
+using namespace ::com::sun::star;
+
+namespace canvas::tools
+{
+ geometry::RealSize2D createInfiniteSize2D()
+ {
+ return geometry::RealSize2D(
+ std::numeric_limits<double>::infinity(),
+ std::numeric_limits<double>::infinity() );
+ }
+
+ rendering::RenderState& initRenderState( rendering::RenderState& renderState )
+ {
+ // setup identity transform
+ setIdentityAffineMatrix2D( renderState.AffineTransform );
+ renderState.Clip.clear();
+ renderState.DeviceColor = uno::Sequence< double >();
+ renderState.CompositeOperation = rendering::CompositeOperation::OVER;
+
+ return renderState;
+ }
+
+ rendering::ViewState& initViewState( rendering::ViewState& viewState )
+ {
+ // setup identity transform
+ setIdentityAffineMatrix2D( viewState.AffineTransform );
+ viewState.Clip.clear();
+
+ return viewState;
+ }
+
+ ::basegfx::B2DHomMatrix& getViewStateTransform( ::basegfx::B2DHomMatrix& transform,
+ const rendering::ViewState& viewState )
+ {
+ return ::basegfx::unotools::homMatrixFromAffineMatrix( transform, viewState.AffineTransform );
+ }
+
+ rendering::ViewState& setViewStateTransform( rendering::ViewState& viewState,
+ const ::basegfx::B2DHomMatrix& transform )
+ {
+ ::basegfx::unotools::affineMatrixFromHomMatrix( viewState.AffineTransform, transform );
+
+ return viewState;
+ }
+
+ ::basegfx::B2DHomMatrix& getRenderStateTransform( ::basegfx::B2DHomMatrix& transform,
+ const rendering::RenderState& renderState )
+ {
+ return ::basegfx::unotools::homMatrixFromAffineMatrix( transform, renderState.AffineTransform );
+ }
+
+ rendering::RenderState& setRenderStateTransform( rendering::RenderState& renderState,
+ const ::basegfx::B2DHomMatrix& transform )
+ {
+ ::basegfx::unotools::affineMatrixFromHomMatrix( renderState.AffineTransform, transform );
+
+ return renderState;
+ }
+
+ rendering::RenderState& appendToRenderState( rendering::RenderState& renderState,
+ const ::basegfx::B2DHomMatrix& rTransform )
+ {
+ ::basegfx::B2DHomMatrix transform;
+
+ getRenderStateTransform( transform, renderState );
+ return setRenderStateTransform( renderState, transform * rTransform );
+ }
+
+ rendering::RenderState& prependToRenderState( rendering::RenderState& renderState,
+ const ::basegfx::B2DHomMatrix& rTransform )
+ {
+ ::basegfx::B2DHomMatrix transform;
+
+ getRenderStateTransform( transform, renderState );
+ return setRenderStateTransform( renderState, rTransform * transform );
+ }
+
+ ::basegfx::B2DHomMatrix& mergeViewAndRenderTransform( ::basegfx::B2DHomMatrix& combinedTransform,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState )
+ {
+ ::basegfx::B2DHomMatrix viewTransform;
+
+ ::basegfx::unotools::homMatrixFromAffineMatrix( combinedTransform, renderState.AffineTransform );
+ ::basegfx::unotools::homMatrixFromAffineMatrix( viewTransform, viewState.AffineTransform );
+
+ // this statement performs combinedTransform = viewTransform * combinedTransform
+ combinedTransform *= viewTransform;
+
+ return combinedTransform;
+ }
+
+ geometry::AffineMatrix2D& setIdentityAffineMatrix2D( geometry::AffineMatrix2D& matrix )
+ {
+ matrix.m00 = 1.0;
+ matrix.m01 = 0.0;
+ matrix.m02 = 0.0;
+ matrix.m10 = 0.0;
+ matrix.m11 = 1.0;
+ matrix.m12 = 0.0;
+
+ return matrix;
+ }
+
+ geometry::Matrix2D& setIdentityMatrix2D( geometry::Matrix2D& matrix )
+ {
+ matrix.m00 = 1.0;
+ matrix.m01 = 0.0;
+ matrix.m10 = 0.0;
+ matrix.m11 = 1.0;
+
+ return matrix;
+ }
+
+ namespace
+ {
+ class StandardColorSpace : public cppu::WeakImplHelper< css::rendering::XIntegerBitmapColorSpace >
+ {
+ private:
+ uno::Sequence< sal_Int8 > maComponentTags;
+ uno::Sequence< sal_Int32 > maBitCounts;
+
+ virtual ::sal_Int8 SAL_CALL getType( ) override
+ {
+ return rendering::ColorSpaceType::RGB;
+ }
+ virtual uno::Sequence< ::sal_Int8 > SAL_CALL getComponentTags( ) override
+ {
+ return maComponentTags;
+ }
+ virtual ::sal_Int8 SAL_CALL getRenderingIntent( ) override
+ {
+ return rendering::RenderingIntent::PERCEPTUAL;
+ }
+ virtual uno::Sequence< beans::PropertyValue > SAL_CALL getProperties( ) override
+ {
+ return uno::Sequence< beans::PropertyValue >();
+ }
+ virtual uno::Sequence< double > SAL_CALL convertColorSpace( const uno::Sequence< double >& deviceColor,
+ const uno::Reference< rendering::XColorSpace >& targetColorSpace ) override
+ {
+ // TODO(P3): if we know anything about target
+ // colorspace, this can be greatly sped up
+ uno::Sequence<rendering::ARGBColor> aIntermediate(
+ convertToARGB(deviceColor));
+ return targetColorSpace->convertFromARGB(aIntermediate);
+ }
+ virtual uno::Sequence< rendering::RGBColor > SAL_CALL convertToRGB( const uno::Sequence< double >& deviceColor ) override
+ {
+ const double* pIn( deviceColor.getConstArray() );
+ const std::size_t nLen( deviceColor.getLength() );
+ ENSURE_ARG_OR_THROW2(nLen%4==0,
+ "number of channels no multiple of 4",
+ static_cast<rendering::XColorSpace*>(this), 0);
+
+ uno::Sequence< rendering::RGBColor > aRes(nLen/4);
+ rendering::RGBColor* pOut( aRes.getArray() );
+ for( std::size_t i=0; i<nLen; i+=4 )
+ {
+ *pOut++ = rendering::RGBColor(pIn[0],pIn[1],pIn[2]);
+ pIn += 4;
+ }
+ return aRes;
+ }
+ virtual uno::Sequence< rendering::ARGBColor > SAL_CALL convertToARGB( const uno::Sequence< double >& deviceColor ) override
+ {
+ SAL_WARN_IF(!deviceColor.hasElements(), "canvas", "empty deviceColor argument");
+ const double* pIn( deviceColor.getConstArray() );
+ const std::size_t nLen( deviceColor.getLength() );
+ ENSURE_ARG_OR_THROW2(nLen%4==0,
+ "number of channels no multiple of 4",
+ static_cast<rendering::XColorSpace*>(this), 0);
+
+ uno::Sequence< rendering::ARGBColor > aRes(nLen/4);
+ rendering::ARGBColor* pOut( aRes.getArray() );
+ for( std::size_t i=0; i<nLen; i+=4 )
+ {
+ *pOut++ = rendering::ARGBColor(pIn[3],pIn[0],pIn[1],pIn[2]);
+ pIn += 4;
+ }
+ return aRes;
+ }
+ virtual uno::Sequence< rendering::ARGBColor > SAL_CALL convertToPARGB( const uno::Sequence< double >& deviceColor ) override
+ {
+ const double* pIn( deviceColor.getConstArray() );
+ const std::size_t nLen( deviceColor.getLength() );
+ ENSURE_ARG_OR_THROW2(nLen%4==0,
+ "number of channels no multiple of 4",
+ static_cast<rendering::XColorSpace*>(this), 0);
+
+ uno::Sequence< rendering::ARGBColor > aRes(nLen/4);
+ rendering::ARGBColor* pOut( aRes.getArray() );
+ for( std::size_t i=0; i<nLen; i+=4 )
+ {
+ *pOut++ = rendering::ARGBColor(pIn[3],pIn[3]*pIn[0],pIn[3]*pIn[1],pIn[3]*pIn[2]);
+ pIn += 4;
+ }
+ return aRes;
+ }
+ virtual uno::Sequence< double > SAL_CALL convertFromRGB( const uno::Sequence< rendering::RGBColor >& rgbColor ) override
+ {
+ const rendering::RGBColor* pIn( rgbColor.getConstArray() );
+ const std::size_t nLen( rgbColor.getLength() );
+
+ uno::Sequence< double > aRes(nLen*4);
+ double* pColors=aRes.getArray();
+ for( std::size_t i=0; i<nLen; ++i )
+ {
+ *pColors++ = pIn->Red;
+ *pColors++ = pIn->Green;
+ *pColors++ = pIn->Blue;
+ *pColors++ = 1.0;
+ ++pIn;
+ }
+ return aRes;
+ }
+ virtual uno::Sequence< double > SAL_CALL convertFromARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor ) override
+ {
+ const rendering::ARGBColor* pIn( rgbColor.getConstArray() );
+ const std::size_t nLen( rgbColor.getLength() );
+
+ uno::Sequence< double > aRes(nLen*4);
+ double* pColors=aRes.getArray();
+ for( std::size_t i=0; i<nLen; ++i )
+ {
+ *pColors++ = pIn->Red;
+ *pColors++ = pIn->Green;
+ *pColors++ = pIn->Blue;
+ *pColors++ = pIn->Alpha;
+ ++pIn;
+ }
+ return aRes;
+ }
+ virtual uno::Sequence< double > SAL_CALL convertFromPARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor ) override
+ {
+ const rendering::ARGBColor* pIn( rgbColor.getConstArray() );
+ const std::size_t nLen( rgbColor.getLength() );
+
+ uno::Sequence< double > aRes(nLen*4);
+ double* pColors=aRes.getArray();
+ for( std::size_t i=0; i<nLen; ++i )
+ {
+ *pColors++ = pIn->Red/pIn->Alpha;
+ *pColors++ = pIn->Green/pIn->Alpha;
+ *pColors++ = pIn->Blue/pIn->Alpha;
+ *pColors++ = pIn->Alpha;
+ ++pIn;
+ }
+ return aRes;
+ }
+
+ // XIntegerBitmapColorSpace
+ virtual ::sal_Int32 SAL_CALL getBitsPerPixel( ) override
+ {
+ return 32;
+ }
+ virtual uno::Sequence< ::sal_Int32 > SAL_CALL getComponentBitCounts( ) override
+ {
+ return maBitCounts;
+ }
+ virtual ::sal_Int8 SAL_CALL getEndianness( ) override
+ {
+ return util::Endianness::LITTLE;
+ }
+ virtual uno::Sequence<double> SAL_CALL convertFromIntegerColorSpace( const uno::Sequence< ::sal_Int8 >& deviceColor,
+ const uno::Reference< rendering::XColorSpace >& targetColorSpace ) override
+ {
+ if( dynamic_cast<StandardColorSpace*>(targetColorSpace.get()) )
+ {
+ const sal_Int8* pIn( deviceColor.getConstArray() );
+ const std::size_t nLen( deviceColor.getLength() );
+ ENSURE_ARG_OR_THROW2(nLen%4==0,
+ "number of channels no multiple of 4",
+ static_cast<rendering::XColorSpace*>(this), 0);
+
+ uno::Sequence<double> aRes(nLen);
+ double* pOut( aRes.getArray() );
+ for( std::size_t i=0; i<nLen; i+=4 )
+ {
+ *pOut++ = vcl::unotools::toDoubleColor(*pIn++);
+ *pOut++ = vcl::unotools::toDoubleColor(*pIn++);
+ *pOut++ = vcl::unotools::toDoubleColor(*pIn++);
+ *pOut++ = vcl::unotools::toDoubleColor(*pIn++);
+ }
+ return aRes;
+ }
+ else
+ {
+ // TODO(P3): if we know anything about target
+ // colorspace, this can be greatly sped up
+ uno::Sequence<rendering::ARGBColor> aIntermediate(
+ convertIntegerToARGB(deviceColor));
+ return targetColorSpace->convertFromARGB(aIntermediate);
+ }
+ }
+ virtual uno::Sequence< ::sal_Int8 > SAL_CALL convertToIntegerColorSpace( const uno::Sequence< ::sal_Int8 >& deviceColor,
+ const uno::Reference< rendering::XIntegerBitmapColorSpace >& targetColorSpace ) override
+ {
+ if( dynamic_cast<StandardColorSpace*>(targetColorSpace.get()) )
+ {
+ // it's us, so simply pass-through the data
+ return deviceColor;
+ }
+ else
+ {
+ // TODO(P3): if we know anything about target
+ // colorspace, this can be greatly sped up
+ uno::Sequence<rendering::ARGBColor> aIntermediate(
+ convertIntegerToARGB(deviceColor));
+ return targetColorSpace->convertIntegerFromARGB(aIntermediate);
+ }
+ }
+ virtual uno::Sequence< rendering::RGBColor > SAL_CALL convertIntegerToRGB( const uno::Sequence< ::sal_Int8 >& deviceColor ) override
+ {
+ const sal_Int8* pIn( deviceColor.getConstArray() );
+ const std::size_t nLen( deviceColor.getLength() );
+ ENSURE_ARG_OR_THROW2(nLen%4==0,
+ "number of channels no multiple of 4",
+ static_cast<rendering::XColorSpace*>(this), 0);
+
+ uno::Sequence< rendering::RGBColor > aRes(nLen/4);
+ rendering::RGBColor* pOut( aRes.getArray() );
+ for( std::size_t i=0; i<nLen; i+=4 )
+ {
+ *pOut++ = rendering::RGBColor(
+ vcl::unotools::toDoubleColor(pIn[0]),
+ vcl::unotools::toDoubleColor(pIn[1]),
+ vcl::unotools::toDoubleColor(pIn[2]));
+ pIn += 4;
+ }
+ return aRes;
+ }
+
+ virtual uno::Sequence< rendering::ARGBColor > SAL_CALL convertIntegerToARGB( const uno::Sequence< ::sal_Int8 >& deviceColor ) override
+ {
+ const sal_Int8* pIn( deviceColor.getConstArray() );
+ const std::size_t nLen( deviceColor.getLength() );
+ ENSURE_ARG_OR_THROW2(nLen%4==0,
+ "number of channels no multiple of 4",
+ static_cast<rendering::XColorSpace*>(this), 0);
+
+ uno::Sequence< rendering::ARGBColor > aRes(nLen/4);
+ rendering::ARGBColor* pOut( aRes.getArray() );
+ for( std::size_t i=0; i<nLen; i+=4 )
+ {
+ *pOut++ = rendering::ARGBColor(
+ vcl::unotools::toDoubleColor(pIn[3]),
+ vcl::unotools::toDoubleColor(pIn[0]),
+ vcl::unotools::toDoubleColor(pIn[1]),
+ vcl::unotools::toDoubleColor(pIn[2]));
+ pIn += 4;
+ }
+ return aRes;
+ }
+
+ virtual uno::Sequence< rendering::ARGBColor > SAL_CALL convertIntegerToPARGB( const uno::Sequence< ::sal_Int8 >& deviceColor ) override
+ {
+ const sal_Int8* pIn( deviceColor.getConstArray() );
+ const std::size_t nLen( deviceColor.getLength() );
+ ENSURE_ARG_OR_THROW2(nLen%4==0,
+ "number of channels no multiple of 4",
+ static_cast<rendering::XColorSpace*>(this), 0);
+
+ uno::Sequence< rendering::ARGBColor > aRes(nLen/4);
+ rendering::ARGBColor* pOut( aRes.getArray() );
+ for( std::size_t i=0; i<nLen; i+=4 )
+ {
+ const sal_Int8 nAlpha( pIn[3] );
+ *pOut++ = rendering::ARGBColor(
+ vcl::unotools::toDoubleColor(nAlpha),
+ vcl::unotools::toDoubleColor(nAlpha*pIn[0]),
+ vcl::unotools::toDoubleColor(nAlpha*pIn[1]),
+ vcl::unotools::toDoubleColor(nAlpha*pIn[2]));
+ pIn += 4;
+ }
+ return aRes;
+ }
+
+ virtual uno::Sequence< ::sal_Int8 > SAL_CALL convertIntegerFromRGB( const uno::Sequence< rendering::RGBColor >& rgbColor ) override
+ {
+ const rendering::RGBColor* pIn( rgbColor.getConstArray() );
+ const std::size_t nLen( rgbColor.getLength() );
+
+ uno::Sequence< sal_Int8 > aRes(nLen*4);
+ sal_Int8* pColors=aRes.getArray();
+ for( std::size_t i=0; i<nLen; ++i )
+ {
+ *pColors++ = vcl::unotools::toByteColor(pIn->Red);
+ *pColors++ = vcl::unotools::toByteColor(pIn->Green);
+ *pColors++ = vcl::unotools::toByteColor(pIn->Blue);
+ *pColors++ = 0;
+ ++pIn;
+ }
+ return aRes;
+ }
+
+ virtual uno::Sequence< ::sal_Int8 > SAL_CALL convertIntegerFromARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor ) override
+ {
+ const rendering::ARGBColor* pIn( rgbColor.getConstArray() );
+ const std::size_t nLen( rgbColor.getLength() );
+
+ uno::Sequence< sal_Int8 > aRes(nLen*4);
+ sal_Int8* pColors=aRes.getArray();
+ for( std::size_t i=0; i<nLen; ++i )
+ {
+ *pColors++ = vcl::unotools::toByteColor(pIn->Red);
+ *pColors++ = vcl::unotools::toByteColor(pIn->Green);
+ *pColors++ = vcl::unotools::toByteColor(pIn->Blue);
+ *pColors++ = vcl::unotools::toByteColor(pIn->Alpha);
+ ++pIn;
+ }
+ return aRes;
+ }
+
+ virtual uno::Sequence< ::sal_Int8 > SAL_CALL convertIntegerFromPARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor ) override
+ {
+ const rendering::ARGBColor* pIn( rgbColor.getConstArray() );
+ const std::size_t nLen( rgbColor.getLength() );
+
+ uno::Sequence< sal_Int8 > aRes(nLen*4);
+ sal_Int8* pColors=aRes.getArray();
+ for( std::size_t i=0; i<nLen; ++i )
+ {
+ *pColors++ = vcl::unotools::toByteColor(pIn->Red/pIn->Alpha);
+ *pColors++ = vcl::unotools::toByteColor(pIn->Green/pIn->Alpha);
+ *pColors++ = vcl::unotools::toByteColor(pIn->Blue/pIn->Alpha);
+ *pColors++ = vcl::unotools::toByteColor(pIn->Alpha);
+ ++pIn;
+ }
+ return aRes;
+ }
+
+ public:
+ StandardColorSpace() :
+ maComponentTags(4),
+ maBitCounts(4)
+ {
+ sal_Int8* pTags = maComponentTags.getArray();
+ sal_Int32* pBitCounts = maBitCounts.getArray();
+ pTags[0] = rendering::ColorComponentTag::RGB_RED;
+ pTags[1] = rendering::ColorComponentTag::RGB_GREEN;
+ pTags[2] = rendering::ColorComponentTag::RGB_BLUE;
+ pTags[3] = rendering::ColorComponentTag::ALPHA;
+
+ pBitCounts[0] =
+ pBitCounts[1] =
+ pBitCounts[2] =
+ pBitCounts[3] = 8;
+ }
+ };
+
+ class StandardNoAlphaColorSpace : public cppu::WeakImplHelper< css::rendering::XIntegerBitmapColorSpace >
+ {
+ private:
+ uno::Sequence< sal_Int8 > maComponentTags;
+ uno::Sequence< sal_Int32 > maBitCounts;
+
+ virtual ::sal_Int8 SAL_CALL getType( ) override
+ {
+ return rendering::ColorSpaceType::RGB;
+ }
+ virtual uno::Sequence< ::sal_Int8 > SAL_CALL getComponentTags( ) override
+ {
+ return maComponentTags;
+ }
+ virtual ::sal_Int8 SAL_CALL getRenderingIntent( ) override
+ {
+ return rendering::RenderingIntent::PERCEPTUAL;
+ }
+ virtual uno::Sequence< beans::PropertyValue > SAL_CALL getProperties( ) override
+ {
+ return uno::Sequence< beans::PropertyValue >();
+ }
+ virtual uno::Sequence< double > SAL_CALL convertColorSpace( const uno::Sequence< double >& deviceColor,
+ const uno::Reference< rendering::XColorSpace >& targetColorSpace ) override
+ {
+ // TODO(P3): if we know anything about target
+ // colorspace, this can be greatly sped up
+ uno::Sequence<rendering::ARGBColor> aIntermediate(
+ convertToARGB(deviceColor));
+ return targetColorSpace->convertFromARGB(aIntermediate);
+ }
+ virtual uno::Sequence< rendering::RGBColor > SAL_CALL convertToRGB( const uno::Sequence< double >& deviceColor ) override
+ {
+ const double* pIn( deviceColor.getConstArray() );
+ const std::size_t nLen( deviceColor.getLength() );
+ ENSURE_ARG_OR_THROW2(nLen%4==0,
+ "number of channels no multiple of 4",
+ static_cast<rendering::XColorSpace*>(this), 0);
+
+ uno::Sequence< rendering::RGBColor > aRes(nLen/4);
+ rendering::RGBColor* pOut( aRes.getArray() );
+ for( std::size_t i=0; i<nLen; i+=4 )
+ {
+ *pOut++ = rendering::RGBColor(pIn[0],pIn[1],pIn[2]);
+ pIn += 4;
+ }
+ return aRes;
+ }
+ virtual uno::Sequence< rendering::ARGBColor > SAL_CALL convertToARGB( const uno::Sequence< double >& deviceColor ) override
+ {
+ const double* pIn( deviceColor.getConstArray() );
+ const std::size_t nLen( deviceColor.getLength() );
+ ENSURE_ARG_OR_THROW2(nLen%4==0,
+ "number of channels no multiple of 4",
+ static_cast<rendering::XColorSpace*>(this), 0);
+
+ uno::Sequence< rendering::ARGBColor > aRes(nLen/4);
+ rendering::ARGBColor* pOut( aRes.getArray() );
+ for( std::size_t i=0; i<nLen; i+=4 )
+ {
+ *pOut++ = rendering::ARGBColor(1.0,pIn[0],pIn[1],pIn[2]);
+ pIn += 4;
+ }
+ return aRes;
+ }
+ virtual uno::Sequence< rendering::ARGBColor > SAL_CALL convertToPARGB( const uno::Sequence< double >& deviceColor ) override
+ {
+ const double* pIn( deviceColor.getConstArray() );
+ const std::size_t nLen( deviceColor.getLength() );
+ ENSURE_ARG_OR_THROW2(nLen%4==0,
+ "number of channels no multiple of 4",
+ static_cast<rendering::XColorSpace*>(this), 0);
+
+ uno::Sequence< rendering::ARGBColor > aRes(nLen/4);
+ rendering::ARGBColor* pOut( aRes.getArray() );
+ for( std::size_t i=0; i<nLen; i+=4 )
+ {
+ *pOut++ = rendering::ARGBColor(1.0,pIn[0],pIn[1],pIn[2]);
+ pIn += 4;
+ }
+ return aRes;
+ }
+ virtual uno::Sequence< double > SAL_CALL convertFromRGB( const uno::Sequence< rendering::RGBColor >& rgbColor ) override
+ {
+ const rendering::RGBColor* pIn( rgbColor.getConstArray() );
+ const std::size_t nLen( rgbColor.getLength() );
+
+ uno::Sequence< double > aRes(nLen*4);
+ double* pColors=aRes.getArray();
+ for( std::size_t i=0; i<nLen; ++i )
+ {
+ *pColors++ = pIn->Red;
+ *pColors++ = pIn->Green;
+ *pColors++ = pIn->Blue;
+ *pColors++ = 1.0; // the value does not matter
+ ++pIn;
+ }
+ return aRes;
+ }
+ virtual uno::Sequence< double > SAL_CALL convertFromARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor ) override
+ {
+ const rendering::ARGBColor* pIn( rgbColor.getConstArray() );
+ const std::size_t nLen( rgbColor.getLength() );
+
+ uno::Sequence< double > aRes(nLen*4);
+ double* pColors=aRes.getArray();
+ for( std::size_t i=0; i<nLen; ++i )
+ {
+ *pColors++ = pIn->Red;
+ *pColors++ = pIn->Green;
+ *pColors++ = pIn->Blue;
+ *pColors++ = 1.0; // the value does not matter
+ ++pIn;
+ }
+ return aRes;
+ }
+ virtual uno::Sequence< double > SAL_CALL convertFromPARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor ) override
+ {
+ const rendering::ARGBColor* pIn( rgbColor.getConstArray() );
+ const std::size_t nLen( rgbColor.getLength() );
+
+ uno::Sequence< double > aRes(nLen*4);
+ double* pColors=aRes.getArray();
+ for( std::size_t i=0; i<nLen; ++i )
+ {
+ *pColors++ = pIn->Red/pIn->Alpha;
+ *pColors++ = pIn->Green/pIn->Alpha;
+ *pColors++ = pIn->Blue/pIn->Alpha;
+ *pColors++ = 1.0; // the value does not matter
+ ++pIn;
+ }
+ return aRes;
+ }
+
+ // XIntegerBitmapColorSpace
+ virtual ::sal_Int32 SAL_CALL getBitsPerPixel( ) override
+ {
+ return 32;
+ }
+ virtual uno::Sequence< ::sal_Int32 > SAL_CALL getComponentBitCounts( ) override
+ {
+ return maBitCounts;
+ }
+ virtual ::sal_Int8 SAL_CALL getEndianness( ) override
+ {
+ return util::Endianness::LITTLE;
+ }
+ virtual uno::Sequence<double> SAL_CALL convertFromIntegerColorSpace( const uno::Sequence< ::sal_Int8 >& deviceColor,
+ const uno::Reference< rendering::XColorSpace >& targetColorSpace ) override
+ {
+ if( dynamic_cast<StandardNoAlphaColorSpace*>(targetColorSpace.get()) )
+ {
+ const sal_Int8* pIn( deviceColor.getConstArray() );
+ const std::size_t nLen( deviceColor.getLength() );
+ ENSURE_ARG_OR_THROW2(nLen%4==0,
+ "number of channels no multiple of 4",
+ static_cast<rendering::XColorSpace*>(this), 0);
+
+ uno::Sequence<double> aRes(nLen);
+ double* pOut( aRes.getArray() );
+ for( std::size_t i=0; i<nLen; i+=4 )
+ {
+ *pOut++ = vcl::unotools::toDoubleColor(*pIn++);
+ *pOut++ = vcl::unotools::toDoubleColor(*pIn++);
+ *pOut++ = vcl::unotools::toDoubleColor(*pIn++);
+ *pOut++ = 1.0; pIn++;
+ }
+ return aRes;
+ }
+ else
+ {
+ // TODO(P3): if we know anything about target
+ // colorspace, this can be greatly sped up
+ uno::Sequence<rendering::ARGBColor> aIntermediate(
+ convertIntegerToARGB(deviceColor));
+ return targetColorSpace->convertFromARGB(aIntermediate);
+ }
+ }
+ virtual uno::Sequence< ::sal_Int8 > SAL_CALL convertToIntegerColorSpace( const uno::Sequence< ::sal_Int8 >& deviceColor,
+ const uno::Reference< rendering::XIntegerBitmapColorSpace >& targetColorSpace ) override
+ {
+ if( dynamic_cast<StandardNoAlphaColorSpace*>(targetColorSpace.get()) )
+ {
+ // it's us, so simply pass-through the data
+ return deviceColor;
+ }
+ else
+ {
+ // TODO(P3): if we know anything about target
+ // colorspace, this can be greatly sped up
+ uno::Sequence<rendering::ARGBColor> aIntermediate(
+ convertIntegerToARGB(deviceColor));
+ return targetColorSpace->convertIntegerFromARGB(aIntermediate);
+ }
+ }
+ virtual uno::Sequence< rendering::RGBColor > SAL_CALL convertIntegerToRGB( const uno::Sequence< ::sal_Int8 >& deviceColor ) override
+ {
+ const sal_Int8* pIn( deviceColor.getConstArray() );
+ const std::size_t nLen( deviceColor.getLength() );
+ ENSURE_ARG_OR_THROW2(nLen%4==0,
+ "number of channels no multiple of 4",
+ static_cast<rendering::XColorSpace*>(this), 0);
+
+ uno::Sequence< rendering::RGBColor > aRes(nLen/4);
+ rendering::RGBColor* pOut( aRes.getArray() );
+ for( std::size_t i=0; i<nLen; i+=4 )
+ {
+ *pOut++ = rendering::RGBColor(
+ vcl::unotools::toDoubleColor(pIn[0]),
+ vcl::unotools::toDoubleColor(pIn[1]),
+ vcl::unotools::toDoubleColor(pIn[2]));
+ pIn += 4;
+ }
+ return aRes;
+ }
+
+ virtual uno::Sequence< rendering::ARGBColor > SAL_CALL convertIntegerToARGB( const uno::Sequence< ::sal_Int8 >& deviceColor ) override
+ {
+ const sal_Int8* pIn( deviceColor.getConstArray() );
+ const std::size_t nLen( deviceColor.getLength() );
+ ENSURE_ARG_OR_THROW2(nLen%4==0,
+ "number of channels no multiple of 4",
+ static_cast<rendering::XColorSpace*>(this), 0);
+
+ uno::Sequence< rendering::ARGBColor > aRes(nLen/4);
+ rendering::ARGBColor* pOut( aRes.getArray() );
+ for( std::size_t i=0; i<nLen; i+=4 )
+ {
+ *pOut++ = rendering::ARGBColor(
+ 1.0,
+ vcl::unotools::toDoubleColor(pIn[0]),
+ vcl::unotools::toDoubleColor(pIn[1]),
+ vcl::unotools::toDoubleColor(pIn[2]));
+ pIn += 4;
+ }
+ return aRes;
+ }
+
+ virtual uno::Sequence< rendering::ARGBColor > SAL_CALL convertIntegerToPARGB( const uno::Sequence< ::sal_Int8 >& deviceColor ) override
+ {
+ const sal_Int8* pIn( deviceColor.getConstArray() );
+ const std::size_t nLen( deviceColor.getLength() );
+ ENSURE_ARG_OR_THROW2(nLen%4==0,
+ "number of channels no multiple of 4",
+ static_cast<rendering::XColorSpace*>(this), 0);
+
+ uno::Sequence< rendering::ARGBColor > aRes(nLen/4);
+ rendering::ARGBColor* pOut( aRes.getArray() );
+ for( std::size_t i=0; i<nLen; i+=4 )
+ {
+ *pOut++ = rendering::ARGBColor(
+ 1.0,
+ vcl::unotools::toDoubleColor(pIn[0]),
+ vcl::unotools::toDoubleColor(pIn[1]),
+ vcl::unotools::toDoubleColor(pIn[2]));
+ pIn += 4;
+ }
+ return aRes;
+ }
+
+ virtual uno::Sequence< ::sal_Int8 > SAL_CALL convertIntegerFromRGB( const uno::Sequence< rendering::RGBColor >& rgbColor ) override
+ {
+ const rendering::RGBColor* pIn( rgbColor.getConstArray() );
+ const std::size_t nLen( rgbColor.getLength() );
+
+ uno::Sequence< sal_Int8 > aRes(nLen*4);
+ sal_Int8* pColors=aRes.getArray();
+ for( std::size_t i=0; i<nLen; ++i )
+ {
+ *pColors++ = vcl::unotools::toByteColor(pIn->Red);
+ *pColors++ = vcl::unotools::toByteColor(pIn->Green);
+ *pColors++ = vcl::unotools::toByteColor(pIn->Blue);
+ *pColors++ = 1.0;
+ ++pIn;
+ }
+ return aRes;
+ }
+
+ virtual uno::Sequence< ::sal_Int8 > SAL_CALL convertIntegerFromARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor ) override
+ {
+ const rendering::ARGBColor* pIn( rgbColor.getConstArray() );
+ const std::size_t nLen( rgbColor.getLength() );
+
+ uno::Sequence< sal_Int8 > aRes(nLen*4);
+ sal_Int8* pColors=aRes.getArray();
+ for( std::size_t i=0; i<nLen; ++i )
+ {
+ *pColors++ = vcl::unotools::toByteColor(pIn->Red);
+ *pColors++ = vcl::unotools::toByteColor(pIn->Green);
+ *pColors++ = vcl::unotools::toByteColor(pIn->Blue);
+ *pColors++ = -1;
+ ++pIn;
+ }
+ return aRes;
+ }
+
+ virtual uno::Sequence< ::sal_Int8 > SAL_CALL convertIntegerFromPARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor ) override
+ {
+ const rendering::ARGBColor* pIn( rgbColor.getConstArray() );
+ const std::size_t nLen( rgbColor.getLength() );
+
+ uno::Sequence< sal_Int8 > aRes(nLen*4);
+ sal_Int8* pColors=aRes.getArray();
+ for( std::size_t i=0; i<nLen; ++i )
+ {
+ *pColors++ = vcl::unotools::toByteColor(pIn->Red/pIn->Alpha);
+ *pColors++ = vcl::unotools::toByteColor(pIn->Green/pIn->Alpha);
+ *pColors++ = vcl::unotools::toByteColor(pIn->Blue/pIn->Alpha);
+ *pColors++ = -1;
+ ++pIn;
+ }
+ return aRes;
+ }
+
+ public:
+ StandardNoAlphaColorSpace() :
+ maComponentTags(3),
+ maBitCounts(3)
+ {
+ sal_Int8* pTags = maComponentTags.getArray();
+ sal_Int32* pBitCounts = maBitCounts.getArray();
+ pTags[0] = rendering::ColorComponentTag::RGB_RED;
+ pTags[1] = rendering::ColorComponentTag::RGB_GREEN;
+ pTags[2] = rendering::ColorComponentTag::RGB_BLUE;
+
+ pBitCounts[0] =
+ pBitCounts[1] =
+ pBitCounts[2] = 8;
+ }
+ };
+
+ }
+
+ uno::Reference<rendering::XIntegerBitmapColorSpace> const & getStdColorSpace()
+ {
+ static uno::Reference<rendering::XIntegerBitmapColorSpace> SPACE = new StandardColorSpace();
+ return SPACE;
+ }
+
+ uno::Reference<rendering::XIntegerBitmapColorSpace> const & getStdColorSpaceWithoutAlpha()
+ {
+ static uno::Reference<rendering::XIntegerBitmapColorSpace> SPACE = new StandardNoAlphaColorSpace();
+ return SPACE;
+ }
+
+ rendering::IntegerBitmapLayout getStdMemoryLayout( const geometry::IntegerSize2D& rBmpSize )
+ {
+ rendering::IntegerBitmapLayout aLayout;
+
+ aLayout.ScanLines = rBmpSize.Height;
+ aLayout.ScanLineBytes = rBmpSize.Width*4;
+ aLayout.ScanLineStride = aLayout.ScanLineBytes;
+ aLayout.PlaneStride = 0;
+ aLayout.ColorSpace = getStdColorSpace();
+ aLayout.Palette.clear();
+ aLayout.IsMsbFirst = false;
+
+ return aLayout;
+ }
+
+ uno::Sequence<sal_Int8> colorToStdIntSequence( const ::Color& rColor )
+ {
+ uno::Sequence<sal_Int8> aRet(4);
+ sal_Int8* pCols( aRet.getArray() );
+#ifdef OSL_BIGENDIAN
+ pCols[0] = rColor.GetRed();
+ pCols[1] = rColor.GetGreen();
+ pCols[2] = rColor.GetBlue();
+ pCols[3] = rColor.GetAlpha();
+#else
+ *reinterpret_cast<sal_Int32*>(pCols) = sal_Int32(rColor);
+#endif
+ return aRet;
+ }
+
+ // Create a corrected view transformation out of the give one,
+ // which ensures that the rectangle given by (0,0) and
+ // rSpriteSize is mapped with its left,top corner to (0,0)
+ // again. This is required to properly render sprite
+ // animations to buffer bitmaps.
+ ::basegfx::B2DHomMatrix& calcRectToOriginTransform( ::basegfx::B2DHomMatrix& o_transform,
+ const ::basegfx::B2DRange& i_srcRect,
+ const ::basegfx::B2DHomMatrix& i_transformation )
+ {
+ if( i_srcRect.isEmpty() )
+ {
+ o_transform = i_transformation;
+ return o_transform;
+ }
+
+ // transform by given transformation
+ ::basegfx::B2DRectangle aTransformedRect;
+
+ calcTransformedRectBounds( aTransformedRect,
+ i_srcRect,
+ i_transformation );
+
+ // now move resulting left,top point of bounds to (0,0)
+ const basegfx::B2DHomMatrix aCorrectedTransform(basegfx::utils::createTranslateB2DHomMatrix(
+ -aTransformedRect.getMinX(), -aTransformedRect.getMinY()));
+
+ // prepend to original transformation
+ o_transform = aCorrectedTransform * i_transformation;
+
+ return o_transform;
+ }
+
+ ::basegfx::B2DRange& calcTransformedRectBounds( ::basegfx::B2DRange& outRect,
+ const ::basegfx::B2DRange& inRect,
+ const ::basegfx::B2DHomMatrix& transformation )
+ {
+ outRect.reset();
+
+ if( inRect.isEmpty() )
+ return outRect;
+
+ // transform all four extremal points of the rectangle,
+ // take bounding rect of those.
+
+ // transform left-top point
+ outRect.expand( transformation * inRect.getMinimum() );
+
+ // transform bottom-right point
+ outRect.expand( transformation * inRect.getMaximum() );
+
+ ::basegfx::B2DPoint aPoint;
+
+ // transform top-right point
+ aPoint.setX( inRect.getMaxX() );
+ aPoint.setY( inRect.getMinY() );
+
+ aPoint *= transformation;
+ outRect.expand( aPoint );
+
+ // transform bottom-left point
+ aPoint.setX( inRect.getMinX() );
+ aPoint.setY( inRect.getMaxY() );
+
+ aPoint *= transformation;
+ outRect.expand( aPoint );
+
+ // over and out.
+ return outRect;
+ }
+
+ bool isInside( const ::basegfx::B2DRange& rContainedRect,
+ const ::basegfx::B2DRange& rTransformRect,
+ const ::basegfx::B2DHomMatrix& rTransformation )
+ {
+ if( rContainedRect.isEmpty() || rTransformRect.isEmpty() )
+ return false;
+
+ ::basegfx::B2DPolygon aPoly(
+ ::basegfx::utils::createPolygonFromRect( rTransformRect ) );
+ aPoly.transform( rTransformation );
+
+ return ::basegfx::utils::isInside( aPoly,
+ ::basegfx::utils::createPolygonFromRect(
+ rContainedRect ),
+ true );
+ }
+
+ namespace
+ {
+ bool clipAreaImpl( ::basegfx::B2IRange* o_pDestArea,
+ ::basegfx::B2IRange& io_rSourceArea,
+ ::basegfx::B2IPoint& io_rDestPoint,
+ const ::basegfx::B2IRange& rSourceBounds,
+ const ::basegfx::B2IRange& rDestBounds )
+ {
+ const ::basegfx::B2IPoint aSourceTopLeft(
+ io_rSourceArea.getMinimum() );
+
+ ::basegfx::B2IRange aLocalSourceArea( io_rSourceArea );
+
+ // clip source area (which must be inside rSourceBounds)
+ aLocalSourceArea.intersect( rSourceBounds );
+
+ if( aLocalSourceArea.isEmpty() )
+ return false;
+
+ // calc relative new source area points (relative to orig
+ // source area)
+ const ::basegfx::B2IVector aUpperLeftOffset(
+ aLocalSourceArea.getMinimum()-aSourceTopLeft );
+ const ::basegfx::B2IVector aLowerRightOffset(
+ aLocalSourceArea.getMaximum()-aSourceTopLeft );
+
+ ::basegfx::B2IRange aLocalDestArea( io_rDestPoint + aUpperLeftOffset,
+ io_rDestPoint + aLowerRightOffset );
+
+ // clip dest area (which must be inside rDestBounds)
+ aLocalDestArea.intersect( rDestBounds );
+
+ if( aLocalDestArea.isEmpty() )
+ return false;
+
+ // calc relative new dest area points (relative to orig
+ // source area)
+ const ::basegfx::B2IVector aDestUpperLeftOffset(
+ aLocalDestArea.getMinimum()-io_rDestPoint );
+ const ::basegfx::B2IVector aDestLowerRightOffset(
+ aLocalDestArea.getMaximum()-io_rDestPoint );
+
+ io_rSourceArea = ::basegfx::B2IRange( aSourceTopLeft + aDestUpperLeftOffset,
+ aSourceTopLeft + aDestLowerRightOffset );
+ io_rDestPoint = aLocalDestArea.getMinimum();
+
+ if( o_pDestArea )
+ *o_pDestArea = aLocalDestArea;
+
+ return true;
+ }
+ }
+
+ bool clipScrollArea( ::basegfx::B2IRange& io_rSourceArea,
+ ::basegfx::B2IPoint& io_rDestPoint,
+ std::vector< ::basegfx::B2IRange >& o_ClippedAreas,
+ const ::basegfx::B2IRange& rBounds )
+ {
+ ::basegfx::B2IRange aResultingDestArea;
+
+ // compute full destination area (to determine uninitialized
+ // areas below)
+ const ::basegfx::B2I64Tuple& rRange( io_rSourceArea.getRange() );
+ ::basegfx::B2IRange aInputDestArea( io_rDestPoint.getX(),
+ io_rDestPoint.getY(),
+ (io_rDestPoint.getX()
+ + static_cast<sal_Int32>(rRange.getX())),
+ (io_rDestPoint.getY()
+ + static_cast<sal_Int32>(rRange.getY())) );
+ // limit to output area (no point updating outside of it)
+ aInputDestArea.intersect( rBounds );
+
+ // clip to rBounds
+ if( !clipAreaImpl( &aResultingDestArea,
+ io_rSourceArea,
+ io_rDestPoint,
+ rBounds,
+ rBounds ) )
+ return false;
+
+ // finally, compute all areas clipped off the total
+ // destination area.
+ ::basegfx::computeSetDifference( o_ClippedAreas,
+ aInputDestArea,
+ aResultingDestArea );
+
+ return true;
+ }
+
+ ::basegfx::B2IRange spritePixelAreaFromB2DRange( const ::basegfx::B2DRange& rRange )
+ {
+ if( rRange.isEmpty() )
+ return ::basegfx::B2IRange();
+
+ const ::basegfx::B2IPoint aTopLeft( ::basegfx::fround( rRange.getMinX() ),
+ ::basegfx::fround( rRange.getMinY() ) );
+ return ::basegfx::B2IRange( aTopLeft,
+ aTopLeft + ::basegfx::B2IPoint(
+ ::basegfx::fround( rRange.getWidth() ),
+ ::basegfx::fround( rRange.getHeight() ) ) );
+ }
+
+ uno::Sequence< uno::Any >& getDeviceInfo( const uno::Reference< rendering::XCanvas >& i_rxCanvas,
+ uno::Sequence< uno::Any >& o_rxParams )
+ {
+ o_rxParams.realloc( 0 );
+
+ if( !i_rxCanvas.is() )
+ return o_rxParams;
+
+ try
+ {
+ uno::Reference< rendering::XGraphicDevice > xDevice( i_rxCanvas->getDevice(),
+ uno::UNO_SET_THROW );
+
+ uno::Reference< lang::XServiceInfo > xServiceInfo( xDevice,
+ uno::UNO_QUERY_THROW );
+ uno::Reference< beans::XPropertySet > xPropSet( xDevice,
+ uno::UNO_QUERY_THROW );
+
+ o_rxParams = { uno::Any(xServiceInfo->getImplementationName()),
+ xPropSet->getPropertyValue( "DeviceHandle" ) };
+ }
+ catch( const uno::Exception& )
+ {
+ // ignore, but return empty sequence
+ }
+
+ return o_rxParams;
+ }
+
+ awt::Rectangle getAbsoluteWindowRect( const awt::Rectangle& rRect,
+ const uno::Reference< awt::XWindow2 >& xWin )
+ {
+ awt::Rectangle aRetVal( rRect );
+
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow(xWin);
+ if( pWindow )
+ {
+ ::Point aPoint( aRetVal.X,
+ aRetVal.Y );
+
+ aPoint = pWindow->OutputToScreenPixel( aPoint );
+
+ aRetVal.X = aPoint.X();
+ aRetVal.Y = aPoint.Y();
+ }
+
+ return aRetVal;
+ }
+
+ ::basegfx::B2DPolyPolygon getBoundMarksPolyPolygon( const ::basegfx::B2DRange& rRange )
+ {
+ ::basegfx::B2DPolyPolygon aPolyPoly;
+ ::basegfx::B2DPolygon aPoly;
+
+ const double nX0( rRange.getMinX() );
+ const double nY0( rRange.getMinY() );
+ const double nX1( rRange.getMaxX() );
+ const double nY1( rRange.getMaxY() );
+
+ aPoly.append( ::basegfx::B2DPoint( nX0+4,
+ nY0 ) );
+ aPoly.append( ::basegfx::B2DPoint( nX0,
+ nY0 ) );
+ aPoly.append( ::basegfx::B2DPoint( nX0,
+ nY0+4 ) );
+ aPolyPoly.append( aPoly ); aPoly.clear();
+
+ aPoly.append( ::basegfx::B2DPoint( nX1-4,
+ nY0 ) );
+ aPoly.append( ::basegfx::B2DPoint( nX1,
+ nY0 ) );
+ aPoly.append( ::basegfx::B2DPoint( nX1,
+ nY0+4 ) );
+ aPolyPoly.append( aPoly ); aPoly.clear();
+
+ aPoly.append( ::basegfx::B2DPoint( nX0+4,
+ nY1 ) );
+ aPoly.append( ::basegfx::B2DPoint( nX0,
+ nY1 ) );
+ aPoly.append( ::basegfx::B2DPoint( nX0,
+ nY1-4 ) );
+ aPolyPoly.append( aPoly ); aPoly.clear();
+
+ aPoly.append( ::basegfx::B2DPoint( nX1-4,
+ nY1 ) );
+ aPoly.append( ::basegfx::B2DPoint( nX1,
+ nY1 ) );
+ aPoly.append( ::basegfx::B2DPoint( nX1,
+ nY1-4 ) );
+ aPolyPoly.append( aPoly );
+
+ return aPolyPoly;
+ }
+
+ int calcGradientStepCount( ::basegfx::B2DHomMatrix& rTotalTransform,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState,
+ const rendering::Texture& texture,
+ int nColorSteps )
+ {
+ // calculate overall texture transformation (directly from
+ // texture to device space).
+ ::basegfx::B2DHomMatrix aMatrix;
+
+ rTotalTransform.identity();
+ ::basegfx::unotools::homMatrixFromAffineMatrix( rTotalTransform,
+ texture.AffineTransform );
+ ::canvas::tools::mergeViewAndRenderTransform(aMatrix,
+ viewState,
+ renderState);
+ rTotalTransform *= aMatrix; // prepend total view/render transformation
+
+ // determine size of gradient in device coordinate system
+ // (to e.g. determine sensible number of gradient steps)
+ ::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 *= rTotalTransform;
+ aLeftBottom *= rTotalTransform;
+ aRightTop *= rTotalTransform;
+ aRightBottom*= rTotalTransform;
+
+ // longest line in gradient bound rect
+ const int nGradientSize(
+ static_cast<int>(
+ std::max(
+ ::basegfx::B2DVector(aRightBottom-aLeftTop).getLength(),
+ ::basegfx::B2DVector(aRightTop-aLeftBottom).getLength() ) + 1.0 ) );
+
+ // typical number for pixel of the same color (strip size)
+ const int nStripSize( nGradientSize < 50 ? 2 : 4 );
+
+ // use at least three steps, and at utmost the number of color
+ // steps
+ return std::max( 3,
+ std::min(
+ nGradientSize / nStripSize,
+ nColorSteps ) );
+ }
+
+ void clipOutDev(const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState,
+ OutputDevice& rOutDev,
+ OutputDevice* p2ndOutDev)
+ {
+ // accumulate non-empty clips into one region
+ vcl::Region aClipRegion(true);
+
+ if( viewState.Clip.is() )
+ {
+ ::basegfx::B2DPolyPolygon aClipPoly(
+ ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(viewState.Clip) );
+
+ if( aClipPoly.count() )
+ {
+ // setup non-empty clipping
+ ::basegfx::B2DHomMatrix aMatrix;
+ aClipPoly.transform(
+ ::basegfx::unotools::homMatrixFromAffineMatrix( aMatrix,
+ viewState.AffineTransform ) );
+
+ aClipRegion = vcl::Region::GetRegionFromPolyPolygon( ::tools::PolyPolygon( aClipPoly ) );
+ }
+ else
+ {
+ // clip polygon is empty
+ aClipRegion.SetEmpty();
+ }
+ }
+
+ if( renderState.Clip.is() )
+ {
+ ::basegfx::B2DPolyPolygon aClipPoly(
+ ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(renderState.Clip) );
+
+ ::basegfx::B2DHomMatrix aMatrix;
+ aClipPoly.transform(
+ ::canvas::tools::mergeViewAndRenderTransform( aMatrix,
+ viewState,
+ renderState ) );
+
+ if( aClipPoly.count() )
+ {
+ // setup non-empty clipping
+ vcl::Region aRegion = vcl::Region::GetRegionFromPolyPolygon( ::tools::PolyPolygon( aClipPoly ) );
+ aClipRegion.Intersect( aRegion );
+ }
+ else
+ {
+ // clip polygon is empty
+ aClipRegion.SetEmpty();
+ }
+ }
+
+ // setup accumulated clip region. Note that setting an
+ // empty clip region denotes "clip everything" on the
+ // OutputDevice (which is why we translate that into
+ // SetClipRegion() here). When both view and render clip
+ // are empty, aClipRegion remains default-constructed,
+ // i.e. empty, too.
+ if( aClipRegion.IsNull() )
+ {
+ rOutDev.SetClipRegion();
+
+ if( p2ndOutDev )
+ p2ndOutDev->SetClipRegion();
+ }
+ else
+ {
+ rOutDev.SetClipRegion( aClipRegion );
+
+ if( p2ndOutDev )
+ p2ndOutDev->SetClipRegion( aClipRegion );
+ }
+ }
+
+ void extractExtraFontProperties(const uno::Sequence<beans::PropertyValue>& rExtraFontProperties,
+ sal_uInt32 &rEmphasisMark)
+ {
+ for(const beans::PropertyValue& rPropVal : rExtraFontProperties)
+ {
+ if (rPropVal.Name == "EmphasisMark")
+ rPropVal.Value >>= rEmphasisMark;
+ }
+ }
+
+} // namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/tools/elapsedtime.cxx b/canvas/source/tools/elapsedtime.cxx
new file mode 100644
index 000000000..46167dd9a
--- /dev/null
+++ b/canvas/source/tools/elapsedtime.cxx
@@ -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 .
+ */
+
+#include <sal/config.h>
+
+#include <canvas/elapsedtime.hxx>
+
+#include <tools/time.hxx>
+#include <utility>
+
+namespace canvas::tools {
+
+double ElapsedTime::getSystemTime()
+{
+ return ::tools::Time::GetMonotonicTicks() / 1.0E6;
+}
+
+ElapsedTime::ElapsedTime()
+ : m_pTimeBase(),
+ m_fLastQueriedTime( 0.0 ),
+ m_fStartTime( getSystemTime() ),
+ m_fFrozenTime( 0.0 ),
+ m_bInPauseMode( false ),
+ m_bInHoldMode( false )
+{
+}
+
+ElapsedTime::ElapsedTime(
+ std::shared_ptr<ElapsedTime> pTimeBase )
+ : m_pTimeBase(std::move( pTimeBase )),
+ m_fLastQueriedTime( 0.0 ),
+ m_fStartTime( getCurrentTime() ),
+ m_fFrozenTime( 0.0 ),
+ m_bInPauseMode( false ),
+ m_bInHoldMode( false )
+{
+}
+
+void ElapsedTime::reset()
+{
+ m_fLastQueriedTime = 0.0;
+ m_fStartTime = getCurrentTime();
+ m_fFrozenTime = 0.0;
+ m_bInPauseMode = false;
+ m_bInHoldMode = false;
+}
+
+void ElapsedTime::adjustTimer( double fOffset )
+{
+ // to make getElapsedTime() become _larger_, have to reduce
+ // m_fStartTime.
+ m_fStartTime -= fOffset;
+
+ // also adjust frozen time, this method must _always_ affect the
+ // value returned by getElapsedTime()!
+ if (m_bInHoldMode || m_bInPauseMode)
+ m_fFrozenTime += fOffset;
+}
+
+double ElapsedTime::getCurrentTime() const
+{
+ return m_pTimeBase == nullptr ? getSystemTime() : m_pTimeBase->getElapsedTimeImpl();
+}
+
+double ElapsedTime::getElapsedTime() const
+{
+ m_fLastQueriedTime = getElapsedTimeImpl();
+ return m_fLastQueriedTime;
+}
+
+double ElapsedTime::getElapsedTimeImpl() const
+{
+ if (m_bInHoldMode || m_bInPauseMode)
+ return m_fFrozenTime;
+
+ return getCurrentTime() - m_fStartTime;
+}
+
+void ElapsedTime::pauseTimer()
+{
+ m_fFrozenTime = getElapsedTimeImpl();
+ m_bInPauseMode = true;
+}
+
+void ElapsedTime::continueTimer()
+{
+ m_bInPauseMode = false;
+
+ // stop pausing, time runs again. Note that
+ // getElapsedTimeImpl() honors hold mode, i.e. a
+ // continueTimer() in hold mode will preserve the latter
+ const double fPauseDuration( getElapsedTimeImpl() - m_fFrozenTime );
+
+ // adjust start time, such that subsequent getElapsedTime() calls
+ // will virtually start from m_fFrozenTime.
+ m_fStartTime += fPauseDuration;
+}
+
+void ElapsedTime::holdTimer()
+{
+ // when called during hold mode (e.g. more than once per time
+ // object), the original hold time will be maintained.
+ m_fFrozenTime = getElapsedTimeImpl();
+ m_bInHoldMode = true;
+}
+
+void ElapsedTime::releaseTimer()
+{
+ m_bInHoldMode = false;
+}
+
+} // namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/tools/page.cxx b/canvas/source/tools/page.cxx
new file mode 100644
index 000000000..3537fa0b6
--- /dev/null
+++ b/canvas/source/tools/page.cxx
@@ -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 .
+ */
+
+#include <sal/config.h>
+
+#include "page.hxx"
+
+namespace canvas
+{
+ Page::Page( const std::shared_ptr<IRenderModule> &rRenderModule ) :
+ mpRenderModule(rRenderModule),
+ mpSurface(rRenderModule->createSurface(::basegfx::B2ISize()))
+ {
+ }
+
+ void Page::validate()
+ {
+ if(!(isValid()))
+ {
+ for( const auto& rFragmentPtr : mpFragments )
+ rFragmentPtr->refresh();
+ }
+ }
+
+ bool Page::isValid() const
+ {
+ return mpSurface && mpSurface->isValid();
+ }
+
+ FragmentSharedPtr Page::allocateSpace( const ::basegfx::B2ISize& rSize )
+ {
+ SurfaceRect rect(rSize);
+ if(insert(rect))
+ {
+ FragmentSharedPtr pFragment = std::make_shared<PageFragment>(rect,this);
+ mpFragments.push_back(pFragment);
+ return pFragment;
+ }
+
+ return FragmentSharedPtr();
+ }
+
+ bool Page::nakedFragment( const FragmentSharedPtr& pFragment )
+ {
+ SurfaceRect rect(pFragment->getSize());
+ if(insert(rect))
+ {
+ pFragment->setPage(this);
+ mpFragments.push_back(pFragment);
+ return true;
+ }
+
+ return false;
+ }
+
+ void Page::free( const FragmentSharedPtr& pFragment )
+ {
+ // the fragment passes as argument is no longer
+ // dedicated to this page. either it is about to
+ // be relocated to some other page or it will
+ // currently be deleted. in either case, simply
+ // remove the reference from our internal storage.
+ FragmentContainer_t::iterator it(
+ std::remove(
+ mpFragments.begin(),mpFragments.end(),pFragment));
+ mpFragments.erase(it,mpFragments.end());
+ }
+
+ bool Page::insert( SurfaceRect& r )
+ {
+ for( const auto& pFragment : mpFragments )
+ {
+ const SurfaceRect &rect = pFragment->getRect();
+ const sal_Int32 x = rect.maPos.getX();
+ const sal_Int32 y = rect.maPos.getY();
+ // to avoid interpolation artifacts from other textures,
+ // one pixel gap between them
+ const sal_Int32 w = rect.maSize.getX()+1;
+ const sal_Int32 h = rect.maSize.getY()+1;
+
+ // probe location to the right
+ r.maPos.setX(x+w);
+ r.maPos.setY(y);
+ if(isValidLocation(r))
+ return true;
+
+ // probe location at bottom
+ r.maPos.setX(x);
+ r.maPos.setY(y+h);
+ if(isValidLocation(r))
+ return true;
+ }
+
+ r.maPos.setX(0);
+ r.maPos.setY(0);
+
+ return isValidLocation(r);
+ }
+
+ bool Page::isValidLocation( const SurfaceRect& r ) const
+ {
+ // the rectangle passed as argument has a valid
+ // location if and only if there's no intersection
+ // with existing areas.
+ SurfaceRect aBoundary(mpRenderModule->getPageSize());
+ if( !r.inside(aBoundary) )
+ return false;
+
+ for( const auto& pFragment : mpFragments )
+ {
+ if( r.intersection( pFragment->getRect() ) )
+ return false;
+ }
+
+ return true;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/tools/page.hxx b/canvas/source/tools/page.hxx
new file mode 100644
index 000000000..14fa17c53
--- /dev/null
+++ b/canvas/source/tools/page.hxx
@@ -0,0 +1,147 @@
+/* -*- 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/b2isize.hxx>
+#include <basegfx/range/b2irectangle.hxx>
+#include <rendering/icolorbuffer.hxx>
+#include <rendering/irendermodule.hxx>
+#include <rendering/isurface.hxx>
+
+#include <memory>
+#include <vector>
+#include "surfacerect.hxx"
+
+namespace canvas
+{
+ class PageFragment;
+
+ typedef std::shared_ptr< PageFragment > FragmentSharedPtr;
+
+ /** One page of IRenderModule-provided texture space
+ */
+ class Page
+ {
+ public:
+ explicit Page( const std::shared_ptr<IRenderModule>& rRenderModule );
+
+ FragmentSharedPtr allocateSpace( const ::basegfx::B2ISize& rSize );
+ bool nakedFragment( const FragmentSharedPtr& pFragment );
+ void free( const FragmentSharedPtr& pFragment );
+ const std::shared_ptr<ISurface>& getSurface() const { return mpSurface; }
+ bool isValid() const;
+ void validate();
+
+ private:
+ typedef std::vector<FragmentSharedPtr> FragmentContainer_t;
+
+ std::shared_ptr<IRenderModule> mpRenderModule;
+ std::shared_ptr<ISurface> mpSurface;
+ FragmentContainer_t mpFragments;
+
+ bool insert( SurfaceRect& r );
+ bool isValidLocation( const SurfaceRect& r ) const;
+ };
+
+ typedef std::shared_ptr< Page > PageSharedPtr;
+
+
+ /** A part of a page, which gets allocated to a surface
+ */
+ class PageFragment
+ {
+ public:
+ PageFragment( const SurfaceRect& r,
+ Page* pPage ) :
+ mpPage(pPage),
+ maRect(r),
+ mpBuffer(),
+ maSourceOffset()
+ {
+ }
+
+ /// Creates a 'naked' fragment.
+ explicit PageFragment( const ::basegfx::B2ISize& rSize ) :
+ mpPage(nullptr),
+ maRect(rSize),
+ mpBuffer(),
+ maSourceOffset()
+ {
+ }
+
+ bool isNaked() const { return (mpPage == nullptr); }
+ const SurfaceRect& getRect() const { return maRect; }
+ const ::basegfx::B2IPoint& getPos() const { return maRect.maPos; }
+ const ::basegfx::B2ISize& getSize() const { return maRect.maSize; }
+ void setColorBuffer( const std::shared_ptr<IColorBuffer>& pColorBuffer ) { mpBuffer=pColorBuffer; }
+ void setSourceOffset( const ::basegfx::B2IPoint& rOffset ) { maSourceOffset=rOffset; }
+ void setPage( Page* pPage ) { mpPage=pPage; }
+
+ void free( const FragmentSharedPtr& pFragment )
+ {
+ if(mpPage)
+ mpPage->free(pFragment);
+
+ mpPage=nullptr;
+ }
+
+ bool select( bool bRefresh )
+ {
+ // request was made to select this fragment,
+ // but this fragment has not been located on any
+ // of the available pages, we need to hurry now.
+ if(!mpPage)
+ return false;
+
+ std::shared_ptr<ISurface> pSurface(mpPage->getSurface());
+
+ // select this surface before wiping the contents
+ // since a specific implementation could trigger
+ // a rendering operation here...
+ if(!(pSurface->selectTexture()))
+ return false;
+
+ // call refresh() if requested, otherwise we're up to date...
+ return !bRefresh || refresh();
+ }
+
+ bool refresh()
+ {
+ if(!mpPage)
+ return false;
+
+ std::shared_ptr<ISurface> pSurface(mpPage->getSurface());
+
+ return pSurface->update( maRect.maPos,
+ ::basegfx::B2IRectangle(
+ maSourceOffset,
+ maSourceOffset + maRect.maSize ),
+ *mpBuffer );
+ }
+
+ private:
+ Page* mpPage;
+ SurfaceRect maRect;
+ std::shared_ptr<IColorBuffer> mpBuffer;
+ ::basegfx::B2IPoint maSourceOffset;
+ };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/tools/pagemanager.cxx b/canvas/source/tools/pagemanager.cxx
new file mode 100644
index 000000000..c1e874ca4
--- /dev/null
+++ b/canvas/source/tools/pagemanager.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 "pagemanager.hxx"
+
+namespace canvas
+{
+ FragmentSharedPtr PageManager::allocateSpace( const ::basegfx::B2ISize& rSize )
+ {
+ // we are asked to find a location for the requested size.
+ // first we try to satisfy the request from the
+ // remaining space in the existing pages.
+ for( const auto& pPage : maPages )
+ {
+ FragmentSharedPtr pFragment( pPage->allocateSpace(rSize) );
+ if(pFragment)
+ {
+ // the page created a new fragment, since we maybe want
+ // to consolidate sparse pages we keep a reference to
+ // the fragment.
+ maFragments.push_back(pFragment);
+ return pFragment;
+ }
+ }
+
+ // otherwise try to create a new page and allocate space there...
+ PageSharedPtr pPage = std::make_shared<Page>(mpRenderModule);
+ if(pPage->isValid())
+ {
+ maPages.push_back(pPage);
+ FragmentSharedPtr pFragment(pPage->allocateSpace(rSize));
+ if (pFragment)
+ maFragments.push_back(pFragment);
+ return pFragment;
+ }
+
+ // the rendermodule failed to create a new page [maybe out
+ // of videomemory], and all other pages could not take
+ // the new request. we decide to create a 'naked' fragment
+ // which will receive its location later.
+ FragmentSharedPtr pFragment = std::make_shared<PageFragment>(rSize);
+ maFragments.push_back(pFragment);
+ return pFragment;
+ }
+
+ void PageManager::free( const FragmentSharedPtr& pFragment )
+ {
+ // erase the reference to the given fragment from our
+ // internal container.
+ FragmentContainer_t::iterator it(
+ std::remove(
+ maFragments.begin(),maFragments.end(),pFragment));
+ maFragments.erase(it,maFragments.end());
+
+ // let the fragment itself know about it...
+ // we need to pass 'this' as argument since the fragment
+ // needs to pass this to the page and can't create
+ // shared_ptr from itself...
+ pFragment->free(pFragment);
+ }
+
+ void PageManager::nakedFragment( const FragmentSharedPtr& pFragment )
+ {
+ if(maPages.empty())
+ return;
+
+ // okay, one last chance is left, we try all available
+ // pages again. maybe some other fragment was deleted
+ // and we can exploit the space.
+ while( !( relocate( pFragment ) ) )
+ {
+ // no way, we need to free up some space...
+ // TODO(F1): this is a heuristic, could
+ // be designed as a policy.
+ auto aEnd( maFragments.cend() );
+ auto aCurrMax( aEnd );
+ sal_uInt32 nCurrMaxArea = 0;
+ for( auto aCurr = maFragments.begin(); aCurr != aEnd; ++aCurr )
+ {
+ if( *aCurr && !( ( *aCurr )->isNaked() ) )
+ {
+ const ::basegfx::B2ISize& rSize( ( *aCurr )->getSize() );
+ sal_uInt32 nArea( rSize.getX() * rSize.getY() );
+
+ if( nCurrMaxArea < nArea )
+ {
+ aCurrMax = aCurr;
+ nCurrMaxArea = nArea;
+ }
+ }
+ }
+ // this does not erase the candidate,
+ // but makes it 'naked'...
+ if( aCurrMax != aEnd )
+ ( *aCurrMax )->free( *aCurrMax );
+ else
+ break;
+ }
+ }
+
+ bool PageManager::relocate( const FragmentSharedPtr& pFragment )
+ {
+ // the fragment passed as argument is assumed to
+ // be naked, that is it is not located on any page.
+ // we try all available pages again, maybe some
+ // other fragment was deleted and we can exploit the space.
+ for( const auto& pPage : maPages )
+ {
+ // if the page at hand takes the fragment, we immediately
+ // call select() to pull the information from the associated
+ // image to the hardware surface.
+ if( pPage->nakedFragment( pFragment ) )
+ {
+ // dirty, since newly allocated.
+ pFragment->select(true);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ void PageManager::validatePages()
+ {
+ for( const auto& rPagePtr : maPages )
+ rPagePtr->validate();
+ }
+
+ ::basegfx::B2ISize PageManager::getPageSize() const
+ {
+ return mpRenderModule->getPageSize();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/tools/pagemanager.hxx b/canvas/source/tools/pagemanager.hxx
new file mode 100644
index 000000000..57de912a1
--- /dev/null
+++ b/canvas/source/tools/pagemanager.hxx
@@ -0,0 +1,76 @@
+/* -*- 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/b2isize.hxx>
+#include <rendering/irendermodule.hxx>
+#include <utility>
+
+#include "page.hxx"
+
+namespace canvas
+{
+ // PageManager
+ class PageManager
+ {
+ public:
+ explicit PageManager(std::shared_ptr<canvas::IRenderModule> xRenderModule)
+ : mpRenderModule(std::move(xRenderModule))
+ {
+ }
+
+ // returns the maximum size of a hardware
+ // accelerated page, e.g. OpenGL texture.
+ ::basegfx::B2ISize getPageSize() const;
+
+ const std::shared_ptr<canvas::IRenderModule>& getRenderModule() const { return mpRenderModule; }
+
+ FragmentSharedPtr allocateSpace( const ::basegfx::B2ISize& rSize );
+ void free( const FragmentSharedPtr& pFragment );
+
+ void nakedFragment( const FragmentSharedPtr& pFragment );
+
+ void validatePages();
+
+ private:
+ // the pagemanager needs access to the rendermodule
+ // since we query for system resources from it.
+ std::shared_ptr<canvas::IRenderModule> mpRenderModule;
+
+ // here we collect all fragments that will be created
+ // since we need them for relocation purposes.
+ typedef std::vector<FragmentSharedPtr> FragmentContainer_t;
+ FragmentContainer_t maFragments;
+
+ // this is the container holding all created pages,
+ // behind the scenes these are real hardware surfaces.
+ std::vector<PageSharedPtr> maPages;
+
+ bool relocate( const FragmentSharedPtr& pFragment );
+ };
+
+
+ // PageManagerSharedPtr
+
+
+ typedef std::shared_ptr< PageManager > PageManagerSharedPtr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/tools/parametricpolypolygon.cxx b/canvas/source/tools/parametricpolypolygon.cxx
new file mode 100644
index 000000000..acad3b58b
--- /dev/null
+++ b/canvas/source/tools/parametricpolypolygon.cxx
@@ -0,0 +1,238 @@
+/* -*- 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/point/b2dpoint.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/range/b2drectangle.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+#include <com/sun/star/rendering/XGraphicDevice.hpp>
+
+#include <parametricpolypolygon.hxx>
+#include <utility>
+
+using namespace ::com::sun::star;
+
+namespace canvas
+{
+ uno::Sequence<OUString> ParametricPolyPolygon::getAvailableServiceNames()
+ {
+ return {"LinearGradient",
+ "EllipticalGradient",
+ "RectangularGradient"};
+ }
+
+ rtl::Reference<ParametricPolyPolygon> ParametricPolyPolygon::create(
+ const uno::Reference< rendering::XGraphicDevice >& rDevice,
+ std::u16string_view rServiceName,
+ const uno::Sequence< uno::Any >& rArgs )
+ {
+ double fAspectRatio=1.0;
+
+ // defaults
+ uno::Sequence< uno::Sequence< double > > colorSequence{
+ rDevice->getDeviceColorSpace()->convertFromRGB({ rendering::RGBColor(0,0,0) }),
+ rDevice->getDeviceColorSpace()->convertFromRGB({ rendering::RGBColor(1,1,1) })
+ };
+ uno::Sequence< double > colorStops{ 0, 1 };
+
+ // extract args
+ for( const uno::Any& rArg : rArgs )
+ {
+ beans::PropertyValue aProp;
+ if( rArg >>= aProp )
+ {
+ if ( aProp.Name == "Colors" )
+ {
+ aProp.Value >>= colorSequence;
+ }
+ else if ( aProp.Name == "Stops" )
+ {
+ aProp.Value >>= colorStops;
+ }
+ else if ( aProp.Name == "AspectRatio" )
+ {
+ aProp.Value >>= fAspectRatio;
+ }
+ }
+ }
+
+ if ( rServiceName == u"LinearGradient" )
+ {
+ return createLinearHorizontalGradient(rDevice, colorSequence, colorStops);
+ }
+ else if ( rServiceName == u"EllipticalGradient" )
+ {
+ return createEllipticalGradient(rDevice, colorSequence, colorStops, fAspectRatio);
+ }
+ else if ( rServiceName == u"RectangularGradient" )
+ {
+ return createRectangularGradient(rDevice, colorSequence, colorStops, fAspectRatio);
+ }
+ else if ( rServiceName == u"VerticalLineHatch" )
+ {
+ // TODO: NYI
+ }
+ else if ( rServiceName == u"OrthogonalLinesHatch" )
+ {
+ // TODO: NYI
+ }
+ else if ( rServiceName == u"ThreeCrossingLinesHatch" )
+ {
+ // TODO: NYI
+ }
+ else if ( rServiceName == u"FourCrossingLinesHatch" )
+ {
+ // TODO: NYI
+ }
+
+ return nullptr;
+ }
+
+ rtl::Reference<ParametricPolyPolygon> ParametricPolyPolygon::createLinearHorizontalGradient(
+ const uno::Reference< rendering::XGraphicDevice >& rDevice,
+ const uno::Sequence< uno::Sequence< double > >& colors,
+ const uno::Sequence< double >& stops )
+ {
+ // TODO(P2): hold gradient brush statically, and only setup
+ // the colors
+ return new ParametricPolyPolygon( rDevice, GradientType::Linear, colors, stops );
+ }
+
+ rtl::Reference<ParametricPolyPolygon> ParametricPolyPolygon::createEllipticalGradient(
+ const uno::Reference< rendering::XGraphicDevice >& rDevice,
+ const uno::Sequence< uno::Sequence< double > >& colors,
+ const uno::Sequence< double >& stops,
+ double fAspectRatio )
+ {
+ // TODO(P2): hold gradient polygon statically, and only setup
+ // the colors
+ return new ParametricPolyPolygon(
+ rDevice,
+ ::basegfx::utils::createPolygonFromCircle(
+ ::basegfx::B2DPoint(0,0), 1 ),
+ GradientType::Elliptical,
+ colors, stops, fAspectRatio );
+ }
+
+ rtl::Reference<ParametricPolyPolygon> ParametricPolyPolygon::createRectangularGradient( const uno::Reference< rendering::XGraphicDevice >& rDevice,
+ const uno::Sequence< uno::Sequence< double > >& colors,
+ const uno::Sequence< double >& stops,
+ double fAspectRatio )
+ {
+ // TODO(P2): hold gradient polygon statically, and only setup
+ // the colors
+ return new ParametricPolyPolygon(
+ rDevice,
+ ::basegfx::utils::createPolygonFromRect(
+ ::basegfx::B2DRectangle( -1, -1, 1, 1 ) ),
+ GradientType::Rectangular,
+ colors, stops, fAspectRatio );
+ }
+
+ void ParametricPolyPolygon::disposing(std::unique_lock<std::mutex>&)
+ {
+ mxDevice.clear();
+ }
+
+ uno::Reference< rendering::XPolyPolygon2D > SAL_CALL ParametricPolyPolygon::getOutline( double /*t*/ )
+ {
+ // TODO(F1): outline NYI
+ return uno::Reference< rendering::XPolyPolygon2D >();
+ }
+
+ uno::Sequence< double > SAL_CALL ParametricPolyPolygon::getColor( double /*t*/ )
+ {
+ // TODO(F1): color NYI
+ return uno::Sequence< double >();
+ }
+
+ uno::Sequence< double > SAL_CALL ParametricPolyPolygon::getPointColor( const geometry::RealPoint2D& /*point*/ )
+ {
+ // TODO(F1): point color NYI
+ return uno::Sequence< double >();
+ }
+
+ uno::Reference< rendering::XColorSpace > SAL_CALL ParametricPolyPolygon::getColorSpace()
+ {
+ std::unique_lock aGuard( m_aMutex );
+
+ return mxDevice.is() ? mxDevice->getDeviceColorSpace() : uno::Reference< rendering::XColorSpace >();
+ }
+
+
+ OUString SAL_CALL ParametricPolyPolygon::getImplementationName( )
+ {
+ return "Canvas::ParametricPolyPolygon";
+ }
+
+ sal_Bool SAL_CALL ParametricPolyPolygon::supportsService( const OUString& ServiceName )
+ {
+ return cppu::supportsService(this, ServiceName);
+ }
+
+ uno::Sequence< OUString > SAL_CALL ParametricPolyPolygon::getSupportedServiceNames( )
+ {
+ return { "com.sun.star.rendering.ParametricPolyPolygon" };
+ }
+
+ ParametricPolyPolygon::~ParametricPolyPolygon()
+ {
+ }
+
+ ParametricPolyPolygon::ParametricPolyPolygon( uno::Reference< rendering::XGraphicDevice > xDevice,
+ const ::basegfx::B2DPolygon& rGradientPoly,
+ GradientType eType,
+ const uno::Sequence< uno::Sequence< double > >& rColors,
+ const uno::Sequence< double >& rStops,
+ double nAspectRatio ) :
+ mxDevice(std::move( xDevice )),
+ maValues( rGradientPoly,
+ rColors,
+ rStops,
+ nAspectRatio,
+ eType )
+ {
+ }
+
+ ParametricPolyPolygon::ParametricPolyPolygon( uno::Reference< rendering::XGraphicDevice > xDevice,
+ GradientType eType,
+ const uno::Sequence< uno::Sequence< double > >& rColors,
+ const uno::Sequence< double >& rStops ) :
+ mxDevice(std::move( xDevice )),
+ maValues( ::basegfx::B2DPolygon(),
+ rColors,
+ rStops,
+ 1.0,
+ eType )
+ {
+ }
+
+ ParametricPolyPolygon::Values ParametricPolyPolygon::getValues() const
+ {
+ std::unique_lock aGuard( m_aMutex );
+
+ return maValues;
+ }
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/tools/propertysethelper.cxx b/canvas/source/tools/propertysethelper.cxx
new file mode 100644
index 000000000..24a07cb7d
--- /dev/null
+++ b/canvas/source/tools/propertysethelper.cxx
@@ -0,0 +1,156 @@
+/* -*- 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 <string_view>
+
+#include <propertysethelper.hxx>
+#include <com/sun/star/beans/PropertyVetoException.hpp>
+#include <com/sun/star/beans/UnknownPropertyException.hpp>
+
+using namespace ::com::sun::star;
+
+namespace canvas
+{
+ namespace
+ {
+ void throwUnknown( std::u16string_view aPropertyName )
+ {
+ throw beans::UnknownPropertyException(
+ OUString::Concat("PropertySetHelper: property ") +
+ aPropertyName + " not found."
+ );
+ }
+
+ void throwVeto( std::u16string_view aPropertyName )
+ {
+ throw beans::PropertyVetoException(
+ OUString::Concat("PropertySetHelper: property ") +
+ aPropertyName + " access was vetoed." );
+ }
+
+ struct EntryComparator
+ {
+ bool operator()( const PropertySetHelper::MapType::MapEntry& rLHS,
+ const PropertySetHelper::MapType::MapEntry& rRHS )
+ {
+ return strcmp( rLHS.maKey,
+ rRHS.maKey ) < 0;
+ }
+ };
+ }
+
+ PropertySetHelper::PropertySetHelper()
+ {
+ }
+
+ void PropertySetHelper::initProperties( InputMap&& rMap )
+ {
+ mpMap.reset();
+ maMapEntries = std::move(rMap);
+
+ std::sort( maMapEntries.begin(),
+ maMapEntries.end(),
+ EntryComparator() );
+
+ if( !maMapEntries.empty() )
+ mpMap.reset( new MapType(maMapEntries.data(),
+ maMapEntries.size(),
+ true) );
+ }
+
+ void PropertySetHelper::addProperties( const InputMap& rMap )
+ {
+ InputMap aMerged( maMapEntries );
+ aMerged.insert( aMerged.end(),
+ rMap.begin(),
+ rMap.end() );
+
+ initProperties( std::move(aMerged) );
+ }
+
+ bool PropertySetHelper::isPropertyName( const OUString& aPropertyName ) const
+ {
+ if (!mpMap)
+ return false;
+
+ Callbacks aDummy;
+ return mpMap->lookup( aPropertyName,
+ aDummy );
+ }
+
+ uno::Reference< beans::XPropertySetInfo > PropertySetHelper::getPropertySetInfo() const
+ {
+ // we're a stealth property set
+ return uno::Reference< beans::XPropertySetInfo >();
+ }
+
+ void PropertySetHelper::setPropertyValue( const OUString& aPropertyName,
+ const uno::Any& aValue )
+ {
+ Callbacks aCallbacks;
+ if (!mpMap || !mpMap->lookup(aPropertyName, aCallbacks))
+ {
+ throwUnknown( aPropertyName );
+ }
+
+ if (!aCallbacks.setter)
+ throwVeto( aPropertyName );
+
+ aCallbacks.setter(aValue);
+ }
+
+ uno::Any PropertySetHelper::getPropertyValue( const OUString& aPropertyName ) const
+ {
+ Callbacks aCallbacks;
+ if (!mpMap || !mpMap->lookup(aPropertyName, aCallbacks))
+ {
+ throwUnknown( aPropertyName );
+ }
+
+ if (aCallbacks.getter)
+ return aCallbacks.getter();
+
+ // TODO(Q1): subtlety, empty getter method silently returns
+ // the empty any
+ return uno::Any();
+ }
+
+ void PropertySetHelper::addPropertyChangeListener( const OUString& aPropertyName,
+ const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/ )
+ {
+ // check validity of property, but otherwise ignore the
+ // request
+ if( !isPropertyName( aPropertyName ) )
+ throwUnknown( aPropertyName );
+ }
+
+ void PropertySetHelper::addVetoableChangeListener( const OUString& aPropertyName,
+ const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/ )
+ {
+ // check validity of property, but otherwise ignore the
+ // request
+ if( !isPropertyName( aPropertyName ) )
+ throwUnknown( aPropertyName );
+ }
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/tools/spriteredrawmanager.cxx b/canvas/source/tools/spriteredrawmanager.cxx
new file mode 100644
index 000000000..d60646484
--- /dev/null
+++ b/canvas/source/tools/spriteredrawmanager.cxx
@@ -0,0 +1,483 @@
+/* -*- 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 <basegfx/range/b2drectangle.hxx>
+#include <basegfx/utils/canvastools.hxx>
+#include <tools/diagnose_ex.h>
+#include <sal/log.hxx>
+
+#include <spriteredrawmanager.hxx>
+#include <boost/range/adaptor/reversed.hpp>
+#include <utility>
+
+namespace canvas
+{
+ namespace
+ {
+ /** Helper class to condense sprite updates into a single action
+
+ This class tracks the sprite changes over the recorded
+ change list, and generates a single update action from
+ that (note that per screen update, several moves,
+ visibility changes and content updates might happen)
+ */
+ class SpriteTracer
+ {
+ public:
+ explicit SpriteTracer( Sprite::Reference rAffectedSprite ) :
+ mpAffectedSprite(std::move(rAffectedSprite)),
+ mbIsMove( false ),
+ mbIsGenericUpdate( false )
+ {
+ }
+
+ void operator()( const SpriteRedrawManager::SpriteChangeRecord& rSpriteRecord )
+ {
+ // only deal with change events from the currently
+ // affected sprite
+ if( rSpriteRecord.mpAffectedSprite != mpAffectedSprite )
+ return;
+
+ switch( rSpriteRecord.meChangeType )
+ {
+ case SpriteRedrawManager::SpriteChangeRecord::ChangeType::move:
+ if( !mbIsMove )
+ {
+ // no move yet - this must be the first one
+ maMoveStartArea = ::basegfx::B2DRectangle(
+ rSpriteRecord.maOldPos,
+ rSpriteRecord.maOldPos + rSpriteRecord.maUpdateArea.getRange() );
+ mbIsMove = true;
+ }
+
+ maMoveEndArea = rSpriteRecord.maUpdateArea;
+ break;
+
+ case SpriteRedrawManager::SpriteChangeRecord::ChangeType::update:
+ // update end update area of the
+ // sprite. Thus, every update() action
+ // _after_ the last move will correctly
+ // update the final repaint area. And this
+ // does not interfere with subsequent
+ // moves, because moves always perform a
+ // hard set of maMoveEndArea to their
+ // stored value
+ maMoveEndArea.expand( rSpriteRecord.maUpdateArea );
+ mbIsGenericUpdate = true;
+ break;
+
+ default:
+ ENSURE_OR_THROW( false,
+ "Unexpected case in SpriteUpdater::operator()" );
+ break;
+ }
+ }
+
+ void commit( SpriteRedrawManager::SpriteConnectedRanges& rUpdateCollector ) const
+ {
+ if( mbIsMove )
+ {
+ if( !maMoveStartArea.isEmpty() ||
+ !maMoveEndArea.isEmpty() )
+ {
+ // if mbIsGenericUpdate is false, this is a
+ // pure move (i.e. no other update
+ // operations). Pass that information on to
+ // the SpriteInfo
+ const bool bIsPureMove( !mbIsGenericUpdate );
+
+ // ignore the case that start and end update
+ // area overlap - the b2dconnectedranges
+ // handle that, anyway. doing it this way
+ // ensures that we have both old and new area
+ // stored
+
+ // round all given range up to enclosing
+ // integer rectangle - since the whole thing
+ // here is about
+
+ // first, draw the new sprite position
+ rUpdateCollector.addRange(
+ ::basegfx::unotools::b2DSurroundingIntegerRangeFromB2DRange( maMoveEndArea ),
+ SpriteRedrawManager::SpriteInfo(
+ mpAffectedSprite,
+ maMoveEndArea,
+ true,
+ bIsPureMove ) );
+
+ // then, clear the old place (looks smoother
+ // this way)
+ rUpdateCollector.addRange(
+ ::basegfx::unotools::b2DSurroundingIntegerRangeFromB2DRange( maMoveStartArea ),
+ SpriteRedrawManager::SpriteInfo(
+ Sprite::Reference(),
+ maMoveStartArea,
+ true,
+ bIsPureMove ) );
+ }
+ }
+ else if( mbIsGenericUpdate &&
+ !maMoveEndArea.isEmpty() )
+ {
+ rUpdateCollector.addRange(
+ ::basegfx::unotools::b2DSurroundingIntegerRangeFromB2DRange( maMoveEndArea ),
+ SpriteRedrawManager::SpriteInfo(
+ mpAffectedSprite,
+ maMoveEndArea,
+ true ) );
+ }
+ }
+
+ private:
+ Sprite::Reference mpAffectedSprite;
+ ::basegfx::B2DRectangle maMoveStartArea;
+ ::basegfx::B2DRectangle maMoveEndArea;
+
+ /// True, if at least one move was encountered
+ bool mbIsMove;
+
+ /// True, if at least one generic update was encountered
+ bool mbIsGenericUpdate;
+ };
+
+
+ /** SpriteChecker functor, which for every sprite checks the
+ given update vector for necessary screen updates
+ */
+ class SpriteUpdater
+ {
+ public:
+ /** Generate update area list
+
+ @param rUpdater
+ Reference to an updater object, which will receive the
+ update areas.
+
+ @param rChangeContainer
+ Container with all sprite change requests
+
+ */
+ SpriteUpdater( SpriteRedrawManager::SpriteConnectedRanges& rUpdater,
+ const SpriteRedrawManager::VectorOfChangeRecords& rChangeContainer ) :
+ mrUpdater( rUpdater ),
+ mrChangeContainer( rChangeContainer )
+ {
+ }
+
+ /** Call this method for every sprite on your screen
+
+ This method scans the change container, collecting all
+ update info for the given sprite into one or two
+ update operations, which in turn are inserted into the
+ connected ranges processor.
+
+ @param rSprite
+ Current sprite to collect update info for.
+ */
+ void operator()( const Sprite::Reference& rSprite )
+ {
+ SpriteTracer aSpriteTracer( rSprite );
+
+ for (auto const& aChange : mrChangeContainer)
+ aSpriteTracer( aChange );
+
+ aSpriteTracer.commit( mrUpdater );
+ }
+
+ private:
+ SpriteRedrawManager::SpriteConnectedRanges& mrUpdater;
+ const SpriteRedrawManager::VectorOfChangeRecords& mrChangeContainer;
+ };
+ }
+
+ void SpriteRedrawManager::setupUpdateAreas( SpriteConnectedRanges& rUpdateAreas ) const
+ {
+ // TODO(T3): This is NOT thread safe at all. This only works
+ // under the assumption that NOBODY changes ANYTHING
+ // concurrently, while this method is on the stack. We should
+ // really rework the canvas::Sprite interface, in such a way
+ // that it dumps ALL its state with a single, atomic
+ // call. Then, we store that state locally. This prolly goes
+ // in line with the problem of having sprite state available
+ // for the frame before the last frame; plus, it avoids
+ // frequent locks of the object mutexes
+ SpriteWeakOrder aSpriteComparator;
+
+ // put all sprites that have changed content into update areas
+ for( const auto& pSprite : maSprites )
+ {
+ if( pSprite->isContentChanged() )
+ const_cast< SpriteRedrawManager* >( this )->updateSprite( pSprite,
+ pSprite->getPosPixel(),
+ pSprite->getUpdateArea() );
+ }
+
+ // sort sprites after prio
+ VectorOfSprites aSortedSpriteVector( maSprites.begin(), maSprites.end() );
+ std::sort( aSortedSpriteVector.begin(),
+ aSortedSpriteVector.end(),
+ aSpriteComparator );
+
+ // extract all referenced sprites from the maChangeRecords
+ // (copy sprites, make the list unique, regarding the
+ // sprite pointer). This assumes that, until this scope
+ // ends, nobody changes the maChangeRecords vector!
+ VectorOfSprites aUpdatableSprites;
+ for( const auto& rChangeRecord : maChangeRecords )
+ {
+ const Sprite::Reference& rSprite( rChangeRecord.getSprite() );
+ if( rSprite.is() )
+ aUpdatableSprites.push_back( rSprite );
+ }
+
+ std::sort( aUpdatableSprites.begin(),
+ aUpdatableSprites.end(),
+ aSpriteComparator );
+
+ VectorOfSprites::iterator aEnd=
+ std::unique( aUpdatableSprites.begin(),
+ aUpdatableSprites.end() );
+
+ // for each unique sprite, check the change event vector,
+ // calculate the update operation from that, and add the
+ // result to the aUpdateArea.
+ std::for_each( aUpdatableSprites.begin(),
+ aEnd,
+ SpriteUpdater( rUpdateAreas,
+ maChangeRecords) );
+
+ // TODO(P2): Implement your own output iterator adapter, to
+ // avoid that totally superfluous temp aUnchangedSprites
+ // vector.
+
+ // add all sprites to rUpdateAreas, that are _not_ already
+ // contained in the uniquified vector of changed ones
+ // (i.e. the difference between aSortedSpriteVector and
+ // aUpdatableSprites).
+ VectorOfSprites aUnchangedSprites;
+ std::set_difference( aSortedSpriteVector.begin(),
+ aSortedSpriteVector.end(),
+ aUpdatableSprites.begin(),
+ aEnd,
+ std::back_insert_iterator< VectorOfSprites >(aUnchangedSprites),
+ aSpriteComparator );
+
+ // add each remaining unchanged sprite to connected ranges,
+ // marked as "don't need update"
+ for( const auto& pUnchangedSprite : aUnchangedSprites )
+ {
+ const ::basegfx::B2DRange& rUpdateArea( pUnchangedSprite->getUpdateArea() );
+ rUpdateAreas.addRange(
+ ::basegfx::unotools::b2DSurroundingIntegerRangeFromB2DRange( rUpdateArea ),
+ SpriteInfo( pUnchangedSprite,
+ rUpdateArea,
+ false ) );
+ }
+ }
+
+#if OSL_DEBUG_LEVEL > 0
+ static bool impIsEqualB2DRange(const basegfx::B2DRange& rRangeA, const basegfx::B2DRange& rRangeB, double fSmallValue)
+ {
+ return fabs(rRangeB.getMinX() - rRangeA.getMinX()) <= fSmallValue
+ && fabs(rRangeB.getMinY() - rRangeA.getMinY()) <= fSmallValue
+ && fabs(rRangeB.getMaxX() - rRangeA.getMaxX()) <= fSmallValue
+ && fabs(rRangeB.getMaxY() - rRangeA.getMaxY()) <= fSmallValue;
+ }
+
+ static bool impIsEqualB2DVector(const basegfx::B2DVector& rVecA, const basegfx::B2DVector& rVecB, double fSmallValue)
+ {
+ return fabs(rVecB.getX() - rVecA.getX()) <= fSmallValue
+ && fabs(rVecB.getY() - rVecA.getY()) <= fSmallValue;
+ }
+#endif
+
+ bool SpriteRedrawManager::isAreaUpdateScroll( ::basegfx::B2DRectangle& o_rMoveStart,
+ ::basegfx::B2DRectangle& o_rMoveEnd,
+ const UpdateArea& rUpdateArea,
+ std::size_t nNumSprites ) const
+ {
+ // check for a solitary move, which consists of exactly two
+ // pure-move entries, the first with valid, the second with
+ // invalid sprite (see SpriteTracer::commit()). Note that we
+ // cannot simply store some flag in SpriteTracer::commit()
+ // above and just check that here, since during the connected
+ // range calculations, other sprites might get merged into the
+ // same region (thus spoiling the scrolling move
+ // optimization).
+ if( nNumSprites != 2 )
+ return false;
+
+ const SpriteConnectedRanges::ComponentListType::const_iterator aFirst(
+ rUpdateArea.maComponentList.begin() );
+ SpriteConnectedRanges::ComponentListType::const_iterator aSecond(
+ aFirst );
+ ++aSecond;
+
+ if( !aFirst->second.isPureMove() ||
+ !aSecond->second.isPureMove() ||
+ !aFirst->second.getSprite().is() ||
+ // use _true_ update area, not the rounded version
+ !aFirst->second.getSprite()->isAreaUpdateOpaque( aFirst->second.getUpdateArea() ) ||
+ aSecond->second.getSprite().is() )
+ {
+ // either no move update, or incorrect sprite, or sprite
+ // content not fully opaque over update region.
+ return false;
+ }
+
+ o_rMoveStart = aSecond->second.getUpdateArea();
+ o_rMoveEnd = aFirst->second.getUpdateArea();
+
+#if OSL_DEBUG_LEVEL > 0
+ ::basegfx::B2DRectangle aTotalBounds( o_rMoveStart );
+ aTotalBounds.expand( o_rMoveEnd );
+
+ SAL_WARN_IF(!impIsEqualB2DRange(rUpdateArea.maTotalBounds, basegfx::unotools::b2DSurroundingIntegerRangeFromB2DRange(aTotalBounds), 0.5),
+ "canvas",
+ "SpriteRedrawManager::isAreaUpdateScroll(): sprite area and total area mismatch");
+ SAL_WARN_IF(!impIsEqualB2DVector(o_rMoveStart.getRange(), o_rMoveEnd.getRange(), 0.5),
+ "canvas",
+ "SpriteRedrawManager::isAreaUpdateScroll(): scroll start and end area have mismatching size");
+#endif
+
+ return true;
+ }
+
+ bool SpriteRedrawManager::isAreaUpdateNotOpaque( const ::basegfx::B2DRectangle& rUpdateRect,
+ const AreaComponent& rComponent ) const
+ {
+ const Sprite::Reference& pAffectedSprite( rComponent.second.getSprite() );
+
+ if( !pAffectedSprite.is() )
+ return true; // no sprite, no opaque update!
+
+ return !pAffectedSprite->isAreaUpdateOpaque( rUpdateRect );
+ }
+
+ bool SpriteRedrawManager::isAreaUpdateOpaque( const UpdateArea& rUpdateArea,
+ std::size_t nNumSprites ) const
+ {
+ // check whether the sprites in the update area's list will
+ // fully cover the given area _and_ do that in an opaque way
+ // (i.e. no alpha, no non-rectangular sprite content).
+
+ // TODO(P1): Come up with a smarter early-exit criterion here
+ // (though, I think, the case that _lots_ of sprites _fully_
+ // cover a rectangular area _without_ any holes is extremely
+ // improbable)
+
+ // avoid checking large number of sprites (and probably fail,
+ // anyway). Note: the case nNumSprites < 1 should normally not
+ // happen, as handleArea() calls backgroundPaint() then.
+ if( nNumSprites > 3 || nNumSprites < 1 )
+ return false;
+
+ // now, calc the _true_ update area, by merging all sprite's
+ // true update areas into one rectangle
+ ::basegfx::B2DRange aTrueArea( rUpdateArea.maComponentList.begin()->second.getUpdateArea() );
+ for( const auto& rArea : rUpdateArea.maComponentList )
+ aTrueArea.expand(rArea.second.getUpdateArea());
+
+ const SpriteConnectedRanges::ComponentListType::const_iterator aEnd(
+ rUpdateArea.maComponentList.end() );
+
+ // and check whether _any_ of the sprites tells that its area
+ // update will not be opaque.
+ return std::none_of( rUpdateArea.maComponentList.begin(),
+ aEnd,
+ [&aTrueArea, this]( const std::pair< ::basegfx::B2DRange, SpriteInfo >& cp )
+ { return this->isAreaUpdateNotOpaque(aTrueArea, cp); } );
+ }
+
+ bool SpriteRedrawManager::areSpritesChanged( const UpdateArea& rUpdateArea ) const
+ {
+ // check whether SpriteInfo::needsUpdate returns false for
+ // all elements of this area's contained sprites
+
+ // if not a single changed sprite found - just ignore this
+ // component (return false)
+ const SpriteConnectedRanges::ComponentListType::const_iterator aEnd(
+ rUpdateArea.maComponentList.end() );
+ return std::any_of( rUpdateArea.maComponentList.begin(),
+ aEnd,
+ []( const std::pair< ::basegfx::B2DRange, SpriteInfo >& cp )
+ { return cp.second.needsUpdate(); } );
+ }
+
+ SpriteRedrawManager::SpriteRedrawManager()
+ {
+ }
+
+ void SpriteRedrawManager::disposing()
+ {
+ // drop all references
+ maChangeRecords.clear();
+
+ // dispose all sprites - the spritecanvas, and by delegation,
+ // this object, is the owner of the sprites. After all, a
+ // sprite without a canvas to render into makes not terribly
+ // much sense.
+ for( const auto& rCurr : boost::adaptors::reverse(maSprites) )
+ rCurr->dispose();
+
+ maSprites.clear();
+ }
+
+ void SpriteRedrawManager::clearChangeRecords()
+ {
+ maChangeRecords.clear();
+ }
+
+ void SpriteRedrawManager::showSprite( const Sprite::Reference& rSprite )
+ {
+ maSprites.push_back( rSprite );
+ }
+
+ void SpriteRedrawManager::hideSprite( const Sprite::Reference& rSprite )
+ {
+ maSprites.erase(std::remove(maSprites.begin(), maSprites.end(), rSprite), maSprites.end());
+ }
+
+ void SpriteRedrawManager::moveSprite( const Sprite::Reference& rSprite,
+ const ::basegfx::B2DPoint& rOldPos,
+ const ::basegfx::B2DPoint& rNewPos,
+ const ::basegfx::B2DVector& rSpriteSize )
+ {
+ maChangeRecords.emplace_back( rSprite,
+ rOldPos,
+ rNewPos,
+ rSpriteSize );
+ }
+
+ void SpriteRedrawManager::updateSprite( const Sprite::Reference& rSprite,
+ const ::basegfx::B2DPoint& rPos,
+ const ::basegfx::B2DRange& rUpdateArea )
+ {
+ maChangeRecords.emplace_back( rSprite,
+ rPos,
+ rUpdateArea );
+ }
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/tools/surface.cxx b/canvas/source/tools/surface.cxx
new file mode 100644
index 000000000..9dfb0f7c9
--- /dev/null
+++ b/canvas/source/tools/surface.cxx
@@ -0,0 +1,437 @@
+/* -*- 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/matrix/b2dhommatrixtools.hxx>
+#include <basegfx/polygon/b2dpolygonclipper.hxx>
+#include <comphelper/scopeguard.hxx>
+#include <utility>
+
+#include "surface.hxx"
+
+namespace canvas
+{
+ Surface::Surface( PageManagerSharedPtr rPageManager,
+ std::shared_ptr<IColorBuffer> xColorBuffer,
+ const ::basegfx::B2IPoint& rPos,
+ const ::basegfx::B2ISize& rSize ) :
+ mpColorBuffer(std::move(xColorBuffer)),
+ mpPageManager(std::move(rPageManager)),
+ maSourceOffset(rPos),
+ maSize(rSize),
+ mbIsDirty(true)
+ {
+ }
+
+ Surface::~Surface()
+ {
+ if(mpFragment)
+ mpPageManager->free(mpFragment);
+ }
+
+ void Surface::setColorBufferDirty()
+ {
+ mbIsDirty=true;
+ }
+
+ basegfx::B2DRectangle Surface::getUVCoords() const
+ {
+ ::basegfx::B2ISize aPageSize(mpPageManager->getPageSize());
+ ::basegfx::B2IPoint aDestOffset;
+ if( mpFragment )
+ aDestOffset = mpFragment->getPos();
+
+ const double pw( aPageSize.getX() );
+ const double ph( aPageSize.getY() );
+ const double ox( aDestOffset.getX() );
+ const double oy( aDestOffset.getY() );
+ const double sx( maSize.getX() );
+ const double sy( maSize.getY() );
+
+ return ::basegfx::B2DRectangle( ox/pw,
+ oy/ph,
+ (ox+sx)/pw,
+ (oy+sy)/ph );
+ }
+
+ basegfx::B2DRectangle Surface::getUVCoords( const ::basegfx::B2IPoint& rPos,
+ const ::basegfx::B2ISize& rSize ) const
+ {
+ ::basegfx::B2ISize aPageSize(mpPageManager->getPageSize());
+
+ const double pw( aPageSize.getX() );
+ const double ph( aPageSize.getY() );
+ const double ox( rPos.getX() );
+ const double oy( rPos.getY() );
+ const double sx( rSize.getX() );
+ const double sy( rSize.getY() );
+
+ return ::basegfx::B2DRectangle( ox/pw,
+ oy/ph,
+ (ox+sx)/pw,
+ (oy+sy)/ph );
+ }
+
+ bool Surface::draw( double fAlpha,
+ const ::basegfx::B2DPoint& rPos,
+ const ::basegfx::B2DHomMatrix& rTransform )
+ {
+ std::shared_ptr<IRenderModule> pRenderModule(mpPageManager->getRenderModule());
+
+ RenderModuleGuard aGuard( pRenderModule );
+
+ prepareRendering();
+
+ // convert size to normalized device coordinates
+ const ::basegfx::B2DRectangle& rUV( getUVCoords() );
+
+ const double u1(rUV.getMinX());
+ const double v1(rUV.getMinY());
+ const double u2(rUV.getMaxX());
+ const double v2(rUV.getMaxY());
+
+ // concat transforms
+ // 1) offset of surface subarea
+ // 2) surface transform
+ // 3) translation to output position [rPos]
+ // 4) scale to normalized device coordinates
+ // 5) flip y-axis
+ // 6) translate to account for viewport transform
+ basegfx::B2DHomMatrix aTransform(basegfx::utils::createTranslateB2DHomMatrix(
+ maSourceOffset.getX(), maSourceOffset.getY()));
+ aTransform = aTransform * rTransform;
+ aTransform.translate(::basegfx::fround(rPos.getX()),
+ ::basegfx::fround(rPos.getY()));
+
+ /*
+ ######################################
+ ######################################
+ ######################################
+
+ Y
+ ^+1
+ |
+ 2 | 3
+ x------------x
+ | | |
+ | | |
+ ------|-----O------|------>X
+ -1 | | | +1
+ | | |
+ x------------x
+ 1 | 0
+ |
+ |-1
+
+ ######################################
+ ######################################
+ ######################################
+ */
+
+ const ::basegfx::B2DPoint& p0(aTransform * ::basegfx::B2DPoint(maSize.getX(),maSize.getY()));
+ const ::basegfx::B2DPoint& p1(aTransform * ::basegfx::B2DPoint(0.0,maSize.getY()));
+ const ::basegfx::B2DPoint& p2(aTransform * ::basegfx::B2DPoint(0.0,0.0));
+ const ::basegfx::B2DPoint& p3(aTransform * ::basegfx::B2DPoint(maSize.getX(),0.0));
+
+ canvas::Vertex vertex;
+ vertex.r = 1.0f;
+ vertex.g = 1.0f;
+ vertex.b = 1.0f;
+ vertex.a = static_cast<float>(fAlpha);
+ vertex.z = 0.0f;
+
+ {
+ pRenderModule->beginPrimitive( canvas::IRenderModule::PrimitiveType::Quad );
+
+ // issue an endPrimitive() when leaving the scope
+ const ::comphelper::ScopeGuard aScopeGuard(
+ [&pRenderModule]() mutable { pRenderModule->endPrimitive(); } );
+
+ vertex.u=static_cast<float>(u2); vertex.v=static_cast<float>(v2);
+ vertex.x=static_cast<float>(p0.getX()); vertex.y=static_cast<float>(p0.getY());
+ pRenderModule->pushVertex(vertex);
+
+ vertex.u=static_cast<float>(u1); vertex.v=static_cast<float>(v2);
+ vertex.x=static_cast<float>(p1.getX()); vertex.y=static_cast<float>(p1.getY());
+ pRenderModule->pushVertex(vertex);
+
+ vertex.u=static_cast<float>(u1); vertex.v=static_cast<float>(v1);
+ vertex.x=static_cast<float>(p2.getX()); vertex.y=static_cast<float>(p2.getY());
+ pRenderModule->pushVertex(vertex);
+
+ vertex.u=static_cast<float>(u2); vertex.v=static_cast<float>(v1);
+ vertex.x=static_cast<float>(p3.getX()); vertex.y=static_cast<float>(p3.getY());
+ pRenderModule->pushVertex(vertex);
+ }
+
+ return !(pRenderModule->isError());
+ }
+
+ bool Surface::drawRectangularArea(
+ double fAlpha,
+ const ::basegfx::B2DPoint& rPos,
+ const ::basegfx::B2DRectangle& rArea,
+ const ::basegfx::B2DHomMatrix& rTransform )
+ {
+ if( rArea.isEmpty() )
+ return true; // immediate exit for empty area
+
+ std::shared_ptr<IRenderModule> pRenderModule(mpPageManager->getRenderModule());
+
+ RenderModuleGuard aGuard( pRenderModule );
+
+ prepareRendering();
+
+ // these positions are relative to the texture
+ ::basegfx::B2IPoint aPos1(
+ ::basegfx::fround(rArea.getMinimum().getX()),
+ ::basegfx::fround(rArea.getMinimum().getY()));
+ ::basegfx::B2IPoint aPos2(
+ ::basegfx::fround(rArea.getMaximum().getX()),
+ ::basegfx::fround(rArea.getMaximum().getY()) );
+
+ // clip the positions to the area this surface covers
+ aPos1.setX(std::max(aPos1.getX(),maSourceOffset.getX()));
+ aPos1.setY(std::max(aPos1.getY(),maSourceOffset.getY()));
+ aPos2.setX(std::min(aPos2.getX(),maSourceOffset.getX()+maSize.getX()));
+ aPos2.setY(std::min(aPos2.getY(),maSourceOffset.getY()+maSize.getY()));
+
+ // if the resulting area is empty, return immediately
+ ::basegfx::B2IVector aSize(aPos2 - aPos1);
+ if(aSize.getX() <= 0 || aSize.getY() <= 0)
+ return true;
+
+ ::basegfx::B2IPoint aDestOffset;
+ if( mpFragment )
+ aDestOffset = mpFragment->getPos();
+
+ // convert size to normalized device coordinates
+ const ::basegfx::B2DRectangle& rUV(
+ getUVCoords(aPos1 - maSourceOffset + aDestOffset,
+ aSize) );
+ const double u1(rUV.getMinX());
+ const double v1(rUV.getMinY());
+ const double u2(rUV.getMaxX());
+ const double v2(rUV.getMaxY());
+
+ // concatenate transforms
+ // 1) offset of surface subarea
+ // 2) surface transform
+ // 3) translation to output position [rPos]
+ basegfx::B2DHomMatrix aTransform(basegfx::utils::createTranslateB2DHomMatrix(aPos1.getX(), aPos1.getY()));
+ aTransform = aTransform * rTransform;
+ aTransform.translate(::basegfx::fround(rPos.getX()),
+ ::basegfx::fround(rPos.getY()));
+
+
+ /*
+ ######################################
+ ######################################
+ ######################################
+
+ Y
+ ^+1
+ |
+ 2 | 3
+ x------------x
+ | | |
+ | | |
+ ------|-----O------|------>X
+ -1 | | | +1
+ | | |
+ x------------x
+ 1 | 0
+ |
+ |-1
+
+ ######################################
+ ######################################
+ ######################################
+ */
+
+ const ::basegfx::B2DPoint& p0(aTransform * ::basegfx::B2DPoint(aSize.getX(),aSize.getY()));
+ const ::basegfx::B2DPoint& p1(aTransform * ::basegfx::B2DPoint(0.0, aSize.getY()));
+ const ::basegfx::B2DPoint& p2(aTransform * ::basegfx::B2DPoint(0.0, 0.0));
+ const ::basegfx::B2DPoint& p3(aTransform * ::basegfx::B2DPoint(aSize.getX(),0.0));
+
+ canvas::Vertex vertex;
+ vertex.r = 1.0f;
+ vertex.g = 1.0f;
+ vertex.b = 1.0f;
+ vertex.a = static_cast<float>(fAlpha);
+ vertex.z = 0.0f;
+
+ {
+ pRenderModule->beginPrimitive( canvas::IRenderModule::PrimitiveType::Quad );
+
+ // issue an endPrimitive() when leaving the scope
+ const ::comphelper::ScopeGuard aScopeGuard(
+ [&pRenderModule]() mutable { pRenderModule->endPrimitive(); } );
+
+ vertex.u=static_cast<float>(u2); vertex.v=static_cast<float>(v2);
+ vertex.x=static_cast<float>(p0.getX()); vertex.y=static_cast<float>(p0.getY());
+ pRenderModule->pushVertex(vertex);
+
+ vertex.u=static_cast<float>(u1); vertex.v=static_cast<float>(v2);
+ vertex.x=static_cast<float>(p1.getX()); vertex.y=static_cast<float>(p1.getY());
+ pRenderModule->pushVertex(vertex);
+
+ vertex.u=static_cast<float>(u1); vertex.v=static_cast<float>(v1);
+ vertex.x=static_cast<float>(p2.getX()); vertex.y=static_cast<float>(p2.getY());
+ pRenderModule->pushVertex(vertex);
+
+ vertex.u=static_cast<float>(u2); vertex.v=static_cast<float>(v1);
+ vertex.x=static_cast<float>(p3.getX()); vertex.y=static_cast<float>(p3.getY());
+ pRenderModule->pushVertex(vertex);
+ }
+
+ return !(pRenderModule->isError());
+ }
+
+ bool Surface::drawWithClip( double fAlpha,
+ const ::basegfx::B2DPoint& rPos,
+ const ::basegfx::B2DPolygon& rClipPoly,
+ const ::basegfx::B2DHomMatrix& rTransform )
+ {
+ std::shared_ptr<IRenderModule> pRenderModule(mpPageManager->getRenderModule());
+
+ RenderModuleGuard aGuard( pRenderModule );
+
+ prepareRendering();
+
+ // untransformed surface rectangle, relative to the whole
+ // image (note: this surface might actually only be a tile of
+ // the whole image, with non-zero maSourceOffset)
+ const double x1(maSourceOffset.getX());
+ const double y1(maSourceOffset.getY());
+ const double w(maSize.getX());
+ const double h(maSize.getY());
+ const double x2(x1+w);
+ const double y2(y1+h);
+ const ::basegfx::B2DRectangle aSurfaceClipRect(x1,y1,x2,y2);
+
+ // concatenate transforms
+ // we use 'fround' here to avoid rounding errors. the vertices will
+ // be transformed by the overall transform and uv coordinates will
+ // be calculated from the result, and this is why we need to use
+ // integer coordinates here...
+ basegfx::B2DHomMatrix aTransform = rTransform;
+ aTransform.translate(::basegfx::fround(rPos.getX()),
+ ::basegfx::fround(rPos.getY()));
+
+ /*
+ ######################################
+ ######################################
+ ######################################
+
+ Y
+ ^+1
+ |
+ 2 | 3
+ x------------x
+ | | |
+ | | |
+ ------|-----O------|------>X
+ -1 | | | +1
+ | | |
+ x------------x
+ 1 | 0
+ |
+ |-1
+
+ ######################################
+ ######################################
+ ######################################
+ */
+
+ // uv coordinates that map the surface rectangle
+ // to the destination rectangle.
+ const ::basegfx::B2DRectangle& rUV( getUVCoords() );
+
+ basegfx::B2DPolygon rTriangleList(basegfx::utils::clipTriangleListOnRange(rClipPoly,
+ aSurfaceClipRect));
+
+ // Push vertices to backend renderer
+ if(const sal_uInt32 nVertexCount = rTriangleList.count())
+ {
+ canvas::Vertex vertex;
+ vertex.r = 1.0f;
+ vertex.g = 1.0f;
+ vertex.b = 1.0f;
+ vertex.a = static_cast<float>(fAlpha);
+ vertex.z = 0.0f;
+
+ pRenderModule->beginPrimitive( canvas::IRenderModule::PrimitiveType::Triangle );
+
+ // issue an endPrimitive() when leaving the scope
+ const ::comphelper::ScopeGuard aScopeGuard(
+ [&pRenderModule]() mutable { pRenderModule->endPrimitive(); } );
+
+ for(sal_uInt32 nIndex=0; nIndex<nVertexCount; ++nIndex)
+ {
+ const basegfx::B2DPoint &aPoint = rTriangleList.getB2DPoint(nIndex);
+ basegfx::B2DPoint aTransformedPoint(aTransform * aPoint);
+ const double tu(((aPoint.getX()-aSurfaceClipRect.getMinX())*rUV.getWidth()/w)+rUV.getMinX());
+ const double tv(((aPoint.getY()-aSurfaceClipRect.getMinY())*rUV.getHeight()/h)+rUV.getMinY());
+ vertex.u=static_cast<float>(tu);
+ vertex.v=static_cast<float>(tv);
+ vertex.x=static_cast<float>(aTransformedPoint.getX());
+ vertex.y=static_cast<float>(aTransformedPoint.getY());
+ pRenderModule->pushVertex(vertex);
+ }
+ }
+
+ return !(pRenderModule->isError());
+ }
+
+ void Surface::prepareRendering()
+ {
+ mpPageManager->validatePages();
+
+ // clients requested to draw from this surface, therefore one
+ // of the above implemented concrete rendering operations
+ // was triggered. we therefore need to ask the pagemanager
+ // to allocate some space for the fragment we're dedicated to.
+ if(!mpFragment)
+ {
+ mpFragment = mpPageManager->allocateSpace(maSize);
+ if( mpFragment )
+ {
+ mpFragment->setColorBuffer(mpColorBuffer);
+ mpFragment->setSourceOffset(maSourceOffset);
+ }
+ }
+
+ if( mpFragment )
+ {
+ // now we need to 'select' the fragment, which will in turn
+ // pull information from the image on demand.
+ // in case this fragment is still not located on any of the
+ // available pages ['naked'], we force the page manager to
+ // do it now, no way to defer this any longer...
+ if(!(mpFragment->select(mbIsDirty)))
+ mpPageManager->nakedFragment(mpFragment);
+
+ }
+ mbIsDirty=false;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/tools/surface.hxx b/canvas/source/tools/surface.hxx
new file mode 100644
index 000000000..e78925292
--- /dev/null
+++ b/canvas/source/tools/surface.hxx
@@ -0,0 +1,143 @@
+/* -*- 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/point/b2ipoint.hxx>
+#include <basegfx/point/b2dpoint.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/range/b2drectangle.hxx>
+#include <basegfx/vector/b2isize.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <rendering/icolorbuffer.hxx>
+
+#include "pagemanager.hxx"
+
+namespace canvas
+{
+ /** surfaces denote occupied areas within pages.
+
+ pages encapsulate the hardware buffers that
+ contain image data which can be used for texturing.
+ surfaces are areas within those pages.
+ */
+ class Surface
+ {
+ public:
+
+ Surface( PageManagerSharedPtr xPageManager,
+ std::shared_ptr<IColorBuffer> xColorBuffer,
+ const ::basegfx::B2IPoint& rPos,
+ const ::basegfx::B2ISize& rSize );
+ ~Surface();
+
+ void setColorBufferDirty();
+
+ /** Render the surface content to screen.
+
+ @param fAlpha
+ Overall alpha for content
+
+ @param rPos
+ Output position
+
+ @param rTransform
+ Output transformation (does not affect output position)
+ */
+ bool draw( double fAlpha,
+ const ::basegfx::B2DPoint& rPos,
+ const ::basegfx::B2DHomMatrix& rTransform );
+
+ /** Render the surface content to screen.
+
+ @param fAlpha
+ Overall alpha for content
+
+ @param rPos
+ Output position
+
+ @param rArea
+ Subset of the surface to render. Coordinate system are
+ surface area pixel, given area will be clipped to the
+ surface bounds.
+
+ @param rTransform
+ Output transformation (does not affect output position)
+ */
+ bool drawRectangularArea(
+ double fAlpha,
+ const ::basegfx::B2DPoint& rPos,
+ const ::basegfx::B2DRectangle& rArea,
+ const ::basegfx::B2DHomMatrix& rTransform );
+
+ /** Render the surface content to screen.
+
+ @param fAlpha
+ Overall alpha for content
+
+ @param rPos
+ Output position
+
+ @param rClipPoly
+ Clip polygon for the surface. The clip polygon is also
+ subject to the output transformation.
+
+ @param rTransform
+ Output transformation (does not affect output position)
+ */
+ bool drawWithClip( double fAlpha,
+ const ::basegfx::B2DPoint& rPos,
+ const ::basegfx::B2DPolygon& rClipPoly,
+ const ::basegfx::B2DHomMatrix& rTransform );
+
+ private:
+ std::shared_ptr<IColorBuffer> mpColorBuffer;
+
+ // invoking any of the above defined 'draw' methods
+ // will forward primitive commands to the rendermodule.
+ PageManagerSharedPtr mpPageManager;
+
+ FragmentSharedPtr mpFragment;
+
+ // the offset of this surface with regard to the source
+ // image. if the source image had to be tiled into multiple
+ // surfaces, this offset denotes the relative pixel distance
+ // from the source image's upper, left corner
+ ::basegfx::B2IPoint maSourceOffset;
+
+ // the size in pixels of this surface. please note that
+ // this size is likely to be smaller than the size of
+ // the colorbuffer we're associated with since we
+ // maybe represent only a part of it.
+ ::basegfx::B2ISize maSize;
+
+ bool mbIsDirty;
+
+ private:
+ void prepareRendering();
+
+ basegfx::B2DRectangle getUVCoords() const;
+ basegfx::B2DRectangle getUVCoords( const ::basegfx::B2IPoint& rPos,
+ const ::basegfx::B2ISize& rSize ) const;
+ };
+
+ typedef std::shared_ptr< Surface > SurfaceSharedPtr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/tools/surfaceproxy.cxx b/canvas/source/tools/surfaceproxy.cxx
new file mode 100644
index 000000000..3a1bf4fe3
--- /dev/null
+++ b/canvas/source/tools/surfaceproxy.cxx
@@ -0,0 +1,141 @@
+/* -*- 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/polygon/b2dpolypolygon.hxx>
+#include <basegfx/polygon/b2dpolygontriangulator.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <utility>
+
+#include "surfaceproxy.hxx"
+
+namespace canvas
+{
+ SurfaceProxy::SurfaceProxy( std::shared_ptr<canvas::IColorBuffer> xBuffer,
+ PageManagerSharedPtr xPageManager ) :
+ mpPageManager(std::move( xPageManager )),
+ mpBuffer(std::move( xBuffer ))
+ {
+ const ::basegfx::B2ISize aImageSize(mpBuffer->getWidth(),mpBuffer->getHeight());
+ const ::basegfx::B2ISize aPageSize(mpPageManager->getPageSize());
+ const sal_Int32 aPageSizeX(aPageSize.getX());
+ const sal_Int32 aPageSizeY(aPageSize.getY());
+ const sal_Int32 aImageSizeX(aImageSize.getX());
+ const sal_Int32 aImageSizeY(aImageSize.getY());
+
+ // see if the size of the colorbuffer is larger than the size
+ // of a single page. if this is the case we divide the
+ // colorbuffer into as many surfaces as we need to get the
+ // whole area distributed. otherwise (the colorbuffer is
+ // smaller than the size of a single page) we search for free
+ // pages or create a new one.
+ // the incoming image is too large to fit into a single
+ // page. strategy: we split the image into rectangular
+ // areas that are as large as the maximum page size
+ // dictates and follow the strategy for fitting images.
+ size_t dwNumSurfaces(0);
+ for(sal_Int32 y=0; y<aImageSizeY; y+=aPageSizeY)
+ for(sal_Int32 x=0; x<aImageSizeX; x+=aPageSizeX)
+ ++dwNumSurfaces;
+ maSurfaceList.reserve(dwNumSurfaces);
+
+ for(sal_Int32 y=0; y<aImageSizeY; y+=aPageSizeY)
+ {
+ for(sal_Int32 x=0; x<aImageSizeX; x+=aPageSizeX)
+ {
+ // the current surface is located at the position [x,y]
+ // and has the size [min(restx,pagesizex),min(resty,pagesizey)
+ ::basegfx::B2IPoint aOffset(x,y);
+ ::basegfx::B2ISize aSize( std::min( aImageSize.getX()-x,
+ aPageSize.getX() ),
+ std::min( aImageSize.getY()-y,
+ aPageSize.getY() ) );
+
+ maSurfaceList.push_back(
+ std::make_shared<Surface>(
+ mpPageManager,
+ mpBuffer,
+ aOffset,
+ aSize));
+ }
+ }
+ }
+
+ void SurfaceProxy::setColorBufferDirty()
+ {
+ for( const auto& rSurfacePtr : maSurfaceList )
+ rSurfacePtr->setColorBufferDirty();
+ }
+
+ bool SurfaceProxy::draw( double fAlpha,
+ const ::basegfx::B2DPoint& rPos,
+ const ::basegfx::B2DHomMatrix& rTransform )
+ {
+ for( const auto& rSurfacePtr : maSurfaceList )
+ rSurfacePtr->draw( fAlpha, rPos, rTransform );
+
+ return true;
+ }
+
+ bool SurfaceProxy::draw( double fAlpha,
+ const ::basegfx::B2DPoint& rPos,
+ const ::basegfx::B2DRange& rArea,
+ const ::basegfx::B2DHomMatrix& rTransform )
+ {
+ for( const auto& rSurfacePtr : maSurfaceList )
+ rSurfacePtr->drawRectangularArea( fAlpha, rPos, rArea, rTransform );
+
+ return true;
+ }
+
+ bool SurfaceProxy::draw( double fAlpha,
+ const ::basegfx::B2DPoint& rPos,
+ const ::basegfx::B2DPolyPolygon& rClipPoly,
+ const ::basegfx::B2DHomMatrix& rTransform )
+ {
+ const ::basegfx::triangulator::B2DTriangleVector& rTriangulatedVector(
+ ::basegfx::triangulator::triangulate(rClipPoly));
+
+ // we have now an explicit ::B2DTriangle and ::B2DTriangleVector,
+ // but I do not know enough about 'drawWithClip' or 'clipTriangleListOnRange'
+ // to adapt to that. Convert back to old three-point-in-polygon convention
+ ::basegfx::B2DPolygon aTriangulatedPolygon;
+ aTriangulatedPolygon.reserve(rTriangulatedVector.size() * 3);
+
+ for(const auto& rCandidate : rTriangulatedVector)
+ {
+ aTriangulatedPolygon.append(rCandidate.getA());
+ aTriangulatedPolygon.append(rCandidate.getB());
+ aTriangulatedPolygon.append(rCandidate.getC());
+ }
+
+ // dump polygons
+ SAL_INFO("canvas", "Original clip polygon: " << basegfx::utils::exportToSvgD( rClipPoly, true, true, false ));
+ SAL_INFO("canvas", "Triangulated polygon: " << basegfx::utils::exportToSvgD(basegfx::B2DPolyPolygon(aTriangulatedPolygon), true, true, false ));
+
+ for( const auto& rSurfacePtr : maSurfaceList )
+ rSurfacePtr->drawWithClip( fAlpha, rPos, aTriangulatedPolygon, rTransform );
+
+ return true;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/tools/surfaceproxy.hxx b/canvas/source/tools/surfaceproxy.hxx
new file mode 100644
index 000000000..72841d754
--- /dev/null
+++ b/canvas/source/tools/surfaceproxy.hxx
@@ -0,0 +1,118 @@
+/* -*- 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/icolorbuffer.hxx>
+
+#include "pagemanager.hxx"
+#include "surface.hxx"
+
+namespace canvas
+{
+ /** Definition of the surface proxy class.
+
+ Surface proxies are the connection between *one* source image
+ and *one or more* hardware surfaces (or textures). in a
+ logical structure surface proxies represent solely this
+ dependency plus some simple cache management.
+ */
+ class SurfaceProxy : public ISurfaceProxy
+ {
+ public:
+
+ SurfaceProxy( std::shared_ptr<canvas::IColorBuffer> xBuffer,
+ PageManagerSharedPtr xPageManager );
+
+ // ISurfaceProxy interface
+ virtual void setColorBufferDirty() override;
+
+ /** Render the surface content to screen.
+
+ @param fAlpha
+ Overall alpha for content
+
+ @param rPos
+ Output position
+
+ @param rTransform
+ Output transformation (does not affect output position)
+ */
+ virtual bool draw( double fAlpha,
+ const ::basegfx::B2DPoint& rPos,
+ const ::basegfx::B2DHomMatrix& rTransform ) override;
+
+ /** Render the surface content to screen.
+
+ @param fAlpha
+ Overall alpha for content
+
+ @param rPos
+ Output position
+
+ @param rArea
+ Subset of the surface to render. Coordinate system are
+ surface area pixel, given area will be clipped to the
+ surface bounds.
+
+ @param rTransform
+ Output transformation (does not affect output position)
+ */
+ virtual bool draw( double fAlpha,
+ const ::basegfx::B2DPoint& rPos,
+ const ::basegfx::B2DRange& rArea,
+ const ::basegfx::B2DHomMatrix& rTransform ) override;
+
+ /** Render the surface content to screen.
+
+ @param fAlpha
+ Overall alpha for content
+
+ @param rPos
+ Output position
+
+ @param rClipPoly
+ Clip polygon for the surface. The clip polygon is also
+ subject to the output transformation.
+
+ @param rTransform
+ Output transformation (does not affect output position)
+ */
+ virtual bool draw( double fAlpha,
+ const ::basegfx::B2DPoint& rPos,
+ const ::basegfx::B2DPolyPolygon& rClipPoly,
+ const ::basegfx::B2DHomMatrix& rTransform ) override;
+
+ private:
+ PageManagerSharedPtr mpPageManager;
+
+ // the pagemanager will distribute the image
+ // to one or more surfaces, this is why we
+ // need a list here.
+ std::vector<SurfaceSharedPtr> maSurfaceList;
+
+ // pointer to the source of image data
+ // which always is stored in system memory,
+ // 32bit rgba and can have any size.
+ std::shared_ptr<canvas::IColorBuffer> mpBuffer;
+ };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/tools/surfaceproxymanager.cxx b/canvas/source/tools/surfaceproxymanager.cxx
new file mode 100644
index 000000000..b60c5ecb4
--- /dev/null
+++ b/canvas/source/tools/surfaceproxymanager.cxx
@@ -0,0 +1,73 @@
+/* -*- 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 <rendering/isurfaceproxy.hxx>
+#include <rendering/isurfaceproxymanager.hxx>
+
+#include "surfaceproxy.hxx"
+
+namespace canvas
+{
+ namespace {
+
+ class SurfaceProxyManager : public ISurfaceProxyManager
+ {
+ public:
+
+ explicit SurfaceProxyManager( const std::shared_ptr<IRenderModule>& rRenderModule ) :
+ mpPageManager( std::make_shared<PageManager>(rRenderModule) )
+ {
+ }
+
+ /** the whole idea is build around the concept that you create
+ some arbitrary buffer which contains the image data and
+ tell the texture manager about it. from there on you can
+ draw this image using any kind of graphics api you want.
+ in the technical sense we allocate some space in local
+ videomemory or AGP memory which will be filled on demand,
+ which means if there exists any rendering operation that
+ needs to read from this memory location. this method
+ creates a logical hardware surface object which uses the
+ given color buffer as the image source. internally this
+ texture may be distributed to several real hardware
+ surfaces.
+ */
+ virtual std::shared_ptr<ISurfaceProxy> createSurfaceProxy( const std::shared_ptr<IColorBuffer>& pBuffer ) const override
+ {
+ // not much to do for now, simply allocate a new surface
+ // proxy from our internal pool and initialize this thing
+ // properly. we *don't* create a hardware surface for now.
+ return std::make_shared<SurfaceProxy>(pBuffer,mpPageManager);
+ }
+
+ private:
+ PageManagerSharedPtr mpPageManager;
+ };
+
+ }
+
+ std::shared_ptr<ISurfaceProxyManager> createSurfaceProxyManager( const std::shared_ptr<IRenderModule>& rRenderModule )
+ {
+ return std::make_shared<SurfaceProxyManager>(rRenderModule);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/tools/surfacerect.hxx b/canvas/source/tools/surfacerect.hxx
new file mode 100644
index 000000000..808796d6d
--- /dev/null
+++ b/canvas/source/tools/surfacerect.hxx
@@ -0,0 +1,90 @@
+/* -*- 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/point/b2ipoint.hxx>
+#include <basegfx/vector/b2isize.hxx>
+
+namespace canvas
+{
+ /**
+ * This implements some equivalent to basegfx::B2IBox, but instead of two
+ * BasicBox ranges, it uses a position and a size. maPos and maSize could
+ * be replaced by:
+ * - B2IPoint(getMinX(), getMinY()) and
+ * - B2ISize(getMaxX()-getMinX(), getMaxY()-getMinY())
+ *
+ * The current allocation algorithm uses size and pos a lot. Not sure how
+ * time-critical any of this code is and if that would be a problem.
+ */
+ struct SurfaceRect
+ {
+ ::basegfx::B2IPoint maPos;
+ ::basegfx::B2ISize maSize;
+
+ explicit SurfaceRect( const ::basegfx::B2ISize &rSize ) :
+ maPos(),
+ maSize(rSize)
+ {
+ }
+
+ bool pointInside( sal_Int32 px, sal_Int32 py ) const
+ {
+ const sal_Int32 x1(maPos.getX());
+ const sal_Int32 y1(maPos.getY());
+ const sal_Int32 x2(x1 + maSize.getX());
+ const sal_Int32 y2(y1 + maSize.getY());
+ if(px < x1) return false;
+ if(px >= x2) return false;
+ if(py < y1) return false;
+ if(py >= y2) return false;
+ return true;
+ }
+
+ /// returns true if the passed rect intersects this one.
+ bool intersection( const SurfaceRect& r ) const
+ {
+ const sal_Int32 x1(maPos.getX());
+ const sal_Int32 y1(maPos.getY());
+ const sal_Int32 x1w(x1 + maSize.getX() - 1);
+ const sal_Int32 y1h(y1 + maSize.getY() - 1);
+
+ const sal_Int32 x2(r.maPos.getX());
+ const sal_Int32 y2(r.maPos.getY());
+ const sal_Int32 x2w(x2 + r.maSize.getX() - 1);
+ const sal_Int32 y2h(y2 + r.maSize.getY() - 1);
+
+ return !((x1w < x2) || (x2w < x1) || (y1h < y2) || (y2h < y1));
+ }
+
+ bool inside( const SurfaceRect& r ) const
+ {
+ const sal_Int32 x1(maPos.getX());
+ const sal_Int32 y1(maPos.getY());
+ const sal_Int32 x2(x1 + maSize.getX() - 1);
+ const sal_Int32 y2(y1 + maSize.getY() - 1);
+ if(!(r.pointInside(x1,y1))) return false;
+ if(!(r.pointInside(x2,y2))) return false;
+ return true;
+ }
+ };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/tools/verifyinput.cxx b/canvas/source/tools/verifyinput.cxx
new file mode 100644
index 000000000..ae0704d81
--- /dev/null
+++ b/canvas/source/tools/verifyinput.cxx
@@ -0,0 +1,709 @@
+/* -*- 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/range/b2irange.hxx>
+#include <basegfx/utils/canvastools.hxx>
+#include <com/sun/star/geometry/AffineMatrix2D.hpp>
+#include <com/sun/star/geometry/IntegerPoint2D.hpp>
+#include <com/sun/star/geometry/IntegerSize2D.hpp>
+#include <com/sun/star/geometry/Matrix2D.hpp>
+#include <com/sun/star/geometry/RealBezierSegment2D.hpp>
+#include <com/sun/star/geometry/RealPoint2D.hpp>
+#include <com/sun/star/geometry/RealRectangle2D.hpp>
+#include <com/sun/star/geometry/RealSize2D.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/rendering/CompositeOperation.hpp>
+#include <com/sun/star/rendering/FontRequest.hpp>
+#include <com/sun/star/rendering/IntegerBitmapLayout.hpp>
+#include <com/sun/star/rendering/PathCapType.hpp>
+#include <com/sun/star/rendering/PathJoinType.hpp>
+#include <com/sun/star/rendering/RenderState.hpp>
+#include <com/sun/star/rendering/Texture.hpp>
+#include <com/sun/star/rendering/TexturingMode.hpp>
+#include <com/sun/star/rendering/ViewState.hpp>
+#include <com/sun/star/util/Endianness.hpp>
+
+#include <verifyinput.hxx>
+
+
+using namespace ::com::sun::star;
+
+namespace canvas::tools
+{
+ void verifyInput( const geometry::RealPoint2D& rPoint,
+ const char* pStr,
+ const uno::Reference< uno::XInterface >& xIf,
+ ::sal_Int16 nArgPos )
+ {
+#if OSL_DEBUG_LEVEL > 0
+ if( !std::isfinite( rPoint.X ) )
+ {
+ throw lang::IllegalArgumentException(
+ OUString::createFromAscii( pStr ) + ": verifyInput(): point X value contains infinite or NAN",
+ xIf, nArgPos );
+ }
+
+ if( !std::isfinite( rPoint.Y ) )
+ {
+ throw lang::IllegalArgumentException(
+ OUString::createFromAscii( pStr ) + ": verifyInput(): point X value contains infinite or NAN",
+ xIf, nArgPos );
+ }
+#else
+ (void)pStr; (void)xIf; (void)nArgPos;
+ if( !std::isfinite( rPoint.X ) ||
+ !std::isfinite( rPoint.Y ) )
+ {
+ throw lang::IllegalArgumentException();
+ }
+#endif
+ }
+
+ void verifyInput( const geometry::RealBezierSegment2D& rSegment,
+ const char* pStr,
+ const uno::Reference< uno::XInterface >& xIf,
+ ::sal_Int16 nArgPos )
+ {
+#if OSL_DEBUG_LEVEL > 0
+ if( !std::isfinite( rSegment.Px ) )
+ {
+ throw lang::IllegalArgumentException(
+ OUString::createFromAscii( pStr ) +
+ ": verifyInput(): bezier segment's Px value contains infinite or NAN",
+ xIf, nArgPos );
+ }
+
+ if( !std::isfinite( rSegment.Py ) )
+ {
+ throw lang::IllegalArgumentException(
+ OUString::createFromAscii( pStr ) +
+ ": verifyInput(): bezier segment's Py value contains infinite or NAN",
+ xIf, nArgPos );
+ }
+
+ if( !std::isfinite( rSegment.C1x ) )
+ {
+ throw lang::IllegalArgumentException(
+ OUString::createFromAscii( pStr ) +
+ ": verifyInput(): bezier segment's C1x value contains infinite or NAN",
+ xIf, nArgPos );
+ }
+
+ if( !std::isfinite( rSegment.C1y ) )
+ {
+ throw lang::IllegalArgumentException(
+ OUString::createFromAscii( pStr ) +
+ ": verifyInput(): bezier segment's C1y value contains infinite or NAN",
+ xIf, nArgPos );
+ }
+
+ if( !std::isfinite( rSegment.C2x ) )
+ {
+ throw lang::IllegalArgumentException(
+ OUString::createFromAscii( pStr ) +
+ ": verifyInput(): bezier segment's C2x value contains infinite or NAN",
+ xIf, nArgPos );
+ }
+
+ if( !std::isfinite( rSegment.C2y ) )
+ {
+ throw lang::IllegalArgumentException(
+ OUString::createFromAscii( pStr ) +
+ ": verifyInput(): bezier segment's C2y value contains infinite or NAN",
+ xIf, nArgPos );
+ }
+#else
+ (void)pStr; (void)xIf; (void)nArgPos;
+ if( !std::isfinite( rSegment.Px ) ||
+ !std::isfinite( rSegment.Py ) ||
+ !std::isfinite( rSegment.C1x ) ||
+ !std::isfinite( rSegment.C1y ) ||
+ !std::isfinite( rSegment.C2x ) ||
+ !std::isfinite( rSegment.C2y ) )
+ {
+ throw lang::IllegalArgumentException();
+ }
+#endif
+ }
+
+ void verifyInput( const geometry::RealRectangle2D& rRect,
+ const char* pStr,
+ const uno::Reference< uno::XInterface >& xIf,
+ ::sal_Int16 nArgPos )
+ {
+#if OSL_DEBUG_LEVEL > 0
+ if( !std::isfinite( rRect.X1 ) )
+ {
+ throw lang::IllegalArgumentException(
+ OUString::createFromAscii(pStr) +
+ ": verifyInput(): rectangle point X1 contains infinite or NAN",
+ xIf, nArgPos );
+ }
+
+ if( !std::isfinite( rRect.Y1 ) )
+ {
+ throw lang::IllegalArgumentException(
+ OUString::createFromAscii(pStr) +
+ ": verifyInput(): rectangle point Y1 contains infinite or NAN",
+ xIf, nArgPos );
+ }
+
+ if( !std::isfinite( rRect.X2 ) )
+ {
+ throw lang::IllegalArgumentException(
+ OUString::createFromAscii(pStr) +
+ ": verifyInput(): rectangle point X2 contains infinite or NAN",
+ xIf, nArgPos );
+ }
+
+ if( !std::isfinite( rRect.Y2 ) )
+ {
+ throw lang::IllegalArgumentException(
+ OUString::createFromAscii(pStr) +
+ ": verifyInput(): rectangle point Y2 contains infinite or NAN",
+ xIf, nArgPos );
+ }
+#else
+ (void)pStr; (void)xIf; (void)nArgPos;
+ if( !std::isfinite( rRect.X1 ) ||
+ !std::isfinite( rRect.Y1 ) ||
+ !std::isfinite( rRect.X2 ) ||
+ !std::isfinite( rRect.Y2 ) )
+ {
+ throw lang::IllegalArgumentException();
+ }
+#endif
+ }
+
+ void verifyInput( const geometry::AffineMatrix2D& matrix,
+ const char* pStr,
+ const uno::Reference< uno::XInterface >& xIf,
+ ::sal_Int16 nArgPos )
+ {
+#if OSL_DEBUG_LEVEL > 0
+ const sal_Int32 nBinaryState(
+ 100000 * int(!std::isfinite( matrix.m00 )) +
+ 10000 * int(!std::isfinite( matrix.m01 )) +
+ 1000 * int(!std::isfinite( matrix.m02 )) +
+ 100 * int(!std::isfinite( matrix.m10 )) +
+ 10 * int(!std::isfinite( matrix.m11 )) +
+ 1 * int(!std::isfinite( matrix.m12 )) );
+
+ if( nBinaryState )
+ {
+ throw lang::IllegalArgumentException(
+ OUString::createFromAscii(pStr) +
+ ": verifyInput(): AffineMatrix2D contains infinite or NAN value(s) at the following positions (m00-m12): " +
+ OUString::number(nBinaryState),
+ xIf, nArgPos );
+ }
+#else
+ (void)pStr; (void)xIf; (void)nArgPos;
+ if( !std::isfinite( matrix.m00 ) ||
+ !std::isfinite( matrix.m01 ) ||
+ !std::isfinite( matrix.m02 ) ||
+ !std::isfinite( matrix.m10 ) ||
+ !std::isfinite( matrix.m11 ) ||
+ !std::isfinite( matrix.m12 ) )
+ {
+ throw lang::IllegalArgumentException();
+ }
+#endif
+ }
+
+ void verifyInput( const geometry::Matrix2D& matrix,
+ const char* pStr,
+ const uno::Reference< uno::XInterface >& xIf,
+ ::sal_Int16 nArgPos )
+ {
+#if OSL_DEBUG_LEVEL > 0
+ const sal_Int32 nBinaryState(
+ 1000 * int(!std::isfinite( matrix.m00 )) +
+ 100 * int(!std::isfinite( matrix.m01 )) +
+ 10 * int(!std::isfinite( matrix.m10 )) +
+ 1 * int(!std::isfinite( matrix.m11 )) );
+
+ if( nBinaryState )
+ {
+ throw lang::IllegalArgumentException(
+ OUString::createFromAscii(pStr) +
+ ": verifyInput(): Matrix2D contains infinite or NAN value(s) at the following positions (m00-m11): " +
+ OUString::number(nBinaryState),
+ xIf, nArgPos );
+ }
+#else
+ (void)pStr; (void)xIf; (void)nArgPos;
+ if( !std::isfinite( matrix.m00 ) ||
+ !std::isfinite( matrix.m01 ) ||
+ !std::isfinite( matrix.m10 ) ||
+ !std::isfinite( matrix.m11 ) )
+ {
+ throw lang::IllegalArgumentException();
+ }
+#endif
+ }
+
+ void verifyInput( const rendering::ViewState& viewState,
+ const char* pStr,
+ const uno::Reference< uno::XInterface >& xIf,
+ ::sal_Int16 nArgPos )
+ {
+ verifyInput( viewState.AffineTransform,
+ pStr, xIf, nArgPos );
+ }
+
+ void verifyInput( const rendering::RenderState& renderState,
+ const char* pStr,
+ const uno::Reference< uno::XInterface >& xIf,
+ ::sal_Int16 nArgPos,
+ sal_Int32 nMinColorComponents )
+ {
+ verifyInput( renderState.AffineTransform,
+ pStr, xIf, nArgPos );
+
+ if( renderState.DeviceColor.getLength() < nMinColorComponents )
+ {
+#if OSL_DEBUG_LEVEL > 0
+ throw lang::IllegalArgumentException(
+ OUString::createFromAscii(pStr) +
+ ": verifyInput(): render state's device color has too few components (" +
+ OUString::number(nMinColorComponents) +
+ " expected, " +
+ OUString::number(renderState.DeviceColor.getLength()) +
+ " provided)",
+ xIf, nArgPos );
+#else
+ throw lang::IllegalArgumentException();
+#endif
+ }
+
+ if( renderState.CompositeOperation >= rendering::CompositeOperation::CLEAR &&
+ renderState.CompositeOperation <= rendering::CompositeOperation::SATURATE )
+ return;
+
+#if OSL_DEBUG_LEVEL > 0
+ throw lang::IllegalArgumentException(
+ OUString::createFromAscii(pStr) +
+ ": verifyInput(): render state's CompositeOperation value out of range (" +
+ OUString::number(sal::static_int_cast<sal_Int32>(renderState.CompositeOperation)) +
+ " not known)",
+ xIf, nArgPos );
+#else
+ throw lang::IllegalArgumentException();
+#endif
+ }
+
+ void verifyInput( const rendering::Texture& texture,
+ const char* pStr,
+ const uno::Reference< uno::XInterface >& xIf,
+ ::sal_Int16 nArgPos )
+ {
+ verifyInput( texture.AffineTransform,
+ pStr, xIf, nArgPos );
+
+ if( !std::isfinite( texture.Alpha ) ||
+ texture.Alpha < 0.0 ||
+ texture.Alpha > 1.0 )
+ {
+#if OSL_DEBUG_LEVEL > 0
+ throw lang::IllegalArgumentException(
+ OUString::createFromAscii(pStr) +
+ ": verifyInput(): textures' alpha value out of range (is " +
+ OUString::number(texture.Alpha) + ")",
+ xIf, nArgPos );
+#else
+ throw lang::IllegalArgumentException();
+#endif
+ }
+
+ if( texture.NumberOfHatchPolygons < 0 )
+ {
+#if OSL_DEBUG_LEVEL > 0
+ throw lang::IllegalArgumentException(
+ OUString::createFromAscii(pStr) +
+ ": verifyInput(): textures' NumberOfHatchPolygons is negative",
+ xIf, nArgPos );
+#else
+ throw lang::IllegalArgumentException();
+#endif
+ }
+
+ if( texture.RepeatModeX < rendering::TexturingMode::NONE ||
+ texture.RepeatModeX > rendering::TexturingMode::REPEAT )
+ {
+#if OSL_DEBUG_LEVEL > 0
+ throw lang::IllegalArgumentException(
+ OUString::createFromAscii(pStr) +
+ ": verifyInput(): textures' RepeatModeX value is out of range (" +
+ OUString::number(sal::static_int_cast<sal_Int32>(texture.RepeatModeX)) +
+ " not known)",
+ xIf, nArgPos );
+#else
+ throw lang::IllegalArgumentException();
+#endif
+ }
+
+ if( texture.RepeatModeY >= rendering::TexturingMode::NONE &&
+ texture.RepeatModeY <= rendering::TexturingMode::REPEAT )
+ return;
+
+#if OSL_DEBUG_LEVEL > 0
+ throw lang::IllegalArgumentException(
+ OUString::createFromAscii(pStr) +
+ ": verifyInput(): textures' RepeatModeY value is out of range (" +
+ OUString::number(sal::static_int_cast<sal_Int32>(texture.RepeatModeY)) +
+ " not known)",
+ xIf, nArgPos );
+#else
+ throw lang::IllegalArgumentException();
+#endif
+ }
+
+ namespace
+ {
+ struct VerifyDashValue
+ {
+ VerifyDashValue( const char* pStr,
+ const uno::Reference< uno::XInterface >& xIf,
+ ::sal_Int16 nArgPos ) :
+ mpStr( pStr ),
+ mrIf( xIf ),
+ mnArgPos( nArgPos )
+ {
+ }
+
+ void operator()( const double& rVal )
+ {
+ if( !std::isfinite( rVal ) || rVal < 0.0 )
+ {
+ throw lang::IllegalArgumentException(
+ OUString::createFromAscii(mpStr) +
+ ": verifyInput(): one of stroke attributes' DashArray value out of range (is " +
+ OUString::number(rVal) + ")",
+ mrIf, mnArgPos );
+ }
+ }
+
+ const char* mpStr;
+ const uno::Reference< uno::XInterface >& mrIf;
+ sal_Int16 mnArgPos;
+ };
+ }
+
+ void verifyInput( const rendering::StrokeAttributes& strokeAttributes,
+ const char* pStr,
+ const uno::Reference< uno::XInterface >& xIf,
+ ::sal_Int16 nArgPos )
+ {
+ if( !std::isfinite( strokeAttributes.StrokeWidth ) ||
+ strokeAttributes.StrokeWidth < 0.0 )
+ {
+#if OSL_DEBUG_LEVEL > 0
+ throw lang::IllegalArgumentException(
+ OUString::createFromAscii(pStr) +
+ ": verifyInput(): stroke attributes' StrokeWidth value out of range (is " +
+ OUString::number(strokeAttributes.StrokeWidth) +
+ ")",
+ xIf, nArgPos );
+#else
+ throw lang::IllegalArgumentException();
+#endif
+ }
+
+ if( !std::isfinite( strokeAttributes.MiterLimit ) ||
+ strokeAttributes.MiterLimit < 0.0 )
+ {
+#if OSL_DEBUG_LEVEL > 0
+ throw lang::IllegalArgumentException(
+ OUString::createFromAscii(pStr) +
+ ": verifyInput(): stroke attributes' MiterLimit value out of range (is " +
+ OUString::number(strokeAttributes.MiterLimit) + ")",
+ xIf, nArgPos );
+#else
+ throw lang::IllegalArgumentException();
+#endif
+ }
+
+ VerifyDashValue aVerifyDashValue( pStr, xIf, nArgPos );
+ for (auto const& aStrokeAttribute : strokeAttributes.DashArray)
+ aVerifyDashValue( aStrokeAttribute );
+
+ for (auto const& aStrokeAttribute : strokeAttributes.LineArray)
+ aVerifyDashValue( aStrokeAttribute );
+
+ if( strokeAttributes.StartCapType < rendering::PathCapType::BUTT ||
+ strokeAttributes.StartCapType > rendering::PathCapType::SQUARE )
+ {
+#if OSL_DEBUG_LEVEL > 0
+ throw lang::IllegalArgumentException(
+ OUString::createFromAscii(pStr) +
+ ": verifyInput(): stroke attributes' StartCapType value is out of range (" +
+ OUString::number(sal::static_int_cast<sal_Int32>(strokeAttributes.StartCapType)) +
+ " not known)",
+ xIf, nArgPos );
+#else
+ throw lang::IllegalArgumentException();
+#endif
+ }
+
+ if( strokeAttributes.EndCapType < rendering::PathCapType::BUTT ||
+ strokeAttributes.EndCapType > rendering::PathCapType::SQUARE )
+ {
+#if OSL_DEBUG_LEVEL > 0
+ throw lang::IllegalArgumentException(
+ OUString::createFromAscii(pStr) +
+ ": verifyInput(): stroke attributes' StartCapType value is out of range (" +
+ OUString::number(sal::static_int_cast<sal_Int32>(strokeAttributes.EndCapType)) +
+ " not known)",
+ xIf, nArgPos );
+#else
+ throw lang::IllegalArgumentException();
+#endif
+ }
+
+ if( strokeAttributes.JoinType >= rendering::PathJoinType::NONE &&
+ strokeAttributes.JoinType <= rendering::PathJoinType::BEVEL )
+ return;
+
+#if OSL_DEBUG_LEVEL > 0
+ throw lang::IllegalArgumentException(
+ OUString::createFromAscii(pStr) +
+ ": verifyInput(): stroke attributes' JoinType value is out of range (" +
+ OUString::number(sal::static_int_cast<sal_Int32>(strokeAttributes.JoinType)) +
+ " not known)",
+ xIf, nArgPos );
+#else
+ throw lang::IllegalArgumentException();
+#endif
+ }
+
+ void verifyInput( const rendering::IntegerBitmapLayout& bitmapLayout,
+ const char* pStr,
+ const uno::Reference< uno::XInterface >& xIf,
+ ::sal_Int16 nArgPos )
+ {
+ if( bitmapLayout.ScanLines < 0 )
+ {
+#if OSL_DEBUG_LEVEL > 0
+ throw lang::IllegalArgumentException(
+ OUString::createFromAscii(pStr) +
+ ": verifyInput(): bitmap layout's ScanLines is negative",
+ xIf, nArgPos );
+#else
+ (void)pStr; (void)xIf; (void)nArgPos;
+ throw lang::IllegalArgumentException();
+#endif
+ }
+
+ if( bitmapLayout.ScanLineBytes < 0 )
+ {
+#if OSL_DEBUG_LEVEL > 0
+ throw lang::IllegalArgumentException(
+ OUString::createFromAscii(pStr) +
+ ": verifyInput(): bitmap layout's ScanLineBytes is negative",
+ xIf, nArgPos );
+#else
+ throw lang::IllegalArgumentException();
+#endif
+ }
+
+ if( !bitmapLayout.ColorSpace.is() )
+ {
+#if OSL_DEBUG_LEVEL > 0
+ throw lang::IllegalArgumentException(
+ OUString::createFromAscii(pStr) +
+ ": verifyInput(): bitmap layout's ColorSpace is invalid",
+ xIf, nArgPos );
+#else
+ throw lang::IllegalArgumentException();
+#endif
+ }
+ if( bitmapLayout.ColorSpace->getBitsPerPixel() < 0 )
+ {
+#if OSL_DEBUG_LEVEL > 0
+ throw lang::IllegalArgumentException(
+ OUString::createFromAscii(pStr) +
+ ": verifyInput(): bitmap layout's ColorSpace getBitsPerPixel() is negative",
+ xIf, nArgPos );
+#else
+ throw lang::IllegalArgumentException();
+#endif
+ }
+
+ if( bitmapLayout.ColorSpace->getEndianness() >= util::Endianness::LITTLE &&
+ bitmapLayout.ColorSpace->getEndianness() <= util::Endianness::BIG )
+ return;
+
+#if OSL_DEBUG_LEVEL > 0
+ throw lang::IllegalArgumentException(
+ OUString::createFromAscii(pStr) +
+ ": verifyInput(): bitmap layout's ColorSpace getEndianness() value is out of range (" +
+ OUString::number(sal::static_int_cast<sal_Int32>(bitmapLayout.ColorSpace->getEndianness())) +
+ " not known)",
+ xIf, nArgPos );
+#else
+ throw lang::IllegalArgumentException();
+#endif
+ }
+
+ void verifyInput( const rendering::FontRequest& fontRequest,
+ const char* pStr,
+ const uno::Reference< uno::XInterface >& xIf,
+ ::sal_Int16 nArgPos )
+ {
+ verifyInput( fontRequest.FontDescription,
+ pStr, xIf, nArgPos );
+
+ if( !std::isfinite( fontRequest.CellSize ) )
+ {
+#if OSL_DEBUG_LEVEL > 0
+ throw lang::IllegalArgumentException(
+ OUString::createFromAscii(pStr) +
+ ": verifyInput(): font request's CellSize value contains infinite or NAN",
+ xIf, nArgPos );
+#else
+ throw lang::IllegalArgumentException();
+#endif
+ }
+
+ if( !std::isfinite( fontRequest.ReferenceAdvancement ) )
+ {
+#if OSL_DEBUG_LEVEL > 0
+ throw lang::IllegalArgumentException(
+ OUString::createFromAscii(pStr) +
+ ": verifyInput(): font request's ReferenceAdvancement value contains infinite or NAN",
+ xIf, nArgPos );
+#else
+ throw lang::IllegalArgumentException();
+#endif
+ }
+
+ if( fontRequest.CellSize != 0.0 &&
+ fontRequest.ReferenceAdvancement != 0.0 )
+ {
+#if OSL_DEBUG_LEVEL > 0
+ throw lang::IllegalArgumentException(
+ OUString::createFromAscii(pStr) +
+ ": verifyInput(): font request's CellSize and ReferenceAdvancement are mutually exclusive, one of them must be 0.0",
+ xIf, nArgPos );
+#else
+ throw lang::IllegalArgumentException();
+#endif
+ }
+ }
+
+ void verifyIndexRange( const geometry::IntegerRectangle2D& rect,
+ const geometry::IntegerSize2D& size )
+ {
+ const ::basegfx::B2IRange aRect(
+ ::basegfx::unotools::b2IRectangleFromIntegerRectangle2D(
+ rect ) );
+
+ if( aRect.getMinX() < 0 ||
+ aRect.getMaxX() > size.Width ||
+ aRect.getMinY() < 0 ||
+ aRect.getMaxY() > size.Height )
+ {
+ throw css::lang::IndexOutOfBoundsException();
+ }
+ }
+
+ void verifyIndexRange( const geometry::IntegerPoint2D& pos,
+ const geometry::IntegerSize2D& size )
+ {
+ if( pos.X < 0 ||
+ pos.X > size.Width ||
+ pos.Y < 0 ||
+ pos.Y > size.Height )
+ {
+ throw css::lang::IndexOutOfBoundsException();
+ }
+ }
+
+ void verifyBitmapSize( const geometry::IntegerSize2D& size,
+ const char* pStr,
+ const uno::Reference< uno::XInterface >& xIf )
+ {
+ if( size.Width <= 0 )
+ {
+#if OSL_DEBUG_LEVEL > 0
+ throw lang::IllegalArgumentException(
+ OUString::createFromAscii(pStr) +
+ ": verifyBitmapSize(): size has 0 or negative width (value: " +
+ OUString::number(size.Width) + ")",
+ xIf, 0 );
+#else
+ (void)pStr; (void)xIf;
+ throw lang::IllegalArgumentException();
+#endif
+ }
+
+ if( size.Height > 0 )
+ return;
+
+#if OSL_DEBUG_LEVEL > 0
+ throw lang::IllegalArgumentException(
+ OUString::createFromAscii(pStr) +
+ ": verifyBitmapSize(): size has 0 or negative height (value: " +
+ OUString::number(size.Height) +
+ ")",
+ xIf, 0 );
+#else
+ throw lang::IllegalArgumentException();
+#endif
+ }
+
+ void verifySpriteSize( const geometry::RealSize2D& size,
+ const char* pStr,
+ const uno::Reference< uno::XInterface >& xIf )
+ {
+ if( size.Width <= 0.0 )
+ {
+#if OSL_DEBUG_LEVEL > 0
+ throw lang::IllegalArgumentException(
+ OUString::createFromAscii(pStr) +
+ ": verifySpriteSize(): size has 0 or negative width (value: " +
+ OUString::number(size.Width) + ")",
+ xIf, 0 );
+#else
+ (void)pStr; (void)xIf;
+ throw lang::IllegalArgumentException();
+#endif
+ }
+
+ if( size.Height <= 0.0 )
+ {
+#if OSL_DEBUG_LEVEL > 0
+ throw lang::IllegalArgumentException(
+ OUString::createFromAscii(pStr) +
+ ": verifySpriteSize(): size has 0 or negative height (value: " +
+ OUString::number(size.Height) + ")",
+ xIf, 0 );
+#else
+ throw lang::IllegalArgumentException();
+#endif
+ }
+ }
+
+
+} // namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/vcl/backbuffer.cxx b/canvas/source/vcl/backbuffer.cxx
new file mode 100644
index 000000000..684a02c8b
--- /dev/null
+++ b/canvas/source/vcl/backbuffer.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 <vcl/svapp.hxx>
+
+#include "backbuffer.hxx"
+#include "impltools.hxx"
+
+
+namespace vclcanvas
+{
+ BackBuffer::BackBuffer( const OutputDevice& rRefDevice ) :
+ maVDev( VclPtr<VirtualDevice>::Create( rRefDevice, DeviceFormat::DEFAULT ) )
+ {
+ tools::SetDefaultDeviceAntiAliasing( maVDev );
+ }
+
+ BackBuffer::~BackBuffer()
+ {
+ SolarMutexGuard aGuard;
+ maVDev.disposeAndClear();
+ }
+
+ OutputDevice& BackBuffer::getOutDev()
+ {
+ return *maVDev;
+ }
+
+ const OutputDevice& BackBuffer::getOutDev() const
+ {
+ return *maVDev;
+ }
+
+ void BackBuffer::setSize( const ::Size& rNewSize )
+ {
+ maVDev->SetOutputSizePixel( rNewSize );
+ }
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/vcl/backbuffer.hxx b/canvas/source/vcl/backbuffer.hxx
new file mode 100644
index 000000000..0e31111b6
--- /dev/null
+++ b/canvas/source/vcl/backbuffer.hxx
@@ -0,0 +1,50 @@
+/* -*- 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 <vcl/virdev.hxx>
+
+#include "outdevprovider.hxx"
+
+#include <memory>
+
+namespace vclcanvas
+{
+ /// Background buffer abstraction
+ class BackBuffer : public OutDevProvider
+ {
+ public:
+ /** Create a backbuffer for given reference device */
+ BackBuffer( const OutputDevice& rRefDevice );
+ virtual ~BackBuffer() override;
+
+ virtual OutputDevice& getOutDev() override;
+ virtual const OutputDevice& getOutDev() const override;
+
+ void setSize( const ::Size& rNewSize );
+
+ private:
+ VclPtr< VirtualDevice > maVDev;
+ };
+
+ typedef std::shared_ptr< BackBuffer > BackBufferSharedPtr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/vcl/bitmapbackbuffer.cxx b/canvas/source/vcl/bitmapbackbuffer.cxx
new file mode 100644
index 000000000..d6f87e3c0
--- /dev/null
+++ b/canvas/source/vcl/bitmapbackbuffer.cxx
@@ -0,0 +1,147 @@
+/* -*- 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/diagnose.h>
+#include <vcl/bitmapex.hxx>
+#include <vcl/svapp.hxx>
+
+#include "bitmapbackbuffer.hxx"
+#include "impltools.hxx"
+
+namespace vclcanvas
+{
+ BitmapBackBuffer::BitmapBackBuffer( const BitmapEx& rBitmap,
+ const OutputDevice& rRefDevice ) :
+ maBitmap( rBitmap ),
+ mpVDev( nullptr ),
+ mrRefDevice( rRefDevice ),
+ mbBitmapContentIsCurrent( false ),
+ mbVDevContentIsCurrent( false )
+ {
+ }
+
+ BitmapBackBuffer::~BitmapBackBuffer()
+ {
+ // make sure solar mutex is held on deletion (other methods
+ // are supposed to be called with already locked solar mutex)
+ SolarMutexGuard aGuard;
+
+ mpVDev.disposeAndClear();
+ }
+
+ OutputDevice& BitmapBackBuffer::getOutDev()
+ {
+ createVDev();
+ updateVDev();
+ return *mpVDev;
+ }
+
+ const OutputDevice& BitmapBackBuffer::getOutDev() const
+ {
+ createVDev();
+ updateVDev();
+ return *mpVDev;
+ }
+
+ void BitmapBackBuffer::clear()
+ {
+ // force current content to bitmap, make all transparent white
+ getBitmapReference().Erase(COL_TRANSPARENT);
+ }
+
+ BitmapEx& BitmapBackBuffer::getBitmapReference()
+ {
+ OSL_ENSURE( !mbBitmapContentIsCurrent || !mbVDevContentIsCurrent,
+ "BitmapBackBuffer::getBitmapReference(): Both bitmap and VDev are valid?!" );
+
+ if( mbVDevContentIsCurrent && mpVDev )
+ {
+ // VDev content is more current than bitmap - copy contents before!
+ mpVDev->EnableMapMode( false );
+ mpVDev->SetAntialiasing( AntialiasingFlags::Enable );
+ const Point aEmptyPoint;
+ *maBitmap = mpVDev->GetBitmapEx( aEmptyPoint,
+ mpVDev->GetOutputSizePixel() );
+ }
+
+ // client queries bitmap, and will possibly alter content -
+ // next time, VDev needs to be updated
+ mbBitmapContentIsCurrent = true;
+ mbVDevContentIsCurrent = false;
+
+ return *maBitmap;
+ }
+
+ Size BitmapBackBuffer::getBitmapSizePixel() const
+ {
+ Size aSize = maBitmap->GetSizePixel();
+
+ if( mbVDevContentIsCurrent && mpVDev )
+ {
+ mpVDev->EnableMapMode( false );
+ mpVDev->SetAntialiasing( AntialiasingFlags::Enable );
+ aSize = mpVDev->GetOutputSizePixel();
+ }
+
+ return aSize;
+ }
+
+ void BitmapBackBuffer::createVDev() const
+ {
+ if( mpVDev )
+ return;
+
+ // VDev not yet created, do it now. Create an alpha-VDev,
+ // if bitmap has transparency.
+ mpVDev = maBitmap->IsAlpha() ?
+ VclPtr<VirtualDevice>::Create( mrRefDevice, DeviceFormat::DEFAULT, DeviceFormat::DEFAULT ) :
+ VclPtr<VirtualDevice>::Create( mrRefDevice );
+
+ OSL_ENSURE( mpVDev,
+ "BitmapBackBuffer::createVDev(): Unable to create VirtualDevice" );
+
+ mpVDev->SetOutputSizePixel( maBitmap->GetSizePixel() );
+
+ tools::SetDefaultDeviceAntiAliasing( mpVDev );
+ }
+
+ void BitmapBackBuffer::updateVDev() const
+ {
+ OSL_ENSURE( !mbBitmapContentIsCurrent || !mbVDevContentIsCurrent,
+ "BitmapBackBuffer::updateVDev(): Both bitmap and VDev are valid?!" );
+
+ if( mpVDev && mbBitmapContentIsCurrent )
+ {
+ // fill with bitmap content
+ mpVDev->EnableMapMode( false );
+ mpVDev->SetAntialiasing( AntialiasingFlags::Enable );
+ const Point aEmptyPoint;
+ mpVDev->DrawBitmapEx( aEmptyPoint, *maBitmap );
+ }
+
+ // canvas queried the VDev, and will possibly paint into
+ // it. Next time, bitmap must be updated
+ mbBitmapContentIsCurrent = false;
+ mbVDevContentIsCurrent = true;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/vcl/bitmapbackbuffer.hxx b/canvas/source/vcl/bitmapbackbuffer.hxx
new file mode 100644
index 000000000..9098e7e93
--- /dev/null
+++ b/canvas/source/vcl/bitmapbackbuffer.hxx
@@ -0,0 +1,93 @@
+/* -*- 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 <vcl/virdev.hxx>
+#include <vcl/bitmapex.hxx>
+
+#include <vclwrapper.hxx>
+#include "outdevprovider.hxx"
+
+#include <memory>
+
+namespace vclcanvas
+{
+ /** Backbuffer implementation for canvas bitmap.
+
+ This class abstracts away the renderable bitmap for the bitmap
+ canvas. The actual VirtualDevice is only created when
+ necessary, which makes read-only bitmaps a lot smaller.
+ */
+ class BitmapBackBuffer : public OutDevProvider
+ {
+ public:
+ /** Create a backbuffer for given reference device
+ */
+ BitmapBackBuffer( const BitmapEx& rBitmap,
+ const OutputDevice& rRefDevice );
+
+ virtual ~BitmapBackBuffer() override;
+
+ virtual OutputDevice& getOutDev() override;
+ virtual const OutputDevice& getOutDev() const override;
+
+ /// Clear the underlying bitmap to white, all transparent
+ void clear();
+
+ /** Exposing our internal bitmap. Only to be used from
+ CanvasBitmapHelper
+
+ @internal
+ */
+ BitmapEx& getBitmapReference();
+ Size getBitmapSizePixel() const;
+
+ private:
+ void createVDev() const;
+ void updateVDev() const;
+
+ ::canvas::vcltools::VCLObject<BitmapEx> maBitmap;
+ mutable VclPtr<VirtualDevice> mpVDev; // created only on demand
+
+ const OutputDevice& mrRefDevice;
+
+ /** When true, the bitmap contains the last valid
+ content. When false, and mbVDevContentIsCurrent is true,
+ the VDev contains the last valid content (which must be
+ copied back to the bitmap, when getBitmapReference() is
+ called). When both are false, this object is just
+ initialized.
+ */
+ mutable bool mbBitmapContentIsCurrent;
+
+ /** When true, and mpVDev is non-NULL, the VDev contains the
+ last valid content. When false, and
+ mbBitmapContentIsCurrent is true, the bitmap contains the
+ last valid content. When both are false, this object is
+ just initialized.
+ */
+ mutable bool mbVDevContentIsCurrent;
+ };
+
+ typedef std::shared_ptr< BitmapBackBuffer > BitmapBackBufferSharedPtr;
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/vcl/cachedbitmap.cxx b/canvas/source/vcl/cachedbitmap.cxx
new file mode 100644
index 000000000..2d44133a7
--- /dev/null
+++ b/canvas/source/vcl/cachedbitmap.cxx
@@ -0,0 +1,93 @@
+/* -*- 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 <com/sun/star/rendering/XCanvas.hpp>
+#include <com/sun/star/rendering/RepaintResult.hpp>
+#include <utility>
+#include <tools/diagnose_ex.h>
+
+#include "cachedbitmap.hxx"
+#include "repainttarget.hxx"
+
+
+using namespace ::com::sun::star;
+
+namespace vclcanvas
+{
+ CachedBitmap::CachedBitmap( GraphicObjectSharedPtr xGraphicObject,
+ const ::Point& rPoint,
+ const ::Size& rSize,
+ const GraphicAttr& rAttr,
+ const rendering::ViewState& rUsedViewState,
+ rendering::RenderState aUsedRenderState,
+ const uno::Reference< rendering::XCanvas >& rTarget ) :
+ CachedPrimitiveBase( rUsedViewState, rTarget ),
+ mpGraphicObject(std::move( xGraphicObject )),
+ maRenderState(std::move(aUsedRenderState)),
+ maPoint( rPoint ),
+ maSize( rSize ),
+ maAttributes( rAttr )
+ {
+ }
+
+ void SAL_CALL CachedBitmap::disposing()
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ mpGraphicObject.reset();
+
+ CachedPrimitiveBase::disposing();
+ }
+
+ ::sal_Int8 CachedBitmap::doRedraw( const rendering::ViewState& rNewState,
+ const rendering::ViewState& rOldState,
+ const uno::Reference< rendering::XCanvas >& rTargetCanvas,
+ bool bSameViewTransform )
+ {
+ ENSURE_OR_THROW( bSameViewTransform,
+ "CachedBitmap::doRedraw(): base called with changed view transform "
+ "(told otherwise during construction)" );
+
+ // TODO(P1): Could adapt to modified clips as well
+ if( rNewState.Clip != rOldState.Clip )
+ return rendering::RepaintResult::FAILED;
+
+ RepaintTarget* pTarget = dynamic_cast< RepaintTarget* >(rTargetCanvas.get());
+
+ ENSURE_OR_THROW( pTarget,
+ "CachedBitmap::redraw(): cannot cast target to RepaintTarget" );
+
+ if( !pTarget->repaint( mpGraphicObject,
+ rNewState,
+ maRenderState,
+ maPoint,
+ maSize,
+ maAttributes ) )
+ {
+ // target failed to repaint
+ return rendering::RepaintResult::FAILED;
+ }
+
+ return rendering::RepaintResult::REDRAWN;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/vcl/cachedbitmap.hxx b/canvas/source/vcl/cachedbitmap.hxx
new file mode 100644
index 000000000..0ef04654d
--- /dev/null
+++ b/canvas/source/vcl/cachedbitmap.hxx
@@ -0,0 +1,66 @@
+/* -*- 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/RenderState.hpp>
+#include <base/cachedprimitivebase.hxx>
+#include <vcl/GraphicObject.hxx>
+#include <memory>
+
+
+/* Definition of CachedBitmap class */
+
+namespace vclcanvas
+{
+ typedef std::shared_ptr< GraphicObject > GraphicObjectSharedPtr;
+
+ class CachedBitmap : public ::canvas::CachedPrimitiveBase
+ {
+ public:
+
+ /** Create an XCachedPrimitive for given GraphicObject
+ */
+ CachedBitmap( GraphicObjectSharedPtr xGraphicObject,
+ const ::Point& rPoint,
+ const ::Size& rSize,
+ const GraphicAttr& rAttr,
+ const css::rendering::ViewState& rUsedViewState,
+ css::rendering::RenderState aUsedRenderState,
+ const css::uno::Reference< css::rendering::XCanvas >& rTarget );
+
+ /// Dispose all internal references
+ virtual void SAL_CALL disposing() override;
+
+ private:
+ virtual ::sal_Int8 doRedraw( const css::rendering::ViewState& rNewState,
+ const css::rendering::ViewState& rOldState,
+ const css::uno::Reference< css::rendering::XCanvas >& rTargetCanvas,
+ bool bSameViewTransform ) override;
+
+
+ GraphicObjectSharedPtr mpGraphicObject;
+ const css::rendering::RenderState maRenderState;
+ const ::Point maPoint;
+ const ::Size maSize;
+ const GraphicAttr maAttributes;
+ };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/vcl/canvas.cxx b/canvas/source/vcl/canvas.cxx
new file mode 100644
index 000000000..e9ebf85b3
--- /dev/null
+++ b/canvas/source/vcl/canvas.cxx
@@ -0,0 +1,122 @@
+/* -*- 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 "canvas.hxx"
+
+#include <com/sun/star/lang/NoSupportException.hpp>
+#include <sal/log.hxx>
+#include <tools/diagnose_ex.h>
+#include <vcl/outdev.hxx>
+
+#include "outdevholder.hxx"
+
+using namespace ::com::sun::star;
+
+namespace vclcanvas
+{
+ Canvas::Canvas( const uno::Sequence< uno::Any >& aArguments,
+ const uno::Reference< uno::XComponentContext >& /*rxContext*/ ) :
+ maArguments(aArguments)
+ {
+ }
+
+ void Canvas::initialize()
+ {
+ // #i64742# Only perform initialization when not in probe mode
+ if( !maArguments.hasElements() )
+ return;
+
+ /* maArguments:
+ 0: ptr to creating instance (Window or VirtualDevice)
+ 1: current bounds of creating instance
+ 2: bool, denoting always on top state for Window (always false for VirtualDevice)
+ 3: XWindow for creating Window (or empty for VirtualDevice)
+ 4: SystemGraphicsData as a streamed Any
+ */
+ SolarMutexGuard aGuard;
+
+ SAL_INFO("canvas.vcl", "VCLCanvas::initialize called" );
+
+ ENSURE_ARG_OR_THROW( maArguments.getLength() >= 5 &&
+ maArguments[0].getValueTypeClass() == uno::TypeClass_HYPER,
+ "Canvas::initialize: wrong number of arguments, or wrong types" );
+
+ sal_Int64 nPtr = 0;
+ maArguments[0] >>= nPtr;
+
+ OutputDevice* pOutDev = reinterpret_cast<OutputDevice*>(nPtr);
+ if( !pOutDev )
+ throw lang::NoSupportException("Passed OutDev invalid!", nullptr);
+
+ OutDevProviderSharedPtr pOutdevProvider = std::make_shared<OutDevHolder>(*pOutDev);
+
+ // setup helper
+ maDeviceHelper.init( pOutdevProvider );
+ maCanvasHelper.init( *this,
+ pOutdevProvider,
+ true, // OutDev state preservation
+ false ); // no alpha on surface
+
+ maArguments.realloc(0);
+ }
+
+ Canvas::~Canvas()
+ {
+ SAL_INFO("canvas.vcl", "VCLCanvas destroyed" );
+ }
+
+ void Canvas::disposeThis()
+ {
+ SolarMutexGuard aGuard;
+
+ // forward to parent
+ CanvasBaseT::disposeThis();
+ }
+
+ OUString SAL_CALL Canvas::getServiceName( )
+ {
+ return "com.sun.star.rendering.Canvas.VCL";
+ }
+
+ bool Canvas::repaint( const GraphicObjectSharedPtr& rGrf,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState,
+ const ::Point& rPt,
+ const ::Size& rSz,
+ const GraphicAttr& rAttr ) const
+ {
+ SolarMutexGuard aGuard;
+
+ return maCanvasHelper.repaint( rGrf, viewState, renderState, rPt, rSz, rAttr );
+ }
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_rendering_Canvas_VCL_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& args)
+{
+ rtl::Reference<vclcanvas::Canvas> p = new vclcanvas::Canvas(args, context);
+ cppu::acquire(p.get());
+ p->initialize();
+ return static_cast<cppu::OWeakObject*>(p.get());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/vcl/canvas.hxx b/canvas/source/vcl/canvas.hxx
new file mode 100644
index 000000000..be7d7858e
--- /dev/null
+++ b/canvas/source/vcl/canvas.hxx
@@ -0,0 +1,112 @@
+/* -*- 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/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 <cppuhelper/compbase.hxx>
+#include <comphelper/uno3.hxx>
+
+#include <base/basemutexhelper.hxx>
+#include <base/bitmapcanvasbase.hxx>
+#include <base/integerbitmapbase.hxx>
+#include <base/graphicdevicebase.hxx>
+
+#include "canvashelper.hxx"
+#include "impltools.hxx"
+#include "devicehelper.hxx"
+#include "repainttarget.hxx"
+
+namespace vclcanvas
+{
+ typedef ::cppu::WeakComponentImplHelper< css::rendering::XBitmapCanvas,
+ css::rendering::XIntegerBitmap,
+ css::rendering::XGraphicDevice,
+ css::lang::XMultiServiceFactory,
+ css::util::XUpdatable,
+ css::beans::XPropertySet,
+ css::lang::XServiceName > GraphicDeviceBase_Base;
+ typedef ::canvas::GraphicDeviceBase< ::canvas::BaseMutexHelper< GraphicDeviceBase_Base >,
+ DeviceHelper,
+ tools::LocalGuard,
+ ::cppu::OWeakObject > CanvasBase_Base;
+ typedef ::canvas::IntegerBitmapBase<
+ canvas::BitmapCanvasBase2<
+ CanvasBase_Base,
+ CanvasHelper,
+ tools::LocalGuard,
+ ::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 RepaintTarget
+ {
+ public:
+ Canvas( const css::uno::Sequence<
+ css::uno::Any >& aArguments,
+ const css::uno::Reference<
+ css::uno::XComponentContext >& rxContext );
+
+ void initialize();
+
+ /// For resource tracking
+ virtual ~Canvas() override;
+
+ /// 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, GraphicDeviceBase_Base, ::cppu::WeakComponentImplHelperBase )
+
+ // XServiceName
+ virtual OUString SAL_CALL getServiceName( ) override;
+
+ // RepaintTarget
+ virtual bool repaint( const GraphicObjectSharedPtr& rGrf,
+ const css::rendering::ViewState& viewState,
+ const css::rendering::RenderState& renderState,
+ const ::Point& rPt,
+ const ::Size& rSz,
+ const GraphicAttr& rAttr ) const override;
+
+ private:
+ css::uno::Sequence< css::uno::Any > maArguments;
+ };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/vcl/canvasbitmap.cxx b/canvas/source/vcl/canvasbitmap.cxx
new file mode 100644
index 000000000..9ae37c2c4
--- /dev/null
+++ b/canvas/source/vcl/canvasbitmap.cxx
@@ -0,0 +1,122 @@
+/* -*- 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 <cppuhelper/supportsservice.hxx>
+
+#include "canvasbitmap.hxx"
+
+
+using namespace ::com::sun::star;
+
+
+namespace vclcanvas
+{
+ // Currently, the only way to generate an XBitmap is from
+ // XGraphicDevice.getCompatibleBitmap(). Therefore, we don't even
+ // take a bitmap here, but a VDev directly.
+ CanvasBitmap::CanvasBitmap( const ::Size& rSize,
+ bool bAlphaBitmap,
+ rendering::XGraphicDevice& rDevice,
+ const OutDevProviderSharedPtr& rOutDevProvider )
+ {
+ // create bitmap for given reference device
+ // ========================================
+ Bitmap aBitmap(rSize, vcl::PixelFormat::N24_BPP);
+
+ // only create alpha channel bitmap, if factory requested
+ // that. Providing alpha-channeled bitmaps by default has,
+ // especially under VCL, a huge performance penalty (have to
+ // use alpha VDev, then).
+ if( bAlphaBitmap )
+ {
+ AlphaMask aAlpha ( rSize );
+
+ maCanvasHelper.init( BitmapEx( aBitmap, aAlpha ),
+ rDevice,
+ rOutDevProvider );
+ }
+ else
+ {
+ maCanvasHelper.init( BitmapEx( aBitmap ),
+ rDevice,
+ rOutDevProvider );
+ }
+ }
+
+ CanvasBitmap::CanvasBitmap( const BitmapEx& rBitmap,
+ rendering::XGraphicDevice& rDevice,
+ const OutDevProviderSharedPtr& rOutDevProvider )
+ {
+ maCanvasHelper.init( rBitmap, rDevice, rOutDevProvider );
+ }
+
+
+ OUString SAL_CALL CanvasBitmap::getImplementationName( )
+ {
+ return "VCLCanvas.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" };
+ }
+
+ BitmapEx CanvasBitmap::getBitmap() const
+ {
+ SolarMutexGuard aGuard;
+
+ // TODO(T3): Rework to use shared_ptr all over the place for
+ // BmpEx. This is highly un-threadsafe
+ return maCanvasHelper.getBitmap();
+ }
+
+ bool CanvasBitmap::repaint( const GraphicObjectSharedPtr& rGrf,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState,
+ const ::Point& rPt,
+ const ::Size& rSz,
+ const GraphicAttr& rAttr ) const
+ {
+ SolarMutexGuard aGuard;
+
+ mbSurfaceDirty = true;
+
+ return maCanvasHelper.repaint( rGrf, viewState, renderState, rPt, rSz, rAttr );
+ }
+
+ uno::Any SAL_CALL CanvasBitmap::getFastPropertyValue( sal_Int32 nHandle )
+ {
+ if( nHandle == 0 ) {
+ BitmapEx* pBitmapEx = new BitmapEx( getBitmap() );
+
+ return uno::Any( reinterpret_cast<sal_Int64>( pBitmapEx ) );
+ }
+
+ return uno::Any( sal_Int64(0) );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/vcl/canvasbitmap.hxx b/canvas/source/vcl/canvasbitmap.hxx
new file mode 100644
index 000000000..1a95ce8c6
--- /dev/null
+++ b/canvas/source/vcl/canvasbitmap.hxx
@@ -0,0 +1,117 @@
+/* -*- 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 <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 <vcl/bitmapex.hxx>
+
+#include <base/bitmapcanvasbase.hxx>
+#include <base/basemutexhelper.hxx>
+#include <base/integerbitmapbase.hxx>
+#include "canvasbitmaphelper.hxx"
+
+#include "impltools.hxx"
+#include "repainttarget.hxx"
+
+
+/* Definition of CanvasBitmap class */
+
+namespace vclcanvas
+{
+ 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 >,
+ CanvasBitmapHelper,
+ tools::LocalGuard,
+ ::cppu::OWeakObject> > CanvasBitmap_Base;
+
+ class CanvasBitmap : public CanvasBitmap_Base,
+ public RepaintTarget
+ {
+ public:
+ /** Must be called with locked Solar mutex
+
+ @param rSize
+ Size in pixel of the bitmap to generate
+
+ @param bAlphaBitmap
+ When true, bitmap will have an alpha channel
+
+ @param rDevice
+ Reference device, with which bitmap should be compatible
+ */
+ CanvasBitmap( const ::Size& rSize,
+ bool bAlphaBitmap,
+ css::rendering::XGraphicDevice& rDevice,
+ const OutDevProviderSharedPtr& rOutDevProvider );
+
+ /// Must be called with locked Solar mutex
+ CanvasBitmap( const BitmapEx& rBitmap,
+ css::rendering::XGraphicDevice& rDevice,
+ const OutDevProviderSharedPtr& rOutDevProvider );
+
+ // 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;
+
+ // RepaintTarget interface
+ virtual bool repaint( const GraphicObjectSharedPtr& rGrf,
+ const css::rendering::ViewState& viewState,
+ const css::rendering::RenderState& renderState,
+ const ::Point& rPt,
+ const ::Size& rSz,
+ const GraphicAttr& rAttr ) const override;
+
+ /// Not threadsafe! Returned object is shared!
+ BitmapEx getBitmap() const;
+
+ // XFastPropertySet
+ // used to retrieve BitmapEx pointer or X Pixmap handles for this bitmap
+ // handle values have these meanings:
+ // 0 ... get pointer to BitmapEx
+ // 1 ... get X pixmap handle to rgb content
+ // 2 ... get X pixmap handle to alpha mask
+ // returned any contains either BitmapEx pointer or array of three Any value
+ // 1st a bool value: true - free the pixmap after used by XFreePixmap, false do nothing, the pixmap is used internally in the canvas
+ // 2nd the pixmap handle (sal_Int64)
+ // 3rd the pixmap depth
+ 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)
+ */
+ css::uno::Reference<css::rendering::XGraphicDevice> mxDevice;
+ };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/vcl/canvasbitmaphelper.cxx b/canvas/source/vcl/canvasbitmaphelper.cxx
new file mode 100644
index 000000000..85b886d70
--- /dev/null
+++ b/canvas/source/vcl/canvasbitmaphelper.cxx
@@ -0,0 +1,176 @@
+/* -*- 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 <canvas/canvastools.hxx>
+#include <tools/diagnose_ex.h>
+#include <vcl/bitmapex.hxx>
+#include <vcl/BitmapTools.hxx>
+#include <vcl/canvastools.hxx>
+
+#include "canvasbitmap.hxx"
+#include "canvasbitmaphelper.hxx"
+
+
+using namespace ::com::sun::star;
+
+namespace vclcanvas
+{
+ CanvasBitmapHelper::CanvasBitmapHelper()
+ {
+ }
+
+ void CanvasBitmapHelper::init( const BitmapEx& rBitmap,
+ rendering::XGraphicDevice& rDevice,
+ const OutDevProviderSharedPtr& rOutDevReference )
+ {
+ mpOutDevReference = rOutDevReference;
+ mpBackBuffer = std::make_shared<BitmapBackBuffer>( rBitmap, rOutDevReference->getOutDev() );
+
+ // forward new settings to base class (ref device, output
+ // surface, no protection (own backbuffer), alpha depends on
+ // whether BmpEx is transparent or not)
+ CanvasHelper::init( rDevice,
+ mpBackBuffer,
+ false,
+ rBitmap.IsAlpha() );
+ }
+
+ void CanvasBitmapHelper::disposing()
+ {
+ mpBackBuffer.reset();
+ mpOutDevReference.reset();
+
+ // forward to base class
+ CanvasHelper::disposing();
+ }
+
+ geometry::IntegerSize2D CanvasBitmapHelper::getSize() const
+ {
+ if( !mpBackBuffer )
+ return geometry::IntegerSize2D();
+
+ return vcl::unotools::integerSize2DFromSize( mpBackBuffer->getBitmapSizePixel() );
+ }
+
+ void CanvasBitmapHelper::clear()
+ {
+ // are we disposed?
+ if( mpBackBuffer )
+ mpBackBuffer->clear(); // alpha vdev needs special treatment
+ }
+
+ uno::Reference< rendering::XBitmap > CanvasBitmapHelper::getScaledBitmap( const geometry::RealSize2D& newSize,
+ bool beFast )
+ {
+ ENSURE_OR_THROW( mpDevice,
+ "disposed CanvasHelper" );
+
+ SAL_INFO( "canvas.vcl", "::vclcanvas::CanvasBitmapHelper::getScaledBitmap()" );
+
+ if( !mpBackBuffer || mpDevice )
+ return uno::Reference< rendering::XBitmap >(); // we're disposed
+
+ BitmapEx aRes( mpBackBuffer->getBitmapReference() );
+
+ aRes.Scale( vcl::unotools::sizeFromRealSize2D(newSize),
+ beFast ? BmpScaleFlag::Default : BmpScaleFlag::BestQuality );
+
+ return uno::Reference< rendering::XBitmap >(
+ new CanvasBitmap( aRes, *mpDevice, mpOutDevReference ) );
+ }
+
+ uno::Sequence< sal_Int8 > CanvasBitmapHelper::getData( rendering::IntegerBitmapLayout& rLayout,
+ const geometry::IntegerRectangle2D& rect )
+ {
+ SAL_INFO( "canvas.vcl", "::vclcanvas::CanvasBitmapHelper::getData()" );
+
+ if( !mpBackBuffer )
+ return uno::Sequence< sal_Int8 >(); // we're disposed
+
+ rLayout = getMemoryLayout();
+
+ // TODO(F1): Support more formats.
+ const Size aBmpSize( mpBackBuffer->getBitmapReference().GetSizePixel() );
+
+ rLayout.ScanLines = aBmpSize.Height();
+ rLayout.ScanLineBytes = aBmpSize.Width()*4;
+ rLayout.ScanLineStride = rLayout.ScanLineBytes;
+
+ uno::Sequence< sal_Int8 > aRes = vcl::bitmap::CanvasExtractBitmapData(mpBackBuffer->getBitmapReference(), rect);
+ return aRes;
+ }
+
+ uno::Sequence< sal_Int8 > CanvasBitmapHelper::getPixel( rendering::IntegerBitmapLayout& rLayout,
+ const geometry::IntegerPoint2D& pos )
+ {
+ SAL_INFO( "canvas.vcl", "::vclcanvas::CanvasBitmapHelper::getPixel()" );
+
+ if( !mpBackBuffer )
+ return uno::Sequence< sal_Int8 >(); // we're disposed
+
+ rLayout = getMemoryLayout();
+ rLayout.ScanLines = 1;
+ rLayout.ScanLineBytes = 4;
+ rLayout.ScanLineStride = rLayout.ScanLineBytes;
+
+ const Size aBmpSize( mpBackBuffer->getBitmapReference().GetSizePixel() );
+
+ ENSURE_ARG_OR_THROW( pos.X >= 0 && pos.X < aBmpSize.Width(),
+ "X coordinate out of bounds" );
+ ENSURE_ARG_OR_THROW( pos.Y >= 0 && pos.Y < aBmpSize.Height(),
+ "Y coordinate out of bounds" );
+
+ ::Color aColor = mpBackBuffer->getBitmapReference().GetPixelColor(pos.X, pos.Y);
+
+ uno::Sequence< sal_Int8 > aRes( 4 );
+ sal_Int8* pRes = aRes.getArray();
+ pRes[ 0 ] = aColor.GetRed();
+ pRes[ 1 ] = aColor.GetGreen();
+ pRes[ 2 ] = aColor.GetBlue();
+ pRes[ 3 ] = 255 - aColor.GetAlpha();
+
+ return aRes;
+ }
+
+ rendering::IntegerBitmapLayout CanvasBitmapHelper::getMemoryLayout() const
+ {
+ if( !mpOutDevProvider )
+ return rendering::IntegerBitmapLayout(); // we're disposed
+
+ rendering::IntegerBitmapLayout aBitmapLayout( ::canvas::tools::getStdMemoryLayout(getSize()) );
+ if ( !hasAlpha() )
+ aBitmapLayout.ColorSpace = canvas::tools::getStdColorSpaceWithoutAlpha();
+
+ return aBitmapLayout;
+ }
+
+ BitmapEx CanvasBitmapHelper::getBitmap() const
+ {
+ if( !mpBackBuffer )
+ return BitmapEx(); // we're disposed
+ else
+ return mpBackBuffer->getBitmapReference();
+ }
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/vcl/canvasbitmaphelper.hxx b/canvas/source/vcl/canvasbitmaphelper.hxx
new file mode 100644
index 000000000..7e75f92e8
--- /dev/null
+++ b/canvas/source/vcl/canvasbitmaphelper.hxx
@@ -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 .
+ */
+
+#pragma once
+
+#include "canvashelper.hxx"
+
+#include <vcl/bitmapex.hxx>
+
+#include "bitmapbackbuffer.hxx"
+
+
+namespace vclcanvas
+{
+ /** Helper class for basic canvasbitmap functionality. Extends
+ CanvasHelper with some CanvasBitmap specialities, such as alpha
+ support.
+
+ Note that a plain CanvasHelper, although it does support the
+ XBitmap interface, has no provision for alpha channel on VCL
+ (at least no efficient one. If the alpha VDev one day gets
+ part of SAL, we might change that).
+ */
+ class CanvasBitmapHelper : public CanvasHelper
+ {
+ public:
+ CanvasBitmapHelper();
+
+ /** Set a new bitmap on this helper.
+
+ This method late-initializes the bitmap canvas helper,
+ providing it with the necessary device and output
+ objects. The internally stored bitmap representation is
+ updated from the given bitmap, including any size
+ changes. Note that the CanvasHelper does <em>not</em> take
+ ownership of the SpriteCanvas object, nor does it perform
+ any reference counting. Thus, to prevent reference counted
+ objects from deletion, the user of this class is
+ responsible for holding ref-counted references to those
+ objects!
+
+ @param rBitmap
+ Content of this bitmap is used as our new content (our
+ internal size is adapted to the size of the bitmap given)
+
+ @param rDevice
+ Reference device for this canvas bitmap
+
+ @param rOutDevProvider
+ Reference output device. Used to create matching bitmap.
+ */
+ void init( const BitmapEx& rBitmap,
+ css::rendering::XGraphicDevice& rDevice,
+ const OutDevProviderSharedPtr& rOutDevProvider );
+
+
+ // Overridden CanvasHelper functionality
+ // =====================================
+
+ void disposing();
+
+ void clear();
+
+ css::geometry::IntegerSize2D getSize() const;
+
+ 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 );
+
+ css::uno::Sequence< sal_Int8 >
+ getPixel( css::rendering::IntegerBitmapLayout& bitmapLayout,
+ const css::geometry::IntegerPoint2D& pos );
+
+ css::rendering::IntegerBitmapLayout getMemoryLayout() const;
+
+ /// @internal
+ BitmapEx getBitmap() const;
+
+ private:
+
+ BitmapBackBufferSharedPtr mpBackBuffer;
+ OutDevProviderSharedPtr mpOutDevReference;
+ };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/vcl/canvascustomsprite.cxx b/canvas/source/vcl/canvascustomsprite.cxx
new file mode 100644
index 000000000..4bdebd448
--- /dev/null
+++ b/canvas/source/vcl/canvascustomsprite.cxx
@@ -0,0 +1,158 @@
+/* -*- 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/point/b2dpoint.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <tools/diagnose_ex.h>
+#include <vcl/outdev.hxx>
+
+#include "canvascustomsprite.hxx"
+
+using namespace ::com::sun::star;
+
+
+namespace vclcanvas
+{
+
+ CanvasCustomSprite::CanvasCustomSprite( const geometry::RealSize2D& rSpriteSize,
+ rendering::XGraphicDevice& rDevice,
+ const ::canvas::SpriteSurface::Reference& rOwningSpriteCanvas,
+ const OutDevProviderSharedPtr& rOutDevProvider,
+ bool bShowSpriteBounds )
+ {
+ ENSURE_OR_THROW( rOwningSpriteCanvas &&
+ rOutDevProvider,
+ "CanvasCustomSprite::CanvasCustomSprite(): Invalid sprite canvas" );
+
+ // setup back buffer
+
+
+ const ::Size aSize(
+ static_cast<sal_Int32>( std::max( 1.0,
+ ceil( rSpriteSize.Width ))), // round up to nearest int,
+ // enforce sprite to have at
+ // least (1,1) pixel size
+ static_cast<sal_Int32>( std::max( 1.0,
+ ceil( rSpriteSize.Height ))) );
+
+ // create content backbuffer in screen depth
+ BackBufferSharedPtr pBackBuffer = std::make_shared<BackBuffer>( rOutDevProvider->getOutDev() );
+ pBackBuffer->setSize( aSize );
+
+ // create mask backbuffer
+ BackBufferSharedPtr pBackBufferMask = std::make_shared<BackBuffer>( rOutDevProvider->getOutDev() );
+ pBackBufferMask->setSize( aSize );
+
+ // TODO(F1): Implement alpha vdev (could prolly enable
+ // antialiasing again, then)
+
+ // disable font antialiasing (causes ugly shadows otherwise)
+ pBackBuffer->getOutDev().SetAntialiasing( AntialiasingFlags::DisableText );
+ pBackBufferMask->getOutDev().SetAntialiasing( AntialiasingFlags::DisableText );
+
+ // set mask vdev drawmode, such that everything is painted
+ // black. That leaves us with a binary image, white for
+ // background, black for painted content
+ pBackBufferMask->getOutDev().SetDrawMode( DrawModeFlags::BlackLine | DrawModeFlags::BlackFill | DrawModeFlags::BlackText |
+ DrawModeFlags::BlackGradient | DrawModeFlags::BlackBitmap );
+
+
+ // setup canvas helper
+
+
+ // always render into back buffer, don't preserve state (it's
+ // our private VDev, after all), have notion of alpha
+ maCanvasHelper.init( rDevice,
+ pBackBuffer,
+ false,
+ true );
+ maCanvasHelper.setBackgroundOutDev( pBackBufferMask );
+
+
+ // setup sprite helper
+
+
+ maSpriteHelper.init( rSpriteSize,
+ rOwningSpriteCanvas,
+ pBackBuffer,
+ pBackBufferMask,
+ bShowSpriteBounds );
+
+ // clear sprite to 100% transparent
+ maCanvasHelper.clear();
+ }
+
+ OUString SAL_CALL CanvasCustomSprite::getImplementationName()
+ {
+ return "VCLCanvas.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" };
+ }
+
+ // Sprite
+ void CanvasCustomSprite::redraw( OutputDevice& rOutDev,
+ bool bBufferedUpdate ) const
+ {
+ SolarMutexGuard aGuard;
+
+ redraw( rOutDev, maSpriteHelper.getPosPixel(), bBufferedUpdate );
+ }
+
+ void CanvasCustomSprite::redraw( OutputDevice& rOutDev,
+ const ::basegfx::B2DPoint& rOrigOutputPos,
+ bool bBufferedUpdate ) const
+ {
+ SolarMutexGuard aGuard;
+
+ maSpriteHelper.redraw( rOutDev,
+ rOrigOutputPos,
+ mbSurfaceDirty,
+ bBufferedUpdate );
+
+ mbSurfaceDirty = false;
+ }
+
+ bool CanvasCustomSprite::repaint( const GraphicObjectSharedPtr& rGrf,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState,
+ const ::Point& rPt,
+ const ::Size& rSz,
+ const GraphicAttr& rAttr ) const
+ {
+ SolarMutexGuard aGuard;
+
+ mbSurfaceDirty = true;
+
+ return maCanvasHelper.repaint( rGrf, viewState, renderState, rPt, rSz, rAttr );
+ }
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/vcl/canvascustomsprite.hxx b/canvas/source/vcl/canvascustomsprite.hxx
new file mode 100644
index 000000000..1a7d3ccfb
--- /dev/null
+++ b/canvas/source/vcl/canvascustomsprite.hxx
@@ -0,0 +1,116 @@
+/* -*- 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/rendering/XBitmapCanvas.hpp>
+#include <com/sun/star/rendering/XCustomSprite.hpp>
+#include <com/sun/star/rendering/XIntegerBitmap.hpp>
+
+#include <base/basemutexhelper.hxx>
+#include <base/spritesurface.hxx>
+#include <base/canvascustomspritebase.hxx>
+
+#include "sprite.hxx"
+#include "canvashelper.hxx"
+#include "spritehelper.hxx"
+#include "impltools.hxx"
+#include "repainttarget.hxx"
+
+
+namespace vclcanvas
+{
+ 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,
+ CanvasHelper,
+ tools::LocalGuard,
+ ::cppu::OWeakObject > CanvasCustomSpriteBaseT;
+
+ /* Definition of CanvasCustomSprite class */
+
+ class CanvasCustomSprite : public CanvasCustomSpriteBaseT,
+ public RepaintTarget
+ {
+ public:
+ CanvasCustomSprite( const css::geometry::RealSize2D& rSpriteSize,
+ css::rendering::XGraphicDevice& rDevice,
+ const ::canvas::SpriteSurface::Reference& rOwningSpriteCanvas,
+ const OutDevProviderSharedPtr& rOutDevProvider,
+ bool bShowSpriteBounds );
+
+ // 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( OutputDevice& rOutDev,
+ bool bBufferedUpdate ) const override;
+ virtual void redraw( OutputDevice& rOutDev,
+ const ::basegfx::B2DPoint& rPos,
+ bool bBufferedUpdate ) const override;
+
+ // RepaintTarget
+ virtual bool repaint( const GraphicObjectSharedPtr& rGrf,
+ const css::rendering::ViewState& viewState,
+ const css::rendering::RenderState& renderState,
+ const ::Point& rPt,
+ const ::Size& rSz,
+ const GraphicAttr& rAttr ) const override;
+ };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/vcl/canvasfont.cxx b/canvas/source/vcl/canvasfont.cxx
new file mode 100644
index 000000000..e7fab0492
--- /dev/null
+++ b/canvas/source/vcl/canvasfont.cxx
@@ -0,0 +1,177 @@
+/* -*- 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 <canvas/canvastools.hxx>
+#include <com/sun/star/rendering/PanoseProportion.hpp>
+#include <cppuhelper/supportsservice.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <rtl/math.hxx>
+#include <vcl/metric.hxx>
+#include <vcl/virdev.hxx>
+
+#include "canvasfont.hxx"
+#include "textlayout.hxx"
+
+using namespace ::com::sun::star;
+
+
+namespace vclcanvas
+{
+ CanvasFont::CanvasFont( const rendering::FontRequest& rFontRequest,
+ const uno::Sequence< beans::PropertyValue >& rExtraFontProperties,
+ const geometry::Matrix2D& rFontMatrix,
+ rendering::XGraphicDevice& rDevice,
+ const OutDevProviderSharedPtr& rOutDevProvider ) :
+ CanvasFont_Base( m_aMutex ),
+ maFont( vcl::Font( rFontRequest.FontDescription.FamilyName,
+ rFontRequest.FontDescription.StyleName,
+ Size( 0, ::basegfx::fround(rFontRequest.CellSize) ) ) ),
+ maFontRequest( rFontRequest ),
+ mpRefDevice( &rDevice ),
+ mpOutDevProvider( rOutDevProvider )
+ {
+ maFont->SetAlignment( ALIGN_BASELINE );
+ maFont->SetCharSet( (rFontRequest.FontDescription.IsSymbolFont==css::util::TriState_YES) ? RTL_TEXTENCODING_SYMBOL : RTL_TEXTENCODING_UNICODE );
+ maFont->SetVertical( rFontRequest.FontDescription.IsVertical==css::util::TriState_YES );
+
+ // TODO(F2): improve panose->vclenum conversion
+ maFont->SetWeight( static_cast<FontWeight>(rFontRequest.FontDescription.FontDescription.Weight) );
+ maFont->SetItalic( (rFontRequest.FontDescription.FontDescription.Letterform<=8) ? ITALIC_NONE : ITALIC_NORMAL );
+ maFont->SetPitch(
+ rFontRequest.FontDescription.FontDescription.Proportion == rendering::PanoseProportion::MONO_SPACED
+ ? PITCH_FIXED : PITCH_VARIABLE);
+
+ maFont->SetLanguage( LanguageTag::convertToLanguageType( rFontRequest.Locale, false));
+
+ // adjust to stretched/shrunk font
+ if( !::rtl::math::approxEqual( rFontMatrix.m00, rFontMatrix.m11) )
+ {
+ OutputDevice& rOutDev( rOutDevProvider->getOutDev() );
+
+ const bool bOldMapState( rOutDev.IsMapModeEnabled() );
+ rOutDev.EnableMapMode(false);
+
+ const Size aSize = rOutDev.GetFontMetric( *maFont ).GetFontSize();
+
+ const double fDividend( rFontMatrix.m10 + rFontMatrix.m11 );
+ double fStretch = rFontMatrix.m00 + rFontMatrix.m01;
+
+ if( !::basegfx::fTools::equalZero( fDividend) )
+ fStretch /= fDividend;
+
+ const ::tools::Long nNewWidth = ::basegfx::fround( aSize.Width() * fStretch );
+
+ maFont->SetAverageFontWidth( nNewWidth );
+
+ rOutDev.EnableMapMode(bOldMapState);
+ }
+
+ sal_uInt32 nEmphasisMark = 0;
+
+ ::canvas::tools::extractExtraFontProperties(rExtraFontProperties, nEmphasisMark);
+
+ if (nEmphasisMark)
+ maFont->SetEmphasisMark(FontEmphasisMark(nEmphasisMark));
+ }
+
+ void SAL_CALL CanvasFont::disposing()
+ {
+ SolarMutexGuard aGuard;
+
+ mpOutDevProvider.reset();
+ mpRefDevice.clear();
+ }
+
+ uno::Reference< rendering::XTextLayout > SAL_CALL CanvasFont::createTextLayout( const rendering::StringContext& aText, sal_Int8 nDirection, sal_Int64 )
+ {
+ SolarMutexGuard aGuard;
+
+ if( !mpRefDevice.is() )
+ return uno::Reference< rendering::XTextLayout >(); // we're disposed
+
+ return new TextLayout( aText,
+ nDirection,
+ Reference( this ),
+ mpRefDevice,
+ mpOutDevProvider);
+ }
+
+ rendering::FontRequest SAL_CALL CanvasFont::getFontRequest( )
+ {
+ SolarMutexGuard aGuard;
+
+ return maFontRequest;
+ }
+
+ rendering::FontMetrics SAL_CALL CanvasFont::getFontMetrics( )
+ {
+ SolarMutexGuard aGuard;
+
+ OutputDevice& rOutDev = mpOutDevProvider->getOutDev();
+ ScopedVclPtrInstance< VirtualDevice > pVDev( rOutDev );
+ pVDev->SetFont(getVCLFont());
+ const ::FontMetric& aMetric( pVDev->GetFontMetric() );
+
+ return rendering::FontMetrics(
+ aMetric.GetAscent(),
+ aMetric.GetDescent(),
+ aMetric.GetInternalLeading(),
+ aMetric.GetExternalLeading(),
+ 0,
+ aMetric.GetDescent() / 2.0,
+ aMetric.GetAscent() / 2.0);
+ }
+
+ uno::Sequence< double > SAL_CALL CanvasFont::getAvailableSizes( )
+ {
+ // TODO(F1)
+ return uno::Sequence< double >();
+ }
+
+ uno::Sequence< beans::PropertyValue > SAL_CALL CanvasFont::getExtraFontProperties( )
+ {
+ // TODO(F1)
+ return uno::Sequence< beans::PropertyValue >();
+ }
+
+ OUString SAL_CALL CanvasFont::getImplementationName()
+ {
+ return "VCLCanvas::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" };
+ }
+
+ vcl::Font const & CanvasFont::getVCLFont() const
+ {
+ return *maFont;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/vcl/canvasfont.hxx b/canvas/source/vcl/canvasfont.hxx
new file mode 100644
index 000000000..fdfa870f5
--- /dev/null
+++ b/canvas/source/vcl/canvasfont.hxx
@@ -0,0 +1,87 @@
+/* -*- 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/geometry/Matrix2D.hpp>
+#include <com/sun/star/rendering/FontRequest.hpp>
+#include <com/sun/star/rendering/XCanvasFont.hpp>
+#include <com/sun/star/rendering/XGraphicDevice.hpp>
+
+#include <vcl/font.hxx>
+
+#include <vclwrapper.hxx>
+
+#include "outdevprovider.hxx"
+
+
+/* Definition of CanvasFont class */
+
+namespace vclcanvas
+{
+ typedef ::cppu::WeakComponentImplHelper< css::rendering::XCanvasFont,
+ css::lang::XServiceInfo > CanvasFont_Base;
+
+ class CanvasFont : public ::cppu::BaseMutex,
+ public CanvasFont_Base
+ {
+ public:
+ typedef rtl::Reference<CanvasFont> Reference;
+
+ /// 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& rFontMatrix,
+ css::rendering::XGraphicDevice& rDevice,
+ const OutDevProviderSharedPtr& rOutDevProvider );
+
+ /// 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;
+
+ vcl::Font const & getVCLFont() const;
+
+ private:
+ ::canvas::vcltools::VCLObject<vcl::Font> maFont;
+ css::rendering::FontRequest maFontRequest;
+ css::uno::Reference< css::rendering::XGraphicDevice> mpRefDevice;
+ OutDevProviderSharedPtr mpOutDevProvider;
+ };
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/vcl/canvashelper.cxx b/canvas/source/vcl/canvashelper.cxx
new file mode 100644
index 000000000..918a8f5f9
--- /dev/null
+++ b/canvas/source/vcl/canvashelper.cxx
@@ -0,0 +1,1184 @@
+/* -*- 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/matrix/b2dhommatrix.hxx>
+#include <basegfx/numeric/ftools.hxx>
+#include <basegfx/point/b2dpoint.hxx>
+#include <basegfx/polygon/b2dlinegeometry.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/range/b2drectangle.hxx>
+#include <basegfx/utils/canvastools.hxx>
+#include <basegfx/vector/b2dsize.hxx>
+#include <com/sun/star/drawing/LineCap.hpp>
+#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/TextDirection.hpp>
+#include <comphelper/sequence.hxx>
+#include <rtl/math.hxx>
+#include <tools/diagnose_ex.h>
+#include <tools/poly.hxx>
+#include <vcl/bitmapex.hxx>
+#include <vcl/BitmapReadAccess.hxx>
+#include <vcl/canvastools.hxx>
+#include <vcl/BitmapAlphaClampFilter.hxx>
+#include <vcl/skia/SkiaHelper.hxx>
+
+#include <canvas/canvastools.hxx>
+
+#include "canvasbitmap.hxx"
+#include "canvasfont.hxx"
+#include "canvashelper.hxx"
+#include "impltools.hxx"
+#include "textlayout.hxx"
+
+
+using namespace ::com::sun::star;
+
+namespace vclcanvas
+{
+ namespace
+ {
+ basegfx::B2DLineJoin b2DJoineFromJoin( sal_Int8 nJoinType )
+ {
+ switch( nJoinType )
+ {
+ case rendering::PathJoinType::NONE:
+ return basegfx::B2DLineJoin::NONE;
+
+ case rendering::PathJoinType::MITER:
+ return basegfx::B2DLineJoin::Miter;
+
+ case rendering::PathJoinType::ROUND:
+ return basegfx::B2DLineJoin::Round;
+
+ case rendering::PathJoinType::BEVEL:
+ return basegfx::B2DLineJoin::Bevel;
+
+ default:
+ ENSURE_OR_THROW( false,
+ "b2DJoineFromJoin(): Unexpected join type" );
+ }
+
+ return basegfx::B2DLineJoin::NONE;
+ }
+
+ drawing::LineCap unoCapeFromCap( sal_Int8 nCapType)
+ {
+ switch ( nCapType)
+ {
+ case rendering::PathCapType::BUTT:
+ return drawing::LineCap_BUTT;
+
+ case rendering::PathCapType::ROUND:
+ return drawing::LineCap_ROUND;
+
+ case rendering::PathCapType::SQUARE:
+ return drawing::LineCap_SQUARE;
+
+ default:
+ ENSURE_OR_THROW( false,
+ "unoCapeFromCap(): Unexpected cap type" );
+ }
+ return drawing::LineCap_BUTT;
+ }
+ }
+
+ CanvasHelper::CanvasHelper() :
+ mpDevice(),
+ mbHaveAlpha( false )
+ {
+ }
+
+ void CanvasHelper::disposing()
+ {
+ mpDevice = nullptr;
+ mpProtectedOutDevProvider.reset();
+ mpOutDevProvider.reset();
+ mp2ndOutDevProvider.reset();
+ }
+
+ void CanvasHelper::init( rendering::XGraphicDevice& rDevice,
+ const OutDevProviderSharedPtr& rOutDev,
+ bool bProtect,
+ bool bHaveAlpha )
+ {
+ // cast away const, need to change refcount (as this is
+ // ~invisible to client code, still logically const)
+ mpDevice = &rDevice;
+ mbHaveAlpha = bHaveAlpha;
+
+ setOutDev( rOutDev, bProtect );
+ }
+
+ void CanvasHelper::setOutDev( const OutDevProviderSharedPtr& rOutDev,
+ bool bProtect )
+ {
+ if( bProtect )
+ mpProtectedOutDevProvider = rOutDev;
+ else
+ mpProtectedOutDevProvider.reset();
+
+ mpOutDevProvider = rOutDev;
+ }
+
+ void CanvasHelper::setBackgroundOutDev( const OutDevProviderSharedPtr& rOutDev )
+ {
+ mp2ndOutDevProvider = rOutDev;
+ mp2ndOutDevProvider->getOutDev().EnableMapMode( false );
+ mp2ndOutDevProvider->getOutDev().SetAntialiasing( AntialiasingFlags::Enable );
+ }
+
+ void CanvasHelper::clear()
+ {
+ // are we disposed?
+ if( !mpOutDevProvider )
+ return;
+
+ OutputDevice& rOutDev( mpOutDevProvider->getOutDev() );
+ tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDevProvider );
+
+ rOutDev.EnableMapMode( false );
+ rOutDev.SetAntialiasing( AntialiasingFlags::Enable );
+ rOutDev.SetLineColor( COL_WHITE );
+ rOutDev.SetFillColor( COL_WHITE );
+ rOutDev.SetClipRegion();
+ rOutDev.DrawRect( ::tools::Rectangle( Point(),
+ rOutDev.GetOutputSizePixel()) );
+
+ if( !mp2ndOutDevProvider )
+ return;
+
+ OutputDevice& rOutDev2( mp2ndOutDevProvider->getOutDev() );
+
+ rOutDev2.SetDrawMode( DrawModeFlags::Default );
+ rOutDev2.EnableMapMode( false );
+ rOutDev2.SetAntialiasing( AntialiasingFlags::Enable );
+ rOutDev2.SetLineColor( COL_WHITE );
+ rOutDev2.SetFillColor( COL_WHITE );
+ rOutDev2.SetClipRegion();
+ rOutDev2.DrawRect( ::tools::Rectangle( Point(),
+ rOutDev2.GetOutputSizePixel()) );
+ rOutDev2.SetDrawMode( DrawModeFlags::BlackLine | DrawModeFlags::BlackFill | DrawModeFlags::BlackText |
+ DrawModeFlags::BlackGradient | DrawModeFlags::BlackBitmap );
+ }
+
+ void CanvasHelper::drawLine( const rendering::XCanvas* ,
+ const geometry::RealPoint2D& aStartRealPoint2D,
+ const geometry::RealPoint2D& aEndRealPoint2D,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState )
+ {
+ // are we disposed?
+ if( !mpOutDevProvider )
+ return;
+
+ // nope, render
+ tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDevProvider );
+ setupOutDevState( viewState, renderState, LINE_COLOR );
+
+ const Point aStartPoint( tools::mapRealPoint2D( aStartRealPoint2D,
+ viewState, renderState ) );
+ const Point aEndPoint( tools::mapRealPoint2D( aEndRealPoint2D,
+ viewState, renderState ) );
+ // TODO(F2): alpha
+ mpOutDevProvider->getOutDev().DrawLine( aStartPoint, aEndPoint );
+
+ if( mp2ndOutDevProvider )
+ mp2ndOutDevProvider->getOutDev().DrawLine( aStartPoint, aEndPoint );
+ }
+
+ void CanvasHelper::drawBezier( const rendering::XCanvas* ,
+ const geometry::RealBezierSegment2D& aBezierSegment,
+ const geometry::RealPoint2D& _aEndPoint,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState )
+ {
+ if( !mpOutDevProvider )
+ return;
+
+ tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDevProvider );
+ setupOutDevState( viewState, renderState, LINE_COLOR );
+
+ const Point& rStartPoint( tools::mapRealPoint2D( geometry::RealPoint2D(aBezierSegment.Px,
+ aBezierSegment.Py),
+ viewState, renderState ) );
+ const Point& rCtrlPoint1( tools::mapRealPoint2D( geometry::RealPoint2D(aBezierSegment.C1x,
+ aBezierSegment.C1y),
+ viewState, renderState ) );
+ const Point& rCtrlPoint2( tools::mapRealPoint2D( geometry::RealPoint2D(aBezierSegment.C2x,
+ aBezierSegment.C2y),
+ viewState, renderState ) );
+ const Point& rEndPoint( tools::mapRealPoint2D( _aEndPoint,
+ viewState, renderState ) );
+
+ ::tools::Polygon aPoly(4);
+ aPoly.SetPoint( rStartPoint, 0 );
+ aPoly.SetFlags( 0, PolyFlags::Normal );
+ aPoly.SetPoint( rCtrlPoint1, 1 );
+ aPoly.SetFlags( 1, PolyFlags::Control );
+ aPoly.SetPoint( rCtrlPoint2, 2 );
+ aPoly.SetFlags( 2, PolyFlags::Control );
+ aPoly.SetPoint( rEndPoint, 3 );
+ aPoly.SetFlags( 3, PolyFlags::Normal );
+
+ // TODO(F2): alpha
+ mpOutDevProvider->getOutDev().DrawPolygon( aPoly );
+ if( mp2ndOutDevProvider )
+ mp2ndOutDevProvider->getOutDev().DrawPolygon( aPoly );
+ }
+
+ uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawPolyPolygon( const rendering::XCanvas* ,
+ const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState )
+ {
+ ENSURE_ARG_OR_THROW( xPolyPolygon.is(),
+ "polygon is NULL");
+
+ if( mpOutDevProvider )
+ {
+ tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDevProvider );
+ setupOutDevState( viewState, renderState, LINE_COLOR );
+
+ const ::basegfx::B2DPolyPolygon& rPolyPoly(
+ ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(xPolyPolygon) );
+ const ::tools::PolyPolygon aPolyPoly( tools::mapPolyPolygon( rPolyPoly, viewState, renderState ) );
+
+ if( rPolyPoly.isClosed() )
+ {
+ mpOutDevProvider->getOutDev().DrawPolyPolygon( aPolyPoly );
+
+ if( mp2ndOutDevProvider )
+ mp2ndOutDevProvider->getOutDev().DrawPolyPolygon( aPolyPoly );
+ }
+ else
+ {
+ // mixed open/closed state. Cannot render open polygon
+ // via DrawPolyPolygon(), since that implicitly
+ // closed every polygon. OTOH, no need to distinguish
+ // further and render closed polygons via
+ // DrawPolygon(), and open ones via DrawPolyLine():
+ // closed polygons will simply already contain the
+ // closing segment.
+ sal_uInt16 nSize( aPolyPoly.Count() );
+
+ for( sal_uInt16 i=0; i<nSize; ++i )
+ {
+ mpOutDevProvider->getOutDev().DrawPolyLine( aPolyPoly[i] );
+
+ if( mp2ndOutDevProvider )
+ mp2ndOutDevProvider->getOutDev().DrawPolyLine( aPolyPoly[i] );
+ }
+ }
+ }
+
+ // TODO(P1): Provide caching here.
+ return uno::Reference< rendering::XCachedPrimitive >(nullptr);
+ }
+
+ uno::Reference< rendering::XCachedPrimitive > CanvasHelper::strokePolyPolygon( const rendering::XCanvas* ,
+ const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState,
+ const rendering::StrokeAttributes& strokeAttributes )
+ {
+ ENSURE_ARG_OR_THROW( xPolyPolygon.is(),
+ "polygon is NULL");
+
+ if( mpOutDevProvider )
+ {
+ tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDevProvider );
+
+ ::basegfx::B2DHomMatrix aMatrix;
+ ::canvas::tools::mergeViewAndRenderTransform(aMatrix, viewState, renderState);
+
+ ::basegfx::B2DPolyPolygon aPolyPoly(
+ ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(xPolyPolygon) );
+
+ std::vector<double> aDashArray;
+ if( strokeAttributes.DashArray.hasElements() )
+ aDashArray = ::comphelper::sequenceToContainer< std::vector<double> >(strokeAttributes.DashArray);
+
+ // First try to draw directly using VCL.
+ bool directFailed = false;
+ setupOutDevState( viewState, renderState, LINE_COLOR );
+ for( sal_uInt32 i=0; i<aPolyPoly.count(); ++i )
+ {
+ if( !mpOutDevProvider->getOutDev().DrawPolyLineDirect( aMatrix, aPolyPoly.getB2DPolygon(i),
+ strokeAttributes.StrokeWidth, 0, !aDashArray.empty() ? &aDashArray : nullptr,
+ b2DJoineFromJoin(strokeAttributes.JoinType), unoCapeFromCap(strokeAttributes.StartCapType)))
+ {
+ directFailed = true;
+ break;
+ }
+ }
+ if(!directFailed)
+ return uno::Reference< rendering::XCachedPrimitive >(nullptr);
+
+ // Do it all manually.
+
+ // apply dashing, if any
+ if( strokeAttributes.DashArray.hasElements() )
+ {
+ ::basegfx::B2DPolyPolygon aDashedPolyPoly;
+
+ for( sal_uInt32 i=0; i<aPolyPoly.count(); ++i )
+ {
+ // AW: new interface; You may also get gaps in the same run now
+ basegfx::utils::applyLineDashing(aPolyPoly.getB2DPolygon(i), aDashArray, &aDashedPolyPoly);
+ //aDashedPolyPoly.append(
+ // ::basegfx::utils::applyLineDashing( aPolyPoly.getB2DPolygon(i),
+ // aDashArray ) );
+ }
+
+ aPolyPoly = aDashedPolyPoly;
+ }
+
+ ::basegfx::B2DSize aLinePixelSize(strokeAttributes.StrokeWidth,
+ strokeAttributes.StrokeWidth);
+ aLinePixelSize *= aMatrix;
+ ::basegfx::B2DPolyPolygon aStrokedPolyPoly;
+ if( aLinePixelSize.getLength() < 1.42 )
+ {
+ // line width < 1.0 in device pixel, thus, output as a
+ // simple hairline poly-polygon
+ setupOutDevState( viewState, renderState, LINE_COLOR );
+
+ aStrokedPolyPoly = aPolyPoly;
+ }
+ else
+ {
+ // render as a 'thick' line
+ setupOutDevState( viewState, renderState, FILL_COLOR );
+
+ for( sal_uInt32 i=0; i<aPolyPoly.count(); ++i )
+ {
+ double fMiterMinimumAngle;
+ if (strokeAttributes.MiterLimit <= 1.0)
+ {
+ fMiterMinimumAngle = M_PI_2;
+ }
+ else
+ {
+ fMiterMinimumAngle = 2.0 * asin(1.0/strokeAttributes.MiterLimit);
+ }
+
+ // TODO(F2): Also use Cap settings from
+ // StrokeAttributes, the
+ // createAreaGeometryForLineStartEnd() method does not
+ // seem to fit very well here
+
+ // AW: New interface, will create bezier polygons now
+ aStrokedPolyPoly.append(basegfx::utils::createAreaGeometry(
+ aPolyPoly.getB2DPolygon(i),
+ strokeAttributes.StrokeWidth*0.5,
+ b2DJoineFromJoin(strokeAttributes.JoinType),
+ unoCapeFromCap(strokeAttributes.StartCapType),
+ basegfx::deg2rad(12.5) /* default fMaxAllowedAngle*/ ,
+ 0.4 /* default fMaxPartOfEdge*/ ,
+ fMiterMinimumAngle
+ ));
+ //aStrokedPolyPoly.append(
+ // ::basegfx::utils::createAreaGeometryForPolygon( aPolyPoly.getB2DPolygon(i),
+ // strokeAttributes.StrokeWidth*0.5,
+ // b2DJoineFromJoin(strokeAttributes.JoinType) ) );
+ }
+ }
+
+ // transform only _now_, all the StrokeAttributes are in
+ // user coordinates.
+ aStrokedPolyPoly.transform( aMatrix );
+
+ // TODO(F2): When using alpha here, must handle that via
+ // temporary surface or somesuch.
+
+ // Note: the generated stroke poly-polygon is NOT free of
+ // self-intersections. Therefore, if we would render it
+ // via OutDev::DrawPolyPolygon(), on/off fill would
+ // generate off areas on those self-intersections.
+ for( sal_uInt32 i=0; i<aStrokedPolyPoly.count(); ++i )
+ {
+ const basegfx::B2DPolygon& polygon = aStrokedPolyPoly.getB2DPolygon( i );
+ if( polygon.isClosed()) {
+ mpOutDevProvider->getOutDev().DrawPolygon( polygon );
+ if( mp2ndOutDevProvider )
+ mp2ndOutDevProvider->getOutDev().DrawPolygon( polygon );
+ } else {
+ mpOutDevProvider->getOutDev().DrawPolyLine( polygon );
+ if( mp2ndOutDevProvider )
+ mp2ndOutDevProvider->getOutDev().DrawPolyLine( polygon );
+ }
+ }
+ }
+
+ // TODO(P1): Provide caching here.
+ return uno::Reference< rendering::XCachedPrimitive >(nullptr);
+ }
+
+ uno::Reference< rendering::XCachedPrimitive > CanvasHelper::strokeTexturedPolyPolygon( const rendering::XCanvas* ,
+ const uno::Reference< rendering::XPolyPolygon2D >& ,
+ const rendering::ViewState& ,
+ const rendering::RenderState& ,
+ const uno::Sequence< rendering::Texture >& ,
+ const rendering::StrokeAttributes& )
+ {
+ return uno::Reference< rendering::XCachedPrimitive >(nullptr);
+ }
+
+ uno::Reference< rendering::XCachedPrimitive > CanvasHelper::strokeTextureMappedPolyPolygon( const rendering::XCanvas* ,
+ const uno::Reference< rendering::XPolyPolygon2D >& ,
+ const rendering::ViewState& ,
+ const rendering::RenderState& ,
+ const uno::Sequence< rendering::Texture >& ,
+ const uno::Reference< geometry::XMapping2D >& ,
+ const rendering::StrokeAttributes& )
+ {
+ return uno::Reference< rendering::XCachedPrimitive >(nullptr);
+ }
+
+ uno::Reference< rendering::XPolyPolygon2D > CanvasHelper::queryStrokeShapes( const rendering::XCanvas* ,
+ const uno::Reference< rendering::XPolyPolygon2D >& ,
+ const rendering::ViewState& ,
+ const rendering::RenderState& ,
+ const rendering::StrokeAttributes& )
+ {
+ return uno::Reference< rendering::XPolyPolygon2D >(nullptr);
+ }
+
+ uno::Reference< rendering::XCachedPrimitive > CanvasHelper::fillPolyPolygon( const rendering::XCanvas* ,
+ const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState )
+ {
+ ENSURE_ARG_OR_THROW( xPolyPolygon.is(),
+ "polygon is NULL");
+
+ if( mpOutDevProvider )
+ {
+ tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDevProvider );
+
+ const int nAlpha( setupOutDevState( viewState, renderState, FILL_COLOR ) );
+ ::basegfx::B2DPolyPolygon aB2DPolyPoly(
+ ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(xPolyPolygon));
+ aB2DPolyPoly.setClosed(true); // ensure closed poly, otherwise VCL does not fill
+ const ::tools::PolyPolygon aPolyPoly( tools::mapPolyPolygon(
+ aB2DPolyPoly,
+ viewState, renderState ) );
+ const bool bSourceAlpha( renderState.CompositeOperation == rendering::CompositeOperation::SOURCE );
+ if( nAlpha == 255 || bSourceAlpha )
+ {
+ mpOutDevProvider->getOutDev().DrawPolyPolygon( aPolyPoly );
+ }
+ else
+ {
+ const int nTransPercent( ((255 - nAlpha) * 100 + 128) / 255 ); // normal rounding, no truncation here
+ mpOutDevProvider->getOutDev().DrawTransparent( aPolyPoly, static_cast<sal_uInt16>(nTransPercent) );
+ }
+
+ if( mp2ndOutDevProvider )
+ {
+ // HACK. Normally, CanvasHelper does not care about
+ // actually what mp2ndOutDev is... well, here we do &
+ // assume a 1bpp target - everything beyond 97%
+ // transparency is fully transparent
+ if( nAlpha > 2 )
+ {
+ mp2ndOutDevProvider->getOutDev().SetFillColor( COL_BLACK );
+ mp2ndOutDevProvider->getOutDev().DrawPolyPolygon( aPolyPoly );
+ }
+ }
+ }
+
+ // TODO(P1): Provide caching here.
+ return uno::Reference< rendering::XCachedPrimitive >(nullptr);
+ }
+
+ uno::Reference< rendering::XCachedPrimitive > CanvasHelper::fillTextureMappedPolyPolygon( const rendering::XCanvas* ,
+ const uno::Reference< rendering::XPolyPolygon2D >& ,
+ const rendering::ViewState& ,
+ const rendering::RenderState& ,
+ const uno::Sequence< rendering::Texture >& ,
+ const uno::Reference< geometry::XMapping2D >& )
+ {
+ return uno::Reference< rendering::XCachedPrimitive >(nullptr);
+ }
+
+ uno::Reference< rendering::XCanvasFont > CanvasHelper::createFont( const rendering::XCanvas* ,
+ const rendering::FontRequest& fontRequest,
+ const uno::Sequence< beans::PropertyValue >& extraFontProperties,
+ const geometry::Matrix2D& fontMatrix )
+ {
+ if( mpOutDevProvider && mpDevice )
+ {
+ // TODO(F2): font properties and font matrix
+ return uno::Reference< rendering::XCanvasFont >(
+ new CanvasFont(fontRequest, extraFontProperties, fontMatrix,
+ *mpDevice, mpOutDevProvider) );
+ }
+
+ return uno::Reference< rendering::XCanvasFont >();
+ }
+
+ uno::Sequence< rendering::FontInfo > CanvasHelper::queryAvailableFonts( const rendering::XCanvas* ,
+ const rendering::FontInfo& ,
+ const uno::Sequence< beans::PropertyValue >& )
+ {
+ // TODO(F2)
+ return uno::Sequence< rendering::FontInfo >();
+ }
+
+ uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawText( const rendering::XCanvas* ,
+ const rendering::StringContext& text,
+ const uno::Reference< rendering::XCanvasFont >& xFont,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState,
+ sal_Int8 textDirection )
+ {
+ ENSURE_ARG_OR_THROW( xFont.is(),
+ "font is NULL");
+
+ if( mpOutDevProvider )
+ {
+ tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDevProvider );
+
+ ::Point aOutpos;
+ if( !setupTextOutput( aOutpos, viewState, renderState, xFont ) )
+ return uno::Reference< rendering::XCachedPrimitive >(nullptr); // no output necessary
+
+ // change text direction and layout mode
+ vcl::text::ComplexTextLayoutFlags nLayoutMode(vcl::text::ComplexTextLayoutFlags::Default);
+ switch( textDirection )
+ {
+ case rendering::TextDirection::WEAK_LEFT_TO_RIGHT:
+ case rendering::TextDirection::STRONG_LEFT_TO_RIGHT:
+ nLayoutMode |= vcl::text::ComplexTextLayoutFlags::BiDiStrong;
+ nLayoutMode |= vcl::text::ComplexTextLayoutFlags::TextOriginLeft;
+ break;
+
+ case rendering::TextDirection::WEAK_RIGHT_TO_LEFT:
+ nLayoutMode |= vcl::text::ComplexTextLayoutFlags::BiDiRtl;
+ [[fallthrough]];
+ case rendering::TextDirection::STRONG_RIGHT_TO_LEFT:
+ nLayoutMode |= vcl::text::ComplexTextLayoutFlags::BiDiRtl | vcl::text::ComplexTextLayoutFlags::BiDiStrong;
+ nLayoutMode |= vcl::text::ComplexTextLayoutFlags::TextOriginRight;
+ break;
+ }
+
+ // TODO(F2): alpha
+ mpOutDevProvider->getOutDev().SetLayoutMode( nLayoutMode );
+ mpOutDevProvider->getOutDev().DrawText( aOutpos,
+ text.Text,
+ ::canvas::tools::numeric_cast<sal_uInt16>(text.StartPosition),
+ ::canvas::tools::numeric_cast<sal_uInt16>(text.Length) );
+
+ if( mp2ndOutDevProvider )
+ {
+ mp2ndOutDevProvider->getOutDev().SetLayoutMode( nLayoutMode );
+ mp2ndOutDevProvider->getOutDev().DrawText( aOutpos,
+ text.Text,
+ ::canvas::tools::numeric_cast<sal_uInt16>(text.StartPosition),
+ ::canvas::tools::numeric_cast<sal_uInt16>(text.Length) );
+ }
+ }
+
+ return uno::Reference< rendering::XCachedPrimitive >(nullptr);
+ }
+
+ uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawTextLayout( const rendering::XCanvas* ,
+ const uno::Reference< rendering::XTextLayout >& xLayoutedText,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState )
+ {
+ ENSURE_ARG_OR_THROW( xLayoutedText.is(),
+ "layout is NULL");
+
+ TextLayout* pTextLayout = dynamic_cast< TextLayout* >( xLayoutedText.get() );
+
+ if( pTextLayout )
+ {
+ if( mpOutDevProvider )
+ {
+ tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDevProvider );
+
+ // TODO(T3): Race condition. We're taking the font
+ // from xLayoutedText, and then calling draw() at it,
+ // without exclusive access. Move setupTextOutput(),
+ // e.g. to impltools?
+
+ ::Point aOutpos;
+ if( !setupTextOutput( aOutpos, viewState, renderState, xLayoutedText->getFont() ) )
+ return uno::Reference< rendering::XCachedPrimitive >(nullptr); // no output necessary
+
+ // TODO(F2): What about the offset scalings?
+ // TODO(F2): alpha
+ pTextLayout->draw( mpOutDevProvider->getOutDev(), aOutpos, viewState, renderState );
+
+ if( mp2ndOutDevProvider )
+ pTextLayout->draw( mp2ndOutDevProvider->getOutDev(), aOutpos, viewState, renderState );
+ }
+ }
+ else
+ {
+ ENSURE_ARG_OR_THROW( false,
+ "TextLayout not compatible with this canvas" );
+ }
+
+ return uno::Reference< rendering::XCachedPrimitive >(nullptr);
+ }
+
+ uno::Reference< rendering::XCachedPrimitive > CanvasHelper::implDrawBitmap( const rendering::XCanvas* pCanvas,
+ const uno::Reference< rendering::XBitmap >& xBitmap,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState,
+ bool bModulateColors )
+ {
+ ENSURE_ARG_OR_THROW( xBitmap.is(),
+ "bitmap is NULL");
+
+ ::canvas::tools::verifyInput( renderState,
+ __func__,
+ mpDevice,
+ 4,
+ bModulateColors ? 3 : 0 );
+
+ if( mpOutDevProvider )
+ {
+ tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDevProvider );
+ setupOutDevState( viewState, renderState, IGNORE_COLOR );
+
+ ::basegfx::B2DHomMatrix aMatrix;
+ ::canvas::tools::mergeViewAndRenderTransform(aMatrix, viewState, renderState);
+
+ ::basegfx::B2DPoint aOutputPos( 0.0, 0.0 );
+ aOutputPos *= aMatrix;
+
+ BitmapEx aBmpEx( tools::bitmapExFromXBitmap(xBitmap) );
+
+ // TODO(F2): Implement modulation again for other color
+ // channels (currently, works only for alpha). Note: this
+ // is already implemented in transformBitmap()
+ if( bModulateColors &&
+ renderState.DeviceColor.getLength() > 3 )
+ {
+ // optimize away the case where alpha modulation value
+ // is 1.0 - we then simply switch off modulation at all
+ bModulateColors = !::rtl::math::approxEqual(
+ renderState.DeviceColor[3], 1.0);
+ }
+
+ // check whether we can render bitmap as-is: must not
+ // modulate colors, matrix must either be the identity
+ // transform (that's clear), _or_ contain only
+ // translational components.
+ if( !bModulateColors &&
+ (aMatrix.isIdentity() ||
+ (::basegfx::fTools::equalZero( aMatrix.get(0,1) ) &&
+ ::basegfx::fTools::equalZero( aMatrix.get(1,0) ) &&
+ ::rtl::math::approxEqual(aMatrix.get(0,0), 1.0) &&
+ ::rtl::math::approxEqual(aMatrix.get(1,1), 1.0)) ) )
+ {
+ // optimized case: identity matrix, or only
+ // translational components.
+ mpOutDevProvider->getOutDev().DrawBitmapEx( vcl::unotools::pointFromB2DPoint( aOutputPos ),
+ aBmpEx );
+
+ if( mp2ndOutDevProvider )
+ {
+ // HACK. Normally, CanvasHelper does not care about
+ // actually what mp2ndOutDev is... well, here we do &
+ // assume a 1bpp target - everything beyond 97%
+ // transparency is fully transparent
+ if( aBmpEx.IsAlpha() && !SkiaHelper::isVCLSkiaEnabled())
+ {
+ BitmapFilter::Filter(aBmpEx, BitmapAlphaClampFilter(253));
+ }
+
+ mp2ndOutDevProvider->getOutDev().DrawBitmapEx( vcl::unotools::pointFromB2DPoint( aOutputPos ),
+ aBmpEx );
+ }
+
+ // Returning a cache object is not useful, the XBitmap
+ // itself serves this purpose
+ return uno::Reference< rendering::XCachedPrimitive >(nullptr);
+ }
+ else if( mpOutDevProvider->getOutDev().HasFastDrawTransformedBitmap())
+ {
+ ::basegfx::B2DHomMatrix aSizeTransform;
+ aSizeTransform.scale( aBmpEx.GetSizePixel().Width(), aBmpEx.GetSizePixel().Height() );
+ aMatrix = aMatrix * aSizeTransform;
+ const double fAlpha = bModulateColors ? renderState.DeviceColor[3] : 1.0;
+
+ mpOutDevProvider->getOutDev().DrawTransformedBitmapEx( aMatrix, aBmpEx, fAlpha );
+ if( mp2ndOutDevProvider )
+ {
+ // HACK. Normally, CanvasHelper does not care about
+ // actually what mp2ndOutDev is... well, here we do &
+ // assume a 1bpp target - everything beyond 97%
+ // transparency is fully transparent
+ if( aBmpEx.IsAlpha() && !SkiaHelper::isVCLSkiaEnabled())
+ {
+ BitmapFilter::Filter(aBmpEx, BitmapAlphaClampFilter(253));
+ }
+
+ mp2ndOutDevProvider->getOutDev().DrawTransformedBitmapEx( aMatrix, aBmpEx );
+ }
+ return uno::Reference< rendering::XCachedPrimitive >(nullptr);
+ }
+ else
+ {
+ // Matrix contains non-trivial transformation (or
+ // color modulation is requested), decompose to check
+ // whether GraphicObject suffices
+ ::basegfx::B2DVector aScale;
+ double nRotate;
+ double nShearX;
+ aMatrix.decompose( aScale, aOutputPos, nRotate, nShearX );
+
+ GraphicAttr aGrfAttr;
+ GraphicObjectSharedPtr pGrfObj;
+
+ ::Size aBmpSize( aBmpEx.GetSizePixel() );
+
+ // setup alpha modulation
+ if( bModulateColors )
+ {
+ const double nAlphaModulation( renderState.DeviceColor[3] );
+
+ // TODO(F1): Note that the GraphicManager has a
+ // subtle difference in how it calculates the
+ // resulting alpha value: it's using the inverse
+ // alpha values (i.e. 'transparency'), and
+ // calculates transOrig + transModulate, instead
+ // of transOrig + transModulate -
+ // transOrig*transModulate (which would be
+ // equivalent to the origAlpha*modulateAlpha the
+ // DX canvas performs)
+ aGrfAttr.SetAlpha(
+ static_cast< sal_uInt8 >(
+ ::basegfx::fround( 255.0 * nAlphaModulation ) ) );
+ }
+
+ if( ::basegfx::fTools::equalZero( nShearX ) )
+ {
+ // no shear, GraphicObject is enough (the
+ // GraphicObject only supports scaling, rotation
+ // and translation)
+
+ // #i75339# don't apply mirror flags, having
+ // negative size values is enough to make
+ // GraphicObject flip the bitmap
+
+ // The angle has to be mapped from radian to tenths of
+ // degrees with the orientation reversed: [0,2Pi) ->
+ // (3600,0]. Note that the original angle may have
+ // values outside the [0,2Pi) interval.
+ const double nAngleInTenthOfDegrees (3600.0 - basegfx::rad2deg<10>(nRotate));
+ aGrfAttr.SetRotation( Degree10(::basegfx::fround(nAngleInTenthOfDegrees)) );
+
+ pGrfObj = std::make_shared<GraphicObject>( aBmpEx );
+ }
+ else
+ {
+ // modify output position, to account for the fact
+ // that transformBitmap() always normalizes its output
+ // bitmap into the smallest enclosing box.
+ ::basegfx::B2DRectangle aDestRect;
+ ::canvas::tools::calcTransformedRectBounds( aDestRect,
+ ::basegfx::B2DRectangle(0,
+ 0,
+ aBmpSize.Width(),
+ aBmpSize.Height()),
+ aMatrix );
+
+ aOutputPos.setX( aDestRect.getMinX() );
+ aOutputPos.setY( aDestRect.getMinY() );
+
+ // complex transformation, use generic affine bitmap
+ // transformation
+ aBmpEx = tools::transformBitmap( aBmpEx,
+ aMatrix );
+
+ pGrfObj = std::make_shared<GraphicObject>( aBmpEx );
+
+ // clear scale values, generated bitmap already
+ // contains scaling
+ aScale.setX( 1.0 ); aScale.setY( 1.0 );
+
+ // update bitmap size, bitmap has changed above.
+ aBmpSize = aBmpEx.GetSizePixel();
+ }
+
+ // output GraphicObject
+ const ::Point aPt( vcl::unotools::pointFromB2DPoint( aOutputPos ) );
+ const ::Size aSz( ::basegfx::fround( aScale.getX() * aBmpSize.Width() ),
+ ::basegfx::fround( aScale.getY() * aBmpSize.Height() ) );
+
+ pGrfObj->Draw(mpOutDevProvider->getOutDev(),
+ aPt,
+ aSz,
+ &aGrfAttr);
+
+ if( mp2ndOutDevProvider )
+ pGrfObj->Draw(mp2ndOutDevProvider->getOutDev(),
+ aPt,
+ aSz,
+ &aGrfAttr);
+
+ // created GraphicObject, which possibly cached
+ // display bitmap - return cache object, to retain
+ // that information.
+ return uno::Reference< rendering::XCachedPrimitive >(
+ new CachedBitmap( pGrfObj,
+ aPt,
+ aSz,
+ aGrfAttr,
+ viewState,
+ renderState,
+ // cast away const, need to
+ // change refcount (as this is
+ // ~invisible to client code,
+ // still logically const)
+ const_cast< rendering::XCanvas* >(pCanvas)) );
+ }
+ }
+
+ // Nothing rendered
+ 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 )
+ {
+ return implDrawBitmap( pCanvas,
+ xBitmap,
+ viewState,
+ renderState,
+ false );
+ }
+
+ uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawBitmapModulated( const rendering::XCanvas* pCanvas,
+ const uno::Reference< rendering::XBitmap >& xBitmap,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState )
+ {
+ return implDrawBitmap( pCanvas,
+ xBitmap,
+ viewState,
+ renderState,
+ true );
+ }
+
+ geometry::IntegerSize2D CanvasHelper::getSize()
+ {
+ if( !mpOutDevProvider )
+ return geometry::IntegerSize2D(); // we're disposed
+
+ return vcl::unotools::integerSize2DFromSize( mpOutDevProvider->getOutDev().GetOutputSizePixel() );
+ }
+
+ uno::Reference< rendering::XBitmap > CanvasHelper::getScaledBitmap( const geometry::RealSize2D& newSize,
+ bool beFast )
+ {
+ if( !mpOutDevProvider || !mpDevice )
+ return uno::Reference< rendering::XBitmap >(); // we're disposed
+
+ OutputDevice& rOutDev( mpOutDevProvider->getOutDev() );
+
+ tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDevProvider );
+ rOutDev.EnableMapMode( false );
+ rOutDev.SetAntialiasing( AntialiasingFlags::Enable );
+
+ // TODO(F2): Support alpha vdev canvas here
+ const Point aEmptyPoint(0,0);
+ const Size aBmpSize( rOutDev.GetOutputSizePixel() );
+
+ BitmapEx aBitmap( rOutDev.GetBitmapEx(aEmptyPoint, aBmpSize) );
+
+ aBitmap.Scale( vcl::unotools::sizeFromRealSize2D(newSize),
+ beFast ? BmpScaleFlag::Default : BmpScaleFlag::BestQuality );
+
+ return uno::Reference< rendering::XBitmap >(
+ new CanvasBitmap( aBitmap, *mpDevice, mpOutDevProvider ) );
+ }
+
+ uno::Sequence< sal_Int8 > CanvasHelper::getData( rendering::IntegerBitmapLayout& rLayout,
+ const geometry::IntegerRectangle2D& rect )
+ {
+ if( !mpOutDevProvider )
+ return uno::Sequence< sal_Int8 >(); // we're disposed
+
+ rLayout = getMemoryLayout();
+
+ // TODO(F2): Support alpha canvas here
+ const ::tools::Rectangle aRect( vcl::unotools::rectangleFromIntegerRectangle2D(rect) );
+
+ OutputDevice& rOutDev( mpOutDevProvider->getOutDev() );
+
+ tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDevProvider );
+ rOutDev.EnableMapMode( false );
+ rOutDev.SetAntialiasing( AntialiasingFlags::Enable );
+
+ Bitmap aBitmap( rOutDev.GetBitmapEx(aRect.TopLeft(),
+ aRect.GetSize()).GetBitmap() );
+
+ Bitmap::ScopedReadAccess pReadAccess( aBitmap );
+
+ ENSURE_OR_THROW( pReadAccess.get() != nullptr,
+ "Could not acquire read access to OutDev bitmap" );
+
+ const sal_Int32 nWidth( rect.X2 - rect.X1 );
+ const sal_Int32 nHeight( rect.Y2 - rect.Y1 );
+
+ rLayout.ScanLines = nHeight;
+ rLayout.ScanLineBytes = nWidth*4;
+ rLayout.ScanLineStride = rLayout.ScanLineBytes;
+
+ uno::Sequence< sal_Int8 > aRes( 4*nWidth*nHeight );
+ sal_Int8* pRes = aRes.getArray();
+
+ int nCurrPos(0);
+ for( int y=0; y<nHeight; ++y )
+ {
+ for( int x=0; x<nWidth; ++x )
+ {
+ pRes[ nCurrPos++ ] = pReadAccess->GetColor( y, x ).GetRed();
+ pRes[ nCurrPos++ ] = pReadAccess->GetColor( y, x ).GetGreen();
+ pRes[ nCurrPos++ ] = pReadAccess->GetColor( y, x ).GetBlue();
+ pRes[ nCurrPos++ ] = -1;
+ }
+ }
+
+ return aRes;
+ }
+
+ uno::Sequence< sal_Int8 > CanvasHelper::getPixel( rendering::IntegerBitmapLayout& rLayout,
+ const geometry::IntegerPoint2D& pos )
+ {
+ if( !mpOutDevProvider )
+ return uno::Sequence< sal_Int8 >(); // we're disposed
+
+ rLayout = getMemoryLayout();
+ rLayout.ScanLines = 1;
+ rLayout.ScanLineBytes = 4;
+ rLayout.ScanLineStride = rLayout.ScanLineBytes;
+
+ OutputDevice& rOutDev( mpOutDevProvider->getOutDev() );
+
+ tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDevProvider );
+ rOutDev.EnableMapMode( false );
+ rOutDev.SetAntialiasing( AntialiasingFlags::Enable );
+
+ const Size aBmpSize( rOutDev.GetOutputSizePixel() );
+
+ ENSURE_ARG_OR_THROW( pos.X >= 0 && pos.X < aBmpSize.Width(),
+ "X coordinate out of bounds" );
+ ENSURE_ARG_OR_THROW( pos.Y >= 0 && pos.Y < aBmpSize.Height(),
+ "Y coordinate out of bounds" );
+
+ // TODO(F2): Support alpha canvas here
+ return ::canvas::tools::colorToStdIntSequence(
+ rOutDev.GetPixel(
+ vcl::unotools::pointFromIntegerPoint2D( pos )));
+ }
+
+ rendering::IntegerBitmapLayout CanvasHelper::getMemoryLayout()
+ {
+ if( !mpOutDevProvider )
+ return rendering::IntegerBitmapLayout(); // we're disposed
+
+ rendering::IntegerBitmapLayout aBitmapLayout( ::canvas::tools::getStdMemoryLayout(getSize()) );
+ if ( !mbHaveAlpha )
+ aBitmapLayout.ColorSpace = canvas::tools::getStdColorSpaceWithoutAlpha();
+
+ return aBitmapLayout;
+ }
+
+ int CanvasHelper::setupOutDevState( const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState,
+ ColorType eColorType ) const
+ {
+ ENSURE_OR_THROW( mpOutDevProvider,
+ "outdev null. Are we disposed?" );
+
+ ::canvas::tools::verifyInput( renderState,
+ __func__,
+ mpDevice,
+ 2,
+ eColorType == IGNORE_COLOR ? 0 : 3 );
+
+ OutputDevice& rOutDev( mpOutDevProvider->getOutDev() );
+ OutputDevice* p2ndOutDev = nullptr;
+
+ rOutDev.EnableMapMode( false );
+ rOutDev.SetAntialiasing( AntialiasingFlags::Enable );
+
+ if( mp2ndOutDevProvider )
+ p2ndOutDev = &mp2ndOutDevProvider->getOutDev();
+
+ int nAlpha(255);
+
+ // TODO(P2): Don't change clipping all the time, maintain current clip
+ // state and change only when update is necessary
+ ::canvas::tools::clipOutDev(viewState, renderState, rOutDev, p2ndOutDev);
+
+ Color aColor( COL_WHITE );
+
+ if( renderState.DeviceColor.getLength() > 2 )
+ {
+ aColor = vcl::unotools::stdColorSpaceSequenceToColor(
+ renderState.DeviceColor );
+ }
+
+ // extract alpha, and make color opaque
+ // afterwards. Otherwise, OutputDevice won't draw anything
+ nAlpha = aColor.GetAlpha();
+ aColor.SetAlpha(255);
+
+ if( eColorType != IGNORE_COLOR )
+ {
+ switch( eColorType )
+ {
+ case LINE_COLOR:
+ rOutDev.SetLineColor( aColor );
+ rOutDev.SetFillColor();
+
+ if( p2ndOutDev )
+ {
+ p2ndOutDev->SetLineColor( aColor );
+ p2ndOutDev->SetFillColor();
+ }
+ break;
+
+ case FILL_COLOR:
+ rOutDev.SetFillColor( aColor );
+ rOutDev.SetLineColor();
+
+ if( p2ndOutDev )
+ {
+ p2ndOutDev->SetFillColor( aColor );
+ p2ndOutDev->SetLineColor();
+ }
+ break;
+
+ case TEXT_COLOR:
+ rOutDev.SetTextColor( aColor );
+
+ if( p2ndOutDev )
+ p2ndOutDev->SetTextColor( aColor );
+ break;
+
+ default:
+ ENSURE_OR_THROW( false,
+ "Unexpected color type");
+ break;
+ }
+ }
+
+ return nAlpha;
+ }
+
+ bool CanvasHelper::setupTextOutput( ::Point& o_rOutPos,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState,
+ const uno::Reference< rendering::XCanvasFont >& xFont ) const
+ {
+ ENSURE_OR_THROW( mpOutDevProvider,
+ "outdev null. Are we disposed?" );
+
+ OutputDevice& rOutDev( mpOutDevProvider->getOutDev() );
+
+ setupOutDevState( viewState, renderState, TEXT_COLOR );
+
+ CanvasFont* pFont = dynamic_cast< CanvasFont* >( xFont.get() );
+
+ ENSURE_ARG_OR_THROW( pFont,
+ "Font not compatible with this canvas" );
+
+ vcl::Font aVCLFont = pFont->getVCLFont();
+
+ Color aColor( COL_BLACK );
+
+ if( renderState.DeviceColor.getLength() > 2 )
+ {
+ aColor = vcl::unotools::stdColorSpaceSequenceToColor(
+ renderState.DeviceColor );
+ }
+
+ // setup font color
+ aVCLFont.SetColor( aColor );
+ aVCLFont.SetFillColor( aColor );
+
+ // no need to replicate this for mp2ndOutDev, we're modifying only aVCLFont here.
+ if( !tools::setupFontTransform( o_rOutPos, aVCLFont, viewState, renderState, rOutDev ) )
+ return false;
+
+ rOutDev.SetFont( aVCLFont );
+
+ if( mp2ndOutDevProvider )
+ mp2ndOutDevProvider->getOutDev().SetFont( aVCLFont );
+
+ return true;
+ }
+
+ bool CanvasHelper::repaint( const GraphicObjectSharedPtr& rGrf,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState,
+ const ::Point& rPt,
+ const ::Size& rSz,
+ const GraphicAttr& rAttr ) const
+ {
+ ENSURE_OR_RETURN_FALSE( rGrf,
+ "Invalid Graphic" );
+
+ if( !mpOutDevProvider )
+ return false; // disposed
+ else
+ {
+ tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDevProvider );
+ setupOutDevState( viewState, renderState, IGNORE_COLOR );
+
+ if (!rGrf->Draw(mpOutDevProvider->getOutDev(), rPt, rSz, &rAttr))
+ return false;
+
+ // #i80779# Redraw also into mask outdev
+ if (mp2ndOutDevProvider)
+ return rGrf->Draw(mp2ndOutDevProvider->getOutDev(), rPt, rSz, &rAttr);
+
+ return true;
+ }
+ }
+
+ void CanvasHelper::flush() const
+ {
+ if (mpOutDevProvider)
+ mpOutDevProvider->getOutDev().Flush();
+
+ if (mp2ndOutDevProvider)
+ mp2ndOutDevProvider->getOutDev().Flush();
+ }
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/vcl/canvashelper.hxx b/canvas/source/vcl/canvashelper.hxx
new file mode 100644
index 000000000..ba385110b
--- /dev/null
+++ b/canvas/source/vcl/canvashelper.hxx
@@ -0,0 +1,309 @@
+/* -*- 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/geometry/IntegerPoint2D.hpp>
+#include <com/sun/star/geometry/IntegerRectangle2D.hpp>
+#include <com/sun/star/rendering/IntegerBitmapLayout.hpp>
+#include <com/sun/star/rendering/XCanvas.hpp>
+
+#include "cachedbitmap.hxx"
+#include "outdevprovider.hxx"
+
+
+namespace vclcanvas
+{
+ class SpriteCanvas;
+
+ /** Helper class for basic canvas functionality. Also offers
+ optional backbuffer painting, when providing it with a second
+ OutputDevice to render into.
+ */
+ class CanvasHelper
+ {
+ public:
+ /** Create canvas helper
+ */
+ 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
+
+ @param rOutDev
+ Set primary output device for this canvas. That's where
+ all content is output to.
+
+ @param bProtect
+ When true, all output operations preserve outdev
+ state. When false, outdev state might change at any time.
+
+ @param bHaveAlpha
+ When true, hasAlpha() will always return true, otherwise, false.
+ */
+ void init( css::rendering::XGraphicDevice& rDevice,
+ const OutDevProviderSharedPtr& rOutDev,
+ bool bProtect,
+ bool bHaveAlpha );
+
+ /** Set primary output device
+
+ This changes the primary output device, where rendering is
+ sent to.
+ */
+ void setOutDev( const OutDevProviderSharedPtr& rOutDev,
+ bool bProtect);
+
+ /** Set secondary output device
+
+ Used for sprites, to generate mask bitmap.
+ */
+ void setBackgroundOutDev( const OutDevProviderSharedPtr& rOutDev );
+
+
+ // CanvasHelper functionality
+ // ==========================
+
+ // XCanvas (only providing, not implementing the
+ // interface. Also note subtle method parameter differences)
+ void clear();
+ void drawLine( const css::rendering::XCanvas* rCanvas,
+ 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* rCanvas,
+ 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* rCanvas,
+ 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* rCanvas,
+ 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* rCanvas,
+ 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* rCanvas,
+ 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* rCanvas,
+ 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* rCanvas,
+ 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* rCanvas,
+ 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* rCanvas,
+ 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* rCanvas,
+ 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* rCanvas,
+ const css::rendering::FontInfo& aFilter,
+ const css::uno::Sequence<
+ css::beans::PropertyValue >& aFontProperties );
+
+ css::uno::Reference< css::rendering::XCachedPrimitive >
+ drawText( const css::rendering::XCanvas* rCanvas,
+ 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* rCanvas,
+ 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* rCanvas,
+ 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* rCanvas,
+ const css::uno::Reference<
+ css::rendering::XBitmap >& xBitmap,
+ const css::rendering::ViewState& viewState,
+ const css::rendering::RenderState& renderState );
+ // cast away const, need to change refcount (as this is
+ // ~invisible to client code, still logically const)
+ css::uno::Reference< css::rendering::XGraphicDevice >
+ getDevice() { return css::uno::Reference< css::rendering::XGraphicDevice >(mpDevice); }
+
+
+ // BitmapCanvasHelper functionality
+ // ================================
+
+ 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 );
+
+ css::uno::Sequence< sal_Int8 >
+ getPixel( css::rendering::IntegerBitmapLayout& bitmapLayout,
+ const css::geometry::IntegerPoint2D& pos );
+
+ css::rendering::IntegerBitmapLayout getMemoryLayout();
+
+ /// Repaint a cached bitmap
+ bool repaint( const GraphicObjectSharedPtr& rGrf,
+ const css::rendering::ViewState& viewState,
+ const css::rendering::RenderState& renderState,
+ const ::Point& rPt,
+ const ::Size& rSz,
+ const GraphicAttr& rAttr ) const;
+
+ /** Flush drawing queue.
+
+ This only works for Window canvases, and ensures that all
+ pending render operations are flushed to the
+ driver/hardware.
+ */
+ void flush() const;
+
+ enum ColorType
+ {
+ LINE_COLOR, FILL_COLOR, TEXT_COLOR, IGNORE_COLOR
+ };
+
+ // returns alpha of color
+ int setupOutDevState( const css::rendering::ViewState& viewState,
+ const css::rendering::RenderState& renderState,
+ ColorType eColorType ) const;
+
+ bool hasAlpha() const { return mbHaveAlpha; }
+
+ protected:
+ /** Phyical output device
+
+ Deliberately not a refcounted reference, because of
+ potential circular references for spritecanvas.
+ */
+ css::rendering::XGraphicDevice* mpDevice;
+
+ /// Rendering to this outdev preserves its state
+ OutDevProviderSharedPtr mpProtectedOutDevProvider;
+
+ /// Rendering to this outdev does not preserve its state
+ OutDevProviderSharedPtr mpOutDevProvider;
+
+ /// Rendering to this outdev does not preserve its state
+ OutDevProviderSharedPtr mp2ndOutDevProvider;
+
+ /// When true, content is able to represent alpha
+ bool mbHaveAlpha;
+
+ private:
+ css::uno::Reference< css::rendering::XCachedPrimitive >
+ implDrawBitmap( const css::rendering::XCanvas* rCanvas,
+ const css::uno::Reference<
+ css::rendering::XBitmap >& xBitmap,
+ const css::rendering::ViewState& viewState,
+ const css::rendering::RenderState& renderState,
+ bool bModulateColors );
+
+ bool setupTextOutput( ::Point& o_rOutPos,
+ const css::rendering::ViewState& viewState,
+ const css::rendering::RenderState& renderState,
+ const css::uno::Reference< css::rendering::XCanvasFont >& xFont ) const;
+
+ };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/vcl/canvashelper_texturefill.cxx b/canvas/source/vcl/canvashelper_texturefill.cxx
new file mode 100644
index 000000000..fa8b97395
--- /dev/null
+++ b/canvas/source/vcl/canvashelper_texturefill.cxx
@@ -0,0 +1,1088 @@
+/* -*- 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 <tuple>
+
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/numeric/ftools.hxx>
+#include <basegfx/point/b2dpoint.hxx>
+#include <basegfx/polygon/b2dpolygon.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 <tools/diagnose_ex.h>
+#include <tools/poly.hxx>
+#include <vcl/bitmapex.hxx>
+#include <vcl/canvastools.hxx>
+#include <vcl/virdev.hxx>
+#include <vcl/gradient.hxx>
+
+#include <canvas/canvastools.hxx>
+#include <parametricpolypolygon.hxx>
+
+#include "canvashelper.hxx"
+#include "impltools.hxx"
+
+
+using namespace ::com::sun::star;
+
+namespace vclcanvas
+{
+ namespace
+ {
+ bool textureFill( OutputDevice& rOutDev,
+ const GraphicObject& rGraphic,
+ const ::Point& rPosPixel,
+ const ::Size& rNextTileX,
+ const ::Size& rNextTileY,
+ sal_Int32 nTilesX,
+ sal_Int32 nTilesY,
+ const ::Size& rTileSize,
+ const GraphicAttr& rAttr)
+ {
+ bool bRet( false );
+ Point aCurrPos;
+ int nX, nY;
+
+ for( nY=0; nY < nTilesY; ++nY )
+ {
+ aCurrPos.setX( rPosPixel.X() + nY*rNextTileY.Width() );
+ aCurrPos.setY( rPosPixel.Y() + nY*rNextTileY.Height() );
+
+ for( nX=0; nX < nTilesX; ++nX )
+ {
+ // update return value. This method should return true, if
+ // at least one of the looped Draws succeeded.
+ bRet |= rGraphic.Draw(rOutDev,
+ aCurrPos,
+ rTileSize,
+ &rAttr);
+
+ aCurrPos.AdjustX(rNextTileX.Width() );
+ aCurrPos.AdjustY(rNextTileX.Height() );
+ }
+ }
+
+ return bRet;
+ }
+
+
+ /** Fill linear or axial gradient
+
+ Since most of the code for linear and axial gradients are
+ the same, we've a unified method here
+ */
+ void fillLinearGradient( OutputDevice& rOutDev,
+ const ::basegfx::B2DHomMatrix& rTextureTransform,
+ const ::tools::Rectangle& rBounds,
+ unsigned int nStepCount,
+ const ::canvas::ParametricPolyPolygon::Values& rValues,
+ const std::vector< ::Color >& rColors )
+ {
+ // determine general position of gradient in relation to
+ // the bound rect
+ // =====================================================
+
+ ::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 *= rTextureTransform;
+ aLeftBottom *= rTextureTransform;
+ aRightTop *= rTextureTransform;
+ aRightBottom*= rTextureTransform;
+
+ // calc length of bound rect diagonal
+ const ::basegfx::B2DVector aBoundRectDiagonal(
+ vcl::unotools::b2DPointFromPoint( rBounds.TopLeft() ) -
+ vcl::unotools::b2DPointFromPoint( rBounds.BottomRight() ) );
+ const double nDiagonalLength( aBoundRectDiagonal.getLength() );
+
+ // create direction of gradient:
+ // _______
+ // | | |
+ // -> | | | ...
+ // | | |
+ // -------
+ ::basegfx::B2DVector aDirection( aRightTop - aLeftTop );
+ aDirection.normalize();
+
+ // 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,
+ vcl::unotools::b2DRectangleFromRectangle(rBounds) );
+
+
+ // render gradient
+ // ===============
+
+ // First try to use directly VCL's DrawGradient(), as that one is generally
+ // a better choice than here decomposing to polygons. The VCL API allows
+ // only 2 colors, but that should generally do.
+ // Do not use nStepCount, it limits optimized implementations, and it's computed
+ // by vclcanvas based on number of colors, so it's practically irrelevant.
+
+ // 2 colors and 2 stops (at 0 and 1) is a linear gradient:
+ if( rColors.size() == 2 && rValues.maStops.size() == 2 && rValues.maStops[0] == 0 && rValues.maStops[1] == 1)
+ {
+ // tdf#144073: Note that the code below adjusts the gradient area this way.
+ // No, I have no idea why.
+ aLeftTop -= 2.0*nDiagonalLength*aDirection;
+ aLeftBottom -= 2.0*nDiagonalLength*aDirection;
+ aRightTop += 2.0*nDiagonalLength*aDirection;
+ aRightBottom += 2.0*nDiagonalLength*aDirection;
+ Gradient vclGradient( GradientStyle::Linear, rColors[ 0 ], rColors[ 1 ] );
+ ::tools::Polygon aTempPoly( static_cast<sal_uInt16>(5) );
+ aTempPoly[0] = ::Point( ::basegfx::fround( aLeftTop.getX() ),
+ ::basegfx::fround( aLeftTop.getY() ) );
+ aTempPoly[1] = ::Point( ::basegfx::fround( aRightTop.getX() ),
+ ::basegfx::fround( aRightTop.getY() ) );
+ aTempPoly[2] = ::Point( ::basegfx::fround( aRightBottom.getX() ),
+ ::basegfx::fround( aRightBottom.getY() ) );
+ aTempPoly[3] = ::Point( ::basegfx::fround( aLeftBottom.getX() ),
+ ::basegfx::fround( aLeftBottom.getY() ) );
+ aTempPoly[4] = aTempPoly[0];
+ rOutDev.DrawGradient( aTempPoly, vclGradient );
+ return;
+ }
+ // 3 colors with first and last being equal and 3 stops (at 0, 0.5 and 1) is an axial gradient:
+ if( rColors.size() == 3 && rColors[ 0 ] == rColors[ 2 ]
+ && rValues.maStops.size() == 3 && rValues.maStops[0] == 0
+ && rValues.maStops[1] == 0.5 && rValues.maStops[2] == 1)
+ {
+ // tdf#144073: Note that the code below adjusts the gradient area this way.
+ // No, I have no idea why.
+ aLeftTop -= 2.0*nDiagonalLength*aDirection;
+ aLeftBottom -= 2.0*nDiagonalLength*aDirection;
+ aRightTop += 2.0*nDiagonalLength*aDirection;
+ aRightBottom += 2.0*nDiagonalLength*aDirection;
+ Gradient vclGradient( GradientStyle::Axial, rColors[ 1 ], rColors[ 0 ] );
+ ::tools::Polygon aTempPoly( static_cast<sal_uInt16>(5) );
+ aTempPoly[0] = ::Point( ::basegfx::fround( aLeftTop.getX() ),
+ ::basegfx::fround( aLeftTop.getY() ) );
+ aTempPoly[1] = ::Point( ::basegfx::fround( aRightTop.getX() ),
+ ::basegfx::fround( aRightTop.getY() ) );
+ aTempPoly[2] = ::Point( ::basegfx::fround( aRightBottom.getX() ),
+ ::basegfx::fround( aRightBottom.getY() ) );
+ aTempPoly[3] = ::Point( ::basegfx::fround( aLeftBottom.getX() ),
+ ::basegfx::fround( aLeftBottom.getY() ) );
+ aTempPoly[4] = aTempPoly[0];
+ rOutDev.DrawGradient( aTempPoly, vclGradient );
+ return;
+ }
+
+ // for linear gradients, it's easy to render
+ // non-overlapping polygons: just split the gradient into
+ // nStepCount small strips. Prepare the strip now.
+
+ // 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)
+ ::tools::Polygon aTempPoly( static_cast<sal_uInt16>(5) );
+
+ OSL_ENSURE( nStepCount >= 3,
+ "fillLinearGradient(): stepcount smaller than 3" );
+
+
+ // fill initial strip (extending two times the bound rect's
+ // diagonal to the 'left'
+
+
+ // calculate left edge, by moving left edge of the
+ // gradient rect two times the bound rect's diagonal to
+ // the 'left'. Since we postpone actual rendering into the
+ // loop below, we set the _right_ edge here, which will be
+ // readily copied into the left edge in the loop below
+ const ::basegfx::B2DPoint& rPoint1( aLeftTop - 2.0*nDiagonalLength*aDirection );
+ aTempPoly[1] = ::Point( ::basegfx::fround( rPoint1.getX() ),
+ ::basegfx::fround( rPoint1.getY() ) );
+
+ const ::basegfx::B2DPoint& rPoint2( aLeftBottom - 2.0*nDiagonalLength*aDirection );
+ aTempPoly[2] = ::Point( ::basegfx::fround( rPoint2.getX() ),
+ ::basegfx::fround( rPoint2.getY() ) );
+
+
+ // iteratively render all other strips
+
+
+ // ensure that nStepCount matches color stop parity, to
+ // have a well-defined middle color e.g. for axial
+ // gradients.
+ if( (rColors.size() % 2) != (nStepCount % 2) )
+ ++nStepCount;
+
+ rOutDev.SetLineColor();
+
+ basegfx::utils::KeyStopLerp aLerper(rValues.maStops);
+
+ // only iterate nStepCount-1 steps, as the last strip is
+ // explicitly painted below
+ for( unsigned int i=0; i<nStepCount-1; ++i )
+ {
+ std::ptrdiff_t nIndex;
+ double fAlpha;
+ std::tie(nIndex,fAlpha)=aLerper.lerp(double(i)/nStepCount);
+
+ rOutDev.SetFillColor(
+ Color( static_cast<sal_uInt8>(basegfx::utils::lerp(rColors[nIndex].GetRed(),rColors[nIndex+1].GetRed(),fAlpha)),
+ static_cast<sal_uInt8>(basegfx::utils::lerp(rColors[nIndex].GetGreen(),rColors[nIndex+1].GetGreen(),fAlpha)),
+ static_cast<sal_uInt8>(basegfx::utils::lerp(rColors[nIndex].GetBlue(),rColors[nIndex+1].GetBlue(),fAlpha)) ));
+
+ // copy right edge of polygon to left edge (and also
+ // copy the closing point)
+ aTempPoly[0] = aTempPoly[4] = aTempPoly[1];
+ aTempPoly[3] = aTempPoly[2];
+
+ // calculate new right edge, from interpolating
+ // between start and end line. Note that i is
+ // increased by one, to account for the fact that we
+ // calculate the right border here (whereas the fill
+ // color is governed by the left edge)
+ const ::basegfx::B2DPoint& rPoint3(
+ (nStepCount - i-1)/double(nStepCount)*aLeftTop +
+ (i+1)/double(nStepCount)*aRightTop );
+ aTempPoly[1] = ::Point( ::basegfx::fround( rPoint3.getX() ),
+ ::basegfx::fround( rPoint3.getY() ) );
+
+ const ::basegfx::B2DPoint& rPoint4(
+ (nStepCount - i-1)/double(nStepCount)*aLeftBottom +
+ (i+1)/double(nStepCount)*aRightBottom );
+ aTempPoly[2] = ::Point( ::basegfx::fround( rPoint4.getX() ),
+ ::basegfx::fround( rPoint4.getY() ) );
+
+ rOutDev.DrawPolygon( aTempPoly );
+ }
+
+ // fill final strip (extending two times the bound rect's
+ // diagonal to the 'right'
+
+
+ // copy right edge of polygon to left edge (and also
+ // copy the closing point)
+ aTempPoly[0] = aTempPoly[4] = aTempPoly[1];
+ aTempPoly[3] = aTempPoly[2];
+
+ // calculate new right edge, by moving right edge of the
+ // gradient rect two times the bound rect's diagonal to
+ // the 'right'.
+ const ::basegfx::B2DPoint& rPoint3( aRightTop + 2.0*nDiagonalLength*aDirection );
+ aTempPoly[0] = aTempPoly[4] = ::Point( ::basegfx::fround( rPoint3.getX() ),
+ ::basegfx::fround( rPoint3.getY() ) );
+
+ const ::basegfx::B2DPoint& rPoint4( aRightBottom + 2.0*nDiagonalLength*aDirection );
+ aTempPoly[3] = ::Point( ::basegfx::fround( rPoint4.getX() ),
+ ::basegfx::fround( rPoint4.getY() ) );
+
+ rOutDev.SetFillColor( rColors.back() );
+
+ rOutDev.DrawPolygon( aTempPoly );
+ }
+
+ void fillPolygonalGradient( OutputDevice& rOutDev,
+ const ::basegfx::B2DHomMatrix& rTextureTransform,
+ const ::tools::Rectangle& rBounds,
+ unsigned int nStepCount,
+ const ::canvas::ParametricPolyPolygon::Values& rValues,
+ const std::vector< ::Color >& rColors )
+ {
+ const ::basegfx::B2DPolygon& rGradientPoly( rValues.maGradientPoly );
+
+ ENSURE_OR_THROW( rGradientPoly.count() > 2,
+ "fillPolygonalGradient(): polygon without area given" );
+
+ // 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;
+
+ // only transform outer polygon _after_ copying it into
+ // aInnerPoly, because inner polygon has to be scaled before
+ // the actual texture transformation takes place
+ aOuterPoly.transform( rTextureTransform );
+
+ // determine overall transformation for inner polygon (might
+ // have to be prefixed by anisotrophic scaling)
+ ::basegfx::B2DHomMatrix aInnerPolygonTransformMatrix;
+
+
+ // 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 *= rTextureTransform;
+
+ // apply final matrix to polygon
+ aInnerPoly.transform( aInnerPolygonTransformMatrix );
+
+
+ const sal_uInt32 nNumPoints( aOuterPoly.count() );
+ ::tools::Polygon aTempPoly( static_cast<sal_uInt16>(nNumPoints+1) );
+
+ // increase number of steps by one: polygonal gradients have
+ // the outermost polygon rendered in rColor2, and the
+ // innermost in rColor1. The innermost polygon will never
+ // have zero area, thus, we must divide the interval into
+ // nStepCount+1 steps. For example, to create 3 steps:
+
+ // | |
+ // |-------|-------|-------|
+ // | |
+ // 3 2 1 0
+
+ // This yields 4 tick marks, where 0 is never attained (since
+ // zero-area polygons typically don't display perceivable
+ // color).
+ ++nStepCount;
+
+ rOutDev.SetLineColor();
+
+ basegfx::utils::KeyStopLerp aLerper(rValues.maStops);
+
+ // fill background
+ rOutDev.SetFillColor( rColors.front() );
+ rOutDev.DrawRect( rBounds );
+
+ // render polygon
+ // ==============
+
+ for( unsigned int i=1,p; i<nStepCount; ++i )
+ {
+ const double fT( i/double(nStepCount) );
+
+ std::ptrdiff_t nIndex;
+ double fAlpha;
+ std::tie(nIndex,fAlpha)=aLerper.lerp(fT);
+
+ // lerp color
+ rOutDev.SetFillColor(
+ Color( static_cast<sal_uInt8>(basegfx::utils::lerp(rColors[nIndex].GetRed(),rColors[nIndex+1].GetRed(),fAlpha)),
+ static_cast<sal_uInt8>(basegfx::utils::lerp(rColors[nIndex].GetGreen(),rColors[nIndex+1].GetGreen(),fAlpha)),
+ static_cast<sal_uInt8>(basegfx::utils::lerp(rColors[nIndex].GetBlue(),rColors[nIndex+1].GetBlue(),fAlpha)) ));
+
+ // scale and render polygon, by interpolating between
+ // outer and inner polygon.
+
+ for( p=0; p<nNumPoints; ++p )
+ {
+ const ::basegfx::B2DPoint& rOuterPoint( aOuterPoly.getB2DPoint(p) );
+ const ::basegfx::B2DPoint& rInnerPoint( aInnerPoly.getB2DPoint(p) );
+
+ aTempPoly[static_cast<sal_uInt16>(p)] = ::Point(
+ basegfx::fround( fT*rInnerPoint.getX() + (1-fT)*rOuterPoint.getX() ),
+ basegfx::fround( fT*rInnerPoint.getY() + (1-fT)*rOuterPoint.getY() ) );
+ }
+
+ // close polygon explicitly
+ aTempPoly[static_cast<sal_uInt16>(p)] = aTempPoly[0];
+
+ // TODO(P1): compare with vcl/source/gdi/outdev4.cxx,
+ // OutputDevice::ImplDrawComplexGradient(), there's a note
+ // that on some VDev's, rendering disjunct poly-polygons
+ // is faster!
+ rOutDev.DrawPolygon( aTempPoly );
+ }
+ }
+
+ void doGradientFill( OutputDevice& rOutDev,
+ const ::canvas::ParametricPolyPolygon::Values& rValues,
+ const std::vector< ::Color >& rColors,
+ const ::basegfx::B2DHomMatrix& rTextureTransform,
+ const ::tools::Rectangle& rBounds,
+ unsigned int nStepCount )
+ {
+ switch( rValues.meType )
+ {
+ case ::canvas::ParametricPolyPolygon::GradientType::Linear:
+ fillLinearGradient( rOutDev,
+ rTextureTransform,
+ rBounds,
+ nStepCount,
+ rValues,
+ rColors );
+ break;
+
+ case ::canvas::ParametricPolyPolygon::GradientType::Elliptical:
+ case ::canvas::ParametricPolyPolygon::GradientType::Rectangular:
+ fillPolygonalGradient( rOutDev,
+ rTextureTransform,
+ rBounds,
+ nStepCount,
+ rValues,
+ rColors );
+ break;
+
+ default:
+ ENSURE_OR_THROW( false,
+ "CanvasHelper::doGradientFill(): Unexpected case" );
+ }
+ }
+
+ int numColorSteps( const ::Color& rColor1, const ::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 gradientFill( OutputDevice& rOutDev,
+ OutputDevice* p2ndOutDev,
+ const ::canvas::ParametricPolyPolygon::Values& rValues,
+ const std::vector< ::Color >& rColors,
+ const ::tools::PolyPolygon& rPoly,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState,
+ const rendering::Texture& texture,
+ int nTransparency )
+ {
+ // TODO(T2): It is maybe necessary to lock here, should
+ // maGradientPoly someday cease to be const. But then, beware of
+ // deadlocks, canvashelper calls this method with locked own
+ // mutex.
+
+ // calc step size
+
+ 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);
+
+ rOutDev.SetLineColor();
+
+ // determine maximal bound rect of texture-filled
+ // polygon
+ const ::tools::Rectangle aPolygonDeviceRectOrig(
+ rPoly.GetBoundRect() );
+
+ if( tools::isRectangle( rPoly ) )
+ {
+ // use optimized output path
+
+
+ // this distinction really looks like a
+ // micro-optimization, but in fact greatly speeds up
+ // especially complex gradients. That's because when using
+ // clipping, we can output polygons instead of
+ // poly-polygons, and don't have to output the gradient
+ // twice for XOR
+
+ rOutDev.Push( vcl::PushFlags::CLIPREGION );
+ rOutDev.IntersectClipRegion( aPolygonDeviceRectOrig );
+ doGradientFill( rOutDev,
+ rValues,
+ rColors,
+ aTotalTransform,
+ aPolygonDeviceRectOrig,
+ nStepCount );
+ rOutDev.Pop();
+
+ if( p2ndOutDev && nTransparency < 253 )
+ {
+ // HACK. Normally, CanvasHelper does not care about
+ // actually what mp2ndOutDev is... well, here we do &
+ // assume a 1bpp target - everything beyond 97%
+ // transparency is fully transparent
+ p2ndOutDev->SetFillColor( COL_BLACK );
+ p2ndOutDev->DrawRect( aPolygonDeviceRectOrig );
+ }
+ }
+ else
+ {
+ const vcl::Region aPolyClipRegion( rPoly );
+
+ rOutDev.Push( vcl::PushFlags::CLIPREGION );
+ rOutDev.IntersectClipRegion( aPolyClipRegion );
+
+ doGradientFill( rOutDev,
+ rValues,
+ rColors,
+ aTotalTransform,
+ aPolygonDeviceRectOrig,
+ nStepCount );
+ rOutDev.Pop();
+
+ if( p2ndOutDev && nTransparency < 253 )
+ {
+ // HACK. Normally, CanvasHelper does not care about
+ // actually what mp2ndOutDev is... well, here we do &
+ // assume a 1bpp target - everything beyond 97%
+ // transparency is fully transparent
+ p2ndOutDev->SetFillColor( COL_BLACK );
+ p2ndOutDev->DrawPolyPolygon( rPoly );
+ }
+ }
+
+#ifdef DEBUG_CANVAS_CANVASHELPER_TEXTUREFILL
+ // extra-verbosity
+ {
+ ::basegfx::B2DRectangle aRect(0.0, 0.0, 1.0, 1.0);
+ ::basegfx::B2DRectangle aTextureDeviceRect;
+ ::basegfx::B2DHomMatrix aTextureTransform;
+ ::canvas::tools::calcTransformedRectBounds( aTextureDeviceRect,
+ aRect,
+ aTextureTransform );
+ rOutDev.SetLineColor( COL_RED );
+ rOutDev.SetFillColor();
+ rOutDev.DrawRect( vcl::unotools::rectangleFromB2DRectangle( aTextureDeviceRect ) );
+
+ rOutDev.SetLineColor( COL_BLUE );
+ ::tools::Polygon aPoly1(
+ vcl::unotools::rectangleFromB2DRectangle( aRect ));
+ ::basegfx::B2DPolygon aPoly2( aPoly1.getB2DPolygon() );
+ aPoly2.transform( aTextureTransform );
+ ::tools::Polygon aPoly3( aPoly2 );
+ rOutDev.DrawPolygon( aPoly3 );
+ }
+#endif
+
+ return true;
+ }
+ }
+
+ 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_ARG_OR_THROW( xPolyPolygon.is(),
+ "CanvasHelper::fillPolyPolygon(): polygon is NULL");
+ ENSURE_ARG_OR_THROW( textures.hasElements(),
+ "CanvasHelper::fillTexturedPolyPolygon: empty texture sequence");
+
+ if( mpOutDevProvider )
+ {
+ tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDevProvider );
+
+ const int nTransparency( setupOutDevState( viewState, renderState, IGNORE_COLOR ) );
+ ::tools::PolyPolygon aPolyPoly( tools::mapPolyPolygon(
+ ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(xPolyPolygon),
+ 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 && pGradient->getValues().maColors.hasElements() )
+ {
+ // copy state from Gradient polypoly locally
+ // (given object might change!)
+ const ::canvas::ParametricPolyPolygon::Values& rValues(
+ pGradient->getValues() );
+
+ if( rValues.maColors.getLength() < 2 )
+ {
+ rendering::RenderState aTempState=renderState;
+ aTempState.DeviceColor = rValues.maColors[0];
+ fillPolyPolygon(pCanvas, xPolyPolygon, viewState, aTempState);
+ }
+ else
+ {
+ std::vector< ::Color > aColors(rValues.maColors.getLength());
+ std::transform(&rValues.maColors[0],
+ &rValues.maColors[0]+rValues.maColors.getLength(),
+ aColors.begin(),
+ [](const uno::Sequence< double >& aColor) {
+ return vcl::unotools::stdColorSpaceSequenceToColor( aColor );
+ } );
+
+ // TODO(E1): Return value
+ // TODO(F1): FillRule
+ gradientFill( mpOutDevProvider->getOutDev(),
+ mp2ndOutDevProvider ? &mp2ndOutDevProvider->getOutDev() : nullptr,
+ rValues,
+ aColors,
+ aPolyPoly,
+ viewState,
+ renderState,
+ textures[0],
+ nTransparency );
+ }
+ }
+ else
+ {
+ // TODO(F1): The generic case is missing here
+ ENSURE_OR_THROW( false,
+ "CanvasHelper::fillTexturedPolyPolygon(): unknown parametric polygon encountered" );
+ }
+ }
+ else if( textures[0].Bitmap.is() )
+ {
+ geometry::IntegerSize2D aBmpSize( textures[0].Bitmap->getSize() );
+
+ ENSURE_ARG_OR_THROW( aBmpSize.Width != 0 &&
+ aBmpSize.Height != 0,
+ "CanvasHelper::fillTexturedPolyPolygon(): zero-sized texture bitmap" );
+
+ // determine maximal bound rect of texture-filled
+ // polygon
+ const ::tools::Rectangle aPolygonDeviceRect(
+ aPolyPoly.GetBoundRect() );
+
+
+ // first of all, determine whether we have a
+ // drawBitmap() in disguise
+ // =========================================
+
+ const bool bRectangularPolygon( tools::isRectangle( aPolyPoly ) );
+
+ ::basegfx::B2DHomMatrix aTotalTransform;
+ ::canvas::tools::mergeViewAndRenderTransform(aTotalTransform,
+ viewState,
+ renderState);
+ ::basegfx::B2DHomMatrix aTextureTransform;
+ ::basegfx::unotools::homMatrixFromAffineMatrix( aTextureTransform,
+ textures[0].AffineTransform );
+
+ aTotalTransform *= aTextureTransform;
+
+ const ::basegfx::B2DRectangle aRect(0.0, 0.0, 1.0, 1.0);
+ ::basegfx::B2DRectangle aTextureDeviceRect;
+ ::canvas::tools::calcTransformedRectBounds( aTextureDeviceRect,
+ aRect,
+ aTotalTransform );
+
+ const ::tools::Rectangle aIntegerTextureDeviceRect(
+ vcl::unotools::rectangleFromB2DRectangle( aTextureDeviceRect ) );
+
+ if( bRectangularPolygon &&
+ aIntegerTextureDeviceRect == aPolygonDeviceRect )
+ {
+ rendering::RenderState aLocalState( renderState );
+ ::canvas::tools::appendToRenderState(aLocalState,
+ aTextureTransform);
+ ::basegfx::B2DHomMatrix aScaleCorrection;
+ aScaleCorrection.scale( 1.0/aBmpSize.Width,
+ 1.0/aBmpSize.Height );
+ ::canvas::tools::appendToRenderState(aLocalState,
+ aScaleCorrection);
+
+ // need alpha modulation?
+ if( !::rtl::math::approxEqual( textures[0].Alpha,
+ 1.0 ) )
+ {
+ // setup alpha modulation values
+ aLocalState.DeviceColor.realloc(4);
+ double* pColor = aLocalState.DeviceColor.getArray();
+ pColor[0] =
+ pColor[1] =
+ pColor[2] = 0.0;
+ pColor[3] = textures[0].Alpha;
+
+ return drawBitmapModulated( pCanvas,
+ textures[0].Bitmap,
+ viewState,
+ aLocalState );
+ }
+ else
+ {
+ return drawBitmap( pCanvas,
+ textures[0].Bitmap,
+ viewState,
+ aLocalState );
+ }
+ }
+ else
+ {
+ // No easy mapping to drawBitmap() - calculate
+ // texturing parameters
+ // ===========================================
+
+ BitmapEx aBmpEx( tools::bitmapExFromXBitmap( textures[0].Bitmap ) );
+
+ // scale down bitmap to [0,1]x[0,1] rect, as required
+ // from the XCanvas interface.
+ ::basegfx::B2DHomMatrix aScaling;
+ ::basegfx::B2DHomMatrix aPureTotalTransform; // pure view*render*texture transform
+ aScaling.scale( 1.0/aBmpSize.Width,
+ 1.0/aBmpSize.Height );
+
+ aTotalTransform = aTextureTransform * aScaling;
+ aPureTotalTransform = aTextureTransform;
+
+ // combine with view and render transform
+ ::basegfx::B2DHomMatrix aMatrix;
+ ::canvas::tools::mergeViewAndRenderTransform(aMatrix, viewState, renderState);
+
+ // combine all three transformations into one
+ // global texture-to-device-space transformation
+ aTotalTransform *= aMatrix;
+ aPureTotalTransform *= aMatrix;
+
+ // analyze transformation, and setup an
+ // appropriate GraphicObject
+ ::basegfx::B2DVector aScale;
+ ::basegfx::B2DPoint aOutputPos;
+ double nRotate;
+ double nShearX;
+ aTotalTransform.decompose( aScale, aOutputPos, nRotate, nShearX );
+
+ GraphicAttr aGrfAttr;
+ GraphicObjectSharedPtr pGrfObj;
+
+ if( ::basegfx::fTools::equalZero( nShearX ) )
+ {
+ // no shear, GraphicObject is enough (the
+ // GraphicObject only supports scaling, rotation
+ // and translation)
+
+ // #i75339# don't apply mirror flags, having
+ // negative size values is enough to make
+ // GraphicObject flip the bitmap
+
+ // The angle has to be mapped from radian to tenths of
+ // degrees with the orientation reversed: [0,2Pi) ->
+ // (3600,0]. Note that the original angle may have
+ // values outside the [0,2Pi) interval.
+ const double nAngleInTenthOfDegrees (3600.0 - basegfx::rad2deg<10>(nRotate));
+ aGrfAttr.SetRotation( Degree10(::basegfx::fround(nAngleInTenthOfDegrees)) );
+
+ pGrfObj = std::make_shared<GraphicObject>( aBmpEx );
+ }
+ else
+ {
+ // modify output position, to account for the fact
+ // that transformBitmap() always normalizes its output
+ // bitmap into the smallest enclosing box.
+ ::basegfx::B2DRectangle aDestRect;
+ ::canvas::tools::calcTransformedRectBounds( aDestRect,
+ ::basegfx::B2DRectangle(0,
+ 0,
+ aBmpSize.Width,
+ aBmpSize.Height),
+ aMatrix );
+
+ aOutputPos.setX( aDestRect.getMinX() );
+ aOutputPos.setY( aDestRect.getMinY() );
+
+ // complex transformation, use generic affine bitmap
+ // transformation
+ aBmpEx = tools::transformBitmap( aBmpEx,
+ aTotalTransform);
+
+ pGrfObj = std::make_shared<GraphicObject>( aBmpEx );
+
+ // clear scale values, generated bitmap already
+ // contains scaling
+ aScale.setX( 1.0 ); aScale.setY( 1.0 );
+
+ // update bitmap size, bitmap has changed above.
+ aBmpSize = vcl::unotools::integerSize2DFromSize(aBmpEx.GetSizePixel());
+ }
+
+
+ // render texture tiled into polygon
+ // =================================
+
+ // calc device space direction vectors. We employ
+ // the following approach for tiled output: the
+ // texture bitmap is output in texture space
+ // x-major order, i.e. tile neighbors in texture
+ // space x direction are rendered back-to-back in
+ // device coordinate space (after the full device
+ // transformation). Thus, the aNextTile* vectors
+ // denote the output position updates in device
+ // space, to get from one tile to the next.
+ ::basegfx::B2DVector aNextTileX( 1.0, 0.0 );
+ ::basegfx::B2DVector aNextTileY( 0.0, 1.0 );
+ aNextTileX *= aPureTotalTransform;
+ aNextTileY *= aPureTotalTransform;
+
+ ::basegfx::B2DHomMatrix aInverseTextureTransform( aPureTotalTransform );
+
+ ENSURE_ARG_OR_THROW( aInverseTextureTransform.isInvertible(),
+ "CanvasHelper::fillTexturedPolyPolygon(): singular texture matrix" );
+
+ aInverseTextureTransform.invert();
+
+ // calc bound rect of extended texture area in
+ // device coordinates. Therefore, we first calc
+ // the area of the polygon bound rect in texture
+ // space. To maintain texture phase, this bound
+ // rect is then extended to integer coordinates
+ // (extended, because shrinking might leave some
+ // inner polygon areas unfilled).
+ // Finally, the bound rect is transformed back to
+ // device coordinate space, were we determine the
+ // start point from it.
+ ::basegfx::B2DRectangle aTextureSpacePolygonRect;
+ ::canvas::tools::calcTransformedRectBounds( aTextureSpacePolygonRect,
+ vcl::unotools::b2DRectangleFromRectangle(aPolygonDeviceRect),
+ aInverseTextureTransform );
+
+ // calc left, top of extended polygon rect in
+ // texture space, create one-texture instance rect
+ // from it (i.e. rect from start point extending
+ // 1.0 units to the right and 1.0 units to the
+ // bottom). Note that the rounding employed here
+ // is a bit subtle, since we need to round up/down
+ // as _soon_ as any fractional amount is
+ // encountered. This is to ensure that the full
+ // polygon area is filled with texture tiles.
+ const sal_Int32 nX1( ::canvas::tools::roundDown( aTextureSpacePolygonRect.getMinX() ) );
+ const sal_Int32 nY1( ::canvas::tools::roundDown( aTextureSpacePolygonRect.getMinY() ) );
+ const sal_Int32 nX2( ::canvas::tools::roundUp( aTextureSpacePolygonRect.getMaxX() ) );
+ const sal_Int32 nY2( ::canvas::tools::roundUp( aTextureSpacePolygonRect.getMaxY() ) );
+ const ::basegfx::B2DRectangle aSingleTextureRect(
+ nX1, nY1,
+ nX1 + 1.0,
+ nY1 + 1.0 );
+
+ // and convert back to device space
+ ::basegfx::B2DRectangle aSingleDeviceTextureRect;
+ ::canvas::tools::calcTransformedRectBounds( aSingleDeviceTextureRect,
+ aSingleTextureRect,
+ aPureTotalTransform );
+
+ const ::Point aPtRepeat( vcl::unotools::pointFromB2DPoint(
+ aSingleDeviceTextureRect.getMinimum() ) );
+ const ::Size aSz( ::basegfx::fround( aScale.getX() * aBmpSize.Width ),
+ ::basegfx::fround( aScale.getY() * aBmpSize.Height ) );
+ const ::Size aIntegerNextTileX( vcl::unotools::sizeFromB2DSize(aNextTileX) );
+ const ::Size aIntegerNextTileY( vcl::unotools::sizeFromB2DSize(aNextTileY) );
+
+ const ::Point aPt( textures[0].RepeatModeX == rendering::TexturingMode::NONE ?
+ ::basegfx::fround( aOutputPos.getX() ) : aPtRepeat.X(),
+ textures[0].RepeatModeY == rendering::TexturingMode::NONE ?
+ ::basegfx::fround( aOutputPos.getY() ) : aPtRepeat.Y() );
+ const sal_Int32 nTilesX( textures[0].RepeatModeX == rendering::TexturingMode::NONE ?
+ 1 : nX2 - nX1 );
+ const sal_Int32 nTilesY( textures[0].RepeatModeX == rendering::TexturingMode::NONE ?
+ 1 : nY2 - nY1 );
+
+ OutputDevice& rOutDev( mpOutDevProvider->getOutDev() );
+
+ if( bRectangularPolygon )
+ {
+ // use optimized output path
+
+
+ // this distinction really looks like a
+ // micro-optimization, but in fact greatly speeds up
+ // especially complex fills. That's because when using
+ // clipping, we can output polygons instead of
+ // poly-polygons, and don't have to output the gradient
+ // twice for XOR
+
+ // setup alpha modulation
+ if( !::rtl::math::approxEqual( textures[0].Alpha,
+ 1.0 ) )
+ {
+ // TODO(F1): Note that the GraphicManager has
+ // a subtle difference in how it calculates
+ // the resulting alpha value: it's using the
+ // inverse alpha values (i.e. 'transparency'),
+ // and calculates transOrig + transModulate,
+ // instead of transOrig + transModulate -
+ // transOrig*transModulate (which would be
+ // equivalent to the origAlpha*modulateAlpha
+ // the DX canvas performs)
+ aGrfAttr.SetAlpha(
+ static_cast< sal_uInt8 >(
+ ::basegfx::fround( 255.0 * textures[0].Alpha ) ) );
+ }
+
+ rOutDev.IntersectClipRegion( aPolygonDeviceRect );
+ textureFill( rOutDev,
+ *pGrfObj,
+ aPt,
+ aIntegerNextTileX,
+ aIntegerNextTileY,
+ nTilesX,
+ nTilesY,
+ aSz,
+ aGrfAttr );
+
+ if( mp2ndOutDevProvider )
+ {
+ OutputDevice& r2ndOutDev( mp2ndOutDevProvider->getOutDev() );
+ r2ndOutDev.IntersectClipRegion( aPolygonDeviceRect );
+ textureFill( r2ndOutDev,
+ *pGrfObj,
+ aPt,
+ aIntegerNextTileX,
+ aIntegerNextTileY,
+ nTilesX,
+ nTilesY,
+ aSz,
+ aGrfAttr );
+ }
+ }
+ else
+ {
+ // output texture the hard way: XORing out the
+ // polygon
+ // ===========================================
+
+ if( !::rtl::math::approxEqual( textures[0].Alpha,
+ 1.0 ) )
+ {
+ // uh-oh. alpha blending is required,
+ // cannot do direct XOR, but have to
+ // prepare the filled polygon within a
+ // VDev
+ ScopedVclPtrInstance< VirtualDevice > pVDev( rOutDev );
+ pVDev->SetOutputSizePixel( aPolygonDeviceRect.GetSize() );
+
+ // shift output to origin of VDev
+ const ::Point aOutPos( aPt - aPolygonDeviceRect.TopLeft() );
+ aPolyPoly.Translate( ::Point( -aPolygonDeviceRect.Left(),
+ -aPolygonDeviceRect.Top() ) );
+
+ const vcl::Region aPolyClipRegion( aPolyPoly );
+
+ pVDev->SetClipRegion( aPolyClipRegion );
+ textureFill( *pVDev,
+ *pGrfObj,
+ aOutPos,
+ aIntegerNextTileX,
+ aIntegerNextTileY,
+ nTilesX,
+ nTilesY,
+ aSz,
+ aGrfAttr );
+
+ // output VDev content alpha-blended to
+ // target position.
+ const ::Point aEmptyPoint;
+ BitmapEx aContentBmp(
+ pVDev->GetBitmapEx( aEmptyPoint,
+ pVDev->GetOutputSizePixel() ) );
+
+ sal_uInt8 nCol( static_cast< sal_uInt8 >(
+ ::basegfx::fround( 255.0*( 1.0 - textures[0].Alpha ) ) ) );
+ AlphaMask aAlpha( pVDev->GetOutputSizePixel(),
+ &nCol );
+
+ BitmapEx aOutputBmpEx( aContentBmp.GetBitmap(), aAlpha );
+ rOutDev.DrawBitmapEx( aPolygonDeviceRect.TopLeft(),
+ aOutputBmpEx );
+
+ if( mp2ndOutDevProvider )
+ mp2ndOutDevProvider->getOutDev().DrawBitmapEx( aPolygonDeviceRect.TopLeft(),
+ aOutputBmpEx );
+ }
+ else
+ {
+ const vcl::Region aPolyClipRegion( aPolyPoly );
+
+ rOutDev.Push( vcl::PushFlags::CLIPREGION );
+ rOutDev.IntersectClipRegion( aPolyClipRegion );
+
+ textureFill( rOutDev,
+ *pGrfObj,
+ aPt,
+ aIntegerNextTileX,
+ aIntegerNextTileY,
+ nTilesX,
+ nTilesY,
+ aSz,
+ aGrfAttr );
+ rOutDev.Pop();
+
+ if( mp2ndOutDevProvider )
+ {
+ OutputDevice& r2ndOutDev( mp2ndOutDevProvider->getOutDev() );
+ r2ndOutDev.Push( vcl::PushFlags::CLIPREGION );
+
+ r2ndOutDev.IntersectClipRegion( aPolyClipRegion );
+ textureFill( r2ndOutDev,
+ *pGrfObj,
+ aPt,
+ aIntegerNextTileX,
+ aIntegerNextTileY,
+ nTilesX,
+ nTilesY,
+ aSz,
+ aGrfAttr );
+ r2ndOutDev.Pop();
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // TODO(P1): Provide caching here.
+ return uno::Reference< rendering::XCachedPrimitive >(nullptr);
+ }
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/vcl/devicehelper.cxx b/canvas/source/vcl/devicehelper.cxx
new file mode 100644
index 000000000..40d2575f8
--- /dev/null
+++ b/canvas/source/vcl/devicehelper.cxx
@@ -0,0 +1,217 @@
+/* -*- 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 <basegfx/utils/unopolypolygon.hxx>
+#include <canvas/canvastools.hxx>
+#include <tools/stream.hxx>
+#include <vcl/canvastools.hxx>
+#include <vcl/dibtools.hxx>
+
+#include "canvasbitmap.hxx"
+#include "devicehelper.hxx"
+
+using namespace ::com::sun::star;
+
+namespace vclcanvas
+{
+ DeviceHelper::DeviceHelper()
+ {}
+
+ void DeviceHelper::init( const OutDevProviderSharedPtr& rOutDev )
+ {
+ mpOutDev = rOutDev;
+ }
+
+ geometry::RealSize2D DeviceHelper::getPhysicalResolution()
+ {
+ if( !mpOutDev )
+ return ::canvas::tools::createInfiniteSize2D(); // we're disposed
+
+ // Map a one-by-one millimeter box to pixel
+ OutputDevice& rOutDev = mpOutDev->getOutDev();
+ const MapMode aOldMapMode( rOutDev.GetMapMode() );
+ rOutDev.SetMapMode( MapMode(MapUnit::MapMM) );
+ const Size aPixelSize( rOutDev.LogicToPixel(Size(1,1)) );
+ rOutDev.SetMapMode( aOldMapMode );
+
+ return vcl::unotools::size2DFromSize( aPixelSize );
+ }
+
+ geometry::RealSize2D DeviceHelper::getPhysicalSize()
+ {
+ if( !mpOutDev )
+ return ::canvas::tools::createInfiniteSize2D(); // we're disposed
+
+ // Map the pixel dimensions of the output window to millimeter
+ OutputDevice& rOutDev = mpOutDev->getOutDev();
+ const MapMode aOldMapMode( rOutDev.GetMapMode() );
+ rOutDev.SetMapMode( MapMode(MapUnit::MapMM) );
+ const Size aLogSize( rOutDev.PixelToLogic(rOutDev.GetOutputSizePixel()) );
+ rOutDev.SetMapMode( aOldMapMode );
+
+ return vcl::unotools::size2DFromSize( aLogSize );
+ }
+
+ uno::Reference< rendering::XLinePolyPolygon2D > DeviceHelper::createCompatibleLinePolyPolygon(
+ const uno::Reference< rendering::XGraphicDevice >& ,
+ const uno::Sequence< uno::Sequence< geometry::RealPoint2D > >& points )
+ {
+ uno::Reference< rendering::XLinePolyPolygon2D > xPoly;
+ if( !mpOutDev )
+ return xPoly; // we're disposed
+
+ xPoly.set( new ::basegfx::unotools::UnoPolyPolygon(
+ ::basegfx::unotools::polyPolygonFromPoint2DSequenceSequence( points ) ) );
+ // vcl only handles even_odd polygons
+ xPoly->setFillRule(rendering::FillRule_EVEN_ODD);
+
+ return xPoly;
+ }
+
+ uno::Reference< rendering::XBezierPolyPolygon2D > DeviceHelper::createCompatibleBezierPolyPolygon(
+ const uno::Reference< rendering::XGraphicDevice >& ,
+ const uno::Sequence< uno::Sequence< geometry::RealBezierSegment2D > >& points )
+ {
+ uno::Reference< rendering::XBezierPolyPolygon2D > xPoly;
+ if( !mpOutDev )
+ return xPoly; // we're disposed
+
+ xPoly.set( new ::basegfx::unotools::UnoPolyPolygon(
+ ::basegfx::unotools::polyPolygonFromBezier2DSequenceSequence( points ) ) );
+ // vcl only handles even_odd polygons
+ xPoly->setFillRule(rendering::FillRule_EVEN_ODD);
+
+ return xPoly;
+ }
+
+ uno::Reference< rendering::XBitmap > DeviceHelper::createCompatibleBitmap(
+ const uno::Reference< rendering::XGraphicDevice >& rDevice,
+ const geometry::IntegerSize2D& size )
+ {
+ if( !mpOutDev )
+ return uno::Reference< rendering::XBitmap >(); // we're disposed
+
+ return uno::Reference< rendering::XBitmap >(
+ new CanvasBitmap( vcl::unotools::sizeFromIntegerSize2D(size),
+ false,
+ *rDevice,
+ mpOutDev ) );
+ }
+
+ uno::Reference< rendering::XVolatileBitmap > DeviceHelper::createVolatileBitmap(
+ const uno::Reference< rendering::XGraphicDevice >& ,
+ const geometry::IntegerSize2D& )
+ {
+ return uno::Reference< rendering::XVolatileBitmap >();
+ }
+
+ uno::Reference< rendering::XBitmap > DeviceHelper::createCompatibleAlphaBitmap(
+ const uno::Reference< rendering::XGraphicDevice >& rDevice,
+ const geometry::IntegerSize2D& size )
+ {
+ if( !mpOutDev )
+ return uno::Reference< rendering::XBitmap >(); // we're disposed
+
+ return uno::Reference< rendering::XBitmap >(
+ new CanvasBitmap( vcl::unotools::sizeFromIntegerSize2D(size),
+ true,
+ *rDevice,
+ mpOutDev ) );
+ }
+
+ uno::Reference< rendering::XVolatileBitmap > DeviceHelper::createVolatileAlphaBitmap(
+ const uno::Reference< rendering::XGraphicDevice >& ,
+ const geometry::IntegerSize2D& )
+ {
+ return uno::Reference< rendering::XVolatileBitmap >();
+ }
+
+ void DeviceHelper::disposing()
+ {
+ // release all references
+ mpOutDev.reset();
+ }
+
+ uno::Any DeviceHelper::isAccelerated() const
+ {
+ return css::uno::Any(false);
+ }
+
+ uno::Any DeviceHelper::getDeviceHandle() const
+ {
+ if( !mpOutDev )
+ return uno::Any();
+
+ return uno::Any(
+ reinterpret_cast< sal_Int64 >(&mpOutDev->getOutDev()) );
+ }
+
+ uno::Any DeviceHelper::getSurfaceHandle() const
+ {
+ return getDeviceHandle();
+ }
+
+ namespace
+ {
+ uno::Reference<rendering::XColorSpace>& GetDeviceColorSpace()
+ {
+ static uno::Reference<rendering::XColorSpace> xColorSpace =
+ []()
+ {
+ auto xTmp = canvas::tools::getStdColorSpace();
+ assert( xTmp.is() );
+ return xTmp;
+ }();
+ return xColorSpace;
+ };
+ }
+
+ uno::Reference<rendering::XColorSpace> const & DeviceHelper::getColorSpace() const
+ {
+ // always the same
+ return GetDeviceColorSpace();
+ }
+
+ void DeviceHelper::dumpScreenContent() const
+ {
+ static sal_Int32 nFilePostfixCount(0);
+
+ if( !mpOutDev )
+ return;
+
+ OUString aFilename = "dbg_frontbuffer" + OUString::number(nFilePostfixCount) + ".bmp";
+
+ SvFileStream aStream( aFilename, StreamMode::STD_READWRITE );
+
+ const ::Point aEmptyPoint;
+ OutputDevice& rOutDev = mpOutDev->getOutDev();
+ bool bOldMap( rOutDev.IsMapModeEnabled() );
+ rOutDev.EnableMapMode( false );
+ WriteDIB(rOutDev.GetBitmapEx(aEmptyPoint, rOutDev.GetOutputSizePixel()), aStream, false);
+ rOutDev.EnableMapMode( bOldMap );
+
+ ++nFilePostfixCount;
+ }
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/vcl/devicehelper.hxx b/canvas/source/vcl/devicehelper.hxx
new file mode 100644
index 000000000..f8ff12aed
--- /dev/null
+++ b/canvas/source/vcl/devicehelper.hxx
@@ -0,0 +1,86 @@
+/* -*- 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/XGraphicDevice.hpp>
+
+#include "outdevprovider.hxx"
+
+
+/* Definition of DeviceHelper class */
+
+namespace vclcanvas
+{
+ class DeviceHelper
+ {
+ public:
+ DeviceHelper();
+
+ /// make noncopyable
+ DeviceHelper(const DeviceHelper&) = delete;
+ const DeviceHelper& operator=(const DeviceHelper&) = delete;
+
+ void init( const OutDevProviderSharedPtr& rOutDev );
+
+ /// 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 > const & getColorSpace() const;
+
+ const OutDevProviderSharedPtr& getOutDev() const { return mpOutDev; }
+
+ /** called when DumpScreenContent property is enabled on
+ XGraphicDevice, and writes out bitmaps of current screen.
+ */
+ void dumpScreenContent() const;
+
+ private:
+ /// For retrieving device info
+ OutDevProviderSharedPtr mpOutDev;
+ };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/vcl/impltools.cxx b/canvas/source/vcl/impltools.cxx
new file mode 100644
index 000000000..025b80bd2
--- /dev/null
+++ b/canvas/source/vcl/impltools.cxx
@@ -0,0 +1,239 @@
+/* -*- 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/matrix/b2dhommatrix.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/tuple/b2dtuple.hxx>
+#include <rtl/math.hxx>
+#include <tools/diagnose_ex.h>
+#include <sal/log.hxx>
+#include <vcl/bitmapex.hxx>
+#include <vcl/canvastools.hxx>
+#include <vcl/BitmapTools.hxx>
+#include <vcl/metric.hxx>
+#include <vcl/skia/SkiaHelper.hxx>
+
+#include <canvas/canvastools.hxx>
+
+#include "canvasbitmap.hxx"
+#include "impltools.hxx"
+#include "spritecanvas.hxx"
+
+
+using namespace ::com::sun::star;
+
+namespace vclcanvas::tools
+{
+ ::BitmapEx bitmapExFromXBitmap( const uno::Reference< rendering::XBitmap >& xBitmap )
+ {
+ // TODO(F3): CanvasCustomSprite should also be tunnelled
+ // through (also implements XIntegerBitmap interface)
+ CanvasBitmap* pBitmapImpl = dynamic_cast< CanvasBitmap* >( xBitmap.get() );
+
+ if( pBitmapImpl )
+ {
+ return pBitmapImpl->getBitmap();
+ }
+ else
+ {
+ SpriteCanvas* pCanvasImpl = dynamic_cast< SpriteCanvas* >( xBitmap.get() );
+ if( pCanvasImpl && pCanvasImpl->getBackBuffer() )
+ {
+ // TODO(F3): mind the plain Canvas impl. Consolidate with CWS canvas05
+ const ::OutputDevice& rDev( pCanvasImpl->getBackBuffer()->getOutDev() );
+ const ::Point aEmptyPoint;
+ return rDev.GetBitmapEx( aEmptyPoint,
+ rDev.GetOutputSizePixel() );
+ }
+
+ // TODO(F2): add support for floating point bitmap formats
+ uno::Reference< rendering::XIntegerReadOnlyBitmap > xIntBmp(
+ xBitmap, uno::UNO_QUERY_THROW );
+
+ ::BitmapEx aBmpEx = vcl::unotools::bitmapExFromXBitmap( xIntBmp );
+ if( !aBmpEx.IsEmpty() )
+ return aBmpEx;
+
+ // TODO(F1): extract pixel from XBitmap interface
+ ENSURE_OR_THROW( false,
+ "bitmapExFromXBitmap(): could not extract bitmap" );
+ }
+
+ return ::BitmapEx();
+ }
+
+ bool setupFontTransform( ::Point& o_rPoint,
+ vcl::Font& io_rVCLFont,
+ const rendering::ViewState& rViewState,
+ const rendering::RenderState& rRenderState,
+ ::OutputDevice const & rOutDev )
+ {
+ ::basegfx::B2DHomMatrix aMatrix;
+
+ ::canvas::tools::mergeViewAndRenderTransform(aMatrix,
+ rViewState,
+ rRenderState);
+
+ ::basegfx::B2DTuple aScale;
+ ::basegfx::B2DTuple aTranslate;
+ double nRotate, nShearX;
+
+ aMatrix.decompose( aScale, aTranslate, nRotate, nShearX );
+
+ // query font metric _before_ tampering with width and height
+ if( !::rtl::math::approxEqual(aScale.getX(), aScale.getY()) )
+ {
+ // retrieve true font width
+ const sal_Int32 nFontWidth( rOutDev.GetFontMetric( io_rVCLFont ).GetAverageFontWidth() );
+
+ const sal_Int32 nScaledFontWidth( ::basegfx::fround(nFontWidth * aScale.getX()) );
+
+ if( !nScaledFontWidth )
+ {
+ // scale is smaller than one pixel - disable text
+ // output altogether
+ return false;
+ }
+
+ io_rVCLFont.SetAverageFontWidth( nScaledFontWidth );
+ }
+
+ if( !::rtl::math::approxEqual(aScale.getY(), 1.0) )
+ {
+ const sal_Int32 nFontHeight( io_rVCLFont.GetFontHeight() );
+ io_rVCLFont.SetFontHeight( ::basegfx::fround(nFontHeight * aScale.getY()) );
+ }
+
+ io_rVCLFont.SetOrientation( Degree10( ::basegfx::fround(-basegfx::rad2deg<10>(fmod(nRotate, 2*M_PI))) ) );
+
+ // TODO(F2): Missing functionality in VCL: shearing
+ o_rPoint.setX( ::basegfx::fround(aTranslate.getX()) );
+ o_rPoint.setY( ::basegfx::fround(aTranslate.getY()) );
+
+ return true;
+ }
+
+ bool isRectangle( const ::tools::PolyPolygon& rPolyPoly )
+ {
+ // exclude some cheap cases first
+ if( rPolyPoly.Count() != 1 )
+ return false;
+
+ const ::tools::Polygon& rPoly( rPolyPoly[0] );
+
+ sal_uInt16 nCount( rPoly.GetSize() );
+ if( nCount < 4 )
+ return false;
+
+ // delegate to basegfx
+ return ::basegfx::utils::isRectangle( rPoly.getB2DPolygon() );
+ }
+
+
+ // VCL-Canvas related
+
+
+ ::Point mapRealPoint2D( const geometry::RealPoint2D& rPoint,
+ const rendering::ViewState& rViewState,
+ const rendering::RenderState& rRenderState )
+ {
+ ::basegfx::B2DPoint aPoint( ::basegfx::unotools::b2DPointFromRealPoint2D(rPoint) );
+
+ ::basegfx::B2DHomMatrix aMatrix;
+ aPoint *= ::canvas::tools::mergeViewAndRenderTransform(aMatrix,
+ rViewState,
+ rRenderState);
+
+ return vcl::unotools::pointFromB2DPoint( aPoint );
+ }
+
+ ::tools::PolyPolygon mapPolyPolygon( const ::basegfx::B2DPolyPolygon& rPoly,
+ const rendering::ViewState& rViewState,
+ const rendering::RenderState& rRenderState )
+ {
+ ::basegfx::B2DHomMatrix aMatrix;
+ ::canvas::tools::mergeViewAndRenderTransform(aMatrix,
+ rViewState,
+ rRenderState);
+
+ ::basegfx::B2DPolyPolygon aTemp( rPoly );
+
+ aTemp.transform( aMatrix );
+
+ return ::tools::PolyPolygon( aTemp );
+ }
+
+ ::BitmapEx transformBitmap( const BitmapEx& rBitmap,
+ const ::basegfx::B2DHomMatrix& rTransform )
+ {
+ SAL_INFO( "canvas.vcl", "::vclcanvas::tools::transformBitmap()" );
+ SAL_INFO( "canvas.vcl", "::vclcanvas::tools::transformBitmap: 0x" << std::hex << &rBitmap );
+
+ // calc transformation and size of bitmap to be
+ // generated. Note, that the translational components are
+ // deleted from the transformation; this can be handled by
+ // an offset when painting the bitmap
+ const Size aBmpSize( rBitmap.GetSizePixel() );
+ ::basegfx::B2DRectangle aDestRect;
+
+ // calc effective transformation for bitmap
+ const ::basegfx::B2DRectangle aSrcRect( 0, 0,
+ aBmpSize.Width(),
+ aBmpSize.Height() );
+ ::canvas::tools::calcTransformedRectBounds( aDestRect,
+ aSrcRect,
+ rTransform );
+
+ // re-center bitmap, such that it's left, top border is
+ // aligned with (0,0). The method takes the given
+ // rectangle, and calculates a transformation that maps
+ // this rectangle unscaled to the origin.
+ ::basegfx::B2DHomMatrix aLocalTransform;
+ ::canvas::tools::calcRectToOriginTransform( aLocalTransform,
+ aSrcRect,
+ rTransform );
+
+ return vcl::bitmap::CanvasTransformBitmap(rBitmap, rTransform, aDestRect, aLocalTransform);
+ }
+
+ void SetDefaultDeviceAntiAliasing( OutputDevice* pDevice )
+ {
+#if defined( MACOSX )
+ // use AA on VCLCanvas for Mac
+ pDevice->SetAntialiasing( AntialiasingFlags::Enable | pDevice->GetAntialiasing() );
+#else
+ // switch off AA for WIN32 and UNIX, the VCLCanvas does not look good with it and
+ // is not required to do AA. It would need to be adapted to use it correctly
+ // (especially gradient painting). This will need extra work.
+ if( SkiaHelper::isVCLSkiaEnabled()) // But Skia handles AA fine.
+ pDevice->SetAntialiasing( AntialiasingFlags::Enable | pDevice->GetAntialiasing() );
+ else
+ pDevice->SetAntialiasing(pDevice->GetAntialiasing() & ~AntialiasingFlags::Enable);
+#endif
+ }
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/vcl/impltools.hxx b/canvas/source/vcl/impltools.hxx
new file mode 100644
index 000000000..f8a9db075
--- /dev/null
+++ b/canvas/source/vcl/impltools.hxx
@@ -0,0 +1,178 @@
+/* -*- 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 <osl/mutex.hxx>
+
+#include <vcl/svapp.hxx>
+#include <vcl/outdev.hxx>
+
+#include <basegfx/polygon/b2dpolypolygon.hxx>
+
+#include <com/sun/star/uno/Reference.hxx>
+
+#include "outdevprovider.hxx"
+
+
+class OutputDevice;
+class Point;
+class Size;
+
+namespace basegfx
+{
+ namespace matrix
+ {
+ class B2DHomMatrix;
+ }
+}
+
+namespace com::sun::star::awt
+{
+ struct Point;
+ struct Size;
+ struct Rectangle;
+}
+
+namespace com::sun::star::drawing
+{
+ struct HomogenMatrix3;
+}
+
+namespace com::sun::star::geometry
+{
+ struct RealPoint2D;
+ struct RealSize2D;
+ struct RealRectangle2D;
+}
+
+namespace com::sun::star::rendering
+{
+ struct RenderState;
+ struct ViewState;
+ class XBitmap;
+}
+
+
+namespace vclcanvas
+{
+ namespace tools
+ {
+ ::BitmapEx
+ bitmapExFromXBitmap( const css::uno::Reference<
+ css::rendering::XBitmap >& );
+
+ /** Setup VCL font and output position
+
+ @returns false, if no text output should happen
+ */
+ bool setupFontTransform( ::Point& o_rPoint,
+ vcl::Font& io_rVCLFont,
+ const css::rendering::ViewState& viewState,
+ const css::rendering::RenderState& renderState,
+ ::OutputDevice const & rOutDev );
+
+ /** Predicate, to determine whether polygon is actually an axis-aligned rectangle
+
+ @return true, if the polygon is a rectangle.
+ */
+ bool isRectangle( const ::tools::PolyPolygon& rPolyPoly );
+
+
+ // Little helper to encapsulate locking into policy class
+ class LocalGuard
+ {
+ public:
+ LocalGuard() :
+ aSolarGuard()
+ {
+ }
+
+ /// To be compatible with CanvasBase mutex concept
+ explicit LocalGuard( const ::osl::Mutex& ) :
+ aSolarGuard()
+ {
+ }
+
+ private:
+ SolarMutexGuard aSolarGuard;
+ };
+
+ class OutDevStateKeeper
+ {
+ public:
+ explicit OutDevStateKeeper( OutputDevice& rOutDev ) :
+ mpOutDev( &rOutDev ),
+ mbMappingWasEnabled( mpOutDev->IsMapModeEnabled() ),
+ mnAntiAliasing( mpOutDev->GetAntialiasing() )
+ {
+ init();
+ }
+
+ explicit OutDevStateKeeper( const OutDevProviderSharedPtr& rOutDev ) :
+ mpOutDev( rOutDev ? &(rOutDev->getOutDev()) : nullptr ),
+ mbMappingWasEnabled( mpOutDev && mpOutDev->IsMapModeEnabled() ),
+ mnAntiAliasing( mpOutDev ? mpOutDev->GetAntialiasing() : AntialiasingFlags::NONE )
+ {
+ init();
+ }
+
+ ~OutDevStateKeeper()
+ {
+ if( mpOutDev )
+ {
+ mpOutDev->EnableMapMode( mbMappingWasEnabled );
+ mpOutDev->SetAntialiasing( mnAntiAliasing );
+
+ mpOutDev->Pop();
+ }
+ }
+
+ private:
+ void init()
+ {
+ if( mpOutDev )
+ {
+ mpOutDev->Push();
+ mpOutDev->EnableMapMode(false);
+ mpOutDev->SetAntialiasing( AntialiasingFlags::Enable );
+ }
+ }
+
+ VclPtr<OutputDevice> mpOutDev;
+ const bool mbMappingWasEnabled;
+ const AntialiasingFlags mnAntiAliasing;
+ };
+
+ ::Point mapRealPoint2D( const css::geometry::RealPoint2D& rPoint,
+ const css::rendering::ViewState& rViewState,
+ const css::rendering::RenderState& rRenderState );
+
+ ::tools::PolyPolygon mapPolyPolygon( const ::basegfx::B2DPolyPolygon& rPoly,
+ const css::rendering::ViewState& rViewState,
+ const css::rendering::RenderState& rRenderState );
+
+ ::BitmapEx transformBitmap( const BitmapEx& rBitmap,
+ const ::basegfx::B2DHomMatrix& rTransform );
+
+ void SetDefaultDeviceAntiAliasing( OutputDevice* pDevice );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/vcl/outdevholder.hxx b/canvas/source/vcl/outdevholder.hxx
new file mode 100644
index 000000000..9679687c4
--- /dev/null
+++ b/canvas/source/vcl/outdevholder.hxx
@@ -0,0 +1,50 @@
+/* -*- 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 <vcl/outdev.hxx>
+
+#include "outdevprovider.hxx"
+
+namespace vclcanvas
+{
+class OutDevHolder : public OutDevProvider
+{
+public:
+ OutDevHolder(const OutDevHolder&) = delete;
+ const OutDevHolder& operator=(const OutDevHolder&) = delete;
+
+ explicit OutDevHolder(OutputDevice& rOutDev)
+ : mrOutDev(rOutDev)
+ {
+ }
+
+private:
+ virtual OutputDevice& getOutDev() override { return mrOutDev; }
+ virtual const OutputDevice& getOutDev() const override { return mrOutDev; }
+
+ // TODO(Q2): Lifetime issue. This _only_ works reliably,
+ // if disposing the Canvas correctly disposes all
+ // entities which hold this pointer.
+ OutputDevice& mrOutDev;
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/vcl/outdevprovider.hxx b/canvas/source/vcl/outdevprovider.hxx
new file mode 100644
index 000000000..668212b9d
--- /dev/null
+++ b/canvas/source/vcl/outdevprovider.hxx
@@ -0,0 +1,50 @@
+/* -*- 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>
+
+class OutputDevice;
+
+namespace vclcanvas
+{
+ /* Definition of OutDevProvider interface */
+
+ /** Implementers of this interface provide the CanvasHelper
+ with its OutputDevice.
+
+ This additional level of indirection was necessary, as the
+ OutputDevice is not an interface. There had to be a mechanism
+ to detect the moment when an OutputDevice is rendered to
+ (e.g. for the BitmapBackBuffer).
+ */
+ class OutDevProvider
+ {
+ public:
+ virtual ~OutDevProvider() {}
+
+ virtual OutputDevice& getOutDev() = 0;
+ virtual const OutputDevice& getOutDev() const = 0;
+ };
+
+ typedef std::shared_ptr< OutDevProvider > OutDevProviderSharedPtr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/vcl/repainttarget.hxx b/canvas/source/vcl/repainttarget.hxx
new file mode 100644
index 000000000..b7b4058e9
--- /dev/null
+++ b/canvas/source/vcl/repainttarget.hxx
@@ -0,0 +1,52 @@
+/* -*- 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 "cachedbitmap.hxx"
+
+class Point;
+class Size;
+class GraphicAttr;
+
+namespace vclcanvas
+{
+ /* Definition of RepaintTarget interface */
+
+ /** Target interface for XCachedPrimitive implementations
+
+ This interface must be implemented on all canvas
+ implementations that hand out XCachedPrimitives
+ */
+ class RepaintTarget
+ {
+ public:
+ virtual ~RepaintTarget() {}
+
+ // call this when a bitmap is repainted
+ virtual bool repaint( const GraphicObjectSharedPtr& rGrf,
+ const css::rendering::ViewState& viewState,
+ const css::rendering::RenderState& renderState,
+ const ::Point& rPt,
+ const ::Size& rSz,
+ const GraphicAttr& rAttr ) const = 0;
+ };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/vcl/sprite.hxx b/canvas/source/vcl/sprite.hxx
new file mode 100644
index 000000000..30ab15aaf
--- /dev/null
+++ b/canvas/source/vcl/sprite.hxx
@@ -0,0 +1,65 @@
+/* -*- 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>
+
+class OutputDevice;
+
+namespace vclcanvas
+{
+ /** Specialization of ::canvas::Sprite interface, to also provide
+ redraw methods.
+ */
+ class Sprite : public ::canvas::Sprite
+ {
+ public:
+
+ /** Redraw sprite at the stored position.
+
+ @param bBufferedUpdate
+ When true, the redraw does <em>not</em> happen directly on
+ the front buffer, but within a VDev. Used to speed up
+ drawing.
+ */
+ virtual void redraw( OutputDevice& rOutDev,
+ bool bBufferedUpdate ) const = 0;
+
+ /** Redraw sprite at the given position.
+
+ @param rPos
+ Output position of the sprite. Overrides the sprite's own
+ output position.
+
+ @param bBufferedUpdate
+ When true, the redraw does <em>not</em> happen directly on
+ the front buffer, but within a VDev. Used to speed up
+ drawing.
+ */
+ virtual void redraw( OutputDevice& rOutDev,
+ const ::basegfx::B2DPoint& rPos,
+ bool bBufferedUpdate ) const = 0;
+
+ protected:
+ ~Sprite() {}
+ };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/vcl/spritecanvas.cxx b/canvas/source/vcl/spritecanvas.cxx
new file mode 100644
index 000000000..60c65fbd7
--- /dev/null
+++ b/canvas/source/vcl/spritecanvas.cxx
@@ -0,0 +1,187 @@
+/* -*- 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 <com/sun/star/awt/XTopWindow.hpp>
+#include <com/sun/star/lang/NoSupportException.hpp>
+#include <cppuhelper/supportsservice.hxx>
+#include <tools/diagnose_ex.h>
+
+#include "spritecanvas.hxx"
+#include "outdevholder.hxx"
+#include "windowoutdevholder.hxx"
+
+
+using namespace ::com::sun::star;
+
+namespace vclcanvas
+{
+ SpriteCanvas::SpriteCanvas( const uno::Sequence< uno::Any >& aArguments,
+ const uno::Reference< uno::XComponentContext >& /*rxContext*/ ) :
+ maArguments(aArguments)
+ {
+ }
+
+ void SpriteCanvas::initialize()
+ {
+ SolarMutexGuard aGuard;
+
+ // #i64742# Only call initialize when not in probe mode
+ if( !maArguments.hasElements() )
+ return;
+
+ SAL_INFO("canvas.vcl", "SpriteCanvas created" );
+
+ // add our own property to GraphicDevice
+ maPropHelper.addProperties(
+ ::canvas::PropertySetHelper::MakeMap
+ ("UnsafeScrolling",
+ [this]() { return this->maCanvasHelper.isUnsafeScrolling(); },
+ [this](css::uno::Any const& aAny) mutable { this->maCanvasHelper.enableUnsafeScrolling(aAny); } )
+ ("SpriteBounds",
+ [this]() { return this->maCanvasHelper.isSpriteBounds(); },
+ [this](css::uno::Any const& aAny) mutable { this->maCanvasHelper.enableSpriteBounds(aAny); } ));
+
+ SAL_INFO("canvas.vcl", "VCLSpriteCanvas::initialize called" );
+
+ ENSURE_ARG_OR_THROW( maArguments.hasElements(),
+ "VCLSpriteCanvas::initialize: wrong number of arguments" );
+
+ /* maArguments:
+ 0: ptr to creating instance (Window or VirtualDevice)
+ 1: current bounds of creating instance
+ 2: bool, denoting always on top state for Window (always false for VirtualDevice)
+ 3: XWindow for creating Window (or empty for VirtualDevice)
+ 4: SystemGraphicsData as a streamed Any
+ */
+ ENSURE_ARG_OR_THROW( maArguments.getLength() >= 4 &&
+ maArguments[0].getValueTypeClass() == uno::TypeClass_HYPER &&
+ maArguments[3].getValueTypeClass() == uno::TypeClass_INTERFACE,
+ "VCLSpriteCanvas::initialize: wrong number of arguments, or wrong types" );
+
+ sal_Int64 nPtr = 0;
+ maArguments[0] >>= nPtr;
+
+ OutputDevice* pOutDev = reinterpret_cast<OutputDevice*>(nPtr);
+ if( !pOutDev )
+ throw lang::NoSupportException("Passed OutDev invalid!", nullptr);
+
+ uno::Reference< awt::XWindow > xParentWindow;
+ maArguments[3] >>= xParentWindow;
+
+ OutDevProviderSharedPtr pOutDevProvider;
+ if( xParentWindow.is())
+ pOutDevProvider = std::make_shared<WindowOutDevHolder>(xParentWindow);
+ else
+ pOutDevProvider = std::make_shared<OutDevHolder>(*pOutDev);
+
+ // setup helper
+ maDeviceHelper.init( pOutDevProvider );
+ setWindow( xParentWindow.is()
+ ? uno::Reference<awt::XWindow2>(xParentWindow, uno::UNO_QUERY_THROW)
+ : uno::Reference<awt::XWindow2>());
+ maCanvasHelper.init( maDeviceHelper.getBackBuffer(),
+ *this,
+ maRedrawManager,
+ false, // no OutDev state preservation
+ false ); // no alpha on surface
+
+ maArguments.realloc(0);
+ }
+
+ SpriteCanvas::~SpriteCanvas()
+ {
+ SAL_INFO("canvas.vcl", "SpriteCanvas destroyed" );
+ }
+
+
+ void SpriteCanvas::disposeThis()
+ {
+ SolarMutexGuard aGuard;
+
+ // forward to parent
+ SpriteCanvasBaseT::disposeThis();
+ }
+
+ sal_Bool SAL_CALL SpriteCanvas::showBuffer( sal_Bool bUpdateAll )
+ {
+ return updateScreen( bUpdateAll );
+ }
+
+ sal_Bool SAL_CALL SpriteCanvas::switchBuffer( sal_Bool bUpdateAll )
+ {
+ return updateScreen( bUpdateAll );
+ }
+
+ sal_Bool SAL_CALL SpriteCanvas::updateScreen( sal_Bool bUpdateAll )
+ {
+ SolarMutexGuard aGuard;
+
+ // 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(bUpdateAll,
+ mbSurfaceDirty);
+ }
+
+ OUString SAL_CALL SpriteCanvas::getServiceName( )
+ {
+ return "com.sun.star.rendering.SpriteCanvas.VCL";
+ }
+
+ // XServiceInfo
+ css::uno::Sequence<OUString> SpriteCanvas::getSupportedServiceNames()
+ {
+ return { SpriteCanvas::getServiceName() };
+ }
+ OUString SpriteCanvas::getImplementationName()
+ {
+ return "com.sun.star.comp.rendering.SpriteCanvas.VCL";
+ }
+ sal_Bool SpriteCanvas::supportsService(const OUString& sServiceName)
+ {
+ return cppu::supportsService(this, sServiceName);
+ }
+
+ bool SpriteCanvas::repaint( const GraphicObjectSharedPtr& rGrf,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState,
+ const ::Point& rPt,
+ const ::Size& rSz,
+ const GraphicAttr& rAttr ) const
+ {
+ SolarMutexGuard aGuard;
+
+ return maCanvasHelper.repaint( rGrf, viewState, renderState, rPt, rSz, rAttr );
+ }
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_rendering_SpriteCanvas_VCL_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& args)
+{
+ rtl::Reference<vclcanvas::SpriteCanvas> p = new vclcanvas::SpriteCanvas(args, context);
+ cppu::acquire(p.get());
+ p->initialize();
+ return static_cast<cppu::OWeakObject*>(p.get());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/vcl/spritecanvas.hxx b/canvas/source/vcl/spritecanvas.hxx
new file mode 100644
index 000000000..8f7c76880
--- /dev/null
+++ b/canvas/source/vcl/spritecanvas.hxx
@@ -0,0 +1,163 @@
+/* -*- 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/XServiceInfo.hpp>
+#include <com/sun/star/lang/XServiceName.hpp>
+#include <com/sun/star/awt/XWindowListener.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 "spritecanvashelper.hxx"
+#include "impltools.hxx"
+#include "spritedevicehelper.hxx"
+#include "repainttarget.hxx"
+
+
+namespace vclcanvas
+{
+ 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,
+ tools::LocalGuard,
+ ::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,
+ tools::LocalGuard,
+ ::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 RepaintTarget
+ {
+ public:
+ SpriteCanvas( const css::uno::Sequence<
+ css::uno::Any >& aArguments,
+ const css::uno::Reference<
+ css::uno::XComponentContext >& rxContext );
+
+ void initialize();
+
+ /// For resource tracking
+ virtual ~SpriteCanvas() override;
+
+ /// 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;
+
+ // RepaintTarget
+ virtual bool repaint( const GraphicObjectSharedPtr& rGrf,
+ const css::rendering::ViewState& viewState,
+ const css::rendering::RenderState& renderState,
+ const ::Point& rPt,
+ const ::Size& rSz,
+ const GraphicAttr& rAttr ) const override;
+
+ /// Get backbuffer for this canvas
+ OutDevProviderSharedPtr const & getFrontBuffer() const { return maDeviceHelper.getOutDev(); }
+ /// Get window for this canvas
+ BackBufferSharedPtr const & getBackBuffer() const { return maDeviceHelper.getBackBuffer(); }
+
+ private:
+ css::uno::Sequence< css::uno::Any > maArguments;
+ };
+
+ typedef ::rtl::Reference< SpriteCanvas > SpriteCanvasRef;
+ typedef ::rtl::Reference< SpriteCanvas > DeviceRef;
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/vcl/spritecanvashelper.cxx b/canvas/source/vcl/spritecanvashelper.cxx
new file mode 100644
index 000000000..2eb622431
--- /dev/null
+++ b/canvas/source/vcl/spritecanvashelper.cxx
@@ -0,0 +1,661 @@
+/* -*- 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 <boost/cast.hpp>
+
+#include <basegfx/range/b2drectangle.hxx>
+#include <rtl/math.hxx>
+#include <tools/diagnose_ex.h>
+#include <vcl/canvastools.hxx>
+#include <vcl/outdev.hxx>
+#include <vcl/window.hxx>
+
+#include <canvas/canvastools.hxx>
+
+#include "canvascustomsprite.hxx"
+#include "spritecanvashelper.hxx"
+#include "spritecanvas.hxx"
+
+using namespace ::com::sun::star;
+
+#define FPS_BOUNDS ::tools::Rectangle(0,0,130,90)
+#define INFO_COLOR COL_RED
+
+namespace vclcanvas
+{
+ namespace
+ {
+ /** Sprite redraw at original position
+
+ Used to repaint the whole canvas (background and all
+ sprites)
+ */
+ void spriteRedraw( OutputDevice& rOutDev,
+ const ::canvas::Sprite::Reference& rSprite )
+ {
+ // downcast to derived vclcanvas::Sprite interface, which
+ // provides the actual redraw methods.
+ ::boost::polymorphic_downcast< Sprite* >(rSprite.get())->redraw(rOutDev,
+ true);
+ }
+
+ double calcNumPixel( const ::canvas::Sprite::Reference& rSprite )
+ {
+ const ::basegfx::B2DSize& rSize(
+ ::boost::polymorphic_downcast< Sprite* >(rSprite.get())->getSizePixel() );
+
+ return rSize.getX() * rSize.getY();
+ }
+
+ void repaintBackground( OutputDevice& rOutDev,
+ OutputDevice const & rBackBuffer,
+ const ::basegfx::B2DRange& rArea )
+ {
+ const ::Point& rPos( vcl::unotools::pointFromB2DPoint( rArea.getMinimum()) );
+ const ::Size& rSize( vcl::unotools::sizeFromB2DSize( rArea.getRange()) );
+
+ rOutDev.DrawOutDev( rPos, rSize, rPos, rSize, rBackBuffer );
+ }
+
+ void opaqueUpdateSpriteArea( const ::canvas::Sprite::Reference& rSprite,
+ OutputDevice& rOutDev,
+ const ::basegfx::B2IRange& rArea )
+ {
+ const ::tools::Rectangle& rRequestedArea(
+ vcl::unotools::rectangleFromB2IRectangle( rArea ) );
+
+ // clip output to actual update region (otherwise a)
+ // wouldn't save much render time, and b) will clutter
+ // scrolled sprite content outside this area)
+ rOutDev.EnableMapMode( false );
+ rOutDev.SetAntialiasing( AntialiasingFlags::Enable );
+ rOutDev.SetClipRegion(vcl::Region(rRequestedArea));
+
+ // repaint affected sprite directly to output device (at
+ // the actual screen output position)
+ ::boost::polymorphic_downcast< Sprite* >(
+ rSprite.get() )->redraw( rOutDev,
+ false ); // rendering
+ // directly to
+ // frontbuffer
+ }
+
+ void renderInfoText( OutputDevice& rOutDev,
+ const OUString& rStr,
+ const Point& rPos )
+ {
+ vcl::Font aVCLFont;
+ aVCLFont.SetFontHeight( 20 );
+ aVCLFont.SetColor( INFO_COLOR );
+
+ rOutDev.SetTextAlign(ALIGN_TOP);
+ rOutDev.SetTextColor( INFO_COLOR );
+ rOutDev.SetFont( aVCLFont );
+
+ rOutDev.DrawText( rPos, rStr );
+ }
+
+ }
+
+ SpriteCanvasHelper::SpriteCanvasHelper() :
+ mpRedrawManager( nullptr ),
+ mpOwningSpriteCanvas( nullptr ),
+ maVDev(VclPtr<VirtualDevice>::Create()),
+ mbShowFrameInfo( false ),
+ mbShowSpriteBounds( false ),
+ mbIsUnsafeScrolling( false )
+ {
+#if OSL_DEBUG_LEVEL > 0
+ // inverse defaults for verbose debug mode
+ mbShowFrameInfo = true;
+ // this looks like drawing errors, enable only if explicitly asked for
+ static bool enableShowSpriteBounds = getenv("CANVAS_SPRITE_BOUNDS") != nullptr;
+ mbShowSpriteBounds = enableShowSpriteBounds;
+#endif
+ }
+
+ SpriteCanvasHelper::~SpriteCanvasHelper()
+ {
+ SolarMutexGuard aGuard;
+ maVDev.disposeAndClear();
+ }
+
+ void SpriteCanvasHelper::init( const OutDevProviderSharedPtr& rOutDev,
+ SpriteCanvas& rOwningSpriteCanvas,
+ ::canvas::SpriteRedrawManager& rManager,
+ bool bProtect,
+ bool bHaveAlpha )
+ {
+ mpOwningSpriteCanvas = &rOwningSpriteCanvas;
+ mpRedrawManager = &rManager;
+
+ CanvasHelper::init(rOwningSpriteCanvas,rOutDev,bProtect,bHaveAlpha);
+ }
+
+ void SpriteCanvasHelper::disposing()
+ {
+ mpRedrawManager = nullptr;
+ mpOwningSpriteCanvas = nullptr;
+
+ // forward to base
+ CanvasHelper::disposing();
+ }
+
+ uno::Reference< rendering::XAnimatedSprite > SpriteCanvasHelper::createSpriteFromAnimation(
+ const uno::Reference< rendering::XAnimation >& )
+ {
+ return uno::Reference< rendering::XAnimatedSprite >();
+ }
+
+ uno::Reference< rendering::XAnimatedSprite > SpriteCanvasHelper::createSpriteFromBitmaps(
+ const uno::Sequence< uno::Reference< rendering::XBitmap > >& ,
+ sal_Int8 )
+ {
+ return uno::Reference< rendering::XAnimatedSprite >();
+ }
+
+ uno::Reference< rendering::XCustomSprite > SpriteCanvasHelper::createCustomSprite( const geometry::RealSize2D& spriteSize )
+ {
+ if( !mpRedrawManager || !mpDevice )
+ return uno::Reference< rendering::XCustomSprite >(); // we're disposed
+
+ return uno::Reference< rendering::XCustomSprite >(
+ new CanvasCustomSprite( spriteSize,
+ *mpDevice,
+ mpOwningSpriteCanvas,
+ mpOwningSpriteCanvas->getFrontBuffer(),
+ mbShowSpriteBounds ) );
+ }
+
+ uno::Reference< rendering::XSprite > SpriteCanvasHelper::createClonedSprite( const uno::Reference< rendering::XSprite >& )
+ {
+ return uno::Reference< rendering::XSprite >();
+ }
+
+ bool SpriteCanvasHelper::updateScreen( bool bUpdateAll,
+ bool& io_bSurfaceDirty )
+ {
+ if( !mpRedrawManager ||
+ !mpOwningSpriteCanvas ||
+ !mpOwningSpriteCanvas->getFrontBuffer() ||
+ !mpOwningSpriteCanvas->getBackBuffer() )
+ {
+ return false; // disposed, or otherwise dysfunctional
+ }
+
+ // commit to backbuffer
+ flush();
+
+ OutputDevice& rOutDev( mpOwningSpriteCanvas->getFrontBuffer()->getOutDev() );
+ BackBufferSharedPtr pBackBuffer( mpOwningSpriteCanvas->getBackBuffer() );
+ OutputDevice& rBackOutDev( pBackBuffer->getOutDev() );
+
+ // actual OutputDevice is a shared resource - restore its
+ // state when done.
+ tools::OutDevStateKeeper aStateKeeper( rOutDev );
+
+ const Size aOutDevSize( rBackOutDev.GetOutputSizePixel() );
+ const Point aEmptyPoint(0,0);
+
+ vcl::Window* pTargetWindow = nullptr;
+ if( rOutDev.GetOutDevType() == OUTDEV_WINDOW )
+ {
+ pTargetWindow = rOutDev.GetOwnerWindow(); // TODO(Q3): Evil downcast.
+
+ // we're double-buffered, thus no need for paint area-limiting
+ // clips. besides that, will interfere with animations (as for
+ // Window-invalidate repaints, only parts of the window will
+ // be redrawn otherwise)
+ const vcl::Region aFullWindowRegion( ::tools::Rectangle(aEmptyPoint,
+ aOutDevSize) );
+ pTargetWindow->ExpandPaintClipRegion(aFullWindowRegion);
+ }
+
+ // TODO(P1): Might be worthwhile to track areas of background
+ // changes, too.
+ if( !bUpdateAll && !io_bSurfaceDirty )
+ {
+ if( mbShowFrameInfo )
+ {
+ // also repaint background below frame counter (fake
+ // that as a sprite vanishing in this area)
+ mpRedrawManager->updateSprite( ::canvas::Sprite::Reference(),
+ ::basegfx::B2DPoint(),
+ ::basegfx::B2DRectangle( 0.0, 0.0,
+ FPS_BOUNDS.Right(),
+ FPS_BOUNDS.Bottom() ) );
+ }
+
+ // 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 );
+ }
+ else
+ {
+ // background has changed, so we currently have no choice
+ // but repaint everything (or caller requested that)
+
+ maVDev->SetOutputSizePixel( aOutDevSize );
+ maVDev->EnableMapMode( false );
+ maVDev->DrawOutDev( aEmptyPoint, aOutDevSize,
+ aEmptyPoint, aOutDevSize,
+ rBackOutDev );
+
+ // repaint all active sprites on top of background into
+ // VDev.
+ OutputDevice& rTmpOutDev( *maVDev );
+ mpRedrawManager->forEachSprite(
+ [&rTmpOutDev]( const ::canvas::Sprite::Reference& rSprite )
+ { spriteRedraw( rTmpOutDev, rSprite ); }
+ );
+
+ // flush to screen
+ rOutDev.EnableMapMode( false );
+ rOutDev.SetAntialiasing( AntialiasingFlags::Enable );
+ rOutDev.SetClipRegion();
+ rOutDev.DrawOutDev( aEmptyPoint, aOutDevSize,
+ aEmptyPoint, aOutDevSize,
+ *maVDev );
+ }
+
+ // change record vector must be cleared, for the next turn of
+ // rendering and sprite changing
+ mpRedrawManager->clearChangeRecords();
+
+ io_bSurfaceDirty = false;
+
+ if( mbShowFrameInfo )
+ {
+ renderFrameCounter( rOutDev );
+ renderSpriteCount( rOutDev );
+ renderMemUsage( rOutDev );
+ }
+
+#if OSL_DEBUG_LEVEL > 0
+ static ::canvas::tools::ElapsedTime aElapsedTime;
+
+ // log time immediately after surface flip
+ SAL_INFO("canvas.vcl", "SpriteCanvasHelper::updateScreen(): flip done at " <<
+ aElapsedTime.getElapsedTime() );
+#endif
+
+ // sync output with screen, to ensure that we don't queue up
+ // render requests (calling code might rely on timing,
+ // i.e. assume that things are visible on screen after
+ // updateScreen() returns).
+ if( pTargetWindow )
+ {
+ // commit to screen
+ pTargetWindow->GetOutDev()->Flush();
+ }
+
+ return true;
+ }
+
+ void SpriteCanvasHelper::backgroundPaint( const ::basegfx::B2DRange& rUpdateRect )
+ {
+ ENSURE_OR_THROW( mpOwningSpriteCanvas &&
+ mpOwningSpriteCanvas->getBackBuffer() &&
+ mpOwningSpriteCanvas->getFrontBuffer(),
+ "SpriteCanvasHelper::backgroundPaint(): NULL device pointer " );
+
+ OutputDevice& rOutDev( mpOwningSpriteCanvas->getFrontBuffer()->getOutDev() );
+ BackBufferSharedPtr pBackBuffer( mpOwningSpriteCanvas->getBackBuffer() );
+ OutputDevice& rBackOutDev( pBackBuffer->getOutDev() );
+
+ repaintBackground( rOutDev, rBackOutDev, rUpdateRect );
+ }
+
+ void SpriteCanvasHelper::scrollUpdate( const ::basegfx::B2DRange& rMoveStart,
+ const ::basegfx::B2DRange& rMoveEnd,
+ const ::canvas::SpriteRedrawManager::UpdateArea& rUpdateArea )
+ {
+ ENSURE_OR_THROW( mpOwningSpriteCanvas &&
+ mpOwningSpriteCanvas->getBackBuffer() &&
+ mpOwningSpriteCanvas->getFrontBuffer(),
+ "SpriteCanvasHelper::scrollUpdate(): NULL device pointer " );
+
+ OutputDevice& rOutDev( mpOwningSpriteCanvas->getFrontBuffer()->getOutDev() );
+ BackBufferSharedPtr pBackBuffer( mpOwningSpriteCanvas->getBackBuffer() );
+ OutputDevice& rBackOutDev( pBackBuffer->getOutDev() );
+
+ const Size& rTargetSizePixel( rOutDev.GetOutputSizePixel() );
+ const ::basegfx::B2IRange aOutputBounds( 0,0,
+ rTargetSizePixel.Width(),
+ rTargetSizePixel.Height() );
+
+ // 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.
+ ::basegfx::B2IRange aSourceRect(
+ ::canvas::tools::spritePixelAreaFromB2DRange( rMoveStart ) );
+ const ::basegfx::B2IRange& rDestRect(
+ ::canvas::tools::spritePixelAreaFromB2DRange( rMoveEnd ) );
+ ::basegfx::B2IPoint aDestPos( rDestRect.getMinimum() );
+
+ std::vector< ::basegfx::B2IRange > aUnscrollableAreas;
+
+ // Since strictly speaking, this scroll algorithm is plain
+ // buggy, the scrolled area might actually lie _below_ another
+ // window - we've made this feature configurable via
+ // mbIsUnsafeScrolling.
+
+ // clip to output bounds (cannot properly scroll stuff
+ // _outside_ our screen area)
+ if( !mbIsUnsafeScrolling ||
+ !::canvas::tools::clipScrollArea( aSourceRect,
+ aDestPos,
+ aUnscrollableAreas,
+ aOutputBounds ) )
+ {
+ // fully clipped scroll area: cannot simply scroll
+ // then. Perform normal opaque update (can use that, since
+ // one of the preconditions for scrollable update is
+ // opaque sprite content)
+
+ // repaint all affected sprites directly to output device
+ for( const auto& rComponent : rUpdateArea.maComponentList )
+ {
+ const ::canvas::Sprite::Reference& rSprite( rComponent.second.getSprite() );
+
+ if( rSprite.is() )
+ ::boost::polymorphic_downcast< Sprite* >(
+ rSprite.get() )->redraw( rOutDev,
+ false );
+ }
+ }
+ else
+ {
+ // scroll rOutDev content
+ rOutDev.CopyArea( vcl::unotools::pointFromB2IPoint( aDestPos ),
+ vcl::unotools::pointFromB2IPoint( aSourceRect.getMinimum() ),
+ // TODO(Q2): use numeric_cast to check range
+ ::Size( static_cast<sal_Int32>(aSourceRect.getRange().getX()),
+ static_cast<sal_Int32>(aSourceRect.getRange().getY()) ) );
+
+ const ::canvas::SpriteRedrawManager::SpriteConnectedRanges::ComponentListType::const_iterator
+ aFirst( rUpdateArea.maComponentList.begin() );
+
+ ENSURE_OR_THROW( aFirst->second.getSprite().is(),
+ "VCLCanvas::scrollUpdate(): no sprite" );
+
+ // repaint uncovered areas from sprite. Need to actually
+ // clip here, since we're only repainting _parts_ of the
+ // sprite
+ rOutDev.Push( vcl::PushFlags::CLIPREGION );
+
+ for( const auto& rArea : aUnscrollableAreas )
+ opaqueUpdateSpriteArea( aFirst->second.getSprite(),
+ rOutDev, rArea );
+
+ rOutDev.Pop();
+ }
+
+ // 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& rArea : aUncoveredAreas )
+ repaintBackground( rOutDev, rBackOutDev, rArea );
+ }
+
+ void SpriteCanvasHelper::opaqueUpdate( SAL_UNUSED_PARAMETER const ::basegfx::B2DRange&,
+ const std::vector< ::canvas::Sprite::Reference >& rSortedUpdateSprites )
+ {
+ ENSURE_OR_THROW( mpOwningSpriteCanvas &&
+ mpOwningSpriteCanvas->getBackBuffer() &&
+ mpOwningSpriteCanvas->getFrontBuffer(),
+ "SpriteCanvasHelper::opaqueUpdate(): NULL device pointer " );
+
+ OutputDevice& rOutDev( mpOwningSpriteCanvas->getFrontBuffer()->getOutDev() );
+
+ // no need to clip output to actual update region - there will
+ // always be ALL sprites contained in the rectangular update
+ // area contained in rTotalArea (that's the way
+ // B2DConnectedRanges work). If rTotalArea appears to be
+ // smaller than the sprite - then this sprite carries a clip,
+ // and the update will be constrained to that rect.
+
+ // repaint all affected sprites directly to output device
+ for( const auto& rSprite : rSortedUpdateSprites )
+ {
+ if( rSprite.is() )
+ ::boost::polymorphic_downcast< Sprite* >(
+ rSprite.get() )->redraw( rOutDev,
+ false );
+ }
+ }
+
+ void SpriteCanvasHelper::genericUpdate( const ::basegfx::B2DRange& rRequestedArea,
+ const std::vector< ::canvas::Sprite::Reference >& rSortedUpdateSprites )
+ {
+ ENSURE_OR_THROW( mpOwningSpriteCanvas &&
+ mpOwningSpriteCanvas->getBackBuffer() &&
+ mpOwningSpriteCanvas->getFrontBuffer(),
+ "SpriteCanvasHelper::genericUpdate(): NULL device pointer " );
+
+ OutputDevice& rOutDev( mpOwningSpriteCanvas->getFrontBuffer()->getOutDev() );
+ BackBufferSharedPtr pBackBuffer( mpOwningSpriteCanvas->getBackBuffer() );
+ OutputDevice& rBackOutDev( pBackBuffer->getOutDev() );
+
+ // limit size of update VDev to target outdev's size
+ const Size& rTargetSizePixel( rOutDev.GetOutputSizePixel() );
+
+ // round output position towards zero. Don't want to truncate
+ // a fraction of a sprite pixel... Clip position at origin,
+ // otherwise, truncation of size below might leave visible
+ // areas uncovered by VDev.
+ const ::Point aOutputPosition(
+ std::max( sal_Int32( 0 ),
+ static_cast< sal_Int32 >(rRequestedArea.getMinX()) ),
+ std::max( sal_Int32( 0 ),
+ static_cast< sal_Int32 >(rRequestedArea.getMinY()) ) );
+ // round output size towards +infty. Don't want to truncate a
+ // fraction of a sprite pixel... Limit coverage of VDev to
+ // output device's area (i.e. not only to total size, but to
+ // cover _only_ the visible parts).
+ const ::Size aOutputSize(
+ std::max( sal_Int32( 0 ),
+ std::min( static_cast< sal_Int32 >(rTargetSizePixel.Width() - aOutputPosition.X()),
+ ::canvas::tools::roundUp( rRequestedArea.getMaxX() - aOutputPosition.X() ))),
+ std::max( sal_Int32( 0 ),
+ std::min( static_cast< sal_Int32 >(rTargetSizePixel.Height() - aOutputPosition.Y()),
+ ::canvas::tools::roundUp( rRequestedArea.getMaxY() - aOutputPosition.Y() ))));
+
+ // early exit for empty output area.
+ if( aOutputSize.Width() == 0 &&
+ aOutputSize.Height() == 0 )
+ {
+ return;
+ }
+
+ const Point aEmptyPoint(0,0);
+ const Size aCurrOutputSize( maVDev->GetOutputSizePixel() );
+
+ // adapt maVDev's size to the area that actually needs the
+ // repaint.
+ if( aCurrOutputSize.Width() < aOutputSize.Width() ||
+ aCurrOutputSize.Height() < aOutputSize.Height() )
+ {
+ // TODO(P1): Come up with a clever tactic to reduce maVDev
+ // from time to time. Reduction with threshold (say, if
+ // maVDev is more than twice too large) is not wise, as
+ // this might then toggle within the same updateScreen(),
+ // but for different disjunct sprite areas.
+ maVDev->SetOutputSizePixel( aOutputSize );
+ }
+
+ // paint background
+ maVDev->EnableMapMode( false );
+ maVDev->SetAntialiasing( AntialiasingFlags::Enable );
+ maVDev->SetClipRegion();
+ maVDev->DrawOutDev( aEmptyPoint, aOutputSize,
+ aOutputPosition, aOutputSize,
+ rBackOutDev );
+
+ // repaint all affected sprites on top of background into
+ // VDev.
+ for( const auto& rSprite : rSortedUpdateSprites )
+ {
+ if( rSprite.is() )
+ {
+ Sprite* pSprite = ::boost::polymorphic_downcast< Sprite* >( rSprite.get() );
+
+ // calc relative sprite position in rUpdateArea (which
+ // need not be the whole screen!)
+ const ::basegfx::B2DPoint& rSpriteScreenPos( pSprite->getPosPixel() );
+ const ::basegfx::B2DPoint& rSpriteRenderPos(
+ rSpriteScreenPos - vcl::unotools::b2DPointFromPoint(aOutputPosition)
+ );
+
+ pSprite->redraw( *maVDev, rSpriteRenderPos, true );
+ }
+ }
+
+ // flush to screen
+ rOutDev.EnableMapMode( false );
+ rOutDev.SetAntialiasing( AntialiasingFlags::Enable );
+ rOutDev.DrawOutDev( aOutputPosition, aOutputSize,
+ aEmptyPoint, aOutputSize,
+ *maVDev );
+ }
+
+ void SpriteCanvasHelper::renderFrameCounter( OutputDevice& rOutDev )
+ {
+ const double denominator( maLastUpdate.getElapsedTime() );
+ maLastUpdate.reset();
+
+ OUString text( ::rtl::math::doubleToUString( denominator == 0.0 ? 100.0 : 1.0/denominator,
+ rtl_math_StringFormat_F,
+ 2,'.',nullptr,' ') );
+
+ // pad with leading space
+ while( text.getLength() < 6 )
+ text = " " + text;
+
+ text += " fps";
+
+ renderInfoText( rOutDev,
+ text,
+ Point(0, 0) );
+ }
+
+ namespace
+ {
+ template< typename T > struct Adder
+ {
+ typedef void result_type;
+
+ Adder( T& rAdderTarget,
+ T nIncrement ) :
+ mpTarget( &rAdderTarget ),
+ mnIncrement( nIncrement )
+ {
+ }
+
+ void operator()( const ::canvas::Sprite::Reference& ) { *mpTarget += mnIncrement; }
+ void operator()( T nIncrement ) { *mpTarget += nIncrement; }
+
+ T* mpTarget;
+ T mnIncrement;
+ };
+
+ template< typename T> Adder<T> makeAdder( T& rAdderTarget,
+ T nIncrement )
+ {
+ return Adder<T>(rAdderTarget, nIncrement);
+ }
+ }
+
+ void SpriteCanvasHelper::renderSpriteCount( OutputDevice& rOutDev )
+ {
+ if( !mpRedrawManager )
+ return;
+
+ sal_Int32 nCount(0);
+
+ mpRedrawManager->forEachSprite( makeAdder(nCount,sal_Int32(1)) );
+ OUString text( OUString::number(nCount) );
+
+ // pad with leading space
+ while( text.getLength() < 3 )
+ text = " " + text;
+
+ text = "Sprites: " + text;
+
+ renderInfoText( rOutDev,
+ text,
+ Point(0, 30) );
+ }
+
+ void SpriteCanvasHelper::renderMemUsage( OutputDevice& rOutDev )
+ {
+ BackBufferSharedPtr pBackBuffer( mpOwningSpriteCanvas->getBackBuffer() );
+
+ if( !(mpRedrawManager &&
+ pBackBuffer) )
+ return;
+
+ double nPixel(0.0);
+
+ // accumulate pixel count for each sprite into fCount
+ mpRedrawManager->forEachSprite(
+ [&nPixel]( const ::canvas::Sprite::Reference& rSprite )
+ { makeAdder( nPixel, 1.0 )( calcNumPixel(rSprite) ); }
+ );
+
+ static const int NUM_VIRDEV(2);
+ static const int BYTES_PER_PIXEL(3);
+
+ const Size& rVDevSize( maVDev->GetOutputSizePixel() );
+ const Size& rBackBufferSize( pBackBuffer->getOutDev().GetOutputSizePixel() );
+
+ const double nMemUsage( nPixel * NUM_VIRDEV * BYTES_PER_PIXEL +
+ rVDevSize.Width()*rVDevSize.Height() * BYTES_PER_PIXEL +
+ rBackBufferSize.Width()*rBackBufferSize.Height() * BYTES_PER_PIXEL );
+
+ OUString text( ::rtl::math::doubleToUString( nMemUsage / 1048576.0,
+ rtl_math_StringFormat_F,
+ 2,'.',nullptr,' ') );
+
+ // pad with leading space
+ while( text.getLength() < 4 )
+ text = " " + text;
+
+ text = "Mem: " + text + "MB";
+
+ renderInfoText( rOutDev,
+ text,
+ Point(0, 60) );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/vcl/spritecanvashelper.hxx b/canvas/source/vcl/spritecanvashelper.hxx
new file mode 100644
index 000000000..af3f5526a
--- /dev/null
+++ b/canvas/source/vcl/spritecanvashelper.hxx
@@ -0,0 +1,169 @@
+/* -*- 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/XAnimatedSprite.hpp>
+#include <com/sun/star/rendering/XAnimation.hpp>
+#include <com/sun/star/rendering/XCustomSprite.hpp>
+
+#include <vcl/vclptr.hxx>
+#include <vcl/virdev.hxx>
+
+#include <spriteredrawmanager.hxx>
+#include <canvas/elapsedtime.hxx>
+#include "canvashelper.hxx"
+
+
+namespace vclcanvas
+{
+ class SpriteCanvas;
+
+ class SpriteCanvasHelper : public CanvasHelper
+ {
+ public:
+ SpriteCanvasHelper();
+ ~SpriteCanvasHelper();
+
+ void init( const OutDevProviderSharedPtr& rOutDev,
+ SpriteCanvas& rOwningSpriteCanvas,
+ ::canvas::SpriteRedrawManager& rManager,
+ bool bProtect,
+ bool bHaveAlpha );
+
+ /// 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 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( 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 );
+
+ css::uno::Any isUnsafeScrolling() const
+ {
+ return css::uno::Any(mbIsUnsafeScrolling);
+ }
+ void enableUnsafeScrolling( const css::uno::Any& rAny )
+ {
+ mbIsUnsafeScrolling = rAny.get<bool>();
+ }
+
+ css::uno::Any isSpriteBounds() const
+ {
+ return css::uno::Any(mbShowSpriteBounds);
+ }
+ void enableSpriteBounds( const css::uno::Any& rAny )
+ {
+ mbShowSpriteBounds = rAny.get<bool>();
+ }
+
+ private:
+ void renderFrameCounter( OutputDevice& rOutDev );
+ void renderSpriteCount( OutputDevice& rOutDev );
+ void renderMemUsage( OutputDevice& rOutDev );
+
+ /// Set from the SpriteCanvas: instance coordinating sprite redraw
+ ::canvas::SpriteRedrawManager* mpRedrawManager;
+
+ /// Set from the init method. used to generate sprites
+ SpriteCanvas* mpOwningSpriteCanvas;
+
+ /** Background compositing surface.
+
+ Typically, sprites will be composited in the background,
+ before pushing them to screen. This happens here.
+ */
+ VclPtr< VirtualDevice > maVDev;
+
+ /// For the frame counter timings
+ ::canvas::tools::ElapsedTime maLastUpdate;
+
+ /// When true, canvas displays debug info on each frame
+ bool mbShowFrameInfo;
+
+ /// When true, canvas creates all new sprites with red lines in the corners
+ bool mbShowSpriteBounds;
+
+ /// When true, canvas uses the scroll optimization (direct scrolls in front buffer)
+ bool mbIsUnsafeScrolling;
+ };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/vcl/spritedevicehelper.cxx b/canvas/source/vcl/spritedevicehelper.cxx
new file mode 100644
index 000000000..99f8b1914
--- /dev/null
+++ b/canvas/source/vcl/spritedevicehelper.cxx
@@ -0,0 +1,119 @@
+/* -*- 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/diagnose.h>
+#include <vcl/bitmapex.hxx>
+#include <vcl/dibtools.hxx>
+#include <tools/stream.hxx>
+
+#include "spritedevicehelper.hxx"
+#include "impltools.hxx"
+
+using namespace ::com::sun::star;
+
+namespace vclcanvas
+{
+ SpriteDeviceHelper::SpriteDeviceHelper()
+ {
+ }
+
+ void SpriteDeviceHelper::init( const OutDevProviderSharedPtr& pOutDev )
+ {
+ DeviceHelper::init(pOutDev);
+
+ // setup back buffer
+ OutputDevice& rOutDev( pOutDev->getOutDev() );
+ mpBackBuffer = std::make_shared<BackBuffer>( rOutDev );
+ mpBackBuffer->setSize( rOutDev.GetOutputSizePixel() );
+
+ tools::SetDefaultDeviceAntiAliasing( &mpBackBuffer->getOutDev());
+ }
+
+ bool SpriteDeviceHelper::showBuffer( bool, bool )
+ {
+ OSL_FAIL("Not supposed to be called, handled by SpriteCanvas");
+ return false;
+ }
+
+ bool SpriteDeviceHelper::switchBuffer( bool, bool )
+ {
+ OSL_FAIL("Not supposed to be called, handled by SpriteCanvas");
+ return false;
+ }
+
+ void SpriteDeviceHelper::disposing()
+ {
+ // release all references
+ mpBackBuffer.reset();
+
+ DeviceHelper::disposing();
+ }
+
+ uno::Any SpriteDeviceHelper::isAccelerated() const
+ {
+ return DeviceHelper::isAccelerated();
+ }
+
+ uno::Any SpriteDeviceHelper::getDeviceHandle() const
+ {
+ return DeviceHelper::getDeviceHandle();
+ }
+
+ uno::Any SpriteDeviceHelper::getSurfaceHandle() const
+ {
+ if( !mpBackBuffer )
+ return uno::Any();
+
+ return uno::Any(
+ reinterpret_cast< sal_Int64 >(&mpBackBuffer->getOutDev()) );
+ }
+
+ void SpriteDeviceHelper::notifySizeUpdate( const awt::Rectangle& rBounds )
+ {
+ if( mpBackBuffer )
+ mpBackBuffer->setSize( ::Size(rBounds.Width,
+ rBounds.Height) );
+ }
+
+ void SpriteDeviceHelper::dumpScreenContent() const
+ {
+ DeviceHelper::dumpScreenContent();
+
+ static sal_Int32 nFilePostfixCount(0);
+
+ if( mpBackBuffer )
+ {
+ OUString aFilename = "dbg_backbuffer" + OUString::number(nFilePostfixCount) + ".bmp";
+
+ SvFileStream aStream( aFilename, StreamMode::STD_READWRITE );
+
+ const ::Point aEmptyPoint;
+ mpBackBuffer->getOutDev().EnableMapMode( false );
+ mpBackBuffer->getOutDev().SetAntialiasing( AntialiasingFlags::Enable );
+ WriteDIB(mpBackBuffer->getOutDev().GetBitmapEx(aEmptyPoint, mpBackBuffer->getOutDev().GetOutputSizePixel()), aStream, false);
+ }
+
+ ++nFilePostfixCount;
+ }
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/vcl/spritedevicehelper.hxx b/canvas/source/vcl/spritedevicehelper.hxx
new file mode 100644
index 000000000..5f4ef570a
--- /dev/null
+++ b/canvas/source/vcl/spritedevicehelper.hxx
@@ -0,0 +1,60 @@
+/* -*- 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 "backbuffer.hxx"
+#include "devicehelper.hxx"
+
+
+/* Definition of DeviceHelper class */
+
+namespace vclcanvas
+{
+ class SpriteDeviceHelper : public DeviceHelper
+ {
+ public:
+ SpriteDeviceHelper();
+
+ void init( const OutDevProviderSharedPtr& rOutDev );
+
+ /// Dispose all internal references
+ void disposing();
+
+ bool showBuffer( bool bWindowVisible, bool bUpdateAll );
+ bool switchBuffer( bool bWindowVisible, bool bUpdateAll );
+
+ css::uno::Any isAccelerated() const;
+ css::uno::Any getDeviceHandle() const;
+ css::uno::Any getSurfaceHandle() const;
+
+ void dumpScreenContent() const;
+ const BackBufferSharedPtr& getBackBuffer() const { return mpBackBuffer; }
+
+ void notifySizeUpdate( const css::awt::Rectangle& rBounds );
+
+ private:
+ /// This buffer holds the background content for all associated canvases
+ BackBufferSharedPtr mpBackBuffer;
+ };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/vcl/spritehelper.cxx b/canvas/source/vcl/spritehelper.cxx
new file mode 100644
index 000000000..8e8d88ac3
--- /dev/null
+++ b/canvas/source/vcl/spritehelper.cxx
@@ -0,0 +1,231 @@
+/* -*- 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/range/b2drectangle.hxx>
+#include <basegfx/utils/canvastools.hxx>
+#include <rtl/math.hxx>
+#include <tools/diagnose_ex.h>
+#include <vcl/alpha.hxx>
+#include <vcl/bitmapex.hxx>
+#include <vcl/canvastools.hxx>
+#include <vcl/outdev.hxx>
+
+#include <canvas/canvastools.hxx>
+
+#include "spritehelper.hxx"
+
+using namespace ::com::sun::star;
+
+
+namespace vclcanvas
+{
+ SpriteHelper::SpriteHelper() :
+ mbShowSpriteBounds(false)
+ {
+ }
+
+ void SpriteHelper::init( const geometry::RealSize2D& rSpriteSize,
+ const ::canvas::SpriteSurface::Reference& rOwningSpriteCanvas,
+ const BackBufferSharedPtr& rBackBuffer,
+ const BackBufferSharedPtr& rBackBufferMask,
+ bool bShowSpriteBounds )
+ {
+ ENSURE_OR_THROW( rOwningSpriteCanvas && rBackBuffer && rBackBufferMask,
+ "SpriteHelper::init(): Invalid sprite canvas or back buffer" );
+
+ mpBackBuffer = rBackBuffer;
+ mpBackBufferMask = rBackBufferMask;
+ mbShowSpriteBounds = bShowSpriteBounds;
+
+ init( rSpriteSize, rOwningSpriteCanvas );
+ }
+
+ void SpriteHelper::disposing()
+ {
+ mpBackBuffer.reset();
+ mpBackBufferMask.reset();
+
+ // forward to parent
+ CanvasCustomSpriteHelper::disposing();
+ }
+
+ void SpriteHelper::redraw( OutputDevice& rTargetSurface,
+ const ::basegfx::B2DPoint& rPos,
+ bool& io_bSurfacesDirty,
+ bool /*bBufferedUpdate*/ ) const
+ {
+ if( !mpBackBuffer ||
+ !mpBackBufferMask )
+ {
+ return; // we're disposed
+ }
+
+ // log output pos in device pixel
+ SAL_INFO("canvas.vcl", "SpriteHelper::redraw(): output pos is (" <<
+ rPos.getX() << "," << rPos.getY() << ")");
+
+ const double fAlpha( getAlpha() );
+
+ if( !isActive() || ::basegfx::fTools::equalZero( fAlpha ) )
+ return;
+
+ const ::basegfx::B2DVector& rOrigOutputSize( getSizePixel() );
+
+ // might get changed below (e.g. adapted for
+ // transformations). IMPORTANT: both position and size are
+ // rounded to integer values. From now on, only those
+ // rounded values are used, to keep clip and content in
+ // sync.
+ ::Size aOutputSize( vcl::unotools::sizeFromB2DSize( rOrigOutputSize ) );
+ ::Point aOutPos( vcl::unotools::pointFromB2DPoint( rPos ) );
+
+
+ // TODO(F3): Support for alpha-VDev
+
+ // Do we have to update our bitmaps (necessary if virdev
+ // was painted to, or transformation changed)?
+ const bool bNeedBitmapUpdate( io_bSurfacesDirty ||
+ hasTransformChanged() ||
+ maContent->IsEmpty() );
+
+ // updating content of sprite cache - surface is no
+ // longer dirty in relation to our cache
+ io_bSurfacesDirty = false;
+ transformUpdated();
+
+ if( bNeedBitmapUpdate )
+ {
+ const Point aEmptyPoint;
+ BitmapEx aBmp( mpBackBuffer->getOutDev().GetBitmapEx( aEmptyPoint,
+ aOutputSize ) );
+
+ if( isContentFullyOpaque() )
+ {
+ // optimized case: content canvas is fully
+ // opaque. Note: since we retrieved aBmp directly
+ // from an OutDev, it's already a 'display bitmap'
+ // on windows.
+ maContent = aBmp;
+ }
+ else
+ {
+ // sprite content might contain alpha, create
+ // BmpEx, then.
+ BitmapEx aMask( mpBackBufferMask->getOutDev().GetBitmapEx( aEmptyPoint,
+ aOutputSize ) );
+
+ // Note: since we retrieved aBmp and aMask
+ // directly from an OutDev, it's already a
+ // 'display bitmap' on windows.
+ maContent = BitmapEx( aBmp.GetBitmap(), AlphaMask( aMask.GetBitmap()) );
+ }
+ }
+
+ ::basegfx::B2DHomMatrix aTransform( getTransformation() );
+
+ rTargetSurface.Push( vcl::PushFlags::CLIPREGION );
+
+ // apply clip (if any)
+ if( getClip().is() )
+ {
+ ::basegfx::B2DPolyPolygon aClipPoly(
+ ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(
+ getClip() ));
+
+ if( aClipPoly.count() )
+ {
+ // Move the clip to the final sprite output position.
+ ::basegfx::B2DHomMatrix aClipTransform( aTransform );
+ aClipTransform.translate( aOutPos.X(), aOutPos.Y() );
+ aClipPoly.transform( aClipTransform );
+
+ if( mbShowSpriteBounds )
+ {
+ // Paint green sprite clip area
+ rTargetSurface.SetLineColor( Color( 0,255,0 ) );
+ rTargetSurface.SetFillColor();
+
+ rTargetSurface.DrawPolyPolygon(::tools::PolyPolygon(aClipPoly)); // #i76339#
+ }
+
+ vcl::Region aClipRegion( aClipPoly );
+ rTargetSurface.SetClipRegion( aClipRegion );
+ }
+ }
+
+ ::basegfx::B2DHomMatrix aSizeTransform, aMoveTransform;
+ aSizeTransform.scale( aOutputSize.Width(), aOutputSize.Height() );
+ aMoveTransform.translate( aOutPos.X(), aOutPos.Y() );
+ aTransform = aMoveTransform * aTransform * aSizeTransform;
+
+ rTargetSurface.DrawTransformedBitmapEx( aTransform, *maContent, fAlpha );
+
+ rTargetSurface.Pop();
+
+ if( !mbShowSpriteBounds )
+ return;
+
+ ::tools::PolyPolygon aMarkerPoly(
+ ::canvas::tools::getBoundMarksPolyPolygon(
+ ::basegfx::B2DRectangle(aOutPos.X(),
+ aOutPos.Y(),
+ aOutPos.X() + aOutputSize.Width()-1,
+ aOutPos.Y() + aOutputSize.Height()-1) ) );
+
+ // Paint little red sprite area markers
+ rTargetSurface.SetLineColor( COL_RED );
+ rTargetSurface.SetFillColor();
+
+ for( int i=0; i<aMarkerPoly.Count(); ++i )
+ {
+ rTargetSurface.DrawPolyLine( aMarkerPoly.GetObject(static_cast<sal_uInt16>(i)) );
+ }
+
+ // paint sprite prio
+ vcl::Font aVCLFont;
+ aVCLFont.SetFontHeight( std::min(::tools::Long(20),aOutputSize.Height()) );
+ aVCLFont.SetColor( COL_RED );
+
+ rTargetSurface.SetTextAlign(ALIGN_TOP);
+ rTargetSurface.SetTextColor( COL_RED );
+ rTargetSurface.SetFont( aVCLFont );
+
+ OUString text( ::rtl::math::doubleToUString( getPriority(),
+ rtl_math_StringFormat_F,
+ 2,'.',nullptr,' ') );
+
+ rTargetSurface.DrawText( aOutPos+Point(2,2), text );
+ SAL_INFO( "canvas.vcl",
+ "sprite " << this << " has prio " << getPriority());
+ }
+
+ ::basegfx::B2DPolyPolygon SpriteHelper::polyPolygonFromXPolyPolygon2D( uno::Reference< rendering::XPolyPolygon2D >& xPoly ) const
+ {
+ return ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D( xPoly );
+ }
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/vcl/spritehelper.hxx b/canvas/source/vcl/spritehelper.hxx
new file mode 100644
index 000000000..41d9883f6
--- /dev/null
+++ b/canvas/source/vcl/spritehelper.hxx
@@ -0,0 +1,108 @@
+/* -*- 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 <vcl/bitmapex.hxx>
+
+#include <base/canvascustomspritehelper.hxx>
+#include <base/spritesurface.hxx>
+#include <vclwrapper.hxx>
+
+#include "backbuffer.hxx"
+
+
+namespace vclcanvas
+{
+ /* 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:
+ SpriteHelper();
+
+ // make CanvasCustomSpriteHelper::init visible for name lookup
+ using ::canvas::CanvasCustomSpriteHelper::init;
+
+ /** 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 rBackBuffer
+ Buffer of the sprite content (non-alpha part)
+
+ @param rBackBufferMask
+ Buffer of the sprite content (alpha part)
+ */
+ void init( const css::geometry::RealSize2D& rSpriteSize,
+ const ::canvas::SpriteSurface::Reference& rOwningSpriteCanvas,
+ const BackBufferSharedPtr& rBackBuffer,
+ const BackBufferSharedPtr& rBackBufferMask,
+ bool bShowSpriteBounds );
+
+ void disposing();
+
+ /** Repaint sprite content to associated sprite canvas
+
+ @param rPos
+ Output position (sprite's own position is disregarded)
+
+ @param io_bSurfacesDirty
+ When true, the referenced sprite surfaces (backBuffer and
+ backBufferMask) have been modified since last call.
+
+ @param bBufferedUpdate
+ When true, the redraw does <em>not</em> happen directly on
+ the front buffer, but within a VDev. Used to speed up
+ drawing.
+ */
+ void redraw( OutputDevice& rOutDev,
+ const ::basegfx::B2DPoint& rPos,
+ bool& bSurfacesDirty,
+ bool bBufferedUpdate ) const;
+
+ private:
+ virtual ::basegfx::B2DPolyPolygon polyPolygonFromXPolyPolygon2D(
+ css::uno::Reference< css::rendering::XPolyPolygon2D >& xPoly ) const override;
+
+ // for the redraw
+ BackBufferSharedPtr mpBackBuffer;
+ BackBufferSharedPtr mpBackBufferMask;
+
+ /// Cached bitmap for the current sprite content
+ mutable ::canvas::vcltools::VCLObject<BitmapEx> maContent;
+
+ /// When true, line sprite corners in red
+ bool mbShowSpriteBounds;
+
+ };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/vcl/textlayout.cxx b/canvas/source/vcl/textlayout.cxx
new file mode 100644
index 000000000..63a3453ff
--- /dev/null
+++ b/canvas/source/vcl/textlayout.cxx
@@ -0,0 +1,428 @@
+/* -*- 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 <tools/diagnose_ex.h>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/numeric/ftools.hxx>
+#include <basegfx/utils/canvastools.hxx>
+#include <com/sun/star/rendering/CompositeOperation.hpp>
+#include <com/sun/star/rendering/RenderState.hpp>
+#include <com/sun/star/rendering/TextDirection.hpp>
+#include <com/sun/star/rendering/ViewState.hpp>
+#include <comphelper/sequence.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <utility>
+#include <vcl/metric.hxx>
+#include <vcl/virdev.hxx>
+
+#include <canvas/canvastools.hxx>
+
+#include "textlayout.hxx"
+
+#include <memory>
+
+using namespace ::com::sun::star;
+
+namespace vclcanvas
+{
+ namespace
+ {
+ void setupLayoutMode( OutputDevice& rOutDev,
+ sal_Int8 nTextDirection )
+ {
+ // TODO(P3): avoid if already correctly set
+ vcl::text::ComplexTextLayoutFlags nLayoutMode = vcl::text::ComplexTextLayoutFlags::Default;
+ switch( nTextDirection )
+ {
+ case rendering::TextDirection::WEAK_LEFT_TO_RIGHT:
+ break;
+ case rendering::TextDirection::STRONG_LEFT_TO_RIGHT:
+ nLayoutMode = vcl::text::ComplexTextLayoutFlags::BiDiStrong;
+ break;
+ case rendering::TextDirection::WEAK_RIGHT_TO_LEFT:
+ nLayoutMode = vcl::text::ComplexTextLayoutFlags::BiDiRtl;
+ break;
+ case rendering::TextDirection::STRONG_RIGHT_TO_LEFT:
+ nLayoutMode = vcl::text::ComplexTextLayoutFlags::BiDiRtl | vcl::text::ComplexTextLayoutFlags::BiDiStrong;
+ break;
+ default:
+ break;
+ }
+
+ // set calculated layout mode. Origin is always the left edge,
+ // as required at the API spec
+ rOutDev.SetLayoutMode( nLayoutMode | vcl::text::ComplexTextLayoutFlags::TextOriginLeft );
+ }
+ }
+
+ TextLayout::TextLayout( rendering::StringContext aText,
+ sal_Int8 nDirection,
+ CanvasFont::Reference rFont,
+ uno::Reference<rendering::XGraphicDevice> xDevice,
+ OutDevProviderSharedPtr xOutDev ) :
+ TextLayout_Base( m_aMutex ),
+ maText(std::move( aText )),
+ mpFont(std::move( rFont )),
+ mxDevice(std::move( xDevice )),
+ mpOutDevProvider(std::move( xOutDev )),
+ mnTextDirection( nDirection )
+ {}
+
+ void SAL_CALL TextLayout::disposing()
+ {
+ SolarMutexGuard aGuard;
+
+ mpOutDevProvider.reset();
+ mxDevice.clear();
+ mpFont.clear();
+ }
+
+ // XTextLayout
+ uno::Sequence< uno::Reference< rendering::XPolyPolygon2D > > SAL_CALL TextLayout::queryTextShapes( )
+ {
+ SolarMutexGuard aGuard;
+
+ OutputDevice& rOutDev = mpOutDevProvider->getOutDev();
+ ScopedVclPtrInstance< VirtualDevice > pVDev( rOutDev );
+ pVDev->SetFont( mpFont->getVCLFont() );
+
+ setupLayoutMode( *pVDev, mnTextDirection );
+
+ const rendering::ViewState aViewState(
+ geometry::AffineMatrix2D(1,0,0, 0,1,0),
+ nullptr);
+
+ rendering::RenderState aRenderState (
+ geometry::AffineMatrix2D(1,0,0,0,1,0),
+ nullptr,
+ uno::Sequence<double>(4),
+ rendering::CompositeOperation::SOURCE);
+
+ std::vector<sal_Int32> aOffsets(maLogicalAdvancements.getLength());
+ setupTextOffsets(aOffsets.data(), maLogicalAdvancements, aViewState, aRenderState);
+
+ std::vector< uno::Reference< rendering::XPolyPolygon2D> > aOutlineSequence;
+ ::basegfx::B2DPolyPolygonVector aOutlines;
+ if (pVDev->GetTextOutlines(
+ aOutlines,
+ maText.Text,
+ maText.StartPosition,
+ maText.StartPosition,
+ maText.Length,
+ 0,
+ aOffsets))
+ {
+ aOutlineSequence.reserve(aOutlines.size());
+ sal_Int32 nIndex (0);
+ for (auto const& outline : aOutlines)
+ {
+ aOutlineSequence[nIndex++] = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
+ mxDevice,
+ outline);
+ }
+ }
+
+ return comphelper::containerToSequence(aOutlineSequence);
+ }
+
+ uno::Sequence< geometry::RealRectangle2D > SAL_CALL TextLayout::queryInkMeasures( )
+ {
+ SolarMutexGuard aGuard;
+
+
+ OutputDevice& rOutDev = mpOutDevProvider->getOutDev();
+ ScopedVclPtrInstance< VirtualDevice > pVDev( rOutDev );
+ pVDev->SetFont( mpFont->getVCLFont() );
+
+ setupLayoutMode( *pVDev, mnTextDirection );
+
+ const rendering::ViewState aViewState(
+ geometry::AffineMatrix2D(1,0,0, 0,1,0),
+ nullptr);
+
+ rendering::RenderState aRenderState (
+ geometry::AffineMatrix2D(1,0,0,0,1,0),
+ nullptr,
+ uno::Sequence<double>(4),
+ rendering::CompositeOperation::SOURCE);
+
+ std::unique_ptr< sal_Int32 []> aOffsets(new sal_Int32[maLogicalAdvancements.getLength()]);
+ setupTextOffsets(aOffsets.get(), maLogicalAdvancements, aViewState, aRenderState);
+
+ std::vector< ::tools::Rectangle > aMetricVector;
+ uno::Sequence<geometry::RealRectangle2D> aBoundingBoxes;
+ if (pVDev->GetGlyphBoundRects(
+ Point(0,0),
+ maText.Text,
+ ::canvas::tools::numeric_cast<sal_uInt16>(maText.StartPosition),
+ ::canvas::tools::numeric_cast<sal_uInt16>(maText.Length),
+ aMetricVector))
+ {
+ aBoundingBoxes.realloc(aMetricVector.size());
+ auto pBoundingBoxes = aBoundingBoxes.getArray();
+ sal_Int32 nIndex (0);
+ for (auto const& metric : aMetricVector)
+ {
+ pBoundingBoxes[nIndex++] = geometry::RealRectangle2D(
+ metric.Left(),
+ metric.Top(),
+ metric.Right(),
+ metric.Bottom());
+ }
+ }
+ return aBoundingBoxes;
+ }
+
+ uno::Sequence< geometry::RealRectangle2D > SAL_CALL TextLayout::queryMeasures( )
+ {
+ // TODO(F1)
+ return uno::Sequence< geometry::RealRectangle2D >();
+ }
+
+ uno::Sequence< double > SAL_CALL TextLayout::queryLogicalAdvancements( )
+ {
+ SolarMutexGuard aGuard;
+
+ return maLogicalAdvancements;
+ }
+
+ void SAL_CALL TextLayout::applyLogicalAdvancements( const uno::Sequence< double >& aAdvancements )
+ {
+ SolarMutexGuard aGuard;
+
+ ENSURE_ARG_OR_THROW( aAdvancements.getLength() == maText.Length,
+ "TextLayout::applyLogicalAdvancements(): mismatching number of advancements" );
+
+ maLogicalAdvancements = aAdvancements;
+ }
+
+ geometry::RealRectangle2D SAL_CALL TextLayout::queryTextBounds( )
+ {
+ SolarMutexGuard aGuard;
+
+ if( !mpOutDevProvider )
+ return geometry::RealRectangle2D();
+
+ OutputDevice& rOutDev = mpOutDevProvider->getOutDev();
+
+ ScopedVclPtrInstance< VirtualDevice > pVDev( rOutDev );
+ pVDev->SetFont( mpFont->getVCLFont() );
+
+ // need metrics for Y offset, the XCanvas always renders
+ // relative to baseline
+ const ::FontMetric& aMetric( pVDev->GetFontMetric() );
+
+ setupLayoutMode( *pVDev, mnTextDirection );
+
+ const sal_Int32 nAboveBaseline( -aMetric.GetAscent() );
+ const sal_Int32 nBelowBaseline( aMetric.GetDescent() );
+
+ if( maLogicalAdvancements.hasElements() )
+ {
+ return geometry::RealRectangle2D( 0, nAboveBaseline,
+ maLogicalAdvancements[ maLogicalAdvancements.getLength()-1 ],
+ nBelowBaseline );
+ }
+ else
+ {
+ return geometry::RealRectangle2D( 0, nAboveBaseline,
+ pVDev->GetTextWidth(
+ maText.Text,
+ ::canvas::tools::numeric_cast<sal_uInt16>(maText.StartPosition),
+ ::canvas::tools::numeric_cast<sal_uInt16>(maText.Length) ),
+ nBelowBaseline );
+ }
+ }
+
+ double SAL_CALL TextLayout::justify( double )
+ {
+ // TODO(F1)
+ return 0.0;
+ }
+
+ double SAL_CALL TextLayout::combinedJustify( const uno::Sequence< uno::Reference< rendering::XTextLayout > >&,
+ double )
+ {
+ // TODO(F1)
+ return 0.0;
+ }
+
+ rendering::TextHit SAL_CALL TextLayout::getTextHit( const geometry::RealPoint2D& )
+ {
+ // TODO(F1)
+ return rendering::TextHit();
+ }
+
+ rendering::Caret SAL_CALL TextLayout::getCaret( sal_Int32, sal_Bool )
+ {
+ // TODO(F1)
+ return rendering::Caret();
+ }
+
+ sal_Int32 SAL_CALL TextLayout::getNextInsertionIndex( sal_Int32, sal_Int32, sal_Bool )
+ {
+ // TODO(F1)
+ return 0;
+ }
+
+ uno::Reference< rendering::XPolyPolygon2D > SAL_CALL TextLayout::queryVisualHighlighting( sal_Int32, sal_Int32 )
+ {
+ // TODO(F1)
+ return uno::Reference< rendering::XPolyPolygon2D >();
+ }
+
+ uno::Reference< rendering::XPolyPolygon2D > SAL_CALL TextLayout::queryLogicalHighlighting( sal_Int32, sal_Int32 )
+ {
+ // TODO(F1)
+ return uno::Reference< rendering::XPolyPolygon2D >();
+ }
+
+ double SAL_CALL TextLayout::getBaselineOffset( )
+ {
+ // TODO(F1)
+ return 0.0;
+ }
+
+ sal_Int8 SAL_CALL TextLayout::getMainTextDirection( )
+ {
+ SolarMutexGuard aGuard;
+
+ return mnTextDirection;
+ }
+
+ uno::Reference< rendering::XCanvasFont > SAL_CALL TextLayout::getFont( )
+ {
+ SolarMutexGuard aGuard;
+
+ return mpFont;
+ }
+
+ rendering::StringContext SAL_CALL TextLayout::getText( )
+ {
+ SolarMutexGuard aGuard;
+
+ return maText;
+ }
+
+ void TextLayout::draw( OutputDevice& rOutDev,
+ const Point& rOutpos,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState ) const
+ {
+ SolarMutexGuard aGuard;
+
+ setupLayoutMode( rOutDev, mnTextDirection );
+
+ if( maLogicalAdvancements.hasElements() )
+ {
+ // TODO(P2): cache that
+ std::vector<sal_Int32> aOffsets(maLogicalAdvancements.getLength());
+ setupTextOffsets( aOffsets.data(), maLogicalAdvancements, viewState, renderState );
+
+ // TODO(F3): ensure correct length and termination for DX
+ // array (last entry _must_ contain the overall width)
+
+ rOutDev.DrawTextArray( rOutpos,
+ maText.Text,
+ aOffsets,
+ ::canvas::tools::numeric_cast<sal_uInt16>(maText.StartPosition),
+ ::canvas::tools::numeric_cast<sal_uInt16>(maText.Length) );
+ }
+ else
+ {
+ rOutDev.DrawText( rOutpos,
+ maText.Text,
+ ::canvas::tools::numeric_cast<sal_uInt16>(maText.StartPosition),
+ ::canvas::tools::numeric_cast<sal_uInt16>(maText.Length) );
+ }
+ }
+
+ namespace
+ {
+ class OffsetTransformer
+ {
+ public:
+ explicit OffsetTransformer( ::basegfx::B2DHomMatrix aMat ) :
+ maMatrix(std::move( aMat ))
+ {
+ }
+
+ sal_Int32 operator()( const double& rOffset )
+ {
+ // This is an optimization of the normal rMat*[x,0]
+ // transformation of the advancement vector (in x
+ // direction), followed by a length calculation of the
+ // resulting vector: advancement' =
+ // ||rMat*[x,0]||. Since advancements are vectors, we
+ // can ignore translational components, thus if [x,0],
+ // it follows that rMat*[x,0]=[x',0] holds. Thus, we
+ // just have to calc the transformation of the x
+ // component.
+
+ // TODO(F2): Handle non-horizontal advancements!
+ return ::basegfx::fround( hypot(maMatrix.get(0,0)*rOffset,
+ maMatrix.get(1,0)*rOffset) );
+ }
+
+ private:
+ ::basegfx::B2DHomMatrix maMatrix;
+ };
+ }
+
+ void TextLayout::setupTextOffsets( sal_Int32* outputOffsets,
+ const uno::Sequence< double >& inputOffsets,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState ) const
+ {
+ ENSURE_OR_THROW( outputOffsets!=nullptr,
+ "TextLayout::setupTextOffsets offsets NULL" );
+
+ ::basegfx::B2DHomMatrix aMatrix;
+
+ ::canvas::tools::mergeViewAndRenderTransform(aMatrix,
+ viewState,
+ renderState);
+
+ // fill integer offsets
+ std::transform( inputOffsets.begin(),
+ inputOffsets.end(),
+ outputOffsets,
+ OffsetTransformer( aMatrix ) );
+ }
+
+ OUString SAL_CALL TextLayout::getImplementationName()
+ {
+ return "VCLCanvas::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/vcl/textlayout.hxx b/canvas/source/vcl/textlayout.hxx
new file mode 100644
index 000000000..5a403c280
--- /dev/null
+++ b/canvas/source/vcl/textlayout.hxx
@@ -0,0 +1,103 @@
+/* -*- 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/StringContext.hpp>
+#include <com/sun/star/rendering/XTextLayout.hpp>
+
+#include "canvasfont.hxx"
+#include "impltools.hxx"
+
+
+/* Definition of TextLayout class */
+
+namespace vclcanvas
+{
+ typedef ::cppu::WeakComponentImplHelper< css::rendering::XTextLayout,
+ css::lang::XServiceInfo > TextLayout_Base;
+
+ class TextLayout : public ::cppu::BaseMutex,
+ public TextLayout_Base
+ {
+ public:
+ /// make noncopyable
+ TextLayout(const TextLayout&) = delete;
+ const TextLayout& operator=(const TextLayout&) = delete;
+
+ TextLayout( css::rendering::StringContext aText,
+ sal_Int8 nDirection,
+ CanvasFont::Reference rFont,
+ css::uno::Reference<
+ css::rendering::XGraphicDevice> xDevice,
+ OutDevProviderSharedPtr xOutDev );
+
+ /// 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::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;
+
+ void draw( OutputDevice& rOutDev,
+ const Point& rOutpos,
+ const css::rendering::ViewState& viewState,
+ const css::rendering::RenderState& renderState ) const;
+
+ private:
+ void setupTextOffsets( sal_Int32* outputOffsets,
+ const css::uno::Sequence< double >& inputOffsets,
+ const css::rendering::ViewState& viewState,
+ const css::rendering::RenderState& renderState ) const;
+
+ css::rendering::StringContext maText;
+ css::uno::Sequence< double > maLogicalAdvancements;
+ CanvasFont::Reference mpFont;
+ css::uno::Reference< css::rendering::XGraphicDevice> mxDevice;
+ OutDevProviderSharedPtr mpOutDevProvider;
+ sal_Int8 mnTextDirection;
+ };
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/vcl/vclcanvas.component b/canvas/source/vcl/vclcanvas.component
new file mode 100644
index 000000000..c2e0ffc09
--- /dev/null
+++ b/canvas/source/vcl/vclcanvas.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.Canvas.VCL"
+ constructor="com_sun_star_comp_rendering_Canvas_VCL_get_implementation">
+ <service name="com.sun.star.rendering.Canvas.VCL"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.rendering.SpriteCanvas.VCL"
+ constructor="com_sun_star_comp_rendering_SpriteCanvas_VCL_get_implementation">
+ <service name="com.sun.star.rendering.SpriteCanvas.VCL"/>
+ </implementation>
+</component>
diff --git a/canvas/source/vcl/windowoutdevholder.cxx b/canvas/source/vcl/windowoutdevholder.cxx
new file mode 100644
index 000000000..4776ee554
--- /dev/null
+++ b/canvas/source/vcl/windowoutdevholder.cxx
@@ -0,0 +1,49 @@
+/* -*- 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 <com/sun/star/lang/NoSupportException.hpp>
+#include <toolkit/helper/vclunohelper.hxx>
+
+#include "windowoutdevholder.hxx"
+
+using namespace ::com::sun::star;
+
+namespace vclcanvas
+{
+ namespace
+ {
+ vcl::Window& windowFromXWin( const uno::Reference<awt::XWindow>& xWin )
+ {
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow(xWin);
+ if( !pWindow )
+ throw lang::NoSupportException(
+ "Parent window not VCL window, or canvas out-of-process!",
+ nullptr);
+ return *pWindow;
+ }
+ }
+
+ WindowOutDevHolder::WindowOutDevHolder( const uno::Reference<awt::XWindow>& xWin ) :
+ mrOutputWindow( windowFromXWin(xWin) )
+ {}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/vcl/windowoutdevholder.hxx b/canvas/source/vcl/windowoutdevholder.hxx
new file mode 100644
index 000000000..87138a8a9
--- /dev/null
+++ b/canvas/source/vcl/windowoutdevholder.hxx
@@ -0,0 +1,56 @@
+/* -*- 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/awt/XWindow.hpp>
+#include <vcl/window.hxx>
+
+#include "outdevprovider.hxx"
+
+namespace vclcanvas
+{
+ class WindowOutDevHolder : public OutDevProvider
+ {
+ public:
+ WindowOutDevHolder(const WindowOutDevHolder&) = delete;
+ const WindowOutDevHolder operator=(const WindowOutDevHolder&) = delete;
+
+ explicit WindowOutDevHolder( const css::uno::Reference< css::awt::XWindow>& xWin );
+
+ private:
+ virtual OutputDevice& getOutDev() override { return *mrOutputWindow.GetOutDev(); }
+ virtual const OutputDevice& getOutDev() const override { return *mrOutputWindow.GetOutDev(); }
+
+ // TODO(Q2): Lifetime issue. Though WindowGraphicDeviceBase
+ // now listens to the window component, I still consider
+ // holding a naked reference unsafe here (especially as we
+ // pass it around via getOutDev). This _only_ works reliably,
+ // if disposing the SpriteCanvas correctly disposes all
+ // entities which hold this pointer.
+ // So: as soon as the protocol inside
+ // vcl/source/window/window.cxx is broken, that disposes the
+ // canvas during window deletion, we're riding a dead horse
+ // here
+ vcl::Window& mrOutputWindow;
+ };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */